Apk脫殼聖戰之---脫掉“愛加密”家的殼

一、前言

今天是端午節,然而小編不能吃糉子了,只能繼續破解之路,今天我們來看一下在瞭解了破解三部曲之後,如何開始脫掉各個市場中的apk殼,關於破解三部曲在之前已經介紹了:

第一篇:Android中使用Eclipse動態調試smali源碼

第二篇:Android中使用IDA動態調試so源碼

第三篇:Android中破解加固的apk

在看完這三篇文章之後,我們開始操作如何破解市場中的加殼方案,現在市場中比較流行的加殼平臺就那麼幾個:愛加密,梆梆加固,360加固,騰訊加固等,所以後面會一一介紹如何脫掉這些平臺的殼。之前也說過現在加固的方案大體思路都是:把源apk進行加密拆分處理,然後在套一個外部的殼Application做一些初始化操作:比如解密apk,動態加載運行即可。但是我們已經知道了如何去破解那些加固的apk了,就是使用IDA給dvmDexFileOpenPartial函數下斷點,然後dump出內存中的dex數據即可。因爲內存中的dex肯定是解密之後的,所以大體思路知道了,但是這些加固平臺也有對策,他們會把做一些反調試操作,對so文件進行混淆加密等,讓我們的調試變得比較困難。這纔是我們脫殼的阻礙地方。

 

二、案例分析

好了,說了這麼多,下面我們就開始脫殼第一站:愛加密家的殼

爲了脫掉他家的殼,我們得首先有一個案例程序,這個比較簡單,我們自己弄一個demo程序,然後去他家的網站上加固一下,得到加固之後的apk,然後這時候我們開始破解了,按照慣例:

第一步:解壓apk,看看大體的目錄,得到classes.dex文件,然後用dex2jar+jd-gui得到Java源碼

看到,這裏只有Application的殼,而且這個是愛加密加固之後的特點,都是這兩個Application的。

第二步:使用apktool來反編譯apk,獲取資源文件信息

分析一下愛加密的加密流程

也是國際慣例,愛加密把我們的源程序進行加密操作然後隱藏到了一個地方,在之前破解加固apk的那篇文章中也說過了,隱藏的地方就那麼幾個:assets目錄、libs目錄、自己的dex文件中

這裏我們直接看assets目錄:

多了這個東東,猜想這個可能就是處理之後的源apk了。我們在AndroidManifest.xml中看到了入口的Application類,先來看這個類

下面我們來分析一下這個SuperApplication類:

這裏一般都是在attachBaseContext這個方法中進行操作的,這裏的時機比較早,我們看到首先會調用loadLibs方法進行加載libs:

這裏區分不同的平臺,然後進行拷貝不同的so文件,繼續看copyLib方法:

這裏我們可以看到了,從assets目錄下把愛加密增加的兩個so文件:libexec.so和libexecmain.so拷貝到應用程序的files目錄下,我們可以去看看assets/ijm_lib目錄下的so文件:

到這裏loadLibs方法就執行完了,下面就開始調用NativeApplication的load方法進行加載數據,繼續看NativeApplication類:

這裏會開始從應用程序的files目錄中加載這兩個so文件,而且load方法也是一個native方法,我們繼續看看這兩個so文件內容:

我們首先用IDA打開libexecmain.so文件,但是發現,他裏面並沒有什麼重要信息,連JNI_OnLoad函數都沒有東東

我們繼續在查看libexec.so文件:

擦,可惜的是,打開提示so文件格式錯誤,到這裏,我們就猜到了,這個so可能被加密處理了,elf格式改了,關於so如何進行加密操作的,不瞭解的同學可以看這裏:Android中如何對so文件進行加密 那麼這裏我們點擊Yes繼續強制打開之後,在使用Ctrl+S查看so的各個段信息:

現在可以百分百的確定,這個so文件被處理了,段格式被修改了。我們沒辦法分析so文件了,當然這裏我們可以在dump出內存中的so文件,然後在分析的,但是這個不是今天講解的重點。我們先分析到這裏,也知道了愛加密的大體加密流程。

好了,到這裏,我們差不多分析完了愛加密的加密流程了:

1、按照國際慣例把源apk進行加密處理存放在一個地方,通過分析猜想是assets目錄下的ijiami.dat文件

2、添加殼Application:SuperApplication類,在這個殼的attachContext方法中,主要做了兩件事:

1》第一件事是把assets/ijm_lib目錄下的兩個so文件copy到程序的files目錄中;

2》第二件事是調用NativeApplication的load方法,在這個類中同時也把上面的兩個so文件加載到內存中

3、對apk的加密操作都是放在底層的兩個so文件中操作的,我們通過IDA去分析這兩個so文件之後,發現核心功能的so文件被加密了,IDA打開是看不到具體信息了

到這裏,我們知道愛加密加固之後的特點是:在程序的assets目錄下多了一個ijiami.dat文件和兩個so文件,同時這兩個so文件被加密處理了,增加破解難度。

 

三、破解脫殼

上面就簡單分析了愛加密的原理和流程,但是我們沒有繼續往下面分析了,因爲這個不是我們今天講解的重點,我們今天的重點是如何脫掉愛加密的殼,那麼還是開始說到的,脫殼的核心就一個:給dvmDexFileOpenPartial函數下斷點,dump出內存的dex文件即可,那麼下面我們就是用IDA開始脫殼操作了:

第一步:啓動設備中的android_server,然後進行端口轉發

adb forward tcp:23946 tcp:23946

第二步:用debug模式啓動程序

adb shell am start -D -n com.droider.crackme0201/.MainActivity

這裏的包名和入口Activity都可以在上面反編譯之後的AndroidManifest.xml中找到

第三步:雙開IDA,一個用於靜態分析libdvm.so,一個用於動態調試

記錄dvmDexFileOpenPartial函數的相對地址:4777C

再次打開一個IDA,進行attach調試進程

第四步:使用jdb命令attach上調試器

jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700

第五步:對dvmDexFileOpenPartial函數下斷點

進入調試頁面之後,Ctrl+S查找libdvm.so的內存基地址:415BB000

在第三步得到相對地址:4777C+415BB000=4160277C 得到了dvmDexFileOpenPartial在內存中的絕對地址

注意:

當然這裏還有一個更方便的辦法:

就是直接打開Modules View:

在這裏查找libdvm.so文件:

然後雙擊libdvm.so文件:

查找需要下斷點的函數名稱,看到這裏的絕地地址也是:4160277C

這裏有兩種方式可以得到一個函數在內存中的絕對地址。

然後我們使用G鍵,直接跳轉到函數處,下斷點:

第六步:設置Debugger Options選項

能夠讓程序斷在dvmDexFileOpenPartial函數處

注意:

上面的第四步,第五步,第六步,沒有順序的,只要在運行之前設置到就可以了。

第七步:運行程序

出現這個對話框,不要在意,一路點擊Cancel即可

jdb也attach上了調試程序:

我們一路點擊運行按鈕,知道運行到dvmDexFileOpenPartial處的斷點,但是可惜的是,這裏我們遇到了錯誤:

我們點擊OK之後,出現了下面對話框:

再次點擊任何一個按鈕,都會退出了調試頁面:

我們在重新嘗試一次上面的流程,開始調試,但是錯誤是一樣的,好了,到這裏我們就立馬想到了,之前說的IDA調試so的那篇文章遇到的那個問題:反調試檢測

當時我們也是遇到這個情況,在沒有運行到我們下的斷點處,就退出了調試頁面,其實這個是現在加固平臺必要選擇的一種方式,其實反調試原理很簡單,就是在程序運行最早的時機比如so加載的時候即:JNI_OnLoad方法中,讀取本進程的status文件,查看TracerPid字段是否爲0,如果不爲0,那麼就表示自己的進程被別人跟蹤了,也就是attach了,那麼這時候立馬退出程序,下面我們使用IDA在attach進程成功之後,查看本進程的status信息:

看到這裏的TracerPid爲11340,不爲0,表示被11340進程attach了,那麼我們可以查看一下這個進程是誰:

其實這個進程就是我們在設備中安插的android_server,它用於和IDA進行通信。

好了到這裏,我們可以看到愛加密做了反調試檢測,但是按照之前的那篇文章中,我們可以給JNI_OnLoad函數下斷點,然後找到檢測代碼,把對應的arm指令改成空指令,檢測失效了,但是這裏我們知道愛加密的兩個so文件被處理了,IDA沒法分析了,那麼這裏我們該怎麼辦呢?如何應對反調試呢?其實我們可以藉助IDA可以修改寄存器和內存數據的特性來做到?

首先我們上面分析了反調試的原理,一般在native代碼去做檢測的話,都是用fopen系統函數打開status文件,然後用fgets函數讀取一行的內容,這個是國際慣例的,操作文件都是用的fopen函數的

好了,那麼這裏思路就有了:既然反調試肯定用到了fopen和fgets這兩個函數,那麼我們直接像給dvmDexFileOpenPartial下斷點的方式一樣,給這兩個函數下斷點,然後運行到fgets斷點處的時候,發現如果是讀取TracerPid這行內容的時候,就開始修改內存內容,把TracerPid字段的值改成0,或者修改R0寄存器的內容,跳過反調試檢測

這兩個函數是在libc.so文件中的,我們可以把設備的/system/lib/libc.so使用adb pull到本地即可,然後用IDA得到他的相對地址,在調試頁面得到基地址,然後相加得到絕對地址,跳轉即可,但是這裏不用這種複雜的方式,有兩種方式可以進行跳轉:

第一種方式:在Modules界面,找到libc.so,然後在找到這兩個函數,就可以得到他們的絕對地址了

然後使用G鍵,跳轉下斷點即可:

第二種方式:也是最簡單的方式,就是G鍵,本身就有可以直接輸入函數名進行跳轉的功能

下斷點:

看到了吧,這種方式是不是非常簡單高效

好了到這裏就給這兩個函數下好了斷點,當然這裏還需要給dvmDexFileOpenPartial函數下斷點,一切弄好了之後,這時候我們再次運行:

停在了fopen斷點處,我們使用F8單步調試,看到R7寄存器中的內容是/proc/...,我們直接點擊R7查看全部內容:

內容有點長,大致的內容是:/proc/self/cmdline.debug.atrace.app_cmdlines,這個是幹什麼的?

我們看看這個目錄內容:

發現沒有這個文件內容,只有cmdline文件,但是這裏先不管他了,我們知道這個肯定不是讀取status文件的,那我們直接略過這個斷點,點擊F9運行到下一個斷點,中間過程先忽略,一路F9,直到運行到了fopen這個斷點:

果然,這裏使用了fopen來讀取status文件了,點擊R7寄存器查看全部內容:

這個16396就是我們本進程的id:

到這裏,我們知道下一個斷點肯定是fgets,所以點擊F9進入到fgets斷點處:

這裏還看不到什麼信息,我們繼續點擊F8單步調試:

途中,會看到有memchr和memcpy這兩個重要函數,這個也是操作字符串的核心點,繼續往下走:

到了fgets函數結束的地方,我們看到了R0寄存器的內容是Name...點擊R0查看全部內容:

全部內容是:Name:   der.crackme0201;這個就是status文件的第一行內容:

到這裏,我們知道了,開始讀取status文件的每行內容了,但是到TracerPid那行還要繼續執行5次fgets函數,所以還會進入5次斷點,爲了節省時間,這裏點擊5次F9,直接運行到讀取TracerPid那行的內容的fgets斷點處:

看到了關鍵的內容了TracerPid字段了,這時候,我們打開Hex View 查看16進制的內存數據:

但是我們看到,這個並沒有和調試頁面View位置相對應,我們可以這麼操作:

在寄存器窗口查看到R0寄存器的內容:

這裏就是TracerPid字段在內存的地址,記錄一下,然後在Hex View頁面中使用G鍵,進行跳轉,這裏一定要注意是在HexView,而不是調試頁面,調試頁面使用G鍵跳轉到的是指令地址了。

好了,這裏我們看到了TracerPid的內存內容了,這裏我們就開始修改吧,選擇我們要修改的內容:是11340那裏:

選擇內容開始處,右擊,選擇Edit,進入修改狀態:

改了之後的內容是橘黃色的,修改完成之後,在點擊右鍵,選擇Apply changes:

完成修改,顏色變成灰土色了:

注意:

這裏其實還可以直接修改寄存器R0的值:

這時候就表示修改成了,我們繼續使用F8單步調試下去:

這裏就開始把TracerPid字段的值和0作比較了,我們點擊R0寄存器查看全部內容:

這裏的值已經被改成了0,所以這裏就騙過去了。繼續運行,我們會發現,又進入了fopen函數的斷點處,而且查看還是讀取status文件,這個也不好奇,因爲是反調試檢測肯定是一個輪訓機制的,所以肯定會反覆的讀取的這個文件,fopen走多次也是正常的,但是這個反調試肯定是在子線程的,所以只要到了主線程中解密dex文件就肯定到了dvmDexFileOpenPartial,所以這裏會多次重複上面的操作,修改多次TracerPid的值,這裏就不在演示了,我在操作的過程中修改了三次,當沒有在走fopen函數的時候,遇到了這個錯誤,這裏不關心,直接點擊ok就可以了。

再次點擊運行:

這裏說明已經改開始解密dex文件了,應該離成功不遠了,繼續運行:

終於到了我們想要的地方了,到這裏就好辦了,直接點擊Shirt+F2,打開腳本運行窗口,運行下面腳本:

static main(void)
{
auto fp, dex_addr, end_addr;
fp = fopen(“F:\\dump.dex”, “wb”);
end_addr = r0 + r1;
for ( dex_addr = r0; dex_addr < end_addr; dex_addr ++ )
fputc(Byte(dex_addr), fp);
}

把內存中的dex保存到F:\dump.dex中,這裏不再解釋了,之前的一篇文章已經介紹過了,這裏R0寄存器就是dex在內存中的起始地址,R1寄存器就是dex文件的大小:

我們使用G鍵,可以在HexView頁面中查看R0寄存器中的地址內容:

看到了吧,這裏就是dex的頭文件格式。

 

四、還原應用apk

我們得到了內存中的dex數據之後,可以使用baksmali工具轉化成smali源碼,查看代碼邏輯即可,這裏不再演示了。

然後最後還有一步:還原apk

首先我們修改反編譯之後的AndroidManifest.xml中:

把這段內容刪除,如果有自己的Application的話,就改成自己的Application即可,同時刪除assets目錄下面的文件。

然後使用apktool進行回編譯,這時候,先不要着急簽名apk,而是替換classes.dex:

我們把上面得到的dump.dex改成classes.dex然後直接用壓縮軟件,替換未簽名的apk中的dex文件即可

最後在進行簽名操作,完成還原apk工作。

 

五、總結愛加密的破解流程

好了到這裏,我們算是脫殼成功了,下面來總結一下吧:

目標:在脫殼的過程中,我們就一個目標:dump處內存中的dex文件

但是在上面分析了愛加密的加固流程之後,發現他做了這些事:

1、把源程序apk加密處理放到了assets目錄下的ijiami.dat,也同時在assets\ijm_lib目錄下添加兩個so文件:libexec.so和libexecmain.so,這裏兩個so文件用來處理整個apk解密,動態加載等邏輯,但是我們用IDA查看得知,這兩個so文件被加密處理了

2、添加自己的殼Application:SuperApplication類,這個類中的attachContext方法中,首先把assets目錄中的兩個so文件copy到應用的files目錄下,然後在使用System.load方法,加載這個files目錄中的兩個so文件

3、我們在給dvmDexFileOpenPartial下斷點,進行調試的時候,發現有反調試檢測,因爲無法給JNI_OnLoad下斷點來去除反調試功能的arm指令,所以只能去修改內存數據,把TracerPid的值變成0,騙過檢測了。這裏我們的思路就是給fopen和fgets這兩個函數下斷點,因爲我們知道反調試的原理就是讀取本進程的status文件,然後獲取TracerPid那行內容即可,所以這裏肯定用到了fopen和fgets函數,在使用fgets這個函數的時候,會讀取每行內容,那麼我們只要發現在讀取到TracerPid那行內容的時候,去修改內存值,把TracerPid字段的值改成0即可。

4、有了上面的反調試思路之後,我們就開始進行操作了,但是在操作的過程中發現多次執行了fopen和fgets函數,而且我們需要修改多次TracerPid的值,原因很簡單,因爲是反調試檢測,肯定是在子線程中輪訓去檢測這個值,所以會執行多次很正常,所以我們要修改多次TracerPid的值,騙過檢測,直到當在主線程中,代碼運行到了解密dex文件的時候,即到了dvmDexFileOpenPartial函數處的斷點處爲止

5、最後修改多次TracerPid值,騙過檢測,到了dvmDexFileOpenPartial這裏,這時候,在執行dump腳本,把內存中的dex數據dump到本地即可。

通過上面的調試和破解流程其實不難發現,愛加密的流程是這樣的:

1》fopen—/proc/self/cmdline.debug.atrace.app_cmdlines
2》fgets—-com.droider.crackme0201
3》dvmLoadNativeCode–加載libexec.so
4》dvmLoadNativeCode–加載libexecmain.so
5》建立反調試線程(通過檢查是否存在調試進程)
6》調用fopen—-打開/proc/pid/status
7》調用fgets—讀取調試進程pid

這裏除了dvmDexFileOpenPartial函數,還有一個重要的函數dvmLoadNativeCode,它是加載和初始化so的函數,如果感興趣的同學,可以去給這個函數下斷點看看運行邏輯。

所以我們只要記住我們的目的只有一個:達到dvmDexFileOpenPartial函數處,dump處內存中的dex文件,就算是完成脫殼工作

 

本文的目的只有一個就是學習更多的逆向技巧和思路,如果有人利用本文技術去進行非法商業獲取利益帶來的法律責任都是操作者自己承擔,和本文以及作者沒關係,本文涉及到的代碼項目可以去編碼美麗小密圈自取,歡迎加入小密圈一起學習探討技術

 

六、總結

到這裏我們就分析完了如何去脫掉愛加密的殼,其實在之前說過,現在各個加固平臺的原理都差不多,最後看到的就是各家的加固算法了,所以我們在脫殼的過程中目標也很明確,就是dump出內存中的dex文件即可。不管他上層再怎麼牛逼的加密拆分操作,到了內存肯定是完整的dex文件了,所以現在加固平臺也是一個思想就是不讓你dump出來,就是讓你給dvmDexFileOpenPartial函數下斷點失敗,調試失敗。

 

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

點擊立即購買:京東  天貓  

更多內容:點擊這裏

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

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