我正在通过MSIL,并注意到MSIL中有很多nop指令。
MSDN文章说,如果对操作码进行了修补,它们将不采取任何措施,并被用来填充空间。在调试版本中使用的版本比发行版本更多。
我知道在汇编语言中会使用这些类型的语句来对齐后面的指令,但是为什么在MSIL中需要MSIL nop?
((编者注:接受的答案是关于机器代码NOP的,而不是问题最初询问的MSIL / CIL NOP。)
NOP有几个用途:
至少,这是他们的传统用途之一。
我不熟悉破解,但是我认为使用NOP覆盖堆栈很常见,因此您不必精确计算恶意功能的开始位置。
以下是调试如何使用MSIL / CIL nops(不是x86机器代码nop
):
语言编译器(C#,VB等)使用Nops定义隐式序列点。这些告诉JIT编译器在哪里确保可以将机器指令映射回IL指令。
[Rick Byer在nop
上的博客条目,解释了一些细节。
C#还将Nops放在呼叫说明之后,以便源中的返回站点位置是呼叫,而不是呼叫之后的行。
[它为代码中的基于行的标记(例如断点)提供了机会,而发行版本不会发出任何标记。
当针对特定处理器或体系结构进行优化时,它还可以使代码运行更快:
长时间以来,处理器采用了多个并行运行的流水线,因此可以同时执行两条独立的指令。在具有两个管线的简单处理器上,第一个可以支持所有指令,而第二个仅支持一个子集。另外,当必须等待尚未完成的上一条指令的结果时,在流水线之间会有一些停顿。
在这种情况下,专用的[[nop可能会迫使下一条指令进入特定的流水线(第一条,或者不是第一条),并改善后续指令的配对,从而使nop的成本下降被摊销了。
将值加载到寄存器(需要8个周期)8月加1进行注册
这确保在进行加法运算之前,寄存器具有正确的值。
另一个用途是填充执行单元,例如中断向量必须为一定大小(32字节),因为向量0的地址为0,向量1的地址为0x20,依此类推,因此编译器将NOP放入如果需要的话。
edit-and-continue”。它为调试器提供了工作空间,可以在不更改偏移量的情况下用新代码替换旧代码,等等。
Nop很有用,如果您手动键入汇编代码。如果必须删除代码,则可以不输入旧的操作码。
类似地,您可以通过覆盖一些操作码来插入新代码,然后跳转到其他地方。在这里放置了覆盖的操作码,然后插入新代码。准备好后,您跳回去。
有时您必须使用可用的工具。在某些情况下,这只是一个非常基本的机器代码编辑器。
如今,对于编译器而言,这些技术已经毫无意义了。