自己的反汇编引擎--Intel指令编码(2)

第三部分:指令前缀(Prefixes)    

指令前缀是解析一条指令之前所要做的第一项工作。从某种意义上来说,指令前缀是指令操作的辅助说明信息。

什么是指令前缀

    指令前缀是作为指令的辅助说明信息的,指令前缀是独立于指令之外的,最熟悉的一个恐怕就是重复指令前缀了repz, repnz...。这些前缀作用于链式指令之前,可以在一定条件下重复这些操作,看看一个实际的例子,首先打开ollydbg,载入作为“空白纸”的blank.exe,在汇编窗口Ctrl + E输入A4这时你可以看到:
    00401000 >    A4            movs    byte ptr es:[edi], byte ptr [esi]
    
这是链式指令之中的一条movsb指令,现在再Ctrl + E输入:F3 A4看到了吧:
    00401001      F3:A4         rep     movs byte ptr es:[edi], byte ptr [esi]
    
这里F3就是指令前缀(rep)。链式指令前缀的位置和作用是很明显的,你也可以使用不同的指令(movsd, stosb等指令和repnz(F2)做一下测试。如果不知道指令的编码,可以在汇编窗口的汇编栏,双击鼠标,输入对应的汇编指令,就可以在右边的窗口中看到对应的指令编码了)。
    早期的intel处理器对内存的管理是采用分段式的,不同的分段由段寄存器来索引,所有的寻址方式跟这种管理策略密切相关。但是如果把段寄存器信息也编入指令之中,将使寻址编码方式太过复杂,这里intel采用了一种比较简单的方法,那就是对每一种寻址方式都假设一个默认的段,这就是我们学习汇编语言初期所学到的:bx, si, di等寄存器寻址默认的寄存器为dsbp,sp等默认的寄存器为ssip默认的寄存器为cs...等等(其实默认寄存器也用到了指令编码之中,比如muldiv等指令都默认结果或被除数等为算术寄存器,这样的好处就是,可以大大减少指令大小,然而也同样破坏了指令的一致性)。这里就存在一个问题,如果我想访问特定的段怎么办?比如我想用bx, si, di等索引es, fs, gs等怎么办(保护模式之下,段寄存器的内容为段选择子,一般其内容都是由操作系统维护,我们现在用到的机会大大减少)?intel使用指令前缀来解决这个问题,输入以下指令(ctrl+e输入编码,或双击输入汇编代码):
    00401004      8B07          mov     eax, dword ptr [edi]
    [edi]
默认的寻址寄存器为ds,再输入:
    00401006      2E:8B07       mov     eax, dword ptr cs:[edi]
    2E
在这里就是段改写指令前缀(cs),还有其他的5个段寄存器的改写指令前缀(ss-36H, ds-3EH, es-26H, fs-64H, gs-65H),你可以用这些前缀再任何有内存寻址操作的指令之前,看看效果。
    
    
当然还有其他一些指令前缀,后面将一一介绍,这里需要说明一下的是,指令前缀有一个特点,那就是它只针对特定的指令才有效。例如前面的重复指令,只有当它们用在链式指令之前的时候才有效,你也可以强行把它用在其他指令之前,做以下实验:
    00401000 >    40            inc     eax
    
强行输入(Ctrl + E):
    00401001      F3:           prefix rep:                                      
    00401002      40            inc     eax
    
这里虽然ollydbg识别了这种前缀指令,但是其实cpu还是把它当作同一条指令来执行的,把ollydbg调试选项-〉异常-〉无效或特权指令关掉,运行这条指令。你会看到并没有重复事件发生。
    再来看看段前缀指令,在没有内存寻址的指令中试验一下:
    00401003      40            inc     eax
    00401004      26:40         inc     eax
    
前缀指令被完全忽略。
    intel在官方文档有中这样描述:such use may cause unpredictable behavior。所以在解析段前缀指令的时候,一定要主要该段前缀指令使用在何种指令之前。

指令前缀的分类:

intel指令格式图中对指令前缀的字节数是这样说明的:Up to four prefixes of 1 byte each (optional)。很显然reprepnz是不能同时使用的,intel把这些分为了一组,同样上面介绍的段改写指令也分成了一组。一条指令之前可以同时包含多个不同的组,不同的组只能使用改组其中一条前缀。
    intel指令前缀目前总共有四类,除了前面介绍的两类,还有两类关于操作数大小的指令:操作数数大小改写指令和地址大小改写指令。如图:
 
    

    

2.1 重复指令前缀(lock repz repnz
     已经介绍了repz, repnz,还有一条lock前缀,这个前缀是用在多处理器之中的,具体的用途大家可以可以查看intel官方文档的相关的说明。

2.2 段改写指令前缀(cs ds ss es fs gs) 

    除了上面的说明,还有一点,当指令中有两个默认段(一般会出现在指令中有两个操作对象都为内存的时候)的时候,段改写指令前缀改写哪一个?例如:
    00401000 >    A5            movs    dword ptr es:[edi], dword ptr [esi]
    
加上段改写指令,到底哪个会变?
    00401001      26:A5         movs    dword ptr es:[edi], dword ptr es:[esi]
    
结果很明显。大家可以用其他链式指令做做实验,然后详细读一下svin关于这部分的讲述(opcode6#)。

    Svin
还有关于win32下段值的改变对寻址结果影响的实验(Opcode6#)。由于这跟指令编码没有什么关系,这里只是提一下实验结果:
    1)在win32下,所有的cs, ds, ss使用的是同一个端选择子(尽管值不一样?高手解释一下)。
    2es的值对一些默认使用该段的指令是有影响的。比如movs默认的源为:ds:[esi]目的为:es:[edi],在执行这些指令之前,改变es的值,将会导致不正确的结果。
    3)用户模式下,唯一用得到的段寄存器就是

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值