2008-10-24 129 views
69

我正在通过MSIL,并注意到有很多nop说明。 MSDN文章说,他们不采取任何行动,如果操作码被修补,则用于填充空间。它们在调试构建中比发布构建中用得更多。我知道在汇编语言中使用这些类型的语句来确保操作码符合字边界,但为什么它在MSIL中需要?nop操作码的用途是什么?

+18

这些答案在语言编译程序发出的MSIL nop指令与程序集运行时JIT编译器发出的x86 nop指令(在该平台上)之间存在质量混淆。 [事实上,接受的答案是关于x86的nops,并且与MSIL没有任何关系。] 理想情况下,这个问题应该分成两个不同的问题:MSIL :: nop的目的是什么?和本地平台nop的目的? – 2009-11-29 19:57:38

回答

88

的NOP一举数得:

  • 他们允许调试器放置一个断点,即使与他人在生成的代码相结合的线。
  • 它允许加载程序用不同大小的目标偏移量来跳转跳转。
  • 它允许一段代码在特定的边界对齐,这对缓存很有好处。
  • 它允许增量链接以调用新的部分来覆盖代码块,而不必担心整体功能会改变大小。
8

它为代码中的基于行的标记(例如断点)提供了一个机会,在该代码中,发布版本不会发出任何消息。

+0

断点是否需要一个nop?为什么不在普通操作码上放一个断点? – 2008-10-24 19:23:43

+4

许多常规操作码在发布版本中得到了优化。这会弄乱你的断点,除非有一个占位符,否则断点仍然指向 – Jimmy 2008-10-24 19:28:49

+1

在调试版本中,它们也被用来提供一个指令来打破代码没有指令的地方。例如,打开大括号。 – 2008-10-24 19:31:29

3

它们的一个经典用法是让调试器始终可以将源代码行与IL指令相关联。

+0

难道这不可能发生吗? – 2008-10-24 19:24:17

+0

由于msil在运行时编译为JIT,因此可能会丢失该映射(例如,本机指令没有唯一的MSIL指令)。 NOP被用作从语言编译器到JIT编译器的通信机制来保存这种映射。 – 2008-12-28 18:18:50

4

老兄! No-op太棒了!这是一种无用之事,但消耗时间的指令。在昏暗的黑暗时代,您可以用它在关键循环中进行微调,或者更重要的是作为自我修改代码中的填充。

1

它们允许链接器用较短的指令(短跳转)替换较长的指令(通常是跳转)。 NOP需要额外的空间 - 代码不能移动,因为它会阻止其他跳转工作。这发生在链接时间,所以编译器无法知道长短跳是否合适。

至少,这是他们的传统用途之一。

3

他们可能会在调试时使用它们来支持编辑和继续。它为调试器提供了工作空间,可以在不更改偏移量的情况下替换旧代码。

1

这不是您的具体问题的答案,但回到过去,您可以使用NOP填充branch delay slot ,如果你不能用其他有用的指令填写它。

1

.NET编译器对齐MSIL输出吗?我想这对于加速访问IL可能是有用的......另外,我的理解是,它的设计是可移植的,并且在一些其他硬件平台上需要对齐访问。

2

在软件破解的场景中,解锁应用程序的一个经典方法是使用NOP补丁来检查密钥或注册或时间段,或者不会做任何事情的行,并简单地继续启动应用程序如果它被注册。

2

我也看到了代码中的NOP,它修改自身以混淆它作为占位符(veeery旧版本保护)的功能。

1

的第一组装我的教训是SPARC所以我很熟悉的分支延迟槽,如果你不能与其他指令填充它,通常是指示你打算把分支指令高于或递增计数器循环,你使用NOP。

我不熟悉的开裂,但我认为这是常见的覆盖使用NOP所以你没有精确地计算你的恶意功能开始在堆栈。

4

它也可能使代码运行速度更快,在为特定处理器或体系结构优化时:

处理器很长一段时间采用的是并行粗略工作的多个管道,让两个独立的指令能够在同一时间exceuted。在具有两个管道的简单处理器上,第一个可以支持所有指令,而第二个仅支持一个子集。另外,当需要等待前一条尚未完成的指令的结果时,管道之间会有一些停顿。

在这种情况下,专用NOP可能迫使下一个指令到特定的管道(第一,或不是第一次),并提高了以下说明配对,以便NOP的费用比较比摊销。

3

Nop在缓冲区溢出攻击的有效负载中非常重要。

3

正如ddaa所说,nops让你在堆栈中考虑了方差,这样当你覆盖返回地址时,它会跳转到nop sled(连续很多nops),然后正确地命中可执行代码,而不是跳转到不是开始的指令中的某个字节。

9

下面是空指令是如何被用于调试:

瑞尔是由语言编译器使用(C#,VB等)来定义隐序列点。这些告诉JIT编译器在哪里确保机器指令可以映射回IL指令。

DebuggingModes.IgnoreSymbolStoreSequencePoints里克·拜尔的博客文章,解释了一些细节。

C#还会在呼叫指令后放置Nops,以便源中的返回站点位置是呼出而不是呼叫后的线路。

1

我使用NOP来自动调整进入ISR后累积的延迟。非常方便指定时间死亡。

2

一种略为非正统的用途是NOP-Slides,在缓冲区溢出攻击使用。

3

50年太晚了,但嘿。

如果您手动输入汇编代码,则Nop's非常有用。 如果您必须删除代码,则可以禁用旧操作码。

similary,你可以通过覆盖一些操作码并在其他地方跳转来插入新的代码。在那里你把覆盖的操作码,并插入你的新代码。准备好后,你跳回来。有时你不得不使用可用的工具。在某些情况下,这只是一个非常基本的机器码编辑器。

现在使用编译器,这些技术再也没有意义了。

2

在我最近工作的一个处理器(四年)中,NOP被用来确保在下一个操作开始之前先前的操作完成。例如:

负载值寄存器(需要8个周期) NOP 8 加1寄存器

这确信寄存器有该加载操作之前的正确的值。

另一个用途是填充执行单元,例如中断向量必须是一定的大小(32字节),因为vector0的地址是0,对于向量1 0x20等等,所以编译器把NOP在那里如果需要的话。