成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

利用 Frida 和 QBDI 動(dòng)態(tài)分析 Android Native 的各項(xiàng)函數(shù)

開(kāi)發(fā)
這篇文章將介紹如何通過(guò)充分利用Frida和QBDI來(lái)更好地理解Android Native 的各項(xiàng)函數(shù)。

[[343697]]

由于可以檢索應(yīng)用程序代碼的Java表示形式,因此通常認(rèn)為Android應(yīng)用程序的逆向工程比較容易。攻擊者就是通過(guò)了解這些代碼版本,收集應(yīng)用程序信息,來(lái)發(fā)現(xiàn)漏洞的。如今,大多數(shù)Android應(yīng)用程序編輯器已經(jīng)意識(shí)到了這一點(diǎn),并盡力使反向工程不再那么容易。由于Java本地接口(Java Native Interface,簡(jiǎn)稱JNI),攻擊者通常依靠集成混淆策略或?qū)⒚舾泻瘮?shù)從Java / Kotlin端轉(zhuǎn)移到本機(jī)代碼。但是,當(dāng)他們決定同時(shí)使用兩者時(shí)(即,混淆的本機(jī)代碼),逆向工程過(guò)程變得更加復(fù)雜。結(jié)果,靜態(tài)查看本機(jī)庫(kù)的反匯編結(jié)果非常繁瑣且耗時(shí)。幸運(yùn)的是,運(yùn)行時(shí)檢查仍然是可能的,并且提供了一種便捷的方法來(lái)有效地掌握應(yīng)用程序的內(nèi)部機(jī)制,甚至避免混淆。JNI(Java Native Interface) Java本地接口,又叫Java原生接口。它允許Java調(diào)用C/C++的代碼,同時(shí)也允許在C/C++中調(diào)用Java的代碼。可以把JNI理解為一個(gè)橋梁,連接Java和底層。其實(shí)根據(jù)字面意思,JNI就是一個(gè)介于Java層和Native層的接口,而Native層就是C/C++層面。

由于針對(duì)常規(guī)調(diào)試器的保護(hù)在流行的應(yīng)用程序中非常普遍,因此使用動(dòng)態(tài)二進(jìn)制工具(DBI)框架(例如Frida)仍然是進(jìn)行全面檢查的理想選擇。從技術(shù)上講,在其他強(qiáng)大函數(shù)中,F(xiàn)rida允許用戶在本機(jī)函數(shù)的開(kāi)頭和結(jié)尾插入自己的代碼,或替換整個(gè)實(shí)現(xiàn)。但是,F(xiàn)rida在某些時(shí)候缺乏粒度,特別是在以指令規(guī)模檢查執(zhí)行情況時(shí)。在這種情況下,Quarkslab開(kāi)發(fā)的DBI框架QBDI可以幫助Frida在調(diào)用給定的本機(jī)函數(shù)時(shí)確定已執(zhí)行了代碼的哪些部分。

首先,我們必須正確設(shè)置測(cè)試環(huán)境。我們假設(shè)設(shè)備已經(jīng)植根并且Frida服務(wù)器已經(jīng)在運(yùn)行并且可以使用。除了Frida,我們還需要安裝QBDI。我們可以從源代碼編譯它或下載Android的發(fā)行版,使用說(shuō)明可以直接從官方頁(yè)面檢索到。解壓縮后,我們必須將共享庫(kù)libQBDI.so推送到設(shè)備上的/ data / local / tmp中。除此之外,我們還可以注意到在frida-qbdi.js中定義的QBDI綁定,該文件負(fù)責(zé)提供QBDI函數(shù)的接口。換句話說(shuō),它充當(dāng)QBDI和Frida之間的橋梁。

請(qǐng)注意,必須先關(guān)閉SELinux,否則由于某些限制規(guī)則,F(xiàn)rida無(wú)法將QBDI共享庫(kù)加載到內(nèi)存中。這將會(huì)顯示一條明確的錯(cuò)誤消息,告訴用戶權(quán)限被拒絕。在大多數(shù)情況下,僅使用root特權(quán)運(yùn)行此命令行即可完成此工作: 

  1. setenforce 0 

現(xiàn)在我們已經(jīng)具備了基于Frida和QBDI編寫(xiě)腳本的所有要求。

跟蹤本機(jī)函數(shù)

在對(duì)JNI共享庫(kù)執(zhí)行反向工程時(shí),始終值得檢查JNI_OnLoad()。確實(shí),此函數(shù)在庫(kù)加載后立即調(diào)用,并負(fù)責(zé)初始化。它能夠與Java端進(jìn)行交互,例如設(shè)置類的屬性,調(diào)用Java函數(shù)以及通過(guò)幾個(gè)JNI函數(shù)注冊(cè)其他本機(jī)方法。攻擊者通常依靠這些屬性來(lái)隱藏一些敏感的檢查和秘密的內(nèi)部機(jī)制。

接下來(lái),讓我們假設(shè)我們要分析一個(gè)流行的Android應(yīng)用程序,比如Whatsapp,其軟件包名稱為com.whatsapp,這是當(dāng)前Android上最廣泛的即時(shí)消息解決方案。它嵌入了一堆共享庫(kù),其中一個(gè)是libwhatsapp.so。不過(guò)要注意的是,該庫(kù)并不位于常規(guī)的lib /目錄中,因?yàn)樵谶\(yùn)行時(shí)存在一種解壓縮機(jī)制,該機(jī)制可將其從存檔中提取出來(lái),然后將其加載到內(nèi)存中,我們的目標(biāo)是弄清楚它的初始化函數(shù)在做什么。

利用 Frida

  1. /** * frida -Uf com.whatsapp --no-pause -l script.js */function processJniOnLoad(libraryName) { 
  2.     const funcSym = "JNI_OnLoad"
  3.     const funcPtr = Module.findExportByName(libraryName, funcSym); 
  4.  
  5.     console.log("[+] Hooking " + funcSym + "() @ " + funcPtr + "..."); 
  6.     // jint JNI_OnLoad(JavaVM *vm, void *reserved); 
  7.     var funcHook = Interceptor.attach(funcPtr, { 
  8.         onEnter: function (args) { 
  9.             const vm = args[0]; 
  10.             const reserved = args[1]; 
  11.             console.log("[+] " + funcSym + "(" + vm + ", " + reserved + ") called"); 
  12.         }, 
  13.         onLeave: function (retval) { 
  14.             console.log("[+]\t= " + retval); 
  15.         } 
  16.     });}function waitForLibLoading(libraryName) { 
  17.     var isLibLoaded = false
  18.  
  19.     Interceptor.attach(Module.findExportByName(null"android_dlopen_ext"), { 
  20.         onEnter: function (args) { 
  21.             var libraryPath = Memory.readCString(args[0]); 
  22.             if (libraryPath.includes(libraryName)) { 
  23.                 console.log("[+] Loading library " + libraryPath + "..."); 
  24.                 isLibLoaded = true
  25.             } 
  26.         }, 
  27.         onLeave: function (args) { 
  28.             if (isLibLoaded) { 
  29.                 processJniOnLoad(libraryName); 
  30.                 isLibLoaded = false
  31.             } 
  32.         } 
  33.     });}Java.perform(function() { 
  34.     const libraryName = "libwhatsapp.so"
  35.     waitForLibLoading(libraryName);}); 

首先,借助Frida提供的便捷API,我們可以輕松地掛接我們要研究的函數(shù)。但是,由于Android應(yīng)用程序中嵌入的庫(kù)是通過(guò)System.loadLibrary()動(dòng)態(tài)加載的,該函數(shù)在后臺(tái)調(diào)用了本機(jī)android_dlopen_ext(),因此我們需要等待將目標(biāo)庫(kù)放入進(jìn)程的內(nèi)存中。使用此腳本,我們可以只訪問(wèn)函數(shù)的輸入(參數(shù))和輸出(返回值),也就是說(shuō),我們位于函數(shù)層。這是非常有限的,僅憑這一點(diǎn)基本上還不足以準(zhǔn)確掌握內(nèi)部的情況。因此,在這種精確的情況下,我們希望在較低級(jí)別上徹底檢查該函數(shù)。

利用 Frida 和 QBDI

QBDI提供的導(dǎo)入函數(shù)可以幫助我們克服以上的問(wèn)題,實(shí)際上,該DBI框架允許用戶通過(guò)跟蹤執(zhí)行的指令來(lái)執(zhí)行細(xì)粒度的分析。這對(duì)我們非常有用,因?yàn)槲覀兛梢陨钊肓私馕覀兊哪繕?biāo)函數(shù)。

這樣做的想法是,不是讓JNI_OnLoad()在常規(guī)啟動(dòng)期間運(yùn)行,而是在基本塊/指令范圍內(nèi)通過(guò)有條件的上下文來(lái)執(zhí)行它,以便確切地知道已執(zhí)行了什么。由于我們可以將這兩個(gè)DBI框架結(jié)合在一起,因此可以在我們之前編寫(xiě)的Frida腳本的基礎(chǔ)上集成這一全新的部分。

但是,我們使用的Interceptor.attach()函數(shù)只允許我們定義onEnter和onLeave回調(diào)。它意味著真正的函數(shù)總是被執(zhí)行,而不管你的條目回調(diào)應(yīng)該做什么。因此,初始化函數(shù)將執(zhí)行兩次:首先通過(guò)QBDI執(zhí)行,然后正常執(zhí)行。這是有問(wèn)題的,因?yàn)楦鶕?jù)情況不同,可能會(huì)出現(xiàn)一些意外的運(yùn)行時(shí)錯(cuò)誤,因?yàn)檫@個(gè)函數(shù)只需要調(diào)用一次。

幸運(yùn)的是,我們可以利用Frida的攔截器模塊帶來(lái)的另一個(gè)函數(shù),該函數(shù)包括替換本機(jī)函數(shù)的實(shí)現(xiàn)。這樣做,我們能夠設(shè)置QBDI上下文,執(zhí)行檢測(cè)的函數(shù)并像往常一樣無(wú)縫地將返回值轉(zhuǎn)發(fā)給調(diào)用方,以防止應(yīng)用程序崩潰,該技術(shù)旨在使過(guò)程足夠穩(wěn)定以恢復(fù)正常執(zhí)行。

然而,我們?nèi)匀幻媾R一個(gè)問(wèn)題,初始函數(shù)已被我們自己的新實(shí)現(xiàn)完全覆蓋。換句話說(shuō),該函數(shù)的代碼不是原始代碼,而是由Frida早些時(shí)候進(jìn)行檢測(cè)的。因此,在我們的代碼中,我們必須在使用QBDI執(zhí)行該函數(shù)之前恢復(fù)到真正的版本。

修改腳本后,processJniOnLoad()函數(shù)如下所示:

初始化

現(xiàn)在讓我們編寫(xiě)負(fù)責(zé)在QBDI上下文中執(zhí)行該函數(shù)的函數(shù),首先,我們需要初始化一個(gè)VM,實(shí)例化它的相關(guān)狀態(tài)(通用寄存器),并分配一個(gè)偽堆棧,該堆棧將在函數(shù)執(zhí)行期間使用。然后,我們必須將QBDI的上下文與當(dāng)前上下文進(jìn)行同步,也就是說(shuō),將實(shí)際CPU寄存器的值放入將要使用的QBDI。現(xiàn)在我們可以決定要檢測(cè)代碼的哪些部分。我們可以顯式定義一個(gè)任意地址范圍,也可以要求DBI檢測(cè)函數(shù)地址所在模塊的整個(gè)地址空間。為方便起見(jiàn),在本示例中將使用后者。

回調(diào)函數(shù)設(shè)置

我們必須指定所需的回調(diào)函數(shù)的種類,接下來(lái),我們要跟蹤已執(zhí)行的每條指令,因此我要放置一條預(yù)指令代碼回調(diào),這意味著將在位于目標(biāo)模塊中的每個(gè)已執(zhí)行指令之前調(diào)用我的函數(shù)。

此外,我們還可以添加幾個(gè)事件回調(diào)函數(shù),以便在執(zhí)行轉(zhuǎn)移到QBDI未檢測(cè)到的部分代碼中或從中返回時(shí)通知該事件。當(dāng)代碼與其他模塊(例如系統(tǒng)庫(kù))(libc.so,libart.so,libbinder.so等)進(jìn)行交互時(shí),此函數(shù)非常有用。請(qǐng)注意,根據(jù)您要監(jiān)視的內(nèi)容,其他幾種回調(diào)類型可能會(huì)很有幫助。

函數(shù)調(diào)用

現(xiàn)在我們準(zhǔn)備通過(guò)QBDI調(diào)用目標(biāo)函數(shù),當(dāng)然,我們需要傳遞預(yù)期的參數(shù),在我們的例子中是一個(gè)指向JavaVM對(duì)象的指針和一個(gè)空指針。然后,我們可以根據(jù)使用的調(diào)用約定在特定的QBDI寄存器或虛擬堆棧上檢索返回值。這個(gè)值必須從我們之前編寫(xiě)的本機(jī)替換函數(shù)中被轉(zhuǎn)發(fā)和返回。否則,應(yīng)用程序很可能會(huì)因?yàn)閷?duì)JNI版本的檢查不滿意而停止運(yùn)行,JNI_OnLoad()應(yīng)該返回JNI版本。

我們可以選擇使用QBDI的CPU恢復(fù)真正的CPU上下文。

  1. const qbdi = require("/path/to/frida-qbdi");qbdi.import();function qbdiExec(ctx, funcPtr, funcSym, args, postSync) { 
  2.     var vm = new QBDI(); // create a QBDI VM 
  3.     var state = vm.getGPRState(); 
  4.     var stack = vm.allocateVirtualStack(state, 0x10000); // allocate a virtual stack 
  5.     state.synchronizeContext(ctx, SyncDirection.FRIDA_TO_QBDI); // set up QBDI's context 
  6.  
  7.     vm.addInstrumentedModuleFromAddr(funcPtr); 
  8.  
  9.     var icbk = vm.newInstCallback(function (vm, gpr, fpr, data) { 
  10.         var inst = vm.getInstAnalysis(); 
  11.         console.log("0x" + inst.address.toString(16) + " " + inst.disassembly); 
  12.         return VMAction.CONTINUE
  13.     }); 
  14.     var iid = vm.addCodeCB(InstPosition.PREINST, icbk); // register pre-instruction callback 
  15.  
  16.     var vcbk = vm.newVMCallback(function (vm, evt, gpr, fpr, data) { 
  17.         const module = Process.getModuleByAddress(evt.basicBlockStart); 
  18.         const offset = ptr(evt.basicBlockStart - module.base); 
  19.         if (evt.event & VMEvent.EXEC_TRANSFER_CALL) { 
  20.             console.warn(" -> transfer call to 0x" + evt.basicBlockStart.toString(16) + " (" + module.name + "@" + offset + ")"); 
  21.         } 
  22.         if (evt.event & VMEvent.EXEC_TRANSFER_RETURN) { 
  23.             console.warn(" <- transfer return from 0x" + evt.basicBlockStart.toString(16) + " (" + module.name + "@" + offset + ")"); 
  24.         } 
  25.         return VMAction.CONTINUE
  26.     }); 
  27.     var vid = vm.addVMEventCB(VMEvent.EXEC_TRANSFER_CALL, vcbk); // register transfer callback 
  28.     var vid2 = vm.addVMEventCB(VMEvent.EXEC_TRANSFER_RETURN, vcbk); // register return callback 
  29.  
  30.     const javavm = ptr(args[0]); 
  31.     const reserved = ptr(args[1]); 
  32.  
  33.     console.log("[+] Executing " + funcSym + "(" + javavm + ", " + reserved + ") through QBDI..."); 
  34.     vm.call(funcPtr, [javavm, reserved]); 
  35.     var retVal = state.getRegister(0); // x86 so return value is stored on $eax 
  36.     console.log("[+] " + funcSym + "() returned " + retVal); 
  37.     if (postSync) { 
  38.         state.synchronizeContext(ctx, SyncDirection.QBDI_TO_FRIDA); 
  39.     } 
  40.     return retVal;} 

最終,此腳本必須使用frida-compile進(jìn)行編譯,以便正確包含包含QBDI綁定的frida-qbdi.js。官方文檔頁(yè)對(duì)編譯過(guò)程進(jìn)行了詳細(xì)說(shuō)明。

生成一個(gè)覆蓋文件

具有包含已執(zhí)行的所有指令的跟蹤是很有必要的,但對(duì)于反向工程來(lái)說(shuō)并不方便。事實(shí)上,我們不能一眼就分辨出整個(gè)執(zhí)行過(guò)程中的路徑。為了正確地呈現(xiàn)捕獲的軌跡,在反匯編器中集成可能是一個(gè)好主意。這樣,人們就可以準(zhǔn)確地看到全部的路徑。然而,大多數(shù)反匯編器本身并沒(méi)有提供這樣的選項(xiàng)。對(duì)我們來(lái)說(shuō)幸運(yùn)的是,各種插件都提供了這樣的選項(xiàng)。在本例中,我們使用Lighthouse和Dragondance分別用于IDA Pro和Ghidra。這些插件可以通過(guò)導(dǎo)入drcov格式的代碼覆蓋文件來(lái)輕松配置,DynamioRIO使用這種格式存儲(chǔ)關(guān)于代碼覆蓋率的信息。

drcov格式非常簡(jiǎn)單:除了標(biāo)頭字段外,還必須指定描述進(jìn)程的內(nèi)存布局的模塊表,為每個(gè)模塊分配一個(gè)惟一的ID。此后,就有了所謂的基本塊表。該表包含執(zhí)行期間已命中的每個(gè)基本塊,一個(gè)基本塊由三個(gè)屬性定義:它的開(kāi)始(相對(duì))地址,它的大小和它所屬模塊的ID。

由于我們能夠在每個(gè)基本塊的開(kāi)頭放置一個(gè)回調(diào),因此我們可以確定這些值,從而生成我們自己的文件。現(xiàn)在,我們需要檢索基地址和所有已執(zhí)行的基本塊的大小,而不是按指令規(guī)模工作。實(shí)際上,我們必須定義一個(gè)類型為BASIC_BLOCK_NEW 的QBDI事件回調(diào)函數(shù),該函數(shù)負(fù)責(zé)收集此類信息。每當(dāng)QBDI將要執(zhí)行一個(gè)新的基本程序塊時(shí),我們的函數(shù)都會(huì)被調(diào)用,到目前為止尚不知道。在本示例中,我們不僅要打印有關(guān)此基本塊的一些有趣的值,還要?jiǎng)?chuàng)建一個(gè)代碼覆蓋率文件,以后可以在反匯編器中將其導(dǎo)入。但是,在Frida腳本的上下文中,我們無(wú)法操作文件。結(jié)果,我們必須停止使用frida命令行實(shí)用程序,并直接依賴于Frida提供的消息傳遞系統(tǒng)從底層Python腳本運(yùn)行我們的JS腳本。這樣做使我們能夠在JS和Python端之間進(jìn)行通信,然后對(duì)所需的文件系統(tǒng)執(zhí)行所有操作。

  1. var vcbk = vm.newVMCallback(function (vm, evt, gpr, fpr, data) { 
  2.     const module = Process.getModuleByAddress(evt.basicBlockStart); 
  3.     const base_addr = ptr(evt.basicBlockStart - module.base); // address must be relative to the module's start 
  4.     const size = evt.basicBlockEnd - evt.basicBlockStart; 
  5.     send({"bb": 1}, getBBInfo(base_addr, size, module)); // send the newly discovered basic block to the Python side 
  6.     return VMAction.CONTINUE;});var vid = vm.addVMEventCB(VMEvent.BASIC_BLOCK_NEW, vcbk); 

請(qǐng)注意,getBBInfo()函數(shù)僅在發(fā)送消息之前先序列化有關(guān)基本塊的信息。顯然,Python端必須處理此類消息,將與執(zhí)行相關(guān)的內(nèi)容保留在內(nèi)存中,并最終以上述正確的格式相應(yīng)地生成代碼覆蓋文件。如果一切順利,由于其相應(yīng)的代碼覆蓋插件,可以將輸出文件加載到IDA Pro或Ghidra中。所有已執(zhí)行的基本塊都將突出顯示,現(xiàn)在我們可以更清楚地遵循執(zhí)行流程,而只關(guān)注代碼的相關(guān)部分。

總結(jié)

Java/Kotlin逆向工程的易用性使得Android應(yīng)用程序開(kāi)發(fā)人員可以使用C/ c++來(lái)實(shí)現(xiàn)某些漏洞層面的操作。因此,本文所講的方法就是要讓逆向工程師逆向的過(guò)程變得很困難。因此,將QBDI與Frida一起使用是一個(gè)非常好的選擇,尤其是在研究那些本機(jī)函數(shù)時(shí)。這種組合確實(shí)提供了一種方法,可以弄清一個(gè)函數(shù)在不同層次上的作用,即函數(shù)、基本塊和指令規(guī)模。此外,還可以利用QBDI的執(zhí)行傳輸事件來(lái)解析對(duì)系統(tǒng)庫(kù)的外部調(diào)用,或者跟蹤內(nèi)存訪問(wèn),然后了解執(zhí)行的總體情況。為了有效地協(xié)助反向工程師,可以將收集的信息明智地集成到一些現(xiàn)有的面向反向工程的工具中,以完善其靜態(tài)分析。除了生成執(zhí)行流程的直觀表示之外,從運(yùn)行時(shí)獲取此類反饋對(duì)于其他與安全相關(guān)的目的(如模糊測(cè)試)也很有價(jià)值。還值得注意的是,如果函數(shù)很重要,F(xiàn)rida和QBDI都可以提供C / C ++ API。

本文翻譯自:https://blog.quarkslab.com/why-are-frida-and-qbdi-a-great-blend-on-android.html如若轉(zhuǎn)載,請(qǐng)注明原文地址:

 

責(zé)任編輯:姜華 來(lái)源: 嘶吼網(wǎng)
相關(guān)推薦

2021-01-16 16:07:51

RustAndroid Nat內(nèi)存

2017-05-11 21:30:01

Android動(dòng)態(tài)代理ServiceHook

2009-11-16 16:43:24

PHP數(shù)組刪除

2020-10-28 14:58:21

漏洞uTorrent協(xié)議層

2020-09-21 09:58:01

Frida

2010-03-04 09:51:07

Linux動(dòng)態(tài)庫(kù)

2009-11-16 10:16:24

PHP文件上傳

2010-07-14 14:31:27

POP3和IMAP4

2023-10-24 07:22:22

Nginx運(yùn)維管理

2020-09-22 10:05:14

AsyncRAT

2023-10-30 11:45:44

FridaC++函數(shù)

2022-01-19 08:00:00

靜態(tài)代碼動(dòng)態(tài)代碼開(kāi)發(fā)

2011-08-23 14:46:59

云計(jì)算

2021-03-08 00:11:02

Spring注解開(kāi)發(fā)

2023-02-13 14:01:32

2009-12-18 10:39:50

路由器關(guān)鍵技術(shù)

2009-07-06 17:47:44

2009-06-29 12:30:08

2016-11-23 16:48:20

react-nativandroidjavascript

2021-03-18 22:06:01

數(shù)據(jù)分析編程語(yǔ)言大數(shù)據(jù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 亚洲一区在线免费观看 | 国产一区二区三区在线免费 | 亚洲经典一区 | 欧洲视频一区 | 少妇黄色 | 日本三级网址 | 91短视频网址| 成人无遮挡毛片免费看 | 久久亚洲一区二区三区四区 | 免费在线色 | 午夜视频在线观看网址 | 成人九色 | 亚洲国产一区二区三区在线观看 | 日本在线视 | 一区在线视频 | 午夜日韩精品 | 欧洲精品码一区二区三区免费看 | 亚洲一区二区在线视频 | 国产精品免费一区二区 | 中文一区二区视频 | 96av麻豆蜜桃一区二区 | 久草在线在线精品观看 | 高清一区二区 | 亚洲久草 | 神马久久久久久久久久 | 欧美a在线| 亚洲精品一区在线 | 精品在线一区二区三区 | 国产一区二区免费 | 一区二区三区视频在线观看 | 男人电影天堂 | 性高湖久久久久久久久3小时 | 玖玖综合在线 | 欧美一级片在线看 | 国产在线观看不卡一区二区三区 | 男女午夜免费视频 | 久久久精品网 | 亚洲 中文 欧美 日韩 在线观看 | 91麻豆精品国产91久久久更新资源速度超快 | 中文字幕av中文字幕 | www.国产.com|