Link to this headingFrida

Example Scripts:
https://github.com/0xdea/frida-scripts
https://gitlab.com/roxanagogonea/frida-scripts
https://github.com/noobpk/frida-android-hook

Link to this headingInstalling Frida

Frida is as easy to install as pip install frida.

Link to this headingInstalling Frida on Android Device

TODO but should already be done on testing devices.

Link to this headingConnecting Devices

Connecting your device to your computer.

Then check to make sure that frida is installed correctly by executing the frida-ps from your local computer.

>>> frida-ps -U PID Name ---- -------------------------------------------------- 696 adbd 5828 android.ext.services 6188 android.process.acore 5210 audioserver 5211 cameraserver 8334 com.android.calendar 6685 com.android.chrome 6245 com.android.deskclock 5528 com.android.inputmethod.latin 6120 com.android.phone 6485 com.android.printspooler 8355 com.android.providers.calendar 5844 com.android.systemui 7944 com.google.android.apps.nexuslauncher 6416 com.google.android.gms [...]

This shows that the device is connected and setup properly.

Link to this headingEnumerating Java Classes

There is no classdump for Android but since its an APK it is possible to view the Smali and Java code through apktool and bytecode-viewer. Using this

This can also be done by tracing the functions that are called using Frida-Trace. Where -U is the Application name and -i is grep for the functions to trace.

frida-trace -U Gadget -i "*evbuffer_*"

Link to this headingUsing Java

FridaAndroidTracer Usage:

$ java -jar FridaAndroidTracer.jar -a,--expand-array expand array values -c,--classes <arg> classes to be hooked -j,--jars <arg> jar files to be included -o,--output <arg> output script path -p,--include-private include private methods -s,--skip <arg> methods to be skipped

Link to this headingWriting a Java Hook

Below is a sample java hook. First you make a variable of the Class then replace the function with the new function.

Where “com.test.app.utils.AppStateCallbacks” is the Class and “isAdbRunning” is the function that should be replaced.

This function returns false as shown with return Boolean(0);.

if(Java.available) { Java.perform(function () { var Handler = Java.use("com.test.app.utils.AppStateCallbacks"); send("Handler set") Handler.isAdbRunning.implementation = function (a) { send("ADB Callback"); return Boolean(0); }; }); }

If this does not work the class may be overloaded.

if(Java.available) { Java.perform(function () { var Handler = Java.use("com.test.app.androidsecureframework.AndroidSecureSocket"); send("Handler set") Handler.writeData.overload("java.lang.Byte","java.lang.Integer","java.lang.Integer").implementation = function (a, b, c) { send("AndroidSecureSocket.writeData(" + a + ", " + b + ", " + c + ")"); this.call(this,a,b,c); }; } }

Link to this headingBacktracing a function

Using backtracing it is possible to view the function callback for the function that is hooked.

var openPtr = Module.findExportByName("libAndroidSecureFramework.so", "_Z29secureframework_checkIsRootedP7_JNIEnvP7_jclass"); Interceptor.attach(openPtr, { onEnter: function (args) { send("%s called from: " + Thread.backtrace(this.context, Backtracer.ACCURATE).join(" ")); //send("%s(" + args[1] +")"); }, onLeave: function (retval) { //send("Exit: %s") } });

Link to this headingTracing a function

Java.perform(function() { trace("com.mwr.dz.service_connectors.ClientServiceConnection.send"); });

Link to this headingWriting a Native Hook

Return a NULL Value:

var openPtr = Module.findExportByName("libAndroidSecureFramework.so", "_Z35secureframework_findBlacklistedAppsP7_JNIEnvP7_jclass"); Interceptor.attach(openPtr, { onEnter: function (args) { send("Bypassing Blacklisted Apps"); }, onLeave: function (retval) { //Return null string array retval.replace(ptr("0")); send("Blacklisted apps will be null"); } });

Dump Buffers:

var openPtr2 = Module.findExportByName("libAndroidSecureFramework.so", "evbuffer_remove"); Interceptor.attach(openPtr2, { onEnter: function (args) { var message = hexdump(Memory.readByteArray(args[1], args[2].toInt32()), {header: false, ansi: false}) send("evbuffer_remove " + message) }, onLeave: function (retval) { } });

Pointer Math Sample:

// offsets to specific functions identified in disassembler var offset_anti_debug_x64 = 0x000075f0; var offset_protect_secret64 = 0x0000779c; var offset_strncmp_xor64 = 0x000077ec; function do_native_hooks_libfoo(){ var p_foo = Module.findBaseAddress("libfoo.so"); if (!p_foo) { send("p_foo is null (libfoo.so). Returning now..."); return 0; } // calculate absolute address (base + offset) to each function var p_protect_secret = p_foo.add(offset_protect_secret64); var p_strncmp_xor64 = p_foo.add(offset_strncmp_xor64); send("libfoo.so @ " + p_foo.toString()); send("ptr_protect_secret @ " + p_protect_secret.toString()); send("ptr_strncmp_xor64 @ " + p_strncmp_xor64.toString()); // attach to target functions and dump arguments Interceptor.attach( p_protect_secret, { onEnter: function (args) { send("onEnter() p_protect_secret"); send("args[0]: " + args[0]); }, onLeave: function (retval) { send("onLeave() p_protect_secret"); } }); Interceptor.attach( p_strncmp_xor64, { onEnter: function (args) { send("onEnter() p_strncmp_xor64"); send("args[0]: " + args[0]); // hexdump is a custom function that handles raw hex values in memory (for better readability) send(hexdump(args[0], { offset: 0, length: 24, header: false, ansi: true })); send("args[1]: " + args[1]); var secret = hexdump(args[1], { offset: 0, length: 24, header: false, ansi: true }) send(secret); }, onLeave: function (retval) { send("onLeave() p_strncmp_xor64"); send(retval); } }); }

Read and Write Floats to an address:

// Find Player::GetWalkingSpeed() var walkSpeed = Module.findExportByName("libGameLogic.so", "_ZN6Player15GetWalkingSpeedEv"); console.log("Player::GetWalkingSpeed() at address: " + walkSpeed); // Check Speed Interceptor.attach(walkSpeed, { // Get Player * this location onEnter: function (args) { console.log("Player at address: " + args[0]); this.walkingSpeedAddr = ptr(args[0]).add(736) // Offset m_walkingSpeed console.log("WalkingSpeed at address: " + this.walkingSpeedAddr); }, // Get the return value and write the new value onLeave: function (retval) { console.log("Walking Speed: " + Memory.readFloat(this.walkingSpeedAddr)); Memory.writeFloat(this.walkingSpeedAddr, 9999); } });

Allocate and Write Data:

//Teleport var setPositionAddr = Module.findExportByName("libGameLogic.so", "_ZN5Actor11SetPositionERK7Vector3"); var setPosition = new NativeFunction(setPositionAddr, 'void', ['pointer', 'pointer']); var Vector3 = Memory.alloc(16); function teleport(thisReference, x, y, z) { Memory.writeFloat(Vector3, x); Memory.writeFloat(ptr(Vector3).add(4), y); Memory.writeFloat(ptr(Vector3).add(8), z); setPosition(thisReference, Vector3); }

Read and Write Integers:

var getMana = Module.findExportByName("libGameLogic.so", "_ZN6Player7GetManaEv"); console.log("Player::GetMana at address: " + getMana); Interceptor.attach(getMana, { onEnter: function (args) { if (cheatStatus.infiniteMana == 1) { m_manaAddr = ptr(args[0]).add(544) // Offset m_mana Memory.writeInt(m_manaAddr, 100); } } });

Link to this headingFrida on a non rooted Android device

Link to this headingDecompile the APK

Decompiling to smali is great because you don’t have to deal with decompiling and recompiling the application from class/java/jar files. This makes it a bit better to actually change and be able to recompile the application.

apktool d APK_FILE.apk -o outputfolder

These file can be edited and then recompiled back to the binary

Link to this headingRecompiling and Signing a decompiled application to an apk

Following the directions above it is possible to decompile an application to the smali files.

Remove Original Signatures:

rm -r outputfolder/original/META-INF

Rebuild the Apk File:

apktool b -o unsigned.apk outputfolder

Generate Signing Certificate:

keytool -genkeypair -alias androiddebugkey -dname 'CN=Android Debug,O=Android,C=US' -keystore debug.keystore -keyalg RSA -validity 10000

Zipalign the apk:

~/Android/Sdk/build-tools/25.0.3/zipalign 4 unsigned.apk alligned.apk

Then resign the application:

jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -tsa http://timestamp.digicert.com -keystore alligned.apk androiddebugkey

Verify that it is signed correctly:

jarsigner -verify alligned.apk

Link to this headingInjecting the Frida-gadget lib into the APK

Download the frida-gadget-{Version}-android-{Architecture}.so.xz that matches the version that you have on your computer and the architecture for the device.

Find Installed Frida Version:

>>> frida --version 10.0.1

Add the shared library into the APK:

cp frida-gadget-10.0.1-android-arm.so outputfolder/libslib/armeabi-v7a/libfrida-gadget.so

Add the Samali Code at some point in the application. Frida will not be able to hook before the library is loaded.

Load the shared Library:

const-string v0, "frida-gadget" invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V

Then use frida as normal with the application ID as “Gadget”.

Link to this headingErrors

Link to this headingJava class is not loaded

This usually means that the APK is doing some weird runtime things using Java Inflection. An error will look like this.

Error: java.lang.ClassNotFoundException: Didn't find class "gi.Km" on path: DexPathList[[directory "."],nativeLibraryDirectories=[/system/lib64, /vendor/lib64, /system/lib64, /vendor/lib64]]

There are two functions that will help you figure out what is going on.
deflector() will hook the Java loader and show you what is being loaded.
MainClassLoader("/data/app/<application_firectory>/base.apk") This will manually load the APK classes in to the Java Virtual Machine.