Archives
Trending
Support
Login
clear text
XML
Django
JavaScript
MATLAB
C
C++
C#
Python
SQL
Shell
Bash
Markdown
YAML
JSON
HTML
CSS
PHP
Java
Ruby
Go
Rust
Swift
Kotlin
Arduino
TypeScript
Perl
Autohotkey
Lua
SQF
R
Scala
Haskell
Groovy
Dart
Clojure
VB.NET
Objective-C
PowerShell
Bash
CoffeeScript
Verilog
# DexGuard Protection Analysis & Frida Bypass — AI Agent Methodology ## Objective You are an AI agent tasked with analyzing and bypassing DexGuard root detection on an Android application. The target app detects rooted devices and terminates itself immediately upon launch. Your goal is to use Frida to dynamically analyze the app, identify all detection mechanisms, and bypass them so the app runs normally on a rooted device. --- ## 1. What is DexGuard? DexGuard is a commercial protection tool developed by **Guardsquare**, designed specifically for Android applications. It is the enterprise-grade evolution of ProGuard/R8 and provides multiple layers of protection including code obfuscation, runtime self-protection, integrity verification, and environment checks. --- ## 2. Protection Types Provided by DexGuard ### 2.1 Code Obfuscation - **Class/Method/Field Renaming** — Converts meaningful names into unreadable symbols. - **Control Flow Obfuscation** — Injects opaque predicates, dead code, and flattened control flow. - **String Encryption** — All hardcoded strings are encrypted at compile time and decrypted only at runtime. - **Arithmetic Obfuscation** — Replaces simple arithmetic with complex equivalent expressions. ### 2.2 Runtime Application Self-Protection (RASP) - **Root Detection** — Detects rooted devices through multiple file system, package, property, and native checks. - **Hooking Framework Detection** — Detects Frida, Xposed, Cydia Substrate, and similar frameworks. - **Debugger Detection** — Detects attached debuggers via `TracerPid`, `isDebuggerConnected()`, and ptrace checks. - **Emulator Detection** — Detects execution on emulators via build properties, sensors, and hardware fingerprints. ### 2.3 Integrity Protection - **APK Signature Verification** — Validates the APK signing certificate at runtime. - **DEX File Integrity** — Checks checksums/hashes of DEX files to detect tampering. - **Anti-Repackaging** — Prevents modified APKs from running. ### 2.4 Environment Checks - **SafetyNet / Play Integrity API** — Queries Google's attestation service. - **USB Debugging Detection** — Checks if `adb_enabled` is set. - **Developer Options Detection** — Checks if developer settings are enabled. --- ## 3. Root Detection Mechanisms in Detail DexGuard uses a combination of the following techniques to detect root: ### 3.1 File System Checks The app checks for the existence of these files/paths: ``` /system/app/Superuser.apk /system/xbin/su /system/bin/su /sbin/su /data/local/xbin/su /data/local/bin/su /su/bin/su /system/bin/failsafe/su /system/sd/xbin/su /data/local/su /system/bin/.ext/.su /system/usr/we-need-root/su-backup /system/xbin/mu /system/app/Superuser.apk /system/etc/init.d/99telecominfra /dev/com.koushikdutta.superuser.daemon/ ``` ### 3.2 Package Manager Checks The app queries the PackageManager for known root-related packages: ``` com.topjohnwu.magisk eu.chainfire.supersu com.koushikdutta.superuser com.noshufou.android.su com.thirdparty.superuser com.yellowes.su com.devadvance.rootcloak com.devadvance.rootcloakplus de.robv.android.xposed.installer com.saurik.substrate ``` ### 3.3 System Property Checks ``` ro.build.tags == "test-keys" → indicates custom build ro.debuggable == "1" → debuggable build ro.secure == "0" → insecure build ``` ### 3.4 Command Execution Checks The app attempts to execute: ``` su which su id (checks for uid=0) ``` ### 3.5 Native Layer Checks - Reads `/proc/self/maps` looking for Frida libraries (`frida-agent`, `frida-gadget`, `gmain`, `gum-js-loop`). - Reads `/proc/self/status` checking `TracerPid` for attached debuggers. - Uses `dlopen` / `dlsym` to check for injected shared libraries. - Scans open ports (default Frida port `27042`). - Uses `strstr` on memory maps to find hooking framework signatures. ### 3.6 Magisk-Specific Checks - Checks for MagiskHide / Zygisk presence. - Inspects mount namespaces for overlay mounts. - Looks for Magisk Manager package under randomized names. --- ## 4. App Termination Methods When any check returns positive, DexGuard triggers termination via one or more of: - `System.exit(0)` or `System.exit(1)` - `android.os.Process.killProcess(Process.myPid())` - `Activity.finish()` or `Activity.finishAffinity()` - Throwing an unhandled exception - Native `exit()` or `abort()` via JNI --- ## 5. AI Agent Step-by-Step Bypass Workflow Follow these steps in order. Each step builds on the previous one. ### Step 1: Static Analysis (Pre-Frida) Decompile the APK and gather intelligence before dynamic analysis. **Actions:** 1. Decompile with `apktool d target.apk` and `jadx target.apk`. 2. Search for root-related strings: `grep -ri "su" --include="*.smali"`, `grep -ri "root"`, `grep -ri "magisk"`, `grep -ri "detect"`. 3. Identify suspicious class names containing: `root`, `detect`, `guard`, `security`, `integrity`, `check`, `safe`, `tamper`, `hook`, `frida`, `xposed`. 4. Analyze `AndroidManifest.xml` for custom Application classes (root checks often run in `Application.attachBaseContext()` or `Application.onCreate()`). 5. Check for native libraries (`.so` files) in `lib/` — DexGuard may perform checks in native code. 6. Document all findings before proceeding. ### Step 2: Launch App with Frida in Spawn Mode and Block Termination **Critical:** Always use spawn mode (`-f`) because detection runs at app startup. **Critical:** Change frida-server name and port to avoid detection: ```bash # On the device mv frida-server frida-server-renamed ./frida-server-renamed -l 0.0.0.0:1337 & # On host frida -H 192.168.x.x:1337 -f com.target.app -l bypass.js --no-pause ``` **First script — block all termination methods and capture stack traces:** ```javascript Java.perform(function() { // Block System.exit var System = Java.use("java.lang.System"); System.exit.implementation = function(code) { console.log("[BLOCKED] System.exit(" + code + ")"); console.log("[STACK] " + Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())); }; // Block Process.killProcess var Process = Java.use("android.os.Process"); Process.killProcess.implementation = function(pid) { console.log("[BLOCKED] Process.killProcess(" + pid + ")"); console.log("[STACK] " + Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())); }; // Block Activity.finish var Activity = Java.use("android.app.Activity"); Activity.finish.overload().implementation = function() { console.log("[BLOCKED] Activity.finish()"); console.log("[STACK] " + Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())); }; Activity.finishAffinity.implementation = function() { console.log("[BLOCKED] Activity.finishAffinity()"); console.log("[STACK] " + Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new())); }; console.log("[+] Termination blockers installed"); }); ``` **Analyze the output:** The stack traces will reveal exactly which classes and methods trigger the termination. Record every class and method name from the traces. ### Step 3: Enumerate and Identify Detection Classes ```javascript Java.perform(function() { Java.enumerateLoadedClasses({ onMatch: function(className) { var keywords = ["root", "detect", "guard", "security", "integrity", "check", "safe", "tamper", "emulator", "hook", "frida", "xposed", "magisk", "superuser", "dexguard", "protect", "verify", "shield"]; var lower = className.toLowerCase(); for (var i = 0; i < keywords.length; i++) { if (lower.indexOf(keywords[i]) !== -1) { console.log("[SUSPECT] " + className); // List all methods in this class try { var clazz = Java.use(className); var methods = clazz.class.getDeclaredMethods(); for (var j = 0; j < methods.length; j++) { console.log(" → " + methods[j].getName()); } } catch(e) {} break; } } }, onComplete: function() { console.log("[*] Enumeration complete"); } }); }); ``` ### Step 4: Hook File System Checks ```javascript Java.perform(function() { var File = Java.use("java.io.File"); File.exists.implementation = function() { var path = this.getAbsolutePath(); var blocked = ["su", "magisk", "superuser", "Superuser", "SuperSU", "busybox", "xposed", "substrate", "frida", "daemonsu", ".magisk", "rootcloak", "hideroot", "sbin/su"]; for (var i = 0; i < blocked.length; i++) { if (path.indexOf(blocked[i]) !== -1) { console.log("[FS BYPASS] File.exists(" + path + ") → false"); return false; } } return this.exists(); }; File.canRead.implementation = function() { var path = this.getAbsolutePath(); if (path.indexOf("su") !== -1 || path.indexOf("magisk") !== -1) { console.log("[FS BYPASS] File.canRead(" + path + ") → false"); return false; } return this.canRead(); }; console.log("[+] File system hooks installed"); }); ``` ### Step 5: Hook Package Manager Checks ```javascript Java.perform(function() { var PM = Java.use("android.app.ApplicationPackageManager"); PM.getPackageInfo.overload('java.lang.String', 'int').implementation = function(pkg, flags) { var rootPackages = [ "com.topjohnwu.magisk", "eu.chainfire.supersu", "com.koushikdutta.superuser", "com.noshufou.android.su", "com.thirdparty.superuser", "com.yellowes.su", "com.devadvance.rootcloak", "com.devadvance.rootcloakplus", "de.robv.android.xposed.installer", "com.saurik.substrate", "com.zachspong.temprootremovejb", "com.amphoras.hidemyroot", "com.formyhm.hiderootPremium", "com.koushikdutta.rommanager" ]; for (var i = 0; i < rootPackages.length; i++) { if (pkg === rootPackages[i]) { console.log("[PM BYPASS] Blocked: " + pkg); throw Java.use("android.content.pm.PackageManager$NameNotFoundException").$new(pkg); } } return this.getPackageInfo(pkg, flags); }; // Also hook getInstalledPackages and getInstalledApplications PM.getInstalledPackages.overload('int').implementation = function(flags) { var packages = this.getInstalledPackages(flags); console.log("[PM] getInstalledPackages called, filtering root packages"); return packages; // Agent should filter if needed }; console.log("[+] Package manager hooks installed"); }); ``` ### Step 6: Hook Runtime Command Execution ```javascript 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 || cmd.indexOf("id") !== -1) { console.log("[EXEC BYPASS] Blocked: " + cmd); throw Java.use("java.io.IOException").$new("Permission denied"); } return this.exec(cmd); }; Runtime.exec.overload('[Ljava.lang.String;').implementation = function(cmds) { var joined = cmds.join(" "); if (joined.indexOf("su") !== -1 || joined.indexOf("which") !== -1) { console.log("[EXEC BYPASS] Blocked: " + joined); throw Java.use("java.io.IOException").$new("Permission denied"); } return this.exec(cmds); }; console.log("[+] Runtime.exec hooks installed"); }); ``` ### Step 7: Hook System Properties and Build Fields ```javascript Java.perform(function() { // Spoof Build fields var Build = Java.use("android.os.Build"); Build.TAGS.value = "release-keys"; Build.FINGERPRINT.value = Build.FINGERPRINT.value.replace("test-keys", "release-keys"); Build.TYPE.value = "user"; // Hook SystemProperties.get try { var SystemProperties = Java.use("android.os.SystemProperties"); SystemProperties.get.overload('java.lang.String').implementation = function(key) { var result = this.get(key); if (key === "ro.build.tags") return "release-keys"; if (key === "ro.debuggable") return "0"; if (key === "ro.secure") return "1"; if (key === "ro.build.type") return "user"; return result; }; SystemProperties.get.overload('java.lang.String', 'java.lang.String').implementation = function(key, def) { if (key === "ro.build.tags") return "release-keys"; if (key === "ro.debuggable") return "0"; if (key === "ro.secure") return "1"; return this.get(key, def); }; } catch(e) { console.log("[!] SystemProperties hook failed: " + e); } // Hook Settings.Secure for developer options and ADB var Secure = Java.use("android.provider.Settings$Secure"); Secure.getInt.overload('android.content.ContentResolver', 'java.lang.String', 'int').implementation = function(resolver, name, def) { if (name === "adb_enabled") { console.log("[SETTINGS] adb_enabled → 0"); return 0; } if (name === "development_settings_enabled") { console.log("[SETTINGS] dev_settings → 0"); return 0; } return this.getInt(resolver, name, def); }; console.log("[+] Property and settings hooks installed"); }); ``` ### Step 8: Hook Native Layer Checks ```javascript // Hook fopen — blocks reading sensitive proc files and su paths Interceptor.attach(Module.findExportByName("libc.so", "fopen"), { onEnter: function(args) { try { this.path = args[0].readUtf8String(); } catch(e) { this.path = null; } }, onLeave: function(retval) { if (this.path) { var blocked = ["su", "magisk", "frida", "xposed", "substrate", "/proc/self/maps", "/proc/self/status", "superuser"]; for (var i = 0; i < blocked.length; i++) { if (this.path.indexOf(blocked[i]) !== -1) { console.log("[NATIVE] fopen blocked: " + this.path); retval.replace(ptr(0)); break; } } } } }); // Hook access — used to check file existence at native level Interceptor.attach(Module.findExportByName("libc.so", "access"), { onEnter: function(args) { try { this.path = args[0].readUtf8String(); } catch(e) { this.path = null; } }, onLeave: function(retval) { if (this.path) { if (this.path.indexOf("su") !== -1 || this.path.indexOf("magisk") !== -1 || this.path.indexOf("superuser") !== -1) { console.log("[NATIVE] access blocked: " + this.path); retval.replace(-1); } } } }); // Hook stat Interceptor.attach(Module.findExportByName("libc.so", "stat"), { onEnter: function(args) { try { this.path = args[0].readUtf8String(); } catch(e) { this.path = null; } }, onLeave: function(retval) { if (this.path && (this.path.indexOf("su") !== -1 || this.path.indexOf("magisk") !== -1)) { console.log("[NATIVE] stat blocked: " + this.path); retval.replace(-1); } } }); // Hook strstr — primary anti-Frida detection bypass Interceptor.attach(Module.findExportByName("libc.so", "strstr"), { onEnter: function(args) { try { this.needle = args[1].readUtf8String(); } catch(e) { this.needle = null; } }, onLeave: function(retval) { if (this.needle) { var blocked = ["frida", "gadget", "gmain", "gum-js-loop", "linjector", "agent", "server"]; for (var i = 0; i < blocked.length; i++) { if (this.needle.indexOf(blocked[i]) !== -1) { console.log("[NATIVE] strstr blocked needle: " + this.needle); retval.replace(ptr(0)); break; } } } } }); // Hook connect — block Frida default port detection Interceptor.attach(Module.findExportByName("libc.so", "connect"), { onEnter: function(args) { var sockAddr = args[1]; var family = sockAddr.readU16(); if (family === 2) { // AF_INET var port = (sockAddr.add(2).readU8() << 8) | sockAddr.add(3).readU8(); if (port === 27042 || port === 27043) { console.log("[NATIVE] connect to Frida port " + port + " → redirecting"); sockAddr.add(2).writeU8(0); sockAddr.add(3).writeU8(0); } } } }); console.log("[+] Native hooks installed"); ``` ### Step 9: Verify and Iterate After loading all hooks: 1. **Check if the app stays open** — If it still crashes, check Frida console for new stack traces. 2. **Look for additional detection methods** — DexGuard may use multiple layers. If one bypass works but the app still closes, there are more checks. 3. **Check for delayed checks** — Some checks run on timers or background threads. Monitor for several minutes. 4. **Check for native-only checks** — If Java hooks are not enough, the detection may be entirely in native `.so` libraries. Use `Interceptor` on the specific native library. 5. **Test app functionality** — Ensure the app works normally after bypass (network calls, login, etc.). Some apps have server-side root detection via SafetyNet/Play Integrity. **If the app still terminates after all hooks:** - Look for `Thread` creation in detection classes — hooks may need to target spawned threads. - Check for `ContentProvider` or `BroadcastReceiver` based checks. - Analyze native `.so` files with Ghidra for hardcoded detection logic. - Consider using Frida Gadget injected into the APK instead of frida-server. ### Step 10: Consolidate Final Bypass Script Combine all working hooks into a single clean script. Remove any hooks that were not needed. Add error handling. Test the final script from a clean app start to confirm full bypass. --- ## 6. Execution Command ```bash # Rename and run frida-server on non-default port adb shell "mv /data/local/tmp/frida-server /data/local/tmp/fs-custom" adb shell "/data/local/tmp/fs-custom -l 0.0.0.0:1337 &" # Launch with spawn mode frida -H
:1337 -f
-l dexguard_bypass.js --no-pause ``` --- ## 7. Important Notes - **Always use spawn mode** (`-f` flag) — detection runs at `Application.attachBaseContext()` or `Application.onCreate()`, before any activity is visible. - **Rename frida-server binary** — DexGuard scans process names for "frida". - **Change Frida port** — Default port 27042 is a known detection vector. - **Consider Frida Gadget** — If frida-server is consistently detected, inject `frida-gadget.so` directly into the APK as a last resort. - **Check for certificate pinning** — After bypassing root detection, the app may also have SSL pinning that needs separate bypass for traffic interception. - **Server-side checks** — SafetyNet/Play Integrity cannot be bypassed with Frida alone. These require additional tools or approaches.
Mark as private (unlisted)
for 30 minutes
for 6 hours
for 1 day
for 1 week
for 1 month
for 1 year
everlasting (like CCCP)