LibAFL QEMU: A Library for Fuzzing-oriented Emulation EURECOM 2024

Romain Malmain, Andrea Fioraldi, Aurelien Francillon

论文笔记、部分代码实现分析

BACKGROUND

Why not hardware-assist emulation

当主机和目标体系结构相似时,虚拟化这样的硬件辅助方法在获得接近本机的性能方面特别有效。然而,这种技术不能用于模拟外来的体系结构。它还限制了我们在运行时对目标的控制,因为管理程序在执行自省之前必须等待虚拟机停止。

<aside> 💡 但是 Nyx 团队一直在做基于硬件虚拟化的研究,并且 KVM 在速度提升上的效果也是有目共睹的。所以支持 Fuzz 的时候开启相关的硬件虚拟化,也是很有潜力的研究方向[Google Code Summer 2024]。

</aside>

所以本文选择以软件模拟作为研究方向,软件模拟当前可以分为:

  1. 动态二进制转换 (DBT),它在运行时将目标二进制代码转换为主机指令。
  2. 静态二进制转换 (SBT) 将二进制作为输入,完全重写它(同时应用任何所需的转换),并输出另一个可执行二进制。

SBT vs DBT

  1. 首先,到目前为止,我们还不知道有什么工具能够为任何现有体系结构重写完整的系统二进制文件。这是一个巨大的问题。同时,许多动态仿真器可以完全模拟多种架构,包括x86、ARM、MIPS、PowerPC、RISC-V等。
  2. 另一方面,静态重写通常需要编写复杂的额外代码来与给定的 ISA 进行交互。
  3. 此外,许多静态重写器通常假定目标二进制文件强制了某种结构,而仿真器没有做任何假设。
  4. 模拟器还可以完全访问宝贵的运行时信息,这是静态重写器无法轻易获得的。
  5. SBT还面临着一个理论问题:disassembly is undecidable。

所以从这些可扩展性和易用性方面来考虑,本文关注于 DBT

QEMU and the Tiny Code Generator

尽管QEMU可以通过利用KVM来利用硬件辅助的虚拟化,但我们将只关注仿真。仿真依赖于 QEMU 的 TCG 模型,大致分两个步骤完成:

  1. (前端)首先,目标代码被翻译成称为TCG (Tiny code Generator) 的中间表示。
  2. (后端)中间代码通过优化管道后,被翻译成主机体系结构的机器语言。

TCG的编译单元是一个翻译块 (TB),在大多数现代编译器中相当于一 Basic Block。QEMU具有多层 TB 缓存,广泛用于大幅提高性能。因此,如果尚未缓存,则会动态生成TB。QEMU还尽可能将TB链接在一起以加快执行速度,并在必须执行进一步翻译时链接回模拟器。QEMU还允许在目标仿真期间直接执行任何C代码,称为辅助函数。

这个背景知识与 AFL++ 中关于 QemuAFL TB Chain 的优化相关

  1. AFL 中的 qemuafl 在 TCG engine 执行一次 execute(TB) 前回调 afl_maybe_log(),如果开启了 TB chain 则一次对 execute(TB) 的调用会连续执行多个 TB,丢失部分 Block 的覆盖信息。AFL++ 中将 afl_maybe_log() 以 helper 的形式 inline 到了每个 TB 的头部,就又可以开启 TB chain 的优化了。
  2. ForkServer 的执行形式会导致子进程的 TB cache 丢失,qemuafl 中实现了 TB cache 同步机制,在 TB Translate 完毕时将结果同步到 forkserver 父进程,那么下次 fork() 出的子进程也会带有该 TB cache,不用再 Translate 一次。

APPENDIX: TCG control flow