本文已授權(quán)微信公眾號(hào)「玉剛說」獨(dú)家發(fā)布。
這篇是我們「Java 混淆那些事」第五講,其實(shí)通過前四篇大家已經(jīng)能夠?qū)懗稣5幕煜?guī)則了,這一篇是簡單的介紹一下不怎么常用的一些命令,個(gè)人覺得重要的會(huì)單獨(dú)拿出來寫個(gè)例子。大家可以簡單看一遍用到的時(shí)候再來查或者直接去參考官方文檔。
輸入輸出選項(xiàng)
命令 | 解釋 |
---|---|
-include filename | 指定其他配置文件,可以指定多個(gè)。例如:-include proguard1.pro |
@filename | -include filename 的縮寫。例如:@proguard1.pro、@test/proguard2.pro |
-basedirectory directoryname | 配置后續(xù)出現(xiàn)相對(duì)路徑的基目錄。他默認(rèn)的基目錄是配置文件的目錄,如果不存在配置文件基目錄就是工作目錄。例如:-basedirectory 'C:\test' ,可以指定多次。注:「后續(xù)」這個(gè)詞很重要 |
-injars class_path | 指定要處理的文件( jar 或 aars,war,ear,zips,apks 或目錄)。默認(rèn)情況下,非類文件不會(huì)進(jìn)行更改。請(qǐng)注意如果目錄中有任何臨時(shí)類文件(例如由 IDE 創(chuàng)建)不想被修改,可以過濾類路徑中的條目。可以使用多個(gè) -injars 選項(xiàng)指定類路徑條目。 |
-outjars class_path | 指定輸出文件(jar 或 aars,wars,ear,zips,apks 或目錄)的名稱。將 -injars 選項(xiàng)的文件處理完成過后輸入到指定的文件。切記輸出文件不能覆蓋任何輸入文件,否則容易出錯(cuò)。為了更好的可讀性,可以使用多個(gè) -outjars 選項(xiàng)指定類路徑條目。如果不寫 -outjars 選項(xiàng),則不會(huì)有輸出。 |
-libraryjars class_path | 指定混淆時(shí)需要依賴的類庫文件( jar 或 aars,wars,ear,zips,apk s或目錄)。這些 jar 里面的文件不會(huì)處理和輸出,可以使用多個(gè) -libraryjars 選項(xiàng)指定類路徑條目。 |
-skipnonpubliclibraryclasses | 不處理 library 里面非 public 修飾的類。以加快處理速度并減少 ProGuard 的內(nèi)存使用量。 |
-dontskipnonpubliclibraryclasses | 處理 library 中非 public 類。從 4.5 版本開始這是默認(rèn)配置。 |
-dontskipnonpubliclibraryclassmembers | 處理 library 中public 的類成員。默認(rèn)不處理。 |
-keepdirectories [directory_filter] | 輸出文件中保留指定目錄。默認(rèn)情況下,目錄會(huì)被移除。這會(huì)減少輸出文件的大小。 |
-target version | 將類文件 java 版本處理為指定版本。默認(rèn)情況下,類文件的版本號(hào)保持不變。例如,我們給予 Java 5 寫的項(xiàng)目想使用在 Java 6 的環(huán)境下,就可以通過指定 -target 更改版本號(hào)并將其預(yù)先驗(yàn)證,將類文件升級(jí)。但是降級(jí)需謹(jǐn)慎可能會(huì)有一些問題。 |
-forceprocessing | 強(qiáng)制輸出,即使保證輸出文件每次都是最新狀態(tài)。 |
壓縮選項(xiàng)
命令 | 解釋 |
---|---|
-dontshrink | 關(guān)閉壓縮功能。默認(rèn)情況下,會(huì)開啟壓縮; |
-printusage [filename] | 把被壓縮的類和方法輸出到文件。主要用來驗(yàn)證自己的混淆規(guī)則正確不正確。 |
-whyareyoukeeping class_specification | 打印出壓縮過程中保留了這些類文件和類成員的具體原因。例如:-whyareyoukeeping class **Manager { *; }
|
混淆選項(xiàng)
命令 | 解釋 |
---|---|
-dontobfuscate | 關(guān)閉混淆功能。默認(rèn)情況下,開啟混淆。 |
-printmapping [filename] | 把重命名的類和類成員的新、舊名稱的映射文件輸入到指定文件。 |
-applymapping filename | 指定要重用的映射文件,映射文件中沒有的類和類成員會(huì)被混淆為新的名稱。如果代碼結(jié)構(gòu)發(fā)生根本變化,ProGuard 可能會(huì)打印出應(yīng)用映射文件導(dǎo)致沖突的警告。您可以通過 -useuniqueclassmembernames 在兩個(gè)混淆運(yùn)行中指定選項(xiàng)來降低此風(fēng)險(xiǎn)。只允許一個(gè)映射文件。僅在混淆時(shí)適用。 |
-obfuscationdictionary filename | 指定類、方法及字段混淆后時(shí)用的混淆字典。默認(rèn)使用 ‘a(chǎn)’,’b’ 等短名稱作為混淆后的名稱。 |
-classobfuscationdictionary filename | 指定類名的混淆字典。 |
-packageobfuscationdictionary filename | 指定包名的混淆字典。 |
-overloadaggressively | 開啟侵入性重載混淆。多個(gè)字段及方法允許同名,只要它們的參數(shù)及返回值類型不同。該選項(xiàng)可使處理后的代碼更小(及更難閱讀)。只有開啟混淆時(shí)可用。注:Dalvik 不能處理重載的靜態(tài)字段。 |
-useuniqueclassmembernames | 類和成員混淆的時(shí)候,使用唯一的名字。 |
-dontusemixedcaseclassnames | 不使用大小寫混合類名,注意,windows用戶必須為 ProGuard 指定該選項(xiàng),因?yàn)?windows 非大小寫敏感,輸出文件可能將會(huì)相互覆蓋。 |
-keeppackagenames [package_filter] | 不混淆指定的包名。有多個(gè)包名可以用逗號(hào)隔開。包名可以包含 ?、*、** 通配符,還可以在包名前加上 ! 否定符。只有開啟混淆時(shí)可用。如果你使用了 mypackage.MyCalss.class.getResource(""); 這些代碼獲取類目錄的代碼,就會(huì)出現(xiàn)問題。需要使用 -keeppackagenames 保留包名。 |
-flattenpackagehierarchy [package_name] | 將所有重命名的包重新打包到給定的單一包中。如果沒參數(shù)或字符串為空,包移動(dòng)到根包下。 |
-repackageclasses [package_name] | 把所有重命名的類重新打包到給定的單一包中。如果沒參數(shù)或字符串為空,類的包會(huì)被完全移除。此選項(xiàng)會(huì)覆蓋該 -flattenpackagehierarchy 選項(xiàng)。低版本的參數(shù)名是 -defaultpackage 。 |
-keepattributes [attribute_filter] | 保留任何可選屬性。過濾器是由逗號(hào)分隔的 JVM 及 ProGuard 支持的屬性列表。屬性名可以包含 ?、*、** 通配符,并且可以在屬性名前加上 ! 否定符。例如:處理庫文件時(shí)應(yīng)該加上 Exceptions,InnerClasses,Signature 屬性。同時(shí)保留 SourceFile 及 LineNumberTable 屬性使混淆后仍能獲取準(zhǔn)確的堆棧信息。同時(shí)如果你的代碼有使用注解你可能會(huì)保留 annotations 屬性。只有開啟混淆時(shí)可用。 |
-keepparameternames | 保留方法參數(shù)名稱和保留的方法類型。 |
-renamesourcefileattribute [string] | 指定一個(gè)常量字符串作為 SourceFile 屬性的值。需要被 -keepattributes 選項(xiàng)指定保留。只有開啟混淆時(shí)可用。 |
-adaptclassstrings [class_filter] | 混淆與完整類名一致的字符串。沒指定過濾器時(shí),所有符合現(xiàn)有類的完整類名的字符串常量均會(huì)混淆。只有開啟混淆時(shí)可用。 |
-adaptresourcefilenames [file_filter] | 以混淆后的類文件作為樣本重命名指定的源文件。沒指定過濾器時(shí),所有源文件都會(huì)重命名。 |
-adaptresourcefilecontents [file_filter] | 以混淆后的類文件作為樣本混淆指定的源文件中與完整類名一致的內(nèi)容。沒指定過濾器時(shí),所有源文件中與完整類名一致的內(nèi)容均會(huì)混淆。 |
優(yōu)化選項(xiàng)
命令 | 解釋 |
---|---|
-dontoptimize | 關(guān)閉優(yōu)化功能。默認(rèn)情況下啟用優(yōu)化。 |
-optimizations optimization_filter | 指定優(yōu)化的粒度規(guī)則,后面的參數(shù)是一個(gè)粒度過濾器,一般不做更改默認(rèn)即可。 |
-optimizationpasses n | 表示對(duì)你的代碼進(jìn)行迭代優(yōu)化的次數(shù)。參數(shù)是整數(shù)。一般來說設(shè)置為 10 以下即可,因?yàn)閮?yōu)化次數(shù)多了也不會(huì)有什么實(shí)質(zhì)性的優(yōu)化了,如果你有什么特殊業(yè)務(wù)需求,按自己需求調(diào)整就好了。 |
-assumenosideeffects class_specification | 優(yōu)化階段刪除指定代碼,比如: 刪除所有日志 -assumenosideeffects class com.Log { \*; }
|
-assumenoexternalsideeffects class_specification | 優(yōu)化階段刪除指定代碼,力度比 -assumenosideeffects 強(qiáng),因?yàn)樗梢詢?yōu)化參數(shù)或堆。例如,刪除日志記錄代碼時(shí),如果日志包含 String 拼接的字節(jié)碼就可以徹底刪除了。 -assumenosideeffects 是無法在字節(jié)碼層面刪除的。 |
-assumenoescapingparameters class_specification | 指定不允許其引用參數(shù)轉(zhuǎn)義到堆的方法。 這些方法可以使用,修改或返回參數(shù),但不能直接或間接地將它們存儲(chǔ)在任何字段中。 例如,System.arrayCopy 方法不允許其引用參數(shù)轉(zhuǎn)義,但方法 System.setSecurityManager 不會(huì)。 |
-assumenoexternalreturnvalues class_specification | 指定在調(diào)用時(shí)不返回已在堆上的引用值的方法。例如,ProcessBuilder#start 返回一個(gè) Process 引用值,但它是一個(gè)在堆上并沒有使用的新實(shí)例。 |
-allowaccessmodification | 優(yōu)化時(shí)允許優(yōu)化并修改類和類的成員的訪問修飾符。對(duì)外提供的 SDK 包需要注意此選項(xiàng)。 |
-mergeinterfacesaggressively | 指定接口可以合并,即使它們的實(shí)現(xiàn)類未實(shí)現(xiàn)合并后接口的所有方法。該選項(xiàng)可以通過減少類的總數(shù)減少輸出文件的大小。只有開啟優(yōu)化時(shí)可用。 |
保持選項(xiàng)
其他選項(xiàng)之前的文章已經(jīng)介紹過了,這里只介紹之前沒說過的。
命令 | 解釋 |
---|---|
-if class_specification | 如果 if 指定類和類成員存在,隨后的keep選項(xiàng)(-keep,-keepclassmembers,...)才會(huì)進(jìn)行匹配。 |
-printseeds [filename] | 將詳盡列出由各種 -keep 選項(xiàng)匹配的類和類成員列表輸出到指定文件。該列表可用于驗(yàn)證是否確實(shí)找到了預(yù)期的類成員,尤其是在使用通配符時(shí)。例如,您可能希望列出您保留的所有應(yīng)用程序或所有小程序。 |
預(yù)校驗(yàn)選項(xiàng)
命令 | 解釋 |
---|---|
-dontpreverify | 關(guān)閉預(yù)校驗(yàn)功能。默認(rèn)情況下,如果類文件針對(duì) Java ME 或 Java 6 或更高版本,則會(huì)對(duì)其進(jìn)行預(yù)驗(yàn)證。對(duì)于 Java ME ,需要預(yù)驗(yàn)證。對(duì)于 Java 6,預(yù)驗(yàn)證是可選的,但從 Java 7開始,它是必需的。如果類文件針對(duì) Android 時(shí),沒有必要,因此您可以將其關(guān)閉以減少處理時(shí)間。 |
-microedition | 指定已處理的類文件以 Java ME 為目標(biāo)。然后,預(yù)驗(yàn)證程序?qū)⑻砑舆m當(dāng)?shù)腟tackMap屬性,這些屬性與 Java SE 的默認(rèn) StackMapTable 屬性不同。例如,如果要處理 midlet,則需要此選項(xiàng)。 |
-android | 指定已處理的類文件以Android平臺(tái)為目標(biāo)。然后ProGuard確保某些功能與 Android 兼容。 |
常規(guī)選項(xiàng)
命令 | 解釋 |
---|---|
-verbose | 在處理期間打印更多信息。如果程序以異常終止,則此選項(xiàng)將打印出整個(gè)堆棧跟蹤,而不僅僅是異常消息。 |
-dontnote [class_filter] | 指定不打印有關(guān)配置中潛在錯(cuò)誤或遺漏的信息,例如配置中的類名拼寫錯(cuò)誤或可能缺失有用的選項(xiàng)。會(huì)有提示。 |
-dontwarn [class_filter] | 指定找不到引用或其他重要問題時(shí)不打印警告信息。例如,在某個(gè)類的引用中找不到相關(guān)類,會(huì)有警告提示。使用 dontwarn 就可以忽略提示。 |
-ignorewarnings | 打印找不到引用或其他重要問題的警告信息。 |
-printconfiguration [filename] | 將已解析的整個(gè)配置輸出到指定文件。 |
-dump [filename] | 指定將類文件的內(nèi)部結(jié)構(gòu)輸出到指定文件。 |
-addconfigurationdebugging | 指定用調(diào)試語句對(duì)處理過的代碼進(jìn)行測試,這些調(diào)試語句會(huì)打印出缺少 ProGuard 配置的建議。如果處理過的代碼由于仍然缺少一些反射配置而崩潰,他會(huì)提示一些簡易的配置。例如,代碼可能正在使用 GSON 庫序列化類,您可能需要對(duì)其進(jìn)行一些配置。通常,您可以將控制臺(tái)中的建議 復(fù)制/粘貼 到配置文件中。注:不要在發(fā)行版本中使用此選項(xiàng),因?yàn)樗鼤?huì)將混淆信息添加到處理過的代碼中。 |
選項(xiàng)參數(shù)格式
上面的命令后面有很多參數(shù)格式,我們來說一說各個(gè)參數(shù)怎么寫。
首先說一下帶 [] 的參數(shù)是可選的不是必填,而不帶 [] 的參數(shù)是必填。
參數(shù)名 | 作用 |
---|---|
filename | 表示具體文件名,可以是相對(duì)路徑,也可以是絕對(duì)路徑。比如 test/test111.txt 、test111.txt
|
string | 表示隨便一段字符串,比如 "xxx"
|
class_filter | 表示類過濾器,具體用法參考上一章類名過濾器 |
class_path | 表示類文件路徑,可以是 jars, aars, wars, ears, zips, apks 或目錄,可以是相對(duì)路徑,也可以是絕對(duì)路徑。 |
file_filter | 表示文件過濾器,具體用法參考上一篇文件相關(guān)的過濾器 |
class_specification | 表示類規(guī)范是類和類成員的模板,上一篇有提到 |
directory_filter | 表示目錄過濾器,比如 com/http 、com/* 具體用法參考上一篇文件相關(guān)的過濾器 |
package_name | 表示包名 com.http
|
package_filter | 表示包名過濾器,比如 com.*
|
version | 表示 Java 版本號(hào)可以是 1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7 (7)、1.8(8)、1.9(9)或 10 。 |
還有兩個(gè)指令不是一兩句話可以說明,我們單獨(dú)拿出來講。
optimization_filter
如果有多個(gè)規(guī)則,可以使用通配符或 ,(逗號(hào)) 隔開寫多個(gè)規(guī)則。在調(diào)整優(yōu)化粒度規(guī)則的時(shí)候,希望你能夠研究明白再使用,以免出現(xiàn)什么問題。
支持三個(gè)通配符 ? 、 * 、! ,它們代表什么我就不多說了。
我列舉一下固定的,其他的不穩(wěn)定的我就不列舉了,有需要希望大家去看看幫助文檔。
規(guī)則 | 作用 |
---|---|
class/marking/final | 盡可能將類標(biāo)記為 fianl 類。 |
class/unboxing/enum | 盡可能將枚舉類型簡化為整數(shù)常量。 |
class/merging/vertical | 垂直合并類 ( 上下層級(jí)的類 進(jìn)行合并 )。 |
class/merging/horizontal | 水平合并類 ( 同一層級(jí)的類 進(jìn)行合并 )。 |
field/removal/writeonly | 移除只讀字段。 |
field/marking/private | 盡可能將字段標(biāo)記為私有。 |
field/propagation/value | 在方法中傳遞屬性值。 |
method/marking/private | 盡可能將方法標(biāo)記為私有。 |
method/marking/static | 盡可能將方法標(biāo)記為靜態(tài)。 |
method/marking/final | 盡可能將方法標(biāo)記為 final 方法。 |
method/removal/parameter | 刪除沒有用到的方法。 |
method/propagation/parameter | 將方法參數(shù)的值從方法調(diào)用傳到調(diào)用的方法。 |
method/propagation/returnvalue | 將方法返回的值從方法傳到它們的調(diào)用處。 |
method/inlining/short | 合并比較短的方法。 |
method/inlining/unique | 合并只調(diào)用一次的方法。 |
method/inlining/tailrecursion | 簡化尾部遞歸調(diào)用。(尾遞歸轉(zhuǎn)循環(huán)) |
code/merging | 合并相同的代碼塊。 |
code/simplification/variable | 變量加載和存儲(chǔ)時(shí),使用窺孔優(yōu)化(peephole optimization)技術(shù),一種優(yōu)化技術(shù)。 |
code/simplification/arithmetic | 對(duì)算術(shù)指令進(jìn)行窺孔優(yōu)化。 |
code/simplification/cast | 對(duì)類型轉(zhuǎn)換進(jìn)行窺孔優(yōu)化。 |
code/simplification/field | 對(duì)屬性加載和存儲(chǔ)使用窺孔優(yōu)化。 |
code/simplification/branch | 對(duì)分支指令使用窺孔優(yōu)化。 |
code/simplification/string | 對(duì)常量字符串使用窺孔優(yōu)化,最好使用 code/removal/variable 刪除。 |
code/simplification/advanced | 基于控制流分析和數(shù)據(jù)流分析簡化代碼。 |
code/removal/advanced | 基于控制流分析和數(shù)據(jù)流分析刪除死代碼。 |
code/removal/simple | 基于簡單控制流分析簡化代碼。 |
code/removal/variable | 從本地變量中,刪除未使用的變量。 |
code/removal/exception | 當(dāng) try-catch 中,try塊內(nèi)為空時(shí),刪除exceptions。 |
code/allocation/variable | 在本地變量中優(yōu)化變量分配。 |
attribute_filter
類文件實(shí)質(zhì)上定義了類,它們的字段和方法。許多基本和非必要數(shù)據(jù)作為屬性附加到這些類,字段和方法。例如,屬性可以包含字節(jié)碼,源文件名,行號(hào)表等。ProGuard 的混淆步驟刪除了執(zhí)行代碼通常不需要的屬性。如果我們需要保留就需要使用 -keepattributes 來進(jìn)行保留。同樣多個(gè)使用逗號(hào),支持三個(gè)通配符 ? 、 * 、!
可選的屬性
可選項(xiàng) | 解釋 |
---|---|
*Annotation* | 注解。 |
SourceFile | 從中編譯類文件的源文件的名稱。 |
SourceDir | 從中編譯類文件的源目錄的名稱。 |
InnerClasses | 類及其內(nèi)部類和外部類之間的關(guān)系。 |
EnclosingMethod | 定義類的方法。 |
Deprecated | 表示不推薦使用類,字段或方法。 |
Synthetic | 表示編譯器生成了類,字段或方法。 |
Signature | 類、字段或方法的通用簽名,代碼可以通過反射訪問此簽名。 |
MethodParameters | 方法參數(shù)的名稱和訪問標(biāo)志。 |
Exceptions | 指定方法可能拋出的異常。 |
LineNumberTable | 方法的行號(hào)。 |
LocalVariableTable | 方法的局部變量的名稱和類型。 |
LocalVariableTypeTable | 方法的局部變量的名稱和泛型類型。 |
RuntimeVisibleAnnotations | 在運(yùn)行時(shí),類,字段和方法可見的注釋。 |
RuntimeInvisibleAnnotations | 在編譯時(shí)對(duì)類,字段和方法可見的注釋。 |
RuntimeVisibleParameterAnnotations | 方法參數(shù)在運(yùn)行時(shí)可見的注釋。 |
RuntimeInvisibleParameterAnnotations | 方法參數(shù)在編譯時(shí)可見的注釋。 |
RuntimeVisibleTypeAnnotations | 在運(yùn)行時(shí)可見的注釋,用于泛型類型,指令等。 |
RuntimeInvisibleTypeAnnotations | 在編譯時(shí)可見的注釋,用于泛型類型,指令等。 |
AnnotationDefault | 注釋的默認(rèn)值。 |
例子
flattenpackagehierarchy
// 混淆規(guī)則
-keep class **Manager {
<fields>;
<methods>;
}
-flattenpackagehierarchy "xxx"
混淆前的結(jié)構(gòu)

混淆后的結(jié)構(gòu)

- 所有改變包名的都放到了 xxx 包下
repackageclasses
// 混淆規(guī)則
-keep class **Manager {
<fields>;
<methods>;
}
-repackageclasses "xxx"
混淆前的結(jié)構(gòu)

混淆后的結(jié)構(gòu)

- 所有改變類名的類都放到了 xxx 包下
adaptclassstrings
// 混淆規(guī)則
-keep class **Manager {
<fields>;
<methods>;
}
-keepclassmembers,allowobfuscation class **{
public java.lang.String get();
}
-adaptclassstrings
// 混淆前的代碼
public String get() {
String ss1 = "com.test.http.HttpRequest";
return "請(qǐng)求成功 "+ss1;
}
// 混淆后的代碼
public String a() {
return "請(qǐng)求成功 " + "com.test.a.a";
}
- 混淆與類名相同的字符串,用途大家可以想一想,比如反射的時(shí)候可以用。
Class.forName("com.test.http.HttpRequest")
adaptresourcefilenames
// 混淆規(guī)則
-keep class **Manager {
<fields>;
<methods>;
}
-adaptresourcefilenames
混淆前的資源文件

混淆后的資源文件

- 混淆與類名相同的資源文件
renamesourcefileattribute
// 混淆腳本
-keepattributes SourceFile
-renamesourcefileattribute "renamesourcefileattribute Test"
// 代碼
/* compiled from: renamesourcefileattribute Test */
public final class a {
}
發(fā)現(xiàn)保留了 SourceFile 屬性,并且指定了一個(gè)字符串。
adaptresourcefilecontents
// 混淆腳本
-adaptresourcefilecontents
//混淆前的資源文件
test:com.test.http.HttpRequest
//混淆后的資源文件
test:com.test.a.a
- 大家可以想想用途,比如 Android 中想混淆 Activity 就需要修改清單文件類路徑,這個(gè)屬性就可以自動(dòng)修改了。
小結(jié)
這一篇內(nèi)容比較多大家過一遍就好,如果你自己覺得還有其他能用到的選項(xiàng)就需要自己動(dòng)手去試一試效果了。