Android Software Reverse Engineering & Decompilation

Hands-on Frida: Unveiling Hidden Android Native API Calls via Targeted Interceptors

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction to Frida and Native Android Reversing

Frida, the dynamic instrumentation toolkit, stands as an indispensable tool in the arsenal of any serious Android reverse engineer. While many focus on Java-level hooks, the true power of an Android application, especially those designed for performance, security, or obfuscation, often lies deep within its native libraries (.so files). These libraries, typically written in C/C++ and interacting with Java through the Java Native Interface (JNI), house critical logic, cryptographic operations, and anti-tampering mechanisms that are invisible to Java-only analysis.

Targeting native API calls allows us to bypass Java obfuscation, understand low-level operations, and even interact with system libraries that are not directly exposed to the Java layer. This guide will walk you through setting up your environment, identifying target functions, and crafting advanced Frida scripts to precisely intercept these elusive native calls.

Setting Up Your Android Reversing Environment

Prerequisites

  • Rooted Android Device or Emulator: Necessary for running frida-server with full permissions.
  • ADB (Android Debug Bridge): For communication with your device.
  • Frida-Tools: The Python client for Frida. Install via pip install frida-tools.
  • Frida-Server: The agent running on the Android device. Download the correct architecture (e.g., arm64, x86) from Frida Releases.
  • A Sample Native Application: An Android app that uses a native library. You can build a simple Android Studio NDK project or use a target application.
  • A Decompiler/Disassembler: Tools like Ghidra or IDA Pro are invaluable for analyzing native binaries and identifying function offsets.

Installing and Running Frida-Server

First, push the downloaded frida-server binary to your device and make it executable. Ensure you pick the correct version (e.g., frida-server-*-android-arm64).

adb push /path/to/frida-server-16.x.x-android-arm64 /data/local/tmp/frida-server
adb shell "chmod 755 /data/local/tmp/frida-server"
adb shell "/data/local/tmp/frida-server &"

Verify that Frida is running by listing processes:

frida-ps -U

If you see a list of processes, your setup is correct.

Identifying Native Functions for Targeted Interception

Initial Reconnaissance with readelf and nm

Before dynamic analysis, static analysis helps identify potential targets. You can pull the target application’s native library from the device and use tools like readelf or nm to list exported symbols.

# Find the .so library path (replace with your app's package name and library name)
adb shell "find /data/app/ -name 'libnative-lib.so'"

# Example pull command
adb pull /data/app/~~com.example.appname-XYZ/lib/arm64/libnative-lib.so .

# List exported symbols
readelf -s libnative-lib.so | grep -E "FUNC|GLOBAL"
nm -D libnative-lib.so

This will show you functions explicitly exported by the library, often JNI functions like Java_com_example_app_MainActivity_stringFromJNI, or other utility functions that the library author chose to make public.

Decompilers: Ghidra and IDA Pro

For unexported functions, or to understand the internal logic and calling conventions of any function, a decompiler is essential. Load your .so file into Ghidra or IDA Pro. These tools will help you:

  • Identify unexported (internal) functions.
  • Determine function signatures (number and types of arguments, return type).
  • Understand the control flow and logic.
  • Pinpoint exact offsets of functions relative to the library’s base address.

Crafting Your First Frida Native Hook

Hooking a Simple, Exported Function

Let’s start with a common scenario: hooking a JNI function. Consider a simple Android NDK project with a native method:

// native-lib.cpp
#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myapp_MainActivity_stringFromJNI(JNIEnv* env, jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

A basic Frida script can intercept this using Module.findExportByName:

// hook_jni.js
Java.perform(function() {
    var targetLib = "libnative-lib.so";
    var targetFunction = "Java_com_example_myapp_MainActivity_stringFromJNI";

    var funcPtr = Module.findExportByName(targetLib, targetFunction);

    if (funcPtr) {
        console.log("Found " + targetFunction + " at " + funcPtr);
        Interceptor.attach(funcPtr, {
            onEnter: function (args) {
                console.log("[*] " + targetFunction + " called!");
                // args[0] is JNIEnv*, args[1] is jobject (this)
                // For JNI methods, the actual arguments start from args[2]
            },
            onLeave: function (retval) {
                console.log("[*] " + targetFunction + " returned: " + new NativePointer(retval).readUtf8String());
            }
        });
        console.log("Hooked " + targetFunction + " successfully.");
    } else {
        console.log("Error: " + targetFunction + " not found in " + targetLib);
    }
});

Run this script using:

frida -U -f com.example.myapp --no-pause -l hook_jni.js

Advanced Interception: Unveiling Unexported or Obscure Native Calls

Understanding Library Base Addresses and Offsets

Native libraries on Android are loaded into memory at different base addresses each time an application starts (due to ASLR – Address Space Layout Randomization). To hook an unexported function, we need its address in memory, which is calculated as: Base Address of Library + Function Offset.

Frida can find the base address of a loaded module:

// find_base_address.js
Java.perform(function() {
    var lib_module = Process.findModuleByName("libmycustomlib.so");
    if (lib_module) {
        console.log("libmycustomlib.so base address: " + lib_module.base);
        console.log("libmycustomlib.so size: " + lib_module.size);
    } else {
        console.log("libmycustomlib.so not found!");
    }
});

The

Android Mobile Specs & Compare Directory

Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!

Compare Devices Specs →
Google AdSense Inline Placement - Content Footer banner