ProGuard 初探,新手入門必知必會

1. Start up

下載并安裝proguard,當前是5.3.3,解壓并將bin所在目錄設置到系統(tǒng)環(huán)境變量中,接著你就可以使用命令行工具開始ProGuard之旅了。
1.1 @myconfig.pro / -include myconfig.pro
指定使用myconfig.pro作為ProGuard文件

>proguard @mycofnig.pro
>proguard -include myconfig.pro

1.2 -injars
指定要進行proguard的類文件集合(jar包、aar包、war包、zip包、apk報或者目錄都可以)

>proguard -injars in.jar
>proguard -injars in.zip

1.3 -outjars
指定proguard后要輸出的類文件集合,可以包含的類型同上

>proguard -outjars out.jar
>proguard -outjars out.zip

1.4 -libraryjars
指定所使用的類依賴的文件集合,可以包含的類型同上 ,注意

>proguard -libraryjars lib.jar

1.5 target
指定要使用的jdk版本,可選項是:1.0、1.1、1.2、1.3、1.4、1.5(或5)、1.6(或6)、1.7(或7)

>proguard -target 8

1.6 總之就是這一套技能打下去就好了。

proguard -include myconfig.pro -injars in.jar -libraryjars rt.jar -outjars out.jar -verbose

2. Keep options(保留選項)

為了方便學習,本著實踐出真知的想法,我們開始定義一些類。
// MainClass.java

public class MainClass{

    public static void main(String[] args){
        private User user = new User(23333,"Shaoxia",new Clothes("uniqlo","jeans","straw hat"));
        System.out.println(user.intro());
    }
}

//User.java

public class User{
    private String name;
    private int id;
    private Clothes clothes;
    
    public User(int id,String name, Clothes clothes){
        this.id = id;
        this.name = name;
        this.clothes = clothes;
    }
    
    public String intro(){
        return this.toString();
    }
    
    public void setName(String name){
        this.name = name;
    }
    
    public String getName(){
        return this.name;
    }
    
    public void setId(int id){
        this.id =id;
    }
    
    public int getId(){
        return this.id;
    }
    
    @Override
    public String toString(){
        return String.format("[name: %s,id: %d]",name,id);
    }
}

// Clothes.java

public class Clothes{
    private String shirt;
    private String trousers;
    private String hat;
    public Clothes(String shirt, String trousers, String hat){
        this.shirt = shirt;
        this.trousers = trousers;
        this.hat = hat;
    }
    
    public String getShirt(){
        return this.shirt;
    }
    
    public String getTrousers(){
        return this.trousers;
    }
    
    public String getHat(){
        return this.hat;
    }
}

2.1 -keep [,modifier,...] specification
指定不混淆指定的類,包括類名和成員名。
//myconfig.pro

-keep class MainClass{*;}
-keep class User{*;}

運行proguard后再使用jd-gui進行反編譯,結果如下:
// MainClass

import java.io.PrintStream;

public class MainClass
{
  public static void main(String[] paramArrayOfString)
  {
    User localUser = new User(23333, "Shaoxia", new a("uniqlo", "jeans", "straw hat"));
    System.out.println(localUser.intro());
  }
}

// User

public class User
{
  private String name;
  private int id;
  private a clothes;
  
  public User(int paramInt, String paramString, a parama)
  {
    this.id = paramInt;
    this.name = paramString;
    this.clothes = parama;
  }
  
  public String intro()
  {
    return toString();
  }
  
  public void setName(String paramString)
  {
    this.name = paramString;
  }
  
  public String getName()
  {
    return this.name;
  }
  
  public void setId(int paramInt)
  {
    this.id = paramInt;
  }
  
  public int getId()
  {
    return this.id;
  }
  
  public String toString()
  {
    return String.format("[name: %s,id: %d]", new Object[] { this.name, Integer.valueOf(this.id) });
  }
}

// Clothes

public class a
{
  private String a;
  private String b;
  private String c;
  
  public a(String paramString1, String paramString2, String paramString3)
  {
    this.a = paramString1;
    this.b = paramString2;
    this.c = paramString3;
  }
}

2.2 -keepclassmembers [, mobifier,...] class spcification
指定保留指定類的的成員,包括成員變量和成員方法。
//myconfig.pro

-keepclassmembers class Clothes{*;}

于是乎,Clothes的所有成員就都被保留了。

public class a
{
  private String shirt;
  private String trousers;
  private String hat;
  
  public a(String paramString1, String paramString2, String paramString3)
  {
    this.shirt = paramString1;
    this.trousers = paramString2;
    this.hat = paramString3;
  }
  
  public String getShirt()
  {
    return this.shirt;
  }
  
  public String getTrousers()
  {
    return this.trousers;
  }
  
  public String getHat()
  {
    return this.hat;
  }
}

2.3 keepclasseswithmembers [, modifier,...] class specification
指定不混淆類和成員。這時Clothes就回到原有的模樣,除了方法參數(shù)變了之外。

2.4 keepnames
保留類名,是 -keep, allowshrinking class specification的簡寫,意思就是如果該類沒有在shrink階段被移除的話,就不混淆其類名。

-keepnames class Clothes{*;}

混淆結果:

public class Clothes
{
  private String shirt;
  private String trousers;
  private String hat;
  
  public Clothes(String paramString1, String paramString2, String paramString3)
  {
    this.shirt = paramString1;
    this.trousers = paramString2;
    this.hat = paramString3;
  }
}

2.5 -keepclassmembernames class specification
指定保留類的成員名,是-keepclassmembers, allowshrinking class specification 的簡寫。

-keepclassmembernames class Clothes{*;}

混淆結果是:

public class a
{
  private String shirt;
  private String trousers;
  private String hat;
  
  public a(String paramString1, String paramString2, String paramString3)
  {
    this.shirt = paramString1;
    this.trousers = paramString2;
    this.hat = paramString3;
  }
}

2.6 -keepclasseswithmembernames class specification
-keepclasseswithmembers, allowshrinking class specification的簡寫,指定保留類名和類成員名,除非被shrink掉了。

-keepclasseswithmembernames class User{*;}

混淆結果是:

public class User
{
  private String name;
  private int id;
  private a clothes;
  
  public User(int paramInt, String paramString, a parama)
  {
    this.id = paramInt;
    this.name = paramString;
    this.clothes = parama;
  }
  
  public String intro()
  {
    return toString();
  }
  
  public String toString()
  {
    return String.format("[name: %s,id: %d]", new Object[] { this.name, Integer.valueOf(this.id) });
  }
}

2.7 -printseeds [filename]
打印出那些被keep住的類和成員,結果輸出到指定文件里。

-keep class MainClass{*;}
-keepclasseswithmembernames class User{*;}
-printseeds whatkeeped.txt

輸出結果是:

MainClass
MainClass: MainClass()
MainClass: void main(java.lang.String[])
User
User: java.lang.String name
User: int id
User: Clothes clothes
User: User(int,java.lang.String,Clothes)
User: java.lang.String intro()
User: void setName(java.lang.String)
User: java.lang.String getName()
User: void setId(int)
User: int getId()
User: java.lang.String toString()

3. Shrink Options(壓縮選項)

3.1 -dontshrink
不壓縮。壓縮功能會把除了被keep住和直接或間接依賴的類和成員會保留外,其他的類或成員都會被移除。
3.2 -printusage [filename]
指定輸出那些沒用的代碼(dead code)到文件中。

Clothes:
    12:12:public java.lang.String getShirt()
    16:16:public java.lang.String getTrousers()
    20:20:public java.lang.String getHat()
User:
    17:18:public void setName(java.lang.String)
    21:21:public java.lang.String getName()
    25:26:public void setId(int)
    29:29:public int getId()

3.3 -whyareyoukeeping class specification
指定打印一些日志告訴你為什么特定的類或者類成員在壓縮階段被保留了。

-whyareyoukeeping class User{ 
    public java.lang.String intro(...);
}

控制臺輸出:

User
  is invoked by    MainClass: void main(java.lang.String[]) (4:6)
  is kept by a directive in the configuration.

User: java.lang.String intro() (13:13)
  is invoked by    MainClass: void main(java.lang.String[]) (4:6)
  is kept by a directive in the configuration.

4. Optimization options(優(yōu)化選項)

4.1 -dontoptimize
不優(yōu)化。默認優(yōu)化是開啟的,所有的方法都會在字節(jié)碼級別進行優(yōu)化。
4.2 -optimizations optimization filter
這是一個高級選項,會進行細粒度的優(yōu)化。
4.3 optimizationpasses n
指定進行優(yōu)化的通道,默認是單通道。如果一個優(yōu)化通道后沒有提升,那么優(yōu)化就結束了。
4.4 -assumenosideeffeccts class specification
假定優(yōu)化對指定的類沒有副作用。意思就是說開啟該選項,ProGuard會移除對程序功能沒有影響的代碼,比如打印日志的代碼,這個選項很危險,要慎重使用。

-assumenosideeffects class android.util.Log { 
    public static boolean isLoggable(java.lang.String, int); 
    public static int v(...); 
    public static int i(...); 
    public static int w(...); 
    public static int d(...); 
    public static int e(...); 
} 

4.5 -allowaccessmodification
允許修改限定符,類以及類成員的修飾符在優(yōu)化步驟會被加寬。這個選項不適合那些類庫,因為會導致本來為是no public的變成public,導致API暴露。
4.6 -mergeinterfacesaggressively
這個選項允許接口被合并,能減少類的數(shù)量,從而達到優(yōu)化的目的。當然這并不總是很用,在JIT編譯器中,反而會降低性能,因為JIT傾向于多接口少實現(xiàn)。

5. Obfuscation option(混淆選項)

5.1 -dontobfuscate
不混淆。默認啟用混淆,類和類成員名會變成短小且隨機的名字。可通過keep選項來保留一些。
5.2 -printmapping [filename]
打印混淆前后的名稱對應關系到文件中,以便crash后進行對照。例中打印的結果是:

Clothes -> a:
    void <init>(java.lang.String,java.lang.String,java.lang.String) -> <init>
MainClass -> MainClass:
    void <init>() -> <init>
    void main(java.lang.String[]) -> main
User -> User:
    java.lang.String name -> name
    int id -> id
    Clothes clothes -> clothes
    void <init>(int,java.lang.String,Clothes) -> <init>
    java.lang.String intro() -> intro
    java.lang.String toString() -> toString

5.3 -applymapping filename
指定重用之前生成的mapping文件,這樣原先混淆的類名和成員名就不會再變了,這個對于那些增量式打補丁式的混淆很有用。當然如果項目結構從根本上就變動了,那么用這種方式就很有風險了,這時候可以使用-useuniqueclasssmembernames來降低風險。
5.4 obfuscationdictionary filename
混淆字典。默認混淆使用的是a、b、c之類的作為類名和文件名。使用這個選項可以自己指定一套字典供混淆使用。不過空白符、 標點符號、重復的單詞和#號后面的注釋會被忽略。我試了下:

-obfuscationdictionary dict.txt

果然:

public final class b
{
  private String Fuck;
  private int Fck = 23333;
  
  public b(int paramInt, String paramString, a parama)
  {
    this.Fuck = paramString;
  }
  
  public final String toString()
  {
    return String.format("[name: %s,id: %d]", new Object[] { this.Fuck, Integer.valueOf(this.Fck) });
  }
}

5.5 -packageobfuscationdictionary filename
將指定文件里的所有合法的單詞作為混淆的包名使用。
5.6 -overloadaggressively
強制進行方法重載,多個不用的成員變量或者方法會被混淆成同一名字,只要它們的參數(shù)和返回值不用即可。
5.7 -useuniqueclassmembernames
指定原本一樣的成員名,混淆后還是一樣;原本不一樣的成員名,混淆后還是不同。這樣對于后面的mapping和增量更新很有用。
5.8 -dontusemixedcaseclassnames
不要生成大小寫混合的類名,這樣就不至于在一些大小寫不敏感的系統(tǒng)上軟件包不可用。注意:默認情況下,混淆會生成大小寫混合的類名的。
5.9 -keeppackagenames [package filter]
指定不要對過濾器中的包名進行混淆。包名過濾器是一個可以包含?、等通配符在內的使用逗號分隔的包名列表。
5.10 -flattenpackagehierarchy [package_name]
指定重打包,將所有的包移動到單個指定的父級包名。如果沒有指定或者指定了空字符串,那么會被移動到根包。
5.11 -repackageclasses [package_name]
指定重打包所有被混淆的類文件,將所有的類移動到單個指定的包。該選項重寫自-flattenpackagehierarchy
5.12 -keepattributes [attribute-filter]
指定保留列表中的屬性,列表仍然是可以使用?、
*的用逗號分隔的屬性。

-keepattributes Exceptions,InnerClasses,Signature,Deprecated,
                SourceFile,LineNumberTable,*Annotation*,EnclosingMethod

5.13 -keepparameternames
保留參數(shù)名。這對于類庫來說很有用,因為可以展示有意義的參數(shù)名供調用者調用。
5.14 -renamesourcefileattribute [string]
指定一個字符串常量作為類文件的源文件屬性的前綴。
5.15 -adaptclassstrings [class filter]
更改類字符串。開啟該選項會將跟類名相符的字符串常量也一并混淆了。-adaptresourcefilenames [file filter]-adaptresourcefilecontents [file filter]見名知意。

6. Preverification options(預校驗)

6.1 -dontpreverify
不要預校驗。預校驗在Java Micro Edition 或者Java 6以上可用。在Java 6和Android上是可選項,在Java Micro Edition 和Java 7以上是必選的。關閉預校驗可以減少處理時間。
6.2 -microedition
指定處理的類文件是面向Java Micro Edition的。

7. 通用選項

7.1 -verbose
控制臺輸出詳細的Proguard日志。
7.2 -dontnote [class filter]
不提示過濾器中的類名。
7.3 -dontwarn [class filter]
不要警告過濾器中的類,忽略警告繼續(xù)proguard。這個對于忽略第三方類庫中的類的警告很有用。
7.4 -ignorewarnings
控制臺輸出所有未解析的應用和其他重要的問題,但是忽略,繼續(xù)執(zhí)行proguard。
7.5 -printconfiguration [filename]
指定輸出所有的配置到指定文件中。這些配置包括混淆文件和替換的變量。
7.6 -dump [filename]
寫出處理后的類文件的結構。

-injars in.jar 

-dontshrink 
-dontoptimize 
-dontobfuscate 
-dontpreverify 

-dump 

參考

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,237評論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,957評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,248評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,356評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,081評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,485評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,534評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,720評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 49,263評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,025評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,204評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,787評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,461評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,874評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,105評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,945評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,205評論 2 375

推薦閱讀更多精彩內容

  • 混淆(Proguard)用法 最近項目中遇到一些混淆相關的問題,由于之前對proguard了解不多,所以每次都是面...
    于曉飛93閱讀 56,841評論 38 230
  • 說明:本文參考(翻譯)自Android SDK根目錄下的proguard目錄下的說明文檔,是其中的一篇。,文中除了...
    一件小毛衣閱讀 85,246評論 9 85
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,806評論 18 139
  • 什么是代碼混淆 代碼混淆就是將代碼中的各種元素,如變量,方法,類和包的名字改寫成無意義的名字,增加項目反編譯后被讀...
    蝸牛家族史閱讀 5,191評論 1 4
  • 當前是有些工具比如apktool,dextojar等是可以對我們android安裝包進行反編譯,獲得源碼的。為了減...
    碼農明明桑閱讀 29,647評論 2 38