在逆向的时候我们经常会看到如下指令:
nop
nop dword ptr [eax]
nop dword ptr [eax + 00h]
nop dword ptr [eax + eax*1 + 00h]
nop dword ptr [eax + 00000000h]
nop dword ptr [eax + eax*1 + 00000000h]
虽然我们都知道他们没什么用,但是我们不知道的是为什么会出现这些指令。
理论上我们只需要一个nop就足以,但是为什么有这么多多字节组成的nop呢?
搜索一番我有了答案:
- 用于解决旧芯片的bug (https://devblogs.microsoft.com/oldnewthing/20110112-00/?p=11773)
- 用于指令对齐或者其他优化
多字节的nop在优化中用于将它之后的指令对齐到16字节,因为cpu抓取指令通常以16字节为一个单元,这样如果下一个指令是一个会被多次执行的指令(如循环最开始的一个指令),那么将不用再次抓取下一个16字节而能够直接解码。这块我不太懂但我觉得似乎有点道理。另外intel在手册中也有提到:
3.4.1.5 - Assembly/Compiler Coding Rule 12. (M impact, H generality) All branch targets should be 16-byte aligned.
另外我在bfd库中发现了相关代码,显示这些多字节指令用于填充buffer
https://android.googlesource.com/toolchain/binutils/+/f226517827d64cc8f9dccb0952731601ac13ef2a/binutils-2.23/bfd/cpu-i386.c#51
另外,多个单字节nop相比于一个多字节nop所画的的cpu时间更长,这也是一个原因
参考:
https://devblogs.microsoft.com/oldnewthing/20110112-00/?p=11773
https://stackoverflow.com/questions/43991155/what-does-nop-dword-ptr-raxrax-x64-assembly-instruction-do
https://softwareengineering.stackexchange.com/questions/158624/are-some-nop-codes-treated-differently-than-others
https://android.googlesource.com/toolchain/binutils/+/f226517827d64cc8f9dccb0952731601ac13ef2a/binutils-2.23/bfd/cpu-i386.c#51
https://stackoverflow.com/questions/27714524/x86-multi-byte-nop-and-instruction-prefix
https://news.ycombinator.com/item?id=12369414
https://stackoverflow.com/questions/18113995/performance-optimisations-of-x86-64-assembly-alignment-and-branch-prediction/18279617#18279617