前言
現(xiàn)在市面上對(duì)App的安全合規(guī)管控越來越嚴(yán)格了,也就要求了APP在上架之前一定要做合規(guī)檢測(cè)和加固處理。對(duì)APP就是加固的好處,可以提高APP的安全性,提高APP被逆向分析破解的門檻,同時(shí)通過加固保護(hù)可以提高過安全合規(guī)的檢測(cè)。
由于APP加固技術(shù)不斷被攻破情況,因此加固技術(shù)也是不斷在快速迭代的過程。現(xiàn)在市面上的加固產(chǎn)品的還是比較多的,并且各個(gè)加固技術(shù)產(chǎn)品都有其各自優(yōu)缺點(diǎn),但是加固產(chǎn)品的所采用技術(shù)去有很多共性的地方。下面就對(duì)加固和脫殼對(duì)抗方案做些梳理總結(jié)。
Android 反編譯的威脅
- 逆向分析: 漏洞挖掘、協(xié)議分析
- 二次打包: 盜版、破解、廣告
保護(hù)方案
- 代碼混淆:JAVA代碼、CC++帶馬甲、JShtml代碼
- 應(yīng)用加固:DEX文件、SO文件、資源文件
APP構(gòu)建過程中用到的工具

編譯流程
- java源碼編譯:通過javac將源碼編譯為.class文件
- 多dex分包:腳本將類根據(jù)一定規(guī)則劃分到主dex和從dex中,生成配置文件
- proguard優(yōu)化/混淆:對(duì).class文件進(jìn)行壓縮、優(yōu)化、混淆處理
- 轉(zhuǎn)化為dex文件:dxd8將.class文件轉(zhuǎn)換為dex文件
DEX加固方案的演進(jìn)

- 動(dòng)態(tài)加載:從服務(wù)器動(dòng)態(tài)加載業(yè)務(wù)的DEX
- DEX內(nèi)存加載:模擬App啟動(dòng)的時(shí)候,將我們的殼、將業(yè)務(wù)DEX文件加載到內(nèi)存中,然后通過一些處理方式,讓DEX文件把Application啟動(dòng)起來,讓應(yīng)用認(rèn)為和普通的啟動(dòng)方式是一樣的 (缺點(diǎn):DEX會(huì)暴露在內(nèi)存中)
- DEX指令抽取:把DEX文件中的一些方法的指令抽取出來,然后在內(nèi)存中開辟一段區(qū)域,然后將我們內(nèi)存加載的DEX解析到相應(yīng)的方法指令偏移的地方,方法指令的偏移指向我們開辟的一段內(nèi)存里面。(缺點(diǎn):不徹底)
- 虛擬機(jī)加固:我們通過自己實(shí)現(xiàn)一套虛擬機(jī),將DEX方法的指令抽取出來,在運(yùn)行的時(shí)候,我們的虛擬機(jī)里面就運(yùn)行被抽取的一段指令。這樣攻擊者想要破解,首先必須要找到我們虛擬機(jī)的入口,將虛擬機(jī)整個(gè)邏輯還原出來,這樣攻擊成本相對(duì)比較高 (缺點(diǎn):本身Android應(yīng)用就運(yùn)行在虛擬機(jī)里面,不論是Dalvik還是ART,增加個(gè)虛擬機(jī),就會(huì)增加一層對(duì)應(yīng)用運(yùn)行時(shí)代碼的解釋執(zhí)行操作,那么解釋執(zhí)行的效率會(huì)大大下降)
- JAVA2C:提升應(yīng)用運(yùn)行時(shí)的效率,我們的方法會(huì)轉(zhuǎn)換為一層在Native(JNI)上實(shí)現(xiàn)的邏輯,最終通過JNI編譯成so,這樣的話在本地執(zhí)行,而不是在虛擬機(jī)執(zhí)行,執(zhí)行效率會(huì)大大提升。
360加固與脫殼流程
360整體加固classes.dex后的apk程序的特點(diǎn),以超信1.1.4版本為例。360加固以后,會(huì)在apk的assets文件的路徑下增加兩個(gè)文件libjiagu.so和libjiagu_x86.so以及修改原apk的AndroidManifest.xml文件的application標(biāo)簽增加兩個(gè)元素。

360加固脫殼需要完成兩個(gè)任務(wù),一個(gè)任務(wù)是過掉360加固的反調(diào)試,另外一個(gè)是獲取原apk的classes.dex文件。下面寫一下具體的脫殼步驟,完成這兩個(gè)任務(wù)。為了脫殼成功,在動(dòng)態(tài)調(diào)試加固apk程序前,需要將android_server調(diào)試程序的名字修改一下,因?yàn)?60加固會(huì)通過查詢/proc/pid/cmdline文件獲取程序的名稱來對(duì)android_server、gdb、ltrace、strace調(diào)試器進(jìn)行反調(diào)試。
在動(dòng)態(tài)調(diào)試加固apk程序的時(shí)候,需要在幾個(gè)關(guān)鍵點(diǎn)函數(shù) open、strtol、mmap、memcmp處下斷點(diǎn),后面具體的分析。由于android系統(tǒng)是由linux系統(tǒng)修改而來,因此Android系統(tǒng)上很多的屬性和Linux系統(tǒng)下的屬性。Linux下進(jìn)程和線程的信息是保存在文件中的,因此要查詢進(jìn)程或者線程的信息必須通過open或者fopen等方式操作文件來完成。
Android的so調(diào)試的步驟略過,已經(jīng)在上面提到的4個(gè)函數(shù)處下斷點(diǎn),注意下斷點(diǎn)的技巧,下斷點(diǎn)的時(shí)候,要在一個(gè)函數(shù)的第1條指令和最后一條指令的地方下斷點(diǎn)。

F9運(yùn)行程序,當(dāng)看到下圖的狀態(tài),調(diào)用open函數(shù)打開文件/proc/self/status時(shí),可以確實(shí)是加固外殼apk程序通過TracePid的反調(diào)試。

過掉通過TracePid方式的反調(diào)試的方法是在strtol函數(shù)處下斷點(diǎn),在strstol函數(shù)的最后一行指令的地方,修改該函數(shù)的返回值R0的值為0即可,然后F9運(yùn)行程序。

360加固的外殼程序apk還會(huì)通過文件/proc.NET/tcp的本地套接字進(jìn)行反調(diào)試,一旦檢測(cè)到程序有23946端口的本地連接的套接字,他就會(huì)結(jié)束當(dāng)前程序,其實(shí)過這個(gè)方式的反調(diào)試的方法很多,修改遠(yuǎn)程調(diào)試的端口號(hào)就可以過掉反調(diào)試。


過掉通過/proc/net/tcp文件的本地套接字的反調(diào)試的方法是在open函數(shù)處下斷點(diǎn),然后F8單步調(diào)試發(fā)現(xiàn)strstr函數(shù),加固的外殼apk程序就是通過strstr函數(shù)進(jìn)行判斷當(dāng)前進(jìn)程是否存在本地連接的套接字,一旦檢測(cè)到,就結(jié)束當(dāng)前進(jìn)程。

發(fā)現(xiàn)程序循環(huán)調(diào)用strstr函數(shù)進(jìn)行判斷,當(dāng)strstr函數(shù)的返回值不為0為字符串“sl local_address rem_address”時(shí),需要注意,說明當(dāng)前進(jìn)程已經(jīng)檢測(cè)到了本地連接的套接字,因此需要修改strstsr函數(shù)的返回值為0即可。


加固apk程序還會(huì)通過"/proc/pid/mdline"對(duì)android_server、gdb、strace、ltrace調(diào)試器進(jìn)行反調(diào)試,但是只要向前文提到的修改android_server的名稱即可過掉該反調(diào)試。

過掉反調(diào)試的方法已經(jīng)說明白了,下面只要F9運(yùn)行,按照前面的方法過掉幾次前面提到的反調(diào)試即可暢快的調(diào)試程序了。當(dāng)發(fā)現(xiàn)當(dāng)前進(jìn)程調(diào)用open函數(shù)打開文件/proc/pid/maps需要注意了,脫殼需要關(guān)注的地方開始了。原被加固的dex文件開始解壓和解密了。

經(jīng)過幾次F9的調(diào)試發(fā)現(xiàn)了下面截圖的狀態(tài),但是不要心急,只是離脫殼進(jìn)了一步,此時(shí)內(nèi)存dump出來的odex文件不是被加固的原dex文件的而是加固外殼qihu360的dex文件。

繼續(xù)F9運(yùn)行程序,經(jīng)過n次F9運(yùn)行memcmp函數(shù)以后(其實(shí)這里可以做一個(gè)處理,暫時(shí)去掉mmap函數(shù)的斷點(diǎn),等到mmap函數(shù)斷點(diǎn)處再次設(shè)置mmap函數(shù)的斷點(diǎn)),再經(jīng)過大約2次mmap函數(shù)運(yùn)行以后,再一次調(diào)用memcmp函數(shù)即到了脫殼的地方,注意此時(shí)加固外殼apk程序呈現(xiàn)的堆棧和內(nèi)存的狀態(tài)。由于360加固自己制作了dvmDexFileOpenPartial函數(shù),但是該函數(shù)需要調(diào)用memcmp進(jìn)行dex文件的魔法字符串”dex“的比較,因此此時(shí)是脫殼的最佳時(shí)機(jī)。

OK,現(xiàn)在的任務(wù)就是從內(nèi)存中dump出原來完整的dex文件,既可以手動(dòng)的dump出內(nèi)存中的dex文件,也可以通過IDC的腳本或者dumpdex的插件將原dex文件dump出來。

原apk程序的Dex文件拿到了,只需替換360加固的外殼程序的dex,刪除AndroidManifest.xml文件的application標(biāo)簽增加的兩個(gè)元素,刪除assets文件的路徑下增加兩個(gè)文件libjiagu.so和libjiagu_x86.so以及原來的簽名,重新簽名一下apk程序即可。
在刪除的application標(biāo)簽增加的元素中,特別是在刪除android:name =””的時(shí)候,一定要檢查脫殼的classes.Dex文件中否重寫過application類,如果脫殼后的classes.Dex文件中重寫了application類,需要將application標(biāo)簽中android:name=””修改過來才行。
以上就是有關(guān)dex加固的方法了,舉例了360加固與脫殼流程。
文末
拓展學(xué)習(xí)
so加固
so反編譯比較困難,比dex的反編譯困難。一般做法是抽取Android工程的關(guān)鍵java代碼轉(zhuǎn)為c/c++, 然后生成so文件。so加密我只是了解了一下破壞.so部分頭文件的方式加固。
加固里面的VMP(虛擬機(jī))是什么?
VMP(虛擬軟件保護(hù)技術(shù))大概思路就是自定義一套虛擬機(jī)指令和對(duì)應(yīng)的解釋器,并將標(biāo)準(zhǔn)的指令轉(zhuǎn)換成自己的指令,然后由解釋器將自己的指令給對(duì)應(yīng)的解釋器。