欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

七 Bit filed(四)

七 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

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功