Hooking & Intercepting

📌What is hooking?

Hooking is the technique of intercepting and altering the functionality of functions or methods within an application or the Android operating system. For instance, by hooking a method in our app, we can modify its behavior by implementing our own logic.

📍How Frida Hooks into Apps

Frida can be injected into an app using two different approaches:

  1. Injected Mode (Recommended)

    • Runs frida-server on your device.

    • Allows you to attach to running apps without modifying the APK.

  2. Embedded Mode

    • Requires modifying the APK manually.

    • You must inject the Frida Gadget (.so file) into the APK, repack it, and resign it.

    • Useful for bypassing anti-debugging protections.

📍When to Run Your Script

Depending on when you want to start hooking the app, use the appropriate command:

  • If the app is already running (attach after objects are loaded):

    frida -U -n package_name
  • If you want to start the app from the beginning (Before app initialization):

    frida -U -f package_name -l script.js

📌What Does Interception Mean?

Interception is the process of hooking into a function at runtime to observe or modify its behavior. With Frida, this lets us:

  • See what arguments are passed to the function.

  • Modify those arguments before they’re used.

  • Run custom code when the function is called.

  • Change the return value before it’s given back to the app.

📍Basic Frida script

  • This is the skeleton we can use

    // Ensure the script runs inside the Java environment
    Java.perform(function () {
        // Store the target class in a variable
        var varName = Java.use("package_name.class_name");
    
        // Hook the onCreate method
        varName.onCreate.implementation = function (bundle) {
             // Log a message when the method is called
            console.log("onCreate called");
    
            // Call the original onCreate method to avoid breaking the app
            this.onCreate(bundle);
        };
    });
  • A short explanation of the functions

    • Java.perform(function() { ... })

      • Ensures that the script runs within the Java environment of the target app

    • Java.use("com.example.app.className")

      • Loads the specified Java class (className) from the target Android app so it can be manipulated.

    • .implementation is used to hook and modify a method’s behavior at runtime. It allows us to intercept method calls, log data, modify parameters, or even change the method's return value.

📍In Java, methods can be overloaded, meaning multiple methods can have the same name but different parameters. For example, an Android Activity class might have multiple onCreate methods:

public void onCreate() { ... }  // No parameters
public void onCreate(Bundle savedInstanceState) { ... }  // Takes a Bundle
public void onCreate(Bundle savedInstanceState, String extra) { ... }  // Two parameters

Since Frida needs to know exactly which method we are targeting, we use .overload(...) to specify the parameter types.

Java.perform(function () {
    var varName = Java.use("com.example.app.className");

    // Hook the onCreate method (assuming it takes a Bundle as a parameter)
    varName.onCreate.overload("android.os.Bundle").implementation = function (bundle) {
        console.log("onCreate called");
        this.onCreate(bundle);
    };
});

📍Modifying a Method's Return Value

This script hooks into the randomDice() method of the DiceGameFragment class and changes its return value to 5.

// Run the script within the Java VM context
Java.perform(function () {

    // 1. Get a reference to the target class
    var DiceGameFragment = Java.use("io.hextree.fridatarget.ui.DiceGameFragment");

    // 2. Hook the 'randomDice' method
    DiceGameFragment.randomDice.implementation = function () {
        // Optional: Call the original method
        this.randomDice();

        // 3. Return a fixed value instead of the original result
        return 5;
    };
});