trapfuzzer-gdb 源码分析
字数 1604 2025-08-09 15:23:15
trapfuzzer-gdb 源码分析与功能实现详解
一、项目概述
trapfuzzer-gdb 是基于 gdb-9.2 修改的调试工具,主要用于 trapfuzzer 的组件,对二进制程序进行 Fuzzing。
项目地址:https://github.com/hac425xxx/trapfuzzer-gdb
二、GDB 核心工作机制
1. 基本架构
- 在 Linux 平台下,GDB 使用
ptrace进行进程调试 - GDB 中称被调试进程为
inferior - 采用事件驱动模型,
start_event_loop是事件循环入口
2. 关键函数调用链
main() → gdb_main() → captured_main() → captured_command_loop() → start_event_loop()
3. 核心事件处理
fetch_inferior_event: 处理 inferior 的各种事件(进程创建、SO 加载、断点命中、信号接收等)handle_inferior_event: 具体事件分发处理handle_signal_stop: 处理信号和断点事件
4. 信号处理机制
当进程命中断点或观察点时:
- GDB 取消断点/观察点
- 单步步过触发指令
- 恢复断点/观察点
- 继续执行
关键标志设置:
ecs->event_thread->stepping_over_breakpoint = 1 // 断点
ecs->event_thread->stepping_over_watchpoint = 1 // 观察点
5. 处理函数返回方式
keep_going: 以 continue 方式恢复执行stop_waiting: 暂停等待用户输入prepare_to_wait: 进程已运行,GDB 进入事件循环
三、功能扩展实现
1. SO 注入功能
命令注册
c = add_com("inject-so", class_run, inject_so_cmd, _("inject so to process.\n" RUN_ARGS_HELP));
set_cmd_completer(c, filename_completer);
实现原理
- 获取目标函数地址
struct value *get_func_value(char* module_name, char* func_name) {
// 获取模块中函数地址
}
- 调用目标函数
CORE_ADDR target_call_malloc(CORE_ADDR size) {
struct value *func = get_func_value("libc", "malloc");
// 调用函数并获取返回值
}
- SO 注入流程
CORE_ADDR target_load_library(char* library) {
// 1. 分配内存存放库路径
// 2. 写入库路径
// 3. 调用__libc_dlopen_mode加载
// 4. 释放内存
}
- SO 卸载实现
void unload_so_cmd(const char *args, int from_tty) {
// 遍历已加载SO列表
// 调用__libc_dlclose卸载
}
2. 软件内存断点
实现原理
- 将目标内存所在页面标记为不可读写
- 进程访问时触发 SEGV
- 判断访问地址是否在监控范围内
- 恢复页面权限并单步执行指令
- 重新设置页面权限
命令实现
void membrk_cmd(const char *args, int from_tty) {
// 解析参数:地址、大小、权限(r/w/x)
// 计算页面边界
// 调用mprotect修改权限
// 保存断点信息
}
核心处理逻辑
if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_SEGV) {
// 获取访问地址
CORE_ADDR access = parse_and_eval_long("$_siginfo._sifields._sigfault.si_addr");
// 判断是否命中监控范围
if (access >= info->address - instr_access_size &&
access <= info->address + info->length + instr_access_size) {
// 恢复页面权限
target_call_mprotect(info->page_address, info->page_size, 7);
// 设置单步执行标志
ecs->event_thread->stepping_over_watchpoint = 1;
keep_going(ecs);
}
}
3. 覆盖率收集功能
基本块信息加载
static void load_trapfuzzer_info(const char *args, int from_tty) {
// 解析IDA导出的基本块信息
// 保存到全局列表cov_mod_info_list
}
断点处理流程
if (cmi != NULL) {
unsigned int voff = pc - cmi->image_base;
// 退出点处理
if (is_exit_bb(voff)) {
// 根据模式决定停止或继续
}
// 命中基本块断点
if (cmi->bb_info_map[voff] != NULL) {
// 恢复原始指令
target_write_memory(pc, info->instr, info->instr_size);
// 记录执行轨迹
cmi->bb_trace.push_back(voff);
// 继续执行
keep_going(ecs);
}
}
四、trapfuzzer 集成
工作流程
- trapfuzzer 通过 GdbRunTracer.trace 请求执行用例
- GdbRunTracer 通知 trapfuzzer-gdb 启动测试进程
- trapfuzzer-gdb 处理断点事件和崩溃事件
- 返回执行结果(基本块信息、崩溃信息等)
五、关键数据结构
1. 内存断点信息
typedef struct {
CORE_ADDR address; // 监控起始地址
unsigned int length; // 监控长度
int prot; // 监控权限
CORE_ADDR page_address; // 页面起始地址
unsigned int page_size; // 页面大小
} MEM_BRK_INFO;
2. 覆盖率模块信息
typedef struct {
char module_name[256]; // 模块名
CORE_ADDR image_base; // 基地址
CORE_ADDR image_end; // 结束地址
char full_path[1024]; // 完整路径
unsigned int rva_size; // RVA大小
int mod_id; // 模块ID
// 基本块信息映射表
std::map<unsigned int, BB_INFO*> bb_info_map;
// 执行轨迹
std::vector<unsigned int> bb_trace;
} COV_MOD_INFO;
3. 基本块信息
typedef struct {
unsigned int voff; // 虚拟偏移
unsigned int instr_size; // 指令大小
unsigned int type; // 类型
unsigned char instr[16]; // 原始指令
} BB_INFO;
六、编译与使用
1. 编译注意事项
- 基于 gdb-9.2 源码修改
- 需要支持 C++11 的编译器
- 依赖标准模板库
2. 主要命令列表
| 命令 | 功能描述 |
|---|---|
| inject-so | 注入指定SO到目标进程 |
| unload-so | 卸载所有注入的SO |
| membrk | 设置软件内存断点 |
| load-trapfuzzer-info | 加载IDA导出的基本块信息 |
七、性能优化建议
-
软件内存断点:
- 尽量减少监控范围
- 合并相邻的监控区域
-
覆盖率收集:
- 预加载所有模块信息
- 使用哈希表加速基本块查找
-
SO注入:
- 缓存常用函数地址
- 批量处理注入请求
八、扩展开发指南
1. 添加新命令
- 使用
add_com注册命令 - 实现命令处理函数
- 可选设置命令补全器
2. 修改信号处理
- 在
handle_signal_stop中添加自定义逻辑 - 根据条件返回
keep_going或stop_waiting
3. 添加新事件类型
- 扩展
fetch_inferior_event处理逻辑 - 添加对应的事件处理函数
- 更新事件分发逻辑
九、常见问题解决
-
SO注入失败:
- 检查目标进程是否加载了libc
- 验证路径是否正确
- 检查内存分配是否成功
-
软件内存断点不触发:
- 确认页面权限修改成功
- 检查监控范围是否正确
- 验证信号处理逻辑
-
覆盖率数据不完整:
- 确认基本块信息加载正确
- 检查模块基地址是否正确
- 验证断点设置逻辑