Android App Penetration Testing & Frida Hooks

Your First Automated Frida Script: A Step-by-Step Guide for Android Pentesting Beginners

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction: The Power of Automated Frida in Android Pentesting

Frida is an indispensable dynamic instrumentation toolkit for penetration testers and reverse engineers. It allows you to inject custom scripts into running processes, modify behavior, and inspect runtime data. While manual Frida usage via the CLI is powerful, automating your Frida hooks with Python dramatically enhances efficiency, repeatability, and the complexity of tests you can perform, especially in Android application penetration testing.

This guide will walk you through setting up your environment, crafting a basic Frida hook, and then automating its injection and execution using a Python script. By the end, you’ll have a foundational understanding of how to build robust automation workflows for your Android pentesting engagements.

Prerequisites and Environment Setup

Before diving into automation, ensure you have the following:

  • An Android device or emulator (rooted is ideal for full access, but Frida works on non-rooted devices for user-installed apps).
  • Android Debug Bridge (ADB) installed and configured on your host machine.
  • Python 3 installed on your host machine.
  • A basic understanding of JavaScript (for Frida scripts) and Python.

1. Install Frida Tools on Your Host Machine

Install Frida’s command-line tools and Python bindings:

pip3 install frida-tools frida

2. Download and Push Frida Server to Android Device

Download the appropriate frida-server binary for your Android device’s architecture (e.g., arm64, x86_64) from the Frida releases page. Then, push it to your device and set permissions:

adb push /path/to/frida-server-<version>-android-<arch> /data/local/tmp/frida-server
adb shell "chmod 755 /data/local/tmp/frida-server"

3. Start Frida Server on Android Device

Start the server in the background on your device. You can verify it’s running by checking frida-ps -U on your host machine.

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

You can also use adb reverse tcp:27042 tcp:27042 if you need to access the Frida server from your host machine via network, but for direct USB connection, this is often handled automatically.

Identifying a Target Function for Hooking

For this tutorial, let’s assume we are targeting a hypothetical Android application named com.example.androidapp. We’ve performed some static analysis (e.g., with JADX or Ghidra) and identified a critical method, com.example.androidapp.MainActivity.checkPin(java.lang.String), which is responsible for verifying a user’s PIN.

Our goal is to bypass this PIN check by always making checkPin return true.

Crafting Your First Frida Hook (Manual Example)

Let’s create a simple JavaScript file, bypass_pin.js, to demonstrate the hook:

Java.perform(function() {
    var MainActivity = Java.use('com.example.androidapp.MainActivity');

    MainActivity.checkPin.implementation = function(pin) {
        console.log("[*] Original checkPin called with PIN: " + pin);
        // Always return true to bypass the PIN check
        return true;
    };

    console.log("[+] MainActivity.checkPin hook installed!");
});

To test this manually, you would run:

frida -U -f com.example.androidapp -l bypass_pin.js --no-pause

The -U flag connects to a USB device, -f spawns the application, -l loads our script, and --no-pause ensures the app starts immediately after injection.

Automating the Frida Hook with Python

Now, let’s convert this manual process into an automated Python script. This script will find the device, spawn the application, inject our JavaScript, and then attach to it.

Create a Python file, e.g., automate_frida.py:

import frida
import sys
import time

def on_message(message, data):
    if message['type'] == 'send':
        print(f"[*] {message['payload']}")
    elif message['type'] == 'error':
        print(f"[!] Error: {message['description']}")

# Our Frida JavaScript payload
# Using triple quotes for multiline string
js_code = """
Java.perform(function() {
    var MainActivity = Java.use('com.example.androidapp.MainActivity');

    MainActivity.checkPin.implementation = function(pin) {
        send("[*] Original checkPin called with PIN: " + pin);
        // Always return true to bypass the PIN check
        return true;
    };

    send("[+] MainActivity.checkPin hook installed!");
});
"""

package_name = "com.example.androidapp"

try:
    # Connect to the USB device
    device = frida.get_usb_device(timeout=10)
    print(f"[+] Connected to device: {device.name}")

    # Spawn the application
    pid = device.spawn(package_name)
    print(f"[+] Spawned {package_name} with PID: {pid}")

    # Attach to the spawned process
    session = device.attach(pid)
    print(f"[+] Attached to session for PID: {pid}")

    # Create and load the script
    script = session.create_script(js_code)
    script.on('message', on_message)
    script.load()
    print("[+] Frida script loaded successfully.")

    # Resume the spawned application
    device.resume(pid)
    print(f"[+] Resumed {package_name}.")

    # Keep the script running to receive messages
    print("[+] Keep the script running (Ctrl+C to stop)...")
    sys.stdin.read()

except frida.core.RPCException as e:
    print(f"[!] Frida RPC Error: {e}")
    print("[!] Ensure frida-server is running on your device and the app exists.")
except Exception as e:
    print(f"[!] An error occurred: {e}")
finally:
    print("[+] Detaching from process.")
    if 'session' in locals() and session:
        session.detach()
    print("[+] Script finished.")

How the Python Script Works:

  1. Import Frida: Imports the necessary `frida` library.
  2. on_message Function: This callback handles messages sent from your Frida JavaScript script (using send()). It’s crucial for seeing output from your hooks.
  3. js_code Variable: This holds our entire JavaScript hook as a multiline string. Notice we changed console.log to send in the JavaScript to communicate back to the Python host.
  4. frida.get_usb_device(): Connects to the first available USB-connected Android device.
  5. device.spawn(package_name): Launches the target application in a suspended state. It returns the Process ID (PID).
  6. device.attach(pid): Attaches to the newly spawned process.
  7. session.create_script(js_code): Creates a Frida script object from our JavaScript code.
  8. script.on('message', on_message): Registers our Python on_message function to receive communications from the JavaScript script.
  9. script.load(): Injects and executes the JavaScript code within the target process.
  10. device.resume(pid): Resumes the execution of the application, allowing our injected script to take effect.
  11. sys.stdin.read(): Keeps the Python script alive so it continues to receive messages from the Frida script until manually interrupted (Ctrl+C).
  12. Error Handling and Detach: Includes basic error handling and ensures the session is detached properly.

To run this script, simply execute it from your terminal:

python3 automate_frida.py

Practical Use Cases and Next Steps

This automated script serves as a powerful foundation. Here are some ideas for extending its capabilities:

  • Dynamic Argument Modification: Instead of just returning true, you could modify arguments passed to a function based on certain conditions.
  • Logging Sensitive Data: Log API keys, user input, or network traffic dynamically.
  • Multiple Hooks: Load multiple JavaScript files or define several hooks within a single script.
  • RPC Exports: Expose functions from your Frida script to be called directly from Python, enabling truly interactive and dynamic testing.
  • Automated Data Exfiltration: Combine with Python’s file I/O to automatically save logged data.
  • Handling Attach vs. Spawn: Modify the script to attach to already running processes, which is useful for long-running services.

Conclusion

Automating Frida hooks with Python dramatically streamlines Android penetration testing. You’ve learned how to set up your environment, craft a basic hook, and integrate it into a Python script for automated execution. This workflow is essential for building scalable and efficient testing routines, allowing you to focus on discovering vulnerabilities rather than repetitive manual interactions. Experiment with different hooks and expand on this foundation to unlock the full potential of automated dynamic instrumentation.

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