FuzzFactory 中,特定于域的模糊测试应用程序是通过检测测试程序来实现的。表 1 描述了实现本文中描述的六个域中的每个域所需的代码行。在我们的应用程序中,我们使用 LLVM 执行检测。
伪代码中用于特定领域模糊化的API。
类型dsf_t; /* 特定于域的反馈图 */
/* 注册一个新域。在初始化期间调用一次。*/ dsf_t new_域(int key_size, function reduce, int a_0);
/* 对 DSF 映射的更新。在测试执行期间调用。*/
int dsf_get(dsf_t dsf, int k); 返回 dsf[k]
void dsf_set(dsf_t dsf, int k, int v); // dsf[k] = v
void dsf_increment(dsf_t dsf, int k, int v); // dsf[k] = dsf[k] + v
void dsf_union(dsf_t dsf, int k, int v); // dsf[k] = dsf[k] | v
void dsf_maximize(dsf_t dsf, int k, int v); // dsf[k] = max(dsf[k], v)
图 11 概述了 FuzzFactory 提供的 API。类型dsf_t定义域特定映射的类型 。在我们的实现中,键和值始终是 32 位无符号整数。但是,用户可以指定DSF映射的大小;也就是说,它将包含的 keys 的数量。API 函数new_domain注册一个新域,其密钥集 K 包含key_size个密钥。参数 reduce 和 a_0分别提供化简器函数(类型 int x int -> int)和初始聚合值。对于慢速域,key_size为 1。对于 K 是一组程序位置 L 的应用程序,我们使用 216 key_size,并将 16 位伪随机数分配给基本块位置,类似于 AFL。对于增量模糊测试应用程序,其中 K = L×L,
我们使用哈希函数将两个基本块位置组合成一个整数值键。集合 V 和 A 通过使用 DSF 映射和 reduce 函数的实现隐式定义 。对于诸如有效性模糊测试之类的应用,其中 A 是一组数量级,我们使用位向量来表示集合。
该函数 new_domain 返回 DSF 映射的句柄,然后在后续中使用
图 11 中列出的 API,例如dsf_increment。在执行任何测试之前, 在测试 program 启动时插入对new_domain调用。由用户负责确保提供的简化器函数满足属性 1 和 2,这反过来又保证了单调聚合(定理 1)。以"dsf_"开头的 API 函数操作 DSF 映射。参数键必须在 [0, key_size) 范围内。
include文件夹中
afl-llvm-rt.o.c
/* Domain-specific fuzzing */
u32* __fuzzfactory_dsf_map = (u32*) (&__afl_area_initial[MAP_SIZE]); /* Additional feedback maps */
static dsf_config dsf_configs[DSF_MAX]; // Array of configs
static u32 dsf_count = 0; // Length of above array (i.e, number of non-zero items)
/* SHM setup. */
/* Set waypoints map to be after the AFL code coverage shared memory region */
__fuzzfactory_dsf_map = (u32*) &__afl_area_ptr[MAP_SIZE];
waypoints.h
/* Type of DSF map reference */
typedef int dsf_t;
/* Functions that update DSF map at run-time */
void __fuzzfactory_dsf_max(dsf_t id, u32 key, u32 value);
void __fuzzfactory_dsf_bitwise_or(dsf_t id, u32 key, u32 value);
void __fuzzfactory_dsf_set(dsf_t id, u32 key, u32 value);
void __fuzzfactory_dsf_increment(dsf_t id, u32 key, u32 value);
/* Same as above but first arg is pointer to DSF map */
void __fuzzfactory_dsfp_max(dsf_t* p, u32 key, u32 value);
void __fuzzfactory_dsfp_bitwise_or(dsf_t* p, u32 key, u32 value);
void __fuzzfactory_dsfp_set(dsf_t* p, u32 key, u32 value);
void __fuzzfactory_dsfp_increment(dsf_t* p, u32 key, u32 value);
/* Macros for use in manual insertion of DSF calls */
#define FUZZFACTORY_DSF_MAX(id, k, v) (__fuzzfactory_dsf_max(id, k, v))
#define FUZZFACTORY_DSF_BIT(id, k, v) (__fuzzfactory_dsf_bitwise_or(id, k, v))
#define FUZZFACTORY_DSF_SET(id, k, v) (__fuzzfactory_dsf_set(id, k, v))
#define FUZZFACTORY_DSF_INC(id, k, v) (__fuzzfactory_dsf_increment(id, k, v))
void __afl_waypoints_set_state(u32 state);
u32 __afl_waypoints_get_state();
void __afl_waypoints_hash_state(int num_args, ...);
#define AFL_WAYPOINTS_SET_STATE(state) __afl_waypoints_set_state(state)
#define AFL_WAYPOINTS_GET_STATE() (__afl_waypoints_get_state())
#define AFL_WAYPOINTS_HASH_STATE(num_args, ...) (__afl_waypoints_hash_state((num_args), ##__VA_ARGS__))
/* Max number of domain-specific maps */
#define DSF_MAX 4
/* Config for domain-specific fuzzing */
typedef struct dsf_config_t {
int start;
int end;
int reducer;
u32 initial;
} dsf_config;
/* Register a new domain-specific fuzzing front-end */
dsf_t __fuzzfactory_new_domain(u32 size, enum fuzzfactory_reducer reducer, u32 initial);
#define FUZZFACTORY_DSF_NEW(name, size, reducer, initial) dsf_t name; \
__attribute__((constructor(0))) static void __init_##name() { \
name = __fuzzfactory_new_domain(size, reducer, initial); \
} \
#else // Not compiling with AFL
reducers.h
这个文件只定义了还原器函数的枚举,它作为枚举值在模糊器(afl-fuzz.c)之间通过 IPC 通信。作为枚举值,在模糊器(afl-fuzz.c)和仪器测试程序之间通过 IPC 通信。和仪器测试程序之间通过IPC通信。
这些还原器函数的实现在其他地方(reducers.c)。 而且必须与此文件保持同步。该实现只链接到模糊器中;测试程序不需要它。
enum fuzzfactory_reducer {
FUZZFACTORY_REDUCER_MAX,
FUZZFACTORY_REDUCER_MIN,
FUZZFACTORY_REDUCER_LOG_BUCKET,
FUZZFACTORY_REDUCER_BIT_UNION,
FUZZFACTORY_REDUCER_BIT_INTERSECT,
};
llvm_mode文件夹中:
afl-catch-dlclose.so.c
int dlclose (void *handle); 函数描述: dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。
afl-clang-fast.c
#ifdef USE_TRACE_PC
cc_params[cc_par_cnt++] = "-fsanitize-coverage=trace-pc-guard";
cc_params[cc_par_cnt++] = "-mllvm";
cc_params[cc_par_cnt++] = "-sanitizer-coverage-block-threshold=0";
#else
if (!getenv("WAYPOINTS_DISABLE_COVERAGE")) {
cc_params[cc_par_cnt++] = "-Xclang";
cc_params[cc_par_cnt++] = "-load";
cc_params[cc_par_cnt++] = "-Xclang";
cc_params[cc_par_cnt++] = alloc_printf("%s/afl-llvm-pass.so", obj_path);
}
/* Waypoints passes */
char* domains = getenv("WAYPOINTS");
if (!domains) {
domains = getenv("DOMAINS");
if (!domains)
domains = "";
}
char* comma = ",";
char* domain = strtok(domains, comma);
while (domain != NULL) {
printf("Found domain: %s\n", domain);
cc_params[cc_par_cnt++] = "-Xclang";
cc_params[cc_par_cnt++] = "-load";
cc_params[cc_par_cnt++] = "-Xclang";
cc_params[cc_par_cnt++] = alloc_printf("%s/waypoints-%s-pass.so", obj_path, domain);
cc_params[cc_par_cnt++] = alloc_printf("%s/waypoints-%s-rt.o", obj_path, domain);
domain = strtok(NULL, comma);
}
afl-llvm-rt.o.c
/* SHM setup. */
static void __afl_map_shm(void) {
u8 *id_str = getenv(SHM_ENV_VAR);
/* If we are running under AFL, attach to the appropriate region, replacing the
early-stage __afl_area_initial region that is needed to allow some really
hacky .init code to work correctly in projects such as OpenSSL. */
if (id_str) {
u32 shm_id = atoi(id_str);
__afl_area_ptr = shmat(shm_id, NULL, 0);
/* Whooooops. */
if (__afl_area_ptr == (void *)-1) _exit(1);
/* Write something into the bitmap so that even with low AFL_INST_RATIO,
our parent doesn't give up on us. */
__afl_area_ptr[0] = 1;
/* Set waypoints map to be after the AFL code coverage shared memory region */
__fuzzfactory_dsf_map = (u32*) &__afl_area_ptr[MAP_SIZE];
# 将航点图设置在AFL代码覆盖共享内存区域之后
}
}
fuzzfactory.hpp
hpp,其实质就是将.cpp的实现代码混入.h头文件当中,定义与实现都包含在同一文件,则该类的调用者只需要include该hpp文件即可,无需再将cpp加入到project中进行编译。
而实现代码将直接编译到调用者的obj文件中,不再生成单独的obj,
采用hpp将大幅度减少调用 project中的cpp文件数与编译次数,也不用再发布烦人的lib与dll,因此非常适合用来编写公用的开源库。
表 2.定义用于注入代码的检测函数,该代码更新特定于 domain 的反馈映射。它们在表 3 到 8 中使用。如果在对被测程序进行编译时传递期间遇到相应的语法对象,则会激活钩子。这些钩子的处理程序逻辑可以在被测的预备忘中注入代码。操作是在检测期间用于实际注入代码的函数。实用程序函数在编译时可用于处理程序逻辑。
检测挂钩 描述
new_basic_block() 在被测程序的控制流图中基本块的开头激活。
entry_point() 在测试执行的入口点激活(例如 ,启动 main 函数)。
func_call(name, args) 在调用名为 name 的函数的表达式中激活,该表达式带有参数 args。
bin_expr(type, left, op, right) 激活通过该格式的二进制操作器进行表达
“左 op right”(例如 x == 42),其中操作数具有类型 类型 (例如 long)。
开关(类型, 阀, 案例) 在类型类型的值 val 上遇到 switch 语句时激活,其中 cases 是 case 子句的列表。
检测操作 描述
insert_after(inst) 在当前激活其检测挂钩的指令之后立即插入指令 inst。
insert_before(inst) 在当前激活其检测挂钩的指令之前插入一条指令 inst。
实用程序函数 描述
current_program_loc() 返回与当前检测位置相对应的程序位置(即集合 L 中的值)。
target_program_loc(case) 返回程序位置(即,集合 L 中的值),该位置是 switch 语句中事例的目标。
comm_bits(a, b, n) 计算两个 n 字节操作数 a 和 b 之间的公共位数。例如,comm_bits(1025, 1026, 4) = 30,因为这些 32 位操作数中只有 2 位不同。
实现。传统上,实现每个这样的领域都需要在修改模糊测试工具(如AFL)以实现特定于领域的目标时付出非凡的努力。使用FuzzFactory,上述六个域中的四个可以在少于30行C++中实现。表 1 列出了使用 FuzzFactory 实现本文中介绍的六个域中的每个域所需的代码行。第 6 节提供了有关我们实现的更多详细信息。对于重新实现先前工作的域,该表还列出了实现相应的专用独立模糊测试工具所需的代码行。
还有其他的:
afl-showdsf.c
模仿showmap的showdsf。一个非常简单的工具,运行目标二进制文件,并以人类可读的形式显示特定领域反馈的内容。
afl-fuzz.c
见下一篇
reducers.c
参数 reduce 和 a_0分别提供化简器函数(类型 int x int -> int)和初始聚合值。
/* Make sure that this array is consistent with the enum defined in reducers.h */
reducer_t dsf_reducers[] = {
[FUZZFACTORY_REDUCER_MAX] = reducer_max,
[FUZZFACTORY_REDUCER_MIN] = reducer_min,
[FUZZFACTORY_REDUCER_LOG_BUCKET] = reducer_log_bucket,
[FUZZFACTORY_REDUCER_BIT_UNION] = reducer_bit_union,
[FUZZFACTORY_REDUCER_BIT_INTERSECT] = reducer_bit_intersection,
};