首页
社区
课程
招聘
[原创]谈谈IA-32的指令格式
2014-6-6 14:38 19907

[原创]谈谈IA-32的指令格式

2014-6-6 14:38
19907
整理资料,发现一篇老文。如果版主觉得过于基础,可以移到初学者那边。现在不知道基础的东西应该发到哪个版块了。
            谈谈IA-32的指令格式 
    废话不多说,作为CISC的代表,x86指令集的解码工作较为复杂。这种工作主要是为了照顾兼容性,实际上现在把一堆东西挤在几个比特里面完全没有必要,看看MIPS的汇编器,除了伪指令外,其他就是一一对应好嘛。
    这部分的参考资料主要是intel手册2的第2章和第三章的3.1,当然还有手册2的附录。intel手册分为三册,手册1是给汇编程序员用的,主要指导你怎么写汇编程序;手册2是给编译器设计者用的,主要指导你怎么给你的编译器写一个x86后端--这也是我从事工作的一部分;手册3是给系统程序员用的,主要是指导操作系统编写者如何利用CPU。
   先看看x86指令(IA-32)基本编码格式:
 
这部分没有明确的一致的术语,中文翻译确实有点不伦不类。
一、前缀:(prefix)
   前缀是可选字段,也就是说它可以不出现。它分为四类(组),intel的术语叫四个group。在一条指令里,每一类前缀最多只能出现一次。这四类是:
  1.锁总线和重复前缀
  F0H:LOCK 锁总线前缀
    F2H:REPNE/REPNZ 
  F3H:REP REPE/REPZ
    注意这三小类占用的是一个group,也就是说,前缀中出现了LOCK就不能再出现REP之类的重复前缀。
   其实应该发现一个奇怪的问题,F1H不见了,它是否是一种前缀呢?事实上,inte和amd手册在这里存在着差异。在intel手册中,F1H的指令槽是空出来的,但是AMD手册里面把F1H给了INT1。这实际上是一个undocumented opcode。
  2.段超越和分支提示前缀
  CS/SS/DS/ES/FS/GS 六个段超越前缀 对应的编码是
    2E/36/3E/26/64/65
    分支不被接受:2E
    分支被接受:3E
    注意到,2E和3E是被复用的。如果我们写一个反汇编引擎的时候,仅仅得到2E和3E是不清楚到底是CS/DS的超越前缀还是分支预测。需要往后看opcode,这是解析前缀指令需要注意的一点。请注意段超越前缀不能用于分支跳转类指令,而分支预测前缀只能用于Jcc(条件跳转指令),它们是不存在交集的。
   3.操作数超越前缀
    实话说,此处“超越”翻译的不咋样。实际上,是可以改变操作数的宽度。
    66H
   4.地址超越前缀
    67H
这些前缀有些需要说明:
  1)不少前缀需要等待opcode才能确定。所以编程时候需要往后看opcode才能决定prefix解释成什么。
  2)第一类的REP和REPE有什么区别?REPE和REPZ有什么区别?
   REP和REPE的区别是opcode不同。opcode如果修改标志位,则F3H是REPE,否则就是REP。这也是需要看标志位的prefix之一,实际上是两类指令前缀复用了这个指令槽。
   REPE和REPZ没什么区别,助记符的两种写法。
3)什么叫地址超越?什么叫操作数超越?
   超越是对英文override的翻译,这个翻译很奇特,也不符合override的真实意义。但是国内大部分介绍8086的书籍都采用这种翻译。实际上override在此处意思是“重写”。
   重写了什么呢?操作数默认的size(宽度)可能是16位或者32位。而操作数超越前缀就是允许此指令使用非默认的宽度。
   同样地址超越作用类似,允许指令使用非默认的地址宽度(16位或者32位)。
   那么默认的操作数宽度、默认的地址宽度在哪儿呢?代码段选择子有一个位(第22位 CS.D)指明了默认值。被置位,上面两个默认宽度都是32位;被清零,上面两个默认宽度都是16位。
   请注意,给指令施加这两类超越前缀不会改变默认宽度,仅仅是改变该指令的执行使用的宽度。
二、opcode
   实际上opcode分为两个部分,一部分就是opcode本身,另一部分是个别指令会占用modr/m的reg/opcode位域。
   学MIPS或者其他RISC指令集的见到intel的opcode编码可能会吓一跳。相比较MIPS那种I J R三类指令,x86的指令opcode编码实在是太复杂了。x86的opcode最短是1个字节,最长是3个字节。这是其一。
   和MIPS解决方案不同,x86对于源操作数和目的操作数是暗含在opcode里面的。对于念MIPS教科书过来的朋友,几乎难以理解。这话意思是:
   比如说mov指令:
   
我框出来的这两条指令没有任何的本质区别,就是源操作数和目的操作数反过来了。也就是说谁是源操作数谁是目的操作数就隐藏在opcode中。这和MIPS的想法不同。
比方说:
   88 D9 
对应  MOV CL,BL
而 8A D9
对应  MOV BL,CL
D9还是那个D9,但是谁是源谁是目的不一样了(也就是说由于opcode不同,对于D9的解释不一样)。这一点对于modr/m字节理解有帮助。
单字节opcode比较好理解。
二字节和三字节opcode比较特殊:
二字节通用opcode是0fh+一字节的编码;但是二字节的SIMD opcode是一个强制前缀+0fh+一字节的操作码。
不知道大家看懂了没。intel的二字节的SIMD opcode实际上是三个字节,有一个字节(“强制前缀”)被计入了前缀部分。对于二字节的SIMD opcode,这个前缀不是可选部分。
同样的,三字节的通用opcode,是0fh+二字节的编码。SIMD opcode格式是强制前缀+0FH+二字节编码。
问:
  1)此处强制前缀,是否计入“前缀”的四个字节限制?
    不计入。此处强制前缀,是为了引导出来此处是SIMD指令,类似某种转义码。实际上,此处强制前缀,应该纳入opcode的范畴,所以此处SIMD所谓二字节、三字节名不副实,应该是三字节、四字节。
    个别的SIMD指令不需要强制前缀来引导,比如addps(0FH+58H)
 2)0FH是什么?
    x86常用的转义码。此处用来提示是多字节opcode。
3)强制前缀有哪些?
    和前面的prefix编码有重叠。是66h f3h F2h。
4 ) 二字节和三字节都用0FH作为转义码,那么解码时候如何知道是二字节还是三字节呢?
    问的好!事实上三字节指令的第二个字节(它和0FH一起构成三字节的转义码)是在二字节指令槽里面的。就像0FH在一字节指令槽里面一样。
三、modr/m字节
   modr/m是一个字节。被切分成三个位域(23),它用来确定寻址方式,并可能对opcode做一定补充。
    
  mod:提供寻址模式,11=寄存器寻址 其余都是内存寻址
  reg/opcode:   
    两种作用,第一种是提供寄存器寻址;另一种为某些opcode提供补充说明。
  R/M:
    结合MOD位域,提供内存/寄存器寻址。
 1.这里先研究reg/opcode位域:
   1)请问此处提供的是源操作数还是目的操作数的寄存器寻址?换而言之,比如我要编码mov eax,ebx ,此处reg/opcode负责编码eax还是ebx?
    都可以。取决于你使用什么opcode。这个问题我们在opcode那里已经解释了一部分,这里着重解释一下。
    
请注意画红框的两条opcode,都可以用来编码mov eax,ebx。如果采用89编码,则eax为r/m位域,编码是:
  89H       11         011   000
  opcode   reg寻址    ebx   eax
请注意89编码时候,目的地址是在r/m字段,也就是说,此处的eax是目的地址。这样编码就是89 D8。
而采用8B编码,则eax为reg/opcode位域:
  8BH       11        000    011
 opcode   reg寻址    eax     ebx
最终编码是8B C3。
这也就是我上面说的,x86把源和目的操作数隐藏在opcode里面了。
   2)什么类型的opcode需要这个位域来补充?为何不干脆再用一个字节编码opcode,要占用这个位域?
   这个问题你得问设计这些指令槽的设计师。实际上再设计一个字节编码也毫无问题。这部分编码类似元素周期表中镧系元素和锕系元素。
   3)这会给反汇编引擎带来什么问题?
   导致有些指令(被称为组编码指令--在原始的opcode表里面一组占用一个格子,用阴影区分)需要读到ModR/m才能确定其作用。
2.下面谈mod 位域和R/M位域
   这个位域配合r/m一起来确定寻址方式,mod有4种,每一种对应一种寻址方式。请注意,16位下的mod r/m编码对应寻址方式和32位下的完全不同--也就是说,它们没有任何可比性。
   16位下面是没有SIB字节的,那么16位怎么实现基址变址寻址呢?只好通过modr/m来实现。为此,16位牺牲了通用的寄存器间接寻址(只提供BP BX SI DI作为间接寻址的寄存器,并且BP实现稍微特殊)。也就是说16位下面没有mov [al],bl这种东西。
   
我们稍微来研究一下这张32位的modr/m编码表。mod=11时候,是寄存器寻址,这很简单。请注意红杠标注的那些个行。我们注意到:
 1)[ESP]这种寻址方式在modR/M里面没有。它被用来引导SIB(可以理解R/M=100作为SIB的转义码)。表示[ESP]的编码方法要在SIB里面。
 2)[EBP]这种寻址方式类似16位里面[BP]的编码方式。在mod=00时候,反倒没有[EBP]。[EBP]是采用[EBP]+disp8或者[EBP]+disp32来编码的。
  稍后我们会看到,在SIB里面,也有类似的特殊设置。
四、SIB字节
   
同modr/m类似,SIB字节也是采用23切分成三个位域,名字分别叫Scale、Index、Base。SIB的名字也来自这三个位域名字的首字母缩写。
 SIB字节由R/M=100 MOD≠11引导出来。
SIB确定的寻址方式是[base+Index* Scale +disp]
disp意思是后面尾随的若干个displacement字节。

这个图,可以补充几点:
1)base中的ebp哪去了?
   SIB.base=ebp的时候,要看mod决定是何种寻址方式。上图的附注里面已经说明了。这是解析SIB字节,需要反过来查modr/m的情况。
2)index里面的esp哪去了?
   esp作为index时候,index自动被忽略--此时scale因子视为0.(请注意scale编码为00时候,因子视为1)上表里面none真是混淆是非的典范。也就是说esp作为index,计算方法是[base+disp],不管你的scale是多少。
   这种表示有一个重要的作用,就是我们可以表示[ESP]这种寻址方式。如果你还记得modr/m那边把本该属于[esp]的编码当做转义码来引导SIB字节了,导致modr/m无法表示[ESP]。只好用sib来表示。
  前面说过sib表示的是[base+ Index*Scale+disp],要想表示[esp],只好把base设置成esp,scale因子设置成0,disp设置成0.但是我们发现,scale没有0这个编码(00、01、10、11分别代表1 2 4 8 没有0)。只好用替代的方法,如果index是100(esp),则scale*index=0。
  当然这个规则是一致的,就算你的base不是esp,而是eax等寄存器,只要把index设置成100,scale*index一样视为0。
五、displacement和immediate
   问:
  1)这两者有什么区别?
    要说核心区别,那就是displacement大部分用在寻址上,而immediate用于操作数的编码上。
   举个例子:
   mov ecx,1
可以编码成
   OPCODE IMME
   B9      01 00 00 00H
请注意目的操作的地址ecx(一个寄存器)已经蕴含在操作码B9里面了
而mov ecx,[0x1]
编码成:
  opcode   mod   reg   r/m  displacement
  8B       00    001   101  01 00 00 00H
                 ecx   

  8B 0D 01 00 00 00H
 其中mod和r/m在一起,表示disp32的寻址方式。
当然了,你也可以额外使用一个SIB字节来编码mov ecx,[0x1]
  opcode   mod   reg   r/m  scale    index  base   displacement
  8B       00   001  100    01     100    101  01 00 00 00H
                     引导   scale随便写
即:8B 0C 65 01 00 00 00
其中scale是可以随便写的,00 01 10 11都行,因为index=100,不管你写什么,都视为index*scale=0.
2)嵌入到指令编码里面所有的立即数不是disp就是imme吗?
  个别指令提供了moffs*的立即数寻址,比如mov指令:
 
此处moffs32等就是moffs*的立即数,可以直接编码在opcode后面,你可以把他们归为一类disp,但是这类disp不需要modr/m字节来引导出来。
比如mov eax,[0x1]就可以编码为以下形式:
a)采用moffs32编码
   opcode moffs32
   A1    01 00 00 00
A1 01 00 00 00H
b)采用MODR/M引导的disp
  opcode   mod   reg   r/m  displacement
  8B       00    000   101  01 00 00 00H
8B 05 01 00 00 00H
c)采用SIB引导的disp
  opcode   mod   reg   r/m  scale    index  base   displacement
  8B       00   000  100    01     100    101  01 00 00 00H
                     引导   scale随便写
8B 04 65 01 00 00 00H

[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

上传的附件:
收藏
点赞1
打赏
分享
最新回复 (5)
雪    币: 61
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
小小笑儿 2014-6-7 18:12
2
0
谢谢分享
雪    币: 11281
活跃值: (2842)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
diasstudy 2014-9-7 20:17
3
0
这个写得清楚明了,赞一个。
雪    币: 346
活跃值: (25)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
OnlyForU 2015-1-3 21:32
4
0
好贴!!!!
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_vavgxvvw 2021-10-13 15:23
5
0
厉害,写的真好。
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_vavgxvvw 2021-10-14 21:25
6
0
你的文章好厉害,越看越厉害
游客
登录 | 注册 方可回帖
返回