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