android的代碼混淆主要包括以下幾個方面:
總共分為四個步驟:
shrink: 檢測并移除沒有用到的類,變量,方法和屬性;
optimize: 優化代碼,非入口節點類會加上private/static/final, 沒有用到的參數會被刪除,一些方法可能會變成內聯代碼。
obfuscate: 使用短又沒有語義的名字重命名非入口類的類名,變量名,方法名。入口類的名字保持不變。
preverify: 預校驗代碼是否符合Java1.6或者更高的規范(唯一一個與入口類不相關的步驟)
Android studio配置
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
proguardFiles可以配置多個混淆文件,proguard-android.txt是android自帶的混淆文件,在android sdk下的tools/proguard目錄下,里面是一些常用的混淆配置,如果工程沒有特殊的混淆配置,只使用這個文件就可以滿足。
常用的配置命令
-skipnonpubliclibraryclasses指定讀取引用庫文件的時候跳過非public類。這樣做可以提高處理速度并節省內存。一般情況下非public在應用內是引用不到的,跳過它們也沒什么關系。但是,在一些java類庫中中出現了public類繼承非public類的情況,這樣就不能用這個選項了。這種情況下,會打印一個警告出來,提示找不到類。
-dontskipnonpubliclibraryclasses跟上面的參數相對。版本4.5以上,這個是默認的選項。
-dontusemixedcaseclassnames指定在混淆的時候不使用大小寫混用的類名。默認情況下,混淆后的類名可能同時包含大寫字母和小寫字母。這樣生成jar包并沒有什么問題。只有在大小寫不敏感的系統(例如windows)上解壓時,才會涉及到這個問題。因為大小寫不區分,可能會導致部分文件在解壓的時候相互覆蓋。如果有在windows系統上解壓輸出包的需求的話,可以加上這個配置。
-verbose聲明在處理過程中輸出更多信息。添加這項配置之后,如果處理過程中出現異常,會輸出整個StackTrace而不是一條簡單的異常說明。
-dontoptimize聲明不優化代碼。Dex會自己優化的
-dontpreverify聲明不預校驗即將執行的類。默認情況下,在類文件的編譯版本為java micro 版本或者大于1.6版本時,預校驗是開啟的。目標文件針對java6的情況下,預校驗是可選的;針對java7的情況下,預校驗是必須的,除非目標運行平臺是Android平臺,設置它可以節省一點點時間。
-keepattributes *Annotation*表示對注解中的參數進行保留
-optimizationpasses代碼混淆壓縮比,在0~7之間,默認為5,一般不下需要修改
-printmapping混淆前后的映射
-optimizations指定混淆時采用的算法,后面的參數是一個過濾器
-keepattributes *SourceFile,LineNumberTable*拋出異常時保留代碼行號
-keepattributes *Signature*避免混淆泛型
-flattenpackagehierarchy保持packagename 不混淆
-ignorewarning忽略警告
keep命令
-keep [,modifier, ...] class_specification指定類和類的成員變量是入口節點,保護它們不被移除混淆。
-keepclassmembers [,modifier] class_specification保護的指定的成員變量不被移除、優化、混淆。
-keepclasseswithmembers [,modifier,...] class_specification擁有指定成員的類將被保護,根據類成員確定一些將要被保護的類。
-keep與-keepnames的關系一開始理解的時候有些混亂。但是它們背后是有一定規則的,下面的表格展示了它們的聯系與不同
先看如下兩個比較常用的命令,很多童鞋可能會比較迷惑以下兩者的區別
-keep class com.xiaoka.test.**
-keep class com.xiaoka.test.*
一顆星表示只是保持該包下的類名,而子包下的類名還是會被混淆;兩顆星表示把本包和所含子包下的類名都保持;用以上方法保持類后,你會發現類名雖然未混淆,但里面的具體方法和變量命名還是變了,這時如果既想保持類名,又想保持里面的內容不被混淆,我們就需要以下方法了
-keep class com.xiaoka.test.* {*;}
混淆后生成的文件
mapping.txt:(配置指令:-printmapping)混淆前后代碼對照,在混淆之后如果出現問題,需要查找此文件進行定位---養成保存mapping.txt文件的習慣
dump.txt:(配置指令:-dump)APK內所有class文件結構
seeds.txt:(配置指令:-printseeds)沒有被混淆的類和成員
usage.txt:(配置指令:-printusage)源代碼中被刪除的代碼
不能混淆的類型
反射用到的類不混淆
JNI方法不混淆
Manifest中類不混淆
四大組件和Application子類、Framework層下所有類默認不混淆
Parcelable子類和Creator靜態成員變量不混淆(BadParcelableException,怕了吧)
GSon、FastJson等庫時,Json對象不混淆(注解可解決此問題)
第三方開源庫或引用其他第三方SDK,加入對應混淆規則(或者庫在打包aar時有指定consumerProguardFiles)
WebView的JS調用接口不混淆
library與組件化的混淆問題
現在的問題:所有的混淆規則都是加在app的proguard文件內,一旦library與組件化,需要調整時,必須調整app里的proguard規則
解決方法1:使用@keep,缺陷就是要標記很多地方
解決方法2:使用在build.gradle文件內的defaultConfig內的consumerProguardFiles,也就是把混淆規則傳遞過去,如下圖:
我們app開始用第一種方式,現在已改成第二種方式
模糊字典
-obfuscationdictionary dictionary.txt指定外部模糊字典
-classobfuscationdictionary filename指定class模糊字典
-packageobfuscationdictionary filename指定package模糊字典
指定這些字典后會使用字典里面的值當做混淆的類名,方法名或字段