Your First Frida Hook: Bypass Root Detection in 8 Lines
For mobile security beginners who want to learn dynamic instrumentation by writing a simple Frida script to hook and modify Android app behavior at runtime.

What You'll Learn
Frida is a dynamic instrumentation toolkit that lets you inject JavaScript into native apps to observe and modify their behavior. We'll hook a simple root detection function and force it to always return "not rooted" — a perfect first Frida script that teaches the fundamentals.
Prerequisites
- Android device or emulator (rooted preferred, but not required)
- ADB installed:
sudo apt install adborbrew install android-platform-tools - Python 3.7+
- A test app with root detection (we'll use a sample APK)
Step 1: Install Frida
Install Frida tools on your computer:
bashpip3 install frida-tools
Verify installation:
bashfrida --version
You should see something like 16.1.4.
Step 2: Install Frida Server on Android
Download the appropriate Frida server for your device architecture:
bash# Check your device's architecture adb shell getprop ro.product.cpu.abi # Output example: arm64-v8a # Download frida-server (replace version and arch as needed) wget https://github.com/frida/frida/releases/download/16.1.4/frida-server-16.1.4-android-arm64.xz unxz frida-server-16.1.4-android-arm64.xz
Push to device and start:
bashadb push frida-server-16.1.4-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 running processes on your device.
Step 3: Find the Target Function
Install a test app with root detection. For this tutorial, we'll use a generic pattern. Most root detection checks look like this in Java:
javapublic boolean isRooted() { // Checks for su binary, Magisk, etc. return true; // Device is rooted }
We need to find this function. Use jadx-gui or apktool to decompile the APK and search for strings like "su", "rooted", or "root".
Let's say we found: com.example.app.SecurityCheck.isRooted()
Step 4: Write Your First Hook
Create a file called bypass-root.js:
javascriptJava.perform(function() { console.log("[*] Starting root detection bypass..."); var SecurityCheck = Java.use("com.example.app.SecurityCheck"); SecurityCheck.isRooted.implementation = function() { console.log("[*] isRooted() called - returning false"); return false; }; console.log("[*] Hook installed successfully!"); });
What each line does:
Java.perform(): Waits for the Java VM to be readyJava.use(): Gets a reference to the target class.implementation: Replaces the original functionreturn false: Our modified return value (not rooted)
Step 5: Run the Hook
Launch the app with Frida:
bashfrida -U -l bypass-root.js -f com.example.app --no-pause
Flags explained:
-U: Use USB device-l: Load script file-f: Spawn the app--no-pause: Don't pause at startup
You should see:
code[*] Starting root detection bypass... [*] Hook installed successfully!
When you trigger the root check in the app, you'll see:
[*] isRooted() called - returning false
The app now thinks it's running on a non-rooted device!
Step 6: Hook Apps Already Running
If the app is already open:
bash# List running apps frida-ps -U | grep example # Attach to running process frida -U -l bypass-root.js com.example.app
Step 7: Inspect Arguments and Return Values
Let's enhance our hook to see what the original function would have returned:
javascriptJava.perform(function() { var SecurityCheck = Java.use("com.example.app.SecurityCheck"); SecurityCheck.isRooted.implementation = function() { var originalResult = this.isRooted.call(this); console.log("[*] Original result: " + originalResult); console.log("[*] Returning: false"); return false; }; });
Now you can see both the real and spoofed values.
Step 8: Handle Overloaded Methods
If isRooted() has multiple versions (overloads):
javascriptJava.perform(function() { var SecurityCheck = Java.use("com.example.app.SecurityCheck"); // Hook the version that takes no arguments SecurityCheck.isRooted.overload().implementation = function() { return false; }; // Hook the version that takes a boolean SecurityCheck.isRooted.overload('boolean').implementation = function(checkDeep) { console.log("[*] checkDeep parameter: " + checkDeep); return false; }; });
Where to Go Next
- Hook multiple functions to bypass certificate pinning
- Use
Java.choose()to find and modify existing class instances - Intercept native (C/C++) functions with
Interceptor.attach() - Write Frida scripts in Python for automation
- Explore frida-trace for automatic API tracing
- Try objection, a higher-level framework built on Frida
You've just written your first dynamic instrumentation! This same pattern works for bypassing SSL pinning, defeating anti-debugging, and modifying any app behavior. The key is finding the right class and method, then replacing its implementation with your own logic.
Comments (0)
// go deeper
- Practice this in a legal lab: Frida Dynamic Hook
- Practice this in a legal lab: Anti-VM Detection Bypass
- Practice this in a legal lab: Android: Defeat Certificate Pinning with Frida
- Read the writeup: Your First Frida Hook: Bypassing Android Root Detection in 12 Lines
- Read the writeup: Your First Frida Hook in 8 Lines of JavaScript