3. 环境搭建(下):AFL/AFL++核心Fuzzer的编译与安装,LibFuzzer的集成方式,Docker镜像封装
上一章我们把基础工具链搭好了。这一章,咱们来搞定真正的核心——Fuzzer本身。
说实话,我见过不少新手在环境搭建这一步就卡住了。不是编译报错,就是版本冲突。我自己也踩过不少坑,所以这一章我会把那些容易翻车的地方都指出来。
3.1 AFL与AFL++:从源码编译到安装
AFL(American Fuzzy Lop)是Fuzz界的经典之作。但说实话,原版AFL已经很久没更新了。我个人建议直接用AFL++,它是社区维护的增强版,修复了大量bug,还支持更多特性。
3.1.1 编译AFL++
先拉代码:
git clone https://github.com/AFLplusplus/AFLplusplus.git
cd AFLplusplus
然后编译。这里要注意,AFL++的编译选项挺多的,我一般这样配:
make all
嗯,就这么简单?其实不是。默认编译会检测你的CPU架构,自动开启一些优化。但如果你在交叉编译环境里,就得手动指定了。
编译完成后,安装:
sudo make install
验证一下:
afl-fuzz --version
看到版本号就说明装好了。
3.1.2 核心组件说明
AFL++安装后会提供几个关键工具:
| 工具名 | 作用 |
|---|---|
| afl-gcc / afl-clang | 带插桩的编译器,用于编译被测程序 |
| afl-fuzz | 主Fuzzer,负责生成变异输入并执行 |
| afl-showmap | 显示单个输入的执行路径覆盖 |
| afl-tmin | 测试用例最小化工具 |
| afl-cmin | 语料库精简工具 |
我曾经在项目里遇到过一个问题:用afl-gcc编译的程序跑起来没问题,但afl-fuzz就是报错。后来发现是编译时忘了加-fno-omit-frame-pointer。这个坑我记了很久。
3.1.3 插桩模式的选择
AFL++支持多种插桩模式。我整理了一个对比表:
| 模式 | 编译命令 | 特点 | 适用场景 |
|---|---|---|---|
| LLVM模式 | afl-clang-fast | 性能最好,支持LTO | 推荐首选 |
| GCC模式 | afl-gcc-fast | 兼容性好 | LLVM不可用时 |
| QEMU模式 | afl-fuzz -Q | 无需源码,二进制级别插桩 | 闭源程序、异架构 |
| Unicorn模式 | afl-fuzz -U | 模拟执行,可插桩任意架构 | 异架构Pwn题 |
对于异架构Pwn题,QEMU模式和Unicorn模式是重点。后面章节会详细讲。
3.2 LibFuzzer的集成方式
LibFuzzer是LLVM项目自带的Fuzzer。它和AFL的思路不太一样——LibFuzzer是库的形式,直接链接到被测程序里。
3.2.1 基本集成步骤
假设你有一个简单的测试函数:
// test_fuzz.cc
#include <stdint.h>
#include <stddef.h>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
// 你的测试逻辑
if (Size > 0 && Data[0] == 'H') {
if (Size > 1 && Data[1] == 'I') {
if (Size > 2 && Data[2] == '!') {
__builtin_trap(); // 模拟崩溃
}
}
}
return 0;
}
编译命令:
clang++ -fsanitize=fuzzer -o test_fuzz test_fuzz.cc
就这么简单。运行:
./test_fuzz
LibFuzzer会自动开始生成输入并测试。
3.2.2 与AFL++的对比
很多人问我:到底该用AFL++还是LibFuzzer?我的看法是:
- AFL++:适合黑盒测试,不需要改源码,支持多核并行,异架构支持好
- LibFuzzer:适合白盒测试,需要写fuzz入口函数,启动快,内存开销小
在实际项目中,我经常两个一起用。先用LibFuzzer快速跑一轮,找出明显的bug。再用AFL++做长时间深度Fuzz。
3.3 Docker镜像封装
环境搭好了,但换个机器就得重来一遍?太麻烦了。Docker就是用来解决这个问题的。
3.3.1 编写Dockerfile
我一般这样写:
FROM ubuntu:22.04
# 安装基础依赖
RUN apt-get update && apt-get install -y \
build-essential \
git \
clang \
llvm \
lld \
python3 \
python3-pip \
qemu-user \
binfmt-support \
&& rm -rf /var/lib/apt/lists/*
# 编译安装AFL++
RUN git clone https://github.com/AFLplusplus/AFLplusplus.git /opt/AFLplusplus \
&& cd /opt/AFLplusplus \
&& make all \
&& make install
# 设置环境变量
ENV AFL_PATH=/opt/AFLplusplus
ENV PATH=$PATH:/opt/AFLplusplus
# 创建工作目录
WORKDIR /workspace
构建镜像:
docker build -t fuzz-env:latest .
3.3.2 运行容器
启动容器:
docker run -it --rm \
-v $(pwd):/workspace \
--cap-add=SYS_PTRACE \
--security-opt seccomp=unconfined \
fuzz-env:latest
3.3.3 多架构支持
对于异架构Pwn题,我们需要在Docker里安装QEMU用户态模拟器:
# 在Dockerfile中添加
RUN apt-get install -y qemu-user binfmt-support
# 注册binfmt支持
RUN update-binfmts --enable qemu-aarch64
这样,容器就能直接运行ARM64、MIPS等架构的二进制文件了。
3.4 知识体系总览
下面这张图把本章的核心内容串起来了:
3.5 验证环境是否可用
装好之后,我建议你跑一个简单的测试来验证:
# 创建一个测试程序
echo 'int main() { return 0; }' > test.c
# 用afl-clang-fast编译
afl-clang-fast -o test test.c
# 创建输入目录
mkdir -p input output
echo "hello" > input/seed.txt
# 启动Fuzz(只跑几秒验证)
afl-fuzz -i input -o output -t 1000 ./test
如果看到AFL的统计界面正常显示,说明环境没问题。
好了,环境搭建到这里就告一段落了。工具链、Fuzzer、Docker镜像都准备好了。接下来,我们就可以真正开始Fuzz了。