这六个域分别是:
prefixes (1 Byte)
code (1/2/3 Bytes;2字节指令是第一个指令码为0x0f,而一般3字节指令的第3字节是ModR/M的一部分)
ModR/M (1 Byte)
SIB (1 Byte)
displacement (1/2/4 Bytes)偏移量
immediate (1/2/4 Bytes)立即数
在任何一条指令中code域必须出现,其他的域都是可选的。
由于这六个域在指令中的排列顺序是固定的,所以反汇编机器码,就是一个对它们的依次识别过程。
1、对prefixes的识别
x86体系结构CPU的4类prefixes,它们分别为:
lock/repeat prefix :F0/F2,F3 (LOCK和REP)
segment override prefixes :2E,36,3E,26,64,65 (忽略段的前缀)
operand-size override prefix :66 (忽略操作数大小的前缀)
address-size override prefix :67 (忽略地址大小的前缀)
指令的prefixes可以由这几类prefixes组成,但是每类prefixes只能在指令中出现一次,至于每类prefixes在指令的出项顺序是没有要求的,这点和指令的六个域是不同的。当某类prefixes在同一指令中出现多次的时候,CPU在执行过程中,可能会出现不可预料的结果,至于会不会出现异常,Intel的官方手册中只用了这句话来说明:such use may cause unpredictable behavior.鬼知道会出项什么情况,因此算法必须对这类机器码具有识别能力。但是也可能异常情况不会发生,在反汇编过程,遇到同一类prefixes出现多次的情况,以最后出现的prefix为准,进行机器码识别。
总结:
(1)Prefixes是唯一的一个可能出现在code之前的域。
(2)所有的Prefixes都只有1个字节。
(3)在一个OpCode中可能会有多个Prefixes。
(4)如果有多个Prefixes,那么它们的顺序可以打乱,不会有任何问题。
(5)如果Prefixes不能对随它之后的OpCode起作用,那么它就会被忽略。
2、对code和operand的识别
CPU在设计时,为了提高比特位的利用率,也为了保证一个code不是另一个code的前缀(否则CPU也无法译码),code的编码采用的是哈夫曼算法。code的最大长度是3个字节,当然可以是1个字节,也可以是2个字节,另外,对于某些特定的code,还有3个比特的信息也会用来表示code.这3比特在ModR/M的3、4和5位。当然每个code也最多只能有三个operand。
对code的识别一般都是采用二维表格来驱动的。二维表格中记录了给各code的详细信息。
ModR/M字节:这个字节指示了后面跟随的操作数的形式,它分成3个部分:第7-6位是Mod部分,第5-3位是Reg部分,第2-0位是R/M部分,其中Mod和R/M部分结合指示了指令操作数的寻址方式,而Reg部分主要是指示用到的寄存器,这个字节的解释要和主操作码结合起来。
SIB字节:除了上面这个ModR/M字节外,有时候指令还需要一个字节来补充指操作数的寻址方式,这个字节也分成3个部分:第7-6位是scale部分,第5-3位是index部分,第2-0位是base部分;
在翻译过程中,以指令的操作码为索引从相应的数组来输出对应的指令字符串,而接下来最主要的就是对操作数的翻译,所有的操作数都可以用:【寻址方式 + 操作数类型】来表示,寻址方式有A(直接地址),C(ModR/M字节的REG部分指示一个控制寄存器),D( ModR/M字节的REG部分指示一个调试寄存器),E(ModR/M字节指示的操作数是一个通用寄存器或一个内存地址),F(EFLAGS寄存器),G(ModR/M字节的REG部分指示一个通用寄存器),I(立即数),J(指令包含一个相对地址),M(ModR/M字节指示一个内存地址),O(没有ModR/M字节,后面的字或双字为偏移地址),P(ModR/M字节的REG部分指示一个4字节MMX寄存器),Q(ModR/M字节指示的操作数是一个MMX寄存器或一个内存地址),R(ModR/M字节的Mod部分指示一个通用寄存器),S(ModR/M字节的REG部分指示一个段寄存器),T(ModR/M字节的REG部分指示一个测试寄存器),V(ModR/M字节的REG部分指示一个128位XMM寄存器),W(ModR/M字节指示的操作数是一个128位XMM寄存器或一个内存地址),X(内存地址由DS:SI寄存器对表示),Y(内存地址由ES:DI寄存器对表示);操作数类型主要有b(字节),d(双字节),w(字),v(字或双字,根据操作数大小定)。
3、现在就可以根据下面的两个表来进行机器码识别了。例如,如果二进制序列是0x03,0x70,0xe8,则操作码是0x03,查指令表Table A-2是"add Gv,Ev",第2个字节0x70是ModR/M字节,二进制是01110000,则Mod部分是1,REG部分是6,R/M部分是0,查ModR/M字节表Table 2-2得到Gv操作数是esi,Ev操作数是[eax+disp8],disp8是8位的偏移量,则读下一个字节0xe8,由于这个字节是负数,取相反值是0x18,则Ev操作数是[eax-0x18],所以输出的最终指令是"add esi,[eax-0x18]"。
4、扩展指令
除了单字节指令和二字节指令外还有一部分指令是三字节的,这些指令称作扩展指令,扩展指令是把ModR/M字节当作操作码的一部分,根据指令属于的不同group和REG部分的值来查Intel Manual中表Table A-4翻译指令的,如操作码为0x80-0x83的指令就是属于group 1,然后根据REG的值来确定操作码的字符串,如REG为5则是"sub"指令。
5、浮点数指令
浮点数指令又叫逃逸码指令(escape opcode instructions。它是主操作码为0xd8-0xdf的指令,并且用这个主操作码分成几组,每组指令中又根据ModR/M字节的值分成值在0x00-0xbf范围中和不在这个范围中两部分,在这个范围中的指令根据ModR/M字节字节中的REG部分进行索引,得到相应的指令;不在这个范围中的指令则按照ModR/M字节的值查找相应的表得到指令,ModR/M字节中的高4位是行号,低4位是列号。
x86结构机器码识别及其反汇编
最新推荐文章于 2024-07-16 00:01:14 发布