AFLGO的源码阅读(一)
字数 1678 2025-09-01 11:26:03
AFLGO源码分析与使用指南
1. AFLGO概述
AFLGO是一个基于AFL(American Fuzzy Lop)的定向模糊测试工具,它通过LLVM插桩和静态分析技术,将模糊测试引导至目标程序中的特定位置。本指南将详细解析AFLGO的核心实现原理和使用方法。
2. 距离计算机制
2.1 距离计算流程
AFLGO的核心是计算程序各基本块到目标位置的距离,主要流程如下:
-
生成控制流图(CFG)和调用图(CG):
- 使用LLVM的
opt工具生成每个模块的调用图 - 通过
merge_callgraphs.py合并所有模块的调用图
- 使用LLVM的
-
距离计算:
- 使用
distance.py计算CG中的函数间距离 - 对每个函数的CFG进行基本块间的距离计算
- 合并所有距离信息到
distance.cfg.txt文件
- 使用
2.2 gen_distance_orig.sh脚本分析
该脚本负责生成目标项目中各块到目标位置的距离:
# 参数检查
if [ $# -lt 2 ]; then
echo "Usage: $0 <binaries-directory> <temporary-directory> [<fuzzer-name>]"
exit 1
fi
# 解析符号链接
BINARIES=$(readlink -f "$1")
TMPDIR=$(readlink -f "$2")
# 查找AFLGO路径
AFLGO=$(readlink -f "$(dirname "$(readlink -f "$0")")/../..")
关键步骤:
- 检查输入参数和路径有效性
- 查找目标项目中的
.bc文件(LLVM bitcode文件) - 构建CFG和CG
- 计算并生成距离信息文件
3. 编译与插桩
3.1 编译选项
AFLGO的第一次编译需要特殊选项:
export TMP_DIR="$TMP_DIR" # 临时文件目录
export CC="$AFLGO/aflgo-clang" # AFLGO包装的clang
export CXX="$AFLGO/aflgo-clang++" # AFLGO包装的clang++
export CFLAGS="-g -fstack-protector-all -fno-omit-frame-pointer -Wno-error"
这些选项确保:
- 保留调试信息(
-g) - 启用栈保护(
-fstack-protector-all) - 保留帧指针(
-fno-omit-frame-pointer) - 将警告保持为警告(
-Wno-error)
3.2 aflgo-clang分析
aflgo-clang是AFLGO对clang的包装,主要功能:
-
参数处理:
- 检查参数有效性
- 处理用户传入的编译选项
-
Pass加载:
-Xclang -load -Xclang $AFLGO/llvm-pass/aflgo-llvm-pass.so加载AFLGO的LLVM Pass进行插桩
-
特殊选项处理:
-distance、-targets、-outdir等AFLGO特有选项- 架构位数设置
- ASAN/MSAN消杀器选项处理
-
优化选项:
- 默认添加
-g和-funroll-loops - 可禁用内置函数优化(
-fno-builtin-strcmp等)
- 默认添加
3.3 运行时库(aflgo-runtime.o)
AFLGO的运行时库提供关键功能:
-
共享内存初始化:
__afl_map_shm() { __afl_area_ptr = (u8 *)shmat(__afl_global_mem_fd, NULL, 0); } -
forkserver实现:
__afl_start_forkserver() { // 与fuzzer通信 // fork子进程 // 处理竞态条件 } -
持久化模式:
__afl_persistent_loop(unsigned int max_cnt) { // 初始化循环 // 执行目标代码 // 挂起等待下一次执行 } -
延迟初始化:
__attribute__((constructor(CONST_PRIO))) void __afl_auto_init(void) { if (!__afl_manual_control) __afl_start_forkserver(); }
4. 插桩技术
4.1 SanitizerCoverage
LLVM提供的高级插桩方式:
// 每条边的插桩点
void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
__afl_area_ptr[*guard]++;
}
// 初始化
void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) {
// 设置插桩率
// 初始化边ID
}
4.2 AFLGO特有插桩
-
基本块记录:
void writeBB(char *bb_name) { // 拼接路径上下文 // 哈希去重 // 记录到文件 } -
哈希集合实现:
hashset_t *hashset_create(size_t size); size_t hashset_num_items(hashset_t *set); void hashset_destroy(hashset_t *set); bool hashset_add_member(hashset_t *set, const char *key);
5. 特殊模式实现
5.1 持久化模式(Persistent Mode)
实现原理:
#define __AFL_LOOP(_A) \
({ static volatile char *_B __attribute__((used)); \
_B = (char*)"##SIG_AFL_PERSIST##"; \
__afl_persistent_loop(_A); })
使用方法:
while (__AFL_LOOP(1000)) {
// 测试代码
}
优点:
- 减少fork开销
- 单次fork执行多次测试
5.2 延迟模式(Deferred Mode)
实现原理:
#define __AFL_INIT() \
do { __afl_manual_init(); } while (0)
使用方法:
// 初始化代码
__AFL_INIT();
// 测试代码
优点:
- 控制forkserver启动时机
- 减少不必要的初始化开销
6. 使用流程总结
-
第一次编译:
- 使用特殊编译选项生成bitcode文件
- 保留调试信息和必要的保护选项
-
距离计算:
- 运行
gen_distance_orig.sh生成距离信息 - 得到
distance.cfg.txt和distance.callgraph.txt
- 运行
-
定向模糊测试:
- 使用距离信息引导fuzzer
- 可选择持久化或延迟模式优化性能
-
结果分析:
- 检查覆盖率信息
- 分析发现的漏洞
7. 关键文件说明
| 文件 | 用途 |
|---|---|
aflgo-clang |
包装的编译器前端 |
aflgo-llvm-pass.so |
LLVM插桩Pass |
aflgo-runtime.o |
运行时库 |
gen_distance_orig.sh |
距离计算脚本 |
distance.py |
距离计算核心 |
merge_callgraphs.py |
调用图合并工具 |
通过本指南,您应该能够深入理解AFLGO的工作原理并有效使用它进行定向模糊测试。如需进一步优化,可以调整距离计算算法或插桩策略。