DEV Community

Vignesh S
Vignesh S

Posted on

Android-Proguard

Proguard on android obfuscates code into some random letters and it makes hard to reverse engineer the code from the published apk.

Enabling proguard on a gradle build system is super easy as follows:

build.gradle (App):

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

Additionaly, enabling shrinkResources will remove the unused code and it drastically reduces the size of the generated apk.

After publishing your apk with proguard enabled and if your app crashes, throws exceptions or does not work properly, you will definitely get logs from third party crash analytics tool or you will try to get the logs directly by reproducing the steps.

With proguard enabled, your stack trace or logs could look like the below one:

06-25 22:19:34.407 14270 14311 E AndroidRuntime: Process: com.litekite.example, PID: 14270
06-25 22:19:34.407 14270 14311 E AndroidRuntime: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
06-25 22:19:34.407 14270 14311 E AndroidRuntime:        at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:8372)
06-25 22:19:34.407 14270 14311 E AndroidRuntime:        at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1444)
06-25 22:19:34.407 14270 14311 E AndroidRuntime:        at android.view.View.requestLayout(View.java:25010)
06-25 22:19:34.407 14270 14311 E AndroidRuntime:        at android.view.View.requestLayout(View.java:25010)
06-25 22:19:34.407 14270 14311 E AndroidRuntime:        at androidx.constraintlayout.widget.ConstraintLayout.requestLayout(Unknown Source:0)
06-25 22:19:34.407 14270 14311 E AndroidRuntime:        at android.view.View.requestLayout(View.java:25010)
06-25 22:19:34.407 14270 14311 E AndroidRuntime:        at android.view.View.requestLayout(View.java:25010)
06-25 22:19:34.407 14270 14311 E AndroidRuntime:        at android.widget.ImageView.setImageDrawable(ImageView.java:597)
06-25 22:19:34.407 14270 14311 E AndroidRuntime:        at androidx.appcompat.widget.AppCompatImageView.setImageDrawable(Unknown Source:0)
06-25 22:19:34.407 14270 14311 E AndroidRuntime:        at d.b.p.m.c(Unknown Source:26)
06-25 22:19:34.407 14270 14311 E AndroidRuntime:        at androidx.appcompat.widget.AppCompatImageView.setImageResource(Unknown Source:4)
06-25 22:19:34.407 14270 14311 E AndroidRuntime:        at com.litekite.example.ui.u(Unknown Source:9)
06-25 22:19:34.407 14270 14311 E AndroidRuntime:        at e.c.a.f.a.d.b(:4)
06-25 22:19:34.407 14270 14311 E AndroidRuntime:        at e.c.a.h.a.a(:10)
06-25 22:19:34.407 14270 14311 E AndroidRuntime:        at e.c.a.h.a$c.onCapabilitiesChanged(:3)
06-25 22:19:34.407 14270 14311 E AndroidRuntime:        at android.net.ConnectivityManager$NetworkCallback.onAvailable(ConnectivityManager.java:3261)
06-25 22:19:34.407 14270 14311 E AndroidRuntime:        at e.c.a.h.a$c.onAvailable(Unknown Source:0)
06-25 22:19:34.407 14270 14311 E AndroidRuntime:        at android.net.ConnectivityManager$CallbackHandler.handleMessage(ConnectivityManager.java:3474)
06-25 22:19:34.407 14270 14311 E AndroidRuntime:        at android.os.Handler.dispatchMessage(Handler.java:107)
06-25 22:19:34.407 14270 14311 E AndroidRuntime:        at android.os.Looper.loop(Looper.java:214)
06-25 22:19:34.407 14270 14311 E AndroidRuntime:        at android.os.HandlerThread.run(HandlerThread.java:67) 

From the above stack trace, it is quite hard to find out the cause of the exception, because the code were obfuscated into some magical letters.

To solve this problem, you really need to deobfuscate the stack trace to find out the cause.

For this deobfuscation, download the Retrace Script which is packaged with Proguard

Download retrace.sh from Guardsquare:

https://www.guardsquare.com/en/products/proguard/manual/retrace

To deobfuscate the stack trace, you need to have two files as an input for this tool to generate the original stack trace.

1) Mapping.txt file - has all the letter mappings of the obfuscated code.

You can get this mapping file from your app build directory:

root_directory/SampleApp/app/build/outputs/mapping/<build-type>/

You can also upload your mapping.txt to Google Play Store or some online crash analytics platform like Firebase that produces deobfuscated stack trace that can be downloaded for quick analysis.

2) Stacktrace.txt file - the obfuscated stack trace.

The last step that is required to get the original stack trace is to run the retrace.sh tool script.

$ retrace.sh -verbose mapping.txt stacktrace.txt > original_stacktrace.txt

The original_stacktrace.txt file would look like this:

2020-06-25 20:11:49.091 5838-5867/com.litekite.example E/AndroidRuntime: FATAL EXCEPTION: ConnectivityThread
    Process: com.litekite.example, PID: 5838
    java.lang.IllegalArgumentException: No enum constant e.c.a.h.a.b.4
        at java.lang.Enum.valueOf(Enum.java:257)
        at com.litekite.example.wifi.WifiController$WifiLevel.valueOf(Unknown Source:2)
        at com.litekite.example.wifi.WifiController.updateWifiState(:4)
        at com.litekite.example.wifi.WifiController$networkCallback$1.com.litekite.example.wifi.WifiController.access$updateWifiState(:3)
                                                                       onCapabilitiesChanged
        at android.net.ConnectivityManager$NetworkCallback.onAvailable(ConnectivityManager.java:3261)
        at com.litekite.example.wifi.WifiController$networkCallback$1.com.litekite.example.wifi.WifiController.access$setSsid$p(Unknown Source:0)
                                                                       onAvailable
                                                                       onAvailable
                                                                       com.litekite.example.wifi.WifiController.access$updateWifiState
                                                                       onAvailable
        at android.net.ConnectivityManager$CallbackHandler.handleMessage(ConnectivityManager.java:3474)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at android.os.Looper.loop(Looper.java:214)
        at android.os.HandlerThread.run(HandlerThread.java:67)

Restoring a stack trace with line numbers will require the below extra options:

-renamesourcefileattribute SourceFile
-keepattributes SourceFile,LineNumberTable

Discussion (2)

Collapse
mdev88 profile image
Martín Vukovic

A dangerous mistake is to forget that the obfuscation only takes place in the release build (if configured like in this article), not in debug, so maybe the app works fine when you are testing it by running it in Android Studio, but when you generate the relase bundle or APK it explores in your face, so be sure to test your app in release before publishing!

Collapse
svignesh93 profile image
Vignesh S Author

Yes, you're correct. Testing debug apk without configuring proguard, and failing to test release app with proguard configured, the app will have many surprises. 👍