Your First Frida Hook in 8 Lines of JavaScript
For mobile security testers and reverse engineers new to dynamic instrumentation who want to intercept and modify Android app behavior in real-time without recompiling.

Prerequisites
- Android device or emulator (rooted)
- ADB installed and device connected (
adb devicesshows your device) - Frida installed:
pip3 install frida-tools - Frida server running on device (download from https://github.com/frida/frida/releases)
- Target app installed (we'll use a demo banking app)
What Is Frida?
Frida is dynamic instrumentation toolkit that lets you inject JavaScript into running processes. You can intercept function calls, read/modify arguments, change return values, and bypass security checks - all without touching the original binary.
Step 1: Setting Up Frida Server
Push Frida server to your device:
bash# Download correct architecture (check with 'adb shell getprop ro.product.cpu.abi') wget https://github.com/frida/frida/releases/download/16.1.10/frida-server-16.1.10-android-arm64.xz unxz frida-server-16.1.10-android-arm64.xz
Install and run:
bashadb push frida-server-16.1.10-android-arm64 /data/local/tmp/frida-server adb shell "chmod 755 /data/local/tmp/frida-server" adb shell "/data/local/tmp/frida-server &"
Verify it's running:
bashfrida-ps -U
You should see a list of processes on your device.
Step 2: Finding Your Target Function
Let's say our banking app has a root detection check. First, find the app package name:
bashfrida-ps -Ua # List all running apps
Look for something like com.example.bankingapp.
Now use jadx-gui or apktool to decompile the APK and search for "root" - you might find:
javapublic class SecurityCheck { public boolean isRooted() { // Detection logic return true; } }
Our goal: make this function always return false.
Step 3: Writing Your First Hook
Create bypass_root.js:
javascriptJava.perform(function() { var SecurityCheck = Java.use("com.example.bankingapp.SecurityCheck"); SecurityCheck.isRooted.implementation = function() { console.log("[*] isRooted() called - bypassing!"); return false; // Always return false }; });
That's it! Let's break it down:
Java.perform()- Wait for Java VM to loadJava.use()- Get reference to the class.implementation- Replace the functionconsole.log()- Print to our terminal
Step 4: Injecting Your Hook
Attach to the running app:
bashfrida -U -f com.example.bankingapp -l bypass_root.js --no-pause
Flags explained:
-U- Connect to USB device-f- Spawn the app (start fresh)-l- Load our script--no-pause- Don't pause at startup
Alternatively, attach to an already running process:
bashfrida -U com.example.bankingapp -l bypass_root.js
When the app calls isRooted(), you'll see:
[*] isRooted() called - bypassing!
Step 5: Inspecting and Modifying Arguments
Let's hook a login function that takes username and password:
javascriptJava.perform(function() { var LoginActivity = Java.use("com.example.bankingapp.LoginActivity"); LoginActivity.validateLogin.implementation = function(username, password) { console.log("[*] Login attempt:"); console.log(" Username: " + username); console.log(" Password: " + password); // Call original function var result = this.validateLogin(username, password); console.log(" Result: " + result); return true; // Force login success }; });
The this.validateLogin() calls the original implementation. You can inspect legitimate behavior before modifying it.
Step 6: Hooking Function Overloads
Java allows multiple functions with the same name (overloading). Specify parameter types:
javascriptJava.perform(function() { var Crypto = Java.use("com.example.bankingapp.Crypto"); // Hook the version that takes String and int Crypto.encrypt.overload('java.lang.String', 'int').implementation = function(data, key) { console.log("[*] Encrypting: " + data + " with key: " + key); return this.encrypt(data, key); }; // Hook the version that takes byte array Crypto.encrypt.overload('[B').implementation = function(data) { console.log("[*] Encrypting byte array, length: " + data.length); return this.encrypt(data); }; });
[B is JNI notation for byte array. Common types:
java.lang.Stringint,boolean,long[B(byte[]),[I(int[])
Step 7: Discovering Functions Dynamically
Don't know the exact function name? Enumerate them:
javascriptJava.perform(function() { var targetClass = Java.use("com.example.bankingapp.SecurityCheck"); var methods = targetClass.class.getDeclaredMethods(); methods.forEach(function(method) { console.log("[*] Method: " + method.getName()); }); });
Run this first to map out the class, then write specific hooks.
Step 8: Saving Your Script for Quick Re-use
Create an alias in ~/.bashrc:
bashalias frida-bank='frida -U -f com.example.bankingapp -l ~/frida-scripts/bypass_root.js --no-pause'
Now just type:
bashfrida-bank
Troubleshooting
"Failed to spawn: unable to find application"
bashfrida-ps -Uai # List installed apps, get exact package name
"ClassNotFoundException"
- Use
jadx-guito verify exact class path - App might use obfuscation (ProGuard) - look for short class names like
a.b.c
Hook not triggering
bashfrida -U com.example.bankingapp # Attach to running process instead of spawning
Where to Go Next
- Hook native functions: Use
Interceptor.attach()for C/C++ functions - Explore Frida Codeshare: https://codeshare.frida.re/ has 1000+ ready-made scripts
- Learn frida-trace: Auto-generate hooks with
frida-trace -U -f com.app -i "*crypto*" - Study SSL pinning bypasses: Critical skill for mobile pentesting
- Master Memory operations:
Memory.readByteArray(),Memory.writeUtf8String() - Build Frida Gadget: Inject Frida into non-rooted devices
- Read the docs: https://frida.re/docs/javascript-api/
Frida transforms static reverse engineering into interactive debugging. Practice on CTF challenges and vulnerable apps like DVIA or InsecureShop before testing real targets.