<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    利用Frida破解黑盒環境的Dex函數抽取殼

    VSole2022-08-07 16:55:43


    看雪論壇作者ID:飛翔的貓咪

    題目出自看雪高研班2021年10月份作業第二題:

    函數抽取殼導致了被保護的函數始終運行在解釋模式下。提供的pixel鏡像已經將默認解釋器修改成了Switch實現,請分析該Switch解釋器,并編寫frida hook腳本,能夠實現對解釋器解釋執行的每一條smali指令的跟蹤記錄。提供的測試apk采用了自解密的函數抽取技術。

    分析:

    Dex函數抽取殼目前算是比較常見的加殼方式,對此種殼目前來說比較流行的脫殼工具是寒冰大佬的fart工具。當然這種殼的脫殼方式不止一種,就像題目中描述的那樣,由于Dex函數最終還是需要通過art虛擬機來執行的,因此在虛擬機執行引擎內部做手腳也可以直接達成脫殼目標,題目已經給出了解法:直接分析Switch實現的解釋器,即可獲取函數執行指令流。就是如果分析的函數比較多,函數比較復雜,在每一條指令執行時做手腳會一定程度上影響App的性能。

    對于題目中的8.0系統,解釋器分為Switch實現和Mterp匯編實現的解釋器,Switch實現要好分析很多,因此手頭上如果有源碼的情況下可以強制解釋器用Switch來實現。

    做這道題目可以開擴脫殼的思路,原來就算沒有源碼的情況下,用ida分析so也可以找到脫殼點。了解殼的根本原理以及虛擬機執行的原理才是以不變應萬變的法則,剩下的就靠創造力去發揮即可。


    解答:

    1.先將題目提供的鏡像刷寫至Pixel手機中,然后將apk安裝上去,發現apk以32位模式運行,那么使用adb pull將/system/lib/libart.so文件拉出來放到ida中分析。

    2.鏡像已經帶有了整體脫殼功能,其/data/data下面的8299712_dexfile.dex就是MainActivity類所在的dex文件,可以看到里邊很多的函數都是抽取型函數,我這里選擇分析其中一個函數:com.xgtl.aggregate.activities.MainActivity.a(android.view.View)。

    3.在ida的exports中尋找解釋器的實現函數ExecuteSwitchImpl(可以結合有源碼的系統來對比,廠商一般不會改動原生虛擬機),找到取指階段的指令地址,通過查看aosp源碼得知該函數為模板函數,實際編譯器生成的函數是四個,它們的地址分別位于0x2089B6,0x021FA66,0x01FA2E0,0x0215028,接下來嘗試著用frida進行inline hook,總是跑著跑著就崩潰了,不是報SIGSEGV就是報TRAP,試了各種方法,還是無法解決崩潰的問題,只好換其他的方式來解決。

    4.通過在源碼中搜索,每一條解釋器指令執行前都會調用PREAMBLE(),因此如果可以hook住PREAMBLE也可以跟蹤每條指令的執行分支。通過觀察可以發現PREAMBLE中會調用shadow_frame.GetThisObject(),因此hook shadow_frame.GetThisObject就可以跟蹤指令的執行,但是必須有個前提:if (UNLIKELY(instrumentation->HasDexPcListeners())) {這個分支必須滿足才會執行下面的shadow_frame.GetThisObject()。

    5.通過跟蹤ida中指令發現執行ExecuteSwitchImpl的時候instrumentation對象的地址存放在r4寄存器中,而HasDexPcListeners()函數被內聯了,它是通過r4+480偏移來判斷if (UNLIKELY(instrumentation->HasDexPcListeners()),因此只要可以通過frida來修改r4寄存器+480的值,使得instrumentation->HasDexPcListeners()返回為true,這樣就可以使shadow_frame.GetThisObject()被調用,然后再hook這個函數即可。

    frida代碼如下:

    var prettyMethodPtr;var prettyMethod; function pretty_method(art_method) {    if (!prettyMethodPtr) {        prettyMethodPtr = Module.getExportByName("libart.so", "_ZN3art9ArtMethod12PrettyMethodEb");        prettyMethod = new NativeFunction(prettyMethodPtr, 'pointer', ['pointer', 'pointer', 'bool']);    }    var result = Memory.alloc(0x100);    prettyMethod(ptr(result), art_method, 1);    return result.add(0x8).readPointer().readCString();} var in_method = false;var target_method_name = "com.xgtl.aggregate.activities.MainActivity.a(android.view.View)"; function hookGetThisObject(base) {    var get_this_object_addr = Module.getExportByName("libart.so", "_ZNK3art11ShadowFrame13GetThisObjectEt");    Interceptor.attach(get_this_object_addr, {        onEnter: function (args) {            console.log("pid :" + Process.getCurrentThreadId() + ", lr:" + ptr(this.context.lr).sub(base).add(0xB000));            console.log(hexdump(ptr(this.context.r11), { length: 32 }));            console.log()         }, onLeave: function (retval) {         }    });} function hook() {    var libartmodule = Process.getModuleByName("libart.so");    var base = libartmodule.base;     libartmodule.enumerateSymbols().forEach(function (symbol) {        if (symbol.name.indexOf("ExecuteSwitchImpl") != -1 && symbol.name.indexOf("CodeItem") != -1) {            Interceptor.attach(symbol.address, {                onEnter: function (args) {                    var instance = ptr(0x43898C).add(base).sub(0xB000).add(0xca9f4).add(480);                    instance.writeU8(1);                    var shadow_frame = args[3];                    var art_method = ptr(shadow_frame).add(Process.pointerSize).readPointer();                    this.method_name = pretty_method(art_method);                    if (this.method_name.indexOf(target_method_name) != -1) {                        console.log("method start : >>>>>>>>>>>" + this.method_name);                        hookGetThisObject(base, this.method_name);                        in_method = true;                    }                    if (in_method) {                        console.log("method start : >>>>>>>>>>>" + this.method_name);                    }                }, onLeave: function (retval) {                    if (this.method_name.indexOf(target_method_name) != -1) {                        console.log("method end : >>>>>>>>>>>" + this.method_name);                        Interceptor.detachAll();                        in_method = false;                    }                }            });         }    });}function main() {    hook();}setImmediate(main);
    

    6.這里要跟蹤的函數為com.xgtl.aggregate.activities.MainActivity.a(android.view.View),需要app進入以后點擊界面下方的tab觸發。

    運行腳本得到的輸出如下:

    [AOSP on msm8996::com.xgtl.assistant]-> method start : >>>>>>>>>>>void com.xgtl.aggregate.activities.MainActivity.a(android.view.View)method start : >>>>>>>>>>>void com.xgtl.aggregate.activities.MainActivity.a(android.view.View)pid :16444, lr:0x206a57           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEFc4bce9f8  28 1b 00 00 00 00 00 00 00 00 00 00 00 00 00 00  (...............c4bcea08  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................ pid :16444, lr:0x203a79           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEFc4bcea2e  14 00 5f 18 00 00 71 10 4e df 00 00 28 e0 02 00  .._...q.N...(...c4bcea3e  02 00 02 00 00 00 00 00 00 00 09 00 00 00 12 01  ................ pid :16444, lr:0x203f87           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEFc4bcea34  71 10 4e df 00 00 28 e0 02 00 02 00 02 00 00 00  q.N...(.........c4bcea44  00 00 00 00 09 00 00 00 12 01 70 20 87 ca 10 00  ..........p .... pid :16444, lr:0x206a57           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEFc4bcea3a  28 e0 02 00 02 00 02 00 00 00 00 00 00 00 09 00  (...............c4bcea4a  00 00 12 01 70 20 87 ca 10 00 0c 01 71 20 50 ca  ....p ......q P.
    

    每條smali指令的執行都會打印出一條lr以及指令所在地址處的值。

    接下來分析打印:

    第一條lr為0x206a57,使用ida跳轉至此地址處,發現該地址處的switch-case對應的case為40,也就是0x28,與c4bce9f8 28 1b這條打印匹配,還原了指令以后就是goto 1b,因此第一條指令為goto 1b。接下來把所有的log按dex格式還原即可,遇到method id或者field id,則在8299712_dexfile.dex中查找對應的簽名:

    method start : >>>>>>>>>>>void com.xgtl.aggregate.activities.MainActivity.a(android.view.View)lr:0x206a57 = goto 1blr:0x203a79 = const v0, 0x0000185flr:0x203f87 =  invoke-static {v0},kind@df4e = invoke-static {v0},Ls/h/e/l/l/H;->i(I)Vlr:0x206a57 = goto e0 lr:0x2046e7 = iget-object v0,v2,field@8188 = Landroid/support/v4/media/MediaBrowserServiceCompat;->mSession:Landroid/support/v4/media/session/MediaSessionCompat$Token;lr:0x205ccf = const/4 vA, #+B = const/4 v1,#0lr:0x204c49 = invoke-virtual {v0, v1}, kind@61b5method start : >>>>>>>>>>>void android.support.v7.widget.AppCompatImageView.drawableStateChanged()lr:0x2064ad = invoke-super {v0}, kind@614clr:0x2046e7 = iget-object v0,v1,field@32aelr:0x20466d = if-eqz v0,0x0005lr:0x204c49 = invoke-virtual {v0},kind@4b5b = void android.support.v7.widget.AppCompatBackgroundHelper.applySupportBackgroundTint()method start : >>>>>>>>>>>void android.support.v7.widget.AppCompatBackgroundHelper.applySupportBackgroundTint()lr:0x2046e7 = iget-object v0,v3,field@3285lr:0x204c49 = invoke-virtual {v0},kind@5cd5lr:0x206c21 = move-result-object v0lr:0x20466d = if-eqz v0,0x0022lr:0x20c951 = return-voidlr:0x2046e7 = iget-object v0,v1,field@32aflr:0x20466d = if-eqz v0,0x0005lr:0x204c49 = invoke-virtual {v0},kind@4befmethod start : >>>>>>>>>>>void android.support.v7.widget.AppCompatImageHelper.applySupportImageTint()lr:0x2046e7 = iget-object v0,v3,field@32adlr:0x204c49 = invoke-virtual {v0},kind@6150lr:0x206c21 = move-result-object v0lr:0x20466d = if-eqz v0,0x0005lr:0x203f87 = invoke-static {v0}, kind@4e6emethod start : >>>>>>>>>>>void android.support.v7.widget.DrawableUtils.fixDrawable(android.graphics.drawable.Drawable)lr:0x203f09 = sget v0, field@0467lr:0x204023 = const/16 v1, 0x0015lr:0x204301  = if-ne v0,v1,0x0015lr:0x20c951 = return-voidlr:0x20466d = if-eqz v0,0x0022lr:0x206729 = invoke-direct {v3}, kind@4bf8method start : >>>>>>>>>>>boolean android.support.v7.widget.AppCompatImageHelper.shouldApplyFrameworkTintUsingColorFilter()lr:0x203f09 = sget v0, field@0467lr:0x205ccf = const/4 v1,#1lr:0x205ccf = const/4 v2,#0lr:0x204023 = const/16 v3, 0x0015lr:0x204b7b = if-le v0,v3,0x0009lr:0x2046e7 = iget-object v0,v4,field@32ablr:0x20466d = if-eqz v0,0x0003lr:0x205ccf = const/4 v1,#0lr:0x20c9f1 = return v1lr:0x2051d5 = move-result v1lr:0x20466d = if-eqz v1,0x0009lr:0x2046e7 = iget-object v1,v3,field@32aalr:0x20466d = if-eqz v1,0x000clr:0x2046e7 = iget-object v1,v3,field@32ablr:0x20466d = if-eqz v1,0x0003lr:0x20c951 = return-voidlr:0x20c951 = return-voidmethod start : >>>>>>>>>>>void android.support.v7.widget.AppCompatTextHelperV17.applyCompoundDrawablesTints()lr:0x2064ad = invoke-super {v3},kind@4cbbmethod start : >>>>>>>>>>>void android.support.v7.widget.AppCompatTextHelper.applyCompoundDrawablesTints()lr:0x2046e7 = iget-object v0,v3,field@32e5lr:0x206943 = if-nez v0,+0x000elr:0x2046e7 = iget-object v0,v3,field@32e7lr:0x206943 = if-nez v0,+0x000alr:0x2046e7 = iget-object v0,v3,field@32e6lr:0x206943 = if-nez v0,+0x0006lr:0x2046e7 = iget-object v0,v3,field@32e4lr:0x20466d = if-eqz v0,0x0028lr:0x20c951 = return-voidlr:0x2046e7 = iget-object v0,v3,field@32eclr:0x206943 = if-nez v0,+0x0006lr:0x2046e7 = iget-object v0,v3,field@32eblr:0x20466d = if-eqz v0,0x0018lr:0x20c951 = return-voidlr:0x2046e7 = iget-object v0,v2,field@818alr:0x204c49 = invoke-virtual {v0,v1},kind@61b5lr:0x2046e7 = iget-object v0,v2,field@818blr:0x204c49 = invoke-virtual {v0,v1},kind@61b5lr:0x2046e7 = iget-object v0,v2,field@8189lr:0x204c49 = invoke-virtual {v0,v1},kind@61b5lr:0x205ccf = const/4 v0,#1lr:0x204c49 = invoke-virtual {v3,v0},kind@5dabmethod start : >>>>>>>>>>>void android.support.v7.widget.AppCompatImageView.drawableStateChanged()lr:0x2064ad = invoke-super {v1},kind@614clr:0x2046e7 = iget-object v0,v1,field@32aelr:0x20466d = if-eqz v0,+0x0005lr:0x204c49 = invoke-virtual {v0},kind@4b5b = void android.support.v7.widget.AppCompatBackgroundHelper.applySupportBackgroundTint()lr:0x2046e7 = iget-object v0,v1,field@32aflr:0x20466d = if-eqz v0,+0x0005lr:0x204c49 = invoke-virtual {v0},kind@4befmethod start : >>>>>>>>>>>void android.support.v7.widget.AppCompatImageHelper.applySupportImageTint()lr:0x2046e7 = iget-object v0,v3,field@32adlr:0x204c49 = invoke-virtual {v0},kind@6150lr:0x206c21 = move-result-object v0lr:0x20466d = if-eqz v0,0x0005lr:0x203f87 = invoke-static {v0}, kind@4e6emethod start : >>>>>>>>>>>void android.support.v7.widget.DrawableUtils.fixDrawable(android.graphics.drawable.Drawable)lr:0x203f09 = sget v0, field@0467lr:0x204023 = const/16 v1, 0x0015lr:0x204301 = if-ne v0,v1,0x0015lr:0x20c951 = return-voidlr:0x20466d = if-eqz v0,0x0022lr:0x206729 = invoke-direct {v3}, kind@4bf8method start : >>>>>>>>>>>boolean android.support.v7.widget.AppCompatImageHelper.shouldApplyFrameworkTintUsingColorFilter()lr:0x203f09 = sget v0, field@0467lr:0x205ccf = const/4 v1,#1lr:0x205ccf = const/4 v2,#0lr:0x204023 = const/16 v3, 0x0015lr:0x204b7b = if-le v0,v3,0x0009lr:0x2046e7 = iget-object v0,v4,field@32ablr:0x20466d = if-eqz v0,0x0003lr:0x205ccf = const/4 v1,#0lr:0x20c9f1 = return v1lr:0x2051d5 = move-result v1lr:0x20466d = if-eqz v1,0x0009lr:0x2046e7 = iget-object v1,v3,field@32aalr:0x20466d = if-eqz v1,0x000clr:0x2046e7 = iget-object v1,v3,field@32ablr:0x20466d = if-eqz v1,0x0003lr:0x20c951 = return-voidlr:0x20c951 = return-voidmethod start : >>>>>>>>>>>void android.support.v7.widget.AppCompatTextHelperV17.applyCompoundDrawablesTints()lr:0x2064ad = invoke-super {v3},kind@4cbbmethod start : >>>>>>>>>>>void android.support.v7.widget.AppCompatTextHelper.applyCompoundDrawablesTints()lr:0x2046e7 = iget-object v0,v3,field@32e5lr:0x206943 = if-nez v0,+0x000elr:0x2046e7 = iget-object v0,v3,field@32e7lr:0x206943 = if-nez v0,+0x000alr:0x2046e7 = iget-object v0,v3,field@32e6lr:0x206943 = if-nez v0,+0x0006lr:0x2046e7 = iget-object v0,v3,field@32e4lr:0x20466d = if-eqz v0,0x0028lr:0x20c951 = return-voidlr:0x2046e7 = iget-object v0,v3,field@32eclr:0x206943 = if-nez v0,+0x0006lr:0x2046e7 = iget-object v0,v3,field@32eblr:0x20466d = if-eqz v0,0x0018lr:0x20c951 = return-voidlr:0x20c951 = return-voidmethod end : >>>>>>>>>>>void com.xgtl.aggregate.activities.MainActivity.a(android.view.View
    

    由于方法里邊調用了其他方法如void android.support.v7.widget.AppCompatBackgroundHelper.applySupportBackgroundTint(),我這里打印出了執行的每條smali指令。

    這里通過查看指令碼就可以大致看出程序的邏輯,如果還想更進一步,就需要分隔各個方法,寫到dex文件中去,用jadx這種工具來打開即可。

    由于附件超出了8M,我用split命令將原始文件分割了開了,合并起來:

    cat test.apk.split1 test.apk.split2 test.apk.split3 > test.apk

    匯編指令const
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    匯編語言是一種用于電子計算機、微處理器、微控制器或其他可編程器件的低級語言,亦稱為符號語言。Smali匯編基礎Smali語言最早是由JesusFreke發布在Google Code上的一個開源項目,并不是擁有官方標準的語言。因此也將Smali語言稱作Android虛擬機的反匯編語言。基本類型Smali基本數據類型中包含兩種類型,原始類型和引用類型。而在Smali中則是以LpackageName/objectName的形式表示對象類型。
    Dobby一共兩個功能,其一是inlinehook,其二是指令插樁,兩者原理差不多,主要介紹指令插樁。所謂指令插樁,就是在任意一條指令,進行插樁,執行到這條指令的時候,會去執行我們定義的回調函數
    得益于Unicorn的強大的指令trace能力,可以很容易實現對cpu執行的每一條匯編指令的跟蹤,進而對ollvm保護的函數進行剪枝,去掉虛假塊,大大提高逆向分析效率。
    能運行的環境包括I/O,權限控制,系統調用,進程管理,內存管理等多項功能都可以歸結到上邊兩點中。需要注意的是,kernel 的crash 通常會引起重啟。注意大多數的現代操作系統只使用了 Ring 0 和 Ring 3。
    看雪論壇作者ID:xxxlion
    前言最近一段時間在研究Android加殼和脫殼技術,其中涉及到了一些hook技術,于是將自己學習的一些hook技術進行了一下梳理,以便后面回顧和大家學習。主要是進行文本替換、宏展開、刪除注釋這類簡單工作。所以動態鏈接是將鏈接過程推遲到了運行時才進行。
    目前絕大部分app都會頻繁的使用syscall去獲取設備指紋和做一些反調試,使用常規方式過反調試已經非常困難了,使用內存搜索svc指令已經不能滿足需求了,開始學習了一下通過ptrace/ptrace配合seccomp來解決svc反調試難定位難繞過等問題。seccompLinux 2.6.12中的導入了第一個版本的seccomp,通過向/proc/PID/seccomp接口中寫入“1”來啟動通過濾器只支持幾個函數。BPF程序由一組BPF指令組成,可以在系統調用執行之前對其進行檢查,以決定是否允許執行該系統調用。
    1.CheatEngine工具的基本使用
    結果分析Hook前Hook后,我們的彈窗本該是hello的但是hook后,程序流程被我們修改了。760D34B2 55 push ebp760D34B3 8BEC mov ebp,esp通過這兩條指令,函數就可以在堆棧中為局部變量分配存儲空間,并在函數執行過程中保存和恢復現場。這樣做的好處是可以避免局部變量和其他函數之間的沖突,同時也可以提高函數的可讀性和可維護性。
    加密算法共4種,第二個任務注冊機,缺一個算法的解密算法,其他三個算法均已寫好C實現的解密算法。隨后在xxx函數通過frida分析找到XTEA加密,然后用frida在內存中找到并提取了密鑰。Dump && Recover IL2CPP雖然用修改后的frida去hook libsec2023.so仍然會被檢測,但是hook其他庫沒有出現問題。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类