18

与反编译本机 x86 二进制文件相比,为什么将 .NET IL 代码反编译为源代码如此容易?(Reflector 大部分时间都产生了相当不错的源代码,而反编译 C++ 编译器的输出几乎是不可能的。)

是因为 IL 包含很多元数据吗?还是因为 IL 是比 x86 指令更高的抽象?我做了一些研究,发现了以下两篇有用的文章,但它们都没有回答我的问题。

4

4 回答 4

26

我想你已经掌握了最重要的部分。

  • 正如您所说,还有更多可用的元数据。我不知道 C 或 C++ 编译器发出的详细信息,但我怀疑IL中包含更多名称和类似信息。只需看看反编译器对特定堆栈帧中的内容的了解,例如 - 就 x86 而言,您只知道堆栈是如何使用的;在 IL 中,您知道堆栈的内容代表什么(或者至少是类型 - 而不是语义!)
  • 同样,正如您已经提到的,IL 是比 x86 更高级别的抽象。x86 不知道方法或函数调用、事件或属性等是什么。IL 中仍然包含所有这些信息。
  • 通常,C 和 C++ 编译器的优化程度远高于(例如)C# 编译器。这是因为 C# 编译器假定大部分优化仍然可以在以后执行 - 由 JIT。在某些方面,C# 编译器不要尝试做太多优化是有意义的,因为 JIT 可以使用各种信息,但 C# 编译器不能使用。优化后的代码更难反编译,因为它远离原始源代码的自然表示。
  • IL 被设计为 JIT 编译;x86 被设计为在本机执行(诚然通过微代码)。JIT 编译器需要的信息与反编译器所需的信息相似,因此反编译器使用 IL 更容易。在某些方面,这实际上只是对第二点的重申。
于 2009-03-22T19:20:20.360 回答
9

有很多事情可以让逆向工程变得相当容易。

  • 类型信息。这是巨大的。在 x86 汇编器中,您必须根据变量的使用方式来推断变量的类型。

  • 结构体。有关应用程序结构的信息在 il disassemblies 中提供更多信息。这与类型信息相结合,为您提供了惊人的数据量。此时您的工作水平相当高(相对于 x86 汇编器)。在本机汇编程序中,您必须根据数据的使用方式来推断结构布局(甚至它们是结构的事实)。并非不可能,但更耗时。

  • 名字。知道事物的名称可能很有用。

这些东西结合起来,意味着你有很多关于可执行文件的数据。与本机代码的编译器相比,Il 基本上在更接近源代码的水平上工作。一般来说,字节码工作的级别越高,逆向工程就越容易。

于 2009-03-22T19:17:12.287 回答
4

C# 和 IL 几乎是一对一的映射。(对于一些较新的 C# 3.0 功能,情况就不那么好了。)映射的紧密性(以及 C# 编译器中缺少优化器)使得事情变得如此“可逆”。

于 2009-03-22T18:36:01.073 回答
3

扩展布赖恩的正确答案

如果您认为所有 IL 都很容易反编译,我建议您编写一个重要的 F# 程序并尝试反编译该代码。F# 做了很多代码转换,因此从实际发出的 IL 和原始代码库的映射非常差。恕我直言,与 C# 或 VB.Net 相比,查看反编译的 F# 代码并取回原始程序要困难得多。

于 2009-03-22T19:21:55.143 回答