Android Software Reverse Engineering & Decompilation

Dynamic Hooking for Android Apps: A Practical Guide to Frida Gadget Injection

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction

Dynamic instrumentation has revolutionized the way security researchers, reverse engineers, and developers analyze and debug Android applications. Frida, a powerful dynamic instrumentation toolkit, stands at the forefront of this methodology. While Frida Server is commonly used for rooted devices, its counterpart, Frida Gadget, offers an indispensable solution for instrumenting applications on non-rooted devices or in scenarios where modifying the device’s system is not feasible or desired. This guide provides a practical, step-by-step walkthrough on how to inject and leverage Frida Gadget within an Android application to perform dynamic hooking.

1. The ‘Why’ of Frida Gadget

Frida Server vs. Frida Gadget

Traditionally, Frida operates with a client-server architecture. A Frida Server runs on the target Android device (often requiring root privileges to function fully), and a Frida client on the host machine communicates with it to inject scripts into running processes. While effective, this approach has limitations:

  • Requires a rooted device or specific configurations for non-rooted devices (e.g., `frida-inject` with `run-as`).
  • The server is a separate process, potentially detectable.

Frida Gadget, on the other hand, is a self-contained native library (`.so` file) that can be embedded directly into an application. When the application loads, the Gadget initializes Frida’s instrumentation engine within the application’s own process space. This makes it ideal for:

  • Non-rooted device analysis.
  • Bypassing root detection mechanisms.
  • Distributing instrumented apps for testing without requiring server setup on the device.
  • Situations where a persistent, hidden instrumentation is needed.

2. Prerequisites and Setup

Before diving into the injection process, ensure you have the following tools set up on your host machine:

  • Android Debug Bridge (ADB): For interacting with your Android device or emulator.
  • Java Development Kit (JDK): Required for `jarsigner` and `keytool`.
  • Apktool: For decompiling and recompiling Android application packages (APKs). Download it from Apktool’s official site.
  • Frida Tools: Install the Python client (`pip install frida-tools`).
  • A Target APK: For this tutorial, you can use any benign APK.

Setting up ADB

Ensure ADB is correctly installed and your device/emulator is recognized. You can verify this by running:

adb devices

This should list your connected devices.

3. Preparing Your Target Application

Obtaining and Decompiling the APK

First, get the APK of the target application. If it’s installed on your device, you can pull it:

# Find the package name of the app (e.g., com.example.myapp)adb shell pm list packages -f | grep your.app.name# Pull the APK (replace with actual path and package name)adb pull /data/app/~~XYZ/com.example.myapp-ABC==/base.apk

Once you have the APK, use Apktool to decompile it:

apktool d base.apk -o myapp_frida

This will create a directory named `myapp_frida` containing the decompiled resources and Smali code.

Identifying the Injection Point: AndroidManifest.xml

The primary injection point for Frida Gadget is the `AndroidManifest.xml` file. Inside the decompiled directory (`myapp_frida`), locate this file. We will modify the <application> tag to load our native library. If the app uses a custom Application class, we’ll leverage that. Otherwise, we can define one.

4. Injecting Frida Gadget

Downloading the Gadget

Download the appropriate Frida Gadget `.so` file for your target device’s architecture (e.g., `frida-gadget.so` for `arm64`, `arm`, `x86`, `x86_64`). You can find them on the Frida releases page. Make sure to download the `frida-gadget-*.so` that matches your target’s CPU architecture.

# Example for ARM64wget https://github.com/frida/frida/releases/download/16.1.4/frida-gadget-16.1.4-android-arm64.so -O frida-gadget.so

Placing the Gadget

Create the necessary native library directories within your decompiled app’s `lib` folder. For example, if your target is `arm64`, create `myapp_frida/lib/arm64-v8a/` and place `frida-gadget.so` inside it. Repeat for other architectures if they exist in the original APK or if you want broader compatibility.

mkdir -p myapp_frida/lib/arm64-v8a/cp frida-gadget.so myapp_frida/lib/arm64-v8a/

Modifying AndroidManifest.xml

Open `myapp_frida/AndroidManifest.xml`. Locate the <application> tag. We need to instruct the Android system to load our library. The simplest way is to create a custom `Application` class that loads the gadget. If the app already defines a custom `android:name` for its application class, you’ll need to either hook into that class’s `onCreate` or subclass it. For this tutorial, we’ll assume a new custom class named `com.example.frida.FridaApplication`.

First, create a new Smali file: `myapp_frida/smali/com/example/frida/FridaApplication.smali` with the following content:

.class public Lcom/example/frida/FridaApplication; .super Landroid/app/Application; .method static constructor <clinit>()V .registers 0 invoke-static {"frida-gadget"} Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V return-void .end method .method public constructor <init>()V .registers 1 invoke-direct {p0}, Landroid/app/Application;-><init>()V return-void .end method .method public onCreate()V .registers 1 .prologue invoke-super {p0}, Landroid/app/Application;->onCreate()V return-void .end method

Now, modify `AndroidManifest.xml`. Add or update the `android:name` attribute of the <application> tag to point to our new class. Also, ensure `android:extractNativeLibs` is set correctly. For modern apps, `false` is often required as libraries might be uncompressed directly from the APK. For communication, `android:usesCleartextTraffic=”true”` might be needed for network-based Frida connections, though typically not for Gadget which defaults to listen on localhost.

<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme" android:name="com.example.frida.FridaApplication" android:extractNativeLibs="true" android:usesCleartextTraffic="true">    <!-- Original activities and components --></application>

Note: `android:extractNativeLibs=”true”` is often required for older Android versions or when `System.loadLibrary` is used after extraction. For API 23+, uncompressed native libraries within the APK (when `android:extractNativeLibs=”false”`) are preferred for faster loading. Choose based on your target API level.

5. Rebuilding and Signing the APK

Rebuilding with Apktool

Navigate back to the directory containing your `myapp_frida` folder and rebuild the APK:

apktool b myapp_frida -o myapp_frida_instrumented.apk

This will compile the Smali code and repackage all resources, including our injected Frida Gadget and modified `AndroidManifest.xml`.

Signing the Modified APK

Android requires all APKs to be digitally signed. Since we modified the original APK, its signature is invalid. We need to sign it with a new key. If you don’t have one, generate a keystore:

keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000

Now, sign your `myapp_frida_instrumented.apk`:

jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore myapp_frida_instrumented.apk alias_name

Finally, optimize the APK using `zipalign` (comes with Android SDK build tools), which is crucial for proper installation and performance:

zipalign -v 4 myapp_frida_instrumented.apk myapp_frida_instrumented_signed_aligned.apk

Or, for modern Android versions, `apksigner` is preferred:

# Assuming apksigner is in your PATH (e.g., Android/sdk/build-tools/<version>/)apksigner sign --ks my-release-key.keystore --ks-key-alias alias_name myapp_frida_instrumented.apk

6. Running and Hooking the Instrumented App

Uninstall the original app if it’s installed, then install your newly signed and aligned APK:

adb uninstall com.example.myapp # Replace with original package nameadb install myapp_frida_instrumented_signed_aligned.apk

Launch the application on your device. The embedded Frida Gadget will now be active within the app’s process.

Writing Your First Frida Script

Frida Gadget, by default, listens on port 27042 on `localhost`. You can connect to it using the Frida client on your host machine, forwarding the port if necessary. First, set up ADB port forwarding:

adb forward tcp:27042 tcp:27042

Now, you can write a simple Frida script (e.g., `hook.js`) to test the injection. Let’s try to hook `android.widget.Toast` to see all toast messages.

Java.perform(function() {    console.log("Frida Gadget is running!");    var Toast = Java.use("android.widget.Toast");    Toast.makeText.overload('android.content.Context', 'java.lang.CharSequence', 'int').implementation = function(context, text, duration) {        var message = text.toString();        console.log("[Toast Message]: " + message);        return this.makeText(context, text, duration);    };    Toast.makeText.overload('android.content.Context', 'int', 'int').implementation = function(context, resId, duration) {        var resourceName = context.getResources().getResourceName(resId);        console.log("[Toast Resource]: " + resourceName);        return this.makeText(context, resId, duration);    };});

To connect and inject the script:

frida -H 127.0.0.1:27042 -f com.example.myapp -l hook.js --no-pause

Replace `com.example.myapp` with your target app’s package name. The `–no-pause` flag ensures the script starts executing immediately once the app launches or attaches.

7. Advanced Considerations and Troubleshooting

Architectural Challenges

Always ensure you’re using the correct `frida-gadget.so` for your target architecture (e.g., `arm64-v8a`, `armeabi-v7a`). Incorrect architecture will lead to runtime crashes.

Obfuscation

Highly obfuscated apps can make identifying the correct Smali code and package names challenging. Tools like Jadx or Ghidra can help in understanding the code structure.

Gadget Configuration

Frida Gadget can be configured via a `frida-gadget.config.json` file placed next to the `.so` file. This allows you to change the listening port, auto-connect to a remote Frida server, or log output. For example:

{  "interaction": {    "type": "listen",    "address": "0.0.0.0:27047",    "on_port_conflict": "fail"  }}

Troubleshooting

  • If the app crashes on startup: Double-check `AndroidManifest.xml` for syntax errors, ensure the correct `frida-gadget.so` architecture is used, and verify `android:extractNativeLibs` setting.
  • No Frida output: Ensure ADB port forwarding is active, the app is running, and your `frida` command specifies the correct package name and port. Check `adb logcat` for any errors related to library loading.

Conclusion

Frida Gadget offers a robust and flexible method for dynamic instrumentation of Android applications, particularly valuable for non-rooted environments. By understanding the process of injecting, rebuilding, and communicating with the gadget, you unlock powerful capabilities for reverse engineering, security analysis, and application debugging. This technique empowers you to gain deep insights into application runtime behavior, even in challenging scenarios.

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