Frida Windows API Hook 原理和实践 - 实战 PicoCTF 2025 两道re题
字数 1923 2025-08-29 08:30:06
Frida Windows API Hook 原理与实践教程
一、Frida 简介与环境配置
Frida 是一款强大的动态插桩工具,允许将 JavaScript 代码注入到正在运行的进程中,拦截和修改函数调用。在安全研究和 CTF 竞赛中有广泛应用。
环境配置步骤
-
安装 Python
- 从 Python 官网下载安装
- 安装时勾选"Add Python to PATH"选项
- 确保可通过命令行调用
python和pip
-
安装 Frida 工具
pip install frida-tools -
验证安装
frida --version成功安装会显示类似
16.x.x的版本号
二、Frida Hook Windows API 原理
基本原理流程
-
查找目标函数地址
- 通过加载相应 DLL (如
kernel32.dll、user32.dll) - 使用
Module.findExportByName()或Module.getExportByName()获取函数地址
- 通过加载相应 DLL (如
-
保存原始指令
- 保存目标函数开头的几条指令(通常5-10字节)
- 用于Hook后仍能调用原始函数
-
写入跳转指令
- 用跳转指令(通常是JMP指令)覆盖目标函数开头
- 将执行流重定向到Frida的JavaScript Hook函数
-
JavaScript Hook函数处理
- 读取和修改函数参数
- 执行自定义逻辑
- 决定是否调用原始函数
- 读取和修改函数返回值
-
调用原始函数(可选)
- 将参数传递给原始函数
- 执行原始函数并获取返回值
-
恢复执行
- Hook函数执行完毕后,控制权返回调用方
技术实现细节
Frida采用Inline Hook方式实现:
- 跳转到
frida-agent.dll处理寄存器环境 - 实现对参数的修改
- 恢复被覆盖的字节完成函数调用
三、实战案例1:PicoCTF 2025 Binary Instrumentation 1 (200 pts)
题目描述
"Can you wake up my program to get the flag?"
程序运行后长时间无响应。
逆向分析
- 程序通过API读取PEB结构体获取ImageBase
- 经过PE格式内存映射过程(类似壳的功能)
- 解密后进入真正程序逻辑
- 真实程序包含大量Sleep函数调用,导致长时间等待
Frida解决方案
-
检测API调用
frida-trace.exe -i "!Sleep*" -f bininst1.exe确认程序调用了Sleep函数
-
Hook Sleep函数脚本
// frida_WinAPI_Sleep_hook.js const sleep = Module.findExportByName("kernel32.dll", "Sleep"); Interceptor.attach(sleep, { onEnter: function(args) { console.log("Original Sleep time: " + args[0].toInt32() + "ms"); args[0] = ptr(1); // 修改Sleep时间为1ms } }); -
执行脚本
frida -l frida_WinAPI_Sleep_hook.js bininst1.exe直接跳过长时间等待获取flag
获取的flag
cGljb0NURnt3NGtlX20zX3VwX3cxdGhfZnIxZGFfZjI3YWNjMzh9 (base64解码后得到真实flag)
四、实战案例2:PicoCTF 2025 Binary Instrumentation 2 (300 pts)
题目描述
程序本应创建文件并写入flag,但出现问题,要求拦截文件写入函数查看问题。
逆向分析
- 同样是PE文件内存映射加载过程
- 真正执行部分调用CreateFileA和WriteFile
- 问题在于文件名为
<Insert path here>,包含非法字符导致失败
Frida解决方案
-
检测API调用
frida-trace -f bininst2.exe -i CreateFile* -i WriteFile* -
Hook CreateFileA
// CreateFileA.js const createFileA = Module.findExportByName("kernel32.dll", "CreateFileA"); Interceptor.attach(createFileA, { onEnter: function(args) { console.log("Original filename: " + args[0].readAnsiString()); // 修改文件名为合法路径 args[0] = Memory.allocUtf8String("C:\\temp\\flag.txt"); }, onLeave: function(retval) { console.log("CreateFileA returned: " + retval); } }); -
Hook WriteFile
// WriteFile.js const writeFile = Module.findExportByName("kernel32.dll", "WriteFile"); Interceptor.attach(writeFile, { onEnter: function(args) { this.fileHandle = args[0]; this.buffer = args[1]; this.length = args[2].toInt32(); }, onLeave: function(retval) { if (this.length > 0) { const data = this.buffer.readByteArray(this.length); console.log("WriteFile content: " + data); } } }); -
获取的flag
cGljb0NURntmcjFkYV9mMHJfYjFuX2luNXRydW0zbnQ0dGlvbiFfYjIxYWVmMzl9(base64解码后得到真实flag)
五、关键API Hook模板
1. 基本Hook模板
const func = Module.findExportByName("dllname.dll", "functionName");
Interceptor.attach(func, {
onEnter: function(args) {
// 访问参数: args[0], args[1], ...
// 修改参数: args[0] = newValue
},
onLeave: function(retval) {
// 访问返回值: retval
// 修改返回值: retval.replace(newValue)
}
});
2. 常用Windows API Hook示例
Sleep Hook
const sleep = Module.findExportByName("kernel32.dll", "Sleep");
Interceptor.attach(sleep, {
onEnter: function(args) {
console.log("Sleep time: " + args[0].toInt32() + "ms");
args[0] = ptr(1); // 修改Sleep时间
}
});
CreateFileA Hook
const createFileA = Module.findExportByName("kernel32.dll", "CreateFileA");
Interceptor.attach(createFileA, {
onEnter: function(args) {
const filename = args[0].readAnsiString();
console.log("Creating file: " + filename);
// 修改文件名
args[0] = Memory.allocUtf8String("new_path.txt");
}
});
WriteFile Hook
const writeFile = Module.findExportByName("kernel32.dll", "WriteFile");
Interceptor.attach(writeFile, {
onEnter: function(args) {
this.buffer = args[1];
this.length = args[2].toInt32();
},
onLeave: function(retval) {
const data = this.buffer.readByteArray(this.length);
console.log("Written data: " + data);
}
});
六、调试技巧与注意事项
-
使用frida-trace快速定位API
frida-trace -i "!Sleep*" -f target.exe -
处理不同参数类型
- 字符串参数:
args[0].readAnsiString()或args[0].readUtf16String() - 数值参数:
args[0].toInt32()或args[0].toInt64() - 指针参数:
args[0]直接使用
- 字符串参数:
-
内存分配
const newStr = Memory.allocUtf8String("new string"); -
错误处理
try { const value = args[0].readAnsiString(); } catch (e) { console.log("Error reading string: " + e); } -
多线程注意事项
- Frida Hook是线程安全的
- 但要注意共享数据的同步问题
七、总结
通过本教程,我们学习了:
- Frida的基本原理和Hook机制
- Windows API Hook的具体实现方式
- 两个实际CTF题目的解决方案
- 通过Hook Sleep跳过等待
- 通过Hook文件操作修复错误并获取flag
- 常用API的Hook模板和调试技巧
Frida的强大之处在于其动态插桩能力,可以无需源代码即可修改程序行为,在逆向工程和安全研究中具有重要价值。