From 05e330fcae99e4c33cfa38112a943b8a45c1a6c1 Mon Sep 17 00:00:00 2001 From: iddoeldor Date: Fri, 8 Jun 2018 16:56:22 +0300 Subject: [PATCH] scripts directory --- README.md | 2 +- check_for_native_calls.py | 83 ----------- dump_dynamically_created_files.py | 119 ---------------- exec_shell_cmd.py | 75 ---------- how_to_access_inner_class_static_field.md | 73 ---------- install_frida_server.sh | 20 --- log_string_builders_and_string_compare.js | 23 --- objc_ssl_unppining_helper.js | 105 -------------- print_native_method_arguments.py | 164 ---------------------- 9 files changed, 1 insertion(+), 663 deletions(-) delete mode 100644 check_for_native_calls.py delete mode 100644 dump_dynamically_created_files.py delete mode 100644 exec_shell_cmd.py delete mode 100644 how_to_access_inner_class_static_field.md delete mode 100644 install_frida_server.sh delete mode 100644 log_string_builders_and_string_compare.js delete mode 100644 objc_ssl_unppining_helper.js delete mode 100644 print_native_method_arguments.py diff --git a/README.md b/README.md index 041e988..247e32c 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ - [Hook JNI by address](#hook-jni-by-address) - [Print all runtime strings & Stacktrace](#print-runtime-strings) - [Find iOS application UUID](#find-ios-application-uuid) - - [Execute shell command](https://github.com/iddoeldor/frida-snippets/blob/master/exec_shell_cmd.py) + - [Execute shell command](https://github.com/iddoeldor/frida-snippets/blob/master/scripts/exec_shell_cmd.py) - [TODO list](#todos) #### Enumerate loaded classes diff --git a/check_for_native_calls.py b/check_for_native_calls.py deleted file mode 100644 index 61f3dfd..0000000 --- a/check_for_native_calls.py +++ /dev/null @@ -1,83 +0,0 @@ -# Check for native library calls and return a stacktrace -import sys -import frida -from pprint import pprint - - -def on_message(m, _data): - if m['type'] == 'send': - print(m['payload']) - else: - if m['type'] == 'error': - pprint(m) - exit(2) - - -jscode = """ -Java.perform(function() { - - var SystemDef = Java.use('java.lang.System'); - - var RuntimeDef = Java.use('java.lang.Runtime'); - - var exceptionClass = Java.use('java.lang.Exception'); - - var SystemLoad_1 = SystemDef.load.overload('java.lang.String'); - - var SystemLoad_2 = SystemDef.loadLibrary.overload('java.lang.String'); - - var RuntimeLoad_1 = RuntimeDef.load.overload('java.lang.String'); - - var RuntimeLoad_2 = RuntimeDef.loadLibrary.overload('java.lang.String'); - - var ThreadDef = Java.use('java.lang.Thread'); - - var ThreadObj = ThreadDef.$new(); - - SystemLoad_1.implementation = function(library) { - send("[1] Loading dynamic library => " + library); - stackTrace(); - return SystemLoad_1.call(this, library); - } - - SystemLoad_2.implementation = function(library) { - send("[2] Loading dynamic library => " + library); - stackTrace(); - SystemLoad_2.call(this, library); - return; - } - - RuntimeLoad_1.implementation = function(library) { - send("[3] Loading dynamic library => " + library); - stackTrace(); - RuntimeLoad_1.call(this, library); - return; - } - - RuntimeLoad_2.implementation = function(library) { - send("[4] Loading dynamic library => " + library); - stackTrace(); - RuntimeLoad_2.call(this, library); - return; - } - - function stackTrace() { - var stack = ThreadObj.currentThread().getStackTrace(); - for (var i = 0; i < stack.length; i++) { - send(i + " => " + stack[i].toString()); - } - send("--------------------------------------------------------------------------"); - } - -}); -""" -APP = 'com.app' -device = frida.get_usb_device() -pid = device.spawn([APP]) -session = device.attach(pid) -script = session.create_script(jscode) -print("[*] Intercepting [{}]".format(pid)) -script.on('message', on_message) -script.load() -device.resume(APP) -sys.stdin.read() diff --git a/dump_dynamically_created_files.py b/dump_dynamically_created_files.py deleted file mode 100644 index a4c1ded..0000000 --- a/dump_dynamically_created_files.py +++ /dev/null @@ -1,119 +0,0 @@ -import os -import sys -import frida - -APP_NAME = 'com.package.name' - - -def on_message(message, _ignored_data): - if message['type'] == 'send': - if type(message['payload']) == dict: - os.makedirs(os.path.dirname('./dump/{}/'.format(APP_NAME)), exist_ok=True) # create sub folder if not exist - with open('./dump/{}/{}'.format(APP_NAME, message['payload']['file']), 'w') as d: - for element in message['payload']['content']: - d.write(chr(element % 256)) - d.close() - print('[*] Successfully dumped to {0}'.format(message['payload']['file'])) - else: - print('[*] {0}'.format(message['payload'].encode('utf-8'))) - else: - print(message) - - -js_code = """ - Java.perform(function() { - var openedfile = ""; - var data = { - "file": "", - "content": [] - }; - var isOpen = false; - var index = 0; - - var fos = Java.use('java.io.FileOutputStream'); - - var fos_construct_2 = fos.$init.overload('java.lang.String'); - var fos_construct_3 = fos.$init.overload('java.io.File'); - var fos_construct_4 = fos.$init.overload('java.lang.String', 'boolean'); - var fos_construct_5 = fos.$init.overload('java.io.File', 'boolean'); - - var fos_write_1 = fos.write.overload('[B', 'int', 'int'); - - var fos_close = fos.close; - - function dump(data) { - send("Got " + data["content"].length + " bytes!"); - var tmp_name = openedfile.split("/"); - tmp_name = tmp_name[tmp_name.length - 1]; - data["file"] = tmp_name; - send(data); - data["content"] = []; - index = 0; - } - - fos_construct_2.implementation = function(file) { - var filename = file; - if (openedfile != filename) { - openedfile = filename; - send("File opened for write " + filename); - isOpen = true; - } - return fos_construct_2.call(this, file); - } - - fos_construct_3.implementation = function(file) { - var filename = file.getAbsolutePath(); - if (openedfile != filename) { - openedfile = filename; - send("File opened for write " + filename); - isOpen = true; - } - return fos_construct_3.call(this, file); - } - - fos_construct_4.implementation = function(file, true_false) { - var filename = file; - if (openedfile != filename) { - openedfile = filename; - send("File opened for write " + filename); - isOpen = true; - } - return fos_construct_4.call(this, file, true_false); - } - - fos_construct_5.implementation = function(file, true_false) { - var filename = file.getAbsolutePath(); - if (openedfile != filename) { - openedfile = filename; - send("File opened for write " + filename); - isOpen = true; - } - return fos_construct_5.call(this, file, true_false); - } - - fos_write_1.implementation = function(arr, offset, length) { - var i = 0; - for (i = offset; i < length; i = i + 1) { - data["content"][index] = arr[i]; - index = index + 1; - } - return fos_write_1.call(this, arr, offset, length); - } - - fos_close.implementation = function() { - dump(data); - return fos_close.call(this); - } - - }); -""" - -device = frida.get_usb_device() -pid = device.spawn([APP_NAME]) -session = device.attach(pid) -script = session.create_script(js_code) -print("[*] Intercepting [{}]".format(pid)) -script.on('message', on_message) -script.load() -device.resume(APP_NAME) -sys.stdin.read() diff --git a/exec_shell_cmd.py b/exec_shell_cmd.py deleted file mode 100644 index 6fc1e31..0000000 --- a/exec_shell_cmd.py +++ /dev/null @@ -1,75 +0,0 @@ -""" -Execute shell command -For example, list directory contents: -def ls(folder): - cmd = Shell(['/bin/sh', '-c', 'ls -la ' + folder], None) - cmd.exec() - for chunk in cmd.output: - print(chunk.strip().decode()) -""" -import frida -from frida.application import Reactor -import threading -import click - - -class Shell(object): - def __init__(self, argv, env): - self._stop_requested = threading.Event() - self._reactor = Reactor(run_until_return=lambda reactor: self._stop_requested.wait()) - - self._device = frida.get_usb_device() - self._sessions = set() - - self._device.on("child-added", lambda child: self._reactor.schedule(lambda: self._on_child_added(child))) - self._device.on("child-removed", lambda child: self._reactor.schedule(lambda: self._on_child_removed(child))) - self._device.on("output", lambda pid, fd, data: self._reactor.schedule(lambda: self._on_output(pid, fd, data))) - - self.argv = argv - self.env = env - self.output = [] # stdout will pushed into array - - def exec(self): - self._reactor.schedule(lambda: self._start()) - self._reactor.run() - - def _start(self): - click.secho("✔ spawn(argv={})".format(self.argv), fg='green', dim=True) - pid = self._device.spawn(self.argv, env=self.env, stdio='pipe') - self._instrument(pid) - - def _stop_if_idle(self): - if len(self._sessions) == 0: - self._stop_requested.set() - - def _instrument(self, pid): - click.secho("✔ attach(pid={})".format(pid), fg='green', dim=True) - session = self._device.attach(pid) - session.on("detached", lambda reason: self._reactor.schedule(lambda: self._on_detached(pid, session, reason))) - click.secho("✔ enable_child_gating()", fg='green', dim=True) - session.enable_child_gating() - # print("✔ resume(pid={})".format(pid)) - self._device.resume(pid) - self._sessions.add(session) - - def _on_child_added(self, child): - click.secho("⚡ child_added: {}".format(child), fg='green', dim=True) - self._instrument(child.pid) - - @staticmethod - def _on_child_removed(child): - click.secho("⚡ child_removed: {}".format(child), fg='green', dim=True) - - def _on_output(self, pid, fd, data): - # if len(data) > 0: - # print("⚡ output: pid={}, fd={}, data={}".format(pid, fd, repr(data))) - self.output.append(data) - - def _on_detached(self, pid, session, reason): - click.secho("⚡ detached: pid={}, reason='{}'".format(pid, reason), fg='green', dim=True) - self._sessions.remove(session) - self._reactor.schedule(self._stop_if_idle, delay=0.5) - - @staticmethod - def _on_message(pid, message): - click.secho("⚡ message: pid={}, payload={}".format(pid, message), fg='green', dim=True) diff --git a/how_to_access_inner_class_static_field.md b/how_to_access_inner_class_static_field.md deleted file mode 100644 index c63e702..0000000 --- a/how_to_access_inner_class_static_field.md +++ /dev/null @@ -1,73 +0,0 @@ -### How to access inner class static field -``` -package tech.yusi.fridademo; - -public class Jingdong { - private int intResult; - - private final static class a { - final static Jingdong a = new Jingdong(); - } - - - public Jingdong() { - intResult = 0; - } - - public static Jingdong a() { - return a.a; - } - - public static int a(int arg0, int arg1) { - return arg0 + arg1; - } - - - public String a(String arg0, String arg1) { - return arg0 + arg1; - } -} -``` - -``` -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import frida,sys - -rdev = frida.get_remote_device() -session = rdev.attach("tech.yusi.fridademo") - -def on_message(message ,data): - if message['type'] == 'send': - print(message['payload']) - elif message['type'] == 'error': - print(message['stack']) - else: - print(message) - -jscode = """ -send(Java.available); -Java.perform(function () { - var JingdongA = Java.use("tech.yusi.fridademo.Jingdong$a"); - var Jingdong = JingdongA.a; - send(Jingdong.fieldType); - - var JingdongInstance = Jingdong.value; - var ret = JingdongInstance.a("G8", "4tar"); - send(ret); - -}); -""" - -script = session.create_script(jscode) -script.on("message" , on_message) -script.load() - -try: - sys.stdin.read() -except KeyboardInterrupt as e: - session.detach() - sys.exit(0) -``` - diff --git a/install_frida_server.sh b/install_frida_server.sh deleted file mode 100644 index dfa506a..0000000 --- a/install_frida_server.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash -# Download latest frida-server, extract, push & run on android device/emulator -# adb 1.0.32, jq 1.5, xz 5.1, wget 1.17.1 -# sudo apt install wget jq xz - -# PARCH = phone architecture -# if oneliner [[ == "armeabi-v7a" ]] is a dirty fix because frida's release for armeabi-v7a is just "arm" - -# TODO fix adb root which does not work on phones, only emulators, use `adb shell su` instead - -PARCH=`adb shell getprop ro.product.cpu.abi`;\ -[[ "${PARCH}" == "armeabi-v7a" ]] && PARCH="arm";\ -wget -q -O - https://api.github.com/repos/frida/frida/releases \ -| jq '.[0] | .assets[] | select(.browser_download_url | match("server(.*?)android-'${PARCH}'*\\.xz")).browser_download_url' \ -| xargs wget -q --show-progress $1 \ -&& unxz frida-server* \ -&& adb root \ -&& adb push frida-server* /data/local/tmp/frida-server \ -&& adb shell "chmod 755 /data/local/tmp/frida-server" \ -&& adb shell "/data/local/tmp/frida-server &" diff --git a/log_string_builders_and_string_compare.js b/log_string_builders_and_string_compare.js deleted file mode 100644 index 18079db..0000000 --- a/log_string_builders_and_string_compare.js +++ /dev/null @@ -1,23 +0,0 @@ -Java.perform(function() { - // string compare - var str = Java.use('java.lang.String'), objectClass = 'java.lang.Object'; - str.equals.overload(objectClass).implementation = function(obj) { - var response = str.equals.overload(objectClass).call(this, obj); - if (obj) { - if (obj.toString().length > 5) { - send(str.toString.call(this) + ' == ' + obj.toString() + ' ? ' + response); - } - } - return response; - } - // log AbstractStringBuilder.toString() - ['java.lang.StringBuilder', 'java.lang.StringBuffer'].forEach(function(clazz, i) { - console.log('[?] ' + i + ' = ' + clazz); - var func = 'toString'; - Java.use(clazz)[func].implementation = function() { - var ret = this[func](); - send('[' + i + '] ' + ret); - return ret; - }; - }); -}); diff --git a/objc_ssl_unppining_helper.js b/objc_ssl_unppining_helper.js deleted file mode 100644 index 0f2641c..0000000 --- a/objc_ssl_unppining_helper.js +++ /dev/null @@ -1,105 +0,0 @@ -/* -* By http://github.com/LotemBY * - -This is a frida script for unpinning and reversing of ObjC applications. -Intercept method's which match regex. - -You may change the following regex arrays to match your needs: -*/ - -// The list of regexs for the moudle name -var moduleKeyWords = [/.*/]; // (It is not recommended to search all the moudles) - -// The list of regexs for the method name -var methodKeyWords = [/cert/i, /trust/i, /ssl/i, /verify/i, /509/]; - -// The list of regexs for the method to override their return value with "1" -var overrideKeyWords = []; - -/* -To run this script with frida on iPhone, follow these steps: - 1. Make sure the iPhone is jailbreaked - 2. Download the frida server from Cydia (package: re.frida.server) - 3. Connect the iPhone to your computer with USB and open the application - 4. Type in console "frida-ps -U" to get the list of running proccess on the iPhone, and find the proccess name of your app - 5. Type in console "frida -U -l " to run this script - 6. Now you should use the app to trigger some of the intercepted methods -*/ -var onCompleteCallback = function (retval) {}; -setImmediate(function () { - if (!ObjC.available) { - console.log("[-] Objective-C Runtime is not available!"); - return; - } - - console.log("=======================================================\n"); - console.log("[*] Searching methods..."); - - var moduleUsed = false; - - Process.enumerateModules({ - onMatch: function(module) { - - if (!matchesRegex(moduleKeyWords, module.name)) { - return; - } - - moduleUsed = false; - Module.enumerateSymbols(module.name, { - onMatch: function(exp) { - if (matchesRegex(methodKeyWords, exp.name)) { - if (!moduleUsed) { - console.log("[*] In module \"" + module.name + "\""); - moduleUsed = true; - } - console.log("\t[*] Matching method: \"" + exp.name + "\", Address: " + Module.findExportByName(module.name, exp.name)); - - if (intercept(module.name, exp.name)) { - console.log("\t\t[+] Now intercepting " + exp.name); - } else { - console.log("\t\t[-] Could not intercept " + exp.name); - } - } - }, - onComplete: onCompleteCallback - }); - }, - onComplete: onCompleteCallback - }); - - console.log("[*] Completed!"); - console.log("=======================================================\n\n"); -}); - -// Return if 'str' match any of the regexs in the array 'regexList' -function matchesRegex(regexList, str) { - regexList.forEach(function(el) { - if (str.search(el) != -1) - return true; - }); - return false; -} - -// Try to intercept a method by moudle name and function name. -// Return 'true' on success and 'false' on failor. -function intercept(module, func) { - try { - Interceptor.attach(Module.findExportByName(module, func), { - onEnter: function(args) { - console.log("[*] Method CALL:\t\"" + func + "\" called!"); - }, - onLeave: function (retval) { - console.log("[*] Method RETURN:\t\"" + func + "\" (return value: " + retval + ")"); - - if (matchesRegex(overrideKeyWords, func)) { - console.log("[!] CHANGED RETURN VALUE of method:\t\"" + func + "\" (new value: " + 1 + ")"); - retval.replace(1); - } - } - }); - - return true; - } catch (err) { - return false; - } -} diff --git a/print_native_method_arguments.py b/print_native_method_arguments.py deleted file mode 100644 index 363254a..0000000 --- a/print_native_method_arguments.py +++ /dev/null @@ -1,164 +0,0 @@ -def on_message(m, _data): - if m['type'] == 'send': - print(m['payload']) - elif m['type'] == 'error': - print(m) - - -def switch(argument_key, idx): - """ - c/c++ variable type to javascript reader switch implementation - # TODO handle other arguments, [long, longlong..] - :param argument_key: variable type - :param idx: index in symbols array - :return: javascript to read the type of variable - """ - argument_key = argument_key.replace(' ', '') - return '%d: %s' % (idx, { - 'int': 'args[%d].toInt32(),', - 'unsignedint': 'args[%d].toInt32(),', - 'std::string': 'Memory.readUtf8String(Memory.readPointer(args[%d])),', - 'bool': 'Boolean(args[%d]),' - }[argument_key] % idx) - - -def list_symbols_from_object_files(module_id): - import subprocess - return subprocess.getoutput('nm --demangle --dynamic %s' % module_id) - - -def parse_nm_output(nm_stdout, symbols): - for line in nm_stdout.splitlines(): - split = line.split() - open_parenthesis_idx = line.find('(') - raw_arguments = [] if open_parenthesis_idx == -1 else line[open_parenthesis_idx + 1:-1] - if len(raw_arguments) > 0: # ignore methods without arguments - raw_argument_list = raw_arguments.split(',') - symbols.append({ - 'address': split[0], - 'type': split[1], # @see Symbol Type Table - 'name': split[2][:split[2].find('(')], # method name - 'args': raw_argument_list - }) - - -def get_js_script(method, module_id): - js_script = """ - var moduleName = "{{moduleName}}", nativeFuncAddr = {{methodAddress}}; - Interceptor.attach(Module.findExportByName(null, "dlopen"), { - onEnter: function(args) { - this.lib = Memory.readUtf8String(args[0]); - console.log("[*] dlopen called with: " + this.lib); - }, - onLeave: function(retval) { - if (this.lib.endsWith(moduleName)) { - Interceptor.attach(Module.findBaseAddress(moduleName).add(nativeFuncAddr), { - onEnter: function(args) { - console.log("[*] hook invoked", JSON.stringify({{arguments}}, null, '\t')); - } - }); - } - } - }); - """ - replace_map = { - '{{moduleName}}': module_id, - '{{methodAddress}}': '0x' + method['address'], - '{{arguments}}': '{' + ''.join([switch(method['args'][i], i + 1) for i in range(len(method['args']))]) + '}' - } - for k, v in replace_map.items(): - js_script = js_script.replace(k, v) - print('[+] JS Script:\n', js_script) - return js_script - - -def main(app_id, module_id, method): - """ - $ python3.x+ script.py --method SomeClass::someMethod --app com.company.app --module libfoo.so - :param app_id: application identifier / bundle id - :param module_id: shared object identifier / known suffix, will iterate loaded modules (@see dlopen) - :param method: method/symbol name - :return: hook native method and print arguments when invoked - """ - # TODO extract all app's modules via `adb shell -c 'ls -lR /data/app/' + app_if + '*' | grep "\.so"` - - nm_stdout = list_symbols_from_object_files(module_id) - - symbols = [] - parse_nm_output(nm_stdout, symbols) - - selection_idx = None - for idx, symbol in enumerate(symbols): - if method is None: # if --method flag is not passed - print("%4d) %s (%d)" % (idx, symbol['name'], len(symbol['args']))) - elif method == symbol['name']: - selection_idx = idx - break - if selection_idx is None: - if method is None: - selection_idx = input("Enter symbol number: ") - else: - print('[+] Method not found, remove method flag to get list of methods to select from, `nm` stdout:') - print(nm_stdout) - exit(2) - - method = symbols[int(selection_idx)] - print('[+] Selected method: %s' % method['name']) - print('[+] Method arguments: %s' % method['args']) - - from frida import get_usb_device - device = get_usb_device() - pid = device.spawn([app_id]) - session = device.attach(pid) - script = session.create_script(get_js_script(method, module_id)) - script.on('message', on_message) - script.load() - device.resume(app_id) - # keep hook alive - from sys import stdin - stdin.read() - - -if __name__ == '__main__': - from argparse import ArgumentParser - parser = ArgumentParser() - parser.add_argument('--app', help='app identifier "com.company.app"') - parser.add_argument('--module', help='loaded module name "libfoo.2.so"') - parser.add_argument('--method', help='method name "SomeClass::someMethod", if empty it will print select-list') - args = parser.parse_args() - main(args.app, args.module, args.method) - - -""" -Symbol Type Table: - "A" The symbol's value is absolute, and will not be changed by further linking. - "B" The symbol is in the uninitialized data section (known as BSS). - "C" The symbol is common. Common symbols are uninitialized data. - When linking, multiple common symbols may appear with the same name. - If the symbol is defined anywhere, the common symbols are treated as undefined references. - "D" The symbol is in the initialized data section. - "G" The symbol is in an initialized data section for small objects. - Some object file formats permit more efficient access to small data objects, such as a global int variable as - opposed to a large global array. - "I" The symbol is an indirect reference to another symbol. - This is a GNU extension to the a.out object file format which is rarely used. - "N" The symbol is a debugging symbol. - "R" The symbol is in a read only data section. - "S" The symbol is in an uninitialized data section for small objects. - "T" The symbol is in the text (code) section. - "U" The symbol is undefined. - "V" The symbol is a weak object. When a weak defined symbol is linked with a normal defined symbol, - the normal defined symbol is used with no error. - When a weak undefined symbol is linked and the symbol is not defined, the value of the weak symbol becomes - zero with no error. - "W" The symbol is a weak symbol that has not been specifically tagged as a weak object symbol. - When a weak defined symbol is linked with a normal defined symbol, - the normal defined symbol is used with no error. - When a weak undefined symbol is linked and the symbol is not defined, the value of the symbol is determined - in a system-specific manner without error. - On some systems, uppercase indicates that a default value has been specified. - "-" The symbol is a stabs symbol in an a.out object file. - In this case, the next values printed are the stabs other field, the stabs desc field, and the stab type. - Stabs symbols are used to hold debugging information. - "?" The symbol type is unknown, or object file format specific. -"""