如何正确反汇编.NET代码?

问题描述:

我试图反汇编我的C#代码,然后在汇编语言级别进行调试。 比方说,我们有一个简单的C#方法:如何正确反汇编.NET代码?

var a = 1235; 
var b = ++a; 
var c = ++b; 

Console.WriteLine("test"); 
Console.ReadKey(); 

我发现了两种不同的方式如何获取汇编代码。 第一个是在VS中启动C#代码调试,然后打开Disassembly窗口。这里我们有以下代码。

first method assembly

一切行和汇编代码是非常简单和短,但问题是,这个汇编代码逻辑由ILDASM产生的IL代码的逻辑是不同的。

cil code

因此,这里是第二种方式。我们可以编译C#代码,使用ILDASM获得从PE文件中的IL代码,然后用ILASM生成PE文件恢复。现在我们有以下汇编代码。

second method code

正如你可以看到这个汇编代码更像是IL代码,但它包含更多的指令,这是更为复杂。

AFAIK C#编译为CIL代码,然后在这两种情况下的汇编代码。但它似乎是以第一种方式编译为汇编代码。

所以,问题是为什么第一方法的汇编代码从IL代码不同?为什么第一种方法的汇编代码与第二种方法的汇编代码不同?

+3

CLR是一个虚拟机,因此代码的区别。类似于Java的JVM。 – t0mm13b

+4

这是调试模式的代码(你可能有“禁止对模块加载JIT优化”打开),这样不是很有趣,但奇怪的是,这取决于它是否已经通过反汇编/ ILASM往返走了是不同的。 – harold

+6

这不是一个“问题”,它是一个功能。在使用Release版本并关闭Suppress JIT优化选项时,仅查看机器代码是明智的。 –

的JIT能够重新排序和合并的机器指令作为optomisation,但会尽量避免移动由PDB提供跨sequence points的影响。编译器通常会为每行代码生成一个序列点,因为您通常一次一行地遍历它。

虽然C#往往会产生每行代码多个IL指令,ILASM被明确给定每个指令并这样生成更多的序列点,留下用于JIT优化的空间更小。

+0

这个答案不是很准确,PDB文件肯定与序列点无关。 “代码行”也不是。代码提升和子表达式消除是JIT优化器知道也适用的标准优化器技术。 Backgrounder post [在这里](https://*.com/a/4045073/17034)。 –

+1

@HansPassant,我想你可能会把PDB的序列点概念和C++概念混淆在同一个名字中。 pdb使用术语“序列点”来描述[IL 源映射](https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/diagnostics/isymunmanagedmethod-getsequencepoints-method)。这个想法是,一个调试器很可能会停止在一个“序列点”的开始处,所以JIT会尝试使这些事情看起来一致(除非通过我链接到的文章中描述的DebuggableAttribute标志来告知其他事项在答案中。) –

+0

感谢您的教训,从未见过这个术语的用法。未来的读者可能想知道,优化器不会尝试保持PDB信息的相关性。使用默认的项目配置,PDB甚至不包含任何行号信息。 –