Root detection blocks security testing on production apps. Bypassing it is a standard step in mobile penetration testing to assess the app’s true security posture.
Common Root Detection Methods Apps check for root via several mechanisms:
1 . File existence checks /su, /system/bin/su, /system/xbin/su, /sbin/su /system/app/Superuser.apk , /data/data/com.noshufou .android .su/ Magisk Manager, SuperSU, KingUser2 . Directory permissions /system is writable (should be read-only on production) /data is accessible3 . Package presence com.noshufou .android .su , eu.chainfire .supersu com.koushikdutta .superuser , com.thirdparty .superuser com.topjohnwu .magisk 4 . Command execution Runtime.exec ("su" ) and check result which su5 . Build props ro.build .tags = test-keys (custom ROM) ro.debuggable = 1 ro.secure = 0 6 . System properties getprop ro.build .type = user (should be for production)7 . Native library checks libc.so hooking detection8 . SafetyNet / Play Integrity API (Google attestation)
Frida — Bypass Root Detection Universal Root Bypass Script Java .perform (function ( ) { try { var RootBeer = Java .use ("com.scottyab.rootbeer.RootBeer" ); RootBeer .isRooted .overload ().implementation = function ( ) { console .log ("[*] RootBeer.isRooted() → false" ); return false ; }; RootBeer .isRootedWithoutBusyBoxCheck .overload ().implementation = function ( ) { return false ; }; } catch (e) {} var File = Java .use ("java.io.File" ); File .exists .implementation = function ( ) { var name = this .getAbsolutePath (); if (name.indexOf ("su" ) !== -1 || name.indexOf ("magisk" ) !== -1 || name.indexOf ("Superuser" ) !== -1 || name.indexOf ("superuser" ) !== -1 ) { console .log ("[*] File.exists() blocked: " + name); return false ; } return this .exists .call (this ); }; var Runtime = Java .use ("java.lang.Runtime" ); Runtime .exec .overload ('[Ljava.lang.String;' ).implementation = function (cmd ) { var cmdStr = cmd.join (' ' ); if (cmdStr.indexOf ("su" ) !== -1 || cmdStr.indexOf ("which" ) !== -1 ) { console .log ("[*] Runtime.exec() blocked: " + cmdStr); throw Java .use ("java.io.IOException" ).$new("not found" ); } return this .exec .overload ('[Ljava.lang.String;' ).call (this , cmd); }; var System = Java .use ("java.lang.System" ); System .getProperty .overload ('java.lang.String' ).implementation = function (key ) { if (key === "ro.build.tags" ) return "release-keys" ; return this .getProperty .overload ('java.lang.String' ).call (this , key); }; console .log ("[+] Root detection bypass loaded" ); });
frida -U -l root_bypass.js -f com.target.app --no-pause
Objection — Automated Root Bypass objection -g com.target.app explore android root disable android root simulate android hooking list classes | grep -i root android hooking list classes | grep -i jail android hooking list classes | grep -i detect
Magisk — System-Level Root Hiding su -c magisk --hide status
Specific Library Bypasses SafetyNet / Play Integrity Java .perform (function ( ) { var SafetyNetClient = Java .use ("com.google.android.gms.safetynet.SafetyNetClient" ); });
Specific Library Detection Java .perform (function ( ) { try { var RootBeer = Java .use ("com.scottyab.rootbeer.RootBeer" ); var methods = ["isRooted" , "isRootedWithoutBusyBoxCheck" , "detectRootManagementApps" , "detectPotentiallyDangerousApps" , "detectRootCloakingApps" , "checkForBinary" , "checkForDangerousProps" , "checkForRWPaths" , "detectTestKeys" , "checkSuExists" ]; methods.forEach (function (m ) { try { RootBeer [m].overload ().implementation = function ( ) { console .log ("[*] Bypassed RootBeer." + m); return false ; }; } catch (e) {} }); } catch (e) { console .log ("RootBeer not found" ); } try { var Shell = Java .use ("com.jrummyapps.android.shell.Shell" ); Shell .SU .available .implementation = function ( ) { return false ; }; } catch (e) {} });
Native / C++ Root Detection Bypass Some apps check for root in native code (NDK):
Interceptor .attach (Module .findExportByName ("libc.so" , "access" ), { onEnter : function (args ) { var path = Memory .readUtf8String (args[0 ]); if (path.indexOf ("su" ) !== -1 || path.indexOf ("magisk" ) !== -1 ) { console .log ("[*] access() blocked: " + path); Memory .writeUtf8String (args[0 ], "/nonexistent" ); } } });Interceptor .attach (Module .findExportByName ("libc.so" , "fopen" ), { onEnter : function (args ) { var path = Memory .readUtf8String (args[0 ]); if (path.indexOf ("su" ) !== -1 ) { console .log ("[*] fopen() blocked: " + path); args[0 ] = Memory .allocUtf8String ("/nonexistent" ); } } });
Detecting What the App Checks android hooking watch class com.target.app.security.RootChecker frida-trace -U -f com.target.app -i "open" -i "fopen" -i "access" apktool d app.apk -o app_decompiled/ grep -r "su" app_decompiled/smali/ | grep -i "exec\|exist\|file" grep -r "root" app_decompiled/smali/ | grep -i "check\|detect\|is" grep -r "SafetyNet\|RootBeer\|rootbeer" app_decompiled/smali/
Frida Script Loader frida -U -l root_bypass.js -f com.target.app --no-pause frida -U -l root_bypass.js com.target.app frida -U -l root_bypass.js --aux="stdio=pipe" -f com.target.app frida --codeshare dzonerzy/fridantiroot -f com.target.app -U --no-pause frida --codeshare pcipolloni/universal-android-ssl-pinning-bypass-with-frida -f com.target.app -U
No comments yet. Be the first.