基于内核状态的Fuzzer:一种LLVM方法

2020-07-25 18:37:09

要将收集内核状态作为syzkaller资源实现,我们必须执行以下步骤:

首先,我们需要实现一个LLVM PASS来执行指令插入。虽然我们已经知道,核的许多状态都位于某个结构域中。跟踪GEPointer变量的存储操作可以检测到有助于模糊化的状态。然后,参考本文档构建带有字段赋值跟踪器的编译器。在构建内核时,需要添加如下行:

要对目标文件执行Makefile操作,您需要对其进行检测。内核状态id是结构名称和字段名称的散列。

参考我们的内核状态采集仪器的实现。然后,照常构建内核。

Clang++-10KState_map.cpp-o KState_MAP-O0-g-fsanitize=地址`llvm-config-10--cxxflag--libs--ldflag--system-libs`。/KState_map LLVM_IR_DIR ASM_DIR VMLINUX Function_List_DIR./KState_map LLVM_IR ASM_DIR VMLINUX Function_LIST_DIR。

Function_List具有我们获取其地址所需的函数名称。IR_DIR:目录我们需要的所有LLVM ir代码。LOG_DIR:运行命令后,KSTATE_MAP将为每个函数创建";.json";和";.state.map";。将输出写入PATH_TO_KERNEL_STATE.map。像往常一样运行打了补丁的syzkaller。该映射基于使用状态的频率来分配权重。

现在,您可以像往常一样运行syzkaller,如果您访问一个";\input";界面,您可以发现有一个内核状态列表。您还可以在";/corpus";界面中获得每个程序的状态权重。

我们重用KCOV接口,而不是使用单独的模式。因此,我们使用最高16位的0xfefe对状态ID进行编码。当syzkaller获得一台以0xfefe启动的kcov PC时,它会意识到这台PC是一个KState id,状态的值和地址将占用随后的2*64位。无论变量使用多少位,我们都将其形式化为64位。注:如果您想要收集其他信息,则必须为其实现相应的syzkaller。

SYZ-Executor必须挑选内核状态,并在发送所有信号后将其发送出去。这些处理可以在我们针对ecutor.cc函数write_coverage_signl的补丁中找到。当Executor读取以0xfefe启动的PC时,这意味着它接收内核状态。并且在覆盖信号共享存储器之后,我们将共享存储器块用于该状态。赛兹-福泽稍后会处理他们的。

相应地,Fuzzer调用pkg/ipc.go中的parseOutput,我们添加了一个readKernState来解析执行器输出。这些内核状态信息将放入pkg/kState/kstate.go中名为KernState的结构中。来自Executor的每个输入都有一个用于kernstate的数组,每个prog都有一个根据kernstate计算的状态权重。此外,KernState支持根据其ID或ID^值(称为散列)搜索地图。

Syz-fuzzer/proc.go:calStateWeight将计算程序的权重。减去用于消除KState长度影响的计数。Prog/rand.go:chooseReaProgramIdx函数根据其状态权重优先选择prog。

这个工具就是我们在上面提到的KSTATE_MAP。我们使用LLVM API静态分析目标函数中状态的使用。在没有意识到状态的价值的情况下,它只会鼓励Fuzzer优先选择和提取那些经常重写重要状态的程序。换句话说,程序具有复杂的状态。

你可以得到一些变量的约束值。修补的syzkaller支持散列模式,如果可以在KState映射中找到ID^值,则将其用作唯一状态。因此,您可以为具有特定值的州指定权重。现在,它只能在kstatemap中手动指定。