<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            原文地址:http://drops.wooyun.org/tips/9471

            0x00 前言


            Android應用的加固和對抗不斷升級,單純的靜態加固效果已無法滿足需求,所以出現了隱藏方法加固,運行時動態恢復和反調試等方法來對抗,本文通過實例來分析有哪些對抗和反調試手段。

            0x01 對抗反編譯


            首先使用apktool進行反編譯,發現該應用使用的加固方式會讓apktool卡死,通過調試apktool源碼(如何調試apktool可參見前文《Android應用資源文件格式解析與保護對抗研究》),發現解析時拋出異常,如下圖:

            根據異常信息可知是readSmallUint出錯,調用者是getDebugInfo,查看源碼如下:

            可見其在計算該偏移處的uleb值時得到的結果小于0,從而拋出異常。 在前文《Android程序的反編譯對抗研究》中介紹了DEX的文件格式,其中提到與DebugInfo相關的字段為DexCode結構的debugInfoOff字段。猜測應該是在此處做了手腳,在010editor中打開dex文件,運行模板DEXTemplate.bt,找到debugInfoOff字段。果然,該值被設置為了0xFEEEEEEE。

            接下來修復就比較簡單了,由于debugInfoOff一般情況下是無關緊要的字段,所以只要關閉異常就行了。

            為了保險起見,在readSmallUint方法后面添加一個新方法readSmallUint_DebugInfo,復制readSmallUint的代碼,if語句內result賦值為0并注釋掉拋異常代碼。

            然后在getDebugInfo中調用readSmallUint_DebugInfo即可。

            重新編譯apktool,對apk進行反編譯,一切正常。

            然而以上只是開胃菜,雖然apktool可以正常反編譯了,但查看反編譯后的smali代碼,發現所有的虛方法都是native方法,而且類的初始化方法中開頭多了2行代碼,如下圖:

            其基本原理是在dex文件中隱藏虛方法,運行后在第一次加載類時通過在方法(如果沒有方法,則會自動添加該方法)中調用ProxyApplication的init方法來恢復被隱藏的虛方法,其中字符串"aHcuaGVsbG93b3JsZC5NYWluQWN0aXZpdHk="是當前類名的base64編碼。

            ProxyApplication類只有2個方法,clinit和init,clinit主要是判斷系統版本和架構,加載指定版本的so保護模塊(X86或ARM);而init方法也是native方法,調用時直接進入了so模塊。

            那么它是如何恢復被隱藏的方法的呢?這就要深入SO模塊內部一探究竟了。

            0x02 動態調試so模塊

            如何使用IDA調試android的SO模塊,網上有很多教程,這里簡單說明一下。

            1. 準備工作

            1.1準備好模擬器并安裝目標APP。

            1.2 將IDA\dbgsrv\目錄下的android_server復制到模擬器里,并賦予可執行權限。

            adb push d:\IDA\dbgsrv\android_server /data/data/sv
            adb shell chmod 755 /data/data/sv
            

            1.3 運行android_server,默認監聽23946端口。

            adb shell /data/data/sv
            

            1.4 端口轉發。

            adb forward tcp:23946 tcp:23946
            

            2 以調試模式啟動APP,模擬器將出現等待調試器的對話框。

            adb shell am start -D -n hw.helloworld/hw.helloworld.MainActivity
            

            3 啟動IDA,打開debugger->attach->remote Armlinux/andoid debugger,設置hostname為localhost,port為23946,點擊OK;然后選擇要調試的APP并點擊OK。

            這時,正常狀態下會斷下來:

            然后設置在模塊加載時中斷:

            點擊OK,按F9運行。

            然后打開DDMS并執行以下命令,模擬器就會自動斷下來:

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

            (如果出現如下無法附加到目標VM的錯誤,可嘗試端口8600)

            此時,可在IDA中正常下斷點調試,這里我們斷JNI_OnLoad和init函數。

            由于IDA調試器還不夠完善,單步調試的時候經常報錯,最好先做一個內存快照,然后分析關鍵點的函數調用,在關鍵點下斷而不是單步調試。

            0x03 反調試初探


            一般反調試在JNI_OnLoad中執行,也有的是在INIT_ARRAY段和INIT段中早于JNI_OnLoad執行。可通過readelf工具查看INIT_ARRAY段和INIT段的信息,定位到對應代碼進行分析。

            INIT_ARRAY如下:

            其中函數sub_80407A88的代碼如下,通過檢測時間差來檢測是否中間有被單步調試執行:

            sub_8040903C函數里就是脫殼了,首先讀取/proc/self/maps找到自身模塊基址,然后解析ELF文件格式,從程序頭部表中找到類型為PT_LOAD,p_offset!=0的程序頭部表項,并從該程序段末尾讀取自定義的數組,該數組保存了被加密的代碼的偏移和大小,然后逐項解密。

            函數check_com_android_reverse里檢測是否加載了com.android.reverse,檢測到則直接退出。

            JNI_OnLoad函數中有幾個關鍵的函數調用:

            call_system_property_get檢測手機上的一些硬件信息,判斷是否在調試器中。

            checkProcStatus函數檢測進程的狀態,打開/proc/$PID/status,讀取第6行得到TracerPid,發現被跟蹤調試則直接退出。

            通過命令行查詢進程信息,一共有3個同名進程,創建順序為33->415->430->431。其中415和431處于調試狀態:

            進程415被進程405(即IDA的android_server)調試:

            進程431被其父進程430調試:

            要過這種反調試可在調用點直接修改跳轉指令,讓代碼在檢測到被調試后繼續正常的執行路徑,或者干脆nop掉整個函數即可。 檢測調試之后,就是調用ptrace附加自身,防止其他進程再一次附加,起到反調試作用。

            修改跳轉指令BNE(0xD1)為B(0xE0),直接返回即可。

            當然,更加徹底的方法是修改android源碼中bionic中的libc中的ptrace系統調用。檢測到一個進程試圖附加自身時直接返回0即可。

            上面幾處反調試點在檢測到調試器后都直接調用exit()退出進程了,所以直接nop掉后按F9執行。然后就斷在了init函數入口,順利過掉反調試:

            init函數在每個類加載的時候被調用,用于恢復當前類的被隱藏方法.首次調用時解密dex文件末尾的附加數據,得到事先保存的所有類的方法屬性,然后根據傳入的類名查找該類的被隱藏方法,并恢復對應屬性字段。 執行完init函數,當前類的方法已經恢復了。然后轉到dex文件的內存地址

            dump出dex文件,保存為dump.dex。

            0x04 恢復隱藏方法


            對比一下原始dex文件,發現dex文件末尾的附加數據被解密出來了:

            仔細分析一下附加數據的數據結構可以發現,它是一個數組,保存了所有類的所有方法的method_idx、access_flags、code_off、debug_info_off屬性,解密后的這些屬性都是uint類型的,如下圖:

            其中黃色框里的就是MainActivity的各方法的屬性,知道這些就可以修復dex文件,恢復出被隱藏的方法了。下圖就是恢復后的MainActivity類:

            0x05 總結


            以上就是通過實例分析展示出來的對抗和反調試手段。so模塊中的反調試手段比較初級,可以非常簡單的手工patch內存指令過掉,而隱藏方法的這種手段對art模式不兼容,不推薦使用這種方法加固應用。總的來說還是過于簡單。預計未來通過虛擬機來加固應用將是一大發展方向。

            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线