現(xiàn)在大多商業(yè)應(yīng)用都是經(jīng)過(guò)混淆的陨簇,混淆后雖然不能防止反編譯,但是可以增加反編譯后的難度迹淌,減小包的大小河绽,做sdk開(kāi)發(fā)一般都是比較核心的技術(shù),所以一般都是需要添加上混淆規(guī)則的
文中大部分轉(zhuǎn)自:https://www.cnblogs.com/zhangmiao14/p/7098168.html
混淆的原理
Java是一種跨平臺(tái)唉窃、解釋型語(yǔ)言耙饰,Java源代碼編譯成的class文件中有大量包含語(yǔ)義的變量名外邓、方法名的信息数苫,很容易被反編譯為Java源代碼粥血。為了防止這種現(xiàn)象,我們可以對(duì)Java字節(jié)碼進(jìn)行混淆忘闻∷是停混淆不僅能將代碼中的類名峡继、字段岸梨、方法名變?yōu)闊o(wú)意義的名稱,保護(hù)代碼篷扩,也由于移除無(wú)用的類兄猩、方法,并使用簡(jiǎn)短名稱對(duì)類鉴未、字段枢冤、方法進(jìn)行重命名縮小了程序的大小。
ProGuard由shrink铜秆、optimize淹真、obfuscate和preverify四個(gè)步驟組成,每個(gè)步驟都是可選的羽峰,需要哪些步驟都可以在腳本中配置趟咆。參見(jiàn)ProGuard官方介紹添瓷。
壓縮(Shrink):默認(rèn)開(kāi)啟梅屉,偵測(cè)并移除代碼中無(wú)用的類、字段鳞贷、方法和特性坯汤,減少應(yīng)用體積,并且會(huì)在優(yōu)化動(dòng)作執(zhí)行之后再次執(zhí)行(因?yàn)閮?yōu)化后可能會(huì)再次暴露一些未使用的類和成員)搀愧。
-dontshrink 關(guān)閉混淆
優(yōu)化(Optimize):默認(rèn)開(kāi)啟惰聂,分析和優(yōu)化字節(jié)碼,讓?xiě)?yīng)用運(yùn)行的更快咱筛。
-dontoptimize 關(guān)閉優(yōu)化搓幌,默認(rèn)混淆配置文件開(kāi)始
-optimizationpasses n 表示proguard對(duì)代碼進(jìn)行迭代優(yōu)化的次數(shù),Android一般為5
混淆(Obfuscate):默認(rèn)開(kāi)啟迅箩,使用a溉愁、b、c饲趋、d這樣簡(jiǎn)短而無(wú)意義的名稱拐揭,對(duì)類、字段和方法進(jìn)行重命名奕塑,增大反編譯難度堂污。
-dontobfuscate 關(guān)閉混淆
上面三個(gè)步驟使代碼大小更小、更高效龄砰,也更難被逆向工程盟猖。
預(yù)檢(Preverify):在java平臺(tái)上對(duì)處理后的代碼進(jìn)行預(yù)檢讨衣。
混淆流程圖:
Proguard讀入input jars(or wars,zip or directories),經(jīng)過(guò)四個(gè)步驟生成處理之后的jars(or wars,ears,zips or directories),Optimization步驟可選擇多次進(jìn)行。
為了確定哪些代碼應(yīng)該被保留式镐,哪些代碼應(yīng)該被移除或混淆值依,需要確定一個(gè)或多個(gè)Entry Point。Entry Point經(jīng)常是帶有main methods,applets,midlets的classes碟案,它們?cè)诨煜^(guò)程中會(huì)被保留愿险。
Proguard的幾個(gè)步驟如何處理Entry Points。
〖鬯怠(1).在壓縮階段辆亏,Proguard從上述Entry Points開(kāi)始遍歷搜索哪些類和類成員被使用。其他沒(méi)有被使用的類和類成員會(huì)移除鳖目。
“邕丁(2).在優(yōu)化階段,Proguard進(jìn)一步設(shè)置非Entry Point的類和方法為private领迈、static和final來(lái)進(jìn)行優(yōu)化彻磁,不使用的參數(shù)會(huì)被移除,某些方法會(huì)被標(biāo)記為內(nèi)聯(lián)狸捅。
≈则选(3).在混淆階段,Proguard重命名非Entry Points的類和類成員尘喝。
〈沤健(4).預(yù)檢階段是唯一沒(méi)有觸及Entry Points的階段。
混淆的使用方法
1. 開(kāi)啟混淆
在build.gradle 中開(kāi)啟混淆
buildTypes {
release {
// 設(shè)置為true朽褪,開(kāi)啟混淆開(kāi)關(guān)
minifyEnabled true
// 'proguard-android.txt' 是AndroidStudio默認(rèn)自動(dòng)導(dǎo)入的規(guī)則置吓,這個(gè)文件位于Android SDK根目錄\tools\proguard\proguard-android.txt。這里面是一些比較常規(guī)的不能被混淆的代碼規(guī)則缔赠。'proguard-rules.pro'是針對(duì)我們自己的項(xiàng)目需要特別定義混淆規(guī)則衍锚,它位于項(xiàng)目根目錄下面,里面的內(nèi)容需要我們自己編寫(xiě)
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
2. 編寫(xiě)自己的編譯規(guī)則
在proguard-rules.pro 中進(jìn)行編寫(xiě)
因?yàn)槲覀兪莏ar包開(kāi)發(fā)嗤堰,完成以上兩步還要自己編寫(xiě)打包規(guī)則
// 依賴一個(gè)我們上邊已經(jīng)寫(xiě)好的打包task
task proguardJar(dependsOn: ['xxx'], type: proguard.gradle.ProGuardTask) {
//Android 默認(rèn)的 proguard 文件
configuration android.getDefaultProguardFile('proguard-android.txt')
//混淆的配置文件
configuration 'proguard-rules.pro'
// 一下為
String inJar = xxx.archivePath.getAbsolutePath()
//輸入 jar
injars inJar
println "inJar->>" + inJar
//輸出 jar
String outJar = inJar.substring(0, inJar.lastIndexOf(File.separator)) + "/proguard-${xxx.archiveName}"
outjars outJar
println "outJar->>" + outJar
//設(shè)置不刪除未引用的資源(類戴质,方法等)
dontshrink
}
3.查看構(gòu)建輸出
構(gòu)建時(shí)Proguard都會(huì)輸出下列文件:(build之后)
(1)dump.txt --- 說(shuō)明APK中所有類文件的內(nèi)部結(jié)構(gòu)
×禾摹(2)mapping.txt --- 提供原始與混淆過(guò)的類置森、方法和字段名稱之間的轉(zhuǎn)換
(3)seeds --- 列出未進(jìn)行混淆的類和成員
》(4)usage.txt --- 列出從APK移除的代碼
這些文件保存在/build/outputs/mapping/release目錄下凫海。
4.解碼混淆過(guò)的堆棧追蹤
使用混淆后,保存好mapping文件男娄,程序csh時(shí)通過(guò)腳本進(jìn)行解碼行贪。
retrace工具位于/tools/proguard/目錄下漾稀,解碼命令為(注意把stacktrace_file 中的AndroidRuntime 等字段去掉)
retrace.bat|retrace.sh [-verbose] mapping.txt [<stacktrace_file>]
混淆的規(guī)則介紹
#? 匹配一個(gè)字符
#* 匹配一個(gè)名字,除了目錄外分隔符外的任意部分
#** 匹配任意名建瘫,可能包含任意路徑分隔符
#崭捍! 排除
#<field> 匹配類中的所有字段
#<method> 匹配類中所有的方法
#<init> 匹配類中所有的構(gòu)造函數(shù)
#代碼混淆壓縮比,在0~7之間
-optimizationpasses 5#
#混淆時(shí)不適用大小寫(xiě)混合啰脚,混合后的類名為小寫(xiě)
-dontusemixedcaseclassnames
#指定不去忽略非公共庫(kù)的類
-dontskipnonpubliclibraryclasses
#不做預(yù)校驗(yàn)殷蛇,preverify是proguard的四個(gè)步驟之一,Android不需要precerify,去掉這一步能夠加快混淆速度橄浓。
-dontpreverify
-verbose
#google推薦算法
-optimizations !code/simplification/arithmetic,!code/simplication/cast,!field/*,!class/mergin/*
#避免混淆Annotation粒梦、內(nèi)部類、泛型荸实、匿名類
-keepattributes *Annotation*,InnerClasses,Signature,EnclosingMethod
#重命名拋出異常時(shí)的文件名稱
-renamesourcefileattribute SourceFile
#拋出異常時(shí)保留代碼行號(hào)
-keepattributes SourceFile,LineNumberTable
#處理support包
-dontnote android.support.**
-dontwarn android.support.**
#保留四大組件匀们,自定義的Application等這些類不被混淆
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * entends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService
#保留本地native方法不被混淆
-keepclasseswithmembernames class * {
native <methods>
}
#保留枚舉類不被混淆
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
#第三方j(luò)ar包不被混淆
-keep class com.github.test.** {*;}
#保留自定義的Test類和類成員不被混淆
-keep class class.lily.Test {*;}
#保留自定義的xlog文件夾下面的類、類成員和方法不被混淆
-keep class com.text.xlog.** {
<fields>;
<methods>;
}
#assume no side effects;刪除android.util.Log輸出的日志
-assumenosideeffects class android.util.Log {
public static *** v(...);
public static *** d(...);
public static *** i(...);
public static *** w(...);
public static *** e(...);
}
#保留keep注解的類名和方法
-keep,allowobfuscation @interface android.support.annotation.Keep
-keep @android.support.annotation.Keep class *
-keepclassmember class * {
@android.support.annotation.Keep *;
}
#避免混淆內(nèi)部類
-keep class com.letv.tracker2.msg.MessageProcessor$* {*;}