Skip to content

Application code not obfuscated

Application code not obfuscated

Description

Obfuscation refers to methods to obscure code and make it hard to understand. Compiled Java classes can be decompiled if there is no obfuscation during compilation step. It works by modifying the code and metadata of the app in a way that makes it difficult for attackers to understand the code and modify it to introduce vulnerabilities or add malicious functionality. Obfuscators typically achieve this by renaming classes, methods, and variables to non-meaningful names, removing debugging information, and adding bogus code to confuse decompilers.

Adversaries can steal code and sensitive information, introduce vulnerabilities that can compromise the app's security, or repurpose and sell the code in a new application or create a malicious fake application based on the initial one. If this code is not obfuscated, it can be easily decompiled using tools like Apktool or JADX.

Code obfuscation only slows the attacker from reverse engineering but does not make it impossible.

Recommendation

Below are steps you can consider to make your application less prone to reverse engineering :

Obfuscate Java source code with Proguard.

To add Proguard to you application, add the following to the build.gradle file:

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

This tells Gradle to use ProGuard for code obfuscation in the release build. You can then create a "proguard-rules.pro" file in the app's "app" directory to configure the obfuscation rules.

Example proguard-rules.pro:

# Keep the names of classes in this package
-keep class com.example.myapplication.** { *; }

# Keep all public and protected methods in these classes
-keepclassmembers class com.example.myapplication.** {
    public protected *;
}

# Keep all native method names
-keepclassmembers class * {
    native <methods>;
}

# Keep all fields with the specified name
-keepclassmembers class * {
    private int myField;
}

# Keep all classes that implement Parcelable
-keep class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator *;
}

# Keep all Enum classes
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

# Keep all annotations
-keepattributes *Annotation*

# Preserve all entry points (like main methods)
-keep public class * {
    public static void main(java.lang.String[]);
}

# Keep all Serializable classes
-keep class * implements java.io.Serializable {
    *;
}

Obfuscate Java source code with Dexguard.

To enable Dexguard in your application, you can add the following to the build.gradle file:

buildTypes {
        release {
            minifyEnabled true
            useProguard false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            dexguard {
                config 'dexguard-release.cfg'
            }
        }
    }

This tells Gradle to use DexGuard for code obfuscation in the release build. You can create a"dexguard-project.txt" file in the app's "app" directory to configure the DexGuard project, and a"dexguard-release.cfg" file to configure the obfuscation for the release build.

By default, when you enable code obfuscation using DexGuard, it will use its own obfuscation rules in addition to any rules specified in the ProGuard configuration file. However, you can disable the use of ProGuard's rules by setting the useProguard option to false.

  • Verification application signing certificate during runtime by checking context.getPackageManager().signature
  • Check application installer to ensure it matches the Android Market by calling context.getPackageManager().getInstallerPackageName
  • Check running environment at runtime

Check if the app is running on an emulator

You can add the following check to your application to detect if it's running on an emulator.

private static String getSystemProperty(String name) throws Exception {
    Class systemPropertyClazz = Class.forName("android.os.SystemProperties");
    return (String) systemPropertyClazz.getMethod("get", new Class[] { String.class }).invoke(systemPropertyClazz, new Object[] { name });
}

public static boolean checkEmulator() {

    try {
        boolean goldfish = getSystemProperty("ro.hardware").contains("goldfish");
        boolean qemu = getSystemProperty("ro.kernel.qemu").length() > 0;
        boolean sdk = getSystemProperty("ro.product.model").equals("sdk");

        if (qemu || goldfish || sdk) {
            return true;
        }

    } catch (Exception e) {}

    return false;
}

Check debug flag at runtime

Similarly, you can check if the application is in debug mode during runtime

    context.getApplicationInfo().applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE;

Standards

  • OWASP_MASVS_RESILIENCE:
    • MSTG_RESILIENCE_9
    • MSTG_RESILIENCE_12
  • PCI_STANDARDS:
    • REQ_2_2
  • OWASP_MASVS_v2_1:
    • MASVS_RESILIENCE_3