Proguard and obfuscation

ProGuard and Obfuscation

As the number of Android-based mobiles has been increased, the threats and malware targeting Android more than before. It means that we need to employ more techniques to protect the applications. Some malware tries to exploit the application by analyzing the code or memory. One of the techniques we can use to make it harder is utilizing some tools such as ProGuard. ProGuard has been known as an open-source obfuscator for Java code. Though it can shrink and optimize the Java code as well. But Let’s talk a bit about obfuscation and why it’s important first.

What is Obfuscation?

Obfuscation is a method used in software development to deter reverse engineering or tampering with an application. By transforming the code into an equivalent, but harder to understand version, obfuscation makes it more difficult for an attacker to understand and manipulate the code. The obfuscated code retains the same functionality as the original, but the variable names, class names, and method names are typically replaced with nonsensical and non-descriptive alternatives.

ProGuard and its Role in Obfuscation

ProGuard is an open-source tool used to obfuscate Java and Kotlin code, and it’s integrated into the Android build system. It not only obfuscates the code but also shrinks and optimizes it, resulting in a smaller and potentially more efficient application. By removing unused code and renaming classes, methods, and fields with short, meaningless names, ProGuard makes it difficult for a person or a program to reverse engineer the application.

The minifyEnabled flag enables or disables ProGuard. The proguardFiles line specifies the ProGuard configuration files. The first file, proguard-android-optimize.txt, is the default configuration file provided by the Android SDK. The second file, proguard-rules.pro, is where you can add project-specific rules.

An Example of ProGuard’s Obfuscation

To better understand the effect of ProGuard’s obfuscation, let’s consider an example. Suppose you have a simple Person class like this:

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

After obfuscation with ProGuard, the Person class might look something like this:

public class a {
private String a;
private int b;

public a(String a, int b) {
    this.a = a;
    this.b = b;
}

public String a() {
    return a;
}

public int b() {
    return b;
}

}

The most important features of ProGuard

Code Shrinking:

ProGuard identifies and removes unused code from your application, including unused classes, methods, and fields. This reduces the size of your application, making it quicker to download and taking up less space on the user’s device.

Code Obfuscation:

ProGuard renames classes, methods, and fields with short, meaningless names. This makes your code harder to understand if someone tries to decompile your APK, adding a layer of protection against reverse engineering.

Code Optimization:

ProGuard optimizes your bytecode, making your app potentially run faster. It does this by performing tasks like inlining methods, removing unused fields, and optimizing loops.

Preverification:

ProGuard also verifies your processed code for Java Micro Edition (JME) and for Java 6 and higher, which have a stack map format.

Here’s how you can enable and use these features in Gradle in Android Studio:

Firstly, make sure the ProGuard settings are included in your build.gradle file:

android {
    ...
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    ...
}

In the example above, minifyEnabled true enables code shrinking and obfuscation. The proguardFiles line specifies the ProGuard configuration files that should be used.

The getDefaultProguardFile(‘proguard-android-optimize.txt’) line gets the default ProGuard settings from the Android SDK. There are two main default files:

  • proguard-android.txt: This is the base configuration file. It provides general shrinking and optimization options.
  • proguard-android-optimize.txt: This extends the base file with additional optimization configurations.

The ‘proguard-rules.pro’ file is where you can add any custom ProGuard rules for your project.

The rules in ProGuard are quite extensive and flexible, allowing you to define how your code should be obfuscated and shrunk. Here’s an example of what some rules might look like in your proguard-rules.pro file:

# Keep - don't obfuscate any class that extends android.app.Activity
-keep public class * extends android.app.Activity

# Don't obfuscate or remove any members or methods that are being used in XML layouts
-keepclassmembers class * {
  @android.view.View$* <methods>;
}

# Don't obfuscate or remove any native method
-keepclassmembers class * {
  native <methods>;
}

Pros and Cons of Using ProGuard

Pros:

Code Protection:

ProGuard’s obfuscation makes your code more difficult to reverse engineer, helping to protect your intellectual property and sensitive information.

Application Size Reduction:

ProGuard removes unused code and renames the remaining code with shorter names, which can significantly reduce the size of your application.

Performance Optimization:

ProGuard can inline methods, remove unused fields, and perform other optimizations that can make your code run faster.

Cons:

Troubleshooting:

Debugging an issue in obfuscated code can be challenging due to the changed names and structure. This is why ProGuard is typically only used in release builds.

Configuration Complexity:

ProGuard can be complex to configure correctly, especially for large projects. You might need to add specific rules to prevent certain code from being obfuscated or shrunk.

Runtime Reflection:

If your application uses reflection, you will need to carefully configure ProGuard to avoid breaking your code. ProGuard doesn’t know about reflection calls, so it might remove or rename a field or method that is actually being used through reflection.

3rd party libraries

Enabling Proguard might impact the imported libraries in the project. To avoid any unforeseen issues in the build-time or even runtime, you need to check the recommended Proguard configurations. Here I have provided some examples on some of the most widely used libraries.

Examples:

Retrofit

Retrofit relies heavily on reflection, so you’ll need to add rules to keep your model classes from being obfuscated.

-keepclassmembers class * {
    @retrofit2.http.* <methods>;
}
Gson

Gson uses reflection to serialize/deserialize JSON, so your model classes need to be preserved:

-keep class com.example.models.** { *; }
Room

Room’s annotations and reflection-based operations also require specific rules:

-keepclassmembers class androidx.room.* { *; }
-keepclassmembers interface androidx.room.* { *; }

In the world of Android development, ProGuard is a valuable tool for protecting your application against reverse engineering, reducing the size of your application, and optimizing its performance. For sure, it does come with some challenges, such as making debugging harder and requiring careful configuration. Understanding how to use and configure ProGuard can help to build secure and efficient applications.

Link to Book: Secure Android Development: Best Practices for Robust Apps

Leave a Comment

Your email address will not be published. Required fields are marked *