新聞中心
Frida由于使用JavaScript語言安裝鉤子的便利性而在最近變得越來越流行。我看到許多研究都將Frida用于移動平臺,但最近Windows似乎在使用方面有了更多的吸引力。在DarunGrim,我們正在研究安全研究人員可以用于日常工作的新方法。Frida是我們認(rèn)為可用于Windows逆向工程的工具之一。但是,在我們的測試過程中,我們發(fā)現(xiàn)符號查找功能是該工具廣泛使用的限制因素。我們進(jìn)行了改進(jìn),現(xiàn)在Frida 12.9.8中可以使用它。我們非常感謝OleAndréVadlaRavn?s在合并變更方面的幫助。

創(chuàng)新互聯(lián)公司服務(wù)項目包括曲靖網(wǎng)站建設(shè)、曲靖網(wǎng)站制作、曲靖網(wǎng)頁制作以及曲靖網(wǎng)絡(luò)營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢、行業(yè)經(jīng)驗、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,曲靖網(wǎng)站推廣取得了明顯的社會效益與經(jīng)濟效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到曲靖省份的部分城市,未來相信會繼續(xù)擴大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!
我們將簡要介紹一下所做的更改,并說明如何在現(xiàn)實世界中解決問題時使用改進(jìn)的符號查找功能。
0x01 對Frida 12.9.8 的改進(jìn)
Frida使用dbghelp.dll API在Windows平臺中查找符號。但是它缺少符號服務(wù)器支持。我們增加了對符號服務(wù)器的支持,并改進(jìn)了Windows中傳遞符號字符串的方式。在較舊的Frida實現(xiàn)中,查找每個符號花費了一些時間,因為它使用通配符模塊名稱查找任何符號?,F(xiàn)在,你可以指定模塊名稱以加快符號查找的速度。
新的Frida將隨symsrv.dll和dbghelp.dll一起提供,以支持包括Microsoft符號服務(wù)器在內(nèi)的符號服務(wù)器。
這些是我們在Ole的幫助下所做的更改。
- · Add load_symbols() and improve the DbgHelp backend
- · Migrate agent to new DbgHelp layout on Windows
- · Frida 12.9.8
0x02 分析office的惡意宏
使用改進(jìn)的Frida對一個Office Macro惡意軟件進(jìn)行分析,我們希望將其應(yīng)用到Frida中進(jìn)行深度分析。
代碼注入
下圖顯示了Frida通常如何安裝hook并從已安裝的hook中獲取消息。
此過程中涉及frida,session,腳本對象,以管理hook安裝。hook回調(diào)是用JavaScript編寫的。
以下代碼顯示了如何使用這些對象來安裝分配給self.script_text變量的JavaScript hook代碼以使用process_id變量進(jìn)行處理的示例。
- https://github.com/ohjeongwook/Frida.examples.vbe/blob/master/code.py
- import os
- import sys
- import frida
- import process
- class Instrumenter:
- def __init__(self, script_text):
- self.sessions = []
- self.script_text = script_text
- self._device = frida.get_local_device()
- self._device.on("child-added", self._on_child_added)
- self._device.on("child-removed", self._on_child_removed)
- self._device.on("output", self._on_output)
- def __del__(self):
- for session in self.sessions:
- session.detach()
- def run(self, process_name):
- proc = process.Runner(process_name, suspended = True)
- if not proc.create():
- return
- process_id = proc.get_id()
- self.instrument(process_id)
- if proc:
- proc.resume()
- def instrument(self, process_id):
- session = frida.attach(process_id)
- self.sessions.append(session)
- session.enable_child_gating()
- script = session.create_script(self.script_text)
- script.on('message', self.on_message)
- script.load()
- def on_message(self, message, data):
- print("[%s] => %s" % (message, data))
- def _on_child_added(self, child):
- print(" new child: {}".format(child))
- self.instrument(child.pid)
- def _on_child_removed(self, child):
- print(" child terminated: {}".format(child))
- def _on_output(self, pid, fd, data):
- print(" output: pid={}, fd={}, data={}".format(pid, fd, repr(data)))
- 符號查找:resolveName
- Frida JavaScript API在API文檔中有很好的描述。
- 使用Frida進(jìn)行hook的第一步是找到目標(biāo)函數(shù)。
- 如果函數(shù)已導(dǎo)出,則只需使用導(dǎo)出的函數(shù)名和DLL名稱調(diào)用Module.findExportByName方法。
- Module.findExportByName(dllName, name)
- 但是,如果該函數(shù)未導(dǎo)出并且僅記錄在例如PDB符號文件中,則可以調(diào)用DebugSymbol.getFunctionByName方法。使用Frida 12.9.8,你可以傳遞“ DLLName!FunctionName”符號,以在指定特定功能時提高準(zhǔn)確性,并在定位它們時獲得更好的性能。
- 有時,為模塊加載符號可能很慢,因為它可能來自遠(yuǎn)程符號服務(wù)器。因此,你需要調(diào)用DebugSymbol.load方法來啟動符號的加載,以便我們加載最少數(shù)量的符號。
- 下面是一個示例代碼,該示例代碼使用Module.findExportByName和DebugSymbol方法查找任何帶符號或?qū)С龅暮瘮?shù)。它使用字典來緩存其發(fā)現(xiàn),以刪除所有重復(fù)的信息。如果你要hook大量函數(shù),則可以節(jié)省整個符號查找時間。
- vbe.js
- https://github.com/ohjeongwook/Frida.examples.vbe/blob/master/vbe.js
- var loadedModules = {}
- var resolvedAddresses = {}
- function resolveName(dllName, name) {
- var moduleName = dllName.split('.')[0]
- var functionName = moduleName + "!" + name
- if (functionName in resolvedAddresses) {
- return resolvedAddresses[functionName]
- }
- log("resolveName " + functionName);
- log("Module.findExportByName " + dllName + " " + name);
- var addr = Module.findExportByName(dllName, name)
- if (!addr || addr.isNull()) {
- if (!(dllName in loadedModules)) {
- log(" DebugSymbol.loadModule " + dllName);
- try {
- DebugSymbol.load(dllName)
- } catch (err) {
- return 0;
- }
- log(" DebugSymbol.load finished");
- loadedModules[dllName] = 1
- }
- try {
- log(" DebugSymbol.getFunctionByName: " + functionName);
- addr = DebugSymbol.getFunctionByName(moduleName + '!' + name)
- log(" DebugSymbol.getFunctionByName: addr = " + addr);
- } catch (err) {
- log(" DebugSymbol.getFunctionByName: Exception")
- }
- }
- resolvedAddresses[functionName] = addr
- return addr
- }
- function loadModuleForAddress(address) {
- var modules = Process.enumerateModules()
- var i
- for (i = 0; i < modules.length; i++) {
- if (address >= modules[i].base && address <= modules[i].base.add(modules[i].size)) {
- log(" " + modules[i].name + ": " + modules[i].base + " " + modules[i].size + " " + modules[i].path)
- var modName = modules[i].path
- if (!(modName in loadedModules)) {
- log(" DebugSymbol.loadModule " + modName);
- try {
- DebugSymbol.load(modName)
- } catch (err) {
- return 0;
- }
- loadedModules[modName] = 1
- }
- break
- }
- }
- }
- var hookedFunctions = {}
- var addressToFunctions = {}
- var blackListedFunctions = {
- 'I_RpcClearMutex': 1
- }
- function hookFunction(dllName, funcName, callback) {
- if (funcName in blackListedFunctions) {
- return
- }
- var symbolName = dllName + "!" + funcName
- if (symbolName in hookedFunctions) {
- return
- }
- hookedFunctions[symbolName] = 1
- var addr = resolveName(dllName, funcName)
- if (!addr || addr.isNull()) {
- return
- }
- if (addr in hookedFunctions) {
- return
- }
- hookedFunctions[addr] = 1
- addressToFunctions[addr] = symbolName
- log('Interceptor.attach: ' + symbolName + '@' + addr);
- Interceptor.attach(addr, callback)
- }
- function hookPointers(address, count) {
- if (address.isNull())
- return
- var currentAddress = address
- for (var i = 0; i < count; i++) {
- var readAddress = ptr(currentAddress).readPointer();
- readAddress = ptr(readAddress)
- var symbolInformation = DebugSymbol.fromAddress(readAddress)
- var name = readAddress
- if (symbolInformation && symbolInformation.name) {
- name = symbolInformation.name
- }
- log('Hooking ' + readAddress + ": " + name)
- try {
- Interceptor.attach(readAddress, {
- onEnter: function (args) {
- log('[+] ' + name)
- }
- })
- } catch (err) {}
- currentAddress = currentAddress.add(4)
- }
- }
- function hookFunctionNames(moduleName, funcNames) {
- for (var i = 0; i < funcNames.length; i++) {
- var funcName = funcNames[i]
- try {
- hookFunction(moduleName, funcName, {
- onEnter: function (args) {
- var name = ''
- if (this.context.pc in addressToFunctions) {
- name = addressToFunctions[this.context.pc]
- }
- log("[+] " + name + " (" + this.context.pc + ")")
- }
- })
- } catch (err) {
- log("Failed to hook " + funcName)
- }
- }
- }
- function BytesToCLSID(address) {
- if (address.isNull())
- return
- var data = new Uint8Array(ptr(address).readByteArray(0x10))
- var clsid = "{" + getHexString(data[3]) + getHexString(data[2]) + getHexString(data[1]) + getHexString(data[0])
- clsid += '-' + getHexString(data[5]) + getHexString(data[4])
- clsid += '-' + getHexString(data[7]) + getHexString(data[6])
- clsid += '-' + getHexString(data[8]) + getHexString(data[9])
- clsid += '-' + getHexString(data[10]) + getHexString(data[11]) + getHexString(data[12]) + getHexString(data[13]) + getHexString(data[14]) + getHexString(data[15])
- clsid += '}'
- return clsid
- }
- function log(message) {
- console.log(message)
- }
- function dumpAddress(address) {
- log('[+] address: ' + address);
- if (address.isNull())
- return
- var data = ptr(address).readByteArray(50);
- log(hexdump(data, {
- offset: 0,
- length: 50,
- header: true,
- ansi: false
- }));
- }
- function dumpBytes(address, length) {
- if (address.isNull())
- return
- var data = ptr(address).readByteArray(length);
- log(hexdump(data, {
- offset: 0,
- length: length,
- header: true,
- ansi: false
- }));
- }
- function dumpSymbols(address, count) {
- if (address.isNull())
- return
- var currentAddress = address
- for (var i = 0; i < count; i++) {
- var readAddress = ptr(currentAddress).readPointer();
- readAddress = ptr(readAddress)
- var symbolInformation = DebugSymbol.fromAddress(readAddress)
- if (symbolInformation && symbolInformation.name) {
- log(currentAddress + ":\t" + readAddress + " " + symbolInformation.name)
- } else {
- log(currentAddress + ":\t" + readAddress)
- }
- currentAddress = currentAddress.add(4)
- }
- }
- function dumpBSTR(address) {
- log('[+] address: ' + address);
- if (address.isNull())
- return
- var length = ptr(address - 4).readULong(4);
- log("length: " + length)
- var data = ptr(address).readByteArray(length);
- log(hexdump(data, {
- offset: 0,
- length: length,
- header: true,
- ansi: false
- }));
- }
- function getString(address) {
- if (address.isNull())
- return
- var dataString = ''
- var offset = 0
- var stringEnded = false
- while (!stringEnded) {
- var data = new Uint8Array(ptr(address.add(offset)).readByteArray(10));
- if (data.length <= 0) {
- break
- }
- var i;
- for (i = 0; i < data.length; i++) {
- if (data[i] == 0x0) {
- stringEnded = true
- break
- }
- dataString += String.fromCharCode(data[i])
- }
- offset += data.length
- }
- log("dataString: " + dataString)
- return dataString;
- }
- function dumpWSTR(address) {
- if (address.isNull())
- return
- var dataString = ''
- var offset = 0
- var stringEnded = false
- while (!stringEnded) {
- var data = new Uint8Array(ptr(address.add(offset)).readByteArray(20));
- if (data.length <= 0) {
- break
- }
- var i;
- for (i = 0; i < data.length; i += 2) {
- if (data[i] == 0x0 && data[i + 1] == 0x0) {
- stringEnded = true
- break
- }
- dataString += String.fromCharCode(data[i])
- }
- offset += data.length
- }
- log("dataString: " + dataString)
- return dataString;
- }
- function hookRtcShell(moduleName) {
- hookFunction(moduleName, "rtcShell", {
- onEnter: function (args) {
- log("[+] rtcShell")
- var variantArg = ptr(args[0])
- dumpAddress(variantArg);
- var bstrPtr = ptr(variantArg.add(8).readULong())
- dumpBSTR(bstrPtr);
- }
- })
- }
- function hookVBAStrCat(moduleName) {
- hookFunction(moduleName, "__vbaStrCat", {
- onEnter: function (args) {
- log("[+] __vbaStrCat")
- // log('[+] ' + name);
- // dumpBSTR(args[0]);
- // dumpBSTR(args[1]);
- },
- onLeave: function (retval) {
- dumpBSTR(retval);
- }
- })
- }
- function hookVBAStrComp(moduleName) {
- hookFunction(moduleName, "__vbaStrComp", {
- onEnter: function (args) {
- log('[+] __vbaStrComp');
- log(ptr(args[1]).readUtf16String())
- log(ptr(args[2]).readUtf16String())
- }
- })
- }
- function hookRtcCreateObject(moduleName) {
- hookFunction(moduleName, "rtcCreateObject", {
- onEnter: function (args) {
- log('[+] rtcCreateObject');
- dumpAddress(args[0]);
- dumpBSTR(args[0]);
- log(ptr(args[0]).readUtf16String())
- },
- onLeave: function (retval) {
- dumpAddress(retval);
- }
- })
- }
- function hookRtcCreateObject2(moduleName) {
- hookFunction(moduleName, "rtcCreateObject2", {
- onEnter: function (args) {
- log('[+] rtcCreateObject2');
- dumpAddress(args[0]);
- dumpBSTR(args[1]);
- log(ptr(args[2]).readUtf16String())
- },
- onLeave: function (retval) {
- dumpAddress(retval);
- }
- })
- }
- // int __stdcall CVbeProcs::CallMacro(CVbeProcs *this, const wchar_t *)
- function hookCVbeProcsCallMacro(moduleName) {
- hookFunction(moduleName, "CVbeProcs::CallMacro", {
- onEnter: function (args) {
- log('[+] CVbeProcs::CallMacro');
- dumpAddress(args[0]);
- dumpWSTR(args[1]);
- },
- onLeave: function (retval) {
- dumpAddress(retval);
- }
- })
- }
- function hookDispCall(moduleName) {
- hookFunction(moduleName, "DispCallFunc", {
- onEnter: function (args) {
- log("[+] DispCallFunc")
- var pvInstance = args[0]
- var oVft = args[1]
- var instance = ptr(ptr(pvInstance).readULong());
- log(' instance:' + instance);
- log(' oVft:' + oVft);
- var vftbPtr = instance.add(oVft)
- log(' vftbPtr:' + vftbPtr);
- var functionAddress = ptr(ptr(vftbPtr).readULong())
- loadModuleForAddress(functionAddress)
- var functionName = DebugSymbol.fromAddress(functionAddress)
- if (functionName) {
- log(' functionName:' + functionName);
- }
- dumpAddress(functionAddress);
- var currentAddress = functionAddress
- for (var i = 0; i < 10; i++) {
- try {
- var instruction = Instruction.parse(currentAddress)
- log(instruction.address + ': ' + instruction.mnemonic + ' ' + instruction.opStr)
- currentAddress = instruction.next
- } catch (err) {
- break
- }
- }
- }
- })
- }
- hookRtcShell('vbe7')
- hookVBAStrCat('vbe7')
- hookVBAStrComp('vbe7')
- hookRtcCreateObject('vbe7')
- hookRtcCreateObject2('vbe7')
- hookCVbeProcsCallMacro('vbe7')
- hookDispCall('oleaut32')
設(shè)置符號路徑
在Windows環(huán)境下設(shè)置符號服務(wù)器有多種方法,建議你從命令行設(shè)置_NT_SYMBOL_PATH變量。Windows調(diào)試器的符號路徑對變量的用法有很好的描述。
https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/symbol-path
以下將使用“ c:\ symbols”作為其本地符號存儲來緩存正式的Microsoft符號服務(wù)器。
setx _NT_SYMBOL_PATH SRV*c:\symbols*https://msdl.microsoft.com/download/symbols
以下命令將使系統(tǒng)使用默認(rèn)的符號存儲目錄。
setx _NT_SYMBOL_PATH SRV*https://msdl.microsoft.com/download/symbols
運行惡意軟件并觀察行為
我們使用以下示例測試Frida的符號查找功能。惡意樣本做了一些混淆,可以使用Frida hook輕松分析。
我們在此處提供的代碼可從以下GitHub存儲庫中找到。
- https://github.com/ohjeongwook/Frida.examples.vbe
- var loadedModules = {}
- var resolvedAddresses = {}
- function resolveName(dllName, name) {
- var moduleName = dllName.split('.')[0]
- var functionName = moduleName + "!" + name
- if (functionName in resolvedAddresses) {
- return resolvedAddresses[functionName]
- }
- log("resolveName " + functionName);
- log("Module.findExportByName " + dllName + " " + name);
- var addr = Module.findExportByName(dllName, name)
- if (!addr || addr.isNull()) {
- if (!(dllName in loadedModules)) {
- log(" DebugSymbol.loadModule " + dllName);
- try {
- DebugSymbol.load(dllName)
- } catch (err) {
- return 0;
- }
- log(" DebugSymbol.load finished");
- loadedModules[dllName] = 1
- }
- try {
- log(" DebugSymbol.getFunctionByName: " + functionName);
- addr = DebugSymbol.getFunctionByName(moduleName + '!' + name)
- log(" DebugSymbol.getFunctionByName: addr = " + addr);
- } catch (err) {
- log(" DebugSymbol.getFunctionByName: Exception")
- }
- }
- resolvedAddresses[functionName] = addr
- return addr
- }
因此,當(dāng)你啟動Word進(jìn)程且進(jìn)程ID為3064時,可以使用以下命令從存儲庫中包含的vbe.js安裝hook。安裝hook之后,你可以打開惡意文檔以觀察其行為
- > python inject.py -p 3064 vbe.js
- resolveName vbe7!rtcShell
- Module.findExportByName vbe7 rtcShell
- Interceptor.attach: vbe7!rtcShell@0x652a2b76
- resolveName vbe7!__vbaStrCat
- Module.findExportByName vbe7 __vbaStrCat
- DebugSymbol.loadModule vbe7
- DebugSymbol.load finished
- DebugSymbol.getFunctionByName: vbe7!__vbaStrCat
- DebugSymbol.getFunctionByName: addr = 0x651e53e6
- Interceptor.attach: vbe7!__vbaStrCat@0x651e53e6
- resolveName vbe7!__vbaStrComp
- Module.findExportByName vbe7 __vbaStrComp
- DebugSymbol.getFunctionByName: vbe7!__vbaStrComp
- DebugSymbol.getFunctionByName: addr = 0x651e56a2
- Interceptor.attach: vbe7!__vbaStrComp@0x651e56a2
- resolveName vbe7!rtcCreateObject
- Module.findExportByName vbe7 rtcCreateObject
- Interceptor.attach: vbe7!rtcCreateObject@0x653e6e4c
- resolveName vbe7!rtcCreateObject2
- Module.findExportByName vbe7 rtcCreateObject2
- Interceptor.attach: vbe7!rtcCreateObject2@0x653e6ece
- resolveName vbe7!CVbeProcs::CallMacro
- Module.findExportByName vbe7 CVbeProcs::CallMacro
- DebugSymbol.getFunctionByName: vbe7!CVbeProcs::CallMacro
- DebugSymbol.getFunctionByName: addr = 0x6529019b
- Interceptor.attach: vbe7!CVbeProcs::CallMacro@0x6529019b
- resolveName oleaut32!DispCallFunc
- Module.findExportByName oleaut32 DispCallFunc
- Interceptor.attach: oleaut32!DispCallFunc@0x747995b0
- [!] Ctrl+D on UNIX, Ctrl+Z on Windows/cmd.exe to detach from instrumented program.
hook監(jiān)控office的宏行為
vbe.js很少有hook來監(jiān)視惡意Office文檔的行為。
__vbaStrCat
vbe7.dll是已找到Visual Basic運行時引擎的DLL,里面有很多有趣的函數(shù)。但是首先,我們想觀察字符串去混淆操作
vbe7!__ vbaStrCat是在Visual Basic中串聯(lián)字符串時調(diào)用的函數(shù)。
.text:651E53E6 ; __stdcall __vbaStrCat(x, x)
.text:651E53E6 ___vbaStrCat@8 proc near ; CODE XREF: _lblEX_ConcatStr↑p
許多基于宏的惡意軟件文檔都使用基于字符串的混淆。通過觀察字符串的動作,你可以觀察最終的去混淆字符串的構(gòu)造。
以下hook代碼將為每個調(diào)用打印出連接的字符串。
- https://github.com/ohjeongwook/Frida.examples.vbe/blob/master/vbe.js
- var loadedModules = {}
- var resolvedAddresses = {}
- function resolveName(dllName, name) {
- var moduleName = dllName.split('.')[0]
- var functionName = moduleName + "!" + name
- if (functionName in resolvedAddresses) {
- return resolvedAddresses[functionName]
- }
- log("resolveName " + functionName);
- log("Module.findExportByName " + dllName + " " + name);
- var addr = Module.findExportByName(dllName, name)
- if (!addr || addr.isNull()) {
- if (!(dllName in loadedModules)) {
- log(" DebugSymbol.loadModule " + dllName);
- try {
- DebugSymbol.load(dllName)
- } catch (err) {
- return 0;
- }
- log(" DebugSymbol.load finished");
- loadedModules[dllName] = 1
- }
- try {
- log(" DebugSymbol.getFunctionByName: " + functionName);
- addr = DebugSymbol.getFunctionByName(moduleName + '!' + name)
- log(" DebugSymbol.getFunctionByName: addr = " + addr);
- } catch (err) {
- log(" DebugSymbol.getFunctionByName: Exception")
- }
- }
- resolvedAddresses[functionName] = addr
- return addr
- }
- function loadModuleForAddress(address) {
- var modules = Process.enumerateModules()
- var i
分享標(biāo)題:使用Frida對Windows平臺的程序進(jìn)行逆向分析
網(wǎng)站網(wǎng)址:http://www.dlmjj.cn/article/dhcedjo.html


咨詢
建站咨詢
