Android-專案中採用的混淆加固多渠道打包方案
前言
當我們萬分努力將專案開發完成之後,提交最後一行程式碼後,會怎麼樣?
長舒一口氣,終於完成了,給自己放一天假,休息一下吧?
不,還沒完,作為一個 Android 開發者,我們接下來做的事情還有不少呢。
第一步 :混淆和加固應用
實不相瞞,我之前從未對我的應用加固過,因為我感覺使用者量不是太大,也不會有人有興趣來反編譯或者攻擊。直到我看到這一句話:
當用戶使用你的應用的那一刻起,作為開發者,你就有責任去保證應用使用整個過程中的使用者裝置和資訊的安全。
回想之前,覺得自己真的好爛,從現在開始,我想成為一個負責任的開發者。
第二步:打各種渠道包,看吶,這百花齊放的應用市場。
因為偉大的牆,沒有了 Google Play ,但是我們收穫了一大片森林,多少個渠道包,取決於我們的運營是有多大的野心,一般情況下,幾十個是少不了的。
混淆和加固的區別
首先要明確的,混淆和加固是兩個非常不同的概念。(明確的原因,在於我之前就傻傻的以為混淆和加固是一回事,掩面羞愧逃~ )
混淆,是一種類似障眼法的作用,讓反編譯後的程式碼閱讀難度增加,本質上來說,並非是防止了反編譯,而是增加了閱讀難度。例如將要混淆類名和函式名,替換為無意義的短名稱,(OrderUtils. createOrder() -> A.b() )。
加固,可以理解為,將APK的外層加了一層殼,如果想反編譯,必須突破這層殼的保護。加固後的APK,反編譯出來,看到的只是外面那層殼的程式碼。加固涉及到的技術手段就很多了,同時也非常的深奧,例如dex 檔案加密和位元組碼變形等。
混淆
開啟混淆很簡單
說到混淆吧,沒用過混淆的人,看到混淆的那些關鍵字,估計都和我之前一樣,簡直一臉懵,但是混淆過的我,告訴你們,完全不要害怕,混淆的道理真的很簡單。
混淆,主要包括三個部分,資源壓縮,程式碼混淆和程式碼壓縮。
資源壓縮
主要壓縮的是物件是專案 res 和 asset 檔案下未被引用的資原始檔。這個工作是通過 Gradle 的 Android 外掛去實現,預設資源壓縮是關閉的,我們在 build.gradle 中可以通過shrinkResources true
來開啟。程式碼壓縮和程式碼混淆
上面針對的是資源,這個針對的就是程式碼檔案了。這個方面,有一個在專門做 Java 位元組碼混淆的工具 ProGuard ,使用起來非常方便,是不是很開心?更開心的是 Android Studio 2.0 之後已經整合 ProGuard,我們再次可以通過在 build.gradle 中通過minifyEnabled true
所以 build.gradle 中新增短短的三行程式碼,專案打包 release 包就已經是混淆後的包了。
buildTypes {
release {
minifyEnabled true //開啟混淆和程式碼壓縮
shrinkResources true //開啟資源壓縮
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
這裡注意 shrinkResources true 在 minifyEnabled true 的前提下,才會生效。因為需要程式碼壓縮,釋放掉無用資源的引用,資源壓縮才能正常工作。
思考->為什麼需要自定義規則,不能自動混淆好嗎?
這是上面混淆三行程式碼的中最長的一句:
//混淆規則的檔案
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
上面程式碼中有涉及到兩個檔案,其中
proguard-android.txt 是 Android SDK 提供的預設混淆檔案,裡面有 Android 應用通用的一些混淆規則,位置在 sdk/tools/proguard/ 中,這個檔案不需要我們修改,預設就好。
proguard-rules.pro 是專案的混淆檔案,提供給開發者,用來編寫自定義的混淆規則,這個檔案,才是我們做混淆工作的主戰場。
寫之前,需要明確的是,我們為什麼要在 proguard-rules.pro中自定義規則?
這個需要明白兩個問題
1.ProGuard 是怎麼做到混淆的?
壓縮: 移除無效的類、屬性、方法等
優化: 優化位元組碼,並刪除未使用的結構
混淆: 將類名、屬性名、方法名混淆為難以讀懂的字母,比如a,b,c
2.ProGuard 混淆的範圍?是全部的類嗎?
ProGuard 是一個優化 Java 位元組碼的工具,也就是說,只要是Java檔案都會被混淆。
但是這並不是我們想要的結果,因為有些類,如果被混淆就會出現問題,舉個例子,佈局中有一個 button 的 android: onClick 引用了 Activity MainActivity 中的方法 buttonClick()。混淆之後,佈局檔案是 .xml 檔案不會被混淆,保持不變,Activity類是 .java 檔案,會被混淆 ,MainActivity 被混淆成 A,buttonClick()被混淆成 b()。然後,讓我們點選 button ,觸發 onClick ,找不到 MainActivity.buttonClick(),是不是就會報錯了,乾脆利落地 Crash 了。
造成這個結果的原因是,ProGuard 並不是專門用來優化 Android 應用的一個工具,所以它並不知道什麼該混淆,什麼不該混淆。
混淆規則的制訂,更多的是一種保護,告訴 ProGuard 某些類某些方法不用混淆。
proguard-android.txt 中預設的一些規則,就是針對所有的 Android 專案都適用的混淆規則。
例如:
#包名不混合大小寫
-dontusemixedcaseclassnames
#不跳過非公共的庫的類
-dontskipnonpubliclibraryclasses
#混淆時記錄日誌
-verbose
#關閉預校驗
-dontpreverify
#不優化輸入的類檔案
-dontoptimize
#保護註解
-keepattributes *Annotation*
//等等,全部內容請自己去看sdk/tools/proguard/proguard-android.txt
由於每個專案的情況都不一樣,所以 專案中 proguard-rules.pro 的檔案,就是留給開發者根據自身專案情況,去設定混淆規則的。可謂是非常人性化。
混淆的99%工作->編寫自定義混淆規則
自定義混淆規則檔案 proguard-rules.pro 的內容可以分為三個部分:
- 一些前輩們總結的,一般專案都會用到的混淆規則
- 第三方庫的混淆規則 ,這個第三方文件上都有。
- 如果有使用ORM型別的資料庫,例如greenDao,需要保護對映資料表的實體類不被混淆
- 保護 JNI 中呼叫的類不被混淆。
- 保護 WebView 中 JavaScript 呼叫的方法不被混淆
- 保護 Layout 佈局使用的View建構函式、android:onClick等不被混淆。
關於怎麼保護,這個按照 ProGuard 制訂規則去保護了,關鍵詞很多,而且視專案不同,混淆檔案一般也不一樣,我也沒法給你提供一個模板什麼。
你可以看這篇文章 寫給 Android 開發者的混淆使用手冊,寫的超級詳細超級棒,我就是看這個學會的混淆。
加固
加固這方面的技術要求太高了,沒有特別的安全需求的話,都會選擇第三方加固平臺,公司專案採用的是梆梆加固。
梆梆加固的使用方式很簡單,上傳 apk 包,點選加固,等待加固完畢,當加固成功後,會得到一個評估報告,要仔細閱讀一下,檢視是否由程式碼上可修改的漏洞等,如果有,修改後再加固,然後,下載加固後的加固包即可。
多渠道打包
只要是需要分發到各大市場的應用,多渠道的統計必不可少,因此多渠道打包必不可少,公司專案採用多渠道打包工具是 PackerNg。
PackerNgV2 提供 gradle 整合打包和指令碼打包,因為我們需要加固,所以選擇指令碼打包方式。具體步驟可以看 PackerNg的文件
完整打包流程
基本的打包流程如下:
注意看,除了上面介紹的部分,多了一個重新簽名的步驟。
因為當加固之後的 Apk 沒有簽名,需要我們重新簽名。自動簽名的工具有很多,包括梆梆加固都有提供再簽名的工具,使用這些簽名之後PackerNg 指令碼打包後會出現 Error: Invalid Signature
錯誤。原因是現在提供的工具大多隻支援了 V1 簽名,而專案整合的 PackerNg 最新版本需要 V2 簽名,所以我們要給 Apk 重新籤 V2 簽名。
命令如下:
zipalign -v 4 <apk_path> <after_apk_path>//對齊
apksigner sign --ks <keystore_path> <after_apk_path> //重新簽名
//以下命令可用來驗證對齊和再簽名的結果
zipalign -c -v 4 <apk_path>//驗證是否對齊
apksigner verify --verbose <after_apk_path> //檢視簽名信息,用來驗證是否是 V2 簽名
其中
apksigner 是Android SDK 自24.0.3開始提供的官方簽名工具,位於:Android SDK/build-tools/對應版本/apksigner。
zipalign 是 Android SDK 提供程式碼對齊工具, 位於 Android SDK/build-tools/對應版本/zipalign。
如果已經配置好 sdk/build-tools/的 Path,直接在 Terminal 中,任何路徑下就能使用,如果沒有可以選擇是配置一下路徑或者切換到 sdk/build-tools/ 對應版本/ 中操作。
最後
真的很感嘆,我也終於自己完整的走通了一個專案到釋出市場的一整套流程,現在做完之後,看起來一切都很簡單,步驟也不是很多,但是對於上週的我來說,因為對混淆和加固的知之甚少,真的膽戰心驚的去做這件事情,混淆之後安裝測試,簽名之後安裝測試等等,因為約了百度的首發,上線時間有限制,真的害怕搞出來自己短時間內無法解決的問題。
我仔細想想,當時的緊張,都是對未知的恐懼。例如,簽名遇到問題了,才知道 Android 7.0 新出了 V2 簽名機制等,因為沒用過,也測了又測,直到放心。
在這個知識的海洋裡,我知道的真是太少了,努力加油吧,為了以能夠自信多一點點。
歡迎關注博主的微信公眾號,快快加入哦,期待與你一起成長!