Frida由于使用JavaScript语言安装钩子的便利性而在最近变得越来越流行。我看到许多研究都将Frida用于移动平台,但最近Windows似乎在使用方面有了更多的吸引力。在DarunGrim,我们正在研究安全研究人员可以用于日常工作的新方法。Frida是我们认为可用于Windows逆向工程的工具之一。但是,在我们的测试过程中,我们发现符号查找功能是该工具广泛使用的限制因素。我们进行了改进,现在Frida 12.9.8中可以使用它。我们非常感谢OleAndréVadlaRavnås在合并变更方面的帮助。
创新互联公司服务项目包括曲靖网站建设、曲靖网站制作、曲靖网页制作以及曲靖网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,曲靖网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到曲靖省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!
我们将简要介绍一下所做的更改,并说明如何在现实世界中解决问题时使用改进的符号查找功能。
0x01 对Frida 12.9.8 的改进
Frida使用dbghelp.dll API在Windows平台中查找符号。但是它缺少符号服务器支持。我们增加了对符号服务器的支持,并改进了Windows中传递符号字符串的方式。在较旧的Frida实现中,查找每个符号花费了一些时间,因为它使用通配符模块名称查找任何符号。现在,你可以指定模块名称以加快符号查找的速度。
新的Frida将随symsrv.dll和dbghelp.dll一起提供,以支持包括Microsoft符号服务器在内的符号服务器。
这些是我们在Ole的帮助下所做的更改。
- · Add load_symbols() and improve the DbgHelp backend
- · Migrate agent to new DbgHelp layout on Windows
- · Frida 12.9.8
0x02 分析office的恶意宏
使用改进的Frida对一个Office Macro恶意软件进行分析,我们希望将其应用到Frida中进行深度分析。
代码注入
下图显示了Frida通常如何安装hook并从已安装的hook中获取消息。
此过程中涉及frida,session,脚本对象,以管理hook安装。hook回调是用JavaScript编写的。
以下代码显示了如何使用这些对象来安装分配给self.script_text变量的JavaScript hook代码以使用process_id变量进行处理的示例。
- 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进行hook的第一步是找到目标函数。
- 如果函数已导出,则只需使用导出的函数名和DLL名称调用Module.findExportByName方法。
- Module.findExportByName(dllName, name)
- 但是,如果该函数未导出并且仅记录在例如PDB符号文件中,则可以调用DebugSymbol.getFunctionByName方法。使用Frida 12.9.8,你可以传递“ DLLName!FunctionName”符号,以在指定特定功能时提高准确性,并在定位它们时获得更好的性能。
- 有时,为模块加载符号可能很慢,因为它可能来自远程符号服务器。因此,你需要调用DebugSymbol.load方法来启动符号的加载,以便我们加载最少数量的符号。
- 下面是一个示例代码,该示例代码使用Module.findExportByName和DebugSymbol方法查找任何带符号或导出的函数。它使用字典来缓存其发现,以删除所有重复的信息。如果你要hook大量函数,则可以节省整个符号查找时间。
- 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')
设置符号路径
在Windows环境下设置符号服务器有多种方法,建议你从命令行设置_NT_SYMBOL_PATH变量。Windows调试器的符号路径对变量的用法有很好的描述。
https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/symbol-path
以下将使用“ c:\ symbols”作为其本地符号存储来缓存正式的Microsoft符号服务器。
setx _NT_SYMBOL_PATH SRV*c:\symbols*https://msdl.microsoft.com/download/symbols
以下命令将使系统使用默认的符号存储目录。
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
- }
因此,当你启动Word进程且进程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监控office的宏行为
vbe.js很少有hook来监视恶意Office文档的行为。
__vbaStrCat
vbe7.dll是已找到Visual Basic运行时引擎的DLL,里面有很多有趣的函数。但是首先,我们想观察字符串去混淆操作
vbe7!__ vbaStrCat是在Visual Basic中串联字符串时调用的函数。
.text:651E53E6 ; __stdcall __vbaStrCat(x, x)
.text:651E53E6 ___vbaStrCat@8 proc near ; CODE XREF: _lblEX_ConcatStr↑p
许多基于宏的恶意软件文档都使用基于字符串的混淆。通过观察字符串的动作,你可以观察最终的去混淆字符串的构造。
以下hook代码将为每个调用打印出连接的字符串。
- 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
名称栏目:使用Frida对Windows平台的程序进行逆向分析
URL分享:http://www.shufengxianlan.com/qtweb/news49/273549.html网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联