Frida是一款基于Python+javascript的Hook框架, 可运行在Windows、Android、iOS、Linux、MacOS全平台系统中,主要使用了动态二进制插桩技术。插桩技术是指将额外的代码注入到目标程序中, 以实现收集目标运行时信息, 插桩技术主要分为两种:源代码插桩和二进制插桩, 源代码插桩是将额外代码注入到程序源代码中,二进制插桩是将额外代码注入到二进制可执行文件中。
使用Frida可以访问目标进程的内存空间,在目标程序运行时可以覆盖一些功能,从导入的类中调用函数,在堆上可以查找对象实例并使用这些对象实例,并可以Hook、跟踪和拦截函数等等。
Frida是一个非常强大的动态instrumentation框架,其主要能力包括:
Frida在实战使用过程中,经常使用的功能语法主要包括以下这些:
导入frida模块:
const frida = require('frida');
附加/注入进程:
// 附加const session = await frida.attach(pid);// 注入const session = await frida.spawn([path], options);
创建/加载/卸载脚本实例:
# 创建const script = await session.createScript(source);# 加载await script.load();# 卸载await script.unload();
导出函数:
rpc.exports = { func1: (args) => { // ... }}
Hook函数:
Interceptor.attach(target, { onEnter: function(args) { }, onLeave: function(retval) { }});
读写内存:
let buf = Memory.readByteArray(addr, len);Memory.writeByteArray(addr, [1, 2, 3]);
枚举/搜索模块:
// 枚举Process.enumerateModules()// 搜索Process.findModuleByName()
枚举/搜索导出函数:
// 枚举Module.enumerateExports()// 搜索Module.findExportByName()
调用函数:
let retval = Module.getExportByName()(args);
这里以Windows10环境为基础进行实验, 首先在Windows搜索框中搜索:PowerShell, 以管理员权限打开, 并执行以下命令:
pip install frida-tools
注意:这里一定要以管理员权限打开PowerShell,否则可能会安装失败。
安装成功后如图所示:
图片
输入命令frida --version 查看Frida的版本号,如果正常显示版本号,则说明安装成功, 如图:
图片
编写测试程序的目的是要验证Frida能否成功Hook测试程序中的指定函数, 并将函数的每个参数内容进行打印。
我在这里的测试程序使用C++编写, 主要完成2个函数, AES加密和解密算法, 设想的步骤是将AES加密算法和解密算法的两个函数编译成Dll,并将两个函数导出, 然后再写一个客户端程序加载Dll并调用导出函数。
AES加密算法的C++代码如下:
extern "C" __declspec(dllexport) void AesEncrypt(unsigned char* plaintext, int plaintext_len, unsigned char* key, unsigned char* iv, unsigned char* ciphertext) { EVP_CIPHER_CTX* ctx; int len; int ciphertext_len; printf("AesEncrypt is at %p/n", AesEncrypt); ctx = EVP_CIPHER_CTX_new(); EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv); EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len); ciphertext_len = len; EVP_EncryptFinal_ex(ctx, ciphertext + len, &len); ciphertext_len += len; EVP_CIPHER_CTX_free(ctx);}
AES加密算法函数AesEncrypt包含了四个参数,分别为: 明文字符串、明文字符串长度、Key、iv向量。
AES解密算法C++代码如下:
// AES解密extern "C" __declspec(dllexport) void AesDecrypt(unsigned char* ciphertext, int ciphertext_len, unsigned char* key, unsigned char* iv, unsigned char* plaintext) { EVP_CIPHER_CTX* ctx; int len; int plaintext_len; printf("AesDecrypt is at %p/n", AesDecrypt); ctx = EVP_CIPHER_CTX_new(); EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv); EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len); plaintext_len = len; EVP_DecryptFinal_ex(ctx, plaintext + len, &len); plaintext_len += len; EVP_CIPHER_CTX_free(ctx);}
同样,AES解密算法函数AesDecrypt也提供了四个参数,分别为:密文文本、密文文本长度、Key、iv向量。
重新建立一个新的C++工程, 这里模拟了真实程序的业务场景,对明文字符串使用AES算法加密,为了防止程序运行太快退出,这里将主要程序逻辑放到一个while循环中,并使用暂停功能进行控制,方便后面的函数Hook实验。为了方便操作, 我在中间插入了pause暂停, 方便后面手动控制函数的调用时机。
应用代码如下:
typedef void(__stdcall* AES_ENCRYPT_TYPE)(unsigned char*, int, unsigned char*, unsigned char*, unsigned char*);typedef void(__stdcall* AES_DECRYPT_TYPE)(unsigned char*, int, unsigned char*, unsigned char*, unsigned char*);int main(){ while (1) { //std::cout << "Hello World!/n"; //原文 unsigned char plaintext[] = "This is a plaintext message"; // 密钥 unsigned char key[32] = "suntiger20232021sdvdiuyt657uhjg"; // 初始化向量 unsigned char iv[16] = "9876kvdfdkkdfdf"; // 密文缓冲区 unsigned char ciphertext[128]; memset(ciphertext, 0, 128); // 加密 AesEncrypt(plaintext, strlen((char*)plaintext), key, iv, ciphertext); system("pause"); unsigned char decryptedtext[128]; memset(decryptedtext, 0, 128); AesDecrypt(ciphertext, strlen((char*)ciphertext), key, iv, decryptedtext); printf("%s/n", decryptedtext); system("pause"); Sleep(1); }}
将以上代码编译执行后,每按一次空格键, 程序便会依次向下执行, 如图:
图片
插桩程序代码由Python和Javascript语言组合而成,功能非常强大, 以下代码是我自己实现的对动态链接库中的导出函数AesEncrypt进行Hook,建立一个Python工程,代码如下:
import fridaimport sysdef on_message(message, data): print(message)# Press the green button in the gutter to run the script.if __name__ == '__main__': local = frida.get_local_device() // 附加进程 session = local.attach("test.exe") script = session.create_script(""" var baseAddr = Module.findBaseAddress('aes.dll'); // 查找函数模块 var aesEncryptAddr = Module.findExportByName("aes.dll", "AesEncrypt"); Interceptor.attach(aesEncryptAddr, { onEnter: function(args) { send(args[0]); console.log(''); console.log('[+]plaintext: ' + Memory.readUtf8String(args[0])); console.log('[+]plaintext len: ' + args[1].toInt32()); console.log('[+]key:' + Memory.readUtf8String(args[2])); console.log('[+]iv:' + Memory.readUtf8String(args[3])); } }); """) script.on('message', on_message) script.load() sys.stdin.read() session.detach()
在上面的Hook代码中,我主要完成了以下操作:
以下是我执行插桩程序后,获取到的参数内容,如图:
图片
可以看到,已经成功把解密函数的四个参数全部打印出来, C++函数Hook成功。
本文链接:http://www.28at.com/showinfo-26-15861-0.html使用Frida在Windows中拦截C++函数
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com
上一篇: JS 常见的 六种继承方式,你知道几种?
下一篇: 四种常见线程池的原理,你学会了吗?