使用Frida在Windows中拦截C++函数

2023年 10月 30日 82.8k 0

1.摘要

Frida是一款基于Python+javascript的Hook框架, 可运行在Windows、Android、iOS、Linux、MacOS全平台系统中,主要使用了动态二进制插桩技术。插桩技术是指将额外的代码注入到目标程序中, 以实现收集目标运行时信息, 插桩技术主要分为两种:源代码插桩和二进制插桩, 源代码插桩是将额外代码注入到程序源代码中,二进制插桩是将额外代码注入到二进制可执行文件中。

使用Frida可以访问目标进程的内存空间,在目标程序运行时可以覆盖一些功能,从导入的类中调用函数,在堆上可以查找对象实例并使用这些对象实例,并可以Hook、跟踪和拦截函数等等。

2.Frida的能力

Frida是一个非常强大的动态instrumentation框架,其主要能力包括:

  • 拦截(Hook)系统API调用:可以hook目标程序的各种系统API调用,如文件、网络、进程、加密等,拦截和修改参数或返回值。
  • 注入JS代码:可以实时向目标程序注入JavaScript代码,调用其函数接口或修改程序运行逻辑。
  • 动态调试:可以动态地设置断点、Dump内存、遍历对象等,无需重启程序。
  • 反混淆和脱壳:可以通过dump内存或hook相关函数的方式对加壳/混淆的目标程序进行反混淆和脱壳。
  • 适用范围广:支持Windows、Linux、macOS、iOS、Android等主流平台。
  • 多语言绑定:提供Java、Python、C#等语言的绑定库, 可以使用脚本语言进行交互。
  • 插件机制:支持扩展自己的插件,实现自定义功能。
  • 开源免费:Frida是完全开源免费的,社区活跃。

3.Frida常用语法

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);

4.Frida安装

这里以Windows10环境为基础进行实验, 首先在Windows搜索框中搜索:PowerShell, 以管理员权限打开, 并执行以下命令:

pip install frida-tools

注意:这里一定要以管理员权限打开PowerShell,否则可能会安装失败。

安装成功后如图所示:

图片图片

输入命令frida --version 查看Frida的版本号,如果正常显示版本号,则说明安装成功, 如图:

图片图片

5.编写测试程序

编写测试程序的目的是要验证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 %pn", 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 %pn", 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

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论