← Back to list

Is ProGuard or R8 Enough to Secure Your Android App? Think Again!

Many developers assume that enabling ProGuard or R8 is enough to secure their Android apps. But is that really the case?

Rycco Atika · 2025-02-21 12:04 · 1 claps · 7.1 min read
#android-security #reverse-engineering #android-development #proguard #dexguard
Open on Medium ↗
Wiki topics: 📱 · Mobile Development

Is ProGuard or R8 Enough to Secure Your Android App? Think Again!

Photo by Jake Walker on Unsplash

Photo by Jake Walker on Unsplash

Introduction

Many developers assume that enabling ProGuard or R8 is enough to secure their Android apps from reverse engineering. But is that really the case? While ProGuard and R8 designed for app optimization. In this article, we will analyze how different levels of protection impact an Android APK and whether ProGuard or R8 alone is enough to prevent decompilation.

ProGuard and R8 were designed for app optimization, and although they employ minimal obfuscation techniques, they are not security tools and do not harden applications effectively against reverse engineering and tampering.

We’ll compare the decompiled output of an APK with:

1. No protection (unobfuscated APK)

2. ProGuard obfuscation

3. R8 obfuscation

4. Advanced code hardening techniques (DexGuard, DProtect, etc.)

By the end, you’ll understand why a multi-layered security approach is necessary to protect your app.

The Sample Project: A Simple Validation App

To demonstrate the effects of different levels of obfuscation and security measures, I created a basic Android app with a simple number and word validation logic.

How It Works:

• The app has two input fields: one for a number and another for a word.

• When the user enters values and taps the “Click Me” button, the app checks whether the inputs match a predefined secret condition.

• If the inputs are correct, the app displays “Correct!” in green text; otherwise, it shows “Wrong!” in red text.

Here’s a simplified version of the core logic:

clickMeButton.setOnClickListener {
    val number = numberInput.text?.toString()?.toIntOrNull() ?: 0
    val word = wordInput.text?.toString() ?: ""

    val isValid = checkMagicInformation(number, word)

    with(label) {
        isVisible = true
        if (isValid) {
            text = "Correct!"
            setTextColor(Color.GREEN)
        } else {
            text = "Wrong!"
            setTextColor(Color.RED)
        }
    }
}

private fun checkMagicInformation(arg1: Int, arg2: String): Boolean {
    return arg1 * MAGIC_NUMBER == 10 && arg2.removeRange(0, 1) == MAGIC_WORD
}

companion object {
    private const val MAGIC_NUMBER = 2
    private const val MAGIC_WORD = "wdlissfm"
}

What’s Interesting About This Code?

• It contains hardcoded constants (MAGIC_NUMBER and MAGIC_WORD) that act as secrets for validation.

• The logic is straightforward, making it easy to decompile and analyze if the APK isn’t properly obfuscated or hardened.

• By decompiling this app under different protection levels, we can see how effective (or ineffective) ProGuard, R8, and code hardening techniques really are.

With the sample project in place, let’s dive into how an unprotected APK looks when decompiled.

1. The Reality of Unprotected APKs

When an APK is compiled without any obfuscation or code hardening, it becomes highly vulnerable to reverse engineering. Attackers can easily decompile the APK using tools like **JADX, Bytecode Viewer, or APKTool** to inspect the original code.

One major risk is that sensitive constants — such as the MAGIC_NUMBER and MAGIC_WORD in our sample project — are stored in plaintext within the compiled APK. These values are not encrypted or hidden, making them easily retrievable from the decompiled .class or .smali files.

Even more concerning is that the business logic remains fully readable. For example, after decompiling the APK, we can clearly see:

Since class names, method names, and logic remain intact, an attacker can immediately understand how the validation function works. They can easily modify the APK to bypass security checks, extract hardcoded values, or manipulate apps behavior using tools such as frida.

This is a huge risk for applications that rely on obfuscation alone for security, as it allows hackers to reverse engineer authentication mechanisms, licensing checks, and encryption logic with minimal effort.

2. ProGuard: The First Line of Defense?

ProGuard is a code shrinker and obfuscator that renames symbols (class names, methods, and variables) and removes unused code. It makes static analysis more difficult but does not provide encryption, anti-debugging, or runtime protection.

Limitations of ProGuard:

Strings remain visible, including API keys, error messages, and URLs.

Class structures are still recognizable, making it possible to understand the overall app architecture.

Can be deobfuscated using tools like Procyon or CFR.

While ProGuard adds some level of obfuscation, it’s far from enough to secure sensitive business logic.

3. R8: More Than ProGuard, But Still Not Secure

R8 is the replacement for ProGuard in Android’s build system, offering better performance and optimizations. It inlines functions, removes unused code, and provides slightly stronger obfuscation compared to ProGuard. However, despite these improvements, R8 does not offer real security — it primarily focuses on code size reduction and optimization rather than actual protection.

During testing, I used **Androguard to decompile the R8-optimized APK, as JADX was unable to process all functions properly. Even after R8’s obfuscation, the MAGIC_NUMBER and MAGIC_WORD remain in plaintext** inside the decompiled files.

While R8 inlines a lot of code and introduces complexity, it does not make the decompiled output unreadable. Attackers with experience in reverse engineering can still analyze the logic and extract critical information. The function below, taken from the decompiled R8 output, still clearly exposes the validation logic:

Decompiled w/ Androguard

Decompiled w/ Androguard

Limitations of R8:

Still susceptible to decompilation — even though some functions are inlined, attackers can still analyze the code structure.

Does not protect against runtime attacks like memory inspection, dynamic hooking, or tampering.

Certain methods remain exposed, making it easier to modify app behavior with tools like Frida.

While R8 is an improvement over ProGuard, it still does not provide real security — it only makes code slightly harder to read.

4. Code Hardening: A Stronger Approach with Advanced Protection

For real security, code hardening adds extra layers of protection beyond ProGuard and R8, making reverse engineering significantly more challenging. Instead of just renaming classes and methods, these techniques modify how the code is structured and executed:

String Encryption — Protects hardcoded strings like API keys, credentials, and sensitive data by dynamically encoding them.

Control-Flow Obfuscation — Rearranges execution flow with unnecessary jumps, loops, and conditional branches, making it harder to analyze.

Arithmetic Obfuscation — Replaces simple mathematical operations with more complex expressions to obscure logic without changing functionality.

There are various commercial and open-source solutions for code hardening. While DexGuard is a powerful enterprise tool, it requires a paid license. For this demonstration, I used DProtect, which is free and open-source.

However, not all code should be hardened. Some techniques impact performance and increase APK size, so developers must carefully decide which parts of their code truly need protection while maintaining a balance between security and efficiency.

Bonus: Dex-Protect Using nmmp Open-Source Tools

Dex-Protect leverages nmmp, an open-source tool designed to significantly increase the difficulty of decompilation by running Dalvik bytecode within a custom dex-vm. This method prevents direct access to the original DEX structure, making static analysis much harder.

By dynamically executing transformed bytecode, Dex-Protect prevents traditional decompilers like JADX or Androguard from retrieving meaningful information. This technique adds an additional security layer beyond standard obfuscation, making reverse engineering significantly more difficult.

The code snippet in the image shows that the onClick function is declared as a native void, meaning that the actual implementation is handled in a JNI (Java Native Interface) C++ library rather than in Java/Kotlin. This suggests that when the onClick method is triggered, it does not execute Java bytecode but instead invokes a native function implemented in C or C++.

🚨 Caution: While nmmp is an open-source project, it is not an officially trusted security tool. Be cautious when using it, as it may introduce security risks or trigger unwanted detections. Always review and test such tools in a controlled environment before implementing them in production.

🚀 Want to try it out? Check out the nmmp project on GitHub: nmmp on GitHub.

5. So, Is ProGuard/R8 Enough?

The short answer: No.

ProGuard and R8 are great for code size reduction and basic obfuscation, but they are not security tools. If your app contains sensitive logic, hardcoded secrets, or authentication mechanisms, relying only on ProGuard or R8 is a major security risk.

Best Practices for Android App Security:

🔹 Implement string encryption to hide sensitive values.

🔹 Use DexGuard/DProtect for advanced obfuscation, optimization and runtime protection.

🔹 Add tamper detection to prevent unauthorized modifications.

🔹 Implement root and emulator detection to prevent debugging.

By combining multiple layers of protection, you make it significantly harder for attackers to reverse-engineer or modify your app.

Final Thoughts

If you’re relying solely on ProGuard or R8 for security, you’re not really securing your app — you’re just making it slightly harder to read. While they offer basic code shrinking and obfuscation, they don’t prevent reverse engineering, memory analysis, or runtime attacks.

For true protection, you need a combination of obfuscation, encryption, and runtime defenses to make your app more resilient against attackers. Think your app is secure? Try decompiling your APK and see how much of your logic is exposed! 🔍

Bonus: The Rooted Android Reality

Did you know that almost every Android app today can run on a rooted device, even banking apps? This creates a huge security risk because attackers can analyze memory dumps, modify app logic, and bypass security measures using tools like Frida and Xposed.

Without proper root detection and runtime protection, a determined hacker can hook into sensitive functions, disable security checks, and even extract encryption keys — all in real-time. So, if your app handles sensitive data, ask yourself: is it really protected against runtime attacks?

Next Steps

🔹 Test your APK with JADX, Bytecode Viewer, or APKTool.

🔹 Experiment with ProGuard, R8, and advanced tools to see the differences.

🔹 Explore DexGuard/DProtect if your app requires stronger protection.

Let me know your thoughts in the comments — do you think ProGuard/R8 is enough? Or have you encountered reverse engineering attempts on your apps? 🚀

References

https://obfuscator.re/dprotect/

https://www.guardsquare.com/blog/proguard-and-r8

https://www.guardsquare.com/manual/configuration/usage


메타데이터
post_id
18de9575589b
slug
is-proguard-or-r8-enough-to-secure-your-android-app-think-again-18de9575589b
url
https://medium.com/@ryccoatika/is-proguard-or-r8-enough-to-secure-your-android-app-think-again-18de9575589b
canonical_url
https://medium.com/@ryccoatika/is-proguard-or-r8-enough-to-secure-your-android-app-think-again-18de9575589b
author_url
https://medium.com/@ryccoatika
status
ok
fetched_at
2026-08-10 12:11:19