RetroWrite: Statically Instrumenting COTS Binaries for Fuzzing and Sanitization 论文阅读笔记

黎 浩然/ 20 10 月, 2023/ 动态分析/DYNAMICANALYSIS, 研究生/POSTGRADUATE/ 0 comments

PIC & Relocatable Code

位置无关代码 (PIC, Position-Independent Code) 和 可重定位代码 (Relocatable Code) 是编译器和链接器生成的两种类型的机器代码。这两者都是为了解决程序代码在内存中位置的问题,但它们的解决方法和目标有所不同。下面是对这两者的描述: 位置无关代码 (PIC)

  • 定义: 位置无关代码是可以在内存中的任何位置运行,而不需要进行修改的代码。
  • 用途: 它主要用于共享库 (如 Unix/Linux 系统中的 .so 文件)。因为多个应用程序可能会共享一个共享库的一个副本,所以这个库需要能够被加载到任意地址并运行。
  • 实现: 为了实现位置无关性,PIC 使用了间接寻址和特殊的跳转技术,这些技术允许代码在不同的地址运行。当程序运行时,动态链接器会处理这些间接寻址和跳转。
  • 缺点: 由于需要使用额外的间接寻址,PIC 可能会有轻微的运行时性能损失。

可重定位代码 (Relocatable Code)

  • 定义: 可重定位代码是编译时生成的,但它的地址还没有最终确定。这意味着它可以被链接器移动到一个最终的地址。
  • 用途: 它是为了在链接时能够和其他可重定位的代码块合并在一起。这是创建静态库 (例如 .a 文件) 和最终的可执行文件的中间步骤。
  • 实现: 在编译时,编译器会为每个外部引用创建一个重定位记录。链接器使用这些记录来更新代码,使其指向正确的地址。
  • 缺点: 这些代码不能直接执行,因为它们的地址还没有最终确定。它们需要通过链接器与其他代码块链接在一起才能形成可执行的程序。

总之两种代码都是为了解决代码在内存中的位置问题。位置无关代码适用于在运行时动态加载的代码,如共享库,而可重定位代码是为了在链接时能够和其他代码块组合在一起。

RetroWrite

二进制重写允许对二进制文件进行后编译(Post-Compilation)修改。特别,可以添加或删除指令以实施新的安全属性或删除不需要的功能,同时仍然保持可执行二进制文件。然而重写二进制文件并不像编辑源代码那样简单,主要是因为二进制文件缺乏类似源代码的 Abstract 。二进制文件缺乏类型信息,数据结构的Abstract 被压平为 Raw 内存访问。此外,用于链接到特定位置(例如调用的函数或者跳转的代码块)所需的符号信息丢失) 。直接添加或删除二进制代码会导致这些地址发生变化,从而破坏二进制文件。 因此,使用二进制重写进行安全审计面临许多你先工程挑战,以恢复关于二进制文件的足够信息,以实施所需的安全属性。

  1. 预处理。第一步是加载Reassembly所需的二进制文件的.text和.data部分。RetroWrite也加载了来自二进制文件的辅助信息,如符号和重定位信息。 这一步还包括使用线性扫描进行反汇编并恢复最大努力的控制流图CFG:识别并添加Direct CFG的Edge。RetroWrite 不需要重量级分析来Indirect CF的目标,从而限制了分析时间并适用于更大的二进制文件。
  2. 符号化。符号化是RetroWrite重写过程核心。RetroWrite使用加载阶段的重定位信息和恢复的 CFG 来识别数据和代码部分中的可符号化常量,并将它们转换为汇编器标签。 在这一步的最后,RetroWrite 输出可重汇编的汇编 Reassembleable Assemble。这个可重汇编的汇编 进一步由其他工具或在 RetroWrite 中开发的工具或插桩过程来处理。
  3. 插桩程序。插桩程序在Reassembleable Assemble进行操作,对目标二进制进行插桩和修改.
  4. 插桩优化。RetroWrite分析插桩来确定所需的寄存器数量和Side Effect。然后使用这些分析的结果来:(i) 在每个插桩点前(和后)选择性地保存(和恢复)状态变化, 例如,条件标志,以及(ii)为插桩分配寄存器。
  5. 重新汇编。作为最后一步,RetroWrite生成一个插桩的汇编文件。在使用任何现成的汇编器汇编成二进制文件之前,其他编译器生成的汇编文件的工具可能会进一步处理这个文件.

优势是不需要将汇编提升到更高级的中间语言(IR),这个过程需要对指令集架构(ISA)进行精确建模。 捕获指令语义, 从反汇编提升到 IR 是困难的,必须基于每种架构进行实现,并且已知容易出错。RetroWrite轻量级,可以直接在任何现成的反汇编器生成的反汇编上工作。

American Fuzzy Lop

a) AFL

AFL 是研究和实践中最受欢迎和最有效的模糊测试工具之一。为减轻收集和分析完整程序追踪成本,AFL 采取一种更实际的方法,通过跟踪边缘覆盖率作为程序追踪的近似值。AFL 在共享内存中维护一个恒定大小的Bitmap,以跟踪应用程序运行期间的边缘命中统计。编译时,CFG中的每一个基本块的开始都被插桩来收集边缘覆盖统计数据(Edge Coverage Statistics)。在静态情况,每个基本块被分配一个Key,eBitmap Index Ie是动态计算的:Ie=cur⊕(prev>>1),其中cur和prev分别对应于当前基本块和前驱基本块的 Key。AFL 在编译期间对程序进行插桩,可以通过afl-gcc或通过更优的llvm模式,afl-clang-fast。

b)Binary AFL

要在RetroWrite上实现Binary-level Coverage插桩需要恢复CFG并在基本代码块开始进行插桩:计算Edge Index并更新Bitmap State。作为RetroWrite的符号化过程一部分,CFG被隐式恢复。

原始的AFL实现是在汇编级别对应用程序进行插桩,即afl-gcc是在编译过程中解析并插桩汇编文件,生成一个插桩的二进制程序。由于RetroWrite生成的汇编文件与编译器生成的文件非常相似,所以afl-gcc能够直接生成AFL 插桩的二进制文件,无需额外的工作。RetroWrite 产生的汇编与现有的在汇编上操作的工具兼容。

Share this Post

Leave a Comment

您的邮箱地址不会被公开。 必填项已用 * 标注

*
*