其他
标准 fuzzer 不够用时,用 LibAFL 构建自定义…
当 libFuzzer 和 AFL++ 缺乏所需的控制力,需要自定义变异/反馈、支持非标准目标或实现模糊测试研究想法时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 48/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 56/100 ×0.15
适用场景
标准 fuzzer 不够用时,用 Lib通用
解决思路
- 安装 Clang:`apt install clang`;或通过 apt.llvm.org 安装特定版本:`wget https://apt.llvm.org/llvm.sh`、`chmod +x llvm.sh`、`sudo ./llvm.sh 15`。
- 配置 Rust 环境:`export RUSTFLAGS="-C linker=/usr/bin/clang-15"`、`export CC="clang-15"`、`export CXX="clang++-15"`。
- 安装 Rust:`curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh`。
- 安装额外依赖:`apt install libssl-dev pkg-config`。
- 使用 libFuzzer 兼容模式时,安装 nightly Rust:`rustup toolchain install nightly --component llvm-tools`。
- 验证安装:进入 `LibAFL/libafl_libfuzzer_runtime`,运行 `./build.sh`,应生成 `libFuzzer.a`。
- 快速开始使用 libFuzzer 替换:定义 `extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)` 函数,调用目标代码并返回 0。
- 构建 LibAFL 的 libFuzzer 兼容层:`git clone https://github.com/AFLplusplus/LibAFL`、`cd LibAFL/libafl_libfuzzer_runtime`、`./build.sh`。
- 编译并运行 libFuzzer 替换:`clang++ -DNO_MAIN -g -O2 -fsanitize=fuzzer-no-link libFuzzer.a harness.cc main.cc -o fuzz`,然后 `./fuzz corpus/`。
- 长时间运行推荐:`./fuzz -fork=1 -ignore_crashes=1 corpus/`。
- 创建自定义 Fuzzer 作为 Rust 库:`cargo init --lib my_fuzzer`、`cd my_fuzzer`。
- 添加 LibAFL 依赖:`cargo add libafl@0.13 libafl_targets@0.13 libafl_bolts@0.13 libafl_cc@0.13 --features "libafl_targets@0.13/libfuzzer,libafl_targets@0.13/sancov_pcguard_hitcounts"`。
- 配置 `Cargo.toml`:添加 `[lib]` 和 `crate-type = ["staticlib"]`。
- 在自定义 Fuzzer 中设置 observers:`let edges_observer = HitcountsMapObserver::new(unsafe { std_edges_map_observer("edges") }).track_indices();` 以及 `let time_observer = TimeObserver::new("time");`。
- 定义 feedback:`feedback_or!(MaxMapFeedback::new(&edges_observer), TimeFeedback::new(&time_observer))`。
- 定义 objective:`feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new())`。
- 创建或恢复 state:`StdState::new(StdRand::new(), InMemoryCorpus::new(), OnDiskCorpus::new(&output_dir).unwrap(), &mut feedback, &mut objective).unwrap()`。
- 设置 mutator:`let mutator = StdScheduledMutator::new(havoc_mutations());` 和 `let mut stages = tuple_list!(StdMutationalStage::new(mutator));`。
- 设置 scheduler:`let scheduler = IndexesLenTimeMinimizerScheduler::new(&edges_observer, QueueScheduler::new());`。
- 创建 fuzzer:`let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);`。
- 定义 harness 闭包:`|input: &BytesInput| { let buf = input.target_bytes().as_slice(); libfuzzer_test_one_input(buf); ExitKind::Ok }`。
- 设置 executor:`InProcessExecutor::with_timeout(&mut harness, tuple_list!(edges_observer, time_observer), &mut fuzzer, &mut state, &mut restarting_mgr, timeout)?`。
- 加载初始输入:若 `state.must_load_initial_inputs()`,则 `state.load_initial_inputs(&mut fuzzer, &mut executor, &mut restarting_mgr, &input_dir)?`。
- 启动 fuzzing:`fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut restarting_mgr)?`。
- 启动 fuzzer:使用 `Launcher::builder().run_client(&mut run_client).cores(&cores).build().launch().unwrap()`。
- 详细模式编译自定义 fuzzer:`clang++-15 -DNO_MAIN -g -O2 -fsanitize-coverage=trace-pc-guard -fsanitize=address -Wl,--whole-archive target/release/libmy_fuzzer.a -Wl,--no-whole-archive main.cc harness.cc -o fuzz`。
- 创建编译器包装器(推荐):创建 `src/bin/libafl_cc.rs`,使用 `ClangWrapper`,配置 `cpp(is_cpp)`、`parse_args(&args)`、`link_staticlib(&dir, "my_fuzzer")`、`add_args(&Configuration::GenerateCoverageMap.to_flags().unwrap())`、`add_args(&Configuration::AddressSanitizer.to_flags().unwrap())`、`run()`。
- 编译并使用编译器包装器:`cargo build --release`,然后 `target/release/libafl_cxx -DNO_MAIN -g -O2 main.cc harness.cc -o fuzz`。
- 运行 campaign 基本:`./fuzz --cores 0 --input corpus/`。
- 多核运行:`./fuzz --cores 0,8-15 --input corpus/`(在 core 0 和一个 8-15 的 8 个核上运行 9 个客户端)。
- 带选项运行:`./fuzz --cores 0-7 --input corpus/ --output crashes/ --timeout 1000`。
- 启用 TUI:`./fuzz -tui=1 corpus/`。
- 使用 `-fork=1 -ignore_crashes=1`:在首次崩溃后继续 fuzzing。
- 使用 `InMemoryOnDiskCorpus`:跨重启持久化 corpus。
- 使用特定 LLVM 版本:避免兼容性问题。
- 正确设置 `RUSTFLAGS`:防止链接错误。
- 崩溃去重:添加 backtrace observer:`let backtrace_observer = BacktraceObserver::owned("BacktraceObserver", libafl::observers::HarnessType::InProcess);`。
- 更新 executor 以包含 backtrace observer:`tuple_list!(edges_observer, time_observer, backtrace_observer)`。
- 更新 objective 使用 hash feedback:`feedback_and!(feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new()), NewHashFeedback::new(&backtrace_observer))`,确保只保存唯一回溯的崩溃。
- 字典 fuzzing:从文件添加 tokens:`let mut tokens = Tokens::new(); if let Some(tokenfile) = &tokenfile { tokens.add_from_file(tokenfile)?; } state.add_metadata(tokens);`。
- 更新 mutator 以合并 tokens 突变:`let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));`。
- 硬编码 tokens 示例(PNG):`state.add_metadata(Tokens::from([ vec![137, 80, 78, 71, 13, 10, 26, 10], "IHDR".as_bytes().to_vec(), "IDAT".as_bytes().to_vec(), "PLTE".as_bytes().to_vec(), "IEND".as_bytes().to_vec(), ]));`。
- 自动 tokens:在 compiler wrapper 中启用:`cc.add_pass(LLVMPasses::AutoTokens)`。
- 在 fuzzer 中加载 auto tokens:`tokens += libafl_targets::autotokens()?;`。
- 验证 tokens section:`echo "p (uint8_t *)__token_start" | gdb fuzz`。
- 性能调优:多核 fuzzing 线性加速;使用 `InMemoryCorpus` 更快但不持久;使用 `InMemoryOnDiskCorpus` 平衡速度和持久性;启用 sanitizers(2-5x 减速,但对发现 bug 必要);使用 `-O2` 优化级别平衡速度和覆盖。
- 调试 fuzzer:单进程模式运行:替换 launcher 为直接调用 `run_client(None, SimpleEventManager::new(monitor), 0).unwrap();`,注释掉 `Launcher::builder()...launch()`。
- 用 GDB 调试:`gdb --args ./fuzz --cores 0 --input corpus/`。
- libpng 示例:获取源码:`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 FUZZER_CARGO_DIR="/path/to/libafl/project"`、`export CC=$FUZZER_CARGO_DIR/target/release/libafl_cc`、`export CXX=$FUZZER_CARGO_DIR/target/release/libafl_cxx`。
- 构建静态库:`./configure --enable-shared=no`、`make`。
- 获取 harness:`curl -O https://raw.githubusercontent.com/glennrp/libpng/f8e5fa92b0e37ab597616f554bee254157998227/contrib/oss-fuzz/libpng_read_fuzzer.cc`。
- 链接 fuzzer:`$CXX libpng_read_fuzzer.cc .libs/libpng16.a -lz -o fuzz`。
- 准备 seeds:`mkdir seeds/`、`curl -o seeds/input.png https://raw.githubusercontent.com/glennrp/libpng/acfd50ae0ba3198ad734e5d4dec2b05341e50924/contrib/pngsuite/iftp1n3p08.png`。
- 获取 dictionary(可选):`curl -O https://raw.githubusercontent.com/glennrp/libpng/2fff013a6935967960a5ae626fc21432807933dd/contrib/oss-fuzz/png.dict`。
- 开始 fuzzing:`./fuzz --input seeds/ --cores 0 -x png.dict`。
- CMake 项目:配置 `CMakeLists.txt`:`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 -g -O2)`。
- 构建非插桩二进制:`cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ .`、`cmake --build . --target buggy_program`。
- 构建 fuzzer:设置 `FUZZER_CARGO_DIR`,然后 `cmake -DCMAKE_C_COMPILER=$FUZZER_CARGO_DIR/target/release/libafl_cc -DCMAKE_CXX_COMPILER=$FUZZER_CARGO_DIR/target/release/libafl_cxx .`、`cmake --build . --target fuzz`。
- 运行 fuzzing:`./fuzz --input seeds/ --cores 0`。
- 故障排除:无覆盖率增长时,验证 compiler wrapper 使用,检查 `-fsanitize-coverage`。
- fuzzer 无法启动时,提供触发代码路径的种子输入。
- 链接错误与 `libafl_main` 时,使用 `-Wl,--whole-archive` 或 `-u libafl_main`。
- LLVM 版本不匹配时,安装兼容的 LLVM 版本(15-18),设置环境变量。
- Rust 编译失败时,用 `rustup update` 更新 Rust 和 Cargo。
- 慢速 fuzzing 时,预期 2-5x 减速,启用 sanitizers 是必要的。
- 环境变量干扰时,构建 LibAFL 项目后 unset `CC`、`CXX`、`RUSTFLAGS`。
- 无法附加调试器时,运行单进程模式(见调试部分)。
适用边界 · 注意事项
- 只需要快速设置、单线程模糊测试时,用 libFuzzer 更简单。
- 需要多核通用模糊测试且不想开发自定义 fuzzer 时,用 AFL++ 更合适。
- 不需要 LibAFL 的高级特性(自定义变异/反馈、非标准架构、研究)时不必用 LibAFL。