Java 混淆那些事(五):ProGuard 其餘的選項

本文已受權微信公衆號「玉剛說」獨家發佈。java

這篇是咱們「Java 混淆那些事」第五講,其實經過前四篇你們已經可以寫出正常的混淆規則了,這一篇是簡單的介紹一下不怎麼經常使用的一些命令,我的以爲重要的會單獨拿出來寫個例子。你們能夠簡單看一遍用到的時候再來查或者直接去參考官方文檔。android

輸入輸出選項

命令 解釋
-include filename 指定其餘配置文件,能夠指定多個。例如:-include proguard1.pro
@filename -include filename 的縮寫。例如:@proguard1.pro、@test/proguard2.pro
-basedirectory directoryname 配置後續出現相對路徑的基目錄。他默認的基目錄是配置文件的目錄,若是不存在配置文件基目錄就是工做目錄。例如:-basedirectory 'C:\test' ,能夠指定屢次。注:「後續」這個詞很重要
-injars class_path 指定要處理的文件( jar 或 aars,war,ear,zips,apks 或目錄)。默認狀況下,非類文件不會進行更改。請注意若是目錄中有任何臨時類文件(例如由 IDE 建立)不想被修改,能夠過濾類路徑中的條目。可使用多個 -injars 選項指定類路徑條目。
-outjars class_path 指定輸出文件(jar 或 aars,wars,ear,zips,apks 或目錄)的名稱。將 -injars 選項的文件處理完成事後輸入到指定的文件。切記輸出文件不能覆蓋任何輸入文件,不然容易出錯。爲了更好的可讀性,可使用多個 -outjars 選項指定類路徑條目。若是不寫 -outjars 選項,則不會有輸出。
-libraryjars class_path 指定混淆時須要依賴的類庫文件( jar 或 aars,wars,ear,zips,apk s或目錄)。這些 jar 裏面的文件不會處理和輸出,可使用多個 -libraryjars 選項指定類路徑條目。
-skipnonpubliclibraryclasses 不處理 library 裏面非 public 修飾的類。以加快處理速度並減小 ProGuard 的內存使用量。
-dontskipnonpubliclibraryclasses 處理 library 中非 public 類。從 4.5 版本開始這是默認配置。
-dontskipnonpubliclibraryclassmembers 處理 library 中public 的類成員。默認不處理。
-keepdirectories [directory_filter] 輸出文件中保留指定目錄。默認狀況下,目錄會被移除。這會減小輸出文件的大小。
-target version 將類文件 java 版本處理爲指定版本。默認狀況下,類文件的版本號保持不變。例如,咱們給予 Java 5 寫的項目想使用在 Java 6 的環境下,就能夠經過指定 -target 更改版本號並將其預先驗證,將類文件升級。可是降級需謹慎可能會有一些問題。
-forceprocessing 強制輸出,即便保證輸出文件每次都是最新狀態。

壓縮選項

命令 解釋
-dontshrink 關閉壓縮功能。默認狀況下,會開啓壓縮;
-printusage [filename] 把被壓縮的類和方法輸出到文件。主要用來驗證本身的混淆規則正確不正確。
-whyareyoukeeping class_specification 打印出壓縮過程當中保留了這些類文件和類成員的具體緣由。例如:-whyareyoukeeping class **Manager { *; }

混淆選項

命令 解釋
-dontobfuscate 關閉混淆功能。默認狀況下,開啓混淆。
-printmapping [filename] 把重命名的類和類成員的新、舊名稱的映射文件輸入到指定文件。
-applymapping filename 指定要重用的映射文件,映射文件中沒有的類和類成員會被混淆爲新的名稱。若是代碼結構發生根本變化,ProGuard 可能會打印出應用映射文件致使衝突的警告。您能夠經過 -useuniqueclassmembernames 在兩個混淆運行中指定選項來下降此風險。只容許一個映射文件。僅在混淆時適用。
-obfuscationdictionary filename 指定類、方法及字段混淆後時用的混淆字典。默認使用 ‘a’,’b’ 等短名稱做爲混淆後的名稱。
-classobfuscationdictionary filename 指定類名的混淆字典。
-packageobfuscationdictionary filename 指定包名的混淆字典。
-overloadaggressively 開啓侵入性重載混淆。多個字段及方法容許同名,只要它們的參數及返回值類型不一樣。該選項可以使處理後的代碼更小(及更難閱讀)。只有開啓混淆時可用。注:Dalvik 不能處理重載的靜態字段。
-useuniqueclassmembernames 類和成員混淆的時候,使用惟一的名字。
-dontusemixedcaseclassnames 不使用大小寫混合類名,注意,windows用戶必須爲 ProGuard 指定該選項,由於 windows 非大小寫敏感,輸出文件可能將會相互覆蓋。
-keeppackagenames [package_filter] 不混淆指定的包名。有多個包名能夠用逗號隔開。包名能夠包含 ?、*、** 通配符,還能夠在包名前加上 ! 否認符。只有開啓混淆時可用。若是你使用了 mypackage.MyCalss.class.getResource(""); 這些代碼獲取類目錄的代碼,就會出現問題。須要使用 -keeppackagenames 保留包名。
-flattenpackagehierarchy [package_name] 將全部重命名的包從新打包到給定的單一包中。若是沒參數或字符串爲空,包移動到根包下。
-repackageclasses [package_name] 把全部重命名的類從新打包到給定的單一包中。若是沒參數或字符串爲空,類的包會被徹底移除。此選項會覆蓋該 -flattenpackagehierarchy 選項。低版本的參數名是 -defaultpackage 。
-keepattributes [attribute_filter] 保留任何可選屬性。過濾器是由逗號分隔的 JVM 及 ProGuard 支持的屬性列表。屬性名能夠包含 ?、*、** 通配符,而且能夠在屬性名前加上 ! 否認符。例如:處理庫文件時應該加上 Exceptions,InnerClasses,Signature 屬性。同時保留 SourceFile 及 LineNumberTable 屬性使混淆後仍能獲取準確的堆棧信息。同時若是你的代碼有使用註解你可能會保留 annotations 屬性。只有開啓混淆時可用。
-keepparameternames 保留方法參數名稱和保留的方法類型。
-renamesourcefileattribute [string] 指定一個常量字符串做爲 SourceFile 屬性的值。須要被 -keepattributes 選項指定保留。只有開啓混淆時可用。
-adaptclassstrings [class_filter] 混淆與完整類名一致的字符串。沒指定過濾器時,全部符合現有類的完整類名的字符串常量均會混淆。只有開啓混淆時可用。
-adaptresourcefilenames [file_filter] 以混淆後的類文件做爲樣本重命名指定的源文件。沒指定過濾器時,全部源文件都會重命名。
-adaptresourcefilecontents [file_filter] 以混淆後的類文件做爲樣本混淆指定的源文件中與完整類名一致的內容。沒指定過濾器時,全部源文件中與完整類名一致的內容均會混淆。

優化選項

命令 解釋
-dontoptimize 關閉優化功能。默認狀況下啓用優化。
-optimizations optimization_filter 指定優化的粒度規則,後面的參數是一個粒度過濾器,通常不作更改默認便可。
-optimizationpasses n 表示對你的代碼進行迭代優化的次數。參數是整數。通常來講設置爲 10 如下便可,由於優化次數多了也不會有什麼實質性的優化了,若是你有什麼特殊業務需求,按本身需求調整就行了。
-assumenosideeffects class_specification 優化階段刪除指定代碼,好比: 刪除全部日誌 -assumenosideeffects class com.Log { \*; }
-assumenoexternalsideeffects class_specification 優化階段刪除指定代碼,力度比 -assumenosideeffects 強,由於它能夠優化參數或堆。例如,刪除日誌記錄代碼時,若是日誌包含 String 拼接的字節碼就能夠完全刪除了。 -assumenosideeffects 是沒法在字節碼層面刪除的。
-assumenoescapingparameters class_specification 指定不容許其引用參數轉義到堆的方法。 這些方法可使用,修改或返回參數,但不能直接或間接地將它們存儲在任何字段中。 例如,System.arrayCopy 方法不容許其引用參數轉義,但方法 System.setSecurityManager 不會。
-assumenoexternalreturnvalues class_specification 指定在調用時不返回已在堆上的引用值的方法。例如,ProcessBuilder#start 返回一個 Process 引用值,但它是一個在堆上並無使用的新實例。
-allowaccessmodification 優化時容許優化並修改類和類的成員的訪問修飾符。對外提供的 SDK 包須要注意此選項。
-mergeinterfacesaggressively 指定接口能夠合併,即便它們的實現類未實現合併後接口的全部方法。該選項能夠經過減小類的總數減小輸出文件的大小。只有開啓優化時可用。

保持選項

其餘選項以前的文章已經介紹過了,這裏只介紹以前沒說過的。小程序

命令 解釋
-if class_specification 若是 if 指定類和類成員存在,隨後的keep選項(-keep,-keepclassmembers,...)纔會進行匹配。
-printseeds [filename] 將詳盡列出由各類 -keep 選項匹配的類和類成員列表輸出到指定文件。該列表可用於驗證是否確實找到了預期的類成員,尤爲是在使用通配符時。例如,您可能但願列出您保留的全部應用程序或全部小程序。

預校驗選項

命令 解釋
-dontpreverify 關閉預校驗功能。默認狀況下,若是類文件針對 Java ME 或 Java 6 或更高版本,則會對其進行預驗證。對於 Java ME ,須要預驗證。對於 Java 6,預驗證是可選的,但從 Java 7開始,它是必需的。若是類文件針對 Android 時,沒有必要,所以您能夠將其關閉以減小處理時間。
-microedition 指定已處理的類文件以 Java ME 爲目標。而後,預驗證程序將添加適當的StackMap屬性,這些屬性與 Java SE 的默認 StackMapTable 屬性不一樣。例如,若是要處理 midlet,則須要此選項。
-android 指定已處理的類文件以Android平臺爲目標。而後ProGuard確保某些功能與 Android 兼容。

常規選項

命令 解釋
-verbose 在處理期間打印更多信息。若是程序以異常終止,則此選項將打印出整個堆棧跟蹤,而不只僅是異常消息。
-dontnote [class_filter] 指定不打印有關配置中潛在錯誤或遺漏的信息,例如配置中的類名拼寫錯誤或可能缺失有用的選項。會有提示。
-dontwarn [class_filter] 指定找不到引用或其餘重要問題時不打印警告信息。例如,在某個類的引用中找不到相關類,會有警告提示。使用 dontwarn 就能夠忽略提示。
-ignorewarnings 打印找不到引用或其餘重要問題的警告信息。
-printconfiguration [filename] 將已解析的整個配置輸出到指定文件。
-dump [filename] 指定將類文件的內部結構輸出到指定文件。
-addconfigurationdebugging 指定用調試語句對處理過的代碼進行測試,這些調試語句會打印出缺乏 ProGuard 配置的建議。若是處理過的代碼因爲仍然缺乏一些反射配置而崩潰,他會提示一些簡易的配置。例如,代碼可能正在使用 GSON 庫序列化類,您可能須要對其進行一些配置。一般,您能夠將控制檯中的建議 複製/粘貼 到配置文件中。注:不要在發行版本中使用此選項,由於它會將混淆信息添加處處理過的代碼中。

選項參數格式

上面的命令後面有不少參數格式,咱們來講一說各個參數怎麼寫。 首先說一下帶 [] 的參數是可選的不是必填,而不帶 [] 的參數是必填。windows

參數名 做用
filename 表示具體文件名,能夠是相對路徑,也能夠是絕對路徑。好比 test/test111.txttest111.txt
string 表示隨便一段字符串,好比 "xxx"
class_filter 表示類過濾器,具體用法參考上一章類名過濾器
class_path 表示類文件路徑,能夠是 jars, aars, wars, ears, zips, apks 或目錄,能夠是相對路徑,也能夠是絕對路徑。
file_filter 表示文件過濾器,具體用法參考上一篇文件相關的過濾器
class_specification 表示類規範是類和類成員的模板,上一篇有提到
directory_filter 表示目錄過濾器,好比 com/httpcom/* 具體用法參考上一篇文件相關的過濾器
package_name 表示包名 com.http
package_filter 表示包名過濾器,好比 com.*
version 表示 Java 版本號能夠是 1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7 (7)、1.8(8)、1.9(9)或 10 。

還有兩個指令不是一兩句話能夠說明,咱們單獨拿出來說。api

optimization_filter

若是有多個規則,可使用通配符或 ,(逗號) 隔開寫多個規則。在調整優化粒度規則的時候,但願你可以研究明白再使用,以避免出現什麼問題。 支持三個通配符 ? 、 * 、! ,它們表明什麼我就很少說了。微信

我列舉一下固定的,其餘的不穩定的我就不列舉了,有須要但願你們去看看幫助文檔。app

規則 做用
class/marking/final 儘量將類標記爲 fianl 類。
class/unboxing/enum 儘量將枚舉類型簡化爲整數常量。
class/merging/vertical 垂直合併類 ( 上下層級的類 進行合併 )。
class/merging/horizontal 水平合併類 ( 同一層級的類 進行合併 )。
field/removal/writeonly 移除只讀字段。
field/marking/private 儘量將字段標記爲私有。
field/propagation/value 在方法中傳遞屬性值。
method/marking/private 儘量將方法標記爲私有。
method/marking/static 儘量將方法標記爲靜態。
method/marking/final 儘量將方法標記爲 final 方法。
method/removal/parameter 刪除沒有用到的方法。
method/propagation/parameter 將方法參數的值從方法調用傳到調用的方法。
method/propagation/returnvalue 將方法返回的值從方法傳到它們的調用處。
method/inlining/short 合併比較短的方法。
method/inlining/unique 合併只調用一次的方法。
method/inlining/tailrecursion 簡化尾部遞歸調用。(尾遞歸轉循環)
code/merging 合併相同的代碼塊。
code/simplification/variable 變量加載和存儲時,使用窺孔優化(peephole optimization)技術,一種優化技術。
code/simplification/arithmetic 對算術指令進行窺孔優化。
code/simplification/cast 對類型轉換進行窺孔優化。
code/simplification/field 對屬性加載和存儲使用窺孔優化。
code/simplification/branch 對分支指令使用窺孔優化。
code/simplification/string 對常量字符串使用窺孔優化,最好使用 code/removal/variable 刪除。
code/simplification/advanced 基於控制流分析和數據流分析簡化代碼。
code/removal/advanced 基於控制流分析和數據流分析刪除死代碼。
code/removal/simple 基於簡單控制流分析簡化代碼。
code/removal/variable 從本地變量中,刪除未使用的變量。
code/removal/exception 當 try-catch 中,try塊內爲空時,刪除exceptions。
code/allocation/variable 在本地變量中優化變量分配。

attribute_filter

類文件實質上定義了類,它們的字段和方法。許多基本和非必要數據做爲屬性附加到這些類,字段和方法。例如,屬性能夠包含字節碼,源文件名,行號表等。ProGuard 的混淆步驟刪除了執行代碼一般不須要的屬性。若是咱們須要保留就須要使用 -keepattributes 來進行保留。一樣多個使用逗號,支持三個通配符 ? 、 * 、!ide

可選的屬性
可選項 解釋
*Annotation* 註解。
SourceFile 從中編譯類文件的源文件的名稱。
SourceDir 從中編譯類文件的源目錄的名稱。
InnerClasses 類及其內部類和外部類之間的關係。
EnclosingMethod 定義類的方法。
Deprecated 表示不推薦使用類,字段或方法。
Synthetic 表示編譯器生成了類,字段或方法。
Signature 類、字段或方法的通用簽名,代碼能夠經過反射訪問此簽名。
MethodParameters 方法參數的名稱和訪問標誌。
Exceptions 指定方法可能拋出的異常。
LineNumberTable 方法的行號。
LocalVariableTable 方法的局部變量的名稱和類型。
LocalVariableTypeTable 方法的局部變量的名稱和泛型類型。
RuntimeVisibleAnnotations 在運行時,類,字段和方法可見的註釋。
RuntimeInvisibleAnnotations 在編譯時對類,字段和方法可見的註釋。
RuntimeVisibleParameterAnnotations 方法參數在運行時可見的註釋。
RuntimeInvisibleParameterAnnotations 方法參數在編譯時可見的註釋。
RuntimeVisibleTypeAnnotations 在運行時可見的註釋,用於泛型類型,指令等。
RuntimeInvisibleTypeAnnotations 在編譯時可見的註釋,用於泛型類型,指令等。
AnnotationDefault 註釋的默認值。

例子

flattenpackagehierarchy

// 混淆規則
-keep class **Manager {
    <fields>;
    <methods>;
}
-flattenpackagehierarchy "xxx"
複製代碼

混淆前的結構 測試

混淆後的結構 優化

  1. 全部改變包名的都放到了 xxx 包下

repackageclasses

// 混淆規則
-keep class **Manager {
    <fields>;
    <methods>;
}
-repackageclasses "xxx"
複製代碼

混淆前的結構

混淆後的結構

  1. 全部改變類名的類都放到了 xxx 包下

adaptclassstrings

// 混淆規則
-keep class **Manager {
    <fields>;
    <methods>;
}
-keepclassmembers,allowobfuscation class **{
	public java.lang.String get();
}
-adaptclassstrings

// 混淆前的代碼
public String get() {    
    String ss1 = "com.test.http.HttpRequest";
    return "請求成功 "+ss1;
}

// 混淆後的代碼
public String a() {
    return "請求成功 " + "com.test.a.a";
}
複製代碼
  1. 混淆與類名相同的字符串,用途你們能夠想想,好比反射的時候能夠用。Class.forName("com.test.http.HttpRequest")

adaptresourcefilenames

// 混淆規則
-keep class **Manager {
    <fields>;
    <methods>;
}
-adaptresourcefilenames
複製代碼

混淆前的資源文件

混淆後的資源文件

  1. 混淆與類名相同的資源文件

renamesourcefileattribute

// 混淆腳本
-keepattributes SourceFile
-renamesourcefileattribute "renamesourcefileattribute Test"

// 代碼
/* compiled from: renamesourcefileattribute Test */
public final class a {
}
複製代碼

發現保留了 SourceFile 屬性,而且指定了一個字符串。

adaptresourcefilecontents

// 混淆腳本
-adaptresourcefilecontents

//混淆前的資源文件
test:com.test.http.HttpRequest

//混淆後的資源文件
test:com.test.a.a
複製代碼
  1. 你們能夠想一想用途,好比 Android 中想混淆 Activity 就須要修改清單文件類路徑,這個屬性就能夠自動修改了。

小結

這一篇內容比較多你們過一遍就好,若是你本身以爲還有其餘能用到的選項就須要本身動手去試一試效果了。

相關文章
相關標籤/搜索