混淆
什么是混淆
- 我們都知道Java代碼最終被執(zhí)行是要被Javac編譯成JVM上的可執(zhí)行碼(class)才可以被JVM運(yùn)行,但是字節(jié)碼的復(fù)雜度不高,網(wǎng)上一些可以反編譯字節(jié)碼的工具,比如jd(java decompiler),可以把jar包反編譯成java源碼,如果想要反編譯apk,只需要用dex2-jar就可以把a(bǔ)pk轉(zhuǎn)換成jar包,然后就可以使用jd來反編譯了,這樣我們的代碼邏輯不都一覽無遺了嗎,所以才有了混淆這種東西
- 混淆簡單理解一下其實就是在編譯前把能換的包名、類名全都變成a b c這樣的名字,讓代碼閱讀起來極其困難,但是只簡單的理解是這樣,其實混淆并不只有這一個作用,也并不一定是在編譯之前做的這些操作
- 混淆有四個步驟
- 壓縮:去除無用的資源,沒有用到的方法、字段,還去除了一些調(diào)試信息,比如行號,從而減小字節(jié)碼文件的體積;所以在這里就要發(fā)現(xiàn),如果你對某些類用了反射,就要配置混淆規(guī)則,對這些類不進(jìn)行混淆
- 優(yōu)化:可能會把一些方法變成內(nèi)聯(lián)的,或者優(yōu)化for語句等,你甚至可以配置混淆規(guī)則,在字節(jié)碼中去除一些方法,比如你可以去除Log方法,當(dāng)然這種配置很危險;官方也提到過,這種優(yōu)化其實是有風(fēng)險的,我也遇到過在debug包下是正常的,但是啟用混淆生成release包時,就出現(xiàn)了一些錯誤
- 混淆:就是我們最常用的功能,防止反編譯以后你的包裸奔,所以混淆器根據(jù)定義好的混淆規(guī)則把能替換的類和變量都替換成了a b c等無意義的名字,同時我們知道在class文件中,引用的類的名字都是存儲在class文件里一個叫做常量池的區(qū)域中的,混淆之后也可以在很大程度上減小包的體積
- 預(yù)檢驗:會在Java6以上檢驗代碼是不是符合Java的規(guī)范
Android
- 因為我對Android的混淆還有所了解,所以只能稍微寫一下Android的東西了;網(wǎng)上有很多將proguard命令的blog,在這里我只講一些比較簡單基礎(chǔ)的吧,不然內(nèi)容實在太多了
- Android中默認(rèn)使用proguard-android.txt來進(jìn)行配置混淆規(guī)則,可以在build.gradle里面配置默認(rèn)的混淆規(guī)則,也可以是使用自定義的proguard文件
#配置默認(rèn)的混淆規(guī)則
proguardFile getDefaultProguardFile('proguard-android.txt')
#使用自己的proguard文件
proguardFile 'proguard-project.txt'
默認(rèn)的proguard-android.txt里面規(guī)定了Activity名字和生命周期方法不會混淆,View的getter和setter也不會混淆,其實是因為Manifest里面要注冊Activity,如果你混淆Activity成了其他名字,那Manifest里面不就找不到了嗎,還有就是在View的屬性動畫里我們在動畫結(jié)束時會改變View的屬性,具體實現(xiàn)就是在animation結(jié)束的時候通過反射,尋找相應(yīng)的prefix為set的方法,通過setter方法進(jìn)行屬性設(shè)置,所以也保留了而沒有混淆,其實本質(zhì)上這也是因為反射而導(dǎo)致的不可混淆。保留不混淆的語法是
-keepclass xxx
-keepclassmembers xxx
#...
- 另外就是因為壓縮的時候會刪除沒有顯示調(diào)用過的方法,但是如果你是使用反射調(diào)用的,就必須在proguard里面配置keep,否則就會出錯
- 優(yōu)化的相關(guān)的我也稍微講一些
#代表指定這個類某些方法被刪除了也沒什么影響,可以用到這個的地方比如說Log
-assumenosideeffects class xxx {xxx}
#進(jìn)行n次優(yōu)化過程,n可以自己指定,如果某次優(yōu)化之后體積和上次比較沒有變化那么無論如何都會停止優(yōu)化
-optimizationpasses n
#禁止優(yōu)化
-dontoptimize