現在主流的加固平臺有:梆梆加固,愛加密,360加固,騰訊加固,在之前的1篇文章中介紹了:如何脫掉“愛加密”的殼,現在這里要脫掉另外1個平臺的殼:360加固,由于有了之前的脫殼經驗,很多基礎知識和準備工作這里就不詳細介紹了,為了能夠脫掉他家的殼,用1個案例來去360平臺進行加固,然落后行脫殼。下面就來開始脫殼:
首先拿到加固以后的apk,這里為了方便查看內部信息,先不用dex2jar+jd-gui工具進行分析了,直接使用我們之前分析了源碼的1個工具:Jadx,直接查看:
其實現在的加固的常規套路都差不多,這里看到和之前分析的愛加密加固的情勢幾近1樣,這里的殼Application是StubApplication在attachBaseContext中做1些初始化操作,1般是將assets目錄中的so文件拷貝到程序的沙盒目錄下:/data/data/xxx/files/..;然后再用System.load進行加載,通過查看可以得知源程序apk已被加密了,就是寄存在這里的so中,之前的文章也是分析了,1般源程序加密以后就寄存在那幾個目錄下,1般是:dex文件尾部,libs目錄,assets目錄。
下面再來看1下他的AndroidManifest.xml文件:
找到了他的入口Activity了,但是這里沒有android:debuggable="true",所以程序是不能被調試的,所以我們需要添加這個屬性,然后在進行回編譯進行調試,這時候候就需要使用到apktool工具了:
好了,這里看到,360加固為了避免apktool反編譯功能,添加了1個qihoo屬性,這個屬性apktool不認識就報錯了,但是我們之前的1篇文章已介紹了:Apktool工具毛病修復,我們有了apktool源碼,可以直接進行修復的,然落后行反編譯:
反編譯成功了,查看他的AndroidManifest.xml文件內容:
的確,是有1個屬性qihoo,這個就是Android系統在解析apk文件的時候,發現不存在的屬性直接略過,但是apktool工具卻不會,360加固就是利用這個漏洞來增加反編譯難度的,但是我們之前的1篇文章中介紹了如何修復,這里修復很簡單了。所以說只要有了apktool源碼,甚么都好做了。
然后我們在添加android:debuggable屬性:
然后回編譯:
這時候候看到,在回編譯的時候也是報錯了,說找不到這個屬性,為了方便這里直接把android:qihoo給干掉,由于其實他沒有任何作用的,就是為了干擾反編譯工作的,所以直接去掉便可,然后在回編譯:
好了,回編譯成功,然后在進行簽名打包便可。這里就不在介紹了。
那末從上面我們可以看到,其實360加固為了避免反編譯,就利用了Android系統本身在解析apk的時候,遇到不認識的屬性直接略過,而apktool工具卻不會的漏洞來給AndroidManifest.xml中添加1個混淆反編譯的屬性:qihoo,幸虧我們有源碼,可以修復這個問題,在進行反編譯便可,這里也希望apktool官網能夠及時修復這個漏洞。為了回編譯成功,我們可以直接把這個屬性刪除。不然回編譯也是會報錯的。這個屬性只是360為了混淆反編譯工作,所以刪除對程序邏輯沒有任何影響的。
這里就要開始介紹本文的第1個重點了:如何在不需要反編譯的情況下,添加android:debuggable屬性,就能夠進行調試。
這個現在已有很多工具可以做了,先來講說具體的原理吧:
其實Android中有1些經常使用的配置信息都是寄存在1個文件中,比如裝備的系統,版本號,cpu型號等信息,而這個文件位置在:
/system/build.prop
我們查看文件的內容,可以看到很多裝備的信息,而且這些ro開頭的表示這些屬性值是只讀的,不能進行修改的。
同時Android中提供了兩個命令來操作這些信息:getprop和setprop命令:
查看系統的sdk版本號
設置系統的sdk版本號為22,可是這里并沒有修改成功,緣由就是由于ro開頭的屬性是不允許后期修改的,改也是可以修改的,需要重新編譯系統鏡像文件boot.img,但是這里其實不是本人介紹的重點了。
既然Android中的1些系統屬性值寄存在1個文件中的,而且這些值是只讀的,固然不但可以通過getprop命令讀取,有1個api也是可以直接讀取的,就是:System.getProperty("ro.build.version.sdk");其實這個方法是native層實現的,具體就不分析了。
那末這個文件是存儲這些屬性值的,那末是誰來進行解析加載到內存中,能夠給每一個app都能訪問到呢?
這個工作就是init.rc進程操作的,我們應當了解了系統啟動的時候第1步就是解析init.rc文件,這個文件是在系統的根目錄下,這里會做很多初始化操作,這里不詳細分析了,后面再分析Android中系統啟動流程的時候在詳細分析。這里同時會做屬性文件的解析工作,所以,Android 屬性系統通過系統服務提供系統配置和狀態的管理。為了讓運行中的所有進程同享系統運行時所需要的各種設置值,系統會開辟1個屬性存儲區域,并提供訪問該內存區域的 API。所有進程都可以訪問屬性值,但是只有 init 進程可以修改屬性值,其他進程若想修改屬性值,需要向 init 進程發出要求,終究由 init 進程負責修改屬性值。
那末上面說到的是system/build.prop文件。里面主要是系統的配置信息,其實還有1個重要文件在根目錄下面:default.prop:
這里有1個重要屬性:ro.debuggable,對這里就是關系到系統中每一個利用是不是能夠被調試的關鍵。其實在Android系統中1個利用能否被調試是這么判斷的:
當Dalvik虛擬機從android利用框架中啟動時,系統屬性ro.debuggable為1,如果該值被置1,系統中所有的程序都是可以調試的。如果系統中的 ro.debuggable 為0,則會判斷程序的AndroidManifest.xml中application標簽中的 android:debuggable元素是不是為true,如果為true則開啟調試支持。
好了到這里,我們可以總結1下了:
Android系統中有1個可以調試所有裝備中的利用的開關,在根目錄中的default.prop文件中的ro.debuggable屬性值,如果把這個值設置成1的話,那末裝備中所有利用都可以被調試,即便在AndroidManifest.xml中沒有android:debuggable=true,還是可以調試的。而這些系統屬性的文件system/build.prop和default.prop,都是init進程來進行解析的,系統啟動的時候就會去解析init.rc文件,這個文件中有配置關于系統屬性的解析工作信息。然后會把這些系統屬性信息解析到內存中,提供給所有app進行訪問,這塊信息也是內存同享的。但是這些ro開頭的屬性信息只能init進程進行修改。下面來分析1下修改這個屬性值的3種方式:
第1種:直接修改default.prop文件中的值,然后重啟裝備
那末現在如果依照上面的目的:就是不需要反編譯apk,添加android:debuggable屬性的話,直接修改default.prop文件,把ro.debuggable屬性改成1便可,但是通過上面的分析,修改完成以后肯定需要重啟裝備的,由于需要讓init進程重新解析屬性文件,把屬性信息加載內存中方可起作用的。但是并沒有那末順利,在實踐的進程中,修改了這個屬性,結果出現的結果就是裝備死機了,其實想一想也是正常的,如果屬性能夠通過這些文件來修改的話,那就感覺系統會出現各種問題了,感覺系統是不會讓修改這些文件的內容的。
第2種:改寫系統文件,重新編譯系統鏡像文件,然后刷入到裝備中
那末上面修改default.prop文件,結果致使死機,終究也是沒有修改成功,我們還有甚么辦法呢?其實上面已提到過1次了,就是這些屬性文件實際上是在系統鏡像文件boot.img在系統啟動的時候,釋放到具體目錄中的,也就是說如果我們能夠直接修改boot.img中的這個屬性便可,那末這個操作是可以進行的,但是困難那是不1般的順利,最少我沒成功過,修改系統文件,然后重新編譯鏡像文件,最后在刷到裝備中。這個進程我嘗試過是失敗了,不過理論上是可以的。而且這類方式如果成功了,那末這個裝備就是永久可以進行各種利用的調試了。
第3種:注入init進程,修改內存中的屬性值
那末上面直接重新編譯boot.img,然后在刷到裝備中的工作是失敗的,那末還有其他方法嗎?肯定是有的,我們其實在上面分析了,init進程會解析這個屬性文件,然后把這些屬性信息解析到內存中,給所有app進行訪問使用,所以在init進程的內存塊中是存在這些屬性值的,那末這時候候就好辦了,有1個技術可以做到了,就是進程注入技術,我們可使用ptrace注入到init進程,然后修改內存中的這些屬性值,只要init進程不重啟的話,那末這些屬性值就會起效。好了,這個方法可以嘗試,但是這個方法有1個弊端,就是如果init進程掛了重啟的話,那末設置就沒有任何效果了,必須重新操作了,所以有效期不是很長,但是1般情況下只要保證裝備不重啟的話,init進程會1直存在的,而且如果產生了init進程掛掉的情況,那末裝備肯定會重啟的。到時候在重新操作1下便可。
好了上面分析了3種方式去設置系統中的調試屬性總開關,那末最后1種方式是最靠譜的。
而且思路也很簡單,但是我們不會重新去寫這個代碼邏輯的,由于已有大神做了這件事,具體工具后面會給出下載地址:
這個工具用法很簡單,首先把可履行文件mprop拷貝到裝備中的目錄下,然后運行命令:
./mprop ro.debuggable 1
這個工具可以修改內存中所有的屬性值,包括機型信息。
這里修改完成以后,使用getprop命令在查看值,發現修改成功了,但是需要注意的是,我們修改的是內存的值,而不是文件中的值。所以default.prop文件中的內容是沒有產生變化的。
這時候候,我們可使用Eclipse的DDMS來查看可以調試的利用列表:
固然也能夠使用adb jdwp命令來查看可以調試的進程id:
但是惋惜的是,發現還是沒有展現裝備中所有的利用,其實這里是有1個細節問題了,由于我們雖然修改了內存值,但是有1個進程我們需要重啟1下,哪一個進程呢?那就是adbd這個進程,這個進程是adb的守護進程,就是裝備連接信息傳輸后臺進程,所以想看到可以調試的進程信息的話,那末需要重啟這個進程,這樣連接信息才會更新。
重啟這個進程很簡單:直接使用stop;start命令便可
其實這是兩個命令,用分號隔開,首先是干掉進程,然后在重啟。
運行完命令以后,再去看DDMS窗口信息:
這時候候所有的利用進程都是可以調試的了,這時候候我們在使用dumpsys package命令查看1個利用的包信息:
這里可以看到,這個利用的flags標志中并沒有debuggable屬性值,但是這個利用是可以調試的。所以看到ro.debuggable這個是總開關,只要他為1,開啟的話,即便沒有android:debuggable也是可以的了。
好了到這里,我們來總結1下:
1、我們的目的是怎樣在不需要反編譯apk包,添加android:debuggable屬性,就能夠進行apk的調試?
2、我們通過分析系統屬性文件和系統啟動流程和解析系統屬性文件的流程,知道了裝備中關于調試有1個總開關屬性值:ro.debuggable,默許是0,不開啟的。那末這時候候我們就能夠料想有這幾種方式可以去修改。
3、分析了3種方式去修改這個屬性值:
第1種方式:直接修改default.prop文件中的這個字段值,但是惋惜的是修改失敗,在修改的進程中出現死機,重啟裝備以后,屬性值還是0。
第2種方式:修改系統源碼的編譯腳本,直接修改屬性值,然后重新編譯鏡像文件boot.img,然后刷入到裝備中,但是在實踐的進程中并沒有成功,所以放棄了,而且這類方式有1個好處就是1旦修改了,只要不在重新刷系統,那末這個字段將永久有效。
第3種方式:注入到init進程,修改內存中的這些系統屬性值,這類方式實現是最簡單的,但是有1個問題,就是1旦裝備重啟,init進程重新解析default.prop文件的話,那末ro.debuggable值將又重新被清空,需要再次注入修改。
4、最后采取了第3種方式,不過網上已有人寫了這樣的工具,用法也很簡單:./mprop ro.debuggable 1;但是修改完成以后,1定要記得重新啟動adbd進程,這樣才能夠獲得到可以調試利用信息。
5、使用工具修改完成以后,在Eclipse中的DDMS窗口發現,裝備中的所有利用都處于可以調試狀態了。也就是說我們的操作成功了。
那末上面的這個進程成功以后的意義還是很大的:標志著我們以后如果是單純的想讓1個apk能夠被調試,去反編譯在添加屬性值的話,其實這類方式很高效的。可讓任意1個apk出于被調試狀態。
講完了上面的1個重點以后,下面我們就開始來說解本文的另外1個重點,開始脫殼了。
第1步:開啟android_server
第2步:端口轉發
第3步:啟動利用
adb shell am start -D -n com.CMapp/com.e4a.runtime.android.mainActivity
第4步:開啟IDA,附加進程
第5步:設置Debugger Option選項
第6步:運行jdb調試等待
jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=10265
注意:這里需要注意了,由于我們改了系統的ro.debuggable屬性,裝備中所有的利用都處于可調式狀態,基本端口8700已被占用了,那末這時候候需要使用被調試程序的獨有端口了,可以在DDMS窗口進行查看。
第7步:關鍵函數下斷點
首先找到mmap函數的內存地址,這里可以直接使用G鍵,通過函數名來跳轉:
注意:這里和之前的脫愛加密的殼方法可能不1樣了,還記得之前脫愛加密的殼的時候,給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函數的結束處下1個斷點,然后直接F9運行到函數的結尾處,由于系統中有很多個so需要加載到內存中,所以mmap函數會履行屢次,但是其實我們最關心的是加載我們自己的so文件,即libjiagu.so文件,由于這個才是我們的native層代碼,所以等出現以下界面:
這時候候,說明這個so文件被加載到內存中了,也就是程序的native層代碼開始履行了,注意不能在F9了,而是使用F8單步調試:
F8單步運行到這里的時候,遇到1個問題,就是F8了很屢次,始終在這個地方履行,后來分析了arm指令以后,發現原來這里是1個循環,初始值是0,存儲在R11中,然后逐漸加1,和R3中存儲的閾值作比較,通過查看寄存器的值,發現R3寄存器中是A7,所以這里得去修改寄存器R11的值了,不然我們得單步A7次,這里直接把R11值修改成A6:
修改寄存器也是很容易的,直接右擊寄存器:
點擊Modify value:
點擊OK,以后再來看看R11的寄存器的值:
修改成功了,這時候候在單步F8,兩次以后就履行完了循環了,從這里也能夠看到,這個地方也算是為了避免被調試,加大調試本錢的1種方式。繼續往下走:
到這里,履行完BL以后就退出調試界面了,嘗試屢次都1樣,所以料想反調試肯定在這里,可以F7跟進去看看:
到BLX這里,每次之前完也是退出調試界面,所以這里還得F7單步進入看看:
這里看到了1行重要的arm指令:CMP比較指令,而且是和0比較,極可能這里就是比較TracerPid的值是不是為0,如果不為0就退出,可以查看R0寄存器的內容:
然后在查看被調試進程的TracerPid的值:
果然R0存儲的是TracerPid的值,為了驗證正確性,這里繼續:
果然,運行到了自殺的地方,1直單步運行:
退出程序了。
那末上面就知道了反調試的地方,就好辦了,直接修改寄存器R0的值為0便可:
然后繼續單步F8運行,后面還有1個CMP和0進行比較的地方,我們1樣進行置零操作,再次單步F8,當運行到此處的時候:
看到memcpy函數的時候,這時候候可以直接運行F9,又會履行到mmap那里,然后順次F9,還是運行到了上面的那個循環,這樣順次類推,在這個進程中我運行了7次循環,改了R0值改了9次,所以這個地方會履行屢次是正常的,但是這里在我屢次調試以后總了1個好的方法,就是看到屢次履行的線路都差不多:
mmap函數=》循環=》(MOV R0,R8)BL=》(MOV LR,R4)BLX=》CMP R0,#0=》mmap....
這個進程中,其實為了簡便我們可以
1》在mmap函數的開始處,結束處下1個斷點,這兩個斷點是為了后面加載內存的dex文件做準備
2》在循環處下1個斷點,這個斷點是為了修改循環值,節省時間
3》在BL處下個斷點,是為了進入BLX
4》在BLX處下個斷點,是為了進入比較TracerPid處
5》在CMP下斷點,是為了修改TracerPid的值
同時在這個進程中,需要使用F9,直接跳轉到下1個斷點,高效,只有在到達了CMP處的時候,要用F8單步調試,而且這個地方1定要謹慎,不能按錯了,不然又得從頭再來,我吃了很屢次虧,也重來了很屢次。只要當看到了memcpy函數的時候,再次F9到下1個斷點處。更需要注意的是:每次到達mmap斷點處的時候,1定要看當前棧信息的視圖窗口,看看是不是出現了classes.dex的字樣,由于終究都是使用mmap來把解密以后的dex加載到內存中的,所以這里1定要注意,是本次調試的核心。
固然這個只是個人的調試思路,每一個人都有自己的思路,只要能成功都可以。
就這樣來回弄了幾次以后,終究看到了曙光:
當再次來到了mmap函數處的時候,終究看到了classes.dex字樣了,說明這里開始解密dex然落后行加載到內存了,這時候候不能在F9跳轉了,而是F8單步運行,然后查看R0寄存器的值:
每次都是履行完__mmap2這個函數以后,R0就有值了,每次看到R0中有值的時候,可以到Hex View窗口中使用G鍵開始地址跳轉,查看是不是為dex內容:
如果發現不是,就還是單步F8,知道mmap函數結束,然后再次F9,到達mmap函數開始處,時刻看緊Hex View,棧窗口,R0寄存器這3個地方的值:
在屢次嘗試以后,終究成功了,這里看到了熟習的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編碼的。
案例下載:http://download.csdn.net/detail/jiangwei0910410003/9561416
好了到這里,我們就成功了脫掉了360加固的殼了,下面來總結1下他的殼的特點和調試需要注意的點:
1、首先360加固仍然是外部套1個Application殼:StubApplication,源程序加密寄存在libjiagu.so,放在了assets目錄下,在Application啟動的時候,釋放到利用的沙盒目錄files下面,然后在使用System.load方法進行加載,這個和愛加密的方式是1樣的
2、關于360加固的反調試,仍然使用的是讀取/proc/[pid]/status中的TracerPid字段值,判斷是不是為0,但是這里和愛加密不1樣的是,在讀取這個文件的時候不是用的fopen系統函數,而是mmap系統函數,所以在解決反調試的時候需要給這個函數下斷點。
3、360加固底層不是采取dvmDexFileOpenPartial這個系統函數來解析dex然后加載到內存中的,而是自己實現了1個函數,所以給這個函數下斷點,然后獲得參數值來dump內存中的dex數據是行不通的,但是有1個思路就是不管他用哪一個函數去解析dex加載到內存,終究都得使用mmap這個系統函數來操作,所以還得給這個函數下斷點,所以這里在調試的時候需要時刻注意的是當斷點到達了mmap函數處的時候,需要視察Stack View棧窗口中是不是出現了classes.dex字樣,如果出現了,說明開始解密dex文件,準備加載到內存中了,那末這時候候需要視察R0寄存器的值,然后在Hex View中跳轉到指定內存地址,可以視察到是不是為dex內存數據
4、在視察是不是為內存數據的時候,需要注意dex文件是有自己的文件格式的,那末頭信息就是個根據,所以我們可以查看開頭為:dex.35 這樣的內容來判斷此處為dex數據,由于dex頭部信息中也有dex的文件大小,那末這時候候就能夠使用腳本dump處內存中的dex數據了。
5、在調試的進程中,會發現很多斷點屢次履行,特別是有1個循環,需要我們修改寄存器的值來快速結束循環,而且在關鍵處下斷點,也是加快調試效力的。
1、本文開始的時候介紹了通過注入系統init進程,修改內存中的系統屬性值:ro.debuggable,讓裝備中所有的利用都可以被調試,這個功能將對后續逆向破解有重大意義,也會省去了反編譯的工作。所以這個方式還是很具有里程碑意義的。
2、在脫愛加密的殼的時候,學習到了給fopen和fgets這兩個系統函數下斷點來解決反調試,在這里我們又多了1個下斷點的好去處就是給mmap下斷點,當發現給fopen函數下斷點不好使的時候,在嘗試給mmap下個斷點吧。
3、在脫愛加密的殼的時候,給dvmDexFileOpenPartial函數下斷點,來獲得dex在內存的起始地址和大小,從而dump處內存中的dex數據,但是360加固并沒有走這個函數,由于在給這個函數下斷點的時候,他壓根沒走到,所以判定它內部使用了其他的函數去解析dex的,然后加載到內存中的,但是如果最后加載到內存中,那肯定要用到mmap函數,所以只要給mmap函數下斷點便可。
本篇文章就介紹了如何脫掉360平臺加固的apk利用的殼,在結合之前的1篇脫掉愛加密家的殼的知識,看到現在在脫殼的時候其實就兩點,1點是找到關鍵處解決反調試,1般都是fopen,fgets,mmap,open等系統函數下斷點,還有1點就是如何找到內存中dex的起始地址和dex的大小,這個1般現在就是dvmDexFileOpenPartial函數下斷點,還有就是給mmap函數下斷點。
更多內容:點擊這里
關注微信公眾號,最新Android技術實時推送
下一篇 撲克牌順子