七 Bit filed(四)
modr/m和sib用于指定操作数.
我们应当记住的第一点是:
可能只有modr/m,也可能既有modr/m又有sib;
不可能只有sib而没有modr/m.
或者你可以这样的认为sib是modr/m的扩展.
我们应当记住的第二件事是:
modr/m和sib都拥有同样的格式2 : 3 : 3.
modr/m的高两位被称为mod,可以有2^2=4种值:
11 - 没有内存操作数,即所有的操作数都是寄存器
10,01,00 - 其中一个操作数是内存操作数,我们等下再讨论详细的情形.
sib的高两位可以有2^2=4种值,对应着被索引寄存器(index register)相应的倍数1,2,4,8.
modr/m和sib剩下的两个3 bit通常被寄存器用于寻址操作数,但我们稍后也会看到一些别的用途.
第三就是:
modr/m和sib能指定什么样的操作数,以及不能指定什么样的操作数.
它们不能指定预定义的操作数.例如在串操作指令中是没有modr/m和sib字节的,以及MUL/DIV等的结果是预先定义的.
它们不能指定立即数,因为在处理器看来并不是立即数而是OpCode的一部分.
它们只能自定非预定义的寄存器操作数,以及带displacement的内存操作数.displacement紧随ModR/M和sib,它的大小是由ModR/M域指定的:
00 - 无 displacement
01 - 8 位的 displacement
10 - 32 位的 displacement
我们先来解答三个简单的问题:
处理器是如何知道:
1. OpCode有1个还是2个操作数?
2. 什么样的寄存器集被ModR/M和sib使用,全寄存器还是局部寄存器?
3. 若有两个操作数,那么哪一个是源,哪一个是目的?
答案是:
处理器是根据code域而不是ModR/M和sib域得知这些信息的.
若指令只使用了寄存器操作数,即不使用内存操作数,modr/m的高两位是11,也就是这样:
[8位code域]:11 *** ***
例如 mov reg,reg:
100010dw:11 reg reg
所以第一个问题的答案是:
处理器通过modr/m的高两位得知是1个还是2个操作数,若只有一个操作数,那么code/reg的低三位即寄存器操作数,中间的三位则是code域的扩展. 例:
MUL EBX
1111 011 1: 11 100 011
看着第二个字节
11 100 011
11 - mod 域,只包含寄存器操作数
100 - code/reg field,在这种情况下意味着只有一个操作数,即该域是code的扩展
011 - EBX寄存器.在只有一个操作数的情况下,操作数一般放在modr/m的低三位.
练习:
在OD中输入一些OpCode,仅改变:
1. code/reg
2. mem/reg
一般的,如果用到两个操作数,那么code/reg和mem/reg都被用来放置操作数.如:
MOV EAX,EBX
1000 1011: 11 000 011
第二个字节(modr/m)
11 - 仅使用寄存器
000 - EAX
011 - EBX
第二个问题:这是由code域的w位决定的.而且我们应该记住的是局部寄存器不能作为指针使用而只能作为寄存器.
if w = 1
全寄存器
else if w = 0
局部寄存器
例:
Mul reg
1111 011w:11 100 reg
w = 1
1111 0111:11 100 001 = MUL ECX
w = 0
1111 0110:11 100 001 = MUL CL
你也可以试试div,idiv,mul,imul,改变它们code域的最低位,看看使用的是什么寄存器集.
又如:
MOV reg,reg
1000 101w: 11 reg reg
w=1
1000 1011: 11 000 001 MOV EAX,ECX
w=0
1000 1010: 11 000 001 MOV AL,CL
它们的modr/m字节也是一样的,不同的仅仅是code域的w位.
试试别的使用寄存器操作数的指令,如
sub reg,reg
add reg,reg
改变它们的w位,并观察寄存器集合的变化.
第三个问题:这是由code域的d 位(code字节的第1位,设从第0位开始计数)决定的.如:
mov reg,reg
1000 10dw:11 reg reg
d = 1
1000 1011:11 000 001 = MOV EAX,ECX
d = 0
1000 1001:11 000 001 = MOV ECX,EAX
可以看到它们的modr/m字节也是一致的.正是由于d 位,导致MOV EAX,ECX有两种编码方式:
1000 1011:11 000 001
1000 1001:11 001 000
这两个OpCode的功能是一致的.
现在试着在OD中输入
instr reg,reg
然后更换编码的方式,例如
add eax,ecx
OD生成的OpCode是 03 C1,即
0000 0011:11 000 001
可以改成如下的编码方式:
1. 将第一个字节的第2位(code域的d 位)置0
0000 0001
2. 然后交换第二个字节的bit 0-2和bit 3-5(modr/m的code/reg和mem/reg)
11 001 000
完整的OpCode即
0000 0001:11 001 000
十六进制为 01C8
现在按Ctrl+e,然后在对话框中输入03 C1
OD显示的助记符也是add eax,ecx,即03C1和01C8对应同样的指令.
再看看他们的二进制格式:
ADD EAX,ECX
0000 0011:11 000 001 ;EAX=000 ECX=001
0000 0001:11 001 000
