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

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

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

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

F9運行程序,當看到下圖的狀態,調用open函數打開文件/proc/self/status時,可以確實是加固外殼apk程序通過TracePid的反調試。

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

360加固的外殼程序apk還會通過文件/proc.NET/tcp的本地套接字進行反調試,一旦檢測到程序有23946端口的本地連接的套接字,他就會結束當前程序,其實過這個方式的反調試的方法很多,修改遠程調試的端口號就可以過掉反調試。


過掉通過/proc/net/tcp文件的本地套接字的反調試的方法是在open函數處下斷點,然后F8單步調試發現strstr函數,加固的外殼apk程序就是通過strstr函數進行判斷當前進程是否存在本地連接的套接字,一旦檢測到,就結束當前進程。

發現程序循環調用strstr函數進行判斷,當strstr函數的返回值不為0為字符串“sl local_address rem_address”時,需要注意,說明當前進程已經檢測到了本地連接的套接字,因此需要修改strstsr函數的返回值為0即可。


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

過掉反調試的方法已經說明白了,下面只要F9運行,按照前面的方法過掉幾次前面提到的反調試即可暢快的調試程序了。當發現當前進程調用open函數打開文件/proc/pid/maps需要注意了,脫殼需要關注的地方開始了。原被加固的dex文件開始解壓和解密了。

經過幾次F9的調試發現了下面截圖的狀態,但是不要心急,只是離脫殼進了一步,此時內存dump出來的odex文件不是被加固的原dex文件的而是加固外殼qihu360的dex文件。

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

OK,現在的任務就是從內存中dump出原來完整的dex文件,既可以手動的dump出內存中的dex文件,也可以通過IDC的腳本或者dumpdex的插件將原dex文件dump出來。

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