欢迎来到 嗅灵易学

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

二 OpCode结构

二 OpCode结构

二 OpCode结构
    机器指令回答处理器3个问题:
    1. What to do ?
    2. With what to do ?
    3. How to do ?
    其中第三个问题是可选的.
    要回答这些问题,机器指令可能有多个逻辑块.在列举这些逻辑块之前我们先来做一些练习.
    为了更好的试验,在开始这些练习之前我们先来定制一个特别的程序:
    让程序变得小一点,这样OllyDbg可以更快的加载我们的程序;
    用一个字节的助记符填充它,这样就可以把代码段当作白纸一样使用;
    我们可以利用批处理文件或者设定快捷键等方式,更方便地加载我们的程序.
    下面是这个程序的源代码:
        .code
    start:
        rept 256
        nop
        endm
        call ExitProcess
    end start
    它将在程序中插入256 个 nop 指令.
    既然现在你已经知道在call  ExitProcess 之前有256个 90h ,我们是否可以用16进制来编码?
        .code
    start:
        rept 256
        db   90h
        endm
        call ExitProcess
    end start
    或者我们检验一下看看 
    db   90h
    nop
    xchg eax,eax
    这三个是不是一样的.我们可以这样改写程序:
        .code
    start:
        rept 20
        nop
        endm
        rept 118
        xchg eax,eax
        endm
        rept 118
        db   90h
        endm
        call ExitProcess
    end  start
    当我们用OllyDbg加载编译好的可执行文件后,就可以看到在call  ExitProcess之前全部是90  nop.
    OpCode域简介
    有6个域是OpCode可能会用到的,它们的名字是什么这并不重要,重要的是它们的排列顺序.
    它们是:
    1. Prefixes
    2. Code
    3. byte Mod r/m
    4. Byte SIB
    5. Offset in command
    6. imm. Operand
    并不是这所有的6个域都会被用到,但是有一项却是一定会有的,那就是第2项 Code,有些指令甚至只会用到这一项.
    在试验程序中输入
    C3
    2F
    90
    AD
    所有输入的这些OpCode都只有Code域.
    (提示:它们对应的助记符依次为:retn, das,nop,lod**.)
    现在我们知道lod**的OpCode是ACh.我们来看看是不是可以增加额外的域来扩展它的功能呢.我们输入
    F3  AC
    可以看到
    rep  lod**.
    我们可以确定AC 是Code域,F3 AC 则是[Prefixes] [Code],所以F3是Prifixes域.F3表示的是Rep Prefixes,它也能与mov**, sto**等指令连用.
    现在请试着输入一些可以使用rep prefixes的助记符,并观察对应的OpCode;然后再在Ctrl+e窗口中输入这些以F3开头的OpCode,观察对应的助记符。
    我们首先应该记住:OpCode由6个域组成,它们不必都用上,但是Code是一定会有的.
    我们再来看六个BCD码运算指令,输入
    DAA
    DAS
    AAS
    AAA
    AAM
    AAD
    在Intel的文档中,这些都是只包含1个域的指令,这意味着它们都只有Code域.然而最后两条指令却是2字节的.我们可以通过观察这些指令,然后断定这些指令的格式和附加的域.
    先不要看下面的答案,试着自己想一想AAM和AAD和别的指令有什么不同……

    27              DAA
    2F              DAS
    3F              AAS
    37              AAA
    D4 0A           AAM
    D5 0A           AAD
    很明显我们可以看到:
    1. AAM和AAD都是2字节的,然而其余的4条指令都是1字节的.
    2. AAM和AAD的OpCode的第2 个字节都是0Ah
    不知你是否想起这两条指令的描述:
    AAM: divide  AL  by  10
    商   放在AH里
    余数 放在AL里
    AAD: AL  =  AL * 10 + AL
    两者的操作都与10 有关,而且它们的第二个字节都是 10(0Ah).那么0Ah会不会是偶然的呢?会不会是操作数的一部分呢?那AAM和AAD的指令格式会不会不是
    AAM:  D4  0A
    AAD:  D5  0A
    而是:
    AAM:  D4  imm8
    AAD:  D5  imm8
    以及 imm8 可以是任何别的值呢?
    我们可以验证一下,输入:
    Mov  al,8
    D4   07  ;作用和D40A相同,只是除以7而不是10
    如果我们是正确的,那么按F8单步执行刚输入的指令后 AH(商) = 1, AL(余数) = 1.
    现在,我们又知道了一种新的指令格式:
    [ CODE ] [ imm ] (域2 和 域6)
    同时我们也发现某些 OpCode 没有对应的助记符.在这里我得说一下,Oleh (OllyDbg的作者)允许使用立即数作为AAM和AAD的操作数.
    理解了OpCode 的规则,将有助于底层程序员明白一些鲜为人知的事情--一些未在文档上列出的OpCode,这也是掌握OpCode的另一个好处.
    之后我们将详细学习OpCode的每一个域.
    现在我们来看一些基本的规则:虽然并不是6个域都是必要的,但是,它们的排列顺序绝对不能乱,必须严格按照上面的顺序进行.有些域或许不会出现,但只要出现了,编号小的域就绝对不允许出现在编号大的域的后面,反之亦然.
    例如,[Prefixes] [code] 的顺序绝不允许变为[code] [Prefixes].
    输入
    0110
    和    
    1001
    你将看到它们的不同.
    (提示:0110 --add  dword ptr [eax], edx         1001--adc  byte ptr [ecx], al)
    下次我们将讨论处理器是如何确认OpCode的开始和结束的,我们也会看到 2 + 2是怎样等于3的.
原文第二帖

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

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

0 0 0 举报
复制成功