x86多字节NOP和指令前缀

x86多字节NOP和指令前缀,x86,prefix,nop,X86,Prefix,Nop,回想一下,x86体系结构将0x0F 0x1F[mod R/M]定义为多字节NOP 现在我来看一个8字节NOP的具体例子:我有 0x0F 0x1F 0x84 0x__ 0x__ 0x__ 0x__ 0x__ 其中最后5个字节具有任意值 第三个字节,[mod R/M],拆分后给出: mod=10b:参数是reg1+一个DWORD大小的位移 reg2=000b:(我们不在乎) reg1=100b:表示参数是SIB字节+DWORD大小的位移 现在,作为一个具体的例子,如果我 0x0F 0x1F

回想一下,x86体系结构将
0x0F 0x1F[mod R/M]
定义为多字节NOP

现在我来看一个8字节NOP的具体例子:我有

0x0F 0x1F 0x84 0x__ 0x__ 0x__ 0x__ 0x__
其中最后5个字节具有任意值

第三个字节,
[mod R/M]
,拆分后给出:

  • mod=10b
    :参数是
    reg1
    +一个DWORD大小的位移
  • reg2=000b
    :(我们不在乎)
  • reg1=100b
    :表示参数是
    SIB
    字节+DWORD大小的位移
现在,作为一个具体的例子,如果我

0x0F 0x1F 0x84 0x12 0x34 0x56 0x78 0x9A
我有

  • SIB=0x12
  • 位移=0x9A785634:双字
现在,我添加了
0x66
指令前缀,以指示位移应该是单词而不是DWORD:

0x66 0x0F 0x1F 0x84 0x12 0x34 0x56 0x78 0x9A
我希望
0x78 0x9A
被“切断”,并被视为新指令。但是,在编译此文件并对生成的可执行文件运行
objdump
时,它仍然使用所有4个字节(一个DWORD)作为置换


在这种情况下,我是否误解了“位移”的含义?或者
0x66
前缀对多字节NOP指令没有任何影响吗?

前缀
66H
将操作数的大小覆盖为16位。
如果希望使用
67H

下面是所有操作数的列表

        F0h = LOCK  -- locks memory reads/writes
        String prefixes
        F3h = REP, REPE  
        F2h = REPNE      
        Segment overrides
        2Eh = CS
        36h = SS
        3Eh = DS
        26h = ES
        64h = FS
        65h = GS
        Operand override 
        66h. Changes size of data expected to 16-bit
        Address override 
        67h. Changes size of address expected to 16-bit
但是,最好不要创建自己的nop指令,而是坚持推荐的(多字节)nop

根据AMD,推荐的多字节NOP如下:

表4-9。NOP指令的推荐多字节序列

bytes  sequence                encoding

 1      90H                            NOP
 2      66 90H                         66 NOP
 3      0F 1F 00H                      NOP DWORD ptr [EAX]
 4      0F 1F 40 00H                   NOP DWORD ptr [EAX + 00H]
 5      0F 1F 44 00 00H                NOP DWORD ptr [EAX + EAX*1 + 00H]
 6      66 0F 1F 44 00 00H             NOP DWORD ptr [AX + AX*1 + 00H]
 7      0F 1F 80 00 00 00 00H          NOP DWORD ptr [EAX + 00000000H]
 8      0F 1F 84 00 00 00 00 00H       NOP DWORD ptr [AX + AX*1 + 00000000H]
 9      66 0F 1F 84 00 00 00 00 00H    NOP DWORD ptr [AX + AX*1 + 00000000H]
Intel不介意最多3个冗余前缀,因此nop最多可以像这样构造11个字节

 10     66 66 0F 1F 84 00 00 00 00 00H     NOP DWORD ptr [AX + AX*1 + 00000000H] 
 11     66 66 66 0F 1F 84 00 00 00 00 00H  NOP DWORD ptr [AX + AX*1 + 00000000H]
当然,您也可以通过在普通指令前加上冗余前缀来消除NOP

e、 g

或者强制cpu使用相同指令的更长版本

test r8d,r8d is one byte longer than: test edx,edx
具有立即操作数的指令有短版本和长版本

and edx,7 //short
and edx,0000007  //long
大多数汇编程序都会帮助您缩短所有指令,因此您必须自己使用
db

在战略位置穿插这些可以帮助您对齐跳转目标,而不必因解码或执行nop而导致延迟


请记住,在大多数CPU上,执行nop仍然会消耗资源

0x66前缀不影响位移的大小,只影响操作数大小。因此,如果指令的组成部分是:前缀、操作码、Mod R/M、SIB、位移、立即数,那么0x66会影响立即数值吗?好吧,这有点道理:多字节NOP不接受立即值,因此0x66没有任何效果。非常感谢。相关:假设您可以编写
添加rax,dword 1
来强制32位立即数而不是imm8,但实际上它不起作用。(使用Ubuntu软件包中的yasm 1.3.0,仍然缩写为imm8)。此外,即使指令不需要REX前缀,也可以使用REX前缀进行填充(无需更改寄存器)
db 0x40
/
测试edx,edx
仍解码为
测试edx,edx
。未设置位的REX仅改变使用AH/BH/CH/DH的insn的含义。(对于SIL等,我必须以某种顺序查找)更新:YASM:
添加rax,严格dword 1强制32位立即数。NASM:
添加rax,严格的qword 1
强制32位立即数。NASM和YASM都被另一个噎住了,抱怨尺寸问题。但无论如何,
strict
是NASM语法中用于即时和寻址模式置换的基本关键字。我建议避免使用
rep
前缀,因为英特尔以后往往会赋予它们新的含义。例如,
rep mov
在英特尔HLE now中有一个含义。@fuz:另一方面,rep imul是一个有趣的概念。
and edx,7 //short
and edx,0000007  //long