欢迎来到 嗅灵易学

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

七 Bit filed(一)

七 Bit filed(一)

在我们继续学习OpCode块中的域之前,记住一个非常重要且常被使用的域--"reg field"--是非常必要的.
    reg field在域mod r/m,sib,以及某些单字节OpCode内部被使用.Reg bit field有3位,所以可以包含2^3 = 8个值,对应8个通用寄存器.
    000 = EAX
    001 = ECX
    010 = EDX
    011 = EBX
    100 = ESP
    101 = EBP
    110 = ESI
    111 = EDI
    Reg field也可以对应一种不同集合的寄存器--"局部"寄存器(partial registers),我们将在后面碰到的时候再详细讨论这个。现在我们只需要记住:它的前四个值表示全寄存器(full registers)的低局部寄存器,
    000 = AL
    001 = CL
    010 = DL
    011 = BL
    后四个值则全寄存器的高局部寄存器,
    100 = AH
    101 = CH
    110 = DH
    111 = BH
    我们先看看只有全寄存器被使用的情况(在带reg field的单字节OpCode中).在这种指令中,高5位是CODE bitfield,低3位是REG field.
    我写了一个简单的用来解码带reg field单字节指令的程序,稍后我将给出有关它的详细文档.希望你们可以充分使用它,并能通过简单的浏览这些文档记住这所有的bit fields.
    在之前的教程里,我们用OllyDbg 就足以实践和讨论OpCode.
    现在我们来看关于指令的至关重要的一部分地址.但这个问题关系到bit field.
    两个最重要的域mod r/m,sib都有bit field,你可以用大脑联想一下如何用一个或多个十六进制数字表示bit field.
    bit field的格式是: 2 : 3 : 3.这意味着高2位代表一个东西,低3位代表一个东西,中间的3位代表另一个东西.但我们将它用16进制表示,它又是2个十六进制数.因为每个十六进制位由四位组成,所以对于位格式2 : 3 : 3,高十六进制位由第一个域的两位和第二个域的前两位组成,剩余的位则组成低十六进制位.
    例如,在mod r/m中,如果要表示两个操作数都是寄存器,且寄存器分别是edx和edi,那么mod r/m的值是F9.如
    mov    edi,ecx的OpCode是8B F9,
    sub    edi,ecx是2B F9,
可以看到两个OpCode的modr/m相同都是F9.F9的二进制位格式是11 111 001,所以
    11  -- mod(11表示操作数都是寄存器)
    111 -- reg(111代表edi)
    001 -- 寄存器或者内存数(001代表ecx)
    关于mod r/m的另外一点是(两个操作数都是寄存器,且这寄存器是edx或者edi):也可以这样编码11 001 111,也就是 1100 1111即CF(可以看到只是111 和001的位置互换),希望这没有使你感到困惑.
    如果想通过mod r/m和sib创建任意地址,我们只要知道
    1.reg field 8个可能的值
    2.modr/m    4个可能的值
    3.sib       4个可能的值
    我们还需要一些简单通用的规律,和少数的几个Exeption.
    当我们学完这个,在输入指令的时候你就能够很容易的确定任意OpCode的大小(尺寸).OpCode中的地址部分是非常耗空间的.我们经常使用操作数,这意味着我们总是指定操作数或者说是它们的地址(包括寄存器).而Exeption是操作数预先定义的助记符,例如串操作指令的操作码都非常简短,这是因为它们没有地址部分.
    假如我们不了解OpCode的地址部分的话,我们无法断定指令长短或者尺寸.
    举个例子,下面两条指令实现同样的功能:
    1.  mov        edx,[ecx*4]
    2.  xor        eax,eax
        Mov        edx,[ecx*4][eax]
从表面看似乎第二个比第一个多一行,它的第二条指令甚至要多一个域,还多一些字符等等.好像似乎一定是第二种情况要长一些^_^.
    然而,这只是我们的主观臆断而已!
    第二个版本比第一个要少两个字节:
    8B 14 8D 00 00 00 00    MOV    EDX,[DWORD DS:ECX*4]
    33 C0                   XOR    EAX,EAX
    8B 14 88                MOV    EDX,[DWORD DS:EAX+ECX*4]
通过一些不是很艰苦的工作,我们也可以在很短的时间内学会这个.
    关于bit field的编码:虽然使用十六进制对位格式5 : 3或者2 : 3 : 3进行编码不是那么简单,但是在汇编源码中使用二进制是非常容易的.例如指令"inc reg"的CODE域是01000,剩下的3位是寄存器.因为
    eax = 000,所以
    inc   eax就是
    01000 000即40h.
    这里唯一的问题是,在位之间加入空格是不会被汇编器接受的,所以我们应该写作01000000b.但这种方式导致我们不易区分不同的bit fields.我们可以编写一个简单的宏(macro),用它来帮助我们轻易的分开这些bit fields.我们给这个宏取名bcr(a Byte OpCode with Code and Reg fields)
    Bcr     macro    _code,_reg
        db  _code & _reg & b
    endm
现在我们可以用下面的写法来代替db 01000000b
    Bcr  01000,000    ;inc    eax
    Bcr  01000,001    ;inc    ecx等等
    Bcr  01010,000    ;xchg    eax,eax 或者 nop
    Bcr  01010,001    ;xchg    eax,ecx
    ......
    Bcr  01010,111    ;xchg    eax,edi等等.
    同样的,我们可以编写一个宏帮助我们编码mod r/m以及sib,它们的格式是2 : 3 : 3.
    Modrm    macro    _mod, _regcode, _rm
        Db   _mod & _regcode & _rm & b
    Endm
由于sib有同样的格式所以我们可以定义
    bsib     EQU    modrm.
现在我们可以很容易的看出各个field:
    Modrm    11,000,101
    比如
    mov      reg1,reg2 的CODE是8Bh,跟在它后面的是mod r/m字节,其中mod field是11.
例如:
    Db       8bh
    Modrm    11,000,001        ;mov    eax,ecx(eax=000, ecx=001)
    Db       8bh
    Modrm    11,111,001        ;mov    edi, ecx(edi=111)
    Db       8bh
    Modrm    11,001,111        ;mov    ecx,edi
这里我举的例子只是说明使用二进制编码bit fields是没有问题的.

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

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

0 0 0 举报
复制成功