ModR/M]字节之Reg/Opcode字段(操作码扩展码)


 在 x86/x64 指令集中,ModR/M 字节紧跟在操作码(Opcode)之后,用于定义指令的操作数。其核心组成部分之一是 Reg/Opcode 字段(第 3、4、5 位),它具有“双重身份”:既可以  表示寄存器,也可以作为操作码扩展(Opcode Extension)。 

 

image

                                            官方文档截图: Intel(R) 64 and IA-32 Architectures Software Developer's Manual Volume 2A



一. ModR/M 字节的位结构
       ModR/M 字节共 8 位,分为三个字段:
  • Mod (7-6位):决定寻址模式(寄存器或内存)。
  • Reg/Opcode (5-3位):指定寄存器编号,或作为主操作码的扩展。
  • R/M (2-0位):指定寄存器或内存寻址的组合。 
 
二. 什么是“操作码扩展码”?
   当一个主操作码(Primary Opcode)无法唯一确定一条指令时,CPU 会借用随后的 ModR/M 字节 中的 Reg 字段(第 3、4、5 位)来作为操作码的补充。 x86 架构将这 3 位用作主操作码的补充,从而在同一个主操作码下区分不同的指令。  在intel 技术手册中,这种扩展通常用 /数字(如 /0 到 /7)表示 ,例如 80 /1 ib 形式。 
  ModR/M 字节的中间 3 位,要么代表一个寄存器(/r),要么代表一个操作码扩展码(/0-7),这取决于主操作码的定义。
 
三. 实例:同一个操作码下的不同指令
       以主操作码 0x80 为例(处理 8 位立即数的算术运算指令簇),CPU 通过 ModR/M 字节中的 Reg/Opcode 扩展位来区分具体的运算逻辑: 
 
扩展码 汇编指令 (以 0x80 为例)运算类型
/0 ADD r/m8, imm8 加法
/1 OR r/m8, imm8 逻辑或
/2 ADC r/m8, imm8 带进位加法
/3 SBB r/m8, imm8 带借位减法
/4 AND r/m8, imm8 逻辑与
/5 SUB r/m8, imm8 减法
/6 XOR r/m8, imm8 逻辑异或
/7 CMP r/m8, imm8 比较
 
 
四. 80 组指令(Group Opcode)详细分析
          操作码 80 后面ModR/M 字节的中间 3 位,可以代表不同的算术/逻辑运算 ,
          80组操作码由几个特点: 1. 寄存器都是8位,2.内存地址为8位  3.立即数也是8位
           

组操作码

扩展码(ModR/M中的Reg字段)       

助记符

被操作数

示例 (Intel 语法)/含义  标志位的影响 指令大小       

80 /2 ib    这种表示法常见于 Intel/AMD 官方开发手册
在 x86 指令集参考手册中,80 /2 ib 代表指令 ADC r/m8, imm8。 ADC r/m8, imm8 是一种综合表述方法,代表 adc reg8,imm8 和adc mem8,imm8 

这意味着将一个 8 位立即数(Immediate byte) 和 进位标志位(CF) 的值,加到一个 8 位寄存器或内存操作数 中。
ADC r/m8, imm8指令区分目标操作数是 寄存器 (r8) 还是 内存 (m8),取决于 ModR/M 字节 的最高两位,即 Mod 字段(第 7、6 位)。

80

 /2

adc

reg8,imm8

 adc byte [reg], imm8 带进位加法

SF,ZF,OF,CF,PF,AF

3

2

1

1

80

/2 

adc

mem8,imm8

 

SF,ZF,OF,CF,PF,AF

3+

7

3

3

  80 /0 ib 这种表示法常见于 Intel/AMD 官方开发手册
(通常对应汇编格式 ADD r/m8, imm8)代表将一个 8 位立即数加到一个 8 位 寄存器或内存地址 指向的内容中。

 注意: ADD r/m8, imm8 指令区分目标操作数是 寄存器 (r8) 还是 内存 (m8),取决于 ModR/M 字节 的最高两位,即 Mod 字段(第 7、6 位)。

 和04 ib的区别,请查看  04 ib 代表什么(硬编码) - jinzi - 博客园

80

 /0

add

reg8,imm8

 add byte [reg], imm8 加法

SF,ZF,OF,CF,PF,AF

3

2

1

1

80

 /0

add

mem8,imm8

 

SF,ZF,OF,CF,PF,AF

3+

7

3

3

80 /4 ib    这种表示法常见于 Intel/AMD 官方开发手册
80 /4 ib 代表指令 AND r/m8, imm8,这意味着将一个 8位立即数(Immediate byte) 与一个 8位寄存器或内存操作数 进行 逻辑与(AND) 运算,结果存回该寄存器或内存地址。

80

 /4

and

reg8,imm8

 and byte [reg], imm8  逻辑与
举例:
AND byte ptr [ebx], 0x0F  ; 将 ebx 指向的字节与 0x0F 做与运算
AND al, 0x12              ; 将寄存器 al 的值与 0x12 做与运算

标志位影响:
CF (进位) 和 OF (溢出) 会被强制清零(0)。
ZF (零标志) 会根据结果是否为 0 进行设置(如果结果全为 0,则 ZF = 1)。
SF (符号标志) 会反映结果的最高位。

SF,ZF,OF,CF,PF,AF

3

2

1

1

80

/4 

and

mem8,imm8

 

SF,ZF,OF,CF,PF,AF

3+

7

3

3

80 /7 ib 代表指令 CMP r/m8, imm8。 这意味着将一个 8位立即数(Immediate byte) 与一个 8位寄存器或内存操作数 进行 比较(Compare) 运算
由于 0x80 对应多个指令,CPU 使用后续 ModR/M 字节 中的 reg/opcode 字段(第 3、4、5 位)来区分具体指令,
当Reg/Opcode字段为 7(二进制 111)时,代表 CMP(比较) 指令。
CMP 指令的实质是执行 减法运算(目标操作数 - 立即数)

80

 /7

cmp

reg8,imm8

 cmp byte [reg], imm8 比较 (不存储结果的减法)

例如:

CMP byte ptr [ebx], 0x05 ; 将内存地址 [ebx] 处的字节与 0x05 比较
CMP al, 0x10 ; 将寄存器 al 的值与 0x10 比较
仅更新标志位:根据减法结果设置 EFLAGS 寄存器(如 ZF、CF、SF、OF 等)。这些标志位随后通常被条件跳转指令(如 JE, JB, JG)使用。

SF,ZF,OF,CF,PF,AF

3

2

1

1

80

 /7

cmp

mem8,imm8

 

SF,ZF,OF,CF,PF,AF

3+

5

2

2

在 x86 指令集参考手册中,80 /1 ib 代表指令 OR r/m8, imm8。这意味着将一个 8 位立即数(Immediate byte) 与一个 8 位寄存器或内存操作数 进行 逻辑或(OR) 运算,结果存回目标操作数中。0x80 是一个通用的入口操作码,专门用于处理 8 位操作数 的算术逻辑运算指令集(如 ADD, OR, ADC, SBB, AND, SUB, XOR, CMP)。
由于 0x80 后面可以接多种不同的运算,CPU 通过 ModR/M 字节 中的 reg/opcode 字段(第 3、4、5 位)来区分具体的指令。
当Reg字段的值为 1(二进制 001)时,该指令被识别为 OR(按位或)

80

 /1

or

reg8,imm8

 or byte [reg], imm8  逻辑或
例如:

OR byte ptr [ebx], 0x0F ; 将内存 [ebx] 指向的字节与 0x0F 做“或”运算
OR al, 0x80 ; 将寄存器 al 的值与 0x80 做“或”运算

标志位影响:
CF(进位)和 OF(溢出)会被强制清零 (0)。
ZF(零标志)和 SF(符号标志)会根据运算结果自动设置。

SF,ZF,OF,CF,PF,AF

3

2

1

1

80

/1 

or

mem8,imm8

 

SF,ZF,OF,CF,PF,AF

3+

7

3

3

在 x86 指令集参考手册中,80 /3 ib 代表指令 SBB r/m8, imm8
这意味着将一个 8位立即数(Immediate byte)和 进位标志位(CF) 的值,从一个 8位寄存器或内存操作数中减去,结果存回该寄存器或内存地址。
该指令执行的实际计算为:
目标 = 目标 - (立即数 + CF)
(注:这里的 CF 代表借位标志,如果之前的减法运算产生了借位,CF 为 1,否则为 0)。

 SBB 主要用于实现多字节(高精度)减法。在处理完低位字节的减法后,使用 SBB 处理高位字节,可以自动把低位产生的“借位”考虑进去。

80

 /3

sbb

reg8,imm8

 sbb byte [reg], imm8 带借位减法
例如:

SBB byte ptr [ebx], 0x05 ; [ebx] = [ebx] - 0x05 - CF
SBB al, 0x12 ; al = al - 0x12 - CF

SF,ZF,OF,CF,PF,AF

3

2

1

1

80

/3 

sbb

mem8,imm8

 sub byte [reg], imm8 减法

SF,ZF,OF,CF,PF,AF

3+

7

3

3

在 x86 指令集参考手册中,80 /5 ib 代表指令 SUB r/m8, imm8

这意味着将一个 8 位立即数(Immediate byte) 从一个 8 位寄存器或内存操作数 中减去,并将结果存回该目标操作数
计算:目标操作数 = 目标操作数 - 立即数。

80

 /5

sub

reg8,imm8

举例:

SUB byte ptr [ebx], 0x10 ; 将内存地址 [ebx] 处的 8 位数据减去 0x10
SUB al, 0x05 ; 将 al 寄存器中的值减去 0x05

标志位:会更新 EFLAGS 寄存器,主要包括:
ZF (零标志):如果结果为 0,则置 1。
CF (进位标志):如果发生借位,则置 1(无符号数溢出)。
SF (符号标志):反映结果的最高位(正负性)。
OF (溢出标志):如果发生有符号数溢出,则置 1。

SF,ZF,OF,CF,PF,AF

3

2

1

1

80

 /5

sub

mem8,imm8

 

SF,ZF,OF,CF,PF,AF

3+

7

3

3

 

80

/6

xor

reg8,imm8

 80 /6 ib 组合代表 XOR r/m8, imm8 
xor byte [reg], imm8 逻辑异或

SF,ZF,OF,CF,PF,AF

3

2

1

1

80

 /6

xor

mem8,imm8

 

SF,ZF,OF,CF,PF,AF

3+


五. ADD r/m8, imm8 怎么区分是 r还是m8
    在解析指令 ADD r/m8, imm8(对应操作码 80 /0 ib)时,区分目标操作数是 寄存器 (r8) 还是 内存 (m8),完全取决于 ModR/M 字节 的最高两位,即 Mod 字段(第 7、6 位)。
 
1. 核心判定规则
   在 ModR/M 字节中:
  • 如果 Mod = 11:  目标是一个 寄存器。
  • 如果 Mod = 0001, 或 10:目标是一个 内存地址。
2. 实例拆解
   假设我们要执行 ADD ..., 0x05,主操作码是 80,扩展码是 /0(即 ModR/M 的中间三位是 000)。
 
情况 A:目标是寄存器(r8)
    指令:ADD AL, 0x05
  • ModR/M 字节解析:
    • Mod = 11 (表示目标是寄存器)
    • Reg/Opcode = 000 (扩展码 /0)
    • R/M = 000 (寄存器编号,000 在 8 位下代表 AL)
  • 最终 ModR/M 字节:11 000 000 = C0
  • 机器码:80 C0 05
 
情况 B:目标是内存地址(m8)
   指令:ADD byte ptr [EBX], 0x05
  • ModR/M 字节解析:
    • Mod = 00 (表示无位移内存寻址即内存间接寻址)
    • Reg/Opcode = 000 (扩展码 /0)
    • R/M = 011 (基址寄存器编号,011 代表 EBX)
  • 最终 ModR/M 字节:00 000 011 = 03
  • 机器码:80 03 05
3. 判定逻辑对照表
 
Mod 位 (Bit 7-6)含义后续寻址说明
11 寄存器直接寻址 R/M 字段(Bit 2-0)直接代表寄存器编号(如 AL, CL, DL)。
00 内存间接寻址 类似于 [EBX]。地址由寄存器指定,无偏移量。
01 内存相对寻址 类似于 [EBX + 0x12]。后面跟 1 字节 偏移量。
10 内存相对寻址 类似于 [EBX + 0x12345678]。后面跟 4 字节 偏移量。
 
总结
         CPU 在解码时首先读取 80,知道这是一个 8 位算术指令。接着读取第二个字节(ModR/M),立刻观察其最高两位:
  1. 若是 11,则查寄存器表找到对应的 8 位寄存器。
  2. 若不是 11,则启动内存寻址逻辑(根据 R/M 位和可能的 SIB 字节计算出内存地址)。
这也是为什么逆向工程或底层调试中,ModR/M 字节 被视为 x86 指令解码最核心的“交通枢纽”。


4. 总结
  • 身份切换:如果手册标注为 /r,则 Reg 字段表示一个寄存器;如果标注为 /0 至 /7,则该字段是操作码扩展。
  • 作用:这种设计使得 x86 能够在一个单字节操作码空间内扩展出 8 倍的指令容量。
  • 现代应用:即使在 2026 年的 x64 架构中,这种编码规则依然是指令集解码的核心基础。 
 
posted @ 2026-01-05 14:41  jinzi  阅读(19)  评论(0)    收藏  举报