AFL源码分析(Ex.3)——附录(Part 3)

robots

 

0. 写在前面

本文全文参考了da1234cao师傅的Github仓库https://github.com/da1234cao/translation/tree/master/AFL 的相关内容,我对全文中机翻程度过高以致未正确表达原文语义、异译程度过高以致偏离原文语义、语句不通顺或不合语法的大量文本进行了修改,特此说明。以下文章中的“原译者”即代指da1234cao师傅。

此文章主要是对AFL仓库中doc目录下的所有文档进行翻译。

  • [x] env_variables.txt(环境变量手册) —— 历史文章(Part 1)
  • [ ] historical_notes.txt(历史记录)
  • [ ] INSTALL(安装说明)
  • [x] life_pro_tips.txt(使用技巧) —— 历史文章(Part 1)
  • [x] notes_for_asan.txt(ASAN模式手册) —— 历史文章(Part 2)
  • [x] parallel_fuzzing.txt(同步fuzz模式手册) —— 历史文章(Part 2)
  • [x] perf_tips.txt(故障排除手册) —— 本文
  • [x] QuickStartGuide.txt(快速上手) —— 本文
  • [ ] sister_projects.txt
  • [x] status_screen.txt(GUI手册) —— 本文
  • [ ] technical_details.txt

后续附录将继续翻译以上列表中的文章。

 

1. perf_tips.txt(故障排除手册)

此文件提供了对缓慢或资源浪费的模糊测试作业进行故障排除的提示。 有关一般说明手册,请参阅自述文件。

1.1 保持你的输入样例为小型文件

这可能是最重要的一步!大型输入文件不仅需要更多的时间和内存来让待测的二进制文件顺利解析此输入,而且还会在其他的几个方面显着降低模糊测试过程的效率。

为了说明这一点,假设您的样本变异规则是随机对种子文件中的内容进行位翻转操作,一次操作一位。 那么假设如果你翻转第47位,你会遇到一个安全漏洞; 翻转任何其他位只会导致被测程序抛出一个”无效文档”的异常。

现在,如果您的种子测试用例的长度为100个字节,那么您将有71%的机会在前1,000个exec中触发错误——针不戳! 但是,如果测试用例的长度为1 KB,则我们将在同一时间范围内随机触发错误的可能性将降低至11%。那么进一步的,如果它具有10 KB的非必需杂内容,则触发错误的可能性将下降到1%

最重要的是,在输入样例较大的情况下,待测目标的运行速度可能比以前慢510倍——因此,模糊效率的总体下降可能很容易高达500倍左右。

在实践中,这意味着您不应该直接使用您使用手机等专业设备拍摄的照片来模糊测试图像解析器。 而是生成一张很小的16x16图片,并通过jpegtranpngcrunch运行该图片以取得良好效果。 大多数其他类型的文档也是如此。

../testcases/*中有很多小型的种子测试用例——试试看使用他们或提交新的测试用例!

如果要以更大的第三方语料库启动测试,请首先在该数据集上运行afl-cmin以缩小输入样例并设置有效的超时时间。

1.2 使用一个简单的目标

请考虑在您的模糊测试工作中使用更简单的目标二进制文件。 例如,对于图像格式,轻量的实用程序(如djpegreadpnggifhisto)比ImageMagick的转换工具快得多(10-20倍)——尽管他们都使用大致相同的库级图像解析代码。

即使您没有针对特定目标的轻量级工具,也请记住,您始终可以使用另一个相关的库来生成语料库,然后稍后将其手动输入到耗费更多资源的程序中。

[原译者注:对于目标中使用的二进制文件,选择尽量小的,且可以达到需要的功能。测试一个程序的时候,能不能不一个大程序分开来测试,不同的阶段有不同的输出。类似于流水线的方式测试,加快测试速度。]

1.3 启用基于 LLVM 的检测模式

当对慢速目标进行模糊处理时,可以使用llvm_mode/README.llvm中介绍的基于LLVM的检测模式,fuzzer的性能将被提高2倍。 请注意,此模式需要使用clang,不适用于GCC

LLVM模式还提供了两种额外的模糊测试模式:

  • persistent mode:进程内持久化fuzz模式,该模式对于某些类型的自有库文件可以很好地工作,对于快速目标,可以将性能提高5-10倍。
  • deferred fork server模式:延迟fork模式,该模式可以为启动开销高的程序提供巨大的增益。两种模式都要求您能编辑待测程序的源代码,但更改通常只是策略性地增加一两行。

1.4 对待测文件进行前置的分析和优化

检查是否有任何明显改善性能的参数或设置。 例如,可以使用以下命令调用IJG jpeglibjpeg-turbo随附的djpeg实用程序:

-dct fast -nosmooth -onepass -dither none -scale 1/4

这将加快此程序的运行速度,尽管这伴随着解码图像的质量相应下降的代价,但这可能并不是您关心的问题。

在某些程序中,可以完全禁用程序的输出流,或者至少使用计算成本较低的输出格式。 例如,使用图像转码工具,转换为BMP文件将比转换为PNG快很多。

对于某些可以忽略错误的解析器,启用更为严格的模式(即,在发生第一个错误后退出)可能会导致生成的样例输入文件更小并改善运行时间,而不会牺牲覆盖率。例如,对于sqlite,您可能需要指定-bail

如果程序仍然太慢,您可以使用strace -tt或等效的性能分析工具来查看测试目标文件是否正在在做任何会导致运行变慢的事情。 有时,您可以通过将目标文件的stdin/stdout/stderr等交互流设置为/dev/null或禁用某些实际测试并不真正需要的编译时功能来加快速度(尝试./configure --help)。 众所周知,会大量消耗资源的事情之一是通过exec*()、popen()、system()或其等效函数去调用其他实用程序。 例如,当tar确定输入文件是压缩档案时,它可以调用外部解压缩工具去解压此文件。

一些程序还可能故意调用sleep()usleep()nanosleep()vim就是一个很好的例子。 其他程序可能会尝试fsync()等。 有第三方库可以帮助您轻松删除此类代码,例如:https://launchpad.net/libeatmydata

[原译者注:这个挺好]

在测试由于不可避免的初始化开销而导致缓慢的程序时,您可能想尝试使用带LLVM延迟的forkserver模式(请参阅 llvm_mode/README.llvm),此模式可以使您的测试速度提高高达10倍。

最后,很重要的是,如果您正在使用ASAN并且发现其运行的性能不可接受,请考虑暂时将其关闭,稍后使用启用ASAN的二进制文件手动检查其生成的语料库。[译者注:这里相当于把检查内存的功能摘出来。如果检查的内容可以分类,每次运行检查不同的内容,并行运行检查?感觉有点得不偿失。那对于过于消耗时间的内容检查,是不是可以摘出来做?以什么合适的尺度来判读是不是适合摘出来做。]

1.5 确保只检测真正想要测试的库

只检测您现在真正想要进行测试的库,一次只测试一个库。 fuzzer不会将系统范围的、非检测目标的库用于此次测试。 例如,在大多数情况下,fuzzer不会仅仅因为您正在测试依赖于bignum数学的加密测试目标而对libgmp进行检测。

当心那些把奇怪的第三方库和他们的源代码捆绑在一起的程序(Spidermonkey就是一个很好的例子)。 检查并调整./configure选项来将程序调整到最佳状态。

1.6 尝试并行化运行 fuzzer

fuzzer被设计为每个作业需要大约1CPU核心。 这意味着在4核系统上,您可以轻松地运行四个并行的模糊测试作业,而性能损失相对较小。 有关如何执行此操作的提示,请参阅parallel_fuzzing.txt

afl-gotcpu实用程序可以帮助您了解系统上是否仍有空闲CPU资源。(它不会告诉您内存带宽情况、缓存丢失情况或类似的资源信息,但这些不太可能成为问题。)

1.7 检查内存限制和超时限制是否不合适

如果您启用了-m-t限制,且这个限制超过了fuzzer的实际需要,请考虑将它们置为合理值。

对于理论上应当非常迅速但在某些输入样例上实际变得缓慢的程序,您还可以尝试使用-t额外设置一个超时限制,这个额外的超时限制应当比fuzzer默认的超时时间更短。 在快速和空闲的机器上,将超时时间降低到-t 5可能是一个可行的计划。

-m参数也值得检查。 当出现异常的输入样例时,待测程序最终可能会花费大量时间来分配和初始化兆字节量级的内存。 使用-m设置更加严格的内存限制可以使fuzzer更快地放弃此异常的输入样例并且不会浪费CPU时间。[原译者注:程序是如何通过-m,起限制作用?]

1.8 检查系统设置

有几个操作系统级别的因素可能会影响模糊测试速度:

  • 避免高系统负载。尽可能使用闲置机器进行系统测试。并杀死任何非必要的会消耗 CPU 的进程(空闲的浏览器窗口、媒体播放器、复杂的屏幕保护程序等)。
  • 避免将网络文件系统用于fuzzerIO或由待测目标访问以读取其所需的配置文件(请特别注意home目录,许多程序在其中搜索.文件)。
  • 按需进行CPU扩展。LinuxCPU需求调度器按特定时间表执行分析,并且众所周知它往往会低估由afl-fuzz(或任何其他模糊器)发出的短期进程的需求。在Linux上,这可以通过以下方式缓解:
    cd /sys/devices/system/cpu
    echo performance | tee cpu*/cpufreq/scaling_governor
    

    在其他系统上,CPU扩展的影响会有所不同;模糊测试时,可以使用特定于操作系统的工具来确定是否所有内核都在全速运行。

  • 禁用透明大内存页(Transparent huge pages,THP)机制当在内核中启用THP时,某些内存分配器(例如jemalloc)可能会导致严重的模糊测试性能损失。您可以通过以下方式禁用此功能:
    echo never > /sys/kernel/mm/transparent_hugepage/enabled
    
  • 选择次优的调度策略。 这个设置的重要性要视测试目标情况而定,但是在Linux上,您可能需要确保设置了以下选项:
      echo 1 >/proc/sys/kernel/sched_child_runs_first
      echo 1 >/proc/sys/kernel/sched_autogroup_enabled
    

    fuzzer进程设置不同的调度策略(例如SCHED_RR)通常也可以加快速度,但是需要格外小心异常的发生。

1.9 最后的解决方案,使用 -d

对于真的很慢的程序,如果您确实无法使用任何工具对巨大的输入样例进行转义或其他处理,或者当您只想尽早获得快速但是混乱的检测结果时,您可以使用于 -d 模式。

该模式会导致afl-fuzz跳过所有确定性检查的模糊测试步骤,这将使得输出变得不那么整洁,并且会使测试的最终深度降低一些,但它会给你一种比其他模糊测试工具更舒适的体验。[原译者注:???]

 

2. QuickStartGuide.txt(快速上手)

你应该读读docs/README。它的篇幅很短。如果你真的不想读它,那此篇文档将指导你如何开始你的模糊测试之旅:

  1. make编译AFL。 如果您构建失败,请参阅docs/INSTALL中的提示修复问题。
  2. 找一个或者编写一个合理且高效简单的程序,该程序可以从文件流或标准输入流STDIN中获取数据,它需要运行一个有测试价值的逻辑,然后干净地退出。如果您想测试网络服务逻辑,请将其修改为在前台运行并从STDIN中读取数据包。 当此程序需要使用一个带校验和的数据格式时,也请注释掉校验和的验证代码。遇到错误时,程序必须以标准形式崩溃。 请一定要注意程序中有关自定义的SIGSEGVSIGABRT处理程序和后台进程。(译者注:此处的后台进程应当代指守护进程)若您想寻找一个不会导致程序崩溃的漏洞或缺陷,请参阅docs/README中的第11节以获取提示。[原译者注:可以自己写一个简单,有明显bug的程序测试。]
  3. 使用afl-gcc编译要进行fuzz的程序和库。 常用的命令是:
    CC=/path/to/afl-gcc CXX=/path/to/afl-g++ ./configure --disable-shared
    make clean all
    

    如果程序构建失败,请联系<a href=”mailto:afl-users@googlegroups.com“”>afl-users@googlegroups.com。[原译者注:注意--disable-shared]

  4. 获取一个对程序有意义的小而有效的输入文件。 在对使用语法冗长(SQL,HTTP等)的文本作为输入的程序进行模糊处理时,也请按照dictionaries/README.dictionaries中的描述创建样例输入字典。
  5. 如果程序从stdin读取数据,则按以下示例代码运行afl-fuzz
    ./afl-fuzz -i testcase_dir -o findings_dir -- /path/to/tested/program [...program's cmdline...]
    

    如果程序从文件中获取输入,可以在程序的命令行的末尾输入@@,此时AFL会为你自动生成一个文件名。

  6. 监视GUI中显示的内容,关于这些内容的解释可以通过docs/status_screen.txt查看。

以上步骤都完成之后,你可以坐好,放松,喝杯茶,如果时间允许,您可以试着浏览以下文件:

  • docs/README :对AFL的概述
  • docs/perf_tips.txt:关于如何快速模糊测试的简单提示
  • docs/status_screen.txt:UI显示的含义解释
  • docs/parallel_fuzzing.txt:关于在多核上运行AFL的建议

 

3. status_screen.txt(GUI手册)

本文档提供了状态监控的概述,以及对任何警告或UI中显示的红色文本进行故障排除的提示。 有关一般说明手册,请参见自述文件。

3.0 多彩输出

状态监控和错误消息都使用了不用的颜色使内容保持可读性并试图通过此吸引您对重要细节的注意力。 例如,红色的信息几乎总是表示“请参考相关文档” ?

不幸的是,只有在您的终端使用传统的Unix配色方案(黑色背景上的白色文本)时或接近此方案的情况下,UI才能正确呈现。

如果您使用的是其他系统下的终端程序,则可能需要更改为以下设置,例如:

  • 对于GNOME终端,请转到Edit>Profile preferences,选择colors选项卡,然后从内置方案列表中选择white on black
  • 对于MacOS X Terminal应用程序,通过Shell> New Window菜单使用Pro方案打开一个新窗口(或将Pro设置为默认设置)。

如果您确实喜欢您终端当前的颜色,则可以编辑config.h以注释掉USE_COLORS,然后执行make clean all

我不知道有任何其他简单的方法使这工作不引起其他副作用——抱歉。(>_<)

好了,接下来让我们谈谈屏幕上的实际内容。

3.1 Process timing(fuzz时间)

+----------------------------------------------------+
|        run time : 0 days, 8 hrs, 32 min, 43 sec    |
|   last new path : 0 days, 0 hrs, 6 min, 40 sec     |
| last uniq crash : none seen yet                    |
|  last uniq hang : 0 days, 1 hrs, 24 min, 32 sec    |
+----------------------------------------------------+

此部分的内容很明显,它显示了fuzzer已经运行了多长时间,以及自从最近一次新发现以来已经花费了多少时间。每个fuzzer的发现分为路径(发现了一个可以触发新执行模式/分支的输入用例)、崩溃和挂起。

测试的时间没有硬性标准,但是大多数测试工作要持续数天或数周。 实际上,对于一个比较复杂的项目,测试的第一阶段可能需要一天左右的时间。有些测试甚至要持续一个月或更久。

尽管我们不用担心fuzzer的运行时间过长,但是有一种重要的情况要提防:如果fuzzer在启动后的几分钟内没有找到新路径,则可能是您没有正确调用目标待测文件,这表明它可能永远也无法解析我们要喂给它的输入文件。另一个可能的解释是,默认内存限制(-m)过于严格,这使得程序未能尽早分配缓冲区后退出。 当然也有可能是输入文件是无效的以致于始此输入终无法通过待测程序的基本标头检查。

如果一段时间内没有新路径出现,您将会看到一个红色的大警告:-)

3.2 Overall results(fuzz计数器)

+-----------------------+
|  cycles done : 0      |
|  total paths : 2095   |
| uniq crashes : 0      |
|   uniq hangs : 19     |
+-----------------------+

cycles done表示到目前为止已完成的样例输入队列完成次数——即fuzzer遍历到目前为止发现的所有有趣样例输入对其进行测试并循环回到最开始的次数。每个模糊测试实例都应至少完成一个周期。 并且理想情况下,运行时间应该比这更长,即,运行更多的周期数。

如前所述,第一次周期完成可能需要一天或更长的时间,因此您请坐下来放松喝喝茶。 如果您想立即获得更广泛的结果,且您能接受代码覆盖率下降,请尝试使用-d选项,这将跳过确定性检查的fuzz步骤,它可以为您提供更熟悉便捷的体验。 但是,它在一些方面不如标准模式。

为了帮助您得知应该在何时按下Ctrl-C以中止测试,cycle counter也使用了多彩输出方案。它将在第一次周期完成时以洋红色显示,如果在随后的循环中仍有新发现,则变为黄色,然后在循环结束时变为蓝色。最终,在较长时间没有任何新发现后后,变成绿色,表示您可以在此时中止。

此部分中的其余字段应该非常明显——到目前为止,已经发现的所有测试用例(路径)以及异常的数目。 如自述文件中所述,可以通过浏览输出目录来实时分析路径、崩溃和挂起。

3.3 Cycle progress(fuzz进度)

+-------------------------------------+
|  now processing : 1296 (61.86%)     |
| paths timed out : 0 (0.00%)         |
+-------------------------------------+

此部分告诉您fuzzer已完成了多少测试进度,当此进度为100%时,代表完成了一个3.2中所述的周期。此外此部分还会告诉您fuzzer因为持续超时而决定丢弃的样例输入数量。

有时在第一行中显示的*后缀表示当前处理的路径不是favored的(此属性将稍后在第3.6中讨论)

如果您认为fuzzer的进度太慢,请参阅本文档第2节中有关-d选项的注释。

3.4 Map coverage(代码覆盖率)

(译者注:若要理解此部分内容,那么需要先明确,AFL将为每个代码块生成一个随机数,作为其“位置”的记录。随后,对分支处的入口位置和结束目标位置进行异或,并将异或的结果作为该分支的key,用于保存每个分支的执行次数。用于保存执行次数的实际上是一个哈希表,大小为MAP_SIZE=64K,当然会存在碰撞的问题;但根据AFL文档中的介绍,对于不是很复杂的目标,碰撞概率还是可以接受的)

+--------------------------------------+
|    map density : 10.15% / 29.07%     |
| count coverage : 4.03 bits/tuple     |
+--------------------------------------+

此部分提供了关于fuzzer代码覆盖率的相关知识。

框中的第一行告诉您,fuzzer已经命中的分支数量占总哈希表容量(MAP_SIZE)的比例。 左边的数字描述了当前输入样例的覆盖率,右边的是整个输入语料库的覆盖率。

请注意以下极端情况:

  • 若出现一个低于200的数字(而非比例值),将表明目标程序出现了以下三种情况之一:
    • 目标程序过于简单
    • 目标程序没有正确的被检测(例如目标程序被链接到了非检测目标库文件的副本)
    • 目标程序在接收到测试用例后过早的退出了

    此时,数据将被标记为粉红色以告知您发生了异常情况

  • 若您的目标程序是一个使用了大量的模板生成代码的非常复杂的程序,那么第一行的数据可能超过70%(尽管发生的可能性很低)。因为过高的分支密度将使fuzzer可靠地识别到新程序状态变的更加困难(译者注:当比例过高将极大地增加哈希表碰撞的概率,读者可以去了解哈希表碰撞的相关概念以进一步了解),所以我建议使用AFL_INST_RATIO = 10左右的设置重新编译二进制文件然后重新启动模糊测试(请参阅env_variables.txt获取更多信息)。fuzzer将以红色标记高百分比的覆盖率。 但是,除非您正在对一个拥有非常多分支的程序进行测试(例如,v8perlffmpeg),您可能永远也看不到此红色标记。

此部分的第二行为某次输入用例中映射碰撞的计数。 本质上,如果对于我们尝试的所有输入样例,每个采用的分支始终采用固定的次数,则该读数将为1.00。 当我们设法触发每个分支的其他命中计数时,指针将开始向8.00移动(8位映射命中的每个位),但可能永远不会达到极限。

那么综合这部分的两个值可用于比较针对同一个待测文件启动的不同fuzzer实例的覆盖率。

[原译者注:不大明白map coverage]

3.5 Stage progress(样本变异状态)

+-------------------------------------+
|  now trying : interest 32/8         |
| stage execs : 3996/34.4k (11.62%)   |
| total execs : 27.4M                 |
|  exec speed : 891.7/sec             |
+-------------------------------------+

这部分让你深入了解fuzzer现在对样例输入做什么。第一行告诉你当前的变异阶段,合法的取值可以是:

  • calibration(初始阶段):预测试阶段,在该阶段检查执行路径以检测异常,确定基线执行速度等。 只要有新发现,就会非常简洁地继续执行。
  • trim L/S(裁去):预测试阶段,尝试将输入用例裁剪为最短的形式,要求修饰前的用例将仍然产生相同的执行路径。裁去长度(L)和间隔步长(S)通常根据文件大小来确定。
  • bitflip L/S(位翻转):确定性变异。在任何给定的时间对输入样例以S位为步长进行遍历,每次遍历循环对L位字节执行位翻转操作(即,每隔S位,翻转L位)。 当前的L/S有效取值为:1/12/14/18/816/832/8
  • arith L/8(算术变异):确定性变异。fuzzer尝试将输入样例中的字节减去或加上8/16/32位值。两次变异的字节位置间隔总是8位。
  • interest L/8(替代变异):确定性变异。fuzzer需要有一个已知且可能触发异常的8/16/32位值的列表来尝试替代输入样例中的字节。两次变异的字节位置间隔总是8位。
  • extras(字典变异):确定性变异。可能显示为userauto,这取决于fuzzer是使用用户通过-x提供的词典还是自动创建的词典。 您还会看到overinsert标识符,具体取决于字典单词是覆盖现有数据还是通过偏移量以适应其长度来插入其中。
  • havoc(综合变异):一种带栈随机调整的固定偏移循环。 在此阶段尝试的操作包括以上的几种变异思路,例如:位翻转、使用随机和可能触发异常的整数进行覆盖,块删除,块复制以及各种与字典相关的操作(如果首先提供字典)。
  • splice(拼接变异):最后一个变异策略,该策略在第一个完整队列周期(3.2中所述)后没有新路径发现时开始。 它等效于havoc,不同的是它首先在任意选择的中点将来自队列的两个随机输入拼接在一起。
  • sync(同步变异):仅在设置-M-S时会使用的阶段(请参见parallel_fuzzing.txt)。此阶段不涉及真正的模糊测试,但是该工具会扫描其他模糊测试器的输出,并在必要时导入输入用例。 第一次执行此操作可能需要几分钟左右。

其余字段应该是字面意思就能理解的:当前阶段的exec计数进度指示器,全局exec计数器以及当前程序执行速度。 这些值可能会在输入用例发生切换时产生波动,但是理想情况下,执行速度在大多数情况下应该超过500 execs/sec——如果它保持在100以下,则该工作可能会花费很长时间。

fuzzer还将明确警告您有关慢速目标的信息。 如果发生这种情况,请参阅perf_tips.txt文件,以获取有关如何加快速度的方案。

3.6 Findings in depth(fuzz发现信息)

+--------------------------------------+
| favored paths : 879 (41.96%)         |
|  new edges on : 423 (20.19%)         |
| total crashes : 0 (0 unique)         |
|  total tmouts : 24 (19 unique)       |
+--------------------------------------+

这为您提供了几个完成度的指标。该部分包括基于嵌入代码中的最小化算法的路径数量(这些路径将加快运行速度),以及实际上导致更好边缘分支覆盖的测试用例数量(而不是仅仅推动分支命中计数器上升)。 还有其他更详细的崩溃和超时计数器。

请注意,超时计数器与挂起计数器有所不同。 挂起计数器包括超过超时时间的所有测试用例也包括一些没有超出超时的程度但将其分类为挂起的用例。

3.7 Fuzzing strategy yields(fuzz详细信息)

+-----------------------------------------------------+
|   bit flips : 57/289k, 18/289k, 18/288k             |
|  byte flips : 0/36.2k, 4/35.7k, 7/34.6k             |
| arithmetics : 53/2.54M, 0/537k, 0/55.2k             |
|  known ints : 8/322k, 12/1.32M, 10/1.70M            |
|  dictionary : 9/52k, 1/53k, 1/24k                   |
|       havoc : 1903/20.0M, 0/0                       |
|        trim : 20.31%/9201, 17.05%                   |
+-----------------------------------------------------+

这只是另一个有关完成度的部分,他用于跟踪我们为前面讨论的每个模糊测试策略建立了多少条路径,与尝试的执行次数成比例。 这有助于令人信服地验证有关afl-fuzz采取的各种方法的有效性的假设。

本节中的trim统计信息与其余统计信息略有不同。该行的第一个数字显示从输入文件中删除的字节数占总文件字节数的比率。第二个数字对应于实现此目标所需的exec数量。 最后,第三个数字表示虽然无法删除但被认为没有效果并且被从某些更重要的确定性模糊处理步骤中所排除的字节所占的比例。

3.8 Path geometry(路径信息)

+---------------------+
|    levels : 5       |
|   pending : 1570    |
|  pend fav : 583     |
| own finds : 0       |
|  imported : 0       |
| stability : 100.00% |
+---------------------+

本部分中的第一个字段表示此次模糊测试过程达到的路径深度。 本质上,用户提供的初始测试用例被认为是Level 1。 通过传统的模糊测试可以从中得出的输入用例被认为是Level 1。 通过将它们用作后续模糊测试循环的输入而得出的结果为Level 1; 等等。因此,最大深度可以大致代表您从afl-fuzz采取的编译策略中获得的测试价值。

下一个字段表示尚未进入任何模糊测试的输入样例数量。对于fuzzer真正想要在此队列周期中访问的受青睐条目,在下一个字段中也给出了相同的统计信息(非受青睐的条目可能需要等待几个周期才能获得机会进入测试)。

接下来的字段在并行化模糊测试中显示在此实例中发现的新路径的数量、从其他实例导入的新路径的数量、相同输入在不同实例中产生行为的一致性。毕竟同样的输入有时会在待测文件中产生可变的行为。

最后一点实际上很有趣:它可以测量观察到的路径的一致性。 如果程序对于相同的输入数据始终表现相同,则它将获得100%的显示。 当该值较低但仍显示为紫色时,fuzz过程不太可能受到负面影响。 但它如果变成红色,则可能会遇到麻烦,因为这表示AFL难以区分调整输入文件的意义。

现在,大多数目标只会得到100%的显示,但是当您看到较低的数字时,有几件事情要注意确认:

  • 在待测文件中,是否使用了未初始化的内存和某些内在的熵增源。 这对AFL无害,但这可能表示待测程序存在安全漏洞。
  • 是否在尝试操纵外部永久资源,例如遗留在临时文件或共享内存对象中的资源。 通常,这是无害的,但是您可能需要仔细检查以确保程序不会过早退出。磁盘空间、SHM句柄或其他全局资源用完也会触发此情况。
  • 是否故意进行随机输出的功能。通常无害。 例如,在对sqlite进行模糊处理时,输入select random();之类的输入将触发变量执行随机路径。
  • 多线程待测程序是否正以半随机顺序单次执行。 当stability指标保持在90%左右时,这是无害的,但如果不是这样,则可能成为问题。 您可以尝试以下方法:
  • 在持久模式下,stability的细微下降可能是正常的,因为并非所有代码在重新输入时都表现出相同的行为。 但是大量的下降可能表示__AFL_LOOP()中的代码在后续迭代中行为不正确(例如,由于状态的不完全清理或重新初始化),这将使得大部分的测试资源都浪费了。

检测到变量行为的路径在<out_dir>/queue/.state/variable_behavior/目录中用匹配的条目标记,因此您可以轻松地查找它们。

3.9 CPU load(CPU利用率)

[cpu: 25%]

这个小部件显示了本地系统上的明显CPU利用率。 通过获取处于可运行状态的进程数,然后将其与系统上的逻辑核心数进行比较来计算获得。

如果该值显示为绿色,则说明您使用的CPU内核数量少于系统上可用的CPU内核数量,并且可能可以通过并行使用fuzzer以提高性能。 有关如何执行此操作的提示,请参见parallel_fuzzing.txt

如果该值显示为红色,则说明CPU可能被超额使用,此时运行其他模糊测试可能不会给您带来任何好处。

当然,此基准非常简单,它告诉您准备运行多少个进程,而不告诉您它们可能需要多少资源。 它还没有区分物理内核,逻辑内核和虚拟CPU。 这些中的每一个的性能特征都会有很大的不同。

如果要更精确的测量,可以从命令行运行afl-gotcpu实用程序。

3.10 Addendum: status and plot files

对于无人值守的操作,一些关键状态屏幕信息也可以在输出目录的fuzzer_stats文件中以机器可读的格式找到。 这包括:

  • start_timeunix 时间,指示 afl-fuzz 的开始时间
  • last_update – 此文件上次更新的unix时间
  • fuzzer_pidfuzzer 进程的 PID
  • cycles_done – 到目前为止完成的队列周期
  • execs_done – 尝试的 execve() 调用次数
  • execs_per_sec – 当前每秒执行次数
  • paths_total – 路径队列中的条目总数
  • paths_found – 通过本地模糊测试发现的路径条目数
  • paths_imported – 从其他实例导入的路径条目数
  • max_depth – 生成的数据集中的路径深度级别数
  • cur_path – 当前处理的路径条目号
  • pending_favs – 仍在等待模糊测试的受青睐路径条目的数量
  • pending_total – 等待模糊测试的所有路径条目的数量
  • stability – 行为一致的哈希表条目的百分比
  • variable_paths – 显示可变行为的测试用例数量
  • unique_crashes – 记录的唯一崩溃次数
  • unique_hangs – 遇到的唯一挂起数
  • command_line – 用于模糊会话的完整命令行
  • slowest_exec_ms– 最慢执行的实时时间,以毫秒为单位
  • peak_rss_mb – 在 mb 中进行模糊测试时达到的最大 rss 使用量

其中大多数直接映射到前面讨论的UI元素。

最重要的是,您还可以找到一个名为plot_data的条目,其中包含大多数这些字段的绘图表历史记录。 如果您安装gnuplot,则可以使用随附的afl-plot工具将其转换为不错的进度报告。

(完)