FuzzAnything/Hopper

GitHub: FuzzAnything/Hopper

Hopper 是一个使用解释式模糊测试自动为库生成模糊测试用例的工具,旨在提升库级模糊测试的覆盖与效率。

Stars: 263 | Forks: 36

# Hopper Hopper 是一个使用**解释式模糊测试**自动为库生成模糊测试用例的工具。它将库模糊测试的问题转化为解释器模糊测试的问题,开箱即可探索库 API 使用的广泛范围。 Hopper 的一些关键特性包括: - 无需任何模糊测试驱动程序的解释式 API 调用。 - 针对参数的类型感知变异。 - 自动的库内与库间 API 约束学习。 - 二进制仪器支持。 如需了解更多关于 Hopper 的信息,请查阅我们的 [论文](https://arxiv.org/pdf/2309.03496),该论文发表于 CCS '23。 ## 构建 Hopper ### 构建要求 - Linux-amd64(已在 Ubuntu 20.04 和 Debian Buster 上测试) - Rust 稳定版(>= 1.60),可通过 [rustup](https://rustup.rs/) 获取 - Clang(>= 5.0,[安装 Clang](https://rust-lang.github.io/rust-bindgen/requirements.html)),[rust-bindgen](https://rust-lang.github.io/rust-bindgen/) 利用 libclang 预处理、解析和类型检查 C 与 C++ 头文件。 ### 构建 Hopper 自身 ``` ./build.sh ``` 该脚本将在 hopper 的根目录创建一个 `install` 目录,随后即可使用 `hopper` 命令。 若要在任意位置使用此命令,可将该项目目录添加到你的 PATH 环境变量中。 ### 使用 Docker 你也可以选择使用 Dockerfile,它会自动构建所需依赖并编译 Hopper。 ``` docker build -t hopper ./ docker run --name hopper_dev --privileged -v /path-to-lib:/fuzz -it --rm hopper /bin/bash ``` ## 使用 Hopper 编译库 以 `csjon` 为例([更多示例](./examples/))。 ``` hopper compile --header ./cJSON.h --library ./libcjson.so --output output ``` 使用 `hopper compile --help` 查看详细用法。如果编译过程中报告头文件相关错误,可参考 [rust-bindgen](https://rust-lang.github.io/rust-bindgen/) 的用法,它被用于解析头文件。 你可能需要为头文件补充缺失的定义。 Hopper 默认使用 [E9Patch](https://github.com/GJDuck/e9patch) 对二进制文件进行仪器化。你也可以选择使用 [LLVM](./hopper-instrument/llvm-mode/) 进行源代码仪器化。 运行 `compile` 后,你将在输出目录中得到以下文件: - `bin/hopper-fuzzer`:生成输入、维护状态,并使用 `harness` 执行输入。 - `bin/hopper-harness`:执行输入。 - `bin/hopper-translate`:将输入翻译为 C 源代码。 - `bin/hopper-generator`:回放生成过程。 - `bin/hopper-sanitizer`:清理并最小化崩溃。 ### 头文件 - 如果存在多个头文件,可以新建一个头文件并 *include* 所有文件。 - 如果头文件的编译依赖特定环境变量,可通过设置 `BINDGEN_EXTRA_CLANG_ARGS` 指定。 - 如果头文件中包含你不想测试的 API 函数,可使用 `--func-pattern` 在运行时过滤。 ### 编译时的环境变量 - `HOPPER_MAP_SIZE_POW2`:控制覆盖率路径的大小。默认值为 16,范围为 [16, 20]。例如:`HOPPER_MAP_SIZE_POW2=18`。 - `HOPPER_INST_RATIO`:控制块被选中进行仪器化的概率。默认值为 100,范围为 (0, 100]。例如:`HOPPER_INST_RATIO=75`。 - `HOPPER_INCLUDE_SEARCH_PATH`:包含头文件中文件的搜索路径。例如:`HOPPER_INCLUDE_SEARCH_PATH=../`。 - `HOPPER_FUNC_BLACKLIST`:包含 Hopper 不会编译的函数黑名单。`bindgen` 不会为这些函数生成代码。例如:`HOPPER_FUNC_BLACKLIST=f1,f2`。 - `HOPPER_TYPE_BLACKLIST`:包含 Hopper 不会编译的类型黑名单。`bindgen` 不会为这些类型生成代码。例如:`HOPPER_TYPE_BLACKLIST=type1,type2`。 - `HOPPER_ITEM_BLACKLIST`:包含 Hopper 不会编译的常量/变量黑名单。`bindgen` 不会为这些项生成代码。例如:`HOPPER_ITEM_BLACKLIST=IPPORT_RESERVED`。 - `HOPPER_CUSTOM_OPAQUE_LIST`:包含我们定义的自定义不透明类型。例如:`HOPPER_CUSTOM_OPAQUE_LIST=type1`。 - `HOPPER_FUZZ_INLINE_FUNCTION`:将内联函数作为目标函数,详见 bindgen 的 [FAQ](https://rust-lang.github.io/rust-bindgen/faq.html#why-isnt-bindgen-generating-bindings-to-inline-functions)。 #### 提示 - 可在名为 `hopper.config` 的配置文件中设置编译与运行参数,详情参见 `examples/*`。 - 降低密度:如果密度超过 20%,边 ID 可能出现哈希冲突。可采取以下措施:a) 增大 `HOPPER_MAP_SIZE_POW2`;b) 降低 `HOPPER_INST_RATIO`。 - 多库支持:(1) 将多个归档文件合并为一个共享库,例如 `gcc -shared -o c.so -Wl,--whole-archive a.a b.a -Wl,--no-whole-archive`;(2) 将所有库通过 `--library a.so b.so` 传递给 Hopper 编译器。 ## 使用 Hopper 进行模糊测试 ``` hopper fuzz output --func-pattern cJSON_* ``` 使用 `hopper fuzz output --help` 查看详细用法。 运行 `fuzz` 后,将生成以下目录: - `queue`:生成的正常输入。 - `hangs`:生成的超时输入。 - `crashes`:生成的崩溃输入。 - `misc`:存储临时文件或统计信息。 ### 运行时的环境变量 - `DISABLE_CALL_DET`:禁用调用的确定性变异。 - `DISABLE_GEN_FAIL`:禁用对调用失败的函数生成程序。 - `HOPPER_SEED_DIR`:为字节类参数提供种子(默认:如果存在则使用 `output/seeds`)。 - `HOPPER_DICT`:为字节类参数提供字典,语法与 AFL 相同。 - `HOPPER_API_INSENSITIVE_COV`:禁用 API 敏感分支计数。 - `HOPPER_FAST_EXECUTE_LOOP`:每个 fork 执行的程序循环次数,设为 0 或 1 可跳出循环。例如:`HOPPER_FAST_EXECUTE_LOOP=10`。 #### 系统配置 将系统核心转储配置为 AFL 模式(在主机上执行,若在 Docker 容器中运行 Hopper)。 ``` echo core | sudo tee /proc/sys/kernel/core_pattern ``` ### 函数模式 Hopper 默认会为头文件和库文件中出现的所有函数生成输入。但有两种方式可用于过滤函数:排除函数或包含函数,从而聚焦于感兴趣的目标。 #### `--func-pattern` ``` hopper fuzz output --func-pattern @cJSON_parse,!cJSON_InitHook,cJSON_* ``` - 模式可以是函数名,例如 `cJSON_parse`,也可以是简单模式,例如 `cJSON_*`。 - 如果有多个模式,可用 `,` 连接,例如 `cJSON_*,HTTP_*`。 - 可使用 `@` 前缀限定模糊器仅对特定函数进行模糊测试,而其他函数可作为提供字段或参数值的候选,例如 `@cJSON_parse,cJSON_*`。 - `!` 用作前缀以排除特定函数,例如 `!cJSON_InitHook,cJSON_*`。 #### `--custom-rules` 模式也可通过在 `--custom-rules` 指定的文件中定义。 ``` // hopper fuzz output --custom-rules path-to-file func_target cJSON_parse func_exclude cJSON_InitHook func_include cJSON_*,HTTP_* ``` ### 约束条件 Hopper 会推断库内与库间的 API 约束,以确保正确调用 API。 约束条件会写入 `output/misc/constraint.config`。你可以删除该文件以重置约束。 此外,用户可通过 `--custom-rules` 指定描述 API 调用自定义约束的文件,这些约束将覆盖推断出的约束。 ``` // hopper fuzz output --custom-rules path-to-file // Grammar: // func, type : prefix for adding a rule for function or type // $[0-9]+ : function's i-th argument, or index in array // [a-zA-Z_]+ : object field // 0, 128 .. : integer constants // "xxxx" : string constants // methods : $len, $range, $null, $non_null, $need_init, $read_file, $write_file, $ret_from, $cast_from, $use, $arr_len, $opaque, $len_factors // others : pointer(&) , option(?), e.g &.$0.len, `len` field in the pointer's first element // // Set one argument in a function to be specific constant func test_add[$0] = 128 // One argument must be the length of another one func test_arr[$1] = $len($0) // Or one field must be the length of another field func test_arr[$0][len] = $len([$0][name]) // One argument must be in a certain range func test_arr[$1] = $range(0, $len($0)) // Argument should be non-null func test_non_null[$0] = $non_null // Argument should be null func test_null[$0] = $null // Argument should be specific string func test_magic[$0] = "magic" // Argument should be a file and the file will be read func test_path[$0] = $read_file // Argument should be use the value of specific function's return func test_use[$0] = $ret_from(test_create) // Argument should be specific type for void pointer. The type should start with *mut or *const. func test_void[$0] = $cast_from(*mut u8) // The array suppose has a minimal array length func test_void[$0][&] = $arr_len(256) // The array's length is formed by the factors func fread[$0][&] = $len_factors(1, $2) // Or func gzfread[$0][&] = $len_factors($1, $2) // Field in argument should be specific constant func test_field[$0][len] = 128 // Deeper fields func test_field[$0][&.elements.$0] = 128 // One field `len` in a type must be the length of another field `p` type ArrayWrap[len] = $len(p) // One nested union `inner_union` in a type must be set to `member2` type ComplicatedStruct[inner_union] = $use(member2) // Type is opaque that used as an opaque pointer type Partial = $opaque // A type should be init with specific function type Partial = $init_with(test_init, 0) // ctx: set context for specific function // Add a context for function ctx test_use[$0] <- test_init // Add implicit context ctx test_use[*] <- test_init // Add optional context that preferred to use ctx test_use[$0] <- test_init ? // Add forbidden context ctx test_use[$0] <- ! test_init // alias: alias types across different function alias handleA <- useA($0),createA($ret),freeA($0) // assert: adding specific assertions for calls assert test_one == 1 assert test_non_zero != 0 ``` ### 字节参数的种子 如果存在名为 `seeds` 的目录(由 `HOPPER_SEED_DIR` 指定),Hopper 会尝试读取其中的文件,并将其用作字节类参数(例如 `char*`)的种子。此外,你也可以通过参数名称为特定参数指定种子,例如将子目录命名为 `@buf`,对应参数名为 `buf`。 ### 日志记录 Hopper 使用 Rust 的 log crate 打印日志信息。默认日志级别为 `INFO`。如需输出全部日志(`DEBUG` 和 `TRACE`),可在运行 Hopper 时设置环境变量 `LOG_TYPE`,例如:`LOG_TYPE=trace ./hopper`。 详细日志将写入 `output/fuzzer_r*.log` 和 `output/harness_r*.log`。 ### 重现执行 Hopper 可在输出目录中重现程序的执行过程。 - `hopper-harness` 可解析并解释 Hopper 的运行时输入,将在执行过程中详细打印内部状态。 ``` ./bin/hopper-harness ./queue/id_000000 ``` - `hopper-translate` 可将输入翻译为 C 源代码,生成的 C 文件可作为报告问题的见证。 ``` ./bin/hopper-translate --input ./queue/id_000000 --header path-to/xx.h --output test.c # 然后使用特定库编译 gcc -I/path-to-head -L/path-to-lib -l:libcjson.so test.c -o test ``` - `hopper-generator` 可重放输入生成过程(除执行外)。你可以用它来分析输入是如何生成或变异的。 - `hopper-sanitizer` 可最小化并验证由 Hopper 生成的崩溃。它会排除违反约束的崩溃,并根据调用栈去重。 ## 测试 ### 测试 Rust 代码 - 运行所有测试用例 ``` RUST_BACKTRACE=1 cargo test -- --nocapture ``` ### 测试套件(测试库) - [如何运行与编写测试套件](./testsuite/README.md) ### 实际示例 - [示例](./examples/) ## 通过基于源代码的覆盖率评估结果 - 使用 [LLVM 源代码级代码覆盖率工具](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html) 编译库源码。需要设置编译标志,例如: ``` export CFLAGS="${CFLAGS:-} -fprofile-instr-generate -fcoverage-mapping -gline-tables-only -g" make ``` - 使用 `cov` 仪器化模式编译库,例如: ``` hopper compile --instrument cov --header ./cJSON.h --library ./libcjson_cov.so --output output_cov ``` - 运行解释器并传入所有生成的种子输入(SEED_DIR)。 ``` # 运行 Hopper 并使用 llvm-cov 计算覆盖率。 SEED_DIR=./output/queue hopper cov output_cov ``` ## 贡献指南 我们已在 [Roadmap](https://github.com/FuzzAnything/hopper/discussions/2) 中列出了一些待办任务。 如果你感兴趣,欢迎随时与我们讨论并贡献代码。 ### 编码规范 - *Zero* `cargo check` 警告 - *Zero* `cargo clippy` 警告 - *Zero* `FAILED` 在 `cargo test` 中 - *Try* 为你的代码编写测试 ### 性能分析 - [Rust 程序性能分析](https://gist.github.com/KodrAus/97c92c07a90b1fdd6853654357fd557a) - [Inferno](https://github.com/jonhoo/inferno) ``` perf record --call-graph=dwarf ./bin/hopper-fuzzer # 直接使用 flamegraph perf script | stackcollapse-perf.pl | rust-unmangle | flamegraph.pl > flame.svg # 使用 inferno perf script | inferno-collapse-perf | inferno-flamegraph > flamegraph.svg ``` 性能分析会产生大量中间数据用于分析,因此*不要*对模糊测试器运行超过 2 分钟。
标签:API 模糊, binary instrumentation, bindgen, CCS 论文, Clang, Docker, E9Patch, fuzzing test cases, interpretative fuzzing, interpreter fuzzing, intra- and inter-API constraints learning, library fuzzing, Linux 编译, Rust, type-aware mutation, 二进制插桩, 可视化界面, 安全防御评估, 库模糊测试, 类型感知变异, 约束学习, 网络流量审计, 自动模糊驱动, 解释式模糊测试, 请求拦截, 通知系统