混淆介紹
Proguard是一個Java類文件壓縮器藕咏、優(yōu)化器、混淆器秽五、預(yù)校驗(yàn)器侈离。壓縮環(huán)節(jié)會檢測以及移除沒有用到的類、字段筝蚕、方法以及屬性。優(yōu)化環(huán)節(jié)會分析以及優(yōu)化方法的字節(jié)碼铺坞∑鹂恚混淆環(huán)節(jié)會用無意義的短變量去重命名類、變量济榨、方法坯沪。這些步驟讓代碼更精簡,更高效擒滑,也更難被逆向(破解)腐晾。
混淆后默認(rèn)會在工程目錄app/build/outputs/mapping/release(debug)下生成一個mapping.txt文件,這就是混淆規(guī)則丐一,我們可以根據(jù)這個文件把混淆后的代碼反推回源本的代碼藻糖,所以這個文件很重要,注意保護(hù)好库车。原則上巨柒,代碼混淆后越亂越無規(guī)律越好,但有些地方我們是要避免混淆的,否則程序運(yùn)行就會出錯洋满。
ProGuard常用操作
壓縮(Shrinking)
壓縮(Shrinking):默認(rèn)開啟晶乔,用以減小應(yīng)用體積,移除未被使用的類和成員牺勾,并且會在優(yōu)化動作執(zhí)行之后再次執(zhí)行(因?yàn)閮?yōu)化后可能會再次暴露一些未被使用的類和成員)正罢。
#關(guān)閉壓縮
-dontshrink
優(yōu)化(Optimization)
優(yōu)化(Optimization):默認(rèn)開啟,在字節(jié)碼級別執(zhí)行優(yōu)化驻民,讓應(yīng)用運(yùn)行的更快翻具。
#關(guān)閉優(yōu)化
#-dontoptimize
#表示proguard對代碼進(jìn)行迭代優(yōu)化的次數(shù),Android一般為5
-optimizationpasses n
混淆(Obfuscation)
混淆(Obfuscation):默認(rèn)開啟川无,增大反編譯難度呛占,類和類成員會被隨機(jī)命名,除非用keep保護(hù)懦趋。
-dontobfuscate #關(guān)閉混淆
-Keep
一顆星表示只是保持該包下的類名晾虑,而子包下的類名還是會被混淆;
-keep class pr.tongson.bean.*
兩顆星表示把本包和所含子包下的類名都保持仅叫;
-keep class pr.tongson.bean.**
(上面兩種方式保持類后帜篇,會發(fā)現(xiàn)類名雖然未混淆,但里面的具體方法和變量命名還是變了)
既可以保持該包下的類名诫咱,又可以保持類里面的內(nèi)容不被混淆;
-keep class pr.tongson.bean.*{*;}
既可以保持該包及子包下的類名笙隙,又可以保持類里面的內(nèi)容不被混淆;
-keep class pr.tongson.bean.**{*;}
保持某個類名不被混淆(但是內(nèi)部內(nèi)容會被混淆)
-keep class pr.tongson.bean.KeyBoardBean
保持某個類的 類名及內(nèi)部的所有內(nèi)容不會混淆
-keep class pr.tongson.bean.KeyBoardBean{*;}
保持類中特定內(nèi)容,而不是所有的內(nèi)容可以使用如下:
-keep class pr.tongson.bean.KeyBoardBean{
#匹配所有構(gòu)造器
<init>;
#匹配所有域
<fields>;
#匹配所有方法
<methods>;
}
(上面就保持住了KeyBoardBean這個類中的所有的構(gòu)造方法坎缭、變量竟痰、和方法)
可以在<fields>或<methods>前面加上private 、public掏呼、native等來進(jìn)一步指定不被混淆的內(nèi)容
-keep class pr.tongson.algorithm.Calculate{
#保持該類下所有的共有方法不被混淆
public <methods>;
#保持該類下所有的共有內(nèi)容不被混淆
public *;
#保持該類下所有的私有方法不被混淆
private <methods>;
#保持該類下所有的私有內(nèi)容不被混淆
private *;
#保持該類的String類型的構(gòu)造方法
public <init>(java.lang.String);
}
在方法后加入?yún)?shù)坏快,限制特定的方法(經(jīng)測試:僅限于構(gòu)造方法可以混淆)
-keep class pr.tongson.algorithm.Calculate{
public <init>(String);
}
要保留一個類中的內(nèi)部類不被混淆需要用 $ 符號
#保持Calculate中的MyClass不被混淆
-keep class pr.tongson.algorithm.Calculate$MyClass{*;}
使用Java的基本規(guī)則來保護(hù)特定類不被混淆,比如用extends憎夷,implement等這些Java規(guī)則莽鸿,如下:保持Android底層組件和類不要混淆
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.view.View
如果不需要保持類名,只需要保持該類下的特定方法保持不被混淆拾给,需要使用keepclassmembers祥得,而不是keep,因?yàn)閗eep方法會保持類名蒋得。
#保持ProguardTest類下test(String)方法不被混淆
-keepclassmembernames class pr.tongson.algorithm.Calculate{
public void test(java.lang.String);
}
如果擁有某成員级及,保留類和類成員
-keepclasseswithmembernames class pr.tongson.algorithm.Calculate
注意事項(xiàng)
jni方法
jni方法不可混淆,因?yàn)閚ative方法是要完整的包名類名方法名來定義的窄锅,不能修改创千,否則找不到缰雇;
#保持native方法不被混淆
-keepclasseswithmembernames class * {
native <methods>;
}
反射
反射用到的類混淆時(shí)需要注意:只要保持反射用到的類名和方法即可,并不需要將整個被反射到的類都進(jìn)行保持
AndroidMainfest中的類
AndroidMainfest中的類不混淆追驴,所以四大組件和Application的子類和Framework層下所有的類默認(rèn)不要進(jìn)行混淆械哟。
自定義的View
自定義的View默認(rèn)也不會被混淆
JSON對象類
與服務(wù)端交互時(shí),使用GSON殿雪、fastjson等框架解析服務(wù)端數(shù)據(jù)時(shí)暇咆,所寫的JSON對象類不混淆,否則無法將JSON解析成對應(yīng)的對象丙曙;
第三方
使用第三方開源庫或者引用其他第三方的SDK包時(shí)爸业,如果有特別要求,也需要在混淆文件中加入對應(yīng)的混淆規(guī)則亏镰;官方文檔一般都有混淆規(guī)則的扯旷,復(fù)制粘貼下即可。
有用到WebView的JS
有用到WebView的JS調(diào)用也需要保證寫的接口方法不混淆索抓,原因和第一條一樣钧忽;
Parcelable的子類和Creator靜態(tài)成員變量
Parcelable的子類和Creator靜態(tài)成員變量不混淆,否則會產(chǎn)生Android.os.BadParcelableException異常逼肯;
-keep class * implements Android.os.Parcelable {
# 保持Parcelable不被混淆
public static final Android.os.Parcelable$Creator *;
}
enum
使用enum類型時(shí)需要注意避免以下兩個方法混淆耸黑,因?yàn)閑num類的特殊性,以下兩個方法會被反射調(diào)用篮幢,見第二條規(guī)則大刊。
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
注解不能混淆
建議
建議:
發(fā)布一款應(yīng)用除了設(shè)minifyEnabled
為ture,你也應(yīng)該設(shè)置zipAlignEnabled
為true三椿,像Google Play強(qiáng)制要求開發(fā)者上傳的應(yīng)用必須是經(jīng)過zipAlign的缺菌,zipAlign可以讓安裝包中的資源按4字節(jié)對齊,這樣可以減少應(yīng)用在運(yùn)行時(shí)的內(nèi)存消耗搜锰。
混淆情況記錄
例子中使用:classA和classB男翰,在加混淆的情況下多種結(jié)果:
如果classA沒有被keep,則不會看到classA的class文件
如果classA沒有被keep纽乱,classB被保持,同時(shí)classB引用到了classA昆箕,這個時(shí)候能夠看到被混淆的classA的class文件鸦列,如顯示為a
如果classA中通過反射,獲取到classB鹏倘,那么classB的類名及反射用到的方法必須keep住
jar包混淆薯嗤,暴露出的類、方法纤泵、方法的參數(shù)需要keep住
情況說明:工程Demo依賴了小米渠道的依賴骆姐,小米依賴又依賴了Common镜粤,對Common進(jìn)行混淆但是不對小米渠道混淆,那么小米的依賴中使用到的Common中的類都需要keep住
參考
模版
#指定壓縮級別
-optimizationpasses 5
#不跳過非公共的庫的類成員
-dontskipnonpubliclibraryclassmembers
#混淆時(shí)采用的算法
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
#把混淆類中的方法名也混淆了
-useuniqueclassmembernames
#優(yōu)化時(shí)允許訪問并修改有修飾符的類和類的成員
-allowaccessmodification
#將文件來源重命名為“SourceFile”字符串
-renamesourcefileattribute SourceFile
#保留行號
-keepattributes SourceFile,LineNumberTable
#保持泛型
-keepattributes Signature
#保持所有實(shí)現(xiàn) Serializable 接口的類成員
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
#Fragment不需要在AndroidManifest.xml中注冊玻褪,需要額外保護(hù)下
-keep public class * extends android.support.v4.app.Fragment
-keep public class * extends android.app.Fragment
# 保持測試相關(guān)的代碼
-dontnote junit.framework.**
-dontnote junit.runner.**
-dontwarn android.test.**
-dontwarn android.support.test.**
-dontwarn org.junit.**
語法
-include {filename} 從給定的文件中讀取配置參數(shù)
-basedirectory {directoryname} 指定基礎(chǔ)目錄為以后相對的檔案名稱
-injars {class_path} 指定要處理的應(yīng)用程序jar,war,ear和目錄
-outjars {class_path} 指定處理完后要輸出的jar,war,ear和目錄的名稱
-libraryjars {classpath} 指定要處理的應(yīng)用程序jar,war,ear和目錄所需要的程序庫文件
-dontskipnonpubliclibraryclasses 指定不去忽略非公共的庫類肉渴。
-dontskipnonpubliclibraryclassmembers 指定不去忽略包可見的庫類的成員。
保留選項(xiàng)
-keep {Modifier} {class_specification} 保護(hù)指定的類文件和類的成員
-keepclassmembers {modifier} {class_specification} 保護(hù)指定類的成員带射,如果此類受到保護(hù)他們會保護(hù)的更好
-keepclasseswithmembers {class_specification} 保護(hù)指定的類和類的成員同规,但條件是所有指定的類和類成員是要存在。
-keepnames {class_specification} 保護(hù)指定的類和類的成員的名稱(如果他們不會壓縮步驟中刪除)
-keepclassmembernames {class_specification} 保護(hù)指定的類的成員的名稱(如果他們不會壓縮步驟中刪除)
-keepclasseswithmembernames {class_specification} 保護(hù)指定的類和類的成員的名稱窟社,如果所有指定的類成員出席(在壓縮步驟之后)
-printseeds {filename} 列出類和類的成員-keep選項(xiàng)的清單券勺,標(biāo)準(zhǔn)輸出到給定的文件
壓縮
-dontshrink 不壓縮輸入的類文件
-printusage {filename}
-dontwarn 如果有警告也不終止
-whyareyoukeeping {class_specification}
優(yōu)化
-dontoptimize 不優(yōu)化輸入的類文件
-assumenosideeffects {class_specification} 優(yōu)化時(shí)假設(shè)指定的方法,沒有任何副作用
-allowaccessmodification 優(yōu)化時(shí)允許訪問并修改有修飾符的類和類的成員
混淆
-dontobfuscate 不混淆輸入的類文件
-printmapping {filename}
-applymapping {filename} 重用映射增加混淆
-obfuscationdictionary {filename} 使用給定文件中的關(guān)鍵字作為要混淆方法的名稱
-overloadaggressively 混淆時(shí)應(yīng)用侵入式重載
-useuniqueclassmembernames 確定統(tǒng)一的混淆類的成員名稱來增加混淆
-flattenpackagehierarchy {package_name} 重新包裝所有重命名的包并放在給定的單一包中
-repackageclass {package_name} 重新包裝所有重命名的類文件中放在給定的單一包中
-dontusemixedcaseclassnames 混淆時(shí)不會產(chǎn)生形形色色的類名
-keepattributes {attribute_name,...} 保護(hù)給定的可選屬性灿里,例如LineNumberTable, LocalVariableTable, SourceFile, Deprecated, Synthetic, Signature, and
InnerClasses.
-renamesourcefileattribute {string} 設(shè)置源文件中給定的字符串常量