FRIDA

How?

Set of tools (framework) for Dynamic Code Instrumentation.

  • Instruments the Application Code with hooks.

  • Not specific for Android, and may be used on other applications and Operating Systems.

Allows for

  • Tracing network communications at the method level.

  • Understand how the application behaves.

  • Manipulate the methods called, arguments and return codes.

Frida-core injecting a Google V8 JS Engine into the App scope.

  • Frida-core Written in C.

  • GumJS (the JS part) is packed as a shared library and loaded into the app.

GumJS has access to the application memory.

  • Can be hooked to methods and intercept calls, even native APIs.

GumJS API allows interaction with GumJS from an external client.

  • Because GumJS resides in the application address space, it has full access to its structures.

Embedded Mode

Frida agent is embedded as a dependency of an existing application library.

  • Requires the application to have an .so

  • libfrida-gadget.so is injected into the existing library and loaded at the same time.

Alternative: the existing library is modified to load the additional library.

  • Requires patching the code in a function will be called (e.g. JNI_OnLoad).

Alternative: patching the smali code to load the library.

  • Obtain APK.

  • Extract smali.

  • Change smali.

  • Pack it and install.

The method implies that the application is repacked/resigned.

$ apktool d app.apk
$ cp libfrida-gadget.so target/lib/arm
$ python3
>>> import lief
>>> native = lief.parse(“target/lib/arm/libsomething.so”)
>>> native.add_library(“libfrida-gadget.so”)
>>> native.write(“target/lib/arm/libsomething.so”)
>>> exit
$ apktool b target
… sign … install

Smali

  • Unpack the app using apktool.

  • Patch the smali with.

const-string v0, "frida-gadget"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
  • Where? In the main activity constructor.

    • Even in as a static property of the class.

  • repack, sign, install.

Caveats

Applications may search for the library name as an anti-debug technique.

  • May need to change the library name.

Must use a version compatible with the target architecture.

An agent may only be loaded after the JNI library is loaded or the code is reached.

  • After System.loadLibrary(“lib.so”)

An agent may impose the need for permissions to access the INTERNET.

  • Manifest may need to be updated.

Injected Mode

Run a Frida Server which injects the agent into the target process.

  • The server provides an API for remote use.

  • The server injects the agents into applications.

Requires the smartphone to be rooted or to be an emulator.

  • To run the server and inject applications.

Cannot be used in production builds, only in development.

  • When in an Emulator, use a base without WITHOUT “Google Services”.

How it works?

  • Create an Agent: it’s an .so with some custom code.

  • Start a server that will be ready to attach to processes.

  • Injection:

    • Create a thread in the remote process using ptrace.

      • PTRACE_ATTACH, PRACE_GETREGS

    • Allocate memory for a bootstrapping code.

      • The minimal amount of code that is required for pulling the agent.

    • Write bootstrapper to memory.

    • Execute bootstrapper in the remote process.

      • Open communication channel to server (FIFO).

      • Loads the agent from a shared library (.so).

      • Executes the agent.

      • Closes communication channel (the agent will expose an API).

Required functionality:

  • ptrace - Process tracing.

  • mmap - Map files to memory. In particular, the agent .so.

  • dlopen - Open the .so with the agent.

  • dlsym - Retrieves addresses of loaded symbols.

  • signal - To handle system signals.

### On the first PC terminal

# wget frida-server from github

adb push frida-server /data/local/tmp
adb shell
su
cd /data/local/tmp
chmod +x frida-server
./frida-server

### On the second PC terminal
# List processes
$ frida-ps -U

Command line tools: frida, frida-trace, frida-ps, frida-discover.

Python interface.

  • Provides a more advanced, programmatic interface.

  • Allows predictable and repeatable instrumentation.

How to instrument code: using JS that overloads existing functions.

Example

Objective: make b.checkAppSignature return false.

int d1 = b.checkAppSignature(this);
if(d1 < 1){
    Toast.makeText(context,"Application Tampered",Toast.LENGTH_LONG).show();
    this.finishAffinity();
}
try{

Java.perform: executes the given payload.

Snippet provides an alternative implementation of the method.

Java.perform(function(){
    Java.use("com.re.lab1.b").checkAppSignature.implementation = function(a) {
        console.log("Signature will fail");
        return 0;
    };
});
cursor = secureDB.rawQuery("SELECT * FROM a;",null);
cursor.moveToFirst();

if(pp.equalsIgnoreCase(cursor.getString(0))){
    Toast.makeText(MainActivity.this, "Right Pin, Congratulations", Toast.LENGTH_SHORT).show();
    pin1.removeAllViews();
    String xo = getResources().getString(R.string.google_api_key);
    a mo = new a();
    xo = mo.func1(xo,xo.substring(4));
    xo = a.func2(xo);
    xo = a.func3(xo.substring(1),xo);
    xo = a.func4(xo,xo,xo.substring(2));
    tv1.setText("Flag: "+xo);
}else{
    Toast.makeText(MainActivity.this, "Incorrect Pin, "+(max_tries+1-i)+" attempts remaining", T
}

pp has the pin provided.

The cursor has the value obtained from the DB.

Objective: reimplement Java.lang.String.equalsIgnoreCase so that it returns true, and prints the correct ping

Java.perform(function(){
    Java.use("java.lang.String").equalsIgnoreCase.implementation = function(a) {
        console.log("Real PIN: " + a);
        return true;
    };
});

Interceptors

Intecept calls to a function.

Define two events where code can be executed.

  • OnEnter: When the function is called.

  • OnLeave: After the function returns.

Can be used as a generic logger, or to trigger other actions.

  • Can intercept calls on lower layers of the application stack.

    • Data that is to be written, SQL queries, etc…

function foo(){
    Interceptor.attach(Module.findExportByName(“libc.so, “open”), {
        onEnter: function(args){
            console.log(“Entering the function”);
        },
        onLeave: function(args){
            console.log(“Leaving the function”);
        },
    });
}

Last updated