我们在前两节的硬编码中学习了定长指令,接下来学习变长指令。学习变长指令要求我们学会查表:intel手册卷2A和2B部分
对于定长指令,我们通过opcode便可知该指令的长度,但是对于变长指令却是不可知的。变长指令长度由opcode,ModR/M,SIB共同决定。变长指令通常在需要操作内存的时候使用
经典变长指令
接下来我们观察一些常见的变长指令:
变长指令 | 汇编代码 |
0x88 | MOV Eb, Gb |
0x89 | MOV Ev, Gv |
0x8A | MOV Gb, Eb |
0x8B | MOV Gv, Ev |
G:通用寄存器
E:寄存器/内存
b:字节
v:字、双字或四字,通常由当前CPU模式决定(此处使用x86环境)
ModR/M
当指令中出现内存操作对象的时候,就需要在操作码(opcode)后面附加一个字节来进行补充说明,这个字节被称为ModR/M,其只有一个字节宽度,用于内存寻址
在本节讲解中,默认Reg/Opcode部分为Reg(寄存器),Opcode(指令)以后再说
其中,Reg部分描述指令中的G部分,即源寄存器,如下是该部分对应寄存器的表示:
Mod部分和R/M部分共同描述指令中的E部分,即目标寄存器/内存。
通过Intel操作手册中表Table 2-2便可知道ModR/M是如何使用的
手动解析指令
0x88 0x01
接下来以0x88 0x01为例,讲解如何解析变长指令
如下是0x88 0x01的解析部分:
Opcode | ModR/M |
0x88 - MOV Eb, Gb | 0x01 |
接着我们拆分ModR/M为其内部的三个部分:
Mod | Reg | R/M | |||||
0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
然后我们开始依照Intel操作手册,一步步的分析该指令
由手册可知:
Reg:000对应寄存器AL
Mod:00和R/M:001对应[ECX]
因此我们得出结论:0x88 0x01对应的汇编代码为MOV BYTE PTR DS:[ECX], AL
0x88 0x10
接下来我们简单的解析0x88 0x10
Opcode | ModR/M |
0x88 - MOV Eb, Gb | 0x10 |
接着我们拆分ModR/M为其内部的三个部分:
Mod | Reg | R/M | |||||
0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
通过查表可知:
Reg:010对应DL
Mod:00和R/M:001对应[EAX]
因此我们得出结论:0x88 0x10对应的汇编代码为MOV BYTE PTR DS:[EAX], DL
0x88 0x15
接下来我们简单的解析0x88 0x15
Opcode | ModR/M |
0x88 - MOV Eb, Gb | 0x15 |
接着我们拆分ModR/M为其内部的三个部分:
Mod | Reg | R/M | |||||
0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 |
通过查表可知:
Reg:010对应DL
Mod:00和R/M:101对应dis32(32位立即数)
dis32跟在0x88 0x10后,现假设dis32为0x12345678
因此我们得出结论:0x88 0x10 0x12345678对应的汇编代码为MOV BYTE PTR DS:[78563412], DL
0x88 0x82
接下来我们简单的解析0x88 0x82
Opcode | ModR/M |
0x88 - MOV Eb, Gb | 0x82 |
接着我们拆分ModR/M为其内部的三个部分:
Mod | Reg | R/M | |||||
1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
通过查表可知:
Reg:000对应AL
Mod:10和R/M:010对应[EDX] + dis32
此处假设dis32为0x12345678
因此我们得出结论:0x88 0x10 0x12345678对应的汇编代码为MOV BYTE PTR DS:[EDX + 78563412], AL
0x89 0x01
接下来我们简单的解析0x89 0x01
Opcode | ModR/M |
0x89 - MOV Ev, Gv | 0x01 |
由于我们使用的是CPU的x86环境,因此v表示32位
接着我们拆分ModR/M为其内部的三个部分:
Mod | Reg | R/M | |||||
0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
通过查表可知:
Reg:000对应EAX
Mod:00和R/M:001对应[ECX]
因此我们得出结论:0x88 0x10对应的汇编代码为MOV DWORD PTR DS:[ECX], EAX