AFL源码学习(二)
字数 1798 2025-08-22 12:23:24
AFL源码学习与工具分析教学文档
一、AFL基础架构回顾
1.1 AFL整体工作流程
-
编译插桩阶段:
- AFL作为源编译器的封装
- 在编译成汇编代码后进行插桩
- 插桩后的程序会在第一个桩处停下并初始化共享内存
-
运行阶段:
- 形成fork server模式
- 主进程不断fork子进程进行探索
- 子进程通过管道向afl-fuzz汇报结果
1.2 关键数据结构
- 共享内存(shm):65536字节大小,用于存储覆盖率信息
- 分桶机制:8个hit count桶(1,2,3,4-7,8-15,16-31,32-127,128+)
二、afl-tmin分析
2.1 功能概述
- 测试用例最小化器
- 两种模式:
- non-crash模式:保持覆盖率不变(需插桩)
- crash模式:保持程序崩溃状态(可不插桩)
2.2 核心工作流程
-
初始化阶段:
- 参数解析与设置
- 共享内存初始化(
setup_shm) - 信号处理设置
- 环境变量处理
-
dry run:
- 检查程序是否超时或崩溃
- 通过
run_target函数执行
-
最小化阶段:
- 调用
minimize函数进行优化
- 调用
2.3 run_target函数详解
-
执行流程:
- 清空共享内存区域
- 写入input文件
- fork子进程
- 重定向流
- 创建新进程组
- 设置内存限制
- 禁用core dump
- 执行目标程序
- 主进程:
- 设置定时器
- 等待子进程结束
- 对shm进行分桶(
classify_counts) - 处理崩溃情况
- 计算hash决定是否保留input
-
分桶机制实现:
static const u8 count_class_lookup8[256] = {
[0] = 0,
[1] = 1,
[2] = 2,
[3] = 3,
[4 ... 7] = 4,
[8 ... 15] = 5,
[16 ... 31] = 6,
[32 ... 127] = 7,
[128 ... 255] = 8
};
2.4 minimize函数优化策略
-
BLOCK NORMALIZATION:
- 将数据分为128个块(2的幂次)
- 尝试将整个块替换为'0'
- 若路径相同则保留更改
-
BLOCK DELETION:
- 将数据分为16个块
- 从前向后尝试删除每个块
- 减半删除长度重复操作
- 加速逻辑:跳过与前一块相同的块
-
ALPHABET MINIMIZATION:
- 字符集最小化
- 将相同字符替换为'0'并尝试删除
-
CHARACTER MINIMIZATION:
- 字符最小化
- 直接替换当前字符为'0'并尝试删除
三、afl-showmap分析
3.1 功能概述
- 分析和显示程序运行时访问的代码路径
- 显示共享内存中的覆盖率信息
3.2 核心实现
-
运行流程:
- 参数解析
- 共享内存初始化
- 环境设置
- 执行
run_target - 通过
write_results输出结果
-
输出模式:
- 用户模式:输出0-8的自然数
- 二进制模式:使用标准分桶机制
四、afl-analyze分析
4.1 功能概述
- 分析输入文件结构
- 通过字节翻转识别关键字段
- 可识别magic number、checksum、length等
4.2 核心实现
-
analyze函数:
- 对每个字节进行4种变异:
- xor 0xff
- xor 0x1
- sub 0x10
- add 0x10
- 分类响应类型:
- 完全无响应(不重要)
- 部分无响应(不关键)
- 固定响应(固定)
- 可变响应(敏感)
- 对每个字节进行4种变异:
-
dump_hex函数:
- 字段类型识别:
- 2字节且值<=输入长度:RESP_LEN
- 2字节且差值>32:RESP_CKSUM
- 4字节且值<=输入长度:RESP_LEN
- 4字节且最高位不同:RESP_CKSUM
- [1,32)字节(非2/4):保持原类型
- ≥32字节:数据区域
- 字段类型识别:
五、关键问题解析
5.1 分桶机制的意义
- 避免corpus爆炸:不区分100次和101次循环
- 保持搜索能力:区分0-4次循环,7-8次循环
- 平衡策略:在循环次数增加时给予适当奖励
5.2 共享内存初始化
void setup_shm() {
shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600);
setenv("__AFL_SHM_ID", shm_str, 1);
trace_bits = shmat(shm_id, NULL, 0);
}
5.3 覆盖率反馈实现
- 插桩代码计算基本块ID:
movl $0xdeadbeef, %edi xorl %eax, %eax call __afl_maybe_log - 共享内存更新:
cur_location = <COMPILE_TIME_RANDOM>; shared_mem[cur_location ^ prev_location]++; prev_location = cur_location >> 1;
六、实践应用
6.1 编译注意事项
- 使用
AFL_DONT_OPTIMIZE=1防止优化移除插桩AFL_DONT_OPTIMIZE=1 afl-gcc test.c -o test -g
6.2 典型用例
-
afl-tmin:
afl-tmin -i input -o minimized_output -- ./target -
afl-showmap:
afl-showmap -o coverage.txt -- ./target -
afl-analyze:
afl-analyze -i test_case -- ./target
七、总结与深入方向
7.1 核心要点
- AFL通过插桩和共享内存实现高效反馈
- 分桶机制平衡了路径敏感性和性能
- 工具链协同工作形成完整模糊测试生态
7.2 深入方向
- afl-fuzz核心算法
- 并行化fuzzing实现
- 自定义变异策略
- 与符号执行结合
通过深入理解这些工具的实现原理,可以更好地定制和优化模糊测试流程,提高漏洞挖掘效率。