为什么没有任何可以生成可重新组装的汇编代码的反汇编程序?

逆向工程 艾达 拆卸 x86 反汇编者 重新组装
2021-06-10 01:19:46

我在这个问题上挣扎了大约三个月:

如何使用反汇编器(IDA Pro 和其他...)生成可重新组装的汇编代码并将其组装回去

我的经验是:

  1. 没有工具可以在 32 位 x86 上生成可重新组装的 asm 代码。

  2. 您需要调整/启发式修改由 IDA Pro 创建的 asm 代码以使其可重新组装。

  3. 可以自动调整/启发式修改良性程序的进程(一个没有混淆的程序)。

  4. 非常繁琐,而且VS编译的PE二进制文件比GCC编译的ELF二进制文件复杂得多。

所以我的问题是:

  1. 为什么没有任何反汇编程序可以生成针对良性程序(没有混淆的程序)的可重新组装的 asm 代码

  2. 如果我想实现这样一个工具(没有IDA Pro的帮助,从头开始画草图),有可能吗?

  3. 是否还有其他与此相关的问题我可能忽略了?

4个回答

因为这真的很难做到。

详细说明:

您还需要提取不是代码的东西。想想导入表、导出表、字符串和其他数据。

当您编写代码时,这只是程序的一部分。另一部分是编译器优化和数据部分。这使得几乎不可能创建可重新编译的程序集。如果您想在汇编级别编辑程序,我建议您使用windbg 和LordPE。

这是来自 IDA Pro 的书,但即使 IDA 再好,最终仍然是猜测。这里的答案来自 Chris Eagle 的“The IDA Pro Book”。

  1. “为什么没有任何反汇编程序可以生成针对良性程序(没有混淆的程序)的可重新组装的 asm 代码?”

编译过程是有损的。

在机器语言层面,没有变量名或函数名, 变量类型信息只能由数据的使用方式决定,而不是明确的类型声明。当您观察到正在传输的 32 位数据时,您需要进行一些调查工作以确定这 32 位是代表整数、32 位浮点值还是 32 位指针。

编译是一个多对多的操作。

这意味着源程序可以通过多种不同的方式被翻译成汇编语言,而机器语言可以通过多种不同的方式被翻译回源代码。因此,编译一个文件并立即反编译它可能会产生与输入的源文件截然不同的源文件,这是很常见的。反编译器非常依赖于语言和库。使用旨在生成 C 代码的反编译器处理由 Delphi 编译器生成的二进制文件可能会产生非常奇怪的结果。同样,通过不了解 Windows 编程 API 的反编译器提供已编译的 Windows 二进制文件可能不会产生任何有用的信息。

基本上,在这一点上它仍然需要人为判断。我听过的最好的比喻是,从源代码编译二进制文件就像计算哈希。

  1. “如果我想实现这样一个工具(没有IDA Pro的帮助,从头开始画草图),有可能吗?”

这对我来说听起来像是一个有趣的理论研究问题:编译真的可以被视为生成哈希签名吗?我的直觉说“是的”。数学将非常复杂,并且可能必须使用可证明的语言来完成。我们通常使用哈希,因为它们不容易逆向工程。但是,您仍然可以使用彩虹表之类的东西来攻击哈希,因此需要考虑一个大型项目。我的直觉告诉我,所有可能的二进制文件的彩虹表都是 NP-Complete。

还要考虑到确定数据类型有点需要人工判断,我们仍然不太擅长自动化那种智能。是否可以?或许。聪明人仍然制作像 IDA 这样的工具是有原因的。

  1. “我可能忽略了与此相关的任何其他问题吗?”

我是拆卸的新手,所以我会把它留给大男孩,但希望至少我回答了为什么你问的事情很难做到的问题。

鹰,克里斯(2011-06-16)。IDA 专业书籍:世界上最受欢迎的反汇编程序的非官方指南(Kindle 位置 151-152)。无淀粉压榨机。Kindle版。

不过,你的问题很有趣,并不是什么新鲜事。

许多人已经使用我们所谓的二进制重写来进行分析。例如,DynInstMAQAO会这样做来分析应用程序,以便定位基本块中的瓶颈。现在您可能会问自己的问题是它是如何完成的?简单的。大多数可用的反汇编程序,如objdumpobjconvIDA等,都在独立模式下工作,通常会打印反汇编指令,但udis86distorm等其他反汇编程序除了在独立模式下可用外,提供一个 API 来访问反汇编代码。但是,什么DynInst , MAQAO,并且大多数二进制重写工具所做的是在重新组装二进制文件之前反汇编二进制文件并在数据结构中的任何适当位置插入探针。因此,所有与地址、分支、上下文保存等相关的必要更改都在重新组装之前得到了正确处理。

您必须知道的是,编写此类工具极其困难。第一个挑战是编写一个可靠的反汇编程序。这当然意味着选择反汇编算法(线性扫描与递归遍历),将指令与数据分离(它们可以混合 - 例如 shellcode),等等。然后是第二个挑战,修补反汇编的代码。这是非常棘手的,我会指出这个应该有很大帮助的文档:http : //www.maqao.org/publications/techreports/madras_techreport.pdf它是由MAQAO中使用的反汇编程序的作者编写的MADRAS - Multi Architecture Disassembler Rewriter and Assembler)。本文档有趣的部分是参考文献(超过 50 篇并且非常有用)和描述所使用算法的附录。

尽管我对MAQAODynInst都不熟悉,但我建议您查看有关它们的出版物(文档、科学论文……)。我还建议您检查PEBIL(PMaCs Efficient Binary Instrumentation Toolkit)、Intel 的PINValgrindPLTOElfsh/ERESIEtch

这些工具中的大多数都广泛地执行二进制重写和修补,我相信它们是如何进行二进制重写的好例子。

我希望我的回答能帮助你找到你要找的东西。

到目前为止,已建立的二进制重写方法是动态重写,其中二进制在实际输入上运行时被重写。想想像PINDynamoRIODyninst这样的检测工具以及qemu这样的二进制转换器

与动态重写相比,静态重写工具有一个基本挑战,后者是精确的控制流图恢复。也就是说,对于二进制文件中的每个基本块,我们需要知道其跳转指令的可能目标集。难点在于二进制文件有很多间接跳转指令。举例来说,如果我们面临的一个基本模块,与两端bx r3那么我们需要有一个精确可靠的值设置分析(VSA),它可以告诉我们,可能的值r3可以在运行时。不幸的是,这种分析通常是不可判定的。然而,行为良好的编译器会生成以某种方式结构化的二进制文件,这在很大程度上是有用的。

请注意,解决 CFG 恢复问题将使我们能够解决作为副产品的代码/数据分离问题。也就是说,在这种情况下,递归下降反汇编将允许我们将代码与代码字节流中的数据完美分离。

这里可以参考去年USENIX Security中介绍的如下论文:

王帅、王沛、吴丁浩:可 拆装USENIX 安全 2015:627-642

他们的工具 Uroboros开源的。它基于使用objdump 的迭代线性扫描反汇编反汇编技术本身在之前的一篇论文中讨论过。尽管如此,它为实际工作的静态二进制重写提供了有趣的技术(或者至少这是他们的主张)。他们甚至多次重写相同的二进制文件而不会破坏它。最后,请注意静态二进制重写在很大程度上不适用于具有运行时代码生成的二进制文件。

更新

似乎这里讨论的许多缺点Uroboros已经得到解决 Ramblr

王等 阿尔。Ramblr:使重组再次伟大”,网络和分布式系统安全研讨会 (NDSS'17) 的论文集。2017年

特别是,他们提到他们重新组装的二进制文件没有执行开销或大小扩展。