1. 程式人生 > >android逆向之多dex(multiDex)檔案apk的逆向

android逆向之多dex(multiDex)檔案apk的逆向

很多大廠的Android App因為業務量大,引用庫多導致其apk包的中的類於方法劇增.這樣就有可能出現因為方法數過多導致編譯失敗的情況.產生這個問題的主因是dex檔案格式的限制.一個DEX檔案中method個數採用使用原生型別short來索引檔案中的方法,也就是4個位元組共計最多表達65536個method,field/class的個數也均有此限制。對於DEX檔案,則是將工程所需全部class檔案合併且壓縮到一個DEX檔案期間,也就是Android打包的DEX過程中,單個DEX檔案可被引用的方法總數被限制為65536.

為解決這個問題,谷歌推出了Multidex技術,簡單來說就是將一個apk中的dex檔案拆分成多個分主次先後載入,當然在這之前業界已經開始使用外掛化來弱化此類問題.現在市面上也有很多Multidex和外掛化兩種方案都使用的app.

Multidex會給逆向工程師帶來如下麻煩:

1.常見工具靜態分析的類和方法不全

2.靜態分析工具因為交叉引用的問題導致反編譯失敗或崩潰

3.動態除錯中無法下斷點

4.hook的時候找不到制定的類和方法

0x01 merge multidex

在逆向apk中經常會遇到一些類丟失,反編譯崩潰的情況.如果這時候去觀察apk壓縮包會發現其中有超過一個dex,上圖中就有兩個dex.那麼這個app分析起來就不會很輕鬆了,因為這個叫dex分包的技術直接意味著你要面對超過65536個java方法.而這篇文章主要就是講解筆者在遇到這種情況的時候一些應對手法.

如果你的dex2jar版本為v2.1之前的版本,那麼dex2jar就會預設轉化第一個dex檔案而忽略其他dex檔案. 52f26c6 2.1版本開始支援multidex,直接執行

d2j-dex2jar.sh the-apk-witch-mutidex.apk

就可以轉化所有dex檔案到一個jar包中.

在dex2jar更新v2.1之前筆者是用的一個比較”耿直”的方法解決這個問題,因為dex的method數量有限但是jar包對method是沒有特別限制的,所以我寫了一個指令碼將apk中多個dex先轉化為jar檔案之後解壓縮最後合併打包壓縮,同樣可以得到完全的反編譯檔案.

Android逆向中出鏡率較高的jeb在早期版本v1.x也同樣有類似尷尬的問題,只預設反編譯第一個dex檔案.但是到v2.2也開始支援multidex採用merge的方式解決多個dex的反編譯問題.也就是在jeb2.2之後就可以無障礙的在multidex中使用交叉引用功能了.

在很長一段時間使用jeb1.5的時候解決這個問題的辦法同樣也也merge多個dex的smali程式碼,因為要回編譯至dex所以就無法突然65K方法數的限制,那麼就要想辦法對multidex進行瘦身.大資料情況下我們只關心其自有程式碼,對其依賴庫往往是帶過的(大多數情況是和配置檔案中application或者入口Activity同路徑的程式碼).這裡就需要選擇一個較小的dex去識別去除一些依賴庫和第三方sdk比如android support / google gson,然後抽取另外的dex的主包smali程式碼合併到較小的dex中.最後回編譯至dex拖入jeb1.5中就可以正常分析了.

0x02 attach multidex

在multidex的測試過程中還會出現一種情況,就是使用xposed hook其方法的時候,如果方法位於預設dex中是可以的正常hook,但是如果方法位於dex分包中xposed就會報錯提示所要hook的方法所在類無法找到.

要分析這個問題的原因以及解決辦法,就要先了解multidex的載入過程以及xposed的hook時機.

dex分包載入大致流程如下,可以得出分包是滯後主包不少時間載入的:

1.檢測是否有分包需要安裝,系統是否支援multidex

2.從apk中解壓出分包

3.通過反射將分包注入到當前classloader

而xposed為了能讓module及時載入執行所以得儘快呼叫handleLoadPackage(),所以此時獲取的context的classloader中只要預設dex主的包的類.

因此我們得想法得到完整的上下文context,比較明顯的獲取完整context的hook時機有如下兩處:

MultiDex.install()
MultiDexApplication.attachBaseContext()

而xposed的作者建議是選擇android.app.Application.attach(),因為attachBaseContext是有概率被重寫的不穩定.所以選擇方法內呼叫了attachBaseContext的Application.attach().

示例程式碼如下

分析到這裡就可以想到一些加殼後的app無法正常hook也可能是類似原因可以用同樣的方法解決(這裡前提當然是能脫殼看到程式碼且殼沒對hook做對抗,如果有對抗還是脫了之後回打包比較穩妥.).下圖這個殼同樣也佐證了attachBaseContext被重寫的可能,直接hook被重寫的attachBaseContext也是可行的.