AFL++ 官方实战翻译复现:用AFL模糊libxml2++

这是我的个人博客,欢迎访问:Kidder1的空间

在开始之前,构建AFL++LLVM模式和QEMU模式。
我假设AFL++的路径是~/AFLplusplus,如果您的安装路径不同,请在命令中更改它。
使用下载libxml2的源代码

$ git clone https://gitlab.gnome.org/GNOME/libxml2.git
$ cd libxml2

笔者注:libxml2是一个xml解析器

现在配置它禁用共享库

$ ./autogen.sh
$ ./configure --enable-shared=no

如果您想启用消毒剂,请使用适当的env变量。
在本教程中,我们将启用ASan和UBSan。

$ export AFL_USE_UBSAN=1
$ export AFL_USE_ASAN=1

使用clang包装器构建库

make CC=~/AFLplusplus/afl-clang-fast CXX=~/AFLplusplus/afl-clang-fast++ LD=~/AFLplusplus/afl-clang-fast

笔者注:CC和CXX是两个环境变量,这里是用AFL++可以插桩的编译器进行了替换,可以用export 环境变量=变量值的方式进行定义,也可以用echo $变量名的方式查看已有的环境变量值。

当作业完成后,我们开始使用工具xmllint作为线束对libxml2进行模糊处理,并从测试文件夹中获取一些测试用例作为初始种子。

$ mkdir fuzz
$ cp xmllint fuzz/xmllint_cov

$ mkdir fuzz/in
$ cp test/*.xml fuzz/in/

$ cd fuzz

在启动afl-fuzz之前,请确保使用我们的脚本配置您的系统

$ sudo ~/AFLplusplus/afl-system-config

我们来了

$ ~/AFLplusplus/afl-fuzz -i in/ -o out -- ./xmllint_cov @@

这是AFL++的默认配置,没有确定性突变和任何记忆限制。
请添加图片描述
现在,知道libxml2是一个库,因此代码是可重入的,我们可以使用持久模式加快模糊处理过程。

持久模式避免了分叉的开销,并提供了很大的加速。

要启用它,我们必须选择一个可重入例程,并设置一个持久循环来修补代码。

diff --git a/xmllint.c b/xmllint.c
index 735d951d..64725e9c 100644
--- a/xmllint.c
+++ b/xmllint.c
@@ -3102,8 +3102,19 @@ static void deregisterNode(xmlNodePtr node)
     nbregister--;
 }
 
+int main(int argc, char** argv) {
+
+  if (argc < 2) return 1;
+
+  while (__AFL_LOOP(10000))
+    parseAndPrintFile(argv[1], NULL);
+
+  return 0;
+
+}
+
 int
-main(int argc, char **argv) {
+old_main(int argc, char **argv) {
     int i, acount;
     int files = 0;
     int version = 0;

在这种情况下,我选择parseAndPrintFile,这是从xmllint main调用的主要解析例程。正如你所看到的,我创建了一个新的主函数,它围绕这个函数循环。

__AFL_LOOP是我们必须告诉AFL++我们想要持久模式的方式。每个模糊迭代,而不是用不同的输入分叉并重新执行目标,只是这个循环的一次执行。

数字10000说明,在使用AFL++生成的模糊输入运行10000次后,线束必须分叉并重置目标的状态。当模糊例程是可重入的,但例如内存泄漏时,这很有用,因此我们希望在固定数量的执行后恢复目标,以避免用无用的分配内存填充堆。

要构建它,只需删除之前编译的xmllint并重新编译它。

$ cd ..
$ rm xmllint
$ make CC=~/AFLplusplus/afl-clang-fast CXX=~/AFLplusplus/afl-clang-fast++ LD=~/AFLplusplus/afl-clang-fast
$ cp xmllint fuzz/xmllint_persistent

现在重新启动模糊器

$ cd fuzz
$ ~/AFLplusplus/afl-fuzz -i in/ -o out -- ./xmllint_persistent @@

正如你所看到的,加速是令人印象深刻的。

现在,我们将使用QEMU中的纯二进制工具对xmllint进行模糊处理。

我们会表现得好像没有源代码一样,因此我们不会修补源代码中的任何内容。

首先,构建一个无乐器的二进制文件。在继续操作之前,请提醒恢复LLVM persistent应用的修补程序。

$ cd ...
$ make clean
$ make
$ cp xmllint fuzz/

要在QEMU下以简单的基于fork的方式对其进行模糊处理,只需在afl-fuzz中添加-Q标志即可。


$ cd fuzz
$ ~/AFLplusplus/afl-fuzz -i in/ -o out -Q -- ./xmllint @@

您可能已经注意到,其速度比基于LLVM fork的模糊处理更快。这是因为我们在之前基于LLVM的步骤中使用了ASan+UBSan(因此平均速度降低了2倍)。
请注意,在这种特定情况下,QEMU的速度大约是2倍,这很好。
但是,如果我们想要一个闭源二进制文件的持久模式的速度呢?
没有痛苦,有QEMU持久模式,这是AFL++中引入的一个新功能。
在持久QEMU中有两种可能性,一种是围绕函数循环(如WinAFL),另一种是绕代码的特定部分循环。
在本教程中,我们将选择简单的路径,我们将围绕parseAndPrintFile循环。
首先,定位函数的地址:

$ nm xmllint | grep parseAndPrintFile
0000000000019be0 t parseAndPrintFile

二进制是与位置无关的,QEMU持久化需要实际地址,而不是偏移量。幸运的是,QEMU在固定地址加载PIE可执行文件,x86_64为0x400000000。
我们可以使用AFL_QEMU_DEBUG_MAPS进行检查。如果您的二进制文件不是PIE,则不需要此步骤。

$ AFL_QEMU_DEBUG_MAPS=1 ~/AFLplusplus/afl-qemu-trace ./xmllint -
4000000000-400013e000 r-xp 00000000 103:06 18676576                      /home/andrea/libxml2/fuzz/xmllint
400013e000-400033e000 ---p 00000000 00:00 0 
400033e000-4000346000 r--p 0013e000 103:06 18676576                      /home/andrea/libxml2/fuzz/xmllint
4000346000-4000347000 rw-p 00146000 103:06 18676576                      /home/andrea/libxml2/fuzz/xmllint
4000347000-4000355000 rw-p 00000000 00:00 0 
...

现在,我们设置必须循环的函数的地址

$ export AFL_QEMU_PERSISTENT_ADDR=0x4000019be0

我们在x86_64上,参数在寄存器中传递。在函数结束时,当我们返回到起始地址时,寄存器会被破坏,因此我们不再有指向rdi中文件名的指针。
为了避免这种情况,我们可以在每次迭代设置AFL_QEMU_PERSISTENT_GPR时保存和恢复通用寄存器的状态。

$ export AFL_QEMU_PERSISTENT_GPR=1

现在,重新运行上一个afl-fuzz命令:

$ ~/AFLplusplus/afl-fuzz -i in/ -o out -Q -- ./xmllint @@

请添加图片描述
至于持久LLVM,其速度之快令人难以置信。
享受AFL++,继续关注未来其他此类初学者教程。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值