DATE

March 27, 2026

Root detection is one of the first lines of defense mobile applications deploy to prevent tampering, reverse engineering, and unauthorized access. Banking apps, fintech platforms, and enterprise applications all rely on it. But for security researchers and penetration testers, bypassing root detection is a fundamental skill. Frida, the dynamic instrumentation toolkit, makes this process both systematic and powerful.

This guide walks through everything from setting up Frida to writing custom bypass scripts, with real commands you can use directly in your testing engagements.

If your organization needs professional mobile application security assessments, Redfox Cybersecurity's penetration testing services cover Android, iOS, and full mobile app security audits conducted by certified experts.

What Is Root Detection and Why Applications Use It

Root detection refers to a set of checks an Android application performs at runtime to determine whether the device it is running on has been rooted. A rooted device grants the user elevated privileges, allowing them to bypass OS-level restrictions, hook function calls, modify app memory, and intercept network traffic.

Applications typically implement root detection to:

  • Prevent reverse engineering and intellectual property theft
  • Comply with financial and regulatory security standards
  • Block runtime manipulation of business logic
  • Detect presence of Magisk, SuperSU, or similar root management tools

Common detection techniques include checking for the presence of binaries like su, verifying the integrity of system partitions, querying installed packages like Magisk Manager, checking for test keys in the build, using native JNI calls to cross-check Java-layer results, and leveraging third-party SDKs like SafetyNet or Play Integrity API.

Setting Up Frida for Android Pentesting

Before bypassing anything, you need a working Frida environment.

Requirements:

  • A rooted Android device or an emulator (AVD with Google APIs, x86 or x86_64)
  • Python 3.x installed on your host machine
  • ADB (Android Debug Bridge) configured

Step 1: Install Frida on the host machine

pip install frida-tools
pip install frida

Step 2: Check the Frida version

frida --version

Step 3: Download the matching frida-server binary

Go to the Frida releases page and download frida-server matching your host Frida version and the Android device architecture (arm64 is most common for modern devices).

Step 4: Push frida-server to the device

adb push frida-server /data/local/tmp/
adb shell chmod 755 /data/local/tmp/frida-server

Step 5: Start frida-server on the device

adb shell
su
/data/local/tmp/frida-server &

Step 6: Verify the connection from the host

frida-ps -U

This lists all running processes on the connected device. If you see the process list, your Frida setup is working correctly.

Identifying Root Detection Mechanisms in the Target App

Before writing bypass scripts, you need to know what you are bypassing. Static analysis gives you the map.

Decompile the APK using JADX:

jadx -d output_dir target_app.apk

Search for common root detection indicators:

grep -r "su" output_dir/sources --include="*.java"
grep -r "RootBeer" output_dir/sources --include="*.java"
grep -r "Magisk" output_dir/sources --include="*.java"
grep -r "isRooted" output_dir/sources --include="*.java"
grep -r "checkRootMethod" output_dir/sources --include="*.java"

Also look for:

  • Runtime.exec("su") calls
  • File.exists() checks on paths like /system/app/Superuser.apk, /sbin/su, /system/xbin/su
  • Package manager queries for com.topjohnwu.magisk or com.noshufou.android.su

Understanding what the app checks allows you to write targeted Frida hooks rather than using generic scripts that may miss custom implementations.

Writing a Basic Frida Root Detection Bypass Script

Frida hooks functions at runtime and replaces their return values. The core idea is intercepting the root check function and forcing it to return false.

Basic JavaScript hook targeting a custom isRooted method:

Java.perform(function () {
   var RootCheck = Java.use("com.example.app.security.RootDetection");

   RootCheck.isRooted.implementation = function () {
       console.log("[*] isRooted() called - returning false");
       return false;
   };
});

Run the script against the app:

frida -U -n com.example.targetapp -s bypass_root.js

Or spawn the app fresh (recommended to catch checks during initialization):

frida -U -f com.example.targetapp -l bypass_root.js --no-pause

The --no-pause flag ensures the app continues execution immediately after injection without waiting for a resume command.

Bypassing RootBeer Library Checks

RootBeer is a widely used open-source root detection library. Many apps rely on it, making it a high-value target during assessments. Redfox Cybersecurity's mobile application penetration testers encounter RootBeer implementations regularly in client engagements.

RootBeer exposes an isRooted() method on the RootBeer class. The hook is straightforward:

Java.perform(function () {
   var RootBeer = Java.use("com.scottyab.rootbeer.RootBeer");

   RootBeer.isRooted.implementation = function () {
       console.log("[*] RootBeer.isRooted() hooked - returning false");
       return false;
   };

   RootBeer.isRootedWithBusyBox.implementation = function () {
       console.log("[*] RootBeer.isRootedWithBusyBox() hooked - returning false");
       return false;
   };
});

Run it:

frida -U -f com.example.targetapp -l rootbeer_bypass.js --no-pause

Some apps also call individual RootBeer check methods directly. You may need to hook checkForSuBinary, checkForDangerousProps, checkForRWPaths, detectTestKeys, and checkSuExists individually if the app has been customized.

Bypassing File-Based Root Checks

Many apps manually check for the existence of root-related files using java.io.File.exists(). This is one of the most primitive but common approaches.

Hook File.exists() to intercept path-based checks:

Java.perform(function () {
   var File = Java.use("java.io.File");

   var suspiciousPaths = [
       "/system/app/Superuser.apk",
       "/sbin/su",
       "/system/bin/su",
       "/system/xbin/su",
       "/data/local/xbin/su",
       "/data/local/bin/su",
       "/system/sd/xbin/su",
       "/system/bin/failsafe/su",
       "/data/local/su",
       "/su/bin/su"
   ];

   File.exists.implementation = function () {
       var path = this.getAbsolutePath();
       if (suspiciousPaths.indexOf(path) >= 0) {
           console.log("[*] Blocked File.exists() check for: " + path);
           return false;
       }
       return this.exists();
   };
});

This approach intercepts any file existence check for known root binary paths and returns false while allowing legitimate file checks to proceed normally.

Bypassing Runtime.exec() Root Checks

Some apps execute shell commands directly and check the output to detect su.

Hook Runtime.exec to neutralize shell-based checks:

Java.perform(function () {
   var Runtime = Java.use("java.lang.Runtime");

   Runtime.exec.overload("java.lang.String").implementation = function (cmd) {
       if (cmd.indexOf("su") !== -1 || cmd.indexOf("which") !== -1) {
           console.log("[*] Blocked Runtime.exec: " + cmd);
           cmd = "echo";
       }
       return this.exec(cmd);
   };

   Runtime.exec.overload("[Ljava.lang.String;").implementation = function (cmds) {
       for (var i = 0; i < cmds.length; i++) {
           if (cmds[i].indexOf("su") !== -1) {
               console.log("[*] Blocked Runtime.exec array command: " + cmds[i]);
               return this.exec("echo");
           }
       }
       return this.exec(cmds);
   };
};

Bypassing Native JNI Root Detection

This is where it gets harder. Developers who know Frida is widely used often push root detection logic into native C/C++ code compiled into .so libraries. Java-level hooks will not touch this.

Step 1: List loaded native libraries

Process.enumerateModules().forEach(function (m) {
   console.log(m.name + " @ " + m.base);
});

Step 2: Find the target native function

adb shell
su
grep -r "isRooted\|checkRoot\|detectRoot" /data/app/com.example.targetapp*/lib/

Or from Frida:

var module = Process.getModuleByName("libnative-lib.so");
module.enumerateExports().forEach(function (exp) {
   console.log(exp.name + " @ " + exp.address);
});

Step 3: Hook the native function

var nativeFunc = Module.findExportByName("libnative-lib.so", "Java_com_example_app_NativeCheck_isRooted");

if (nativeFunc) {
   Interceptor.attach(nativeFunc, {
       onLeave: function (retval) {
           console.log("[*] Native isRooted returned: " + retval);
           retval.replace(0);
       }
   });
}

retval.replace(0) forces a return value of 0 (false/not rooted) regardless of the actual detection logic.

Professional mobile pentesters at Redfox Cybersecurity routinely handle native-layer bypass challenges as part of comprehensive mobile application security testing engagements.

Using Frida Codeshare for Quick Bypass Scripts

Frida Codeshare hosts community-contributed bypass scripts. For quick testing:

frida --codeshare dzonerzy/fridantiroot -f com.example.targetapp -U --no-pause

The fridantiroot script covers a broad range of common root detection techniques. However, custom implementations will still require tailored hooks. Use community scripts as a starting point, not a complete solution.

Detecting Multi-Layered Root Detection

Modern enterprise apps stack multiple detection layers. After bypassing one, another may trigger. Use Frida tracing to discover all root-related function calls at runtime:

frida-trace -U -f com.example.targetapp -j "*!*root*" --no-pause
frida-trace -U -f com.example.targetapp -j "*!*Root*" --no-pause
frida-trace -U -f com.example.targetapp -j "*!*isRooted*" --no-pause

The -j flag traces Java methods matching the glob pattern. This reveals every method containing "root" in its name that gets called during execution, giving you a complete picture of what needs to be hooked.

Handling SafetyNet and Play Integrity API Bypass

SafetyNet Attestation

Google's SafetyNet and its successor, the Play Integrity API, perform server-validated attestation. The device generates a signed JWT containing device health information. The server validates the signature.

Bypassing this at the client level requires hooking the SafetyNet API calls before the attestation response reaches the server validation logic.

Java.perform(function () {
   var SafetyNetClient = Java.use("com.google.android.gms.safetynet.SafetyNetClient");

   SafetyNetClient.attest.implementation = function (nonce, apiKey) {
       console.log("[*] SafetyNet attest called - intercepted");
       return this.attest(nonce, apiKey);
   };
});

Full SafetyNet bypass typically requires Magisk modules like MagiskHide (legacy) or Shamiko combined with Zygisk, rather than pure Frida hooks, since attestation verification happens server-side. Frida is most effective here for understanding what data is being sent rather than bypassing the attestation result itself.

Play Integrity API

The Play Integrity API is stricter and hardware-backed on supported devices. Bypass strategies involve spoofing device fingerprints and using modified GMS components. This is an advanced research area where professional mobile security testing teams at firms like Redfox Cybersecurity have hands-on experience navigating these controls in real-world client applications.

Combining Multiple Hooks into a Single Bypass Script

In practice, you will almost always need several hooks simultaneously. Combine them into one cohesive script:

Java.perform(function () {

   // RootBeer
   try {
       var RootBeer = Java.use("com.scottyab.rootbeer.RootBeer");
       RootBeer.isRooted.implementation = function () { return false; };
   } catch (e) { console.log("RootBeer not found"); }

   // File.exists
   var File = Java.use("java.io.File");
   var blockedPaths = ["/sbin/su", "/system/xbin/su", "/system/bin/su", "/system/app/Superuser.apk"];
   File.exists.implementation = function () {
       if (blockedPaths.indexOf(this.getAbsolutePath()) >= 0) return false;
       return this.exists();
   };

   // Runtime.exec
   var Runtime = Java.use("java.lang.Runtime");
   Runtime.exec.overload("java.lang.String").implementation = function (cmd) {
       if (cmd.indexOf("su") !== -1) return this.exec("echo");
       return this.exec(cmd);
   };

   // Build.TAGS test-keys check
   var Build = Java.use("android.os.Build");
   Build.TAGS.value = "release-keys";

   console.log("[*] All root detection hooks applied");
});

Run the combined script:

frida -U -f com.example.targetapp -l combined_bypass.js --no-pause

Final Thoughts for Security Professionals

Bypassing root detection with Frida is a core competency for Android penetration testers. Mastering it requires understanding both the Java layer and native layer detection strategies, knowing how to trace unknown applications dynamically, and building modular scripts that can be adapted quickly per engagement.

Root detection bypass is only one component of a thorough mobile application security assessment. Real-world assessments also cover insecure data storage, improper session handling, certificate pinning bypass, API security, and business logic flaws.

If your application needs to be stress-tested against these attack vectors by a specialized team, Redfox Cybersecurity's penetration testing services deliver comprehensive mobile, web, network, and cloud security assessments tailored to your risk profile. Their team brings hands-on expertise with real-world attack techniques across regulated industries including fintech, healthcare, and enterprise SaaS.

Get in touch with Redfox Cybersecurity to scope your next security engagement.