AFL++模糊测试Skill aflpp

AFL++ 是一个用于软件安全测试的模糊测试工具,特别适用于 C/C++ 项目的多核模糊测试,能够高效发现漏洞,提高代码覆盖率,并支持多样化变异策略和成熟工具集成,是网络安全和软件测试领域的重要技能。

漏洞挖掘 0 次安装 0 次浏览 更新于 3/24/2026

名称: aflpp 类型: 模糊测试器 描述: > AFL++ 是 AFL 的一个分支,具有更好的模糊测试性能和高级功能。 用于 C/C++ 项目的多核模糊测试。

AFL++

AFL++ 是原始 AFL 模糊测试器的一个分支,提供更好的模糊测试性能和更多高级功能,同时保持稳定性。相比 libFuzzer 的一个主要优势是 AFL++ 稳定支持在多核上运行模糊测试活动,使其成为大规模模糊测试的理想选择。

何时使用

模糊测试器 最适合 复杂性
AFL++ 多核模糊测试、多样化变异、成熟项目 中等
libFuzzer 快速设置、单线程、简单测试工具
LibAFL 自定义模糊测试器、研究、高级用例

选择 AFL++ 当:

  • 您需要多核模糊测试以最大化吞吐量
  • 您的项目可以用 Clang 或 GCC 编译
  • 您想要多样化的变异策略和成熟的工具
  • libFuzzer 已经达到平台期,您需要更多覆盖率
  • 您正在模糊测试生产代码库,受益于并行执行

快速入门

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    // 用模糊测试器提供的数据调用您的代码
    check_buf((char*)data, size);
    return 0;
}

编译和运行:

# 首先设置 AFL++ 包装脚本(见安装)
./afl++ docker afl-clang-fast++ -DNO_MAIN=1 -O2 -fsanitize=fuzzer harness.cc main.cc -o fuzz
mkdir seeds && echo "aaaa" > seeds/minimal_seed
./afl++ docker afl-fuzz -i seeds -o out -- ./fuzz

安装

AFL++ 有许多依赖项,包括 LLVM、Python 和 Rust。我们建议使用当前的 Debian 或 Ubuntu 发行版进行 AFL++ 模糊测试。

方法 何时使用 支持的编译器
Ubuntu/Debian 仓库 较新 Ubuntu,仅基本功能 Ubuntu 23.10: Clang 14 & GCC 13<br>Debian 12: Clang 14 & GCC 12
Docker(来自 Docker Hub) 特定 AFL++ 版本,Apple Silicon 支持 截至 4.35c: Clang 19 & GCC 11
Docker(从源码) 测试未发布功能,应用补丁 在 Dockerfile 中可配置
从源码 避免 Docker,需要特定补丁 通过 LLVM_CONFIG 环境变量可调

Ubuntu/Debian

在安装 afl++ 之前,用 apt-cache show afl++ 检查包的 clang 版本依赖,并安装匹配的 lld 版本(例如 lld-17)。

apt install afl++ lld-17

Docker(来自 Docker Hub)

docker pull aflplusplus/aflplusplus:stable

Docker(从源码)

git clone --depth 1 --branch stable https://github.com/AFLplusplus/AFLplusplus
cd AFLplusplus
docker build -t aflplusplus .

从源码

参考 Dockerfile 获取 Ubuntu 版本要求和依赖项。设置 LLVM_CONFIG 以指定 Clang 版本(例如 llvm-config-18)。

包装脚本设置

创建一个包装脚本以在主机或 Docker 上运行 AFL++:

cat <<'EOF' > ./afl++
#!/bin/sh
AFL_VERSION="${AFL_VERSION:-"stable"}"
case "$1" in
   host)
        shift
        bash -c "$*"
        ;;
    docker)
        shift
        /usr/bin/env docker run -ti \
            --privileged \
            -v ./:/src \
            --rm \
            --name afl_fuzzing \
            "aflplusplus/aflplusplus:$AFL_VERSION" \
            bash -c "cd /src && bash -c \"$*\""
        ;;
    *)
        echo "用法: $0 {host|docker}"
        exit 1
        ;;
esac
EOF
chmod +x ./afl++

安全警告: afl-system-configafl-persistent-config 脚本需要 root 权限并禁用操作系统安全功能。不要在生产系统或您的开发环境中进行模糊测试。使用专用虚拟机。

系统配置

每次重启后运行以获得每秒高达 15% 的更多执行次数:

./afl++ <host/docker> afl-system-config

为了最大性能,禁用内核安全缓解(需要 grub 引导加载器,不支持 Docker):

./afl++ host afl-persistent-config
update-grub
reboot
./afl++ <host/docker> afl-system-config

cat /proc/cmdline 验证 - 输出应包含 mitigations=off

编写测试工具

测试工具结构

AFL++ 支持 libFuzzer 风格的工具:

#include <stdint.h>
#include <stddef.h>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    // 1. 如果需要,验证输入大小
    if (size < MIN_SIZE || size > MAX_SIZE) return 0;

    // 2. 用模糊数据调用目标函数
    target_function(data, size);

    // 3. 返回 0(非零保留供将来使用)
    return 0;
}

测试工具规则

不做
运行间重置全局状态 依赖先前运行的状态
优雅处理边界情况 在无效输入时退出
保持工具确定性 使用随机数生成器
释放分配的内存 创建内存泄漏
验证输入大小 处理无界输入

另见: 有关详细工具编写技术、处理复杂输入的模式和高级策略,请参见 fuzz-harness-writing 技术技能。

编译

AFL++ 提供多种编译模式,具有不同的权衡。

编译模式决策树

选择您的编译模式:

  • LTO 模式 (afl-clang-lto): 最佳性能和工具化。先尝试此模式。
  • LLVM 模式 (afl-clang-fast): 如果 LTO 编译失败,回退到此模式。
  • GCC 插件 (afl-gcc-fast): 用于需要 GCC 的项目。

基本编译(LLVM 模式)

./afl++ <host/docker> afl-clang-fast++ -DNO_MAIN=1 -O2 -fsanitize=fuzzer harness.cc main.cc -o fuzz

GCC 编译

./afl++ <host/docker> afl-g++-fast -DNO_MAIN=1 -O2 -fsanitize=fuzzer harness.cc main.cc -o fuzz

重要: GCC 版本必须与用于编译 AFL++ GCC 插件的版本匹配。

使用消毒剂

./afl++ <host/docker> AFL_USE_ASAN=1 afl-clang-fast++ -DNO_MAIN=1 -O2 -fsanitize=fuzzer harness.cc main.cc -o fuzz

另见: 有关详细消毒剂配置、常见问题和高级标志,请参见 address-sanitizerundefined-behavior-sanitizer 技术技能。

构建标志

注意 -g 不是必需的,默认由 AFL++ 编译器添加。

标志 目的
-DNO_MAIN=1 使用 libFuzzer 工具时跳过主函数
-O2 生产优化级别(推荐用于模糊测试)
-fsanitize=fuzzer 启用 libFuzzer 兼容模式并在链接可执行文件时添加模糊测试运行时
-fsanitize=fuzzer-no-link 工具化而不链接模糊测试运行时(用于静态库和对象文件)

语料库管理

创建初始语料库

AFL++ 需要至少一个非空种子文件:

mkdir seeds
echo "aaaa" > seeds/minimal_seed

对于真实项目,收集代表性输入:

  • 下载您模糊测试格式的示例文件
  • 从项目的测试套件中提取测试案例
  • 使用您文件格式的最小有效输入

语料库最小化

活动后,最小化语料库以仅保留唯一覆盖率:

./afl++ <host/docker> afl-cmin -i out/default/queue -o minimized_corpus -- ./fuzz

另见: 有关语料库创建策略、字典和种子选择,请参见 fuzzing-corpus 技术技能。

运行活动

基本运行

./afl++ <host/docker> afl-fuzz -i seeds -o out -- ./fuzz

设置环境变量

./afl++ <host/docker> AFL_FAST_CAL=1 afl-fuzz -i seeds -o out -- ./fuzz

解释输出

AFL++ UI 显示实时模糊测试统计:

输出 含义
execs/sec 执行速度 - 越高越好
cycles done 完成的队列通过次数
corpus count 队列中唯一测试案例数
saved crashes 发现的唯一崩溃数
stability 稳定边缘百分比(应接近 100%)

输出目录结构

out/default/
├── cmdline          # SUT 如何被调用?
├── crashes/         # 使 SUT 崩溃的输入
│   └── id:000000,sig:06,src:000002,time:286,execs:13105,op:havoc,rep:4
├── hangs/           # 使 SUT 挂起的输入
├── queue/           # 重现最终模糊测试器状态的测试案例
│   ├── id:000000,time:0,execs:0,orig:minimal_seed
│   └── id:000001,src:000000,time:0,execs:8,op:havoc,rep:6,+cov
├── fuzzer_stats     # 活动统计
└── plot_data        # 绘图数据

分析结果

查看实时活动统计:

./afl++ <host/docker> afl-whatsup out

创建覆盖率图:

apt install gnuplot
./afl++ <host/docker> afl-plot out/default out_graph/

重新执行测试案例

./afl++ <host/docker> ./fuzz out/default/crashes/<test_case>

模糊测试器选项

选项 目的
-G 4000 最大测试输入长度(默认:1048576 字节)
-t 1000 每个测试案例的超时时间(毫秒)(默认:1000ms)
-m 1000 内存限制(兆字节)(默认:0 = 无限)
-x ./dict.dict 使用字典文件指导变异

多核模糊测试

AFL++ 擅长多核模糊测试,有两个主要优势:

  1. 每秒更多执行次数(与物理核心数线性扩展)
  2. 不对称模糊测试(例如,一个 ASan 作业,其余无消毒剂)

开始活动

启动主模糊测试器(在后台):

./afl++ <host/docker> afl-fuzz -M primary -i seeds -o state -- ./fuzz 1>primary.log 2>primary.error &

启动辅助模糊测试器(与核心数一样多):

./afl++ <host/docker> afl-fuzz -S secondary01 -i seeds -o state -- ./fuzz 1>secondary01.log 2>secondary01.error &
./afl++ <host/docker> afl-fuzz -S secondary02 -i seeds -o state -- ./fuzz 1>secondary02.log 2>secondary02.error &

监控多核活动

列出所有运行作业:

jobs

查看实时统计(每秒更新):

./afl++ <host/docker> watch -n1 --color afl-whatsup state/

停止所有模糊测试器

kill $(jobs -p)

覆盖率分析

AFL++ 通过边缘工具自动跟踪覆盖率。覆盖率信息存储在 fuzzer_statsplot_data 中。

测量覆盖率

使用 afl-plot 可视化时间上的覆盖率:

./afl++ <host/docker> afl-plot out/default out_graph/

改进覆盖率

  • 使用字典进行格式感知模糊测试
  • 运行更长时间的活动(cycles_wo_finds 表示平台期)
  • 尝试多核模糊测试的不同变异策略
  • 分析覆盖差距并添加针对性种子输入

另见: 有关详细覆盖率分析技术、识别覆盖差距和系统覆盖率改进,请参见 coverage-analysis 技术技能。

CMPLOG

CMPLOG/RedQueen 是任何模糊测试器中可用的最佳路径约束解决机制。 要启用它,模糊目标需要为其进行工具化。 在构建模糊测试目标之前设置环境变量:

./afl++ <host/docker> AFL_LLVM_CMPLOG=1 make

编译和链接工具无需特殊操作。

要运行一个带有 CMPLOG 工具化模糊测试目标的模糊测试器实例,将 -c0 添加到命令参数中:

./afl++ <host/docker> afl-fuzz -c0 -S cmplog -i seeds -o state -- ./fuzz 1>secondary02.log 2>secondary02.error &

消毒剂集成

消毒剂对于发现不导致立即崩溃的内存损坏错误至关重要。

AddressSanitizer(ASan)

./afl++ <host/docker> AFL_USE_ASAN=1 afl-clang-fast++ -DNO_MAIN=1 -O2 -fsanitize=fuzzer harness.cc main.cc -o fuzz

注意: 内存限制(-m)不支持 ASan,由于 20TB 虚拟内存预留。

UndefinedBehaviorSanitizer(UBSan)

./afl++ <host/docker> AFL_USE_UBSAN=1 afl-clang-fast++ -DNO_MAIN=1 -O2 -fsanitize=fuzzer,undefined harness.cc main.cc -o fuzz

常见消毒剂问题

问题 解决方案
ASan 减慢模糊测试 在多核设置中仅使用 1 个 ASan 作业
堆栈耗尽 使用 ASAN_OPTIONS=stack_size=... 增加堆栈
GCC 版本不匹配 确保系统 GCC 与 AFL++ 插件版本匹配

另见: 有关全面消毒剂配置和故障排除,请参见 address-sanitizer 技术技能。

高级用法

技巧和窍门

技巧 为什么有帮助
尽可能使用 LLVMFuzzerTestOneInput 工具 如果模糊测试活动至少有 85% 的稳定性,则这是最有效的模糊测试风格。否则,尝试标准输入或文件输入模糊测试
使用字典 帮助模糊测试器发现格式特定的关键词和魔术字节
设置现实超时 防止来自系统负载的误报
限制输入大小 更大输入不一定探索更多空间
监控稳定性 低稳定性表示非确定性行为

标准输入模糊测试

AFL++ 可以模糊测试从 stdin 读取的程序,无需 libFuzzer 工具:

./afl++ <host/docker> afl-clang-fast++ -O2 main_stdin.c -o fuzz_stdin
./afl++ <host/docker> afl-fuzz -i seeds -o out -- ./fuzz_stdin

这比持久模式慢,但不需要工具代码。

文件输入模糊测试

对于读取文件的程序,使用 @@ 占位符:

./afl++ <host/docker> afl-clang-fast++ -O2 main_file.c -o fuzz_file
./afl++ <host/docker> afl-fuzz -i seeds -o out -- ./fuzz_file @@

为获得更好性能,使用 fmemopen 从内存创建文件描述符。

参数模糊测试

使用 argv-fuzz-inl.h 模糊测试命令行参数:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef __AFL_COMPILER
#include "argv-fuzz-inl.h"
#endif

void check_buf(char *buf, size_t buf_len) {
    if(buf_len > 0 && buf[0] == 'a') {
        if(buf_len > 1 && buf[1] == 'b') {
            if(buf_len > 2 && buf[2] == 'c') {
                abort();
            }
        }
    }
}

int main(int argc, char *argv[]) {
#ifdef __AFL_COMPILER
    AFL_INIT_ARGV();
#endif

    if (argc < 2) {
        fprintf(stderr, "用法: %s <input_string>
", argv[0]);
        return 1;
    }

    char *input_buf = argv[1];
    size_t len = strlen(input_buf);
    check_buf(input_buf, len);
    return 0;
}

下载头文件:

curl -O https://raw.githubusercontent.com/AFLplusplus/AFLplusplus/stable/utils/argv_fuzzing/argv-fuzz-inl.h

编译和运行:

./afl++ <host/docker> afl-clang-fast++ -O2 main_arg.c -o fuzz_arg
./afl++ <host/docker> afl-fuzz -i seeds -o out -- ./fuzz_arg

性能调优

设置 影响
CPU 核心数 与物理核心数线性扩展
持久模式 比 fork 服务器快 10-20 倍
-G 输入大小限制 越小越快,但可能错过错误
ASan 比率 每 4-8 个非 ASan 作业 1 个 ASan 作业

现实世界示例

示例:libpng

模糊测试 libpng 演示了用静态库模糊测试 C 项目:

# 获取源码
curl -L -O https://downloads.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz
tar xf libpng-1.6.37.tar.xz
cd libpng-1.6.37/

# 安装依赖项
apt install zlib1g-dev

# 配置和构建静态库
export CC=afl-clang-fast CFLAGS=-fsanitize=fuzzer-no-link
export CXX=afl-clang-fast++ CXXFLAGS="$CFLAGS"
./configure --enable-shared=no
export AFL_LLVM_CMPLOG=1
export AFL_USE_ASAN=1
make

# 下载工具
curl -O https://raw.githubusercontent.com/glennrp/libpng/f8e5fa92b0e37ab597616f554bee254157998227/contrib/oss-fuzz/libpng_read_fuzzer.cc

# 链接模糊测试器
export AFL_USE_ASAN=1
$CXX -fsanitize=fuzzer libpng_read_fuzzer.cc .libs/libpng16.a -lz -o fuzz

# 准备种子和字典
mkdir seeds/
curl -o seeds/input.png https://raw.githubusercontent.com/glennrp/libpng/acfd50ae0ba3198ad734e5d4dec2b05341e50924/contrib/pngsuite/iftp1n3p08.png
curl -O https://raw.githubusercontent.com/glennrp/libpng/2fff013a6935967960a5ae626fc21432807933dd/contrib/oss-fuzz/png.dict

# 开始模糊测试
./afl++ <host/docker> afl-fuzz -i seeds -o out -- ./fuzz

示例:CMake 基项目

project(BuggyProgram)
cmake_minimum_required(VERSION 3.0)

add_executable(buggy_program main.cc)

add_executable(fuzz main.cc harness.cc)
target_compile_definitions(fuzz PRIVATE NO_MAIN=1)
target_compile_options(fuzz PRIVATE -O2 -fsanitize=fuzzer-no-link)
target_link_libraries(fuzz -fsanitize=fuzzer)

构建和模糊测试:

# 构建非工具化二进制
./afl++ <host/docker> cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ .
./afl++ <host/docker> cmake --build . --target buggy_program

# 构建模糊测试器
./afl++ <host/docker> cmake -DCMAKE_C_COMPILER=afl-clang-fast -DCMAKE_CXX_COMPILER=afl-clang-fast++ .
./afl++ <host/docker> cmake --build . --target fuzz

# 模糊测试
./afl++ <host/docker> afl-fuzz -i seeds -o out -- ./fuzz

故障排除

问题 原因 解决方案
低 exec/sec (<1k) 未使用持久模式 创建 LLVMFuzzerTestOneInput 风格工具
低稳定性 (<85%) 非确定性代码 通过 stdin 或文件输入模糊测试程序,或创建此类工具
GCC 插件错误 GCC 版本不匹配 确保系统 GCC 与 AFL++ 构建匹配并安装 gcc-$GCC_VERSION-plugin-dev
未发现崩溃 需要消毒剂 AFL_USE_ASAN=1 重新编译
内存限制超出 ASan 使用 20TB 虚拟内存 使用 ASan 时移除 -m 标志
Docker 性能损失 虚拟化开销 用于生产模糊测试时使用裸金属或虚拟机

相关技能

技术技能

技能 使用案例
fuzz-harness-writing 编写有效工具的详细指导
address-sanitizer 模糊测试期间的内存错误检测
undefined-behavior-sanitizer 检测未定义行为错误
fuzzing-corpus 构建和管理种子语料库
fuzzing-dictionaries 为格式感知模糊测试创建字典

相关模糊测试器

技能 何时考虑
libfuzzer 快速原型设计、单线程模糊测试足够
libafl 需要自定义变异器或研究级功能

资源

关键外部资源

AFL++ GitHub 仓库 官方仓库,包含全面文档、示例和问题跟踪器。

深入模糊测试 AFL++ 团队的高级文档,涵盖工具化模式、优化技术和高级用例。

AFL++ 幕后 技术深入探讨 AFL++ 内部机制、变异策略和覆盖率跟踪机制。

AFL++: 结合模糊测试研究的渐进步骤 描述 AFL++ 架构和与原始 AFL 相比的性能改进的研究论文。

视频资源