Skip to content

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.