← Back to home

May 25, 2026

Bypassing Android Root Checks with Smali Patching — No Frida Required

A practical Android reverse-engineering guide based on static APK patching: finding root checks, modifying smali code, rebuilding the APK, and verifying the result in a lab environment.

This guide is based on my video about bypassing Android root checks using static Smali patching.

The idea is simple: instead of using Frida at runtime, we modify the APK itself. We decompile the app, find where it checks for root, patch the logic, rebuild the APK, sign it, and run it again.

This is useful for learning how Android apps protect themselves and how static analysis works in practice.

Use this only in a lab, on your own apps, intentionally vulnerable apps, or apps where you have permission to test. Do not patch or redistribute third-party apps.


What We Are Doing

Many Android apps try to detect root by checking things like:

In this guide, we focus on the static approach:

  1. Decompile the APK.
  2. Search through smali code.
  3. Find the root detection method.
  4. Change the return value.
  5. Rebuild and sign the APK.
  6. Install and test it.

Tools Needed

Install the basic tooling first:

sudo apt update
sudo apt install apktool jadx zipalign apksigner adb

You can also use Android Studio if you prefer a GUI for device management and logs.

Useful tools:


Step 1 — Decompile the APK

Create a working directory and decompile the APK:

mkdir android-root-check-lab
cd android-root-check-lab

apktool d app.apk -o app_src

After this, you will see files like:

app_src/
├── AndroidManifest.xml
├── smali/
├── smali_classes2/
├── res/
└── apktool.yml

The interesting part is usually inside smali/ or smali_classes*/.


Step 2 — Search for Root Detection Logic

Start with simple keyword searches:

grep -Rni "root" app_src/
grep -Rni "su" app_src/
grep -Rni "magisk" app_src/
grep -Rni "isDeviceRooted" app_src/

Common suspicious names:

isRooted
isDeviceRooted
checkRoot
detectRoot
checkSuExists
checkForBinary
checkForDangerousProps

If the app uses RootBeer or similar libraries, you may see method names that make the logic obvious.


Step 3 — Use JADX to Understand the Code

Open the APK in JADX:

jadx-gui app.apk

Look for classes related to security, root checks, startup logic, or login screens.

The Java-like code is easier to read than smali, but the actual patch will be done in smali.

Example Java-like logic:

public boolean isDeviceRooted() {
    if (checkSuBinary()) {
        return true;
    }

    if (checkMagisk()) {
        return true;
    }

    return false;
}

In smali, this may become a method returning Z, which means boolean.


Step 4 — Understand Boolean Return Values in Smali

In smali:

.method public isDeviceRooted()Z

The Z means the method returns a boolean.

Boolean values are usually represented like this:

const/4 v0, 0x1
return v0

That means:

return true

And this:

const/4 v0, 0x0
return v0

Means:

return false

So if we find a method that returns whether the device is rooted, we can patch it to always return false.


Step 5 — Patch the Root Check

Example original method:

.method public isDeviceRooted()Z
    .locals 1

    invoke-static {}, Lcom/example/security/RootChecker;->checkSuBinary()Z
    move-result v0

    if-nez v0, :rooted

    invoke-static {}, Lcom/example/security/RootChecker;->checkMagisk()Z
    move-result v0

    if-nez v0, :rooted

    const/4 v0, 0x0
    return v0

:rooted
    const/4 v0, 0x1
    return v0
.end method

Patched version:

.method public isDeviceRooted()Z
    .locals 1

    const/4 v0, 0x0
    return v0
.end method

Now the method always returns false.

The app will think the device is not rooted.


Step 6 — Another Common Patch: Flip a Conditional Jump

Sometimes it is easier to change the condition instead of rewriting the whole method.

Example:

if-nez v0, :root_detected

This means:

if v0 is not zero, jump to root_detected

You can sometimes change it to:

if-eqz v0, :root_detected

But be careful. Flipping conditions without understanding the flow can break the app.

For beginners, patching the root-check method to return false is usually cleaner.


Step 7 — Rebuild the APK

After patching the smali file, rebuild the APK:

apktool b app_src -o app_patched_unsigned.apk

If you get an error, read the line number carefully. Common issues:


Step 8 — Align the APK

Before signing, align the APK:

zipalign -p -f 4 app_patched_unsigned.apk app_patched_aligned.apk

Step 9 — Sign the APK

Generate a debug keystore if you do not already have one:

keytool -genkeypair \
  -v \
  -keystore debug.keystore \
  -alias androiddebugkey \
  -keyalg RSA \
  -keysize 2048 \
  -validity 10000 \
  -storepass android \
  -keypass android

Sign the APK:

apksigner sign \
  --ks debug.keystore \
  --ks-key-alias androiddebugkey \
  --ks-pass pass:android \
  --key-pass pass:android \
  --out app_patched.apk \
  app_patched_aligned.apk

Verify the signature:

apksigner verify --verbose app_patched.apk

Step 10 — Install and Test

Uninstall the original app first if needed:

adb uninstall com.example.app

Install the patched APK:

adb install app_patched.apk

Run the app and check whether the root warning is gone.

You can also watch logs:

adb logcat | grep -i root

Or:

adb logcat | grep -i security

Common Problems

The App Does Not Install

Possible causes:

Try:

adb uninstall com.example.app
adb install app_patched.apk

The App Crashes After Launch

Possible causes:

Check logs:

adb logcat | grep -i AndroidRuntime

Root Detection Still Works

Possible causes:

Search again:

grep -Rni "root" app_src/
grep -Rni "su" app_src/
grep -Rni "magisk" app_src/
grep -Rni "jail" app_src/
grep -Rni "tamper" app_src/

Smali Cheat Sheet

Boolean return:

const/4 v0, 0x0
return v0

Means:

return false

Boolean true:

const/4 v0, 0x1
return v0

Means:

return true

Method returning boolean:

.method public someMethod()Z

Method returning void:

.method public someMethod()V

Method returning string:

.method public someMethod()Ljava/lang/String;

Static Patching vs Frida

Frida is dynamic. It changes app behavior at runtime.

Smali patching is static. It changes the APK itself.

Frida is faster for experiments, but smali patching is great for understanding how the app works internally.

Frida approach

Good for:

Bad for:

Smali patching approach

Good for:

Bad for:


Final Thoughts

Root detection is not real security by itself. It is only one layer.

A serious Android security model should also include:

But from a reverse-engineering perspective, root checks are a great first target. They are easy to understand, easy to test, and they teach the basics of smali patching very well.

If you are learning Android security, this is one of the best exercises to start with: simple enough to follow, but close enough to real-world mobile app protection techniques.


Video Topic

This article is based on the video:

How to Bypass Android Root Checks via Smali Patching — No Frida. Part 2

YouTube: https://youtu.be/WC_FdEfaFug