
Frida
Frida is the dynamic instrumentation toolkit I use on almost every mobile engagement. This is a working reference for installation, attaching to processes, writing hooks, and the patterns I reach for most.
What Frida Is
Frida injects a JavaScript engine into a target process and gives you a bridge to call into the target's runtime. You can read and write memory, hook functions, replace return values, dump arguments, and trace anything that runs.
It works on Android, iOS, Linux, macOS, Windows, and QNX. On mobile, it is the single most useful tool I own.
Installation
# Host side
pip install frida-tools
# Verify
frida --versionFor Android, push the matching frida-server binary to the device:
# Find the right version for your frida-tools install
frida --version
# Download from https://github.com/frida/frida/releases
# Push and run
adb push frida-server-16.x.x-android-arm64 /data/local/tmp/frida-server
adb shell "chmod 755 /data/local/tmp/frida-server"
adb shell "su -c /data/local/tmp/frida-server &"For iOS, install Frida from the frida.re repo in Sileo or Cydia. It runs as a launch daemon automatically.
Attaching to a Process
# List running processes on the connected device
frida-ps -U
# List installed apps (USB-connected)
frida-ps -Uai
# Attach to a running process by name
frida -U -n com.target.app
# Spawn and attach (best for catching early init)
frida -U -f com.target.app --no-pauseThe -U flag means USB. Use -R for remote (Frida over network) or omit for local processes.
The Basics of a Hook
Frida scripts are JavaScript. The Java bridge gives you access to Android classes, the ObjC bridge gives you Objective-C classes on iOS, and Interceptor works on raw native functions.
Android, hook a Java method
Java.perform(function () {
var Activity = Java.use('com.target.app.MainActivity');
Activity.checkLicense.implementation = function () {
console.log('[+] checkLicense called, returning true');
return true;
};
});Run with:
frida -U -f com.target.app -l hook.js --no-pauseiOS, hook an Objective-C method
if (ObjC.available) {
var LoginVC = ObjC.classes.LoginViewController;
Interceptor.attach(LoginVC['- isJailbroken'].implementation, {
onLeave: function (retval) {
console.log('[+] isJailbroken called, forcing false');
retval.replace(0);
}
});
}Native function hook (works anywhere)
Interceptor.attach(Module.getExportByName('libc.so', 'open'), {
onEnter: function (args) {
this.path = args[0].readUtf8String();
},
onLeave: function (retval) {
console.log('[open] ' + this.path + ' = ' + retval);
}
});Patterns I Use Constantly
Dump all method calls on a class
Java.perform(function () {
var TargetClass = Java.use('com.target.app.crypto.CryptoHelper');
var methods = TargetClass.class.getDeclaredMethods();
methods.forEach(function (m) {
var name = m.getName();
try {
TargetClass[name].overloads.forEach(function (overload) {
overload.implementation = function () {
console.log('[->] ' + name + '(' + JSON.stringify(arguments) + ')');
var ret = overload.apply(this, arguments);
console.log('[<-] ' + name + ' returned ' + ret);
return ret;
};
});
} catch (e) {}
});
});Dump strings going through a function
Java.perform(function () {
var String = Java.use('java.lang.String');
String.$init.overload('[B').implementation = function (bytes) {
var s = this.$init(bytes);
console.log('[String] ' + s);
return s;
};
});Bypass a root check by name
Java.perform(function () {
var RootBeer = Java.use('com.scottyab.rootbeer.RootBeer');
RootBeer.isRooted.implementation = function () {
return false;
};
});Find the right class when you do not know its name
Java.perform(function () {
Java.enumerateLoadedClasses({
onMatch: function (name) {
if (name.toLowerCase().includes('login')) {
console.log(name);
}
},
onComplete: function () {}
});
});Useful CLI Tools
| Command | What it does |
|---|---|
frida-ps -Uai | List installed apps on USB device |
frida-trace -U -i "open*" com.target.app | Auto-generate trace handlers for matching functions |
frida-trace -U -j "com.target.app.crypto.*!*" com.target.app | Trace all Java methods in a package |
frida-ls-devices | Show connected devices |
frida-kill -U <pid> | Kill a process |
frida-discover -U -n com.target.app | Find functions worth tracing |
Writing Reusable Scripts
For anything beyond a one-off, I use the frida-compile workflow with TypeScript. It gives you proper modules, types, and IDE support.
npm install -g frida-compile
frida-compile agent/index.ts -o dist/agent.js -wThen load the compiled script:
frida -U -f com.target.app -l dist/agent.js --no-pauseWhen Frida Is Overkill
Sometimes Objection is enough, it ships pre-built scripts for the common cases (root bypass, SSL pinning, IPC enumeration). I reach for raw Frida when the target has custom protections or I need to hook something Objection does not cover.
Related Notes
Last updated on
Mobile Application Security
Mobile application penetration testing for Android and iOS, covering static and dynamic analysis, runtime instrumentation with Frida and Objection, and bypassing common protections like SSL pinning and root detection.
Mobile Pentesting Fundamentals
Setting up a mobile pentesting environment for Android and iOS, choosing physical devices versus emulators, and the methodology I follow on every mobile engagement.