Frida: Dynamic instrumentation tool

 


Android

In this tutorial we show how to do function tracing on your Android device.

Setting up your Android device

Before you start, you will need to root your device in case you haven’t done so already. It is technically also possible to use Frida without rooting your device, for example by repackaging the app to include frida-gadget, or using a debugger to accomplish the same. But, for this introduction we’re going to focus on the simplest case: a rooted device.

Also note that most of our recent testing has been taking place on a Pixel 3 running Android 9. Older ROMs may work too, but if you’re running into basic issues like Frida crashing the system when launching an app, this is due to ROM-specific quirks. We cannot test on all possible devices, so we count on your help to improve on this. However if you’re just starting out with Frida it is strongly recommended to go for a Pixel or Nexus device running the latest official software, or a device whose software is as close to AOSP as possible. Another option is using an emulator, ideally with a Google-provided Android 9 emulator image for arm or arm64. (x86 may work too but has gone through significantly less testing.)

You will also need the adb tool from the Android SDK.

First off, download the latest frida-server for Android from our releases page and uncompress it.

unxz frida-server.xz

Now, let’s get it running on your device:

$ adb root # might be required
$ adb push frida-server /data/local/tmp/
$ adb shell "chmod 755 /data/local/tmp/frida-server"
$ adb shell "/data/local/tmp/frida-server &"

For the last step, make sure you start frida-server as root, i.e. if you are doing this on a rooted device, you might need to su and run it from that shell.

adb on a production build

If you get adbd cannot run as root in production builds after running adb root
you need to prefix each shell command with su -c. For example: adb shell "su -c chmod 755 /data/local/tmp/frida-server"

Next, make sure adb can see your device:

$ adb devices -l

This will also ensure that the adb daemon is running on your desktop, which allows Frida to discover and communicate with your device regardless of whether you’ve got it hooked up through USB or WiFi.

A quick smoke-test

Now, on your desktop it’s time to make sure the basics are working. Run:

$ frida-ps -U

This should give you a process list along the lines of:

PID NAME
1590 com.facebook.katana
13194 com.facebook.katana:providers
12326 com.facebook.orca
13282 com.twitter.android

Great, we’re good to go then!

Tracing open() calls in Chrome

Alright, let’s have some fun. Fire up the Chrome app on your device and return to your desktop and run:

$ frida-trace -U -i open com.android.chrome
Uploading data...
open: Auto-generated handler …/linker/open.js
open: Auto-generated handler …/libc.so/open.js
Started tracing 2 functions. Press Ctrl+C to stop.

Now just play around with the Chrome app and you should start seeing open() calls flying in:

1392 ms open()
1403 ms open()
1420 ms open()

You can now live-edit the aforementioned JavaScript files as you read man open, and start diving deeper and deeper into your Android apps.

Building your own tools

While the CLI tools like frida, frida-trace, etc., are definitely quite useful, there might be times when you’d like to build your own tools harnessing the powerful Frida APIs. For that we would recommend reading the chapters on Functions and Messages, and anywhere you see frida.attach() just substitute that with frida.get_usb_device().attach().



Windows

Example tool for directly monitoring a jvm.dll

Shows how to monitor a jvm.dll which is being executed by a process called fledge.exe (BB Simulator) using Frida.

Save this code as bb.py, run BB Simulator (fledge.exe), then run python.exe bb.py fledge.exe for monitoring AES usage of jvm.dll.

from __future__ import print_function
import frida
import sys
def on_message(message, data):
print("[%s] => %s" % (message, data))
def main(target_process):
session = frida.attach(target_process)
script = session.create_script("""
// Find base address of current imported jvm.dll by main process fledge.exe
var baseAddr = Module.findBaseAddress('Jvm.dll');
console.log('Jvm.dll baseAddr: ' + baseAddr);
var SetAesDeCrypt0 = resolveAddress('0x1FF44870'); // Here we use the function address as seen in our disassembler
Interceptor.attach(SetAesDeCrypt0, { // Intercept calls to our SetAesDecrypt function
// When function is called, print out its parameters
onEnter: function (args) {
console.log('');
console.log('[+] Called SetAesDeCrypt0' + SetAesDeCrypt0);
console.log('[+] Ctx: ' + args[0]);
console.log('[+] Input: ' + args[1]); // Plaintext
console.log('[+] Output: ' + args[2]); // This pointer will store the de/encrypted data
console.log('[+] Len: ' + args[3]); // Length of data to en/decrypt
dumpAddr('Input', args[1], args[3].toInt32());
this.outptr = args[2]; // Store arg2 and arg3 in order to see when we leave the function
this.outsize = args[3].toInt32();
},
// When function is finished
onLeave: function (retval) {
dumpAddr('Output', this.outptr, this.outsize); // Print out data array, which will contain de/encrypted data as output
console.log('[+] Returned from SetAesDeCrypt0: ' + retval);
}
});
function dumpAddr(info, addr, size) {
if (addr.isNull())
return;
console.log('Data dump ' + info + ' :');
var buf = addr.readByteArray(size);
// If you want color magic, set ansi to true
console.log(hexdump(buf, { offset: 0, length: size, header: true, ansi: false }));
}
function resolveAddress(addr) {
var idaBase = ptr('0x1FEE0000'); // Enter the base address of jvm.dll as seen in your favorite disassembler (here IDA)
var offset = ptr(addr).sub(idaBase); // Calculate offset in memory from base address in IDA database
var result = baseAddr.add(offset); // Add current memory base address to offset of function to monitor
console.log('[+] New addr=' + result); // Write location of function in memory to console
return result;
}
""")
script.on('message', on_message)
script.load()
print("[!] Ctrl+D on UNIX, Ctrl+Z on Windows/cmd.exe to detach from instrumented program.\n\n")
sys.stdin.read()
session.detach()
if __name__ == '__main__':
if len(sys.argv) != 2:
print("Usage: %s <process name or PID>" % __file__)
sys.exit(1)
try:
target_process = int(sys.argv[1])
except ValueError:
target_process = sys.argv[1]
main(target_process)

Comments

Popular posts from this blog

Mobexler: Os built for Pentesting

Charles: http/https proxy

Jadx: Reverse engineering tool