Inline hook with Frida (Thumb version)

Interceptor.attach in Frida only can hook one function. So the first argument, target, should be a function address. In my opinion, inline hook just let users can hook arbitrary instruction at any address. In this article, I will try to introduce a method to implement inline hook using Frida. and only implement for Thumb binary so far.

Method

To implement inline hook, we need to do the following thing:

  • Save original bytes at hook target
  • Write a hook handle function in .so file in C/C++.
  • Write a trampoline code in Thumb. And put original bytes into trampoline code.
  • Put a BL instruction at hook target.

I will describe every stage in detail, and for convenience, I will user the following terminology:

Term Description
hook_ptr The address of hook target, same as the target argument in Interceptor.attach
hook_fun_ptr The address of hook handle function, we define this function in .so file
tempoline_ptr The address of trampoline code

So if we set every code correctly, once CPU reaches hook_ptr, CPU will call trampoline code as a function. And the trampoline code will call hook handle function and execute the original instruction copy from hook_ptr. And we can do anything we want in hook handle function.

Save original bytes at hook_ptr

We will put a BL instruction at this address, so we need to save original bytes at first. In Thumb, a BL instruction occupies 4 bytes. So we need to save at least 4 bytes. It may include 1-2 Thumb instructions at hook_ptr, and we must be careful in selection of a hook_ptr.

  • Avoid odd hook_ptr
  • Do not select a hook_ptr at a middle of a Thumb instruction
  • Do not select a hook_ptr at a 2 bytes Thumb instruction followed by a 4 bytes Thumb instruction

I dissembled libMyGame.so with the following command:

1
arm-linux-gnueabihf-objdump -S /tmp/libMyGame.so | tee /tmp/libMyGame.s

And, there are 2 files named libMyGame.so in this game. We should use libMyGame.so file in the path lib/armeabi-v7a. Because this file is according to Thumb version. I selected hook_ptr at 0x267782:

26777e: 9a03 ldr r2, [sp, #12]
267780: 6823 ldr r3, [r4, #0]
267782: 4630 mov r0, r6 @ here, our hook_ptr
267784: 429a cmp r2, r3
267786: d001 beq.n 26778c <_ZN7cocos2d11Application10getVersionEv@@Base+0x3c>

This hook_ptr is in the function cocos2d::Application::getVersion. So when call this getVersion function, just like we did in previous post, CPU should hit this hook_ptr. Now, we saved original bytes 30 46 9a 42.
Note: arm-linux-gnueabihf-objdump displays instruction bytes from high address to low address. We also can use hexdump to show instruction byte, it’s in a clearer way. The command is:

1
hexdump -C -n 4 -s $((0x267782)) /tmp/libMyGame.so

The output should be as follows:

00267782 30 46 9a 42 |0F.B|
00267786

Write a hook handle function.

The prototype of the hook handle function is as follows, and it’s in the file, main.cpp

1
extern "C" void hook_fun(void* baseaddress, void* sp);

This function has 2 arguments. The 1st argument, baseaddress, is the base address of the libMyGame.so, and the 2nd argument, sp, is the value int register SP. Trampoline code will pass these 2 arguments to the hook handle function. baseaddress let us can access any data or function in libMyGame.so very easily. Trampoline code will store most CPU registers in the stack, so we can access these register values using sp.

Write trampoline code

This code is in Thumb assembly. I list the code in here, and I put the offset of instructions at the beginning of every line.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
0x0:	push	{r0, r1, r2, r3, r4, r5, r6, r7}
0x2: push.w {r8, sb, sl, fp, ip, lr}
0x6: mrs r0, apsr
0xa: push {r0}
0xc: nop
0xe: mov r1, sp
0x10: ldr r0, [pc, #0x14] @ --> 0x28
0x12: ldr r4, [pc, #0x18] @ --> 0x2c
0x14: blx r4
0x16: pop {r0}
0x18: msr cpsr_fc, r0
0x1c: pop.w {r8, sb, sl, fp, ip, lr}
0x20: pop {r0, r1, r2, r3, r4, r5, r6, r7}
0x22: nop
0x24: nop
0x26: bx lr
0x28: nop
0x2a: nop
0x2c: nop
0x2e: nop

0x0-0xa, save registers to stack, r0-r12, LR , and CPSR. We can learn more on ARM registers with this page.
0xe, set the 2nd argument,
0x10, set the 1nd argument, we load the 1st argument from 0x28, and we will put the base address of libMyGame.so at 0x28 using Frida.
0x12, load the address of the hook handle function to R4, and we will put hook_fun_ptr at 0x2c using Frida.
0x14, call hook handle function
0x16-0x20, load registers from stack.
0x22-0x24, run original instructions at here. We will put saved original bytes at here using Frida.
0x26, return
0x28-0x2a, We store the base address of libMyGame.so at here.
0x2c-0x2e, We store the address of hook handle function at here.
Whole trampoline code occupies 0x30 bytes.

About selection of trampoline_ptr

We can simple using Memory.alloc for trampoline code. Actually, we use near call instruction. This makes trampoline_ptr can not be too far from hook_ptr. We can find a cave to put our trampoline code in libMyGame.so. CAVE MINER is a great tool for this task. But I just do it manually for this sample case. I use the following command to list all segments in libMyGame.so:

1
readelf -l  /tmp/libMyGame.so

The following is the output snippet:

Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x00000034 0x00000034 0x00120 0x00120 R 0x4
INTERP 0x000154 0x00000154 0x00000154 0x00013 0x00013 R 0x1
[Requesting program interpreter: /system/bin/linker]
LOAD 0x000000 0x00000000 0x00000000 0x691630 0x691630 R E 0x1000
LOAD 0x691f38 0x00692f38 0x00692f38 0x36b0c 0x42fac RW 0x1000
DYNAMIC 0x6bc6c8 0x006bd6c8 0x006bd6c8 0x00150 0x00150 RW 0x4

The first load segment is end at 0x691630, and the second load segment is begin at 0x691f38. So cave at 0x691630 is perfect for our tempoline code.

Put a BL instruction at hook target.

We just use a near instruction to call trampoline code at hook_ptr, using thumbwriter in Frida for this.

Summary

I wrote a method in patchutils.js, it’s defined as follows:

1
export function putThumbHookPatch(trampoline_ptr:NativePointer, hook_ptr:NativePointer, hook_fun_ptr:NativePointer, para1:NativePointer, origin_inst?:number[]):number
  • The 1st argument, trampoline_ptr is the address of trampoline code
  • The 2nd argument, hook_ptr is the address of hook target
  • The 3rd argument, hook_fun_ptr is the address of our hook handle function
  • The 4th argument, para1 is the 1st parameter to our hook handle function, it will be assign to base address of libMyGame.so in caller
  • The 5th argument, origin_inst is the saved original bytes at hook_ptr
    And it returns the length of the trampoline code
    I also wrote a test method in inlinehooktest.ts.

Compile & run

I wrote a makefile for compilation.
We can use the following command to compile.

1
$ make build_inlinehooktest

And use the following command to run.

1
$ make run

If everything is OK, we can see the output as follows.

Hello World from so
cocos2d application version:
#################### Hook Begin ##############################
hook function from so
baseaddress 0xc8902000
CPSR 0x600e0010
R8 0x00000000
R9 0xbbdf8c18
R10 0xbbdf8b80
R11 0xbbdf8b90
R12 0xf3811ce8
LR 0xc8b69787
R0 0xbbdf8b70
R1 0x05c43127
R2 0x05c43127
R3 0x05c43127
R4 0xf3813260
R5 0xbbdf8b70
R6 0xdcc854b8
R7 0xbbdf8b88
#################### Hook end ##############################
1.8.12

The hook handle function just do the following,

  • print base address
  • dump values of all registers stored in the stack

Conclusion

This method still has many way to improve. And it only support Thumb. If you have any idea or issues, please feel free to let me know. I’m always glad to hack and discuss with others.
All code is in my github.

A new method to write CModule in Frida for hacking Android applications

Why a new method

I introduced how to use official CModule in Frida at last post. But I think it have some weaknesses to improve.

  • It can use C, this make us need to use ugly mangled function name when we want to call C++ functions
  • We need to embed C code in to Typescript code. So we can not find the error line quickly when C code does not pass compilation.

New method

In here, I will introduce a new method to write a CModule in Frida. Please note, this method only work with Android platform, and I only tested on Arm32 archtecture. In theory, Arm64 archtecture should work either. This method has the following stage:

  • Write a .so using Android NDK
  • Convert .so to a typescript module to hold all informations we needs when we want to load the .so
  • Load .so in Frida and call one function in it.
    I tried to load .so using Module.load(), but this method always failed on Android platform. I think it because the restrictions of new versions Android, I used Android 10.

Write a .so using Android NDK

First, we write a .so in C++, the following is the code in file main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//////////////////////////////////////////////////
// declaration of functions in frida
extern "C" void frida_log_callback(const char*);
extern "C" void frida_hexdump_callback(void*, unsigned int);

//////////////////////////////////////////////////
// declaration of functions in libMyGame.so
namespace cocos2d{
struct Application {
static Application* getInstance();
std::string& getVersion();
};
};


extern "C" void fun(void)
{
frida_log_callback("Hello World from so");
frida_log_callback("cocos2d application version:");
const std::string& version = cocos2d::Application::getInstance()->getVersion();
frida_log_callback(version.c_str());
return ;
}

Line 3-4, declares functions in Frida. And we need to add a options in our Android.mk

LOCAL_ALLOW_UNDEFINED_SYMBOLS := true

Without this options, we can not pass the link because the compiler can not find these functions.
Line 8-13, declares functions in libMyGame.so. We can use C++ syntax here, and make our code more readable. And we need to declare entire C++ class in libMyGame.so, just declare the functions we want to call.
Line 16-23, defines a function for Typescript code to call. It just does the same thing as we did in previous post, print out cocos2d application version string.
And we need to set correct options in Application.mk.

APP_STL := gnustl_static
APP_ABI=armeabi-v7a

If these options is not compatible with libMyGame.so, our function will crush. Frankly, I tried all APP_STL variables, and finally find only gnustl_static is compatible with this game.

Generate a Typescript file hold all information when loading

I wrote a python script. This python scripts use lief to parse .so file to get the following informations.

  • name, machine type
  • segments
    Only care about segments with load type, discard other segments
  • export symbols
  • relocations
  • ctors
    This is a list of functions, and we need to call these functions after loading.
  • dtors
    These functions should be called when unload the .so file, and I have not write code for it now.

This script generates final Typescript file using jinja2. The name of the template file is so2tsmodule.jinja

Load .so file

I wrote a method, named loadSo, in file soutils.ts. Method prototype as follows

1
export function loadSo(info:SoInfoType, syms?:{[key:string]:NativePointer}, libs?:string[]):LoadSoInfoType
  • info is a object we defined in the Typescript file we generated in the last stage.
  • syms is a list of symbols we pass to the load modules
  • libs is a list of library names. This method try to find symbols in these libraries.

We need to accomplish the following stages when load .so file:

  • Allocate buffer, and set the permission to rwx
  • Write all segments need to load to the buffer
  • Create a variable to hold all export symbols in the .so file
  • Handle relocations
    This stage is a little complicated. We need to find symbol addresses, and write correct number in the correct address accord to different recolcaion type.
  • Call ctors

Run the function in the .so file

The following is the Typescript code to do this. And it’s in file sotest.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let soname ="libMyGame.so"
let m = Process.getModuleByName(soname)
let loadm = soutils.loadSo(
mysoinfo.info,
{
frida_log_callback : fridautils.frida_log_callback,
frida_hexdump_callback : fridautils.frida_hexdump_callback,
},
[
soname,
]
);
console.log(JSON.stringify(loadm))

fridautils.runFunWithExceptHandling(()=>{
let fun = new NativeFunction(loadm.syms.fun,'void',[])
fun();
})

Line 3-12, loads the .so file.
We need to import generated Typescript file as a module using the following code

1
import * as mysoinfo from './mysoinfo'

Line 6-7 lists all functions defined in Frida and will be called in C++ code
Line 10 lists all libraries contained the functions will be called in C++ code
Line 16-17 calls function fun in the C++ code.

Compile & run

I wrote a makefile for compilation.
We can use the following command to compile.

1
$ make build_sotest

And use the following command to run.

1
$ make run

If everything is OK, we can see the output as follows

Hello World from so
cocos2d application version:
1.8.12

Conclusion

All code is in my github.

How to use CModule in Frida

In this article, I will try to introduce how to use CModule in Frida.
My test Android APK is a small game, named “Knife hit”, you can download it from APKpure website. This is a Cocos2d game, it has a native library named ‘libMyGame.so’. This library file has many game logic codes. It exports many functions. We will try to call one of them to get Cocos2d application version and print out it.

Why typescript?

Later version of Frida support typescript. And Frida provides a demo for starting of typescript coding. It use a NPM package, ‘frida-compile‘, for compiling of typescript code. Using typescript needs a compilation, it might take longer time to test the code, but we can avoid many issues in compilation. So I prefer typescript.

What is a CMoudule

This is its official documentation. In my option, we can write some functions in C, and Frida provides a way to invoke these functions. The following is the example in the official documentation.

1
2
3
4
5
6
7
8
9
10
11
12
const cm = new CModule(`
#include <stdio.h>

void hello(void) {
printf("Hello World from CModule\\n");
}
`);

console.log(JSON.stringify(cm));

const hello = new NativeFunction(cm.hello, 'void', []);
hello();

Line 1-7 create a CModule, it includes C code, and exports one function, ‘hello’ to print a welcome string.
Line 9 displays the information of the CModule. Frida is very smart, the C code should be compiled in here. And Frida should load the CModule in the memory space of the target process.
Line 11 convert function pointer to a NativeFunction for calling in Frida. So, cm.hello just is a NativePoiner.
Line 12 call hello function

Write our own CModule

Why not to use printf?

Typescript code and C code is actually run in the target process’ memory space. And Android process usually do not use printf. They use __android_log_print instead. But this function do not print message to Frida, we nee to run adb logcat to check its output. It’s not very convenience. We can write some functions in typescript to let C code to call.

Typescript functions for debugging

I wrote several helper functions in typescript, I put them in file fridautils.ts.

frida_log_callback

1
2
3
4
export let frida_log_callback = new NativeCallback(function(sp:NativePointer){
let s = sp.readUtf8String();
console.log(s)
},'void', ['pointer'])

This function has one argument of type NativePointer. C code passes a pointer to a string to this function.
Line 2 read a string from given address.
Line 3 outputs the string to Frida.

frida_log_cstring_callback

1
2
3
4
export let frida_log_cstring_callback = new NativeCallback(function(pString:NativePointer){
let s = pString.add(0x0).readPointer().readUtf8String(); // get string to a pointer to a std::string object
console.log(s)
},'void', ['pointer'])

Cocos2D stores the version string in a std::string. So we needs a function to extract the actual string from the std::string object and print it.
This function has only one argument, it also is of type NativePoiner, but it should be a pointer to a std::string object
Line 2 extracts the actual string from the given std::string object. Note: Android developers can specify different variable in their Application.mk file. Variables APP_ABI, APP_STL may determine different implementations of std::string class. So current method to extract a string may not work correctly when you hack other Android APKs.
Line 3 outputs extracted string to Frida.

Our own CModule

I wrote a CModule to show the Cocos2D application version string by using the functions has exist in libMyGame.so. I put code it file cmoduletest.ts.

Cocos2d functions for get version string

I read the official documentation in Cocos2d website. Class cocos2d::Application has 2 function we can use:

  • static Application* getInstance ()
    doc
    This function return a pointer to the current application instance.
  • const std::string& getVersion () const
    doc
    This function return a std::string reference to the version string. A C++ reference is a pointer in machine code actually.

Call Cocos2d functions

The following is the code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
let test0 = function() {
let soname ="libMyGame.so"
let m = Process.getModuleByName(soname)

let loadm = new CModule(`
void* _ZN7cocos2d11Application11getInstanceEv ();
const char* _ZN7cocos2d11Application10getVersionEv(void*);
extern void frida_log_callback(const char* s);
extern void frida_log_cstring_callback(void*);
void fun(void) {
frida_log_callback("Hello World from CModule");
void* pApplication = _ZN7cocos2d11Application11getInstanceEv();
const char* version = _ZN7cocos2d11Application10getVersionEv(pApplication);
frida_log_callback("cocos2d application version: ");
frida_log_cstring_callback((void*)version);
}
`,
{
frida_log_callback : fridautils.frida_log_callback,
frida_log_cstring_callback : fridautils.frida_log_cstring_callback,
// cocos2d::Application::getInstance
// use this static function to get a pointer to the current Application instance
_ZN7cocos2d11Application11getInstanceEv : m.getExportByName("_ZN7cocos2d11Application11getInstanceEv"),
// const char* cocos2d::Application::getVersion(void*)
// use this member function of class Application to get a version string
_ZN7cocos2d11Application10getVersionEv: m.getExportByName("_ZN7cocos2d11Application10getVersionEv"),
});

console.log(JSON.stringify(loadm));
fridautils.runFunWithExceptHandling(()=>{
let fun = new NativeFunction(loadm.fun, 'void',[])
fun();
})
}

Line 6-16 is the C code.
Line 6-7 declares the Cocos2d functions we will using. Because we are in C, so we only can use mangled function names.
Line 8-9 declares the helper functions we created in Typescript.
Line 10-16, we create a function named fun to do our actual work
Line 12, we get the current application instance address using function cocos2d::Application::getInstance, this function is a static function, and it has no argument, we store the result to variable pApplication;
Line 13, we get a pointer to a std::string object using function cocos2d::Application::getVersion, this function is a member function, and it needs a this pointer as its first argument. We just pass variable pApplication to it.
Line 14-15. We print version string using helper functions.
LIne 19-26. We needs to pass all functions to the CModule using a dictionary. In the dictionary, key is the function name, and value is the function address. We call Module.getExportByName to get the functions address in the libMyGame.so

Compile & run

I wrote a makefile for compilation.
We can use the following cmd to compile.

1
$ make build_cmoduletest

And use the following cmd to run.

1
$ make run

If everything is OK, we can see the output as follows

Hello World from CModule
cocos2d application version:
1.8.12
This game’s Cocos2d application version is 1.8.12

Conclusion

Now, we know how to call the existing functions using CModule. Cocos2D exports a lot of functions in its native library file. We can do many things using these functions.
All code is in my github

Frida Android basics

This article introduces the basics about how to use Frida on Android platform.

About Frida

Frida is a great tool working on Windows, macOS and GNU/Linux allowing you to inject snippets of code into native apps. In this tutorial we’ll use Frida to inject code into Android applications.

Frida has 2 work modes, one is client-server, the other is gadget mode.

Server mode

The server runs on the Android phone and the client on your computer. Note that you need a phone or an Android emulator that rooted.

Frida installation

Installing Frida on your computer is a super easy:

1
$ pip install frida-tools

Note that the latest Python 3 is recommended. Lets check which version is installed:

1
$ frida --version

And we need to find the architecture of you phone.

1
$ adb shell getprop ro.product.cpu.abi

Now, we need to install the server on our Android phone. Visit frida release page, and find a file named like “frida-server-XX.XX.XX-android-YYYY.xz”. ‘XX.XX.XX’ is the version of your installed frida, and YYYY is the architecture of your Android device. Note frida server version should be better to match the frida version on you computer.
We uncompress the archive and rename the server to “frida-server”
And install the server on the phone:

1
2
3
$ adb root # might be required
$ adb push frida-server /data/local/tmp/
$ adb shell "chmod 777 /data/local/tmp/frida-server"

Now try to start the server.

1
adb shell "/data/local/tmp/frida-server &"

Frida test

Run the following command to list all applications on your Android device:

1
frida-ps -Uai

Gadget mode

This mode needs not a rooted android device, but need to repackage APKs you wnat to hack.

Download Gadget file

Also visit frida release page, and find a file named like “frida-gadget-XX.XX.XX-android-YYYY.so.xz”. ‘XX.XX.XX’ is the version of your installed frida, and YYYY is the architecture of your Android device.
We uncompress the archive and rename the so to “libgadget.so”

Inject libgadget.so to your APK

Try to find any native library file in you APK, and inject libgadget.so to it.
The following is a Python script to inject libgadget.so, just add libgadget.so as a dependency of a native libraries embedded in the APK.

1
2
3
4
import lief # you can install lief package with `pip install lief`
libnative = lief.parse("libnative.so") # you should replace libnative.so to you actual so file name
libnative.add_library("libgadget.so") # Injection!
libnative.write("libnative.so")

Write a gadget config file

Create a file name with ‘libgadget.config.so’. and put the following content in it:

1
2
3
4
5
6
{
"interaction": {
"type": "listen",
"on_load": "wait"
}
}

And copy this file into same directory with your patched libnative.so in.

Repackage APK file

There are many APK repackage utils. I prefer APK easy Tool

Frida test

If you install patched APK to your Android device and open it.
The app seems it sucks, don’t be worry, it’s waiting for you to run frida client to connect it.
Run the following command, you will find a process with a name ‘Gadget’.

1
frida-ps -Uai

This process just is your patched APK, and we can not see other applications, because we’re not root now

Getting started with Frida

Write a javascript file you want to inject

The content of the test javascript file (named tt.js)

1
console.log('hello world')

Inject javascript code to an Android process

Frida provides serval utils to let us life easy. One of them is ‘frida’.
Run the following command to connect to your wait process in gadget mode

1
frida -U -n Gadget -l tt.js --no-pause 

You will see a frida shell, and it print ‘hello world’
For client/server mode, you should run frida with other options:

1
frida -U -f <package name> -l tt.js --no-pause  # this command will reopen your app

or

1
frida -U -n <app title> -l tt.js --no-pause  # this command do not reopen your app

Conclusion

Now we can inject a basic javascript code into an Android app, you can refer frida Javascript documentation for more learning.