基於Xposed的一款脫殼神器ZjDroid工具原理解析

一、前言

本文將介紹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_dex","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/app/org.cocos2d.fishingjoy3/smali 中的,咋們可以把他拷貝出來即可:


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


五、總結

好了,到這裏我們就講解完了基於Xposed框架的脫殼神器ZjDroid的實現原理以及具體用法。而這裏也感受到了Xposed框架的強大之處,當然這也只是一部分,後面還可以利用這個框架編寫遊戲外掛等操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章