Overriding Default Values of Android Constructor Parameters at Runtime
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
-
Launch the Script with the Frida CLI: I’m using
frida-inject
../inject -f com.nstreaming.app.example -s script.js
-
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
, andplatform
values are overridden directly within the constructor, ensuring that every instance ofSubscribeResponseDto
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
, andx86_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!