| name | frida-android-pentesting |
| description | How to use Frida for Android app security testing and reverse engineering. Use this skill whenever the user needs to hook Android Java methods, intercept function calls, modify runtime behavior, find class instances, or create Python-Frida automation scripts. Trigger for any Android pentesting task involving Frida, Java method hooking, runtime instrumentation, or dynamic analysis of APKs. |
Frida Android Pentesting
A skill for performing dynamic analysis and security testing on Android applications using Frida.
Core Concepts
Frida is a dynamic instrumentation toolkit that lets you inject JavaScript into running processes. For Android, this means you can hook Java methods, intercept calls, modify behavior, and extract data at runtime.
Quick Start
Basic Setup
-
Install Frida on your machine:
pip install frida frida-tools
-
Install Frida Server on the Android device:
adb push frida-server-<version>-android-x86 /data/local/tmp/frida-server
adb shell chmod 755 /data/local/tmp/frida-server
adb shell /data/local/tmp/frida-server &
-
Connect and spawn the target app:
frida-ps -U
frida -U -f com.example.targetapp -l hook.js -H 127.0.0.1:27042
Hooking Patterns
Pattern 1: Hook Methods with Overloads
When a method has multiple overloads (same name, different parameters), you must specify which one to hook:
Java.perform(function() {
var myClass = Java.use("com.example.target.MyClass");
myClass.methodName.overload("int", "int").implementation = function(a, b) {
console.log("Hooked: methodName(" + a + ", " + b + ")");
return this.methodName(10, 20);
};
myClass.methodName.overload("java.lang.String").implementation = function(str) {
console.log("Original string: " + str);
return this.methodName("modified value");
};
});
Pattern 2: Find and Call Class Instances
Use Java.choose() to find instances of a class and call methods on them:
Java.perform(function() {
Java.choose("com.example.target.MyClass", {
onMatch: function(instance) {
console.log("Found instance: " + instance);
var result = instance.privateMethod();
console.log("Result: " + result);
},
onComplete: function() {
console.log("Search complete");
}
});
});
Pattern 3: Create New Objects
To create new Java objects in Frida:
Java.perform(function() {
var stringClass = Java.use("java.lang.String");
var newString = stringClass.$new("My custom string");
var myClass = Java.use("com.example.target.MyClass");
var instance = myClass.$new();
});
Pattern 4: Expose Functions to Python
Use rpc.exports to make JavaScript functions callable from Python:
function myHookFunction() {
Java.perform(function() {
var myClass = Java.use("com.example.target.MyClass");
myClass.secretMethod.overload().implementation = function() {
return "hacked value";
};
});
}
function callInstanceMethod() {
Java.perform(function() {
Java.choose("com.example.target.MyClass", {
onMatch: function(instance) {
console.log(instance.privateMethod());
}
});
});
}
rpc.exports = {
myHookFunction: myHookFunction,
callInstanceMethod: callInstanceMethod
};
Pattern 5: Python-JS Communication
Bidirectional communication using send() and recv():
JavaScript side:
Java.perform(function() {
var textViewClass = Java.use("android.widget.TextView");
textViewClass.setText.overload("java.lang.CharSequence").implementation = function(text) {
send(text.toString());
var response = recv(function(msg) {
return msg.modified_data;
}).wait();
console.log("Received: " + response);
return this.setText(response);
};
});
Python side:
import frida
import base64
import time
def message_handler(message, data):
if message["type"] == "send":
payload = message["payload"]
modified = payload.replace("old", "new")
script.post({"modified_data": modified})
device = frida.get_usb_device()
pid = device.spawn(["com.example.target"])
device.resume(pid)
time.sleep(1)
session = device.attach(pid)
script = session.create_script(open("hook.js").read())
script.on("message", message_handler)
script.load()
Python Loader Template
Use this as a starting point for Frida automation:
import frida
import time
import sys
def main():
if len(sys.argv) < 2:
print("Usage: python loader.py <package_name>")
sys.exit(1)
package_name = sys.argv[1]
device = frida.get_usb_device()
pid = device.spawn([package_name])
device.resume(pid)
time.sleep(2)
session = device.attach(pid)
script = session.create_script(open("hook.js").read())
def message_handler(message, data):
print(f"[+] {message}")
script.on("message", message_handler)
script.load()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\n[!] Stopping...")
script.unload()
if __name__ == "__main__":
main()
Common Use Cases
Intercepting Sensitive Data
Java.perform(function() {
var prefs = Java.use("android.content.SharedPreferences");
prefs.getString.overload("java.lang.String", "java.lang.String").implementation = function(key, def) {
console.log("SharedPreferences.get: " + key);
return this.getString(key, def);
};
var urlConnection = Java.use("java.net.HttpURLConnection");
urlConnection.connect.overload().implementation = function() {
console.log("URL: " + this.getURL());
return this.connect();
};
});
Bypassing SSL Pinning
Java.perform(function() {
var X509TrustManager = Java.use("javax.net.ssl.X509TrustManager");
var trustAll = {
checkClientTrusted: function(chain, authType) {},
checkServerTrusted: function(chain, authType) {},
getAcceptedIssuers: function() { return []; }
};
var TrustManagerImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl");
TrustManagerImpl.checkServerTrusted.overload(
"[Ljavax.net.ssl.X509Certificate;", "java.lang.String"
).implementation = function(chain, authType) {
trustAll.checkServerTrusted(chain, authType);
};
});
Logging All Method Calls
Java.perform(function() {
var myClass = Java.use("com.example.target.MyClass");
for (var method in myClass) {
if (typeof myClass[method] === 'object') {
myClass[method].overload().implementation = function() {
console.log("Called: " + method + " with args: " + arguments);
return myClass[method].overload().apply(this, arguments);
};
}
}
});
Best Practices
- Always use
Java.perform() - Ensures code runs on the Java VM thread
- Add
time.sleep(1) in Python - Java.perform silently fails without it
- Handle overloads explicitly - Don't assume single overload
- Use
this.methodName() - To call the original method
- Store instances in arrays - For repeated access to found instances
- Use
rpc.exports - For interactive control from Python
- Test on debug builds first - Release builds may have obfuscation
Troubleshooting
| Issue | Solution |
|---|
Java.perform silently fails | Add time.sleep(1) after device.resume() |
Class not found | Check package name, use frida-ps -U to verify process |
Method not found | Use frida-trace to discover method signatures |
Script won't load | Check Frida server is running on device |
Connection refused | Verify device is connected via USB and ADB works |
Tools
frida-ps - List processes
frida-trace - Trace function calls
frida-ls - List classes and methods
frida-decrypt - Decrypt obfuscated strings
References