Overriding Default Values of Android Constructor Parameters at Runtime

Posted on Oct 29, 2024

Ever found yourself needing to override default values within an Android class constructor without modifying the original codebase? Here, we’ll explore a solution using two powerful tools: Frida and Pine. These dynamic hooking libraries allow you to modify method parameters and manipulate their values at runtime, making testing, customization, and reverse engineering a breeze.

Note: For complete sample code and setup, check out the Fine GitHub Repo, which combines Frida and Pine with multi-architecture and rootless hooking support.


Tools Overview

Frida and Pine allow Android developers and researchers to hook into applications at runtime, creating a versatile environment to modify and analyze application behavior:

  • Frida is a dynamic instrumentation toolkit that allows us to inject our own scripts into processes. Hook any function, spy on crypto APIs or trace private application code, no source code needed.
  • Pine is an efficient, Dynamic java method hook framework on ART. Allowing you to change almost all java methods’ behavior dynamically.

Why Modify Constructor Values?

Modifying constructor values directly changes object properties before they are even used by the app. This can save significant effort when an object is instantiated multiple times in various places throughout an app.

Let’s take an example: you have a class SubscribeResponseDto where the values for isSubscribed and licenseType are set in the constructor. By modifying these values at the constructor level, you can bypass having to alter each place where they are accessed.


Step 1: Setting Up Frida

To begin, ensure you have Frida set up with a custom script. Here’s a sample Frida script that targets the SubscribeResponseDto class constructor, allowing us to modify its initial values.

Frida Script to Modify Constructor Values

  1. Launch the Script with the Frida CLI: I’m using frida-inject.

    ./inject -f com.nstreaming.app.example -s script.js
    
  2. JavaScript Code for the Hook:

    Java.perform(() => {
      const ActivityClass = Java.use("android.app.Activity");
    
      ActivityClass.onResume.implementation = function () {
        const currentActivity = this.getClass().getName();
    
        if (currentActivity === "com.example.app.main.HomeActivity") {
          try {
            const SubscribeResponseDto = Java.use(
              "com.example.module.account.dto.SubscribeResponseDto"
            );
    
            // Hook the constructor with custom values
            SubscribeResponseDto.$init.overload(
              "boolean",
              "java.lang.String",
              "boolean",
              "long",
              "long",
              "java.lang.String",
              "java.lang.String",
              "java.lang.String"
            ).implementation = function (
              isSubscribed,
              platform,
              isAutoRenew,
              expiresDate,
              startDate,
              productId,
              limitType,
              licenseType
            ) {
              // Modify values
              isSubscribed = true;
              licenseType = "Yearly";
              platform = "PlayStore";
    
              return this.$init(
                isSubscribed,
                platform,
                isAutoRenew,
                expiresDate,
                startDate,
                productId,
                limitType,
                licenseType
              );
            };
          } catch (e) {
            console.error("Error setting up hooks:", e);
          }
        }
        return this.onResume();
      };
    });
    

    Here, isSubscribed, licenseType, and platform values are overridden directly within the constructor, ensuring that every instance of SubscribeResponseDto uses these values.


Step 2: Setting Up Pine

For users who prefer Java-based solutions or encounter limitations with Frida, Pine is a powerful alternative for hooking. Pine allows you to hook methods in Android’s Java runtime, modify arguments, or alter almost all java methods’ behavior dynamically.

Pine Hook to Modify Constructor Values

In this example, we hook into the constructor of SubscribeResponseDto class and modify its arguments to achieve the same effect as with Frida.

Constructor<?> constructor = Class.forName("com.example.module.account.dto.SubscribeResponseDto")
    .getDeclaredConstructor(boolean.class, String.class, boolean.class, long.class, long.class, String.class, String.class, String.class);

Pine.hook(constructor, new MethodHook() {
    @Override
    public void beforeCall(Pine.CallFrame callFrame) throws Throwable {
        // Modify constructor arguments
        callFrame.args[0] = true;           // isSubscribed
        callFrame.args[1] = "PlayStore";    // platform
        callFrame.args[7] = "Yearly";       // licenseType
    }
});

In this hook, callFrame.args allows direct manipulation of arguments before they are passed into the constructor, giving you control over isSubscribed, platform, and licenseType.


Combining Frida and Pine with the Fine Project

The Fine repository showcases a project integrating Frida and Pine into an Android Studio project. Here’s what makes it stand out:

  • Rootless Hooking: No root access is needed, making it more versatile across devices.
  • Multi-Architecture Support: Automatically loads libraries based on the device architecture, covering arm64-v8a, armeabi-v7a, x86, and x86_64.
  • App Component Injection: Uses appComponentFactory to inject the hook loader library, enabling smooth integration with the app’s lifecycle.
  • Modularity: With support for both Frida Gadget and Pine, Fine offers flexibility for users to choose their preferred hooking method.

Credits and Resources

  • Pine Library by canyie – for its powerful hooking capabilities in Java.
  • Frida – for its extensive dynamic instrumentation capabilities.

Tha’s it! You now have a solid understanding of how to hook and modify constructor values using Frida and Pine. These techniques can be applied to various scenarios, allowing you to manipulate app behavior and explore the possibilities of Android app modification.

Remember to use these tools responsibly and ethically, always respecting user privacy and data security. Happy hooking!