为什么反编译.NET IL代码如此容易?

为什么反编译.NET IL代码如此容易?,.net,bytecode,cil,decompiling,intermediate-language,.net,Bytecode,Cil,Decompiling,Intermediate Language,与反编译原生x86二进制文件相比,为什么将.NET IL代码反编译为源代码如此容易?(反射器在大多数时候产生了相当好的源代码,而反编译C++编译器的输出几乎是不可能的)。 是因为IL包含大量元数据吗?还是因为IL是比x86指令更高的抽象?我做了一些研究,发现下面两篇有用的文章,但都没有回答我的问题 C#和IL几乎一一对应。(对于一些较新的C#3.0特性来说,情况就不是这样了。)映射的紧密性(以及C#编译器中缺少优化器)使事情变得如此“可逆”。有许多事情使反向工程变得相当容易 类型信息。这

与反编译原生x86二进制文件相比,为什么将.NET IL代码反编译为源代码如此容易?(反射器在大多数时候产生了相当好的源代码,而反编译C++编译器的输出几乎是不可能的)。 是因为IL包含大量元数据吗?还是因为IL是比x86指令更高的抽象?我做了一些研究,发现下面两篇有用的文章,但都没有回答我的问题


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

    有许多事情使反向工程变得相当容易

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

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

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


    这些因素加在一起,意味着您拥有大量关于可执行文件的数据。Il基本上是在比本机代码编译器更接近源代码的级别上工作的。字节码工作的级别越高,一般来说,逆向工程就越容易。

    我认为您已经获得了最重要的位

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

      • 扩展Brian的正确答案


        如果您认为所有IL都很容易反编译,那么我建议您编写一个不平凡的F#程序并尝试反编译该代码。F#执行大量代码转换,因此从实际发出的IL到原始代码库的映射非常差。依我看,查看反编译的F#代码并取回原始程序比C#或VB.Net要困难得多

        有(或曾经有)相当好的C/C++反编译器,有用于不同版本的Watcom、Borland、Microsoft和其他流行编译器的库插件。例如艾达。不过,这并不意味着你说的不对,CLI是一个比x86更抽象、更高级但更干净的运行时环境。IDA不是反编译器,尽管制造它的公司确实制造了一个名为hex-rays的反编译器。x86反编译的质量远远低于jvm或msil反编译的质量。额外原因:IL必须是可验证的类型安全的,这限制了可用的优化类型,否则验证器将不能说“是的,这段代码没有违反任何规则。我将允许它运行。”