Android-Proguard
Tags: android, proguard, obfuscation
Published: 2020-08-10 15:53:53

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:

```groovy
build.gradle (App):

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

Additionaly, enabling <b><mark>shrinkResources</mark></b> 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 <b><mark>Retrace Script</mark></b> which is packaged with <b><mark>Proguard</mark></b>

```
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 <b><mark>mapping.txt</mark></b> 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 <b><mark>retrace.sh</mark></b> 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
```