Frida
Frida¶
Example Scripts:
https://github.com/0xdea/frida-scripts
https://gitlab.com/roxanagogonea/frida-scripts
https://github.com/noobpk/frida-android-hook
Installing Frida¶
Frida is as easy to install as pip install frida
.
Installing Frida on Android Device¶
TODO but should already be done on testing devices.
Connecting 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.
Enumerating 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_*"
Using 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
Writing 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);
};
}
}
Backtracing 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")
}
});
Tracing a function¶
Java.perform(function() {
trace("com.mwr.dz.service_connectors.ClientServiceConnection.send");
});
Writing 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);
}
}
});
Frida on a non rooted Android device¶
Decompile 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
Recompiling 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
Injecting 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".
Errors¶
Java 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.