scripts directory

This commit is contained in:
iddoeldor 2018-06-08 16:56:22 +03:00
parent b7ad44fc54
commit 05e330fcae
9 changed files with 1 additions and 663 deletions

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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)

View File

@ -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)
```

View File

@ -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 &"

View File

@ -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;
};
});
});

View File

@ -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 <APP PROCCESS NAME> -l <PATH TO THIS SCRIPT>" 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;
}
}

View File

@ -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.
"""