1. 何为OPCode
在计算机科学领域中,操作码(Operation Code, OPCode)被用于描述机器语言指令中,指定要执行某种操作的那部分机器码,构成OPCode的指令格式和规范由处理器的指令规范指定。除了指令本身以外通常还有指令所需要的操作数,可能有的指令不需要显示的操作数。这些操作数可能是寄存器中的值,堆栈中的值,某块内存的值或者IO端口中的值等等。
OPCode在不同的场合中通常具有不同的含义,例如PHP虚拟机(Zend VM)、java虚拟机(JVM)以及一些软件保护虚拟机中的最小操作单元都可以称之为OPCode。
2. 常用单字节OPCode概览A -- 40~4F
opcode asm using
0x40 inc eax emit(0x40)
0x41 inc ecx emit(0x41)
0x42 inc edx emit(0x42)
0x43 inc ebx emit(0x43)
0x44 inc esp emit(0x44)
0x45 inc ebp emit(0x45)
0x46 inc esi emit(0x46)
0x47 inc edi emit(0x47)
2. 常用单字节OPCode概览B -- 50~5F
2. 常用单字节OPCode概览C -- 70~7F
opcode asm using
0x70 0x12 Jo 0x12 {_emit(0x70)} {_emit(0x12)}
0x71 ... Jno ... ... ...
0x72 ... Jb ... ... ...
0x73 ... Jae ... ... ...
0x74 ... Je ... ... ...
0x75 ... Jne ... ... ...
0x76 ... Jbe ... ... ...
0x77 ... Ja ... ... ...
0x78 ... Js ... ... ...
0x79 ... Jns ... ... ...
0x7a ... Jp ... ... ...
0x7b ... Jnp ... ... ...
0x7c ... Jl ... ... ...
0x7d ... Jge ... ... ...
0x7e ... Jle ... ... ...
0x7f ... Jg ... ... ...
2. 常用单字节OPCode概览D -- 90~9F
Opcode asm Using
3. OPCode与指令的对应关系
4. OPCode详解A -- 主要数据域
以上数据域只有Code域是必须存在的,其他数据域视指令格式而定,或有或无
一个指令的长度在1Byte~16Byte之间
实际正常的最长指令是,13Byte
4. OPCode详解B -- 前缀
前缀(Prefixes)的大小为1Byte,用于描述指令的前缀情况,他们可以被划分为5个集合:
66 -- 切换操作数大小
67 -- 切换地址大小
F2/F3 -- 重复操作前缀
2E/36/3E/26/64/65 -- 修改默认段
F0 -- 锁定前缀
所以指令独此一份,不可能为其他机器码
注意:
a. "切换"的意思是将其在两种状态间来回切换,而并非特指某种状态
b. 将默认值修改为其他段的操作称之为"修改默认段"
c. 一个OpCode可能会有几个Prefixes
d. 如果有多个Prefixes,那么它们的顺序可能打乱
e. 如果Prefixes不能对随它之后的OpCode起作用,那么它就会被忽略
4. OPCode详解C -- 前缀
切换操作数大小
40 INC EAX
66 40 INC AX
切换顺序: 从大到小
无效的前缀应用
8AC1 MOV AL, CL
66 BAC1 MOV AL, CL
重复操作段前缀
F3 66 AD REP LODSW
F2 AC REPNE LODSB
段超越前缀
8B 03 MOV EAX, [DWORD DS:EBX]
658B 03 MOV EAX, [DWORD GS:EBX]
4. OPCode详解D -- ModR/M
OPCode的主要解析逻辑都集中在ModR/M域,我们可以通过对照Intel手册中的表来解析OPCode中的ModR/M域来确定指令的具体格式
例如:
89 D8 mov eax, ebx
D8 = 11011000
Mod Reg R/M
11 011 000
4. OPCode详解E -- Opcode小注
/0~7: 此ModR/M只使用R/M域的信息
/r : 此ModR/M同时使用R/M域与Reg域信息
+rb: 寄存器码, 此码将加载OPCode原有值上
AL = 0, CL = 1, DL = 2, BL = 3, AH = 4, CH = 5, DH = 6, BH = 7
+rw: 寄存器码,此码将加载OPCode原有值上
AX = 0, CX = 1, DX = 2, BX = 3, SP = 4, BP = 5, SI = 6, DI = 7
+rd: 寄存器码,此码将加载OPCode原有值上
EAX = 0, ECX = 1, EDX = 2, EBX = 3, ESP = 4, EBP = 5, ESI = 6, EDI = 7
+i: 仅限于浮点计算时,增加的数可以是0~7, 用于标记当前使用的FPU
堆栈,此码将加载OPCode原有值上
4. OPCode详解F -- 高大上指令赏析
13字节汇编指令赏析
F0:26:C7 8491AA000000 11000000
LOCK MOV DWORD PTR ES: [EDX*4 + ECX + 0AA], 11
Prefix OPCode ModR/M SIB Displacement Immediate
F0:26: C7 84 91 AA000000 01000000
注1 注2 注3 注4 偏移 立即数
注1: 锁定前缀 + ES段超越
注2: MOV r/m32, imm32
注3: R/M = disp32[--][--] Mod != EAX
注4: Index = [EDX*4] r32 = ECX
84 91 AA000000 --> [EDX*4 + ECX +0xAA]
5. 练习
①. 83C4 04
分析:
83 /0 ib: ADD r/m32,imm8 ;/0~7 只使用R/M信息
C4: ESP
结果:
add esp, 04
②. A1 78812801
A1 : MOV EAX,moffs32* ;Move doubleword at (seg:offset) to EAX, moffs32为32位地址
结果:
mov eax, dword ptr ds:[1288178]
③. A3 54812801
A3 MOV moffs32*,EAX ;Move EAX to (seg:offset),moffs32为32位地址
结果:
mov dword ptr ds:[1288154], eax
④. 68 54812801
PUSH imm32
结果:
push 1288154
⑤. 8B0D 74812801
8B /r MOV r32,r/m32 ;/r 同时使用R/M域与Reg域信息
OD 有效地址: disp32 REG: ECX
结果:
mov ecx, dword ptr ds:[1288174]
⑥. 51
50+rdPUSH r32 ;EAX = 0, ECX = 1, EDX = 2, EBX = 3, ESP = 4, EBP = 5, ESI = 6, EDI = 7
结果:
push ecx
⑦. 68 48812801
68 PUSH imm32
结果:
push 1288148
⑧. FF15 A4922801
FF /3CALL m16:32 ;/0~7 只使用R/M信息
15 有效地址: disp32
结果:
call dword ptr cs:[12892A4]
总结:
ModR/M信息与确认是否有SIB字节
当Mod != 11b并且R/M的值为100b的时候,表示指令后续有SIB字节,并且该内存操作对象由SIB编码。
MODR/M里有三种情况会有SIB字节