Java 混淆那些事(一):重新認(rèn)識 ProGuard

本文已授權(quán)微信公眾號「玉剛說」獨(dú)家發(fā)布。

大家好,你現(xiàn)在看到的是「Java 混淆那些事」系列文章的第一篇,通過這個系列我想帶大家重新認(rèn)識一下 ProGuard 到底能干什么?最終領(lǐng)悟怎么才能寫好混淆規(guī)則。所以說這個系列文章的重點(diǎn)將會放到書寫 keep 規(guī)則上面。我會最大程度用大白話寫明白。

首先我們了解一下 ProGuard 到底是什么能干什么?

ProGuard 是可以對 Java 類文件進(jìn)行壓縮、優(yōu)化、混淆和預(yù)驗(yàn)證的工具。

簡單解釋一下 ProGuard 的功能

  • 壓縮 (Shrinker):刪除無效的類、字段、方法等。
  • 優(yōu)化 (Optimizer):優(yōu)化字節(jié)碼,合并方法,刪除無用字段等。
  • 混淆 (Obfuscator):將類名、屬性名、方法名以及字段名混淆為難以讀懂的字母,比如a, b, c等。
  • 預(yù)校驗(yàn) (Preverifier):對 class 文件進(jìn)行預(yù)檢驗(yàn),確保虛擬機(jī)加載的 class 文件是安全并且可以執(zhí)行的。

我們再來看下一個問題 ProGuard 是以什么樣的流程進(jìn)行工作的。

  1. 壓縮階段
    ProGuard 會從「代碼入口點(diǎn)」開始遞歸查找,把用到的類或變量等留下來,沒用到的全都刪掉。

  2. 優(yōu)化階段
    ProGuard 會優(yōu)化經(jīng)過壓縮階段留下來的類,比如將外部沒有調(diào)用的非代碼入口點(diǎn)的方法或類改為私有的,又或者把一部分方法改為 final 的,相應(yīng)的字段改為 static、final,或者把幾個方法合并成一個,刪除沒有用到的參數(shù)等等的優(yōu)化操作。

  3. 混淆階段
    將代碼入口點(diǎn)調(diào)用到的類和方法(非代碼入口點(diǎn)方法),給他改個名字,比如簡短的或者復(fù)雜的,這個過程中重命名的字典可以自定義。改完名字后還能保證程序的正常運(yùn)行邏輯。

  4. 預(yù)校驗(yàn)階段
    在編譯版本為 Java ME 或 1.6 以及更高版本時是默認(rèn)開啟的。但編譯成 Android版本時,預(yù)校驗(yàn)是不必須的。

那么代碼入口點(diǎn)到底是什么呢?

好的現(xiàn)在就忘記以上這些廢話,我們重點(diǎn)來看第一個知識點(diǎn)「代碼入口點(diǎn)」。我們剛才應(yīng)該也看到了 ProGuard 壓縮階段是從代碼入口點(diǎn)開始遞歸查找用到的代碼的。

舉個例子:比如你寫了一個很方便的下載類,假設(shè)需要使用的就這一個方法 new DowonloadClien("url").start() 那么這個方法就應(yīng)該指定為代碼入口點(diǎn)。

ProGuard 怎么知道哪里是代碼入口點(diǎn)的呢?
沒錯這個代碼入口點(diǎn)如果我們不告訴 ProGuard,他是不會知道的。那么怎么告訴他呢?我們通過 keep 規(guī)則就可以告訴 ProGuard 了,具體用法我們以后文章中具體說,這里就了解一下。

舉個例子

下面我們寫一個通俗的小例子,配合代碼理解一下??纯磯嚎s、優(yōu)化、混淆這些功能。

//測試代碼,如下代碼純屬為了測試,除此之外沒有任何合理性。
src
 -> model
    -> ModelA.java
        int testA = 2;
        
        public void modelA(int age) {
            int a = 1 + age;
            int b = testA + age;
            System.out.println("print " + b);
        }
        
        public void modelB(String name) {
            System.out.println("print " + name);
        }

    -> ModelB.java
        public void modelA(String name) {
            System.out.println("print " + name);
        }

        public void modelB(String name) {
            System.out.println("print " + name);
        }

 -> utils
    -> UtilsA.java
        private static final String UtilA = "utila";

        public static void printA() {
            System.out.println("print " + UtilA);
        }

        public static void printB() {
            System.out.println("print B");
        }

    -> UtilsB.java
        public static void printA(){
            System.out.println("print A");
        }

        public static void printB(){
            System.out.println("print B");
        }

 Main.java
        public static Main sMain = null;

        public static void main(String[] args) {
            sMain = new Main();
            sMain.run();
        }

        private void run() {
            ModelA modelA = new ModelA();
            modelA.modelA(5);
            UtilsA.printA();
        }

//我們先不添加任何混淆參數(shù),混淆之后的結(jié)果

src
 -> a
    -> a.java
        private int a = 2;
        public final void a(int i) {
            System.out.println("print " + (this.a + 5));
        }

 -> defpackage
    -> Main.java
        private static Main a = null;

        public static void main(String[] strArr) {
            a = new Main();
            new a().a(5);
            System.out.println("print utila");
        }

對比一下混淆前和混淆后的 Jar 包內(nèi)容

看到幾個很顯然的效果

  1. 沒有被代碼入口點(diǎn)調(diào)用到的類、方法都刪除了。
  2. 定義的多個變量也都合并到一起了,甚至完全消失不見了。
  3. 很多方法也進(jìn)行了合并。
  4. 除了代碼入口點(diǎn)之外,留下來方法名和變量名全都改變了。
  5. 優(yōu)化了代碼,可以看到上面 public static Main sMain = null; 混淆完自動給改成了 private。
  6. 他還會自動把一部分方法優(yōu)化為 final 的。

為什么 Main 這個類以及 main 方法沒有被混淆呢?

在 ProGuard 默認(rèn)生成的配置文件下有個條匹配規(guī)則

-keepclasseswithmembers public class * {
    public static void main(java.lang.String[]);
}

解釋一下:匹配每個類里面的 main 方法為代碼入口點(diǎn),如果沒有任何一個類有 main 方法。那么我們的上面的例子就是空的文件了,因?yàn)樵趬嚎s階段就已經(jīng)把所有代碼全都刪了。

main 方法是 Java 應(yīng)用程序的入口方法,程序運(yùn)行執(zhí)行的第一個方法。

小結(jié)

經(jīng)過這個小例子,除了預(yù)校驗(yàn)之外,其他特性我們都已經(jīng)明顯的看到了。概念也大概的懂了。恭喜你打怪升級成功,快去看看下一篇吧。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容