
Handgefertigte Frida-Beispiele
& auch Ausgabebeispiele

{
"scope": "source.js",
"completions": [
{"trigger": "fridainterceptor", "contents": "Interceptor.attach(\n ptr,\n {\n onEnter:function(args) {\n\n },\n onLeave: function(retval) {\n\n }\n }\n)"},
{"trigger": "fridaperform", "contents": "function main(){\n console.log('main()');\n}\n\nconsole.log('script loaded');\nJava.perform(main);"},
{"trigger": "fridause", "contents": "var kls = Java.use('kls');"},
{"trigger": "fridahex", "contents": "hexdump(\n ptr,\n {\n offset: 0,\n length: ptr_size\n }\n);" },
{"trigger": "fridabacktrace", "contents": "console.log('called from:\\n' +\n Thread.backtrace(this.context, Backtracer.ACCURATE)\n .map(DebugSymbol.fromAddress).join('\\n') + '\\n'\n);"},
{"trigger": "fridamods", "contents": "var mods = Process.enumerateModules().filter(function(mod){\n return mod.name.includes(\"<name>\");\n});"},
{"trigger": "fridaexport", "contents": "Module.findExportByName(null, \"<export_name>\");"},
{"trigger": "fridabase", "contents": "Module.findBaseAddress(name);"},
{"trigger": "fridaoverload", "contents": "kls.method_name.overload().implementation=function(){}"}
]
}
Abkürzungen auflisten :ab
Erweitern durch Eingabe von key und <Space>
</details>
<details>
<summary>JEB</summary>
Java-Methoden-Hook-Generator mittels Tastenkombination
1. `curl -o ~/$JEB$/scripts/FridaCodeGenerator.py https://raw.githubusercontent.com/iddoeldor/frida-snippets/master/scripts/FridaCodeGenerator.py`
2. Platziere den Cursor auf der Signatur der Java-Methode
3. Drücke `Strg+Umschalt+Z`
4. Der Code wird in die System-Zwischenablage kopiert (mittels `xclip`)
</details>
<hr />
#### SSL-Schlüssel abrufen```js
var keylog_callback = new NativeCallback((ssl, line) => {
send(Memory.readCString(line));
}, 'void', ['pointer', 'pointer']);
if (ObjC.available) {
var CALLBACK_OFFSET = 0x2A8
if (Memory.readDouble(Module.findExportByName('CoreFoundation', 'kCFCoreFoundationVersionNumber')) >= 1751.108) {
CALLBACK_OFFSET = 0x2B8
}
Interceptor.attach(Module.findExportByName('libboringssl.dylib', 'SSL_CTX_set_info_callback'), {
onEnter(args) {
ptr(args[0]).add(CALLBACK_OFFSET).writePointer(keylog_callback)
}
})
} else if (Java.available) {
var set_keylog_callback = new NativeFunction(Module.findExportByName('libssl.so', 'SSL_CTX_set_keylog_callback'), 'void', ['pointer', 'pointer']);
Interceptor.attach(Module.findExportByName('libssl.so', 'SSL_CTX_new'), {
onLeave(retval) {
set_keylog_callback(retval, keylog_callback)
}
})
}
Process .getModuleByName({ linux: 'libc.so', darwin: 'libSystem.B.dylib', windows: 'ws2_32.dll' }[Process.platform]) .enumerateExports().filter(ex => ex.type === 'function' && ['connect', 'recv', 'send', 'read', 'write'].some(prefix => ex.name.indexOf(prefix) === 0)) .forEach(ex => { Interceptor.attach(ex.address, { onEnter: function (args) { var fd = args[0].toInt32(); var socktype = Socket.type(fd); if (socktype !== 'tcp' && socktype !== 'tcp6') return; var address = Socket.peerAddress(fd); if (address === null) return; console.log(fd, ex.name, address.ip + ':' + address.port); } }) })
<details>
<summary>Ausgabebeispiel</summary>
Android-Beispiel```sh
# wrap the script above inside Java.perform
$ frida -Uf com.example.app -l script.js --no-pause
[Android Model-X::com.example.app]-> 117 write 5.0.2.1:5242
117 read 5.0.2.1:5242
135 write 5.0.2.1:4244
135 read 5.0.2.1:4244
135 read 5.0.2.1:4244
Ein Beispiel zum Abfangen von libc#open und Protokollieren des Backtraces, wenn eine bestimmte Datei geöffnet wurde.```js
Interceptor.attach(Module.findExportByName("/system/lib/libc.so", "open"), {
onEnter: function(args) {
this.flag = false;
var filename = Memory.readCString(ptr(args[0]));
console.log('filename =', filename)
if (filename.endsWith(".xml")) {
this.flag = true;
var backtrace = Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n\t");
console.log("file name [ " + Memory.readCString(ptr(args[0])) + " ]\nBacktrace:" + backtrace);
}
},
onLeave: function(retval) {
if (this.flag) // passed from onEnter
console.warn("\nretval: " + retval);
}
});
Standardmäßig werden die Dateien im `bucket` im Verzeichnis `UserData/` gespeichert, das ebenfalls erstellt wird, falls es nicht existiert.
```console
main aws <profile> ls --all
main aws <profile> ls --users
main aws <profile> ls --roles
main aws <profile> ls --user-data # <-- iam:GetUser, iam:ListUserPolicies, iam:ListUserTags, etc.
main aws <profile> ls --role-data # <-- iam:ListRoles, iam:ListRoleTags, etc
main aws <profile> ls --enum-services
main aws <profile> ls --groups
main aws <profile> ls --policies
``````js
var fds = {}; // for f in /proc/`pidof $APP`/fd/*; do echo $f': 'readlink $f; done
Interceptor.attach(Module.findExportByName(null, 'open'), {
onEnter: function (args) {
var fname = args[0].readCString();
if (fname.endsWith('.jar')) {
this.flag = true;
this.fname = fname;
}
},
onLeave: function (retval) {
if (this.flag) {
fds[retval] = this.fname;
}
}
});
['read', 'pread', 'readv'].forEach(fnc => {
Interceptor.attach(Module.findExportByName(null, fnc), {
onEnter: function (args) {
var fd = args[0];
if (fd in fds)
console.log(`${fnc}: ${fds[fd]}
\t${Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n\t')}`);
}
});
});

import frida from frida_tools.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):
# print("⚡ output: pid={}, fd={}, data={}".format(pid, fd, repr(data)))
# fd=0 (input) fd=1(stdout) fd=2(stderr)
if fd != 2:
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)
<details>
<summary>Anwendungsbeispiel</summary>
List directory contents:```python
def ls(folder):
cmd = Shell(['/bin/sh', '-c', 'ls -la ' + folder], None)
cmd.exec()
for chunk in cmd.output:
print(chunk.strip().decode())
Binärdatei von iOS ziehen```python cmd = Shell(['/bin/sh', '-c', 'cat /System/Library/PrivateFrameworks/Example.framework/example'], None) cmd.exec() with open('/tmp/example', 'wb+') as f: f.writelines(cmd.output)
</details>
<br>[⬆ Zurück zum Anfang](#table-of-contents)
#### Module auflisten```js
Process.enumerateModulesSync()
.filter(function(m){ return m['path'].toLowerCase().indexOf('app') !=-1 ; })
.forEach(function(m) {
console.log(JSON.stringify(m, null, ' '));
// to list exports use Module.enumerateExportsSync(m.name)
});
Module & Exporte auflisten```js sudo frida Process --no-pause --eval 'var x={};Process.enumerateModulesSync().forEach(function(m){x[m.name] = Module.enumerateExportsSync(m.name)});x' -q | less +F
<details>
<summary>Ausgabebeispiel</summary>```js
{
"name": "app_process64",
"base": "0x6313a1c000",
"size": 40960,
"path": "/system/bin/app_process64"
}
{
"name": "libappfuse.so",
"base": "0x749ab96000",
"size": 53248,
"path": "/system/lib64/libappfuse.so"
}
{
"name": "[email protected]",
"base": "0x749b448000",
"size": 90112,
"path": "/system/lib64/[email protected]"
}
{
"name": "[email protected]",
"base": "0x749ac9e000",
"size": 94208,
"path": "/system/lib64/[email protected]"
}
{
"name": "[email protected]",
"base": "0x74981e0000",
"size": 98304,
"path": "/system/lib64/[email protected]"
}
{
"name": "[email protected]",
"base": "0x73fb4cc000",
"size": 40960,
"path": "/vendor/lib64/hw/[email protected]"
}
{
"name": "[email protected]",
"base": "0x73fb51f000",
"size": 90112,
"path": "/system/lib64/vndk-sp-29/[email protected]"
}
{
"name": "[email protected]",
"base": "0x73fb542000",
"size": 94208,
"path": "/system/lib64/vndk-sp-29/[email protected]"
}
{
"name": "base.odex",
"base": "0x73ab4cd000",
"size": 16965632,
"path": "/data/app/com.noodlecake.altosadventure-O2YLuwCOq7LbWSkRHkRLcg==/oat/arm64/base.odex"
}
{
"name": "libfrida-gadget.so",
"base": "0x73a2c05000",
"size": 22876160,
"path": "/data/app/com.noodlecake.altosadventure-O2YLuwCOq7LbWSkRHkRLcg==/lib/arm64/libfrida-gadget.so"
}
{
"name": "libmain.so",
"base": "0x73fb894000",
"size": 73728,
"path": "/data/app/com.noodlecake.altosadventure-O2YLuwCOq7LbWSkRHkRLcg==/lib/arm64/libmain.so"
}
{
"name": "libunity.so",
"base": "0x739bf88000",
"size": 24461312,
"path": "/data/app/com.noodlecake.altosadventure-O2YLuwCOq7LbWSkRHkRLcg==/lib/arm64/libunity.so"
}
{
"name": "libil2cpp.so",
"base": "0x7396fe6000",
"size": 25272320,
"path": "/data/app/com.noodlecake.altosadventure-O2YLuwCOq7LbWSkRHkRLcg==/lib/arm64/libil2cpp.so"
}
{
"name": "DynamiteLoader.odex",
"base": "0x73b7ee4000",
"size": 376832,
"path": "/data/user_de/0/com.google.android.gms/app_chimera/m/00000278/oat/arm64/DynamiteLoader.odex"
}
{
"name": "base.odex",
"base": "0x72f2ee3000",
"size": 166838272,
"path": "/data/app/com.google.android.gms-j7RpxBsNAd3ttAYEdp2ahg==/oat/arm64/base.odex"
}
{
"name": "base.odex",
"base": "0x7396e6d000",
"size": 28672,
"path": "/data/app/com.google.android.trichromelibrary_432418133-X7Kc2Mqi-VXkY12N59kGug==/oat/arm64/base.odex"
}
{
"name": "base.odex",
"base": "0x724f15e000",
"size": 13225984,
"path": "/data/app/com.google.android.webview-w6i6OBFZ7T_wK4W4TpDAiQ==/oat/arm64/base.odex"
}
{
"name": "libmonochrome.so",
"base": "0x73b8592000",
"size": 76673024,
"path": "/data/app/com.google.android.webview-w6i6OBFZ7T_wK4W4TpDAiQ==/base.apk!/lib/arm64-v8a/libmonochrome.so"
}
{
"name": "libnativeNoodleNews.so",
"base": "0x723add3000",
"size": 962560,
"path": "/data/app/com.noodlecake.altosadventure-O2YLuwCOq7LbWSkRHkRLcg==/lib/arm64/libnativeNoodleNews.so"
}
{
"name": "libconscrypt_gmscore_jni.so",
"base": "0x7206629000",
"size": 1130496,
"path": "/data/app/com.google.android.gms-j7RpxBsNAd3ttAYEdp2ahg==/base.apk!/lib/arm64-v8a/libconscrypt_gmscore_jni.so"
}
Interceptor.attach(Module.findExportByName('libsqlite.so', 'sqlite3_prepare16_v2'), { onEnter: function(args) { console.log('DB: ' + Memory.readUtf16String(args[0]) + '\tSQL: ' + Memory.readUtf16String(args[1])); } });
<details>
<summary>Ausgabebeispiel</summary>
Noch zu erledigen
</details>
<br>[⬆ Zurück nach oben](#table-of-contents)
#### Systemeigenschaft abrufen```js
Interceptor.attach(Module.findExportByName(null, '__system_property_get'), {
onEnter: function (args) {
this._name = args[0].readCString();
this._value = args[1];
},
onLeave: function (retval) {
console.log(JSON.stringify({
result_length: retval,
name: this._name,
val: this._value.readCString()
}));
}
});
var LAST_MSG = ''; Java.perform(() => { Interceptor.attach(Module.findExportByName('libbinder.so', 'ioctl'), { onEnter: function(args) { var binder_write_read_ptr = args[2]; if (args[1] == 0xC0306201) { // BINDER_WRITE_READ var binder_write_read = { // 'fd': args[0].toInt32(), 'write_size': binder_write_read_ptr.readU64(), 'write_consumed': binder_write_read_ptr.add(Process.pointerSize).readU64(), 'write_buffer': binder_write_read_ptr.add(Process.pointerSize * 2).readPointer(), } if (binder_write_read.write_size > 0) { var ptr = binder_write_read.write_buffer.add(binder_write_read.write_consumed + 4); switch (binder_write_read.write_buffer.readU32() & 0xff) { case 0: // BC_TRANSACTION case 1: // BC_REPLY var binder_transaction_data = { 'target': { 'handle': ptr.readU32(), 'ptr': ptr.readPointer() }, 'cookie': ptr.add(8).readPointer(), 'code': ptr.add(16).readU32(), 'flags': ptr.add(20).readU32(), 'sender_pid': ptr.add(24).readS32(), 'sender_euid': ptr.add(28).readU32(), 'data_size': ptr.add(32).readU64(), 'offsets_size': ptr.add(40).readU64(), 'data': { 'ptr': { 'buffer': ptr.add(48).readPointer(), 'offsets': ptr.add(56).readPointer() }, 'buf': ptr.add(48).readByteArray(8) } } var _log = hexdump(binder_transaction_data.data.ptr.buffer, { length: binder_transaction_data.data_size, ansi: true }); if (LAST_MSG.toString() != _log.toString()) { console.log(JSON.stringify(binder_transaction_data, null, 2)); console.log(_log); } break; } } } } }); });
<details>
<summary>Ausgabebeispiel</summary>```sh
{
"target": {
"handle": 16,
"ptr": "0x10"
},
"cookie": "0x0",
"code": 22,
"flags": 16,
"sender_pid": 0,
"sender_euid": 0,
"data_size": "68",
"offsets_size": "0",
"data": {
"ptr": {
"buffer": "0x78dce3dcf0",
"offsets": "0x0"
},
"buf": {}
}
}
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
78dce3dcf0 04 00 40 01 1d 00 00 00 61 00 6e 00 64 00 72 00 [email protected].
78dce3dd00 6f 00 69 00 64 00 2e 00 6e 00 65 00 74 00 2e 00 o.i.d...n.e.t...
78dce3dd10 77 00 69 00 66 00 69 00 2e 00 49 00 57 00 69 00 w.i.f.i...I.W.i.
78dce3dd20 66 00 69 00 4d 00 61 00 6e 00 61 00 67 00 65 00 f.i.M.a.n.a.g.e.
78dce3dd30 72 00 00 00 r...
registerNativeMethods kann als Anti-Reversing-Technik für native .so-Bibliotheken verwendet werden, z.B. um die Symbole so weit wie möglich zu verstecken, die exportierten Symbole zu verschleiern und schließlich einen gewissen Schutz über die JNI-Brücke hinzuzufügen.
source```js
var RevealNativeMethods = function() {
var pSize = Process.pointerSize;
var env = Java.vm.getEnv();
var RegisterNatives = 215, FindClassIndex = 6; // search "215" @ https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html
var jclassAddress2NameMap = {};
function getNativeAddress(idx) {
return env.handle.readPointer().add(idx * pSize).readPointer();
}
// intercepting FindClass to populate Map<address, jclass>
Interceptor.attach(getNativeAddress(FindClassIndex), {
onEnter: function(args) {
jclassAddress2NameMap[args[0]] = args[1].readCString();
}
});
// RegisterNative(jClass*, .., JNINativeMethod methods[nMethods], uint nMethods) // https://android.googlesource.com/platform/libnativehelper/+/master/include_jni/jni.h#977
Interceptor.attach(getNativeAddress(RegisterNatives), {
onEnter: function(args) {
for (var i = 0, nMethods = parseInt(args[3]); i < nMethods; i++) {
/
https://android.googlesource.com/platform/libnativehelper/+/master/include_jni/jni.h#129
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
/
var structSize = pSize * 3; // = sizeof(JNINativeMethod)
var methodsPtr = ptr(args[2]);
var signature = methodsPtr.add(i * structSize + pSize).readPointer();
var fnPtr = methodsPtr.add(i * structSize + (pSize * 2)).readPointer(); // void fnPtr
var jClass = jclassAddress2NameMap[args[0]].split('/');
var methodName = methodsPtr.add(i * structSize).readPointer().readCString();
console.log('\x1b[3' + '6;01' + 'm', JSON.stringify({
module: DebugSymbol.fromAddress(fnPtr)['moduleName'], // https://www.frida.re/docs/javascript-api/#debugsymbol
package: jClass.slice(0, -1).join('.'),
class: jClass[jClass.length - 1],
method: methodName, // methodsPtr.readPointer().readCString(), // char* name
signature: signature.readCString(), // char* signature TODO Java bytecode signature parser { Z: 'boolean', B: 'byte', C: 'char', S: 'short', I: 'int', J: 'long', F: 'float', D: 'double', L: 'fully-qualified-class;', '[': 'array' }
address: fnPtr
}), '\x1b[39;49;00m');
}
}
});
}
Java.perform(RevealNativeMethods);
@AlteVersion```js
var fIntercepted = false;
function revealNativeMethods() {
if (fIntercepted === true) {
return;
}
var jclassAddress2NameMap = {};
var androidRunTimeSharedLibrary = "libart.so"; // may change between devices
Module.enumerateSymbolsSync(androidRunTimeSharedLibrary).forEach(function(symbol){
switch (symbol.name) {
case "_ZN3art3JNI21RegisterNativeMethodsEP7_JNIEnvP7_jclassPK15JNINativeMethodib":
/*
$ c++filt "_ZN3art3JNI21RegisterNativeMethodsEP7_JNIEnvP7_jclassPK15JNINativeMethodib"
art::JNI::RegisterNativeMethods(_JNIEnv*, _jclass*, JNINativeMethod const*, int, bool)
*/
var RegisterNativeMethodsPtr = symbol.address;
console.log("RegisterNativeMethods is at " + RegisterNativeMethodsPtr);
Interceptor.attach(RegisterNativeMethodsPtr, {
onEnter: function(args) {
var methodsPtr = ptr(args[2]);
var methodCount = parseInt(args[3]);
for (var i = 0; i < methodCount; i++) {
var pSize = Process.pointerSize;
/*
https://android.googlesource.com/platform/libnativehelper/+/master/include_jni/jni.h#129
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
*/
var structSize = pSize * 3; // JNINativeMethod contains 3 pointers
var namePtr = Memory.readPointer(methodsPtr.add(i * structSize));
var sigPtr = Memory.readPointer(methodsPtr.add(i * structSize + pSize));
var fnPtrPtr = Memory.readPointer(methodsPtr.add(i * structSize + (pSize * 2)));
// output schema: className#methodName(arguments)returnVal@address
console.log(
// package & class, replacing forward slash with dot for convenience
jclassAddress2NameMap[args[0]].replace(/\//g, '.') +
'#' + Memory.readCString(namePtr) + // method
Memory.readCString(sigPtr) + // signature (arguments & return type)
'@' + fnPtrPtr // C side address
);
}
},
onLeave: function (ignoredReturnValue) {}
});
break;
case "_ZN3art3JNI9FindClassEP7_JNIEnvPKc": // art::JNI::FindClass
Interceptor.attach(symbol.address, {
onEnter: function(args) {
if (args[1] != null) {
jclassAddress2NameMap[args[0]] = Memory.readCString(args[1]);
}
},
onLeave: function (ignoredReturnValue) {}
});
break;
}
});
fIntercepted = true;
}
Java.perform(revealNativeMethods);
kitty/memes/cat/large/1000.png
kitty/memes/cat/huge/1000.png
kitty/memes/cat/Screenshots/1000.png
kitty/memes/dog/large/900.png
kitty/memes/dog/huge/900.png
kitty/memes/dog/Screenshots/900.png
``````sh
{"class":"org/chromium/net/GURLUtils","method":"nativeGetOrigin","signature":"(Ljava/lang/String;)Ljava/lang/String;","address":"0x..da910"}
..
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)
<details>
<summary>Tabelle der Symboltypen</summary>
<pre>
"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.
</pre>
</details>
<br>[⬆ Zurück zum Inhaltsverzeichnis](#table-of-contents)
#### Geladene Klassen auflisten
Und speichere in einer Datei namens `pkg.classes````bash
$ frida -U com.pkg -qe 'Java.perform(function(){Java.enumerateLoadedClasses({"onMatch":function(c){console.log(c);}});});' -o pkg.classes
Klassenmethoden und -mitglieder abrufen.```js Object.getOwnPropertyNames(Java.use('com.company.CustomClass').proto).join('\n\t')
Wenn es eine Namenskollision gibt, haben Methode & Member denselben Namen, wird ein Unterstrich zu Member hinzugefügt. [source](https://github.com/frida/frida-java/pull/21)```js
let fieldJsName = env.stringFromJni(fieldName);
while (jsMethods.hasOwnProperty(fieldJsName)) {
fieldJsName = '_' + fieldJsName;
}
Es wird WiFi ausschalten, sobald die erste Aktivität erstellt wird.```js var WifiManager = Java.use("android.net.wifi.WifiManager"); Java.use("android.app.Activity").onCreate.overload("android.os.Bundle").implementation = function(bundle) { var wManager = Java.cast(this.getSystemService("wifi"), WifiManager); console.log('isWifiEnabled ?', wManager.isWifiEnabled()); wManager.setWifiEnabled(false); this.$init(bundle); }
<details>
<summary>Ausgabebeispiel</summary>
TODO
</details>
<br>[⬆ Zurück zum Anfang](#table-of-contents)
#### Proxy festlegen
Es wird einen systemweiten Proxy mit der angegebenen IP-Adresse und dem angegebenen Port festlegen.```js
var ActivityThread = Java.use('android.app.ActivityThread');
var ConnectivityManager = Java.use('android.net.ConnectivityManager');
var ProxyInfo = Java.use('android.net.ProxyInfo');
var proxyInfo = ProxyInfo.$new('192.168.1.10', 8080, ''); // change to null in order to disable the proxy.
var context = ActivityThread.currentApplication().getApplicationContext();
var connectivityManager = Java.cast(context.getSystemService('connectivity'), ConnectivityManager);
connectivityManager.setGlobalProxy(proxyInfo);
Kann auch IMEI hooken & ändern.```js function getIMEI(){ console.log('IMEI =', Java.use("android.telephony.TelephonyManager").$new().getDeviceId()); } Java.perform(getIMEI)
<details>
<summary>Ausgabebeispiel</summary>
TODO
</details>
<br>[⬆ Zurück nach oben](#table-of-contents)
#### Hook io InputStream
Hooke `InputputStream` und gib den Puffer als `ascii` mit Zeichenbegrenzung und Ausschlussliste aus.```js
function binaryToHexToAscii(array, readLimit) {
var result = [];
// read 100 bytes #performance
readLimit = readLimit || 100;
for (var i = 0; i < readLimit; ++i) {
result.push(String.fromCharCode( // hex2ascii part
parseInt(
('0' + (array[i] & 0xFF).toString(16)).slice(-2), // binary2hex part
16
)
));
}
return result.join('');
}
function hookInputStream() {
Java.use('java.io.InputStream')['read'].overload('[B').implementation = function(b) {
// execute original and save return value
var retval = this.read(b);
var resp = binaryToHexToAscii(b);
// conditions to not print garbage packets
var reExcludeList = new RegExp(['Mmm'/*, 'Ping' /*, ' Yo'*/].join('|'));
if ( ! reExcludeList.test(resp) ) {
console.log(resp);
}
var reIncludeList = new RegExp(['AAA', 'BBB', 'CCC'].join('|'));
if ( reIncludeList.test(resp) ) {
send( binaryToHexToAscii(b, 1200) );
}
return retval;
};
}
Java.perform(hookInputStream);
// 0 = // https://developer.android.com/reference/android/widget/Toast#LENGTH_LONG Java.scheduleOnMainThread(() => { Java.use("android.widget.Toast") .makeText(Java.use("android.app.ActivityThread").currentApplication().getApplicationContext(), Java.use("java.lang.StringBuilder").$new("Text to Toast here"), 0).show(); });
<details>
<summary>Ausgabebeispiel</summary>
TODO
</details>
<br>[⬆ Zurück zum Inhaltsverzeichnis](#table-of-contents)
#### Auf Bedingung warten
Warten, bis eine bestimmte DLL in der Unity-App geladen wird, kann Hot-Swap implementieren.```javascript
var awaitForCondition = function(callback) {
var int = setInterval(function() {
if (Module.findExportByName(null, "mono_get_root_domain")) {
clearInterval(int);
callback();
return;
}
}, 0);
}
function hook() {
Interceptor.attach(Module.findExportByName(null, "mono_assembly_load_from_full"), {
onEnter: function(args) {
this._dll = Memory.readUtf8String(ptr(args[1]));
console.log('[*]', this._dll);
},
onLeave: function(retval) {
if (this._dll.endsWith("Assembly-CSharp.dll")) {
console.log(JSON.stringify({
retval: retval,
name: this._dll
}, null, 2));
}
}
});
}
Java.perform(awaitForCondition(hook));
Protokollieren, wenn WebView die URL wechselt.```js Java.use("android.webkit.WebView").loadUrl.overload("java.lang.String").implementation = function (s) { send(s.toString()); this.loadUrl.overload("java.lang.String").call(this, s); };
<details>
<summary>Ausgabebeispiel</summary>
TODO
</details>
<br>[⬆ Back to top](#table-of-contents)
#### Laufzeit-Strings ausgeben
Hooking von `toString` von StringBuilder/Buffer und Ausgabe des Stacktraces.```js
Java.perform(function() {
['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]();
if (ret.indexOf('') != -1) {
// print stacktrace if return value contains specific string
Java.perform(function() {
var jAndroidLog = Java.use("android.util.Log"), jException = Java.use("java.lang.Exception");
console.log( jAndroidLog.getStackTraceString( jException.$new() ) );
});
}
send('[' + i + '] ' + ret);
return ret;
}
});
});
Java.perform(function() { var shared_pref_class = Java.use('android.app.SharedPreferencesImpl$EditorImpl');
shared_pref_class.putString.overload('java.lang.String', 'java.lang.String').implementation = function(k, v) {
console.log('Shared preference updated: ', k, '=', v);
return this.putString(k, v);
}
shared_pref_class.putInt.overload('java.lang.String', 'int').implementation = function(k, v) {
console.log('Shared preference updated: ', k, '=', v);
return this.putInt(k, v);
}
shared_pref_class.putFloat.overload('java.lang.String', 'float').implementation = function(k, v) {
console.log('Shared preference updated: ', k, '=', v);
return this.putFloat(k, v);
}
shared_pref_class.putBoolean.overload('java.lang.String', 'boolean').implementation = function(k, v) {
console.log('Shared preference updated: ', k, '=', v);
return this.putBoolean(k, v);
}
shared_pref_class.putLong.overload('java.lang.String', 'long').implementation = function(k, v) {
console.log('Shared preference updated: ', k, '=', v);
return this.putLong(k, v);
}
shared_pref_class.putStringSet.overload('java.lang.String', java.util.Set).implementation = function(k, v) {
console.log('Shared preference updated: ', k, '=', v);
return this.putStringSet(k, v);
}
});
<details>
<summary>Ausgabebeispiel</summary>
TODO
</details>
<br>[⬆ Zurück zum Inhaltsverzeichnis](#table-of-contents)
#### String-Vergleich```js
Java.perform(function() {
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;
}
});
Hooked native Methode anhand von Modulname und Methodenadresse und gibt Argumente aus.```js var moduleName = "libfoo.so"; var nativeFuncAddr = 0x1234; // $ nm --demangle --dynamic libfoo.so | grep "Class::method("
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)) { console.log("ret: " + retval); var baseAddr = Module.findBaseAddress(moduleName); Interceptor.attach(baseAddr.add(nativeFuncAddr), { onEnter: function(args) { console.log("[-] hook invoked"); console.log(JSON.stringify({ a1: args[1].toInt32(), a2: Memory.readUtf8String(Memory.readPointer(args[2])), a3: Boolean(args[3]) }, null, '\t')); } }); } } });
<details>
<summary>Beispielausgabe</summary>
TODO
</details>
<br>[⬆ Zurück zum Inhaltsverzeichnis](#table-of-contents)
#### Hook-Konstruktor```js
Java.use('java.lang.StringBuilder').$init.overload('java.lang.String').implementation = function(stringArgument) {
console.log("c'tor");
return this.$init(stringArgument);
};
`java.lang.reflect.Method#invoke(Object obj, Object... args, boolean bool)````javascript Java.use('java.lang.reflect.Method').invoke.overload('java.lang.Object', '[Ljava.lang.Object;', 'boolean').implementation = function(a,b,c) { console.log('hooked!', a, b, c); return this.invoke(a,b,c); };
<details>
<summary>Ausgabebeispiel</summary>
TODO
</details>
<br>[⬆ Zurück nach oben](#table-of-contents)
#### Trace-Klasse
Trace-Klassenmethode, mit hübschen Farben und Optionen zum Drucken als JSON & Stacktrace.
TODO füge Trace für c'tor hinzu.```js
var Color = {
RESET: "\x1b[39;49;00m", Black: "0;01", Blue: "4;01", Cyan: "6;01", Gray: "7;11", Green: "2;01", Purple: "5;01", Red: "1;01", Yellow: "3;01",
Light: {
Black: "0;11", Blue: "4;11", Cyan: "6;11", Gray: "7;01", Green: "2;11", Purple: "5;11", Red: "1;11", Yellow: "3;11"
}
};
/**
*
* @param input.
* If an object is passed it will print as json
* @param kwargs options map {
* -l level: string; log/warn/error
* -i indent: boolean; print JSON prettify
* -c color: @see ColorMap
* }
*/
var LOG = function (input, kwargs) {
kwargs = kwargs || {};
var logLevel = kwargs['l'] || 'log', colorPrefix = '\x1b[3', colorSuffix = 'm';
if (typeof input === 'object')
input = JSON.stringify(input, null, kwargs['i'] ? 2 : null);
if (kwargs['c'])
input = colorPrefix + kwargs['c'] + colorSuffix + input + Color.RESET;
console[logLevel](https://github.com/iddoeldor/frida-snippets/blob/HEAD/input);
};
var printBacktrace = function () {
Java.perform(function() {
var android_util_Log = Java.use('android.util.Log'), java_lang_Exception = Java.use('java.lang.Exception');
// getting stacktrace by throwing an exception
LOG(android_util_Log.getStackTraceString(java_lang_Exception.$new()), { c: Color.Gray });
});
};
function traceClass(targetClass) {
var hook;
try {
hook = Java.use(targetClass);
} catch (e) {
console.error("trace class failed", e);
return;
}
var methods = hook.class.getDeclaredMethods();
hook.$dispose();
var parsedMethods = [];
methods.forEach(function (method) {
var methodStr = method.toString();
var methodReplace = methodStr.replace(targetClass + ".", "TOKEN").match(/\sTOKEN(.*)\(/)[1];
parsedMethods.push(methodReplace);
});
uniqBy(parsedMethods, JSON.stringify).forEach(function (targetMethod) {
traceMethod(targetClass + '.' + targetMethod);
});
}
function traceMethod(targetClassMethod) {
try {
var delim = targetClassMethod.lastIndexOf('.');
if (delim === -1)
return;
var targetClass = targetClassMethod.slice(0, delim);
var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length);
var hook = Java.use(targetClass);
var overloadCount = hook[targetMethod].overloads.length;
LOG({ tracing: targetClassMethod, overloaded: overloadCount }, { c: Color.Green });
for (var i = 0; i < overloadCount; i++) {
hook[targetMethod].overloads[i].implementation = function () {
var log = { '#': targetClassMethod, args: [] };
for (var j = 0; j < arguments.length; j++) {
var arg = arguments[j];
// quick&dirty fix for java.io.StringWriter char[].toString() impl because frida prints [object Object]
if (j === 0 && arguments[j]) {
if (arguments[j].toString() === '[object Object]') {
var s = [];
for (var k = 0, l = arguments[j].length; k < l; k++) {
s.push(arguments[j][k]);
}
arg = s.join('');
}
}
log.args.push({ i: j, o: arg, s: arg ? arg.toString(): 'null'});
}
var retval;
try {
retval = this[targetMethod].apply(this, arguments); // might crash (Frida bug?)
log.returns = { val: retval, str: retval ? retval.toString() : null };
} catch (e) {
console.error(e);
}
LOG(log, { c: Color.Blue });
return retval;
}
}
} catch(error) {
LOG({ tracing: targetClassMethod, "overloaded": 0}, { c: Color.Red });
}
}
// remove duplicates from array
function uniqBy(array, key) {
var seen = {};
return array.filter(function (item) {
var k = key(item);
return seen.hasOwnProperty(k) ? false : (seen[k] = true);
});
}
var Main = function() {
Java.perform(function () { // avoid java.lang.ClassNotFoundException
[
// "java.io.File",
'java.net.Socket'
].forEach(traceClass);
Java.use('java.net.Socket').isConnected.overload().implementation = function () {
LOG('Socket.isConnected.overload', { c: Color.Light.Cyan });
printBacktrace();
return true;
}
});
};
Java.perform(Main);
Die ANDROID_ID ist in jeder Android-Anwendung eindeutig.```javascript function getContext() { return Java.use('android.app.ActivityThread').currentApplication().getApplicationContext().getContentResolver(); }
function logAndroidId() { console.log('[-]', Java.use('android.provider.Settings$Secure').getString(getContext(), 'android_id')); }
<details>
<summary>Ausgabebeispiel</summary>
https://stackoverflow.com/a/54818023/2655092
</details>
<br>[⬆ Zurück zum Inhaltsverzeichnis](#table-of-contents)
#### Standort ändern```js
Java.perform(() => {
var Location = Java.use('android.location.Location');
Location.getLatitude.implementation = function() {
return LATITUDE;
}
Location.getLongitude.implementation = function() {
return LONGITUDE;
}
})
Screenshot-Verhinderung umgehen stackoverflow question```javascript Java.perform(function() { Java.use('android.view.SurfaceView').setSecure.overload('boolean').implementation = function(flag){ console.log('[1] flag:', flag); this.call(false); }; var LayoutParams = Java.use('android.view.WindowManager$LayoutParams'); Java.use('android.view.ViewWindow').setFlags.overload('int', 'int').implementation = function(flags, mask){ console.log('flag secure: ', LayoutParams.FLAG_SECURE.value); console.log('before:', flags); flags = (flags.value & ~LayoutParams.FLAG_SECURE.value); console.log('after:', flags); this.call(this, flags, mask); }; });
<details>
<summary>Ausgabebeispiel</summary>
https://stackoverflow.com/a/54818023/2655092
</details>
<br>[⬆ Zurück nach oben](#table-of-contents)
#### Shared Preferences aktualisieren```javascript
function notifyNewSharedPreference() {
Java.use('android.app.SharedPreferencesImpl$EditorImpl').putString.overload('java.lang.String', 'java.lang.String').implementation = function(k, v) {
console.log('[SharedPreferencesImpl]', k, '=', v);
return this.putString(k, v);
}
}
function hookOverloads(className, func) { var clazz = Java.use(className); var overloads = clazz[func].overloads; for (var i in overloads) { if (overloads[i].hasOwnProperty('argumentTypes') || overloads[i]['argumentTypes'] != undefined) { var parameters = [];
var curArgumentTypes = overloads[i].argumentTypes, args = [], argLog = '[';
for (var j in curArgumentTypes) {
var cName = curArgumentTypes[j].className;
parameters.push(cName);
argLog += "'(" + cName + ") ' + v" + j + ",";
args.push('v' + j);
}
argLog += ']';
var script = "var ret = this." + func + '(' + args.join(',') + ") || '';\n"
+ "console.log(JSON.stringify(" + argLog + "));\n"
+ "return ret;"
args.push(script);
clazz[func].overload.apply(this, parameters).implementation = Function.apply(null, args);
}
} }
Java.perform(function() { hookOverloads('java.lang.StringBuilder', '$init'); })
<details>
<summary>Ausgabebeispiel</summary>
TODO
</details>
<br>[⬆ Zurück zum Anfang](#table-of-contents)
#### Broadcast-Receiver registrieren```javascript
Java.perform(() => {
const MyBroadcastReceiver = Java.registerClass({
name: 'MyBroadcastReceiver',
superClass: Java.use('android.content.BroadcastReceiver'),
methods: {
onReceive: [{
returnType: 'void',
argumentTypes: ['android.content.Context', 'android.content.Intent'],
implementation: function(context, intent) {
// ..
}
}]
},
});
let ctx = Java.use('android.app.ActivityThread').currentApplication().getApplicationContext();
ctx.registerReceiver(MyBroadcastReceiver.$new(), Java.use('android.content.IntentFilter').$new('com.example.JAVA_TO_AGENT'));
});
function listClassesImplementsInterface(aInterface) { let classLoaders = Java.enumerateClassLoadersSync() Java.enumerateLoadedClassesSync().forEach(className => { for (let i = 0; i < classLoaders.length; i++) { let classLoader = classLoaders[i] Java.classFactory.loader = classLoader try { let jclass = Java.use(className).class let ifaces = jclass.getInterfaces().toString() jclass = null if (ifaces.indexOf(aInterface) != -1) { console.log(JSON.stringify({ name: className, loader: classLoader.toString(), interfaces: ifaces })) break // we found one ClassLoader, that's enough } } catch (e) { // continue to next ClassLoader } } }) }
<br>[⬆ Zurück nach oben](#table-of-contents)
#### Schrittanzahl erhöhen```js
Java.perform(() => {
var customSensorEventListener = null;
var curSteps = 0;
var totalNumberOfRequiredSteps = 10000;
function incSteps() {
Java.perform(() => {
var sEvent = Java.use('android.hardware.SensorEvent').$new(1);
sEvent.values.values = Java.array('float', [curSteps]); // https://developer.android.com/reference/android/hardware/SensorEvent#values
sEvent.timestamp = Java.use('java.lang.Long').$new(Java.use('java.lang.System').nanoTime());
sEvent.accuracy = Java.use('java.lang.Integer').$new(3); // https://developer.android.com/reference/android/hardware/SensorManager#SENSOR_STATUS_ACCURACY_HIGH
customSensorEventListener.onSensorChanged(sEvent);
if (curSteps < totalNumberOfRequiredSteps) {
setTimeout(() => {
curSteps += 50;
incSteps();
}, 1500)
}
});
}
Java.choose('.CustomSensorEventListener', { // class that implements SensorEventListener
onMatch: function (instance) {
customSensorEventListener = instance;
},
onComplete: function () {
incSteps();
}
});
});
var m = 'libsystem_trace.dylib'; // bool os_log_type_enabled(os_log_t oslog, os_log_type_t type); var isEnabledFunc = Module.findExportByName(m, 'os_log_type_enabled'); // _os_log_impl(void *dso, os_log_t log, os_log_type_t type, const char *format, uint8_t *buf, unsigned int size); var logFunc = Module.findExportByName(m, '_os_log_impl');
// Enable all logs Interceptor.attach(isEnabledFunc, { onLeave: function (ret) { ret.replace(0x1); } });
Interceptor.attach(logFunc, { onEnter: function (a) { /* OS_ENUM(os_log_type, uint8_t, OS_LOG_TYPE_DEFAULT = 0x00, OS_LOG_TYPE_INFO = 0x01, OS_LOG_TYPE_DEBUG = 0x02, OS_LOG_TYPE_ERROR = 0x10, OS_LOG_TYPE_FAULT = 0x11); */ var type = a[2]; var format = a[3]; if (type !== 0x2) { console.log(JSON.stringify({ type: type, format: format.readCString(), //buf: a[4].readPointer().readCString() // TODO }, null, 2)); } } })
<details>
<summary>Ausgabebeispiel</summary>
TODO
</details>
<br>[⬆ Zurück nach oben](#table-of-contents)
#### iOS-Warnfeld```js
var UIAlertController = ObjC.classes.UIAlertController;
var UIAlertAction = ObjC.classes.UIAlertAction;
var UIApplication = ObjC.classes.UIApplication;
var handler = new ObjC.Block({ retType: 'void', argTypes: ['object'], implementation: function () {} });
ObjC.schedule(ObjC.mainQueue, function () {
var alert = UIAlertController.alertControllerWithTitle_message_preferredStyle_('Frida', 'Hello from Frida', 1);
var defaultAction = UIAlertAction.actionWithTitle_style_handler_('OK', 0, handler);
alert.addAction_(defaultAction);
// Instead of using `ObjC.choose()` and looking for UIViewController instances on the heap, we have direct access through UIApplication:
UIApplication.sharedApplication().keyWindow().rootViewController().presentViewController_animated_completion_(alert, true, NULL);
})
Protokolliere jede Dateiöffnung```js Interceptor.attach(ObjC.classes.NSFileManager['- fileExistsAtPath:'].implementation, { onEnter: function (args) { console.log('open' , ObjC.Object(args[2]).toString()); } });
<details>
<summary>Ausgabebeispiel</summary>
TODO
</details>
<br>[⬆ Nach oben](#table-of-contents)
#### Beobachterklasse```js
function observeClass(name) {
var k = ObjC.classes[name];
k.$ownMethods.forEach(function(m) {
var impl = k[m].implementation;
console.log('Observing ' + name + ' ' + m);
Interceptor.attach(impl, {
onEnter: function(a) {
this.log = [];
this.log.push('(' + a[0] + ',' + Memory.readUtf8String(a[1]) + ') ' + name + ' ' + m);
if (m.indexOf(':') !== -1) {
var params = m.split(':');
params[0] = params[0].split(' ')[1];
for (var i = 0; i < params.length - 1; i++) {
try {
this.log.push(params[i] + ': ' + new ObjC.Object(a[2 + i]).toString());
} catch (e) {
this.log.push(params[i] + ': ' + a[2 + i].toString());
}
}
}
this.log.push(
Thread.backtrace(this.context, Backtracer.ACCURATE)
.map(DebugSymbol.fromAddress)
.join('\n')
);
},
onLeave: function(r) {
try {
this.log.push('RET: ' + new ObjC.Object(r).toString());
} catch (e) {
this.log.push('RET: ' + r.toString());
}
console.log(this.log.join('\n') + '\n');
}
});
});
}
`observeClass('Someclass$innerClass');```` Observing Someclass$innerClass - func Observing Someclass$innerClass - empty (0x174670040,parameterName) Someclass$innerClass - func 0x10048dd6c libfoo!0x3bdd6c 0x1005a5dd0 libfoo!0x4d5dd0 0x1832151c0 libdispatch.dylib!_dispatch_client_callout 0x183215fb4 libdispatch.dylib!dispatch_once_f RET: 0xabcdef
</details>
<br>[⬆ Back to top](#table-of-contents)
#### iOS-App-UUID ermitteln
Ermittle die UUID für einen bestimmten Pfad, wenn dieser an eine App gebunden ist, indem du die plist-Datei in jedem App-Container ausliest.```js
var PLACEHOLDER = '{UUID}';
function extractUUIDfromPath(path) {
var bundleIdentifier = String(ObjC.classes.NSBundle.mainBundle().objectForInfoDictionaryKey_('CFBundleIdentifier'));
var path_prefix = path.substr(0, path.indexOf(PLACEHOLDER));
var plist_metadata = '/.com.apple.mobile_container_manager.metadata.plist';
var errorPtr = Memory.alloc(Process.pointerSize);
Memory.writePointer(errorPtr, NULL);
var folders = ObjC.classes.NSFileManager.defaultManager().contentsOfDirectoryAtPath_error_(path_prefix, errorPtr);
var error = Memory.readPointer(errorPtr);
if (errorPtr)
console.error( new ObjC.Object( error ) );
for (var i = 0, l = folders.count(); i < l; i++) {
var uuid = folders.objectAtIndex_(i);
var metadata = path_prefix + uuid + plist_metadata;
var dict = ObjC.classes.NSMutableDictionary.alloc().initWithContentsOfFile_(metadata);
var enumerator = dict.keyEnumerator();
var key;
while ((key = enumerator.nextObject()) !== null) {
if (key == 'MCMMetadataIdentifier') {
var appId = String(dict.objectForKey_(key));
if (appId.indexOf(bundleIdentifier) != -1) {
return path.replace(PLACEHOLDER, uuid);
}
}
}
}
}
console.log( extractUUIDfromPath('/var/mobile/Containers/Data/Application/' + PLACEHOLDER + '/Documents') );
Gib eine Map der Mitglieder (mit Werten) für jede Klasseninstanz aus.```js ObjC.choose(ObjC.classes[clazz], { onMatch: function (obj) { console.log('onMatch: ', obj); Object.keys(obj.$ivars).forEach(function(v) { console.log('\t', v, '=', obj.$ivars[v]); }); }, onComplete: function () { console.log('onComplete', arguments.length); } });
<details>
<summary>Ausgabebeispiel</summary>
TODO
</details>
<br>[⬆ Back to top](#table-of-contents)
#### Klassenhierarchie
Object.keys(ObjC.classes) listet alle verfügbaren Objective C-Klassen auf, aber tatsächlich gibt dies alle im aktuellen Prozess geladenen Klassen zurück, einschließlich System-Frameworks. Wenn wir etwas wie weak_classdump wollen, um nur Klassen aus der ausführbaren Datei selbst aufzulisten, bietet die Objective C-Laufzeit bereits eine solche Funktion: [objc_copyClassNamesForImage](#https://developer.apple.com/documentation/objectivec/1418485-objc_copyclassnamesforimage?language=objc)```js
var objc_copyClassNamesForImage = new NativeFunction(
Module.findExportByName(null, 'objc_copyClassNamesForImage'),
'pointer',
['pointer', 'pointer']
);
var free = new NativeFunction(Module.findExportByName(null, 'free'), 'void', ['pointer']);
var classes = new Array(count);
var p = Memory.alloc(Process.pointerSize);
Memory.writeUInt(p, 0);
var path = ObjC.classes.NSBundle.mainBundle().executablePath().UTF8String();
var pPath = Memory.allocUtf8String(path);
var pClasses = objc_copyClassNamesForImage(pPath, p);
var count = Memory.readUInt(p);
for (var i = 0; i < count; i++) {
var pClassName = Memory.readPointer(pClasses.add(i * Process.pointerSize));
classes[i] = Memory.readUtf8String(pClassName);
}
free(pClasses);
var tree = {};
classes.forEach(function(name) {
var clazz = ObjC.classes[name];
var chain = [name];
while (clazz = clazz.$superClass) {
chain.unshift(clazz.$className);
}
var node = tree;
chain.forEach(function(clazz) {
node[clazz] = node[clazz] || {};
node = node[clazz];
});
});
send(tree);
Hooking von `objc_msgSend````py import frida, sys
f = open('/tmp/log', 'w')
def on_message(msg, _data): f.write(msg['payload']+'\n')
frida_script = """ Interceptor.attach(Module.findExportByName('/usr/lib/libobjc.A.dylib', 'objc_msgSend'), { onEnter: function(args) { var m = Memory.readCString(args[1]); if (m != 'length' && !m.startsWith('_fastC')) send(m); } }); """ device = frida.get_usb_device() pid = device.spawn(["com.example"]) # or .get_frontmost_application() session = device.attach(pid) script = session.create_script(frida_script) script.on('message', on_message) script.load() device.resume(pid) sys.stdin.read()
```sh
$ sort /tmp/log | uniq -c | sort -n
Um UI-bezogene Funktionen zu reduzieren, gehe ich wie folgt vor:
-o /tmp/log1 in eine Datei umleiten$ sort /tmp/log1 | uniq -c | sort -rn | head -n20 | cut -d# -f2 | paste -sd "," - in excludesList kopieren```js
var mName = 'MyModule', excludeList = ['Alot', 'Of', 'UI', 'Related', 'Functions'];
Module.enumerateExportsSync(mName)
.filter(function(e) {
var fromTypeFunction = e.type == 'function';·
var notInExcludes = excludeList.indexOf(e.name) == -1;
return fromTypeFunction && notInExcludes;
})
.forEach(function(e) {
Interceptor.attach(Module.findExportByName(mName, e.name), {
onEnter: function(args) {
console.log(mName + "#'" + e.name + "'");
}
})
})<details>
<summary>Beispielausgabe</summary>
TODO
</details>
<br>[⬆ Zurück nach oben](#table-of-contents)
#### Speichersegmente auslesen```js
Process.enumerateRanges('rw-', {
onMatch: function (range) {
var fname = `/sdcard/${range.base}_dump`;
var f = new File(fname, 'wb');
f.write(instance.base.readByteArray(instance.size));
f.flush();
f.close();
console.log(`base=${range.base} size=${range.size} prot=${range.protection} fname=${fname}`);
},
onComplete: function () {}
});
⬆ Zurück zum Inhaltsverzeichnis
function memscan(str) { Process.enumerateModulesSync().filter(m => m.path.startsWith('/data')).forEach(m => { var pattern = str.split('').map(letter => letter.charCodeAt(0).toString(16)).join(' '); try { var res = Memory.scanSync(m.base, m.size, pattern); if (res.length > 0) console.log(JSON.stringify({m, res})); } catch (e) { console.warn(e); } }); }
✅ Input validated. Output: raw Markdown/plain text (no code fences, no JSON, no commentary). This is chunk 140/166.
⚠️ Only natural language text is translated. All code, paths, commands, etc. remain unchanged.
---
### Einfaches Beispiel (mit Flags)

---
### Verwendung als Modul
```js
var memscn = function (str) {
Process.enumerateModulesSync().forEach(function (m) {
var pattern = str.split('').map(function (l) { return l.charCodeAt(0).toString(16) }).join(' ');
try {
var res = Memory.scanSync(m.base, m.size, pattern);
if (res.length > 0)
console.log(JSON.stringify({m, res}, null , 2));
} catch (e) {
console.warn(e);
}
});
}
var _module = Process.findModuleByName('myModule'); var base = ptr(_module.base); var startTraceOffset = 0xabcd1234, numInstructionsToTrace = 50; var startTrace = base.add(startTraceOffset), endTrace = startTrace.add(4 * (numInstructionsToTrace - 1));
Interceptor.attach(ObjC.classes.CustomClass['- func'].implementation, {
onEnter: function (args) {
var tid = Process.getCurrentThreadId();
this.tid = tid;
console.warn(onEnter [ ${tid} ]);
Stalker.follow(tid, {
transform: function (iterator) {
var instruction;
while ((instruction = iterator.next()) !== null) {
// condition to putCallout
if (instruction.address <= endTrace && instruction.address >= startTrace) {
// print instruction & registers values
iterator.putCallout(function(context) {
var offset = ptr(context.pc).sub(base);
var inst = Instruction.parse(context.pc).toString();
var modified_inst = inst;
inst.replace(/,/g, '').split(' ').forEach(op => {
if (op.startsWith('x'))
modified_inst = modified_inst.replace(op, context[op]);
else if (op.startsWith('w'))
modified_inst = modified_inst.replace(op, context[op.replace('w', 'x')]);
});
modified_inst = '\x1b[35;01m' + modified_inst + '\x1b[0m';
console.log(x8=${context.x8} x25=${context.x25} x0=${context.x0} x21=${context.x21})
console.log(${offset} ${inst} # ${modified_inst});
});
}
iterator.keep();
}
}
})
},
onLeave: function (retval) {
console.log(onLeave [ ${this.tid} ]);
// cleanup
Stalker.unfollow(this.tid);
Stalker.garbageCollect();
}
})
<details>
<summary>Ausgabebeispiel</summary>
mul x5, x2, x21 # mul 0x3, 0x4, 0x5
</details>
<br>[⬆ Zurück zum Anfang](#table-of-contents)
#### Cpp demangler```sh
$ npm i frida-compile demangler-js -g
Füge deinem Skript hinzu```js const demangle = require('demangler-js').demangle; ... Module.enumerateExportsSync('library.so') .filter(x => x.name.startsWith('_Z')) .forEach(x => { Interceptor.attach(x.address, { onEnter: function (args) { console.log('[-] ' + demangle(x.name)); } }); });
compile```sh
$ frida-compile script.js -o out.js
run```sh $ frida -Uf com.app -l out.js
<details>
<summary>Ausgabebeispiel</summary>
TODO
</details>
<br>[⬆ Nach oben](#table-of-contents)
#### Früher Hook
Setze Hooks vor DT_INIT_ARRAY ( [Quelle](https://cs.android.com/android/platform/superproject/+/master:bionic/linker/linker_soinfo.cpp;l=386;drc=android-8.0.0_r1?q=call_constructor&ss=android%2Fplatform%2Fsuperproject) )```js
let base;
let do_dlopen = null;
let call_ctor = null;
const target_lib_name = 'targetlib.so';
Process.findModuleByName('linker64').enumerateSymbols().forEach(sym => {
if (sym.name.indexOf('do_dlopen') >= 0) {
do_dlopen = sym.address;
} else if (sym.name.indexOf('call_constructor') >= 0) {
call_ctor = sym.address;
}
})
Interceptor.attach(do_dlopen, function (args) {
if (args[0].readUtf8String().indexOf(target_lib_name) >= 0) {
Interceptor.attach(call_ctor, function () {
const module = Process.findModuleByName(target_lib_name);
base = module.base;
console.log('loading', target_lib_name, '- base @', base);
// DoStuff
})
}
})
Mitwirkung: iGio90
Beispiel für eine schnelle und einfache Extraktion von iOS-Geräteeigenschaften```js var UIDevice = ObjC.classes.UIDevice.currentDevice(); UIDevice.$ownMethods .filter(function(method) { return method.indexOf(':') == -1 /* filter out methods with parameters / && method.indexOf('+') == -1 / filter out public methods */ }) .forEach(function(method) { console.log(method, ':', UIDevicemethod) }) console.log('executablePath =', ObjC.classes.NSBundle.mainBundle().executablePath().toString());
` return to top
Vollständige Installations- und Nutzungsanleitung
------------------------------------------------------------------
## Alle Werkzeuge
### Kategorie: Aufklärungs-Tools```js
if (ObjC.available) {
var processInfo = ObjC.classes.NSProcessInfo.processInfo();
var versionString = processInfo.operatingSystemVersionString().toString();
// E.g. "Version 13.5 (Build 17F75)"
var ver = versionString.split(' ');
var version = ver[1];
// E.g. 13.5
console.log("iOS version: " + version);
}
function screenshot() { ObjC.schedule(ObjC.mainQueue, function() { var getNativeFunction = function (ex, retVal, args) { return new NativeFunction(Module.findExportByName('UIKit', ex), retVal, args); }; var api = { UIWindow: ObjC.classes.UIWindow, UIGraphicsBeginImageContextWithOptions: getNativeFunction('UIGraphicsBeginImageContextWithOptions', 'void', [['double', 'double'], 'bool', 'double']), UIGraphicsBeginImageContextWithOptions: getNativeFunction('UIGraphicsBeginImageContextWithOptions', 'void', [['double', 'double'], 'bool', 'double']), UIGraphicsEndImageContext: getNativeFunction('UIGraphicsEndImageContext', 'void', []), UIGraphicsGetImageFromCurrentImageContext: getNativeFunction('UIGraphicsGetImageFromCurrentImageContext', 'pointer', []), UIImagePNGRepresentation: getNativeFunction('UIImagePNGRepresentation', 'pointer', ['pointer']) }; var view = api.UIWindow.keyWindow(); var bounds = view.bounds(); var size = bounds[1]; api.UIGraphicsBeginImageContextWithOptions(size, 0, 0); view.drawViewHierarchyInRect_afterScreenUpdates_(bounds, true);
var image = api.UIGraphicsGetImageFromCurrentImageContext();
api.UIGraphicsEndImageContext();
var png = new ObjC.Object(api.UIImagePNGRepresentation(image));
send('screenshot', Memory.readByteArray(png.bytes(), png.length()));
}); }
rpc.exports = { takescreenshot: screenshot }
EINGABE:```py
...
def save_screenshot(d):
f = open('/tmp/screenshot.png', 'wb')
f.write(d)
f.close()
def on_message(msg, data):
save_screenshot(data)
script.exports.takescreenshot()
# open screenshot & invoke rpc via input
# <Ctrl+C> will take screenshot, open it with eog & wait for export function name to invoke via input
def on_message(msg, data):
if 'payload' in msg:
if msg['payload'] == 'screenshot':
i = '/tmp/screenshot.png'
f = open(i, 'wb')
f.write(data)
f.close()
subprocess.call(['eog', i])
while True:
try:
time.sleep(1)
except KeyboardInterrupt:
script.exports.takescreenshot()
try:
getattr(script.exports, input())()
except (KeyboardInterrupt, frida.core.RPCException) as e:
print('[!]', e)
Interceptor.attach(ObjC.classes.NMSSHChannel['- execute:error:timeout:'].implementation, {
onEnter: function(args) {
this.cmd = ObjC.Object(args[2]).toString();
this.timeout = args[4];
},
onLeave: function(retv) {
console.log(CMD: ${ObjC.Object(args[2]).toString()} Timeout: ${args[4]} Ret: ${retv});
}
});
<details>
<summary>Beispielausgabe</summary>
TODO
</details>
<br>[⬆ Zurück nach oben](#table-of-contents)
#### TODO-Liste
- GIFs und Beispiele hinzufügen
- Links zu /scripts hinzufügen
- Universelles SSL-Unpinning erweitern für [ios](https://codeshare.frida.re/@dki/ios10-ssl-bypass/) [andoid 1](https://github.com/Fuzion24/JustTrustMe/blob/master/app/src/main/java/just/trust/me/Main.java) [android
$ frida --codeshare FrenchYeti/android-file-system-access-hook -f com.example.app --no-pauseimplementation den Wert null zu.#include #include
extern "C" { void* create_stdstr(char data, int size) { std::string s = new std::string(); (*s).assign(data, size); return s; } }
### Mittn

Dies wurde [fast vollständig](https://0xpatrik.com/subdomain-enumeration-2019/) auf Pentester Land behandelt:
* [Subdomain-Enumeration mit Mittn – Teil 1](https://pentester.land/cheatsheets/2019/11/17/subdomain-enumeration-with-mittn-part-1.html)
* [Subdomain-Enumeration mit Mittn – Teil 2](https://pentester.land/cheatsheets/2019/11/20/subdomain-enumeration-with-mittn-part-2.html)```sh
$ ./android-ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang++ a.cpp -o a -shared -static-libstdc++ && adb push a /data/local/tmp/a
Das Tools-Menü enthält:
#### C-Modul laden
* https://frida.re/docs/javascript-api/#cmodule
* https://frida.re/news/2019/09/18/frida-12-7-released/```sh
$ ./aarch64-linux-android21-clang /tmp/b.c -o /tmp/a -shared ../sysroot/usr/lib/aarch64-linux-android/21/liblog.so && adb push /tmp/a /data/local/tmp/a
[No content provided in the INPUT section. Please provide the Markdown text to translate.]```c #include <stdio.h> #include <stdlib.h> #include <android/log.h>
#define TAG "TEST1" #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, VA_ARGS) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, VA_ARGS)
void test(void) { FILE* fp = popen("ls -l /sdcard 2>&1", "r"); if (fp == NULL) LOGE("executing cmd failed"); char b[256]; while (fgets(b, sizeof(b), fp) != NULL) { LOGI("%s", b); } pclose(fp); }
Please provide the Markdown content to translate.```sh
$ frida -Uf com.app --no-pause --enable-jit -e "Module.load('/data/local/tmp/a')"
[ ] -> new NativeFunction(Module.findExportByName('a', 'test'), 'void', [])()
Fange funcPtr ab und protokolliere, wer auf x2 liest/schreibt, indem die Berechtigungen mit mprotect entfernt werden.```js
Process.setExceptionHandler(function(exp) {
console.warn(JSON.stringify(Object.assign(exp, { _lr: DebugSymbol.fromAddress(exp.context.lr), _pc: DebugSymbol.fromAddress(exp.context.pc) }), null, 2));
Memory.protect(exp.memory.address, Process.pointerSize, 'rw-');
// can also use new NativeFunction(Module.findExportByName(null, 'mprotect'), 'int', ['pointer', 'uint', 'int'])(parseInt(this.context.x2), 2, 0)
return true; // goto PC
});
Interceptor.attach(funcPtr, { onEnter: function (args) { console.log('onEnter', JSON.stringify({ x2: this.context.x2, mprotect_ret: Memory.protect(this.context.x2, 2, '---'), errno: this.errno }, null, 2)); }, onLeave: function (retval) { console.log('onLeave'); } });
<details>
<summary>Ausgabebeispiel</summary>```
[iOS Device::com.app]-> onEnter {
"x2": "0x1c145c6e0",
"mprotect_ret": true,
"errno": 2
}
{
"type": "access-violation",
"address": "0x1853b0198",
"memory": {
"operation": "read",
"address": "0x1c145c6e0"
},
"context": {
"lr": "0x100453358",
"fp": "0x16fb2e860",
"x28": "0x0",
"x27": "0x0",
"x26": "0x104312600",
"x25": "0x0",
"x24": "0x0",
"x23": "0x0",
"x22": "0x0",
"x21": "0xb000000422bbda03",
"x20": "0x1c4a22560",
"x19": "0xb000000422bbda03",
"x18": "0x0",
"x17": "0x100d25290",
"x16": "0x1853b0190",
"x15": "0x0",
"x14": "0x5",
"x13": "0xe5a1c4119597",
"x12": "0x10e80ca30",
"x11": "0x180000003f",
"x10": "0x10e80ca00",
"x9": "0x1020ad7c3",
"x8": "0x0",
"x7": "0x0",
"x6": "0x0",
"x5": "0x0",
"x4": "0xb000000422bbda03",
"x3": "0x1c4a22560",
"x2": "0x1c145c6e0",
"x1": "0x1020ad7c3",
"x0": "0x1c145c6e0",
"sp": "0x16fb2e790",
"pc": "0x1853b0198"
},
"nativeContext": "0x16fc42b24"
}
onLeave
var cookieJar = {}; var cookies = ObjC.classes.NSHTTPCookieStorage.sharedHTTPCookieStorage().cookies(); for (var i = 0, l = cookies.count(); i < l; i++) { var cookie = cookies'- objectAtIndex:'; cookieJar[cookie.Name()] = cookie.Value().toString(); // "- expiresDate".toString() } console.log(JSON.stringify(cookieJar, null, 2));
<details>
<summary>Ausgabebeispiel</summary>```js
{
"key1": "value 1",
"key2": "value 2"
}