Android的程式碼模糊
關於程式碼模糊
Java 的運作方式是會先編譯出中間碼,然後才由 JVM 去轉成機器碼,藉此達到跨平台。這樣的特性導致從中間碼回推原始程式碼是可以輕易達到的,因此,為了讓拿到你程式的人沒辦法輕易解讀你的 source code,你需要執行程式碼模糊。
他的基本原理是把變數名稱,方法名稱通通用無意義的字母取代,例如你原本的 method 長這樣:
1 | api.login( user.getId() , user.getPassword() ); |
執行完之後可能會變成這樣:
1 | a.c( bb.a() , bb.b() ); |
Android 上的程式碼模糊工具
如果你是使用 Android Studio 開發,那執行程式碼模糊是一件很簡單的事情,你只要去\app\build.gradle這個檔案裡面把minifyEnabled設為 true 就完成了…
當然,很多時候事情沒有這麼單純 XD
如同上面提到的,模糊工具會改變 method 的名稱,所以只要有用到 JNI,或者使用 java 的反射去找 method 的程式,通通都會出錯,這時候你就會發現,原本開發中直接送進手機的 debug APK 都能用,但匯出 Signed APK 的時候就會編譯失敗。
這問題特別容易出現在你有引入 third-party library 的時候,畢竟自己寫的自己會注意,別人寫的往往會忘記。
於是,你需要一些補救措施。
編譯失敗怎麼辦?
1.修改 DefaultProguardFile 文件
在剛剛設定minifyEnabled的底下還有一行proguardFiles,用來指定 Proguard 的設定文件,把原始的proguard-android.txt改成proguard-android-optimize.txt (不確定該步驟是否必要)
2.加入例外清單
該步驟才是重點,請在\app\proguard-rules.pro內針對有需要保留名稱的 class 或者 method 設定例外,並關閉警告。
例如如果你要把 android support library 相關的 class 全部保留,就這樣寫:
-keep class android.support.** { *; }
或者你只要保留一個 method:
-keep class com.example.MyClass {
public void myMethod(int);
}
然後是把某個 class 關閉警告:
-dontwarn com.example.MyClass.**
至於有哪些需要被保留,就要自行參考編譯失敗的錯誤訊息了 XD
其他用法
1.停用 Logcat:輸出正式 apk 的時候,往往需要把所有 debug 用的 log 拿掉,除了慢慢註解之外,可以直接用 Proguard 挖掉:
-assumenosideeffects class android.util.Log {
public static *** d(...);
public static *** v(...);
public static *** i(...);
public static *** w(...);
public static *** e(...);
}
2.縮小檔案:關於 Proguard 這個工具,除了可以模糊之外,他還會把你整個專案內沒有用到的 class,以及方法通通過濾掉,連編譯都不編譯,再加上混淆後的變數名稱通常會變短,於是結果就是最後的 apk 檔會變小。
缺點
執行程式碼模糊有個顯而易見的缺點,就是程式當機的時候,你收到的 Call Stack 也是模糊後的結果,所以你也看不懂(汗)
推薦使用Firebase的 Crash Report 功能,他可以上傳 mapping 檔,把有沒有模糊的兩種結果對照起來,有相關需求的人可以考慮採用。