Android動態調試七武器系列文章——Hooking下篇,分享一些常用或原創的調試工具以及手段,希望能對國內移動安全的研究起到一些催化劑的作用。 ...
0x00 序
隨著移動安全越來越火,各種調試工具也都層出不窮,但因為環境和需求的不同,並沒有工具是萬能的。另外工具是死的,人是活的,如果能搞懂工具的原理再結合上自身的經驗,你也可以創造出屬於自己的調試武器。因此,筆者將會在這一系列文章中分享一些自己經常用或原創的調試工具以及手段,希望能對國內移動安全的研究起到一些催化劑的作用。
目錄如下:
安卓動態調試七種武器之長生劍 - Smali Instrumentation
安卓動態調試七種武器之孔雀翎 – Ida Pro
安卓動態調試七種武器之離別鉤 – Hooking (上)
安卓動態調試七種武器之離別鉤 – Hooking (下)
安卓動態調試七種武器之碧玉刀- Customized DVM
安卓動態調試七種武器之多情環- Customized Kernel
安卓動態調試七種武器之霸王槍 - Anti Anti-debugging
安卓動態調試七種武器之拳頭 - Tricks & Summary
文章中所有提到的代碼和工具都可以在我的github下載到,地址是: https://github.com/zhengmin1989/TheSevenWeapons
0x01 利用函數掛鉤實現native層的hook
我們在離別鉤(上)中已經可以做到動態的載入自定義so文件並且運行so文件中的函數了,但還不能做到hook目標函數,這裡我們需要用到函數掛鉤的技術來做到這一點。函數掛鉤的基本原理是先用mprotect()將原代碼段改成可讀可寫可執行,然後修改原函數入口處的代碼,讓pc指針跳轉到動態載入的so文件中的hook函數中,執行完hook函數以後再讓pc指針跳轉回原本的函數中。
用來註入的程式hook5邏輯與之前的hook4相比並沒有太大變化,僅僅少了“調用 dlclose 卸載so文件”這一個步驟,因為我們想要執行的hook後的函數在so中,所以並不需要調用dlclose進行卸載。基本步驟如下:
保存當前寄存器的狀態 -> 獲取目標程式的mmap, dlopen, dlsym, dlclose 地址 -> 調用mmap分配一段記憶體空間用來保存參數信息 –> 調用dlopen載入so文件 -> 調用dlsym找到目標函數地址 -> 使用ptrace_call執行目標函數 -> 恢復寄存器的狀態
hook5的主要代碼邏輯如下:
我們知道arm處理器支持兩種指令集,一種是arm指令集,另一種是thumb指令集。所以要hook的函數可能是被編譯成arm指令集的,也有可能是被編譯成thumb指令集的。Thumb指令集可以看作是arm指令壓縮形式的子集,它是為減小代碼量而提出,具有16bit的代碼密度。Thumb指令體系並不完整,只支持通用功能,必要時仍需要使用ARM指令,如進入異常時。需要註意的一點是thumb指令的長度是不固定的,但arm指令是固定的32位長度。
為了讓大家更容易的理解hook的原理,我們先只考慮arm指令集,因為arm相比thumb要簡單一點,不需要考慮指令長度的問題。所以我們需要將target和hook的so編譯成arm指令集的形式。怎麼做呢?很簡單,只要在Android.mk中的文件名後面加上”.arm”即可 (真正的文件不用加)。
確定了指令集以後,我們來看實現掛鉤最重要的邏輯,這個邏輯是在註入後的so里實現的。首先我們需要一個結構體保存彙編代碼和hook地址:
我們接著來看註入的邏輯,最重要的函數為hook_direct(),他有三個參數,一個參數是我們最開始定義的用來保存彙編代碼和hook地址的結構體,第二個是我們要hook的原函數的地址,第三個是我們用來執行hook的函數。函數的源碼如下:
雖然android有ASLR,但並沒有PIE,所以program image是固定在0x8000這個地址的,因此我們用mprotect()函數將整個target代碼段變成RWX,這樣我們就能修改函數入口處的代碼了。是否修改成功可以通過cat /proc/[pid]/maps查看:
隨後我們需要確定目標函數的地址,這個有兩種方法。如果目標程式本身沒有被strip的話,那些symbol都是存在的,因此可以使用dlopen()和dlsym()等方法來獲取目標函數地址。但很多情況,目標程式都會被strip,特別是可以直接運行的二進位文件預設都會被直接strip。比如target中的sevenWeapons()這個函數名會在編譯的時候去掉,所以我們使用dlsym()的話是無法找到這個函數的。這時候我們就需要使用ida或者objdump來定位一下目標函數的地址。比如我們用objdump找一下target程式裡面sevenWeapons(int number)這個函數的地址:
雖然target這個binary被strip了,但還是可以找到sevenWeapons()這個函數的起始地址是在0x84f4。因為”mov r2, r0”就是載入number這個參數的指令,隨後調用了<printf@plt>用來輸出結果。 最後一個參數也就是我們要執行的hook函數的地址。得到這個地址非常簡單,因為是so中的函數,調用hook_direct()的時候直接寫上函數名即可。
hook_direct(&eph,hookaddr,my_sevenWeapons);
接下來我們看如何修改函數入口(modify function entry),首先我們保存一下原函數的地址和那個函數的前三條指令。隨後我們把目標函數的第一條指令修改為 LDR pc, [pc, #0],這條指令的意思是跳轉到PC指針所指的地址,由於pc寄存器讀出的值實際上是當前指令地址加8,所以我們把後面兩處指令都保存為hook函數的地址,這樣的話,我們就能控制PC跳轉到hook函數的地址了。
最後我們再調用hook_cacheflush()這個函數來刷新一下指令的緩存。因為雖然前面的操作修改了記憶體中的指令,但有可能被修改的指令已經被緩存起來了,再執行的話,CPU可能會優先執行緩存中的指令,使得修改的指令得不到執行。所以我們需要使用一個隱藏的系統調用來刷新一下緩存。hook_cacheflush()代碼如下:
刷新完緩存後,再執行到原函數的時候,pc指針就會跳轉到我們自定義的hook函數中了,hook函數里的代碼如下:
首先在hook函數中,我們可以獲得原函數的參數,並且我們可以對原函數的參數進行修改,比如說將數字乘2。隨後我們使用hook_precall(&eph);將原本函數的內容進行還原。hook_precall()內容如下:
在hook_precall()中,我們先對原本的三條指令進行還原,然後使用hook_cacheflush()對記憶體進行刷新。經過處理之後,我們就可以執行原來的函數orig_sevenWeapons(number)了。執行完後,如果我們還想再次hook這個函數,就需要調用hook_postcall(&eph)將原本的三條指令再進行一次修改。
下麵我們來使用hook5和libinject2.so來註入一下target這個程式:
可以看到經過註入後,我們成功的獲取了參數number的值,並且將”Hello,LiBieGou!”後面的數字變成了原來的兩倍。
0x02 使用adbi實現JNI層的hook
我們在上一節中介紹瞭如何hook native層的函數。下麵我們再來講講如何利用adbi來hook JNI層的函數。Adbi是一個android平臺上的一個註入框架,本身是開源的。Hook的原理和我們之前介紹的技術是一樣的,這個框架支持arm和thumb指令集,也支持通過字元串定位symbol函數的地址。
首先我們需要一個例子用來講解,所以我寫了程式叫test2。
點擊程式中的button後,程式會調用so中的Java_com_mzheng_libiegou_test2_MainActivity_stringFromJNI(JNIEnv* env,jobject thiz,jint a,jint b)函數用來計算a+b的結果。我們預設傳的參數是a=1, b=1。接下來我就來教你如何利用adbi來hook這個JNI函數。
因為adbi是一個註入框架,我們下載好源碼後,只要對應著源碼中給的example照貓畫虎即可。Hijack那個文件夾是保存的用來註入的程式,和我們之前講的hook5.c的原理是一樣的,所以不用做任何修改。我們只需要修改example中的代碼,也就是將要註入的so文件的源碼。
首先,我們在/adbi-master/instruments/example這個文件夾下新建兩個文件”hookjni.c”和” hookjni_arm.c”。“hookjni_arm.c”其實只是一個殼,用來將hook函數的入口編譯成arm指令集的,內容如下:
這個文件的目的僅僅是為了用arm指令集進行編譯,可以看到Android.mk中在”hookjni_arm.c”後面多了個”.arm”:
下麵我們來看”hookjni.c”:
這段代碼和我上一節講的代碼非常像,my_init()用來進行hook操作,我們需要提供想要hook的so文件名和函數名,然後再提供thumb指令集和arm指令集的hook函數地址。
my_Java_com_mzheng_libiegou_test2_MainActivity_stringFromJNI()就是我們提供的hook函數了。我們在這個hook函數中把a和b都改成了10。除此之外,我們還使用counter這個全局變數來控制hook的次數,這裡我們把counter設置為3,當hook了3次以後,就不再進行hook操作了。
編輯好代碼後,我們只需要在adbi的根目錄下執行” build.sh”進行編譯:
編譯好後,我們把hijack和libexample.so拷貝到/data/local/tmp目錄下。然後使用hijack進行註入:
然後我們再點擊button就可以看到我們已經成功的修改了a和b的值為10了,最後顯示1+1=20。
通過cat adbi_example.log我們可以看到hook過程中列印的log:
可以看到adbi是通過thumb指令集進行hook的,原因是test2程式是用thumb指令集進行編譯的。其實hook thumb指令集和arm指令集差不多,在”/adbi-master/instruments/base”中可以找到hook thumb指令集的邏輯:
其實h->jumpt[20]這個字元數組保存的就是thumb指令集下控制pc指針跳轉到hook函數地址的代碼。Hook完之後的流程就和arm指令集的hook一樣了。
0x03 使用Cydia或Xposed實現JAVA層的hook
關於Cydia和Xposed的文章和例子已經很多了,這裡就不再重覆的進行介紹了。這裡推薦一下瘦蛟舞和我寫的文章,基本上就知道怎麼使用這兩個框架了:
Android.Hook框架xposed篇(Http流量監控) http://drops.wooyun.org/papers/7488
Android.Hook框架Cydia篇(脫殼機製作) http://drops.wooyun.org/tips/8084
手把手教你當微信運動第一名 – 利用Android Hook進行微信運動作弊 http://drops.wooyun.org/tips/8416
個人感覺Xposed框架要做的更好一些,主要原因是Cydia的作者已經很久沒有更新過Cydia框架了,不光有很多bug還不支持ART。但是有很多不錯的調試軟體/插件是基於兩個框架製作的,所以有時候還是需要用到Cyida的。
接下來就推薦幾個很實用的基於Cydia和Xposed的插件:
1)ZjDroid: ZjDroid是基於Xposed Framewrok的動態逆向分析模塊,逆向分析者可以通過ZjDroid完成以下工作: 1、DEX文件的記憶體dump 2、基於Dalvik關鍵指針的記憶體BackSmali,有效破解主流加固方案 3、敏感API的動態監控 4、指定記憶體區域數據dump 5、獲取應用載入DEX信息。 6、獲取指定DEX文件載入類信息。 7、dump Dalvik java堆信息。 8、在目標進程動態運行lua腳本。 https://github.com/halfkiss/ZjDroid
2)XPrivacy: XPrivacy是一款基於Xposed框架的模塊應用,可以對所有應用可能泄露隱私的許可權進行管理,對禁止可能會導致崩潰的應用採取欺騙策略,提供偽造信息,比如說可以偽造手機的IMEI號碼等。 https://github.com/M66B/XPrivacy
3)Introspy: Introspy是一款可以追蹤分析移動應用的黑盒測試工具並且可以發現安全問題。這個工具支持很多密碼庫的hook,還支持自定義hook。 https://github.com/iSECPartners/Introspy-Android
0x04 Introspy實戰
我們使用alictf上的evilapk400作為例子講解如何利用introspy來調試程式。Evilapk400使用了比較複雜的dex加殼技術,如果不利用基於自定義dalvik的脫殼工具來進行脫殼的話做起來會非常麻煩。但我們如果換一種思路,直接通過動態調試的方法來獲取加密演算法的字元串,key和IV等信息就可以直接獲取答案了。
首先我們安裝cyida.apk,Introspy-Android Config.apk到手機上,然後用eclipse打開“Introspy-Android Core”的源碼增加一個自定義的hook函數。雖然Introspy預設對密碼庫進行了hook,但卻沒有對一些strings的函數進行hook。所以我們手動添加一個對String.equals()的hook:
然後我們編譯,生成並安裝Introspy-Android Core.apk到手機上。然後我們安裝上EvilApk400。然後打開Introspy-Android Config勾選com.ali.encryption。
接著打開evilapk400,然後隨便輸入點內容並點擊登陸。
然後我們使用adb logcat就能看到Introspy輸出的信息了:
通過log,很容易就能看出來evilapk400使用了DES加密。通過log我們獲取了密文,Key以及IV,所以我們可以寫一個python程式來計算出最後的答案:
0x05 總結
本篇介紹了native層,JNI層以及JAVA層的hook,基本上可以滿足我們平時對於android上hook的需求了。 另外文章中所有提到的代碼和工具都可以在我的github下載到,地址是: https://github.com/zhengmin1989/TheSevenWeapons
0x06 參考資料
- Android平臺下hook框架adbi的研究(下)http://blog.csdn.net/roland_sun/article/details/36049307
- ALICTF Writeups from Dr. Mario
作者:蒸米@阿裡聚安全,更多技術文章,請訪問阿裡聚安全博客