关于x64中ModR/M历史符号/0.../7的技术咨询
x64架构中MOV指令编码相关疑问解答
我在学习x64架构时,对Intel手册里的部分符号存在理解困惑,以0xC7对应的MOV指令为例,其指令编码如下:
| opcode | instruction | Op/Enc | 说明 |
|---|---|---|---|
| C7 /0 iw | MOV r/m16, imm16 | MI | 将imm16移动到r/m16。 |
| C7 /0 id | MOV r/m32, imm32 | MI | 将imm32移动到r/m32。 |
| REX.W + C7 /0 id | MOV r/m64, imm32 | MI | 将imm32符号扩展为64位后移动到r/m64。 |
手册中关于/0../7符号的说明:
历史上,本文档仅使用/0.../7符号指定ModR/M.reg字段的限制,并未在编码框中指定ModR/M.mod和ModR/M.r/m字段的限制。
提出的技术问题
/0后的数字是限制码,具体代表何种限制?在解读指令时它的含义是什么?- 当读取如
c7c701020304...这类字节序列时,如何判断应读取imm16(0102)还是imm32(01020304)?经测试此处为imm32,但我不理解原因,推测与rex+modrm相关。 - 换而言之:为何
ndisasm将该字节序列解析为imm32而非imm16?
测试代码及结果:
{ xxd --ps -r | ndisasm -b64 -; } <<<c7c701020304 00000000 C7C701020304 mov edi,0x4030201
问题解答
1. /0的具体含义
/n(n取0-7)是Intel手册用来限定ModR/M字节中reg字段取值的符号。以C7 /0为例,它要求紧跟在0xC7后的ModR/M字节里,3位的reg字段二进制值必须是000(对应十进制0)。
ModR/M字节由mod(2位)、reg(3位)、r/m(3位)三个部分构成,/0只约束reg字段的值,mod和r/m字段可根据实际寻址需求变化——只要reg字段符合要求,这条指令就匹配C7 /0对应的MOV指令变种。
2. 区分imm16与imm32的判断逻辑
判断立即数长度的核心是当前操作数模式和指令对应的操作数宽度:
- 你提供的字节序列是在64位模式下解析的(ndisasm指定了
-b64参数),64位模式下默认操作数宽度为32位(除非有REX.W前缀显式指定64位操作数)。 - 字节序列
c7 c7 01020304中,第一个字节C7无REX前缀,匹配的是C7 /0 id(MOV r/m32, imm32)指令。第二个字节C7是ModR/M字节,它的reg字段为000(满足/0的要求),同时mod字段为11(寄存器寻址)、r/m字段为111,对应32位寄存器EDI(64位模式下无REX.W时,r/m=111指向EDI)。 - 而
MOV r/m16, imm16(C7 /0 iw)仅在16位操作数模式下生效,或需通过0x66前缀强制切换到16位模式才会被匹配,默认64位模式下不会优先选择该变种。
3. ndisasm解析为imm32的原因
ndisasm以64位模式解析时:
- 识别第一个字节
C7,无REX前缀,对应C7 /0 id指令,该指令要求后续跟4字节的imm32。 - 第二个字节
C7作为ModR/M字节,reg字段为000(符合/0的约束),确认匹配该指令变种。 - 因此ndisasm读取后续4字节作为imm32,解析为
mov edi, 0x4030201;而16位变种需要额外的0x66前缀触发,当前字节序列中没有该前缀,所以不会被选中。
内容的提问来源于stack exchange,提问作者ton
相关产品推荐
相关产品推荐

