阿里P7移動互聯網架構師進階視頻(每日更新中)免費學習請點擊:https://space.bilibili.com/474380680html
本篇文章將繼續從微信資源混淆AndResGuard原理來介紹APK大小優化:
微信的AndResGuard工具是用於Android資源的混淆,做用有兩點:一是經過混淆資源ID長度同時利用7z深度壓縮,減少了apk包大小;二是混淆後在安全性方面有一點提高,提升了逆向破解難度。本文從源碼角度,來探尋AndResGuard實現原理。linux
閱讀本文須要前提知識:掌握Android應用程序打包編譯過程,尤爲是對資源的編譯和打包過程;熟悉resource.arsc文件格式。git
推薦羅昇陽文章:http://blog.csdn.net/luoshengyang/article/details/8744683
微信資源混淆工具源碼地址:https://github.com/shwenzhang/AndResGuard
附上來自網絡神圖:github
0、程序入口CliMain.main()
該函數處理命令行參數、並解析自定義配置文件,混淆工具能夠根據配置項進行特定處理,具體參考config.xml內容,針對其中特定內容,咱們會在後面提到。而後進入真正混淆的入口函數resourceProgurad()正則表達式
特別說明一下解析Configuration中關鍵點,處理複用舊的mapping文件:
一、processOldMappingFile()緩存
該函數主要功能是:對oldmapping文件處理是按照正則表達式把「->」分隔提取兩邊字符串,進行hashmap緩存:安全
其1、若是有這個「/」的話,那就是res path mapping即mOldFileMapping的hashmap中:
mOldFileMapping.put(nameBefore, nameAfter);
(例如res/drawable -> r/c,最終mOldFileMapping是(「res/drawable」,」r/c」))微信
其2、不然判斷若是包含「.R.」,則是resid的mapping,最後按照類別、package保存到oldResMapping的hashmap中:
namesMap.put(beforename, aftername);
(例如com.basket24.demo.R.attr.progress -> com.basket24.demo.R.attr.a,最終namesMap是(「progress」,」a」))
typeMap.put(typeName, namesMap);
(例如com.basket24.demo.R.attr.progress -> com.basket24.demo.R.attr.a,最終typeMap是(「attr」,namesMap))
mOldResMapping.put(packageName, typeMap);
(例如com.basket24.demo.R.attr.progress -> com.basket24.demo.R.attr.a,最終mOldResMapping是(「com.basket24.demo」,typeMap))網絡
二、Main.resourceProguard()是混淆真正的入口。架構
混淆入口resourceProguard裏功能:
其一:decodeResource();//進行混淆資源相關功能。
其二:buildApk(decoder, apkFile, signatureType);//最後buildApk生成簽名包。
三、Main.decodeResource()
decodeResource核心功能就是設置相關變量,並執行ApkDecoder.decode()。
四、ApkDecoder.decode()
五、ensureFilePath()
ensureFilePath主要功能以下:
其1、在輸出目錄下,創建一個temp目錄,用於apk解壓的目錄。unZipAPk解壓apk,獲得mCompressData壓縮條目集合[compress.put(name,entry.getMethod());]
其2、根據config來修改壓縮的值,將知足config的壓縮類型,進行修改壓縮標記爲ZIP_DEFLATED
其3、判斷是否將將res混淆成r
其4、建立須要混淆的temp目錄(apk被解壓到temp目錄)等、使用FileVisitor對目錄進行遍歷,將原始res(」temp/res」)下路徑保存到HashSet中。
其5、建立resources_temp.arsc 和最終resources.arsc等文件及最終mapping命名:resource_mapping_apkname.txt
下面回到第4步ApkDecoder.decode()中繼續執行:
六、RawARSCDecoder.decode()
這一步就是解析原始resources.arsc文件,獲得文件結構並緩存相關數據,如資源類型字符串池mExistTypeNames等。代碼較長,且關鍵步驟較少,故略去代碼。
繼續在第4步ApkDecoder.decode()中往下執行:
七、ARSCDecoder.decode()
八、ARSCDecoder的構造函數中執行proguardFileName()
這裏第8步主要功能是:
其1、其中初始化ProguardStringBuilder,創建混淆字符串池和標記集合。
其2、獲取配置config內容,判斷是否keeproot,是否沿用舊的mapping文件等,進行映射。
其3、generalFileResMapping把緩存的映射hashmap寫入文件,造成mapping文件,其中目前只有資源目錄path映射。
回到第7步中繼續執行decoder.readTable()進行真正混淆
九、decoder.readTable()
readPackage()解析resources.arsc文件,其中關鍵步驟readEntry()以下:
十、readEntry()
readEntry函數主要實現了:
其1、判斷是否啓用whitelist,若是有的話,設置specname的混淆字符串爲原始字符串,即不進行混淆,進行相應對象緩存。
其2、判斷是否複用舊的mapping文件中id的映射,已有的繼續使用舊的映射關係中的混淆字符串,不然從混淆池中獲取一個新的字符串,即獲得replaceString。
其3、根據新的混淆字符串,生成相應的id映射。輸出到新的混淆mapping文件中(裏面已經文件file的映射關係)。
readEntry繼續解析arsc文件,執行到關鍵步驟readValue:
十一、readValue()
readValue主要實現了:
其1、mPkg.getSpecRepplace獲取前面已緩存下的specName對應的混淆字符串如「a」
其2、從mOldFileName中如在(「res/drawable「,」r/c」)中找到newFilePath=」r/c」
其3、生成result如」r/c/a」
其4、建立了混淆後的res文件,把舊的資源文件內容copy到新的混淆後的資源文件中。
其5、從原始資源目錄mRawResourceFiles中刪除掉該已混淆的文件Path
其6、按照Value的index順序,保存result(如」r/c/a」),即把混淆後的資源項的值緩存下來
下面回到第4步中,繼續執行copyOtherResFiles():
十二、 copyOtherResFiles()
該函數主要實現了把沒有紀錄在resources.arsc的資源文件也拷進dest目錄。
回到第4步中,繼續執行ARSCDecoder.write():
1三、ARSCDecoder.write()
這一步一樣是解析resource.arsc,從新修改arsc文件其中幾個字符串池和對應大小,造成新的arsc文件。主要包括:
其1、資源項值字符串池修改,咱們須要把文件指向路徑改變,例如res/layout/test.xml,改成res/layout/a.xml
其2、資源項key池修改,即specsname除了白名單部分所有廢棄,替換成全部咱們混淆方案中用到的字符。
其3、每一個資源項entry中指向的specsname中的id修正。因爲specname已混淆,咱們須要用混淆後的資源項specname的位置改寫。
回到最開始第2步中,執行 buildApk(decoder, apkFile, signatureType);
1四、buildApk()
從新打包生成新的apk並簽名等,這一步再也不贅述。
以上完成了對apk資源混淆的過程分析。
資源混淆核心處理過程以下:
一、生成新的資源文件目錄,裏面對資源文件路徑進行混淆(其中涉及如何複用舊的mapping文件),例如將res/drawable/hello.png混淆爲r/s/a.png,並將映射關係輸出到mapping文件中。
二、對資源id進行混淆(其中涉及如何複用舊的mapping文件),並將映射關係輸出到mapping文件中。
三、生成新的resources.arsc文件,裏面對資源項值字符串池、資源項key字符串池、進行混淆替換,對資源項entry中引用的資源項字符串池位置進行修正、並更改相應大小,並打包生成新的apk。
原文連接https://blog.csdn.net/cg_wang/article/details/70183864
阿里P7移動互聯網架構師進階視頻(每日更新中)免費學習請點擊:https://space.bilibili.com/474380680