360 加固分析

原文html

1、前言

如今主流的加固平臺有:梆梆加固,愛加密,360加固,騰訊加固,在以前的一篇文章中介紹了:如何脫掉「愛加密」的殼,如今這裏要脫掉另一個平臺的殼:360加固,由於有了以前的脫殼經驗,不少基礎知識和準備工做這裏就不詳細介紹了,爲了可以脫掉他家的殼,用一個案例來去360平臺進行加固,而後進行脫殼。下面就來開始脫殼:android

 

2、分析360加固的原理

首先拿到加固以後的apk,這裏爲了方便查看內部信息,先不用dex2jar+jd-gui工具進行分析了,直接使用咱們以前分析了源碼的一個工具:Jadx,直接查看:shell

\

其實如今的加固的常規套路都差很少,這裏看到和以前分析的愛加密加固的形式幾乎同樣,這裏的殼Application是StubApplication在attachBaseContext中作一些初始化操做,通常是將assets目錄中的so文件拷貝到程序的沙盒目錄下:/data/data/xxx/files/..;而後再用System.load進行加載,經過查看能夠得知源程序apk已經被加密了,就是存放在這裏的so中,以前的文章也是分析了,通常源程序加密以後就存放在那幾個目錄下,通常是:dex文件尾部,libs目錄,assets目錄。api

 

下面再來看一下他的AndroidManifest.xml文件:app

\

找到了他的入口Activity了,可是這裏沒有android:debuggable="true",因此程序是不能被調試的,因此咱們須要添加這個屬性,而後在進行回編譯進行調試,這時候就須要使用到apktool工具了:框架

\

好了,這裏看到,360加固爲了防止apktool反編譯功能,添加了一個qihoo屬性,這個屬性apktool不認識就報錯了,可是咱們以前的一篇文章已經介紹了:Apktool工具錯誤修復,咱們有了apktool源碼,能夠直接進行修復的,而後進行反編譯:函數

\

反編譯成功了,查看他的AndroidManifest.xml文件內容:工具

\

的確,是有一個屬性qihoo,這個就是Android系統在解析apk文件的時候,發現不存在的屬性直接略過,可是apktool工具卻不會,360加固就是利用這個漏洞來增長反編譯難度的,可是咱們以前的一篇文章中介紹瞭如何修復,這裏修復很簡單了。因此說只要有了apktool源碼,什麼都好作了。性能

而後咱們在添加android:debuggable屬性:學習

\

而後回編譯:

\

 

這時候看到,在回編譯的時候也是報錯了,說找不到這個屬性,爲了方便這裏直接把android:qihoo給幹掉,由於其實他沒有任何做用的,就是爲了干擾反編譯工做的,因此直接去掉便可,而後在回編譯:

\

好了,回編譯成功,而後在進行簽名打包便可。這裏就不在介紹了。

 

那麼從上面咱們能夠看到,其實360加固爲了防止反編譯,就利用了Android系統自己在解析apk的時候,遇到不認識的屬性直接略過,而apktool工具卻不會的漏洞來給AndroidManifest.xml中添加一個混淆反編譯的屬性:qihoo,幸虧咱們有源碼,能夠修復這個問題,在進行反編譯便可,這裏也但願apktool官網可以及時修復這個漏洞。爲了回編譯成功,咱們能夠直接把這個屬性刪除。否則回編譯也是會報錯的。這個屬性只是360爲了混淆反編譯工做,因此刪除對程序邏輯沒有任何影響的。

 

3、打開系統的調試總開關

這裏就要開始介紹本文的第一個重點了:如何在不須要反編譯的狀況下,添加android:debuggable屬性,就能夠進行調試。

這個如今已經有不少工具能夠作了,先來講說具體的原理吧:

其實Android中有一些經常使用的配置信息都是存放在一個文件中,好比設備的系統,版本號,cpu型號等信息,而這個文件位置在:

/system/build.prop

\

咱們查看文件的內容,能夠看到不少設備的信息,並且這些ro開頭的表示這些屬性值是隻讀的,不能進行修改的。

同時Android中提供了兩個命令來操做這些信息:getprop和setprop命令:

\

查看系統的sdk版本號

\

設置系統的sdk版本號爲22,但是這裏並無修改爲功,緣由就是由於ro開頭的屬性是不容許後期修改的,改也是能夠修改的,須要從新編譯系統鏡像文件boot.img,可是這裏並非本人介紹的重點了。

 

既然Android中的一些系統屬性值存放在一個文件中的,並且這些值是隻讀的,固然不只能夠經過getprop命令讀取,有一個api也是能夠直接讀取的,就是:System.getProperty("ro.build.version.sdk");其實這個方法是native層實現的,具體就不分析了。

那麼這個文件是存儲這些屬性值的,那麼是誰來進行解析加載到內存中,可以給每一個app都能訪問到呢?

 

這個工做就是init.rc進程操做的,咱們應該瞭解了系統啓動的時候第一步就是解析init.rc文件,這個文件是在系統的根目錄下,這裏會作不少初始化操做,這裏不詳細分析了,後面再分析Android中系統啓動流程的時候在詳細分析。這裏同時會作屬性文件的解析工做,因此,Android 屬性系統經過系統服務提供系統配置和狀態的管理。爲了讓運行中的全部進程共享系統運行時所須要的各類設置值,系統會開闢一個屬性存儲區域,並提供訪問該內存區域的 API。全部進程均可以訪問屬性值,可是隻有 init 進程能夠修改屬性值,其餘進程若想修改屬性值,須要向 init 進程發出請求,最終由 init 進程負責修改屬性值。

 

那麼上面說到的是system/build.prop文件。裏面主要是系統的配置信息,其實還有一個重要文件在根目錄下面:default.prop:

\

這裏有一個重要屬性:ro.debuggable,對這裏就是關係到系統中每一個應用是否可以被調試的關鍵。其實在Android系統中一個應用可否被調試是這麼判斷的:

當Dalvik虛擬機從android應用框架中啓動時,系統屬性ro.debuggable爲1,若是該值被置1,系統中全部的程序都是能夠調試的。若是系統中的 ro.debuggable 爲0,則會判斷程序的AndroidManifest.xml中application標籤中的 android:debuggable元素是否爲true,若是爲true則開啓調試支持。

 

好了到這裏,咱們能夠總結一下了:

Android系統中有一個能夠調試全部設備中的應用的開關,在根目錄中的default.prop文件中的ro.debuggable屬性值,若是把這個值設置成1的話,那麼設備中全部應用均可以被調試,即便在AndroidManifest.xml中沒有android:debuggable=true,仍是能夠調試的。而這些系統屬性的文件system/build.prop和default.prop,都是init進程來進行解析的,系統啓動的時候就會去解析init.rc文件,這個文件中有配置關於系統屬性的解析工做信息。而後會把這些系統屬性信息解析到內存中,提供給全部app進行訪問,這塊信息也是內存共享的。可是這些ro開頭的屬性信息只能init進程進行修改。下面來分析一下修改這個屬性值的三種方式:

 

第一種:直接修改default.prop文件中的值,而後重啓設備

那麼如今若是按照上面的目的:就是不須要反編譯apk,添加android:debuggable屬性的話,直接修改default.prop文件,把ro.debuggable屬性改爲1便可,可是經過上面的分析,修改完成以後確定須要重啓設備的,由於須要讓init進程從新解析屬性文件,把屬性信息加載內存中方可起做用的。可是並無那麼順利,在實踐的過程當中,修改了這個屬性,結果出現的結果就是設備死機了,其實想一想也是正常的,若是屬性可以經過這些文件來修改的話,那就感受系統會出現各類問題了,感受系統是不會讓修改這些文件的內容的。

 

第二種:改寫系統文件,從新編譯系統鏡像文件,而後刷入到設備中

那麼上面修改default.prop文件,結果致使死機,最終也是沒有修改爲功,咱們還有什麼辦法呢?其實上面已經提到過一次了,就是這些屬性文件實際上是在系統鏡像文件boot.img在系統啓動的時候,釋放到具體目錄中的,也就是說若是咱們可以直接修改boot.img中的這個屬性便可,那麼這個操做是能夠進行的,可是困難那是不通常的順利,至少我沒成功過,修改系統文件,而後從新編譯鏡像文件,最後在刷到設備中。這個過程我嘗試過是失敗了,不過理論上是能夠的。並且這種方式若是成功了,那麼這個設備就是永遠能夠進行各類應用的調試了。

 

第三種:注入init進程,修改內存中的屬性值

那麼上面直接從新編譯boot.img,而後在刷到設備中的工做是失敗的,那麼還有其餘方法嗎?確定是有的,咱們其實在上面分析了,init進程會解析這個屬性文件,而後把這些屬性信息解析到內存中,給全部app進行訪問使用,因此在init進程的內存塊中是存在這些屬性值的,那麼這時候就好辦了,有一個技術能夠作到了,就是進程注入技術,咱們可使用ptrace注入到init進程,而後修改內存中的這些屬性值,只要init進程不重啓的話,那麼這些屬性值就會起效。好了,這個方法能夠嘗試,可是這個方法有一個弊端,就是若是init進程掛了重啓的話,那麼設置就沒有任何效果了,必須從新操做了,因此有效期不是很長,可是通常狀況下只要保證設備不重啓的話,init進程會一直存在的,並且若是發生了init進程掛掉的狀況,那麼設備確定會重啓的。到時候在從新操做一下便可。

 

好了上面分析了三種方式去設置系統中的調試屬性總開關,那麼最後一種方式是最靠譜的。

並且思路也很簡單,可是咱們不會從新去寫這個代碼邏輯的,由於已經有大神作了這件事,具體工具後面會給出下載地址:

這個工具用法很簡單,首先把可執行文件mprop拷貝到設備中的目錄下,而後運行命令:

./mprop ro.debuggable 1

\

這個工具能夠修改內存中全部的屬性值,包括機型信息。

這裏修改完成以後,使用getprop命令在查看值,發現修改爲功了,可是須要注意的是,咱們修改的是內存的值,而不是文件中的值。因此default.prop文件中的內容是沒有發生變化的。

\

這時候,咱們可使用Eclipse的DDMS來查看能夠調試的應用列表:

\

固然也可使用adb jdwp命令來查看能夠調試的進程id:

\

可是惋惜的是,發現仍是沒有展現設備中全部的應用,其實這裏是有一個細節問題了,由於咱們雖然修改了內存值,可是有一個進程咱們須要重啓一下,哪一個進程呢?那就是adbd這個進程,這個進程是adb的守護進程,就是設備鏈接信息傳輸後臺進程,因此想看到能夠調試的進程信息的話,那麼須要重啓這個進程,這樣鏈接信息纔會更新。

重啓這個進程很簡單:直接使用stop;start命令便可

\

其實這是兩個命令,用分號隔開,首先是幹掉進程,而後在重啓。

運行完命令以後,再去看DDMS窗口信息:

\

這時候全部的應用進程都是能夠調試的了,這時候咱們在使用dumpsys package命令查看一個應用的包信息:

\

這裏能夠看到,這個應用的flags標誌中並無debuggable屬性值,可是這個應用是能夠調試的。因此看到ro.debuggable這個是總開關,只要他爲1,開啓的話,即便沒有android:debuggable也是能夠的了。

 

好了到這裏,咱們來總結一下:

一、咱們的目的是怎麼在不須要反編譯apk包,添加android:debuggable屬性,就能夠進行apk的調試?

二、咱們經過分析系統屬性文件和系統啓動流程以及解析系統屬性文件的流程,知道了設備中關於調試有一個總開關屬性值:ro.debuggable,默認是0,不開啓的。那麼這時候咱們就能夠猜測有這幾種方式能夠去修改。

三、分析了三種方式去修改這個屬性值:

第一種方式:直接修改default.prop文件中的這個字段值,可是惋惜的是修改失敗,在修改的過程當中出現死機,重啓設備以後,屬性值仍是0。

第二種方式:修改系統源碼的編譯腳本,直接修改屬性值,而後從新編譯鏡像文件boot.img,而後刷入到設備中,可是在實踐的過程當中並無成功,因此放棄了,並且這種方式有一個好處就是一旦修改了,只要不在從新刷系統,那麼這個字段將永遠有效。

第三種方式:注入到init進程,修改內存中的這些系統屬性值,這種方式實現是最簡單的,可是有一個問題,就是一旦設備重啓,init進程從新解析default.prop文件的話,那麼ro.debuggable值將又從新被清空,須要再次注入修改。

四、最後採用了第三種方式,不過網上已經有人寫了這樣的工具,用法也很簡單:./mprop ro.debuggable 1;可是修改完成以後,必定要記得從新啓動adbd進程,這樣纔可以獲取到能夠調試應用信息。

五、使用工具修改完成以後,在Eclipse中的DDMS窗口發現,設備中的全部應用都處於能夠調試狀態了。也就是說咱們的操做成功了。

 

那麼上面的這個過程成功以後的意義仍是很大的:標誌着咱們之後若是是單純的想讓一個apk可以被調試,去反編譯在添加屬性值的話,其實這種方式很高效的。可讓任意一個apk出於被調試狀態。

 

4、開始脫殼

講完了上面的一個重點以後,下面咱們就開始來說解本文的另一個重點,開始脫殼了。

第一步:開啓android_server

\

 

第二步:端口轉發

\

 

第三步:啓動應用

adb shell am start -D -n com.CMapp/com.e4a.runtime.android.mainActivity

\

 

第四步:開啓IDA,附加進程

\

 

第五步:設置Debugger Option選項

\

 

第六步:運行jdb調試等待

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

\

注意:這裏須要注意了,由於咱們改了系統的ro.debuggable屬性,設備中全部的應用都處於可調式狀態,基本端口8700已經被佔用了,那麼這時候須要使用被調試程序的獨有端口了,能夠在DDMS窗口進行查看。

 

第七步:關鍵函數下斷點

\

首先找到mmap函數的內存地址,這裏能夠直接使用G鍵,經過函數名來跳轉:

\

注意:這裏和以前的脫愛加密的殼方法可能不同了,還記得以前脫愛加密的殼的時候,給fopen和fgets函數下斷點,由於若是有反調試的話,確定是讀取/proc/pid/status文件中的TracerPid字段值的,而後修改TracerPid值爲0便可,可是這個方法對360加固的很差使了,由於360加固的反調試是經過mmap函數來讀取/proc/pid/status,因此這裏須要給mmap函數下斷點了,並且後面還會看到給dvmDexFileOpenPartial這個函數下斷點也很差使了,緣由是360加固本身在底層實現瞭解析dex的函數來替代了這個dvmDexFileOpenPartial函數。可是無論是他本身實現dex解析加載,最終都是須要把dex文件加載到內存中,仍是得用mmap函數來進行操做。因此在脫360加固的殼的時候mmap函數是重點。

 

好了給mmap函數下了斷點,下面就F9運行程序吧:

\

進入到了mmap的斷點處,這裏由於mmap函數代碼比較長,爲了節省時間,咱們能夠在mmap函數的結束處下一個斷點,而後直接F9運行到函數的結尾處,由於系統中有不少個so須要加載到內存中,因此mmap函數會執行屢次,可是其實咱們最關心的是加載咱們本身的so文件,即libjiagu.so文件,由於這個纔是咱們的native層代碼,因此等出現以下界面:

\

這時候,說明這個so文件被加載到內存中了,也就是程序的native層代碼開始執行了,注意不能在F9了,而是使用F8單步調試:

\

F8單步運行到這裏的時候,遇到一個問題,就是F8了不少次,始終在這個地方執行,後來分析了arm指令以後,發現原來這裏是一個循環,初始值是0,存儲在R11中,而後逐步加1,和R3中存儲的閾值做比較,經過查看寄存器的值,發現R3寄存器中是A7,因此這裏得去修改寄存器R11的值了,否則咱們得單步A7次,這裏直接把R11值修改成A6:

\

修改寄存器也是很容易的,直接右擊寄存器:

\

點擊Modify value:

\

點擊OK,以後再來看看R11的寄存器的值:

\

修改爲功了,這時候在單步F8,兩次以後就執行完了循環了,從這裏也能夠看到,這個地方也算是爲了防止被調試,加大調試成本的一種方式。繼續往下走:

\

到這裏,執行完BL以後就退出調試界面了,嘗試屢次都同樣,因此猜測反調試確定在這裏,能夠F7跟進去看看:

\

到BLX這裏,每次以前完也是退出調試界面,因此這裏還得F7單步進入看看:

\

這裏看到了一行重要的arm指令:CMP比較指令,並且是和0比較,極可能這裏就是比較TracerPid的值是否爲0,若是不爲0就退出,能夠查看R0寄存器的內容:

\

而後在查看被調試進程的TracerPid的值:

\

果真R0存儲的是TracerPid的值,爲了驗證正確性,這裏繼續:

\

果真,運行到了自殺的地方,一直單步運行:

\

退出程序了。

 

那麼上面就知道了反調試的地方,就好辦了,直接修改寄存器R0的值爲0便可:

\

而後繼續單步F8運行,後面還有一個CMP和0進行比較的地方,咱們同樣進行置零操做,再次單步F8,當運行到此處的時候:

\

看到memcpy函數的時候,這時候能夠直接運行F9,又會執行到mmap那裏,而後依次F9,仍是運行到了上面的那個循環,這樣依次類推,在這個過程當中我運行了7次循環,改了R0值改了9次,因此這個地方會執行屢次是正常的,可是這裏在我屢次調試以後總了一個好的方法,就是看到屢次執行的路線都差很少:

mmap函數=》循環=》(MOV R0,R8)BL=》(MOV LR,R4)BLX=》CMP R0,#0=》mmap....

這個過程當中,其實爲了簡便咱們能夠

1》在mmap函數的開始處,結束處下一個斷點,這兩個斷點是爲了後面加載內存的dex文件作準備

2》在循環處下一個斷點,這個斷點是爲了修改循環值,節省時間

3》在BL處下個斷點,是爲了進入BLX

4》在BLX處下個斷點,是爲了進入比較TracerPid處

5》在CMP下斷點,是爲了修改TracerPid的值

同時在這個過程當中,須要使用F9,直接跳轉到下一個斷點,高效,只有在到達了CMP處的時候,要用F8單步調試,並且這個地方必定要當心,不能按錯了,否則又得從頭再來,我吃了不少次虧,也重來了不少次。只要當看到了memcpy函數的時候,再次F9到下一個斷點處。更須要注意的是:每次到達mmap斷點處的時候,必定要看當前棧信息的視圖窗口,看看是否出現了classes.dex的字樣,由於最終都是使用mmap來把解密以後的dex加載到內存中的,因此這裏必定要注意,是本次調試的核心。

固然這個只是我的的調試思路,每一個人都有本身的思路,只要能成功均可以。

 

就這樣來回搞了幾回以後,終於看到了曙光:

\

當再次來到了mmap函數處的時候,終於看到了classes.dex字樣了,說明這裏開始解密dex而後進行加載到內存了,這時候不能在F9跳轉了,而是F8單步運行,而後查看R0寄存器的值:

\

每次都是執行完__mmap2這個函數以後,R0就有值了,每次看到R0中有值的時候,能夠到Hex View窗口中使用G鍵開始地址跳轉,查看是否爲dex內容:

\

若是發現不是,就仍是單步F8,知道mmap函數結束,而後再次F9,到達mmap函數開始處,時刻看緊Hex View,棧窗口,R0寄存器這三個地方的值:

\

在屢次嘗試以後,終於成功了,這裏看到了熟悉的dex文件的頭信息,關於dex文件的頭部信息能夠看這篇文章:Dex文件格式解析

\

因此這裏在頭部信息的第33個字節而後連續4個字節就是dex的長度了,那麼如今有了dex在內存中的其實位置,長度大小,下面就可使用Shirt+F2打開腳本執行窗口,dump出內存中的dex數據:

static main(void)
{
auto fp, begin, end, dexbyte;
fp = fopen("E:\\dump.dex", "wb");
begin = 0x755A9000;
//偏移0x20處,取4字節爲dex文件大小
end = 0x755A9000 + 0x0004BC38;
for ( dexbyte = begin; dexbyte < end; dexbyte ++ )
fputc(Byte(dexbyte), fp);
}

\

保存到E:\dump.dex,而後在使用Jadx工具進行查看:

\

這裏能夠查看到源碼了,並且類名,方法名,變量名都是用中文來命名的,感受好不習慣,可是Java中是支持這麼幹的,由於Java採用的是Unicode編碼的。

 

5、脫殼總結

好了到這裏,咱們就成功了脫掉了360加固的殼了,下面來總結一下他的殼的特色和調試須要注意的點:

一、首先360加固依然是外部套一個Application殼:StubApplication,源程序加密存放在libjiagu.so,放在了assets目錄下,在Application啓動的時候,釋放到應用的沙盒目錄files下面,而後在使用System.load方法進行加載,這個和愛加密的方式是同樣的

二、關於360加固的反調試,依然使用的是讀取/proc/[pid]/status中的TracerPid字段值,判斷是否爲0,可是這裏和愛加密不同的是,在讀取這個文件的時候不是用的fopen系統函數,而是mmap系統函數,因此在解決反調試的時候須要給這個函數下斷點。

三、360加固底層不是採用dvmDexFileOpenPartial這個系統函數來解析dex而後加載到內存中的,而是本身實現了一個函數,因此給這個函數下斷點,而後獲取參數值來dump內存中的dex數據是行不通的,可是有一個思路就是無論他用哪一個函數去解析dex加載到內存,最終都得使用mmap這個系統函數來操做,因此還得給這個函數下斷點,因此這裏在調試的時候須要時刻注意的是當斷點到達了mmap函數處的時候,須要觀察Stack View棧窗口中是否出現了classes.dex字樣,若是出現了,說明開始解密dex文件,準備加載到內存中了,那麼這時候須要觀察R0寄存器的值,而後在Hex View中跳轉到指定內存地址,能夠觀察到是否爲dex內存數據

四、在觀察是否爲內存數據的時候,須要注意dex文件是有本身的文件格式的,那麼頭信息就是個根據,因此咱們能夠查看開頭爲:dex.35 這樣的內容來判斷此處爲dex數據,由於dex頭部信息中也有dex的文件大小,那麼這時候就可使用腳本dump處內存中的dex數據了。

五、在調試的過程當中,會發現不少斷點屢次執行,特別是有一個循環,須要咱們修改寄存器的值來快速結束循環,並且在關鍵處下斷點,也是加快調試效率的。

 

6、技術概要

一、本文開始的時候介紹了經過注入系統init進程,修改內存中的系統屬性值:ro.debuggable,讓設備中全部的應用均可以被調試,這個功能將對後續逆向破解有重大意義,也會省去了反編譯的工做。因此這個方式仍是很具備里程碑意義的。

二、在脫愛加密的殼的時候,學習到了給fopen和fgets這兩個系統函數下斷點來解決反調試,在這裏咱們又多了一個下斷點的好去處就是給mmap下斷點,當發現給fopen函數下斷點很差使的時候,在嘗試給mmap下個斷點吧。

三、在脫愛加密的殼的時候,給dvmDexFileOpenPartial函數下斷點,來獲取dex在內存的起始地址和大小,從而dump處內存中的dex數據,可是360加固並無走這個函數,由於在給這個函數下斷點的時候,他壓根沒走到,因此判定它內部使用了其餘的函數去解析dex的,而後加載到內存中的,可是若是最後加載到內存中,那確定要用到mmap函數,因此只要給mmap函數下斷點便可。

 

7、總結

本篇文章就介紹瞭如何脫掉360平臺加固的apk應用的殼,在結合以前的一篇脫掉愛加密家的殼的知識,看到如今在脫殼的時候其實就兩點,一點是找到關鍵處解決反調試,通常都是fopen,fgets,mmap,open等系統函數下斷點,還有一點就是如何找到內存中dex的起始地址和dex的大小,這個通常如今就是dvmDexFileOpenPartial函數下斷點,還有就是給mmap函數下斷點。

相關文章
相關標籤/搜索