opcode

1.指令编码(硬编码)的结构图

详细表格见Intel手册卷2 B————>APPENDIX A OPCODE MAP————>ONE, TWO, AND THREE-BYTE OPCODE MAPS

2.前缀指令 (分四组,最多四个,每组最多一个)

LOCK 和 REPEAT前缀指令:

LOCK F0 锁地址总线,多核同步使用
REPNE/REPNZ F2 EFLAGS ZF为0 重复执行
REP/REPZ F3 EFLAGS ZF为1 重复执行

段前缀指令:

CS(2E)、SS(36)、DS(3E)、ES(26)、FS(64)、GS(65) 改变默认访问内存地址时使用的段寄存器

操作数宽度前缀指令:

66 改变操作数宽度 (32位←→16位 双向改变)

地址宽度前缀指令:

67 改变CPU寻址方式 (32位←→16位 双向改变)

3.经典定长指令-修改寄存器ERX (ERX代表8个通用寄存器)

PUSH/POP (指令大小1byte)

0x50 PUSH EAX
0x51 PUSH ECX
0x52 PUSH EDX
0x53 PUSH EBX
0x54 PUSH ESP
0x55 PUSH EBP
0x56 PUSH ESI
0x57 PUSH EDI
0x58 POP EAX
0x59 POP ECX
0x5A POP EDX
0x5B POP EBX
0x5C POP ESP
0x5D POP EBP
0x5E POP ESI
0x5F POP EDI

INC/DEC (指令大小1byte)

0x40 - 0x47 INC ERX
0x48 - 0x4F DEC ERX

MOV Rb, Ib ( 指令大小2byte . Rb为16位通用寄存器,Ib为 Immediate Byte 一字节立即数)

0xb0 - 0xb7 MOV Rb, Ib

MOV ERX, Id ( 指令大小5byte . Id为 Immediate Dword 四字节立即数)

0xb8 - 0xbF MOV ERX, Id

XCHG EAX, ERX (指令大小1byte)

0x90 - 0x97 XCHG EAX, ERX

4.经典定长指令-修改EIP

1、0x70 - 0x7F

条件跳转,后跟一个字节立即数的偏移(有符号),共两个字节。

如果条件成立,跳转到 当前指令地址 + 当前指令长度 + Ib

最大值:向前跳7f,向后跳80

0x70 JO
0x71 JNO
0x72 JB/JNAE/JC
0x73 JNB/JAE/JNC
0x74 JZ/JE
0x75 JNZ/JNE
0x76 JBE/JNA
0x77 JNBE/JA
0x78 JS
0x79 JNS
0x7A JP/JPE
0x7B JNP/JPO
0x7C JL/JNGE
0x7D JNL/JGE
0x7E JLE/JNG
0x7F JNLE/JG

2、0x0F 0x80 - 0x0F 0x8F

条件跳转,后跟四个字节立即数的偏移(有符号),共五个字节。

如果条件成立,跳转到 当前指令地址 + 当前指令长度 + Id

最大值:向前跳7FFFFFFFF,向后跳80000000

0x0F 0x80 JO
0x0F 0x81 JNO
0x0F 0x82 JB/JNAE/JC
0x0F 0x83 JNB/JAE/JNC
0x0F 0x84 JZ/JE
0x0F 0x85 JNZ/JNE
0x0F 0x86 JBE/JNA
0x0F 0x87 JNBE/JA
0x0F 0x88 JS
0x0F 0x89 JNS
0x0F 0x8A JP/JPE
0x0F 0x8B JNP/JPO
0x0F 0x8C JL/JNGE
0x0F 0x8D JNL/JGE
0x0F 0x8E JLE/JNG
0x0F 0x8F JNLE/JG

3、其他指令

0xE0 LOOPNE/LOOPNZ Ib (Jb) 共2字节

ECX = ECX - 1 当ZF = 0 && ECX!=0 时跳转到 当前指令地址 + 当前指令长度 + Ib

0XE1 LOOPE/LOOPZ Ib (Jb) 共2字节

ECX = ECX - 1 当ZF = 1 && ECX != 0 时跳转到 当前指令地址 + 当前指令长度 + Ib

0XE2 LOOP Ib (Jb) 共2字节

ECX = ECX - 1 当 ECX!=0 时跳转到 当前指令地址 + 当前指令长度 + Ib

0XE3 JrCXZ Ib (Jb) (在32位模式中,rCX为ECX) 共2字节

当 ECX = 0 时跳转到 当前指令地址 + 当前指令长度 + Ib
(自己控制步长)

0xE8 CALL Id (Jd) 共5字节

CALL指令的下一条指令地址入栈后,跳转到 当前指令地址 + 当前指令长度 + Id

0xE9 JMP Id (Jd) 共5字节

跳转到 当前指令地址 + 当前指令长度 + Id

4、其他指令

8个段寄存器: ES CS SS DS FS GS LDTR TR (顺序固定)

0xEA JMP Ap (Ap:六字节长度的直接地址) 共7字节

JMP CS:Id 将Ap中的高2位赋值给CS,低4位直接赋值给EIP, 即跳转

0xEB JMP Ib (Jb)

跳转到 当前指令地址 + 当前指令长度 + Ib

0xC3 RET 共1字节

EIP出栈

0xC2 RET Iw 共3字节

EIP出栈后,ESP = ESP + Iw

0XCB RETF (return far) 共1字节

出栈8个字节,低4个字节赋值给EIP,高4个字节中低2位赋值给CS

0xCA RETF Iw 共3字节

出栈8个字节,低4个字节赋值给EIP,高4个字节中低2位赋值给CS后,ESP = ESP + Iw

5.经典变长指令-ModR/M字段

下面以MOV的变长指令举例

0x88 MOV Eb, Gb G:通用寄存器
0x89 MOV Ev, Gv E:寄存器/内存
0x8A MOV Gb, Eb b:字节
0x8B MOV Gv, Ev v:Word, doubleword or quadword , depending on operand-size attribute

当指令中出现内存操作对象的时候,就需要在操作码后面附加一个字节来进行补充说明,这个字节被称为ModR/M. 该字节的8个位被分成了三部分:

其中,Reg/Opcpde (第3,4,5位,共3个字节) 描述指令中的G部分,即寄存器(Reg/Opcpde 也有可能用来描述操作码,看第一个opcode来具体分析)

Mod(第6,7位)和R/M(第0,1,2位)共同描述指令中的E部分,即寄存器/内存

那么,这8个位具体是如何工作的呢,Intel操作手册给出了一张表

前面我们已经讨论了Reg/Opcode部分(即3,4,5位)是怎样描述寄存器的,接下来我们重点说一下Mod和R/M是怎样合作描述寄存器/内存的

当 Mod == 0 时,ModR/M字节通过寄存器直接进行内存寻址

例如当操作指令为 88 01 时,其对应的汇编为MOV BYTE PTR DS:[ECX],AL

88 是操作指令,其后跟随的01为ModR/M

0x88描述了操作数宽度为b(byte),且操作数的顺序为Eb Gb,所以这条汇编指令为MOV BYTE PTR DS:[ECX],AL

同理可以分析出如下操作指令对应的汇编指令

1,ESP指向栈顶。是浮动的,不确定的。Intel将这个编码废弃,由另外的格式说明。

2,EBP指向栈底,而[EBP]通常存储上一个EBP,所以[EBP]无数据操作意义,Intel将这个编码废弃,改为立即数寻址

6.经典定长指令-SIB字段

ModR/M字段是用来是用来进行内存寻址的,可当地址形如DS:[EAX+ECX*2+12345678]时,仅仅靠ModR/M字段是描述不出来的。这时就在ModR/M后面增加一个SIB字节,与ModR/M字段共同描述。

下图是之前没有涉及到的三种情况:

这三种情况表示,在ModR/M字节后,还紧跟着一个SIB字节,SIB字节的8位被分成了三部分:

在例子DS:[EAX+ECX*2+12345678] 中,Scale描述 2^1 ,Index 描述ECX,Base描述EAX,而12345678 由ModR/M字段决定。

所以SIB字段描述的方式为:

Base + Index * 2^Scale (Scale描述2^Scale 所以只能为 *1 *2 *4)

下面我们通过分析指令,看看SIB是怎么工作的

MOV BYTE PTR DS:[EAX+ECX*2+12345678] ,AL

操作码

这条指令的原型为MOV Eb Gb ,所以操作码为0x88。且操作数中有Eb,所以必然存在ModR/M字段

ModR/M字节

1.Gb为AL,所以Reg/Opcode部分为000

2.内存地址为 寄存器 + I32 的格式,所以Mod部分为10, 偏移为 78563412

3.内存地址中寄存器部分多个寄存器,即在ModR/M字节后,还跟随着SIB字节,所以R/M部分为100

由此,ModR/M字节为0x84

SIB字节

1.DS:[EAX+ECX*2+12345678]中,Base对应着EAX,所以Base部分为000

2.Index对应着ECX,所以Index部分为001

3.Scale对应着21,所以Scale部分为01

由此,SIB字节为0x48

以上,可总结为如下图

由上,可得出指令编码为 88 84 48 ,再加上偏移12345678,完整的指令编码为88 84 48 12 34 56 78

888448 12345678 | MOV BYTE PTR DS:[EAX+ECX*2+78563412],AL

SIB字段的图表如下

1.当Index =100时,Index被0替代,此时只有Base有效
2.当Base =101时,Base被0替代,此时只用Index有效

由此也可以推出,ModR/M整个字段,最长是6个字节