与反编译本机x86二进制文件相比,为什么将.NET IL-code反编译为源代码如此容易?(Reflector在大多数情况下都会生成相当好的源代码,而反编译C++编译器的输出几乎是不可能的.)
是因为IL包含大量元数据吗?或者是因为IL是比x86指令更高的抽象?我做了一些研究,发现了以下两篇有用的文章,但它们都没有回答我的问题.
MSIL反编译器理论
C Decompiler - 快速入门
Jon Skeet.. 24
我想你已经拥有了最重要的一点.
如你所说,有更多的元数据可用.我不知道什么是由C或C++编译器发出的细节,但我怀疑远远更多的名字和类似信息都包含在IL.只要看看什么反编译器知道什么是在一个特定的堆栈帧,例如-只要86而言,你只知道栈是如何使用的 ; 在IL中你知道堆栈的内容代表什么(或者至少是类型 - 而不是语义!)
再次,正如您已经提到的,IL是比x86更高级别的抽象.x86不知道方法或函数调用是什么,或事件,属性等.IL还有其中的所有信息.
通常,C和C++编译器的优化程度要比(比如说)C#编译器大得多.这是因为C#编译器假设大多数优化仍可以在以后执行 - 由JIT执行.在某些方面,C#编译器不尝试进行大量优化是有意义的,因为JIT可以使用各种信息,但C#编译器没有这些信息.优化的代码更难以反编译,因为它远离原始源代码的自然表示.
IL被设计为JIT编译; x86被设计为本机执行(无可否认地通过微代码).JIT编译器所需的信息类似于反编译器所需的信息,因此反编译器可以更轻松地使用IL.在某些方面,这实际上只是对第二点的重述.
额外原因:IL必须是可验证的类型安全的,这限制了可用的优化类型,否则验证者将无法说"是的,这段代码不会破坏任何规则.我会允许它运行." (4认同)
Brian Mitche.. 9
有许多因素使逆向工程变得相当容易.
输入信息.这是巨大的.在x86汇编程序中,您必须根据变量的使用方式推断变量的类型.
结构体.有关应用程序结构的信息在il拆卸中更为可用.这与信息类型相结合,可为您提供大量数据.你现在处于相当高的水平(相对于x86汇编程序).在本机汇编程序中,您必须根据数据的使用方式推断结构布局(甚至它们是结构的事实).并非不可能,但更耗时.
名.了解事物的名称可能很有用.
这些东西结合起来意味着你有很多关于可执行文件的数据.Il基本上工作在比源代码更接近源的级别上.一般来说,字节码工作的级别越高,逆向工程就越容易.
我想你已经拥有了最重要的一点.
如你所说,有更多的元数据可用.我不知道什么是由C或C++编译器发出的细节,但我怀疑远远更多的名字和类似信息都包含在IL.只要看看什么反编译器知道什么是在一个特定的堆栈帧,例如-只要86而言,你只知道栈是如何使用的 ; 在IL中你知道堆栈的内容代表什么(或者至少是类型 - 而不是语义!)
再次,正如您已经提到的,IL是比x86更高级别的抽象.x86不知道方法或函数调用是什么,或事件,属性等.IL还有其中的所有信息.
通常,C和C++编译器的优化程度要比(比如说)C#编译器大得多.这是因为C#编译器假设大多数优化仍可以在以后执行 - 由JIT执行.在某些方面,C#编译器不尝试进行大量优化是有意义的,因为JIT可以使用各种信息,但C#编译器没有这些信息.优化的代码更难以反编译,因为它远离原始源代码的自然表示.
IL被设计为JIT编译; x86被设计为本机执行(无可否认地通过微代码).JIT编译器所需的信息类似于反编译器所需的信息,因此反编译器可以更轻松地使用IL.在某些方面,这实际上只是对第二点的重述.
有许多因素使逆向工程变得相当容易.
输入信息.这是巨大的.在x86汇编程序中,您必须根据变量的使用方式推断变量的类型.
结构体.有关应用程序结构的信息在il拆卸中更为可用.这与信息类型相结合,可为您提供大量数据.你现在处于相当高的水平(相对于x86汇编程序).在本机汇编程序中,您必须根据数据的使用方式推断结构布局(甚至它们是结构的事实).并非不可能,但更耗时.
名.了解事物的名称可能很有用.
这些东西结合起来意味着你有很多关于可执行文件的数据.Il基本上工作在比源代码更接近源的级别上.一般来说,字节码工作的级别越高,逆向工程就越容易.