硬编码学习笔记(一)—— 经典定长指令
前言
本次学习仅基于intel x86模式
指令结构
描述:对于任何一条指令,都由以下几部分组成,但不是每部分都必须存在
长度:最短1个字节,最长15个字节
Instruction Prefixes:指令前缀
Opcode: 主操作码
ModR/M:在内存中引用一个操作数的许多指令都有一个寻址方式的指定符字节(称为ModR/M字节)跟随在主操作码之后
SIB:ModR/M字节的某些编码需要第二个寻址字节(SIB字节)
Displacement:一些寻址方式包含紧跟ModR/M字节(或者SIB字节,如果有的话)的位移
Immediate:立即数
符号说明
寻址符号
符号 | 意义 |
---|---|
A | 直接寻址 |
C | 控制寄存器 |
D | 调试寄存器 |
E | 寄存器/内存 |
F | EFLAGS/RFLAGS寄存器 |
G | 通用寄存器 |
I | 立即数 |
J | 要添加到指令指针寄存器的相对偏移量 |
M | ModR/M字节可能仅指向内存。 |
N | MMX 技术寄存器 |
O | 该指令没有ModR/M字节。操作数的偏移量在指令中被编码为一个字或双字(取决于地址大小属性)。 |
P | ModR/M字节的reg字段选择一个打包的四字MMX技术寄存器 |
Q | ModR/M字节跟随操作码并指定操作数。该操作数要么是MMX技术寄存器,要么是内存地址。 |
R | ModR/M字节的R/M字段可能仅指一个通用寄存器 |
S | ModR/M字节的reg字段选择一个段寄存器 |
U | ModR/M字节的R/M字段选择一个128位的XMM寄存器。 |
V | ModR/M字节的reg字段选择一个128位的XMM寄存器。 |
W | ModR/M字节跟随操作码并指定操作数。操作数要么是一个128位的XMM寄存器要么是一个内存地址。 |
X | 由DS:rSI寄存器对寻址的内存 |
Y | 由ES:rDI寄存器对寻址的内存 |
操作数符号
符号 | 意义 |
---|---|
a | 内存中的两个单字操作数或内存中的两个双字操作数,具体取决于操作数大小属性(仅由BOUND指令使用) |
b | 字节 |
c | 字节/字 |
d | 双字 |
dq | 四字 |
p | 32位/48位/80位指针 |
pd | 128位封装的双精度浮点数据 |
pi | 双四字,MMX技术寄存器(例如:mm0) |
ps | 128位封装的单精度浮点数据 |
q | 四字 |
s | 6字节或10字节的伪描述符 |
ss | 128位打包的单精度浮点数据的标量元素 |
si | 双字整数寄存器(例如:eax) |
v | 字、双字或四字(取决于当前CPU的模式) |
w | 字 |
z | 16位操作数大小的字或32位或64位操作数大小的双字 |
上标符号
符号 | 意义 |
---|---|
1A | ModR/M字节的第5、4和3位用作操作码扩展 |
1B | 使用0F0B操作码(UD2指令)或0FB9H操作码时,故意尝试生成无效的操作码异常(#UD) |
1C | 在Pentium III处理器中添加的一些指令可以使用相同的双字节操作码。如果指令有变化,或者操作码代表不同的指令,则使用ModR/M字节来区分指令。 |
i64 | 该指令无效或在64位模式下不可编码。40到4F(单字节INC和DEC)是64位模式下的REX前缀组合(对INC和DEC使用FE/FF Grp 4和5)。 |
o64 | 指令仅在64位模式下可用。 |
d64 | 当处于64位模式时,指令默认为64位操作数大小,并且不能对32位操作数大小进行编码。 |
f64 | 在64位模式下,操作数大小被强制为64位操作数大小(在64位模式下,该指令会忽略改变操作数大小的前缀)。 |
One-Byte Opcode Map
经典定长指令:修改寄存器
0x40~0x47
指令长度:共1个字节
40 INC EAX
41 INC ECX
42 INC EDX
43 INC EBX
44 INC ESP
45 INC EBP
46 INC ESI
47 INC EDI
0x48~0x4F
指令长度:共1个字节
48 DEC EAX
49 DEC ECX
4A DEC EDX
4B DEC EBX
4C DEC ESP
4D DEC EBP
4E DEC ESI
4F DEC EDI
0x50~0x57
指令长度:共1个字节
50 PUSH EAX
51 PUSH ECX
52 PUSH EDX
53 PUSH EBX
54 PUSH ESP
55 PUSH EBP
56 PUSH ESI
57 PUSH EDI
0x58~0x5F
指令长度:共1个字节
58 POP EAX
59 POP ECX
5A POP EDX
5B POP EBX
5C POP ESP
5D POP EBP
5E POP ESI
5F POP EDI
0x90~0x97
指令长度:共1个字节
90 NOP //XCHG EAX, EAX
91 XCHG EAX,ECX
92 XCHG EAX,EDX
93 XCHG EAX,EBX
94 XCHG EAX,ESP
95 XCHG EAX,EBP
96 XCHG EAX,ESI
97 XCHG EAX,EDI
0xB0~0xB7
指令长度:共2个字节
B0 00 MOV AL,0x0
B1 01 MOV CL,0x1
B2 02 MOV DL,0x2
B3 03 MOV BL,0x3
B4 04 MOV AH,0x4
B5 05 MOV CH,0x5
B6 06 MOV DH,0x6
B7 07 MOV BH,0x7
0xB8~0xBF
指令长度:共5个字节
B8 00 00 00 10 MOV EAX,0x10000000
B9 01 00 00 10 MOV ECX,0x10000001
BA 02 00 00 10 MOV EDX,0x10000002
BB 03 00 00 10 MOV EBX,0x10000003
BC 04 00 00 10 MOV ESP,0x10000004
BD 05 00 00 10 MOV EBP,0x10000005
BE 06 00 00 10 MOV ESI,0x10000006
BF 07 00 00 10 MOV EDI,0x10000007
经典定长指令:修改EIP
0x70~0x7F
指令长度:共2个字节
偏移量:1个字节
当偏移量<0x80
时:EIP = 当前指令地址+当前指令长度+偏移量
当偏移量>0x80
时:EIP = 当前指令地址+当前指令长度-偏移量
70 JO
71 JNO
72 JB/JNAE/JC
73 JNB/JAE/JNC
74 JZ/JE
75 JNZ/JNE
76 JBE/JNA
77 JNBE/JA
78 JS
79 JNS
7A JP/JPE
7B JNP/JPO
7C JL/JNGE
7D JNL/JGE
7E JLE/JNG
7F JNLE/JG
0x0F80~0x0F8F
指令长度:共6个字节
偏移量:4个字节EIP = 当前指令地址+当前指令长度+偏移量
0F80 JO
0F81 JNO
0F82 JB/JNAE/JC
0F83 JNB/JAE/JNC
0F84 JZ/JE
0F85 JNZ/JNE
0F86 JBE/JNA
0F87 JNBE/JA
0F88 JS
0F89 JNS
0F8A JP/JPE
0F8B JNP/JPO
0F8C JL/JNGE
0F8D JNL/JGE
0F8E JLE/JNG
0F8F JNLE/JG
0xE0
指令长度:共2个字节
LOOPNE/LOOPNZ Ib(Jb)
当ZF==0&&ECX!=0
时:EIP=当前指令地址+当前指令长度+Ib
;ECX=ECX-1
0xE1
指令长度:共2个字节
LOOPE/LOOPZ Ib(Jb)
当ZF==1&&ECX!=0
时:EIP=当前指令地址+当前指令长度+Ib
;ECX=ECX-1
0xE2
指令长度:共2个字节
LOOP Ib(Jb)
当ECX!=0
时:EIP=当前指令地址+当前指令长度+Ib
;ECX=ECX-1
0xE3
指令长度:共2个字节
JrCXZ Ib(Jb) //在32位模式中,rCX为ECX
当ECX==0
时:EIP=当前指令地址+当前指令长度+Ib
0xE8
指令长度:共5个字节
CALL Id(Jd)
下一条指令地址入栈
;EIP=当前指令地址+当前指令长度+Id
0xE9
指令长度:共5个字节
JMP Id(Jd)
EIP=当前指令地址+当前指令长度+Id
0xEA
指令长度:共7个字节
JMP Ap //Ap为六字节长度的直接地址
JMP CS:Id //将Ap中的高2位赋值给CS,低4位赋值给EIP
0xEB
指令长度:共2个字节
JMP Ib(Jb)
EIP=当前指令地址+当前指令长度+Ib
0xC2
指令长度:共3个字节
RET Iw //POP EIP;ESP=ESP+Iw
0xC3
指令长度:共1个字节
RET //POP EIP
0xCA
指令长度:共1个字节
RETF //POP EIP;POP CS
0xCB
指令长度:共3个字节
RETF Iw //POP EIP;POP CS;ESP=ESP+Iw