Android中Xposed框架篇---基於Xposed的一款脫殼神器ZjDroid工具原理解析

尼古拉斯_趙四發表於2016-12-05

一、前言

在前文中我們介紹了如何使用Xposed框架修改地理位置資訊來進行自身的隱藏功能,本文將繼續介紹Xposed框架的另外一個功能就是實現應用的簡單脫殼,其實說是Xposed的作用其實也不是,主要是模組編寫的好就可以了,主要是利用Xposed的牛逼Hook技術實現的,下面就先來介紹一下這個脫殼模組工具ZjDroid的原理,因為他是開源的,所以咋們直接分析原始碼即可,原始碼的下載地址:https://github.com/halfkiss/ZjDroid 不過可惜的時候他只公開了Java層的程式碼,而native層的程式碼並沒有公開,但是分析原始碼之後會發現最重要的功能就在native層,不過也沒關係,等分析到那裡的時候我在給大家講解底層的大致實現方案即可。


二、ZjDroid原理分析

下面就來詳細的分析一下ZjDroid工具的原始碼吧,他是一個Eclipse工程匯入很簡單,基於之前的Xposed模組編寫的經驗,我們知道找到入口程式碼也很簡單,在assets目錄下有一個xposed_init檔案中就記錄了模組的入口類:



我們直接進入到這個類即可:


看到了,遵循統一規則,實現了IXposedHookLoadPackage介面,實現handleLoadPackage回撥方法即可,下面繼續分析入口方法ModuleContext:


這裡開始攔截Application的onCreate方法了,而這個方法一般是每個應用程式的啟動方法,在這裡做攔截操作也是合情合理的,在看看攔截之後做了什麼,也就是ApplicationOnCreateHook類的實現:


在這裡開始了真正的攔截操作了,主要是新增了一個廣播,也就是說裝置中每個應用在啟動的時候都回去註冊這個廣播,而如果後續傳送一個這樣對應Action的廣播的話,每個應用程式都會收到。所以這裡可以看到,核心工作就在這個廣播的接受之後做了,接下來繼續去看這個廣播的定義:


果然在這裡,可以看到了首先會通過傳送廣播的intent中攜帶一些資料過來,主要是兩個資料:

一個是程式id:這個作用主要是為了過濾其他應用,只處理本應用的邏輯,因為這個廣播傳送之後所有的應用都能接收到,但是我們脫殼有時候肯定只是針對於某一個應用,那麼只需要在這個應用的廣播接收中做處理即可。

一個是命令字串:這個是為了傳送廣播可以支援多種功能,後面分析也可以看到的確有很多功能的。

然後這裡得到命令之後就開始構造一個命令執行器類,這裡用到了設計模式中的命令模式。下面繼續看看有哪幾種命令執行器類:


在這個方法中就開始分析了這裡支援的哪幾種命令類,下面來一一分析一下:

第一個命令:dump_dexinfo

獲取應用執行時記憶體中dex的資訊:DumpDexInfoCommandHandler


進入方法在詳細檢視一下:


看到了,這裡的實現邏輯還是比較簡單的,全部通過反射機制獲取每個應用的dex檔案對應的DexFile型別物件,這裡的工作和我們之前介紹了Android中外掛化開發已經很熟悉了,通過應用的預設類載入PathClassLoader類得到DexPathList類,然後在得到具體的DexFile物件即可。這裡要說的就是這個dex檔案對應的cookie值,這個值非常重要,是後續命令操作的基本資訊,他代表的含義就是底層中每個應用的dex檔案對應的唯一id值,系統會維護一個map結構來儲存這些資料的,系統然後通過這個cookie值來找到對應的dex檔案資訊的。

命令用法:am broadcast -a com.zjdroid.invoke --ei target [pid] --es cmd '{"action":"dump_dexinfo"}'

這裡使用的是命令方式傳送一個廣播,通過--ei攜帶目標程式id是一個int型別,通過--es攜帶命令字串


第二個命令:dump_dexfile

這個命令也是後續脫殼的重要命令,就是dump出應用記憶體中的dex檔案:DumpDexFileCommandHandler


這裡可以看到dump出應用的記憶體資料,首先得需要傳入源應用的dex資料也就是apk檔案,這個一般都是存放在/data/app/xxx.apk目錄下的,然後就是這裡自己構建了一個dump之後的dex檔案路徑,通過原始碼檢視是在/data/data/xxx/files/dexdump.odex中。接下來繼續檢視dump的核心程式碼:


看到這裡有一個核心的方法,但是可惜的是這個方法是native的,而這個工具並沒有把native層的程式碼公開,但是通過這裡傳遞的引數可以瞭解到,底層應該是採用了libdvm.so或者是libart.so庫來得到具體的函式,然後通過dex對應的cookie值獲取資訊。

命令用法:am broadcast -a com.zjdroid.invoke --ei target [pid] --es cmd '{"action":"dump_dexfile","dexpath":"*****"}'

注意這裡的dexpath引數是代表需要脫殼的dex檔案,也就是應用程式檔案。


第三個命令:backsmali

這個命令其實是和上面的命令差不多功能,只是這裡的命令多了一層操作就是把dex檔案轉化成smali檔案,所以這裡不再詳細說明了,咋們可以先得到dex檔案,然後在通過工具得到smali檔案也是可以的。

命令用法:am broadcast -a com.zjdroid.invoke --ei target pid --es cmd '{"action":"backsmali","dexpath":"*****"}'

注意這裡的dexpath引數是代表需要脫殼的dex檔案,也就是應用程式檔案。而最終生成的smali資料夾是放在/data/data/xxx/smali下面的。


第四個命令:dump_mem

這個命令是用來dump出應用程式執行時記憶體中指定開始位置和長度的記憶體塊資料的:DumpMemCommandHandler

可惜這個方法也是native層的,但是這個操作就比較簡單了,我們知道每個應用執行時的記憶體地址都在 /proc/[pid]/maps 檔案中:


那麼查詢記憶體地址,然後在使用memcpy進行記憶體資料拷貝也是非常簡單的。

命令用法:am broadcast -a com.zjdroid.invoke --ei target [pid] --es cmd '{"action":"dump_mem","start":111,"length":23}'

注意這裡的start和length都是十進位制的,而不是十六進位制的資料格式。


第五個命令:dump_heap

這個命令是可以dump出虛擬機器的堆記憶體資訊的,檔案可以使用java heap工具進行分析,而對於這個命令我們想一下應該也知道實現邏輯應該是也是在native層的,而且這個程式碼邏輯應該和上面的那個命令差不多的,但是對於這個命令我還沒有想到具體的思路,悲哀呀,如果有了解的同學就告知一下哈!

命令用法:am broadcast -a com.zjdroid.invoke --ei target [pid] --es cmd '{"action":"dump_heap"}'


第六個命令:dump_class

這個命令主要是用於dump出dex檔案中的類資訊,這個操作也是非常簡單的,因為在DexFile物件中有一個隱藏的方法可以把dex檔案中的所有類名獲取到:getClassNameList


這裡可以看到這個方法的傳入引數為一個dex檔案對應的cookie值。

命令用法:am broadcast -a com.zjdroid.invoke --ei target pid --es cmd '{"action":"dump_class","dexpath":"*****"}'

這裡的dexpath是需要得到所有類資訊的dex檔案路徑,也就是應用的apk檔案路徑。


第七個命令:invoke

這個命令是用於執行時動態呼叫Lua指令碼,本人並沒有看懂這個命令的作用,該功能可以通過Lua指令碼動態呼叫java程式碼。使用場景:可以動態呼叫解密函式,完成解密。可以動態觸發特定邏輯。程式碼就不進行分析了,因為我覺得這個命令應該不怎麼會使用

命令用法:am broadcast -a com.zjdroid.invoke --ei target pid --es cmd '{"action":"invoke","filepath":"****"}'

這裡的filepath是lua指令碼檔案的存放路徑。


到這裡就全部介紹完了ZjDroid的所有命令了,下面還有兩個非常重要的列印日誌的tag:

第一個:adb logcat -s zjdroid-shell-{package name}

這個tag可以檢視上面每個命令執行的結果,便於檢視命令執行的狀態。

第二個:adb logcat -s zjdroid-apimonitor-{package name}

這個tag可以監聽對應包名應用呼叫的哪些api資訊,這個作用有點類似於執行時許可權請求的作用。這個做起來就非常簡單了,可以直接通過Xposed提供的方法進行系統的一些敏感api進行攔截然後新增監控程式碼即可。


三、命令總結

上面就從原始碼的角度完全分析完了ZjDroid工具的功能了,下面就來總結一下:

1、獲取APK當前載入DEX檔案資訊
am broadcast -a com.zjdroid.invoke --ei target pid --es cmd '{"action":"dump_dexinfo"}'

2、獲取指定DEX檔案包含可載入類名
am broadcast -a com.zjdroid.invoke --ei target pid --es cmd '{"action":"dump_class","dexpath":"*****"}'

3、根據Dalvik相關記憶體指標動態反編譯指定DEX,並以檔案形式儲存
am broadcast -a com.zjdroid.invoke --ei target pid --es cmd '{"action":"backsmali","dexpath":"*****"}'

4、Dump指定DEX記憶體中的資料並儲存到檔案(資料為odex格式,可在pc上反編譯)
am broadcast -a com.zjdroid.invoke --ei target pid --es cmd '{"action":"dump_dexfile","dexpath":"*****"}'

5、Dump指定記憶體空間區域資料到檔案
am broadcast -a com.zjdroid.invoke --ei target pid --es cmd '{"action":"dump_mem","start":1234567,"length":123}'

6、Dump Dalvik堆疊資訊到檔案,檔案可以通過java heap分析工具分析處理
am broadcast -a com.zjdroid.invoke --ei target pid --es cmd '{"action":"dump_heap"}'

7、執行時動態呼叫Lua指令碼
該功能可以通過Lua指令碼動態呼叫java程式碼。使用場景:可以動態呼叫解密函式,完成解密。可以動態觸發特定邏輯。
am broadcast -a com.zjdroid.invoke --ei target pid --es cmd '{"action":"invoke","filepath":"****"}'

8、相關命令執行結果檢視
1》、命令執行結果

adb shell logcat -s zjdroid-shell-{package name}

2》、敏感API呼叫監控輸出結果

adb shell logcat -s zjdroid-apimonitor-{package name} 


四、案例使用分析

下面咋們就要用一個案例來看看這個工具到底如何使用,有哪些功效,咋們就用一個應用做案例,就是捕魚達人v1.0.1版本,具體的apk檔案可以自行去網上搜尋哈。我們安裝遊戲之後,然後順便把上面的ZjDroid模組工具也安裝上,然後進行重啟生效。

我們最好是單獨開一個CMD視窗用來檢視列印結果,但是從上面的命令可以看到,我們應該需要這遊戲的包名和程式id,那麼這兩個資料怎麼獲取呢?其實我在前面的文章已經介紹了很多次了,用一個命令即可:adb shell dumpsys activity top 但是這時候需要執行起來捕魚達人遊戲:


這裡看到了,就非常簡單的獲取到了遊戲的包名:org.cocos2d.fishingjoy3和程式id=25304,這兩個資料非常重要可以這裡先進行保管,進而後面可以使用。


下面首先來看一下這個應用用了哪些敏感的api資料,使用上面檢視日誌的命令即可:

adb shell logcat -s zjdroid-apimonitor-org.cocos2d.fishingjoy3


看到了,這裡又網路請求資訊,網路切換的廣播等資料的,感覺還是蠻有用的這個工具。


下面在來看一下這個應用的dex檔案資訊,可以使用下面命令即可:

am broadcast -a com.zjdroid.invoke --ei target 25304 --es cmd '{action:dump_dexinfo}'

這裡執行之後的結果:

這時候會發現,執行沒有看到實際效果,原因是因為我們需要通過日誌才能可以看到資料的,因為上面命令執行的結果都是需要通過這個日誌才可以看到的:adb logcat -s zjdroid-shell-org.cocos2d.fishingjoy3


這裡就可以看到了具體的資訊了,看到了有一個filepath這個就是我們後續有些命令需要用到的dex路徑,所以一定要記下來,後面的命令會使用到的。


接下來在看一個關於dump出遊戲中所有的類名的命令:

am broadcast -a com.zjdroid.invoke --ei target 25304 --es cmd '{"action":"dump_class","dexpath":"/data/app/org.cocos2d.fishingjoy3-1.apk"}'

這裡的路徑就是上面獲取到的apk路徑,結果咋們還是需要通過上面的日誌命令才可以看到:


看到了吧,這裡就匯出來了遊戲包含的所有類名了。


最後在來一發,這個是最關鍵的,也是本文的重點也是這個工具最實用的一個功能,就是進行應用的脫殼處理了,而本文的遊戲也是進行加固處理的,我們可以反編譯看看這個遊戲:


你會發現沒幾個類,而且有一個Application類,那麼可以認定了這個遊戲被加固了,本文不介紹是使用哪家的加固平臺操作了,也不再使用IDA等工具去動態除錯脫殼了,這裡直接使用這個工具進行操作即可。咋們為了後續程式碼閱讀方便可以直接獲取他的smali程式碼,也就是使用這個命令:

am broadcast -a com.zjdroid.invoke --ei target 25304 --es cmd '{action:backsmali, "dexpath":"/data/app/org.cocos2d.fishingjoy3-1.apk"}'

這個命令的執行結果咋們通過日誌檢視:


而這個smali資料夾是存放在應用的 /data/data/org.cocos2d.fishingjoy3/smali 中的,咋們可以把他拷貝出來即可:


這裡可以看到咋們就成功的脫殼了,生成了遊戲的所有smali檔案程式碼了。其實這個脫殼操作和我們之前介紹使用IDA工具進行脫殼原理都差不多,因為應用程式不管怎麼加固,最終都會使用一個系統函式將dex檔案載入到記憶體中,而載入之前肯定要進行解密操作,我們只要在載入之前解密之後進行攔截即可。


五、總結

好了,到這裡我們就講解完了基於Xposed框架的脫殼神器ZjDroid的實現原理以及具體用法。而這裡也感受到了Xposed框架的強大之處,當然這也只是一部分,後面還可以利用這個框架編寫遊戲外掛等操作。所以好好膜拜這個框架,也好好拜讀這個框架的原始碼吧,學不完的東東呢!寫的好累好辛苦,記得給小編點贊呢!!


《Android應用安全防護和逆向分析》

點選立即購買:京東  天貓

更多內容:點選這裡

關注微信公眾號,最新技術乾貨實時推送

掃一掃加小編微信
新增時註明:“編碼美麗”否則不予通過!

相關文章