硬编码学习笔记(二)—— 经典变长指令

前言

本次学习仅基于intel x86模式

指令结构

描述:对于任何一条指令,都由以下几部分组成,但不是每部分都必须存在
长度:最短1个字节,最长15个字节
在这里插入图片描述Instruction Prefixes:指令前缀
Opcode: 主操作码
ModR/M:在内存中引用一个操作数的许多指令都有一个寻址方式的指定符字节(称为ModR/M字节)跟随在主操作码之后
SIB:ModR/M字节的某些编码需要第二个寻址字节(SIB字节)
Displacement:一些寻址方式包含紧跟ModR/M字节(或者SIB字节,如果有的话)的位移
Immediate:立即数

符号说明

寻址符号

符号意义
A直接寻址
C控制寄存器
D调试寄存器
E寄存器/内存
FEFLAGS/RFLAGS寄存器
G通用寄存器
I立即数
J要添加到指令指针寄存器的相对偏移量
MModR/M字节可能仅指向内存。
NMMX 技术寄存器
O该指令没有ModR/M字节。操作数的偏移量在指令中被编码为一个字或双字(取决于地址大小属性)。
PModR/M字节的reg字段选择一个打包的四字MMX技术寄存器
QModR/M字节跟随操作码并指定操作数。该操作数要么是MMX技术寄存器,要么是内存地址。
RModR/M字节的R/M字段可能仅指一个通用寄存器
SModR/M字节的reg字段选择一个段寄存器
UModR/M字节的R/M字段选择一个128位的XMM寄存器。
VModR/M字节的reg字段选择一个128位的XMM寄存器。
WModR/M字节跟随操作码并指定操作数。操作数要么是一个128位的XMM寄存器要么是一个内存地址。
X由DS:rSI寄存器对寻址的内存
Y由ES:rDI寄存器对寻址的内存

操作数符号

符号意义
a内存中的两个单字操作数或内存中的两个双字操作数,具体取决于操作数大小属性(仅由BOUND指令使用)
b字节
c字节/字
d双字
dq四字
p32位/48位/80位指针
pd128位封装的双精度浮点数据
pi双四字,MMX技术寄存器(例如:mm0)
ps128位封装的单精度浮点数据
q四字
s6字节或10字节的伪描述符
ss128位打包的单精度浮点数据的标量元素
si双字整数寄存器(例如:eax)
v字、双字或四字(取决于当前CPU的模式)
w
z16位操作数大小的字或32位或64位操作数大小的双字

上标符号

符号意义
1AModR/M字节的第5、4和3位用作操作码扩展
1B使用0F0B操作码(UD2指令)或0FB9H操作码时,故意尝试生成无效的操作码异常(#UD)
1C在Pentium III处理器中添加的一些指令可以使用相同的双字节操作码。如果指令有变化,或者操作码代表不同的指令,则使用ModR/M字节来区分指令。
i64指令在64位模式下无效
o64指令仅在64位模式下有效
d64当处于64位模式时,指令默认为64位操作数大小,并且不能对32位操作数大小进行编码。
f64在64位模式下,操作数大小被强制为64位操作数大小(在64位模式下,该指令会忽略改变操作数大小的前缀)。

One-Byte Opcode Map

在这里插入图片描述
在这里插入图片描述

变长指令

描述:当操作系统遇到诸如0x88这样的Opcode时,其指令序列为MOV Eb, Gb,目的是将一个8位的寄存器存储到一个8位的寄存器或内存中,当表中出现E或G这样的符号时,即存在ModR/M字段

思考:CPU如何知道要把值放在哪个寄存器中?
答案:使用ModR/M字段

ModR/M

描述:指令在引用一个不确定操作数时使用该字段进行定位

结构
在这里插入图片描述
Reg/Opcode:用来确定寄存器或操作码是什么
Mod+R/M:这两个字段拼在一起确定是哪个内存或寄存器
在这里插入图片描述

例:0x88

指令格式MOV Eb, Gb

88 00

Mod:00
Reg/Opcode:000	//使用0号寄存器,是EAX还是AL由操作码决定
R/M:000			//Mod与R/M拼在一起指向[EAX]

在这里插入图片描述在这里插入图片描述

因此字节码88 00对应的指令为:MOV [EAX], AL

注意

  1. Mod为00,R/M为100时,使用另一种指令格式
  2. Mod为00,R/M为101时,向后再取4字节立即数作为Gb的值
    88 15 00 10 40 00	MOV [0x401000],DL
    

例:0x89

指令格式MOV Ev, Gv

89 49 10

Mod:01
Reg/Opcode:001	//使用1号寄存器
R/M:001			//Mod与R/M拼在一起指向[ECX]+disp8

在这里插入图片描述在这里插入图片描述
因此字节码89 49 10对应的指令为:MOV [ECX+0x10], ECX

例:0x8A

指令格式MOV Gb, Eb

8A 92 78 56 34 12

Mod:10
Reg/Opcode:010	//使用2号寄存器
R/M:010			//Mod与R/M拼在一起指向[EDX]+disp32

在这里插入图片描述在这里插入图片描述
因此字节码8A 92 78 56 34 12对应的指令为:MOV DL, [EDX+0x12345678]

例:0x8B

指令格式MOV Gv, Ev

8B DB

Mod:11
Reg/Opcode:011	//使用3号寄存器
R/M:011			//Mod与R/M拼在一起指向EBX

在这里插入图片描述在这里插入图片描述
因此字节码8B DB对应的指令为:MOV EBX, EBX

SIB

描述

  1. Opcode决定了后边是否存在ModR/M字段,而ModR/M决定了后面是否存在SIB字段
  2. 当ModR/M字段低3位为100时,存在SIB字段

结构
在这里插入图片描述
Scale: 2的几次方
Index:下标
Base:确定是哪个寄存器

例:DS:[EAX+ECX2+0x12345678]
scale表示2¹
Index表示ECX
Base表示EAX
0x12345678由ModR/M字段决定
即:SIB=Base+Index
2的scale次方

在这里插入图片描述

[*]:取决于ModR/M中MOD字段的值
00 [scaled index] + disp32
01 [scaled index] + disp8 + [EBP]
10 [scaled index] + disp32 + [EBP]

例:88 84 48 78 56 34 12

Opcode:88

指令格式:MOV Eb, Gb

ModR/M:84

Mod:10
Reg/Opcode:000	//使用0号寄存器AL
R/M:100			//使用SIB字段

在这里插入图片描述在这里插入图片描述

[--][--]:查询SIB字段

SIB:48

Scale:01
Index:001	//Scale与Index结合指向[ECX*2]
Base:000	//使用0号寄存器EAX

在这里插入图片描述因此,字节码88 84 48对应的指令为MOV BYTE PTR DS:[EAX+ECX*2+0x12345678], AL

例:89 84 84 78 56 34 12

Opcode:89

指令格式:MOV Ev, Gv

ModR/M:84

Mod:10
Reg/Opcode:000	//使用0号寄存器AL
R/M:100			//使用SIB字段

在这里插入图片描述在这里插入图片描述

[--][--]:查询SIB字段

SIB:84

Scale:10
Index:001	//Scale与Index结合指向[ECX*2]
Base:000	//使用0号寄存器EAX

在这里插入图片描述
因此,字节码88 84 48对应的指令为MOV DWORD PTR DS:[ESP+EAX*4+0x12345678], EAX

Opcode Extension Tables

描述:当操作系统遇到诸如0x80这样的Opcode时,其指令序列为Eb, Ib,并无指明Opcode

思考:CPU如何知道Opcode是什么?
答案:查询Opcode Extension Tables
在这里插入图片描述
在这里插入图片描述

例:80 65 08 FF

在这里插入图片描述
ModR/M:65

Mod:01
Reg/Opcode:100	//此时不再指向寄存器,用这个值去查Opcode Extension Tables
R/M:101		//Mod与R/M结合指向[EBP]+disp8

在这里插入图片描述
因此,字节码80 65 08 FF对应的指令为AND BYTE PTR SS:[EBP+0x8], 0xFF

Instruction Prefixs

描述:指令前缀

段前缀

描述
1)在早期8086CPU寻址范围较小,Intel采用段寄存器*16+偏移的方式寻址
2)后来80386CPU扩大了寻址范围,段寄存器便被用作了其它用途,不参与寻址
3)但是类似DS:[]这种格式被保留了下来
4)实际上操作码已经决定了寻址时使用哪个段寄存器作为基址,不需要其他字节描述

注意
1)如果没有特别说明,[]前为DS,即DS:[]
2)若是像PUSH和POP指令,以及其它在[]中使用ESP/EBP的指令,默认前缀为SS
3)在[Base+Index*2Scale+I]中,以Base作为判断条件,没有特别说明,默认前缀为DS
4)如果Base为ESP/EBP,默认前缀为SS
5)EIP取指令时默认前缀为CS
6)如果指令加段寄存器前缀,则该条指令一律用这个段; 如果加多个段寄存器前缀,默认只看Opcode前面那个

默认值

CS:2E
SS:36
DS:3E
ES:26
FS:64
GS:65

操作指令前缀:修改地址默认长度

描述:在无指令前缀的Opcode中,B0表示MOV AL, Ib,B8表示MOV EAX, Id,但却不存在MOV AX, I这样的Opcode,这是因为intel使用指令前缀完成这件事情

:0x66
作用:将操作数改为16位模式

50		PUSH EAX
66:50	PUSH AX

操作指令前缀:修改默认寻址方式

:0x67
作用:将操作数改为16位模式

88 01	MOV BYTE PTR DS:[ECX], AL
67:8801	MOV BYTE PTR DS:[BX+DI], AL
  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值