Hacktricks-skills android-frida-pentest

Use this skill whenever you need to perform dynamic analysis, hooking, or instrumentation on Android applications using Frida. Trigger this for any Android app security testing, reverse engineering, DEX dumping, anti-debugging bypass, runtime manipulation, or mobile pentesting tasks. Make sure to use this skill when the user mentions Android app analysis, Frida, dynamic instrumentation, hooking Java methods, DEX dumping, FLAG_SECURE bypass, or any mobile security testing.

install
source · Clone the upstream repo
git clone https://github.com/abelrguezr/hacktricks-skills
manifest: skills/mobile-pentesting/android-app-pentesting/frida-tutorial/frida-tutorial/SKILL.MD
source content

Android Frida Pentesting Skill

A comprehensive guide for dynamic Android application analysis using Frida instrumentation.

Quick Start

Install Frida Tools

pip install frida-tools
pip install frida

Setup Frida Server on Android

Rooted device (one-liner):

adb root; adb connect localhost:6000; sleep 1; adb push frida-server /data/local/tmp/; adb shell "chmod 755 /data/local/tmp/frida-server"; adb shell "/data/local/tmp/frida-server &"

Verify installation:

frida-ps -U  # List all processes
frida-ps -U | grep -i <package_name>  # Find specific app

Basic Hooking Commands

# Spawn app with hooks (hooks before onCreate)
frida -U --no-pause -l script.js -f com.example.app

# Attach to running app
frida -U -n com.example.app -l script.js

# List installed apps
frida-ps -Uai

Frida Gadget (No-Root Option)

When you don't have root access, bundle Frida Gadget inside the APK:

Manual Gadget Integration

  1. Unpack APK:
apktool d app.apk -o app_modified
  1. Add Gadget files:
  • Place
    libfrida-gadget.so
    in
    lib/<abi>/
    (e.g.,
    lib/arm64-v8a/
    )
  • Create
    assets/frida-gadget.config
    :
{
  "interaction": { "type": "script", "path": "/sdcard/hook.js" },
  "runtime": { "logFile": "/sdcard/frida-gadget.log" }
}
  1. Repack and sign:
apktool b app_modified -o app_gadget.apk
uber-apk-signer -a app_gadget.apk -o out_signed
adb install -r out_signed/app_gadget-aligned-debugSigned.apk

Automated with Objection

objection patchapk -s app.apk \
  -c gadget-config.json \
  -l agent.js \
  --use-aapt2

Common Hooking Patterns

Hook Functions Without Parameters

Java.perform(function () {
  var targetClass = Java.use("com.example.ClassName");
  targetClass.methodName.overload().implementation = function () {
    console.log("[+] methodName called");
    return false; // Return custom value
  };
});

Hook Functions With Parameters

Java.perform(function () {
  var targetClass = Java.use("com.example.ClassName");
  
  targetClass.methodName.overload("java.lang.String", "int").implementation = function (arg1, arg2) {
    console.log("[+] Input arg1: " + arg1);
    console.log("[+] Input arg2: " + arg2);
    
    var result = this.methodName(arg1, arg2);
    console.log("[+] Output: " + result);
    return result;
  };
});

Hook Activity Lifecycle Methods

Java.perform(function () {
  var MainActivity = Java.use("com.example.MainActivity");
  
  MainActivity.onCreate.overload("android.os.Bundle").implementation = function (bundle) {
    console.log("[+] MainActivity.onCreate() called");
    return this.onCreate(bundle);
  };
  
  MainActivity.onStart.overload().implementation = function () {
    console.log("[+] MainActivity.onStart() called");
    return this.onStart();
  };
});

Intercept Decryption Functions

function bytesToString(data) {
  var result = "";
  for (var i = 0; i < data.length; i++) {
    result += String.fromCharCode(data[i]);
  }
  return result;
}

Java.perform(function () {
  var cryptoClass = Java.use("com.example.Crypto");
  
  cryptoClass.decrypt.overload("[B", "[B").implementation = function (key, encrypted) {
    console.log("[+] Key: " + bytesToString(key));
    console.log("[+] Encrypted: " + bytesToString(encrypted));
    
    var decrypted = this.decrypt(key, encrypted);
    console.log("[+] Decrypted: " + bytesToString(decrypted));
    return decrypted;
  };
});

Find and Inspect Object Instances

Java.choose("com.example.ClassName", {
  onMatch: function (instance) {
    console.log("[+] Found instance: " + instance);
    console.log("[+] Private field: " + instance.privateMethod());
  },
  onComplete: function () {
    console.log("[+] Search complete");
  }
});

Prevent App Exit

Java.perform(function () {
  var System = Java.use("java.lang.System");
  
  System.exit.overload("int").implementation = function (code) {
    console.log("[!] App tried to exit with code: " + code);
    // Don't call original - app stays running
    return;
  };
});

Anti-Debugging Bypass

Disable Root Detection

Java.perform(function () {
  var Build = Java.use("android.os.Build");
  var SystemProperties = Java.use("android.os.SystemProperties");
  
  // Fake fingerprint
  Build.FINGERPRINT.get = function() { return "generic/generic/generic"; };
  Build.HARDWARE.get = function() { return "generic"; };
  Build.MODEL.get = function() { return "Android SDK built for x86"; };
  Build.MANUFACTURER.get = function() { return "Google"; };
  
  // Block SystemProperties.get
  SystemProperties.get.overload("java.lang.String").implementation = function (key) {
    if (key.contains("ro.debuggable") || key.contains("ro.secure")) {
      return "0";
    }
    return this.get(key);
  };
});

Block SIGSEGV Handlers

Interceptor.attach(Module.findExportByName("libc.so", "sigaction"), {
  onEnter: function (args) {
    if (args[1] !== null) {
      console.log("[+] sigaction called - blocking anti-debug");
      args[1] = null;
    }
  }
});

DEX Dumping with clsdumper

# Install
pip install clsdumper

# Attach to running app
clsdumper com.example.app

# Spawn with hooks before early loaders
clsdumper com.example.app --spawn

# Use specific strategies
clsdumper com.example.app --strategies fart_dump,oat_extract,memory_scan

# Deep scan (slower, more thorough)
clsdumper com.example.app --deep-scan

# Extract classes to smali
clsdumper com.example.app --extract-classes

JDWP Injection (Debuggable Apps, No Root)

For apps with

android:debuggable="true"
:

# Clone and run
python frida-jdwp-loader.py frida -n com.example.app

# Keep breakpoint suspended for early hooks
python frida-jdwp-loader.py frida -n com.example.app -s

# Load script directly
python frida-jdwp-loader.py frida -n com.example.app -i script -l hook.js

Useful Scripts

See the

scripts/
directory for ready-to-use Frida scripts:

  • clear-flag-secure.js
    - Remove FLAG_SECURE to enable screenshots
  • basic-hook-template.js
    - Template for common hooking patterns
  • setup-frida-server.sh
    - Automated frida-server deployment

Best Practices

  1. Spawn vs Attach: Use

    --spawn
    (
    -f
    ) to hook before
    onCreate()
    for early initialization hooks. Use attach for running apps.

  2. Multi-strategy DEX dumping: Hardened apps load code from multiple sources. Use clsdumper with default strategies plus

    --spawn
    for maximum coverage.

  3. Frida 17+ Java Bridge: If your agent hooks Java, include the Java bridge:

npm install frida-java-bridge
npm run build  # Creates bundled _agent.js
  1. Stealth: For Gadget on hardened apps, use obfuscated names and conditional loading to avoid detection.

  2. UI Thread Hooks: For window manipulation, schedule on main thread to avoid flicker:

Java.scheduleOnMainThread(function () {
  // UI operations here
});

References