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. 信号处理机制

当进程命中断点或观察点时:

  1. GDB 取消断点/观察点
  2. 单步步过触发指令
  3. 恢复断点/观察点
  4. 继续执行

关键标志设置:

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

实现原理

  1. 获取目标函数地址
struct value *get_func_value(char* module_name, char* func_name) {
    // 获取模块中函数地址
}
  1. 调用目标函数
CORE_ADDR target_call_malloc(CORE_ADDR size) {
    struct value *func = get_func_value("libc", "malloc");
    // 调用函数并获取返回值
}
  1. SO 注入流程
CORE_ADDR target_load_library(char* library) {
    // 1. 分配内存存放库路径
    // 2. 写入库路径
    // 3. 调用__libc_dlopen_mode加载
    // 4. 释放内存
}
  1. SO 卸载实现
void unload_so_cmd(const char *args, int from_tty) {
    // 遍历已加载SO列表
    // 调用__libc_dlclose卸载
}

2. 软件内存断点

实现原理

  1. 将目标内存所在页面标记为不可读写
  2. 进程访问时触发 SEGV
  3. 判断访问地址是否在监控范围内
  4. 恢复页面权限并单步执行指令
  5. 重新设置页面权限

命令实现

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 集成

工作流程

  1. trapfuzzer 通过 GdbRunTracer.trace 请求执行用例
  2. GdbRunTracer 通知 trapfuzzer-gdb 启动测试进程
  3. trapfuzzer-gdb 处理断点事件和崩溃事件
  4. 返回执行结果(基本块信息、崩溃信息等)

五、关键数据结构

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导出的基本块信息

七、性能优化建议

  1. 软件内存断点:

    • 尽量减少监控范围
    • 合并相邻的监控区域
  2. 覆盖率收集:

    • 预加载所有模块信息
    • 使用哈希表加速基本块查找
  3. SO注入:

    • 缓存常用函数地址
    • 批量处理注入请求

八、扩展开发指南

1. 添加新命令

  1. 使用 add_com 注册命令
  2. 实现命令处理函数
  3. 可选设置命令补全器

2. 修改信号处理

  1. handle_signal_stop 中添加自定义逻辑
  2. 根据条件返回 keep_goingstop_waiting

3. 添加新事件类型

  1. 扩展 fetch_inferior_event 处理逻辑
  2. 添加对应的事件处理函数
  3. 更新事件分发逻辑

九、常见问题解决

  1. SO注入失败

    • 检查目标进程是否加载了libc
    • 验证路径是否正确
    • 检查内存分配是否成功
  2. 软件内存断点不触发

    • 确认页面权限修改成功
    • 检查监控范围是否正确
    • 验证信号处理逻辑
  3. 覆盖率数据不完整

    • 确认基本块信息加载正确
    • 检查模块基地址是否正确
    • 验证断点设置逻辑
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. 关键函数调用链 3. 核心事件处理 fetch_inferior_event : 处理 inferior 的各种事件(进程创建、SO 加载、断点命中、信号接收等) handle_inferior_event : 具体事件分发处理 handle_signal_stop : 处理信号和断点事件 4. 信号处理机制 当进程命中断点或观察点时: GDB 取消断点/观察点 单步步过触发指令 恢复断点/观察点 继续执行 关键标志设置: 5. 处理函数返回方式 keep_going : 以 continue 方式恢复执行 stop_waiting : 暂停等待用户输入 prepare_to_wait : 进程已运行,GDB 进入事件循环 三、功能扩展实现 1. SO 注入功能 命令注册 实现原理 获取目标函数地址 调用目标函数 SO 注入流程 SO 卸载实现 2. 软件内存断点 实现原理 将目标内存所在页面标记为不可读写 进程访问时触发 SEGV 判断访问地址是否在监控范围内 恢复页面权限并单步执行指令 重新设置页面权限 命令实现 核心处理逻辑 3. 覆盖率收集功能 基本块信息加载 断点处理流程 四、trapfuzzer 集成 工作流程 trapfuzzer 通过 GdbRunTracer.trace 请求执行用例 GdbRunTracer 通知 trapfuzzer-gdb 启动测试进程 trapfuzzer-gdb 处理断点事件和崩溃事件 返回执行结果(基本块信息、崩溃信息等) 五、关键数据结构 1. 内存断点信息 2. 覆盖率模块信息 3. 基本块信息 六、编译与使用 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 验证路径是否正确 检查内存分配是否成功 软件内存断点不触发 : 确认页面权限修改成功 检查监控范围是否正确 验证信号处理逻辑 覆盖率数据不完整 : 确认基本块信息加载正确 检查模块基地址是否正确 验证断点设置逻辑