分享自:

无需控制流恢复的静态二进制重写

期刊:Proceedings of the 41st ACM SIGPLAN International Conference on Programming Language Design and Implementation (PLDI '20)DOI:10.1145/3385412.3385972

学术研究报告:一种无需控制流恢复的静态二进制重写工具——E9Patch

一、 研究作者、机构与发表信息

本研究的主要作者为Gregory J. Duck, Xiang Gao 和 Abhik Roychoudhury,三位均来自新加坡国立大学计算机科学系。

该研究成果以论文形式发表,标题为“Binary Rewriting without Control Flow Recovery”。论文发表于2020年6月15日至20日在美国伦敦举行的第41届ACM SIGPLAN编程语言设计与实现国际会议(PLDI ‘20),并收录于该会议的论文集。论文的最终版本可通过ACM数字图书馆获取,DOI为:10.11453385412.3385972。

二、 学术背景与研究目标

本研究属于软件工程、系统安全与程序分析交叉领域,核心关注点是静态二进制重写技术。静态二进制重写是指在程序未运行的情况下,直接修改其编译后的二进制文件(可执行文件或共享库)。这项技术在软件安全与系统领域有重要应用,例如程序加固(hardening)、自动修复(repair)、打补丁(patching)、插桩(instrumentation)、调试(debugging)和优化等。其优势在于,即使软件的源代码不可用(如商业现货软件),也能直接对二进制程序进行修改。

然而,现有的静态二进制重写工具在可扩展性和鲁棒性方面面临巨大挑战。其根本原因在于,大多数工具依赖于从输入二进制文件中恢复控制流信息(例如,确定所有跳转指令的目标地址)。这是因为重写过程可能需要移动指令,导致重写后的二进制文件中跳转目标地址需要相应调整。但是,从二进制代码中静态恢复控制流信息在一般情况下是一个困难甚至不可判定的问题。因此,现有工具通常依赖于一系列简化的启发式方法或假设,例如假设程序由特定编译器编译、使用特定编程语言、或二进制文件包含调试符号/重定位信息等。这些假设在实践中的扩展性很差,使得许多先进工具无法处理非常庞大或复杂的程序(例如,网络浏览器,其二进制文件大小常超过100MB)。

鉴于上述背景,本研究提出了一个核心目标:开发一种无需任何控制流信息的、可扩展的静态二进制重写方法。 具体而言,研究旨在设计一种“控制流不可知”的重写方法论,即在重写过程中完全不需要知道、也无需改变原始二进制文件中的跳转目标集合。通过消除对控制流恢复及其相关启发式假设的依赖,构建一个能够稳健处理大型、复杂、甚至是被剥离(stripped)符号信息的二进制文件的工具。

三、 详细研究流程与方法

本研究的主要成果是设计并实现了名为 E9Patch 的静态二进制重写工具。其核心思想是采用一系列不改变原始指令跳转目标地址的修补策略,从而绕过控制流恢复。研究流程主要包括以下几个关键部分:

1. 方法论基础:现有控制流不可知策略的局限性 首先,研究梳理了三种已有的、不依赖控制流信息的指令级修补方法,作为基线(Baseline): * B0(信号处理器):将待修补指令替换为单字节的int3指令,通过触发信号(SIGTRAP)由信号处理器执行修补逻辑。此方法性能极差,不适合实际应用。 * B1(直接跳转):将待修补指令替换为五字节的相对近跳转指令(jmpq rel32),跳转到一个“蹦床”代码块执行修补逻辑后返回。此方法性能好,但要求待修补指令长度必须大于等于5字节,否则会覆盖后续指令,而覆盖后续指令又需要知道它们不是跳转目标,这违反了控制流不可知原则。 * B2(指令双关):此方法由LiteInst动态插桩工具首次提出。核心思想是“锻造”一个特殊的跳转指令,其机器码字节表示与待替换指令及其后重叠的字节完全一致。这样,一个五字节的跳转可以安全地替换一个短于五字节的指令,而不改变任何其他指令的字节表示。这是实现控制流不可知重写的关键思路。

然而,基线方法B2存在严重覆盖范围问题。由于“锻造”的跳转指令的跳转偏移量(rel32)被重叠字节的值严格约束,这个偏移量可能指向无效的内存地址(如空地址、已被占用的地址或负地址)。因此,单独使用B2通常只能覆盖部分待修补指令。

2. 核心创新:提升覆盖范围的新修补策略 为解决B2覆盖率低的问题,本研究提出了一套新的修补“策略”(Tactics):T1(填充跳转)、T2(后继驱逐)和T3(邻居驱逐)。这些策略在B2失败时依次尝试,旨在不引入控制流依赖的前提下,极大提升成功修补的概率。 * T1:填充跳转(Padded Jumps):利用x86_64指令的冗余前缀(如REX、段覆盖前缀),构造长度大于5字节的跳转指令。通过增加前缀,可以改变rel32在指令中的偏移位置,从而可能找到新的、有效的跳转目标范围。但此方法受限于原始指令长度,且每次增加前缀都使约束更严格。 * T2:后继驱逐(Successor Eviction):当无法修补当前指令(I1)时,转而“驱逐”它的直接后继指令(I2)。驱逐操作本身使用B2策略,将I2替换为一个跳转到“被驱逐者蹦床”的指令。该蹦床会执行I2的原语义后返回。驱逐改变了I2的字节表示,从而可能为修补I1创造出新的、可行的“双关”机会。此策略引入了额外的跳转层,但保持了控制流不可知性。 * T3:邻居驱逐(Neighbour Eviction):这是最复杂但威力最强的策略。当T1和T2都失败时,在待修补指令附近(-128到+127字节内)选择一个“邻居”指令(Ivictim)进行驱逐。驱逐释放的空间用于容纳两条跳转指令:Jpatch(跳转到目标修补蹦床)和Jvictim(跳转到Ivictim的被驱逐者蹦床)。然后,将原始待修补指令替换为一个短跳转(jmp rel8)指向Jpatch。这样,通过一个“双重跳转”(短跳转→Jpatch→修补蹦床)实现了修补,同时通过Jvictim保持了被驱逐指令的语义。此策略极大地增加了成功可能性,因为附近通常有大量候选邻居指令。

3. 策略应用与顺序:逆向顺序修补策略 当需要修补多个指令时,修补操作可能相互干扰(例如,一个指令的双关跳转依赖于后续指令的字节)。为此,研究提出了S1:逆向顺序修补策略。基本思想是按照地址从高到低的顺序处理待修补指令。由于双关、驱逐等操作只依赖于当前指令之后的字节,逆向处理可以避免后处理的指令影响先处理的指令。系统维护一个字节“锁定”状态,任何被修改或用于双关的字节都会被锁定,后续策略不得修改这些字节,从而保证了修补过程的一致性。

4. 内存与文件大小优化:物理页分组 由于指令双关等策略对蹦床位置有严格约束,蹦床在虚拟地址空间中可能非常分散,导致内存碎片化和输出文件膨胀。为解决此问题,本研究提出了物理页分组优化。传统方法为每个虚拟页面对应一个物理页面,即使页面内大部分是空白。物理页分组则将多个虚拟页面中的非重叠蹦床代码合并到一个物理页面中,然后将这个合并后的物理页面通过mmap映射回原来的多个虚拟地址位置。这种“一对多”的映射方式,在保持每个蹦床虚拟地址不变的前提下,显著减少了物理内存占用和输出文件大小。研究者实现了一种贪婪分区算法来自动进行分组,并允许用户通过调整粒度参数来平衡内存节省和内存映射数量。

5. 系统实现与评估对象 研究将上述方法论实现为E9Patch工具。E9Patch的输入包括原始二进制文件、指令位置/大小信息(可由外部反汇编前端提供)、待修补指令位置集合以及蹦床模板。它输出重写后的二进制文件,并集成了物理页分组优化。E9Patch被设计为底层工具,可作为二进制修复、加固、插桩等应用的基础。 为评估E9Patch,研究者进行了全面的实验,对象包括: * SPEC CPU2006基准测试套件:包含C、C++、Fortran编写的程序,编译为非PIE(位置无关可执行文件)模式以增加难度。 * 多种系统二进制文件:如pdflatex, vim, git, libc.so等。 * 大型复杂程序:Google Chrome(~152.5 MB)和Firefox(核心库libxul.so ~115 MB)网络浏览器,以证明其可扩展性。 评估的应用程序(即插桩目标)选取了两类: * A1:插桩所有jmp和条件跳转(jcc)指令,模拟基本块计数。 * A2:插桩所有可能向堆指针进行写操作的指令,用于后续的堆写加固演示。

四、 主要研究结果与分析

实验结果表明,E9Patch在覆盖率、性能和可扩展性方面都取得了显著成果。

1. 覆盖范围(Coverage) E9Patch实现了近乎完美的指令修补覆盖率。在SPEC2006基准测试的A1(跳转指令)和A2(堆写指令)应用中,平均覆盖率分别达到99.94%和99.99%。仅有极少数案例(如gamesszeusmp)因程序静态分配了巨大的.bss段,挤占了可用于放置蹦床的虚拟地址空间,导致覆盖率略低于100%(但仍高于98.5%)。如果将这些程序重新编译为PIE模式,则可达到100%覆盖率。 数据表明,单独的基线方法B2对A1和A2的平均覆盖率仅为72.79%和81.63%。而新增的策略T1、T2、T3显著提升了覆盖范围,特别是T3(邻居驱逐)策略,在处理困难案例时起到了关键作用。研究还发现,对于现代常见的PIE(位置无关可执行文件)二进制,由于其代码段加载在高地址区域,避免了负偏移地址无效的问题,使得即使基线方法B2的覆盖率也普遍高于93%。

2. 文件大小与内存优化 启用物理页分组优化后,输出二进制文件的平均大小增长得到了有效控制。对于A1和A2应用,平均文件大小分别为原文件的157.43%和130.90%。相比之下,如果禁用此优化(使用朴素的虚拟-物理页一对一映射),文件大小平均会膨胀至惊人的2239.83%568.96%。这充分证明了物理页分组在减少内存占用和文件大小方面的巨大价值。

3. 运行时性能 在SPEC2006基准测试上,E9Patch引入的运行时开销在可接受范围内。对于A1(空跳转插桩)和A2(空堆写插桩),平均性能开销分别为+110.81%+64.71%。这些开销主要来自额外的跳转指令(至少两次jmpq)。虽然高于某些采用内联插桩(inlining)并依赖控制流恢复的传统工具(如Multiverse、PEBIL报告的开销约60-70%),但E9Patch的优势在于其无需控制流信息的鲁棒性,这是为可扩展性付出的合理代价。

4. 可扩展性验证 E9Patch成功处理了超大型二进制文件。研究团队对Google ChromeFirefoxlibxul.so库进行了A2(堆写指令)插桩。对于Chrome,E9patch处理了超过260万个堆写指令位置,覆盖率100%;对于Firefox的libxul.so,处理了超过66万个位置,覆盖率同样为100%。使用Dromaeo DOM基准测试的性能评估显示,Chrome和Firefox的运行时开销分别约为+113%+46%。这证明了E9Patch能够切实扩展到现实世界中最为复杂的软件。

5. 应用案例:二进制堆写加固 作为概念验证,研究者将E9Patch应用于一个实际的安全场景:利用低胖指针进行堆空间内存错误检测。他们修改了A2的插桩逻辑,使每个堆写指令的蹦床在执行原操作前,先检查被写指针是否指向了对象的“红色区域”(redzone,对象边界外的保护区域)。这是通过结合E9Patch的插桩能力和一个支持低胖指针的运行时库(liblowfat.so)实现的。实验结果显示,与空插桩相比,增加了边界检查后,SPEC基准测试的平均开销从+64.71%上升至+127.27%,浏览器Chrome和Firefox的开销分别上升至+170%+60%。虽然开销高于源码级别的实现(后者可优化至约13%),但此演示证明了E9Patch能够在不依赖源代码的情况下,实现复杂的、安全关键的二进制级程序加固。

五、 研究结论与价值

本研究成功提出并实现了一种无需控制流恢复的静态二进制重写新范式,并通过E9Patch工具将其具体化。主要结论如下:

  1. 方法论创新:通过结合指令双关、填充、驱逐等控制流不可知的修补策略,以及逆向顺序修补物理页分组优化,构建了一套完整、可行且高效的静态重写方法论。这套方法从根本上消除了对控制流恢复及其相关启发式假设的依赖。
  2. 工具实现与验证:E9Patch工具证明了该方法的实用性。它能够以极高的成功率修补指令,保持合理的性能开销,并首次实现了对超大型、商业化、剥离符号的网络浏览器二进制文件(如Chrome、Firefox)的静态重写,突破了现有工具的可扩展性瓶颈。
  3. 应用价值:E9Patch为二进制软件的安全和系统应用(如漏洞修复、运行时加固、性能剖析、调试)提供了一个强大、鲁棒的基础设施。特别是在源代码缺失的场景下,其价值更为凸显。

六、 研究亮点

  1. 根本性突破:将静态二进制重写从依赖脆弱的控制流恢复中解放出来,通过“控制流不可知”的设计哲学,实现了工具鲁棒性和可扩展性的质的飞跃。
  2. 高覆盖率策略组合:提出的T1/T2/T3修补策略,特别是复杂的“邻居驱逐”策略,系统性地解决了指令双关覆盖率低的核心难题,将覆盖率提升至接近100%。
  3. 创新的空间优化:物理页分组优化巧妙地解决了由指令双关引入的内存碎片化和文件膨胀问题,大幅降低了实际部署的资源消耗。
  4. 卓越的可扩展性实证:成功处理超过100MB的复杂二进制文件(网络浏览器),并通过真实的安全加固案例展示了其应用潜力,这在先前的研究中是未曾实现的。
  5. 设计与实现的简洁性:E9Patch本身不包含反汇编器,而是依赖前端提供指令信息,这种低层次、模块化的设计增加了其灵活性和与不同分析技术结合的可能性。

七、 其他有价值内容

研究还讨论了E9Patch的一些局限性和假设,例如:假设被修补的代码不是自修改代码或作为数据被读取;当前实现假设输入二进制本身不使用重叠/双关指令;在处理极大量指令或地址空间严重受限时,覆盖率可能无法达到100%。这些坦诚的讨论为后续研究指明了改进方向。此外,论文详细比较了E9Patch与静态/动态重写领域众多相关工作(如Dyninst、Pin、LiteInst、Multiverse、RetroWrite等),清晰定位了本研究的独特贡献。E9Patch工具已在GitHub上开源,促进了该领域的研究和实践。

上述解读依据用户上传的学术文献,如有不准确或可能侵权之处请联系本站站长:admin@fmread.com