第49章 IA-32指令
本文最后更新于:2022年5月27日 下午
第49章IA-32指令
本章将学习有关IA-32指令(或称x86指令)的内容。各位刚开始会觉得指令比较复杂,但若
理解了指令的解析方法与原理,就能够轻松地将指令转换为反汇编代码。掌握了这些内容后,各
位的逆向分析技术水平将提高到一个新的层次。
49.1 IA-32指令
简言之,指令是指CPU能够识读的机器语言(Machine Language )。IA-32指令指IA-32 (Intel
Architecture 32位)系列CPU使用的指令。
如图49-1所示,粗线框中的每一行都是1条指令(E8 CC270000、E9 A4FEFFFF、8BFF、55
等都是IA-32指令)。编程人员使用程序语言(C/C++、JAVA, Python等)编写程序,而CPU则使
用机器语言,编程人员编写的程序源代码需要编译/链接后转换为CPU可以识读的机器语言。
提示————————————————————————–
关于IA-32的详细说明请参考以下链接。
维基百科:http://zh.wikipedia.org/wiki/IA-32 (中文版)
http://en.wikipedia.org/wiki/IA-32 (英文版)
http://ko.wikipedia.org/wiki/IA-32 (韩文版)
IA-32用户手册:http://www.intel.com/products/processor/manuals/
49.2常用术语
下面整理一下逆向分析常用术语。讲解IA-32指令的过程中将用到下列术语,准确理解并运
用这些术语将有助于与他人进行顺畅的交流与沟通
使用C/C++语言(或汇编语言)创建出PE文件后,源代码就被转换成了机器码。一名合格的
逆向分析人员必须能够解析这些机器码,并理解其工作原理。但机器码是用二进制(0与1 )表示
的,我们很难读懂它。因此一般要把机器码转换为16进制代码,转换后可读性提高,但我们识读
16进制代码时仍然会感到吃力。所以,最后借助调试器内嵌的反汇编器将机器码转换为反汇编代
码,识读这些反汇编代码就容易多了。
49.2.1反汇编器
图49-2显示的是我们熟悉的OllyDbg调试器的用户界面。OllyDbg调试器内嵌有IA-32反汇编
器(Disassembler )0
图49-2中,1行代码就是1条指令。(A)区域中是16进制表示的IA-32指令,(B)区域中是与之对
应的反汇编代码,(C)区域是指令在内存(或文件)中的实际形式。
以地址401000处的指令为例,(A)区中的 “68 84B34000” 是IA-32指令,(B)区中
的“PUSH 0040B384” 为反汇编代码。
反汇编代码大致由助记符(Mnemonic )与操作数( Operand )组成,助记符表明
指令功能,操作数指示操作对象。“PUSH 0040B384”指令中,PUSH为助记符,0040B384
为操作数。
内嵌在调试器中的反汇编器解析(C)区中的十六进制机器码,将它们切分为(A)区中的一条条
指令,然后将每条指令转换为(B)区中相应的反汇编代码。从易读性来看,(C)中代码低于(A)区代
码,(A)区代码又低于(B)区代码。逆向分析人员一般会阅读(B)区中的反汇编代码,并进行相应分
析。学习IA-32指令后就能分析(A)区中的代码了。
49.2.2反编译器
近来,大量PE文件都是使用C/C++/VB/Delphi语言编写的。反编译器(Decompiler)与反汇
编器在概念上类似,但是反汇编器用来将机器代码转换为反汇编代码,而反编译器则用来将机器
代码反编译为类似于源代码的代码(C/C++/VB+/Delphi)(反编译时需选用相应语言的反编译
器)。当然,反编译岀的代码与源代码还是有一定差距的,但随着技术的不断发展,这种差距会
越来越小。
49.2.3反编译简介
本节我们将在IDA Pro分析工具中借助Hex-Rays Decompiler插件将C语言程序(机器代码)反
编译为类C语言的代码,并比较它与程序源码的不同。先看程序的C语言源代码,如图49-3所示。
get_folder_count(LPCSTRszPath)是个非常简单的函数,用来计算参数给定路径(szPath )中
文件夹的个数^上述源代码经过编译后生成PE文件,在IDA Pro分析工具中使用Hex-Rays
Decompiler插件将生成的PE文件反编译(Decompile )为类C语言代码,如图49~4所不。
各位一定大吃一惊。从图49-4中可以看到,反编译得到的代码与程序的源代码非常相似,仅
函数名称(sub_401000)、变量名称(vl、v2、v3、v8)不同而已。程序的代码较长且较复杂时,
反编译得到的代码可读性可能下降,但是借助反编译代码我们能够快速把握程序的代码结构,从
这个意义来说,反编译仍然是个非常棒的功能。
拥有这种强大功能的IDA Pro分析工具与Hex-Rays Decompiler插件都是付费的商业软件,且
价格昂贵,一般人难以承受,大部分都由公司购买使用。使用OllyDbg调试器打开上面的程序文
件,可以看到反汇编后的代码,如图49-5所示。
从图49-5中可以看到,反汇编代码看上去比较复杂,不如反编译后的代码更容易阅读,由此
可见反编译器多么有用。
提示————————————————————————–
请注意,反编译器也不是万能的。若使用保护器等工具故意打乱程序代码,或在
程序运行中使用一些操作技术,就不能反编译,或者使反编译后的代码更加复杂。所 以,学习高级逆向分析技术时一定要掌握反汇编代码和指令。
49.3 IA-32指令格式
提示————————————————————————–
现在我们学习中级逆向分析技术。如果你仍是逆向分析技术的初学者,不太理解
本部分内容,没关系,阅读后直接跳过即可。以后自己的技术水平提高了,需要了解
指令相关知识时再学习即可。以下内容是我在“ Intel® 64 and IA-32 Architectures
Software Developer’s Manuals”基础上整理而来的。更多详细说明请参考相关用户手册。
正文中使用的有关IA-32指令的图片均出自Intel的用户手册。
下面学习有关IA-32指令格式的知识。
如图49-6所示,IA-32指令由6部分组成,其中操作码项是必需的,其他项目都是可选的。接
下来对指令的各组成部分予以说明
49.3.1指令前缀
指令前缀(Instruction Prefixes)是一个可选项目,后面岀现特定操作码时将补充说明其含义。
下面举个简单的例子。
前缀项大小为1个字节(后面讲解“指令解析方法”(借助操作码映射解析)时会详细说明
Prefix 66的含义)。
49.3.2操作码
Opcode ( Operation Code,操作码)是必不可少的部分,用来表示实际的指令。
操作码长度为1〜3个字节,我们在常见的应用程序调试中遇到的操作码大部分都是1个字节,
有时也会遇到2个字节的操作码。3个字节长度的操作码主要用在MMX ( MultiMedia extension, 多媒体扩展)相关指令中,一般很少有机会接触到。操作码通常都带有操作数(Operand),操作
数种类有寄存器、内存地址、常量(Contanst )0指令中出现的ModR/M与SIB选项辅助操作码确
定操作数。
操作码种类很多,解析时一般需要查看Intel用户手册的操作码映射。后面讲解“指令解析方
法”时会做一些解析操作码的练习。
49.3.3 ModR/M
ModR/M是个可选项,主要用来辅助说明操作码的操作数(操作数的个数、种类[寄存器、地
址、常量])
ModR/M项拥有1个字节(8位)长度,分为3部分,各部分含义如图49-7所示。
49.3.4 SIB
SIB ( Scale-Index-Base )也是一个可选项,用来辅助说明ModR/M。操作码的操作数为内存
地址时,需要与ModR/M项一起使用。
SIB项也拥有1个字节(8位)长度,分为3部分,各部分含义如图49-8所示。
49.3.5位移
位移(Displacement)也是可选项,操作码的操作数为内存地址时,用来表示位移操作。
位移的长度为1、2、4字节。
49.3.6立即数
立即数(Immediate)也是一个可选项,操作码的操作数为常量时,该常量就被称为立即数。
立即数的长度为1、2、4字节。
49.4指令解析手册
首先制作“指令解析手册”,然后借助该手册练习指令解析。
49.4.1下载IA-32用户手册
Intel公司提供的用户手册对IA-32进行了详细说明。代码逆向分析中会经常参考该用户手册,
所以请从下面地址下载。
http://www.intel.com/products/processor/manuals/
进人页面下载这个文件。
Intel® 64 and IA-32 Architectures Software Developer’s Manuals Volume 2A.pdf
Intel® 64 and IA-32 Architectures Software Developer’s Manuals Volume 2B.pdf
49.4.2打印指令解析手册
下载Intel用户手册后,其中有些表格是解析指令时需要参考的,请将下面列岀的表格打印
出来。
上面打印的资料用得多了、泛黄的时候,各位也就成了解析IA-32指令的高手。
我几年间一直使用一部自制的指令手册,有人问有关指令解析的问题时,我就会翻阅
它并给出解答。翻阅得多了,就有了感觉,哪些内容在哪页一清二楚,所以查起来非
常快。我尊敬的一位前辈也有这样的参考资料,当时看上去都用旧了。从某种意义上
说,这种使用痕迹就像一把测量逆向分析人员“年轮”的尺子。希望各位也打印一份
这样的资料,需要的时候随时翻阅查看(参考图49-9)
49.5指令解析练习
我们从本节开始学习IA-32指令解析方法。
49.5.1操作码映射
首先解析1条长度为1个字节的操作码指令。
41 INC ECX
查看前面打印岀的操作码手册(或者Intel Manual Vol.2B )中的“TableA-2. One-byte Opcode Map”。
解析指令时,最先要查看表格的名称是否为“Table A-2. One-byte Opcode Map:(00H-F7H)*”。
然后将要查找的操作码41拆分为4与1,再在操作码映射中将它们分别作为表格的行与列进行查
找。从查找的结果看,操作码41对应的指令为INC,操作数为ECX(同一方格中的REX.B是64位
系统专用操作数,忽略即可)。所以指令41最终被解析为INC ECX指令,使用图49-6中的IA-32指
令格式表示如下:
图49-10中INC指令的上标为i64, REX指令的上标为o64,它们的含义在操作码
用户手册的 “Table A-l. Superscripts Utilized in Opcode Tables” 中给出了 讲解3根据表
格中的说明,i64表示不在64位模式中使用,o64表示只在64位模式中使用。所以操
作码4047在32位模式中作为INC指令使用,在64位模式(o64)中用作REX前缀。
由于这里解析的是IA-32指令,所以应该解析为INC指令。操作数亦是如此,在32位
模式中要选择ECX,在64位模式中要选择REX.B。
49.5.2操作数
下面继续通过练习来学习操作数的形态结构。
使用前面的方法在Table A-2操作码映射中查找指令的第一个字节68,如图所示。
从图49-11中可以看到,操作码68对应于PUSH Iz,为带有1个操作数的PUSH指令。
提示————————————————————————–
Table A-2 One-byte Opcode Map内容繁多,在Intel用户手册中占了不少分量,图
49-11是其中一部分。
Iz用来表示操作数的类型,把握其含义即可准确解析整条指令。大写字母I指寻址方法
(Addressing Method ),小写字母z指操作数类型( Operand Type ),A.2.1 Codes for Addressing Method
与A.2.2 Codes for Operand Type中分别对它们进行了说明。
常用寻址方法整理如表49-2所示。
指示寻址方法的大写字母I代表Immediate (立即数)。若想知道立即数的大小,还要参考操作
数类型。
常用操作数类型整理如表49-3所示。
指示操作数类型的小写字母z在32位模式下表示的大小为DWORD (32位,4个字节)。综合
以上信息,操作码68对应的PUSH Iz指令中,Iz (操作数格式)表示大小为4个字节(32位)的立
即数,所以继续读取68之后的4个字节( 0040B440 ),整条指令最终解析为PUSH 0040B4A0,用
IA-32指令格式表示如下:
以上只是解析指令的“热身”练习,下面正式学习指令解析方法。
49.5.3 ModR/M
首先学习包含ModR/M的指令解析方法。
89C1 MOV ECX,EAX
先在操作码映射中查找Opcode 89,如图49-12所示。
从操作码映射中可以看到,Opcode89对应MOVEv,Gv指令,带有2个操作数,第一个操作数
的格式为Ev,第二个操作数格式为Gv。
由表49-3可知,操作数类型中的小写字母v表示操作数的大小为32位(4个字节)。上面指令
中2个操作数的大小均为4个字节。接下来需要把握大写字母E与G代表的含义,这样才能准确解
析整条指令。根据表49-2中的说明,大写字母E是寄存器或内存地址形式的操作数,大写字母G
只能是寄存器形式的操作数。操作数形式为E或G时,操作码后面一定存在ModR/M选项。
了解操作码后就能掌握操作码映射中的指令格式(Mnemonic、操作数个数、操作
数大小)。若操作数的寻址方法为E或G,则分析操作码后面的ModR/M即可准确解析
操作数。
所以,整条指令89C1中,紧跟在Opcode 89后面的1个字节Cl即为ModR/M选项。ModR/M表
格是指操作码手册中的Table 2-2. 32-Bit Addressing Forms with the ModR/M Byte,如图49-13所7K。
图49-13中的ModR/M表看上去比操作码映射复杂得多,但熟悉原理后就能很容易地掌握其使
用方法。ModR/M长度为1个字节,在图49-13的ModR/M表中的[ModR/M]区域内,从00开始到FF
结束。前面讲解ModR/M时提到过,它按比特位分为3个部分,分别为Mod、Reg、R/M(参考ModR/M
说明)。下面以ModR/M Cl为例讲解。
ModR/M拆分后的各值在图49-13中的Mod、REG、R/M部分表示出来(二进制)。请各位在
图49-13中查找C1,分别确认Mod、Reg、R/M的值。操作数的寻址方法为E时,操作数的形式在
图49-13左侧的[E]区域(图49-13表的左侧部分)中显示。
提示————————————————————————–
寻址方法“E”代表内存地址或寄存器。从图49-13中可以看到,ModR/M值为
00〜BF时,显示在[E]区域中的操作数为内存地址;ModR/M值为C0〜FF时,出现在[E]
区域中的操作数为寄存器。
操作数的寻址方法为G时,操作数的形式出现在图49-13中的[G]区域(图49-13的表上端
部分)。
提示————————————————————————–
寻址方法“G”仅有寄存器形式,表49-2中已经说明。从图49-13可以看到,根
据ModR/M的Reg值(000〜111 ),从EAX变化到EDI (操作数大小为32位时)。
再回来解析指令89C1,Opcode 89对应MOV Ev,Gv指令,且带有ModR/M值。ModR/M为C1,
从图49-13中可以得知,Ev=ECX, Gv=EAX。所以,指令89C1最终解析为MOV ECXJEAX。
提示————————————————————————–
大写字母E与G表示寻址方法,在图49-13中分别出现在[E]区域与[G]区域。小写
字母v表示操作数类型,32位计算中操作数的大小也为32位。根据这些信息在图49-13
中查找ModR/M值Cl,可知Ev、Gv分别为ECX’ EAX。
用IA-32指令格式表示如下:
[Prefixes][Opcode][ModR/M][SIB][Displacement][Immediate]
49.5.4 Group
Gmup指令语句将操作码与ModR/M组合起来,使操作码最多可以表示出8种形式的指令
(Mnemonic )0灵活使用Group指令虽然会使解析变得有些复杂,却能较好地扩展操作码映射。
83C3 12 ADD EBX,12
首先在操作码映射中查找83对应的指令形式。
从图49-14可知,Opcode 83对应的指令为Grp 1 Ev,Ib,带有2个操作数,形式分别为Ev、lb。
由前面的讲解可知,Ev代表4个字节的寄存器(或者内存地址),lb代表1个字节的立即数(根据
表49-3可知,操作数类型的b代表字节大小)
提示————————————————————————–
到现在为止,解析上述语句时仍未出现指令(Mnemonic ),只是表示成了 Immediate
Grp 1的形式,即在多种形式的Group中它是Immediate Grp 1。1条操作码中包含多种
指令,根据后面的ModR/M可以最终确定相应指令。前面图中,Immediate Group 1
的第二个操作数是立即数。该Group中的指令用来计算(ADD、SUB, XOR等)这类
立即数。
操作数中含有Ev符号,所以紧跟在后面的1个字节(C3 )为ModR/M。操作码为Group指令语
句时,后面必须紧跟ModR/M。综合目前获取的各种信息,指令83C312解析如下:
到现在还是无法确切知道指令(Mnemonic)与操作数的内容。分析ModR/M “C3” 后即可
准确解析上述指令(解析与顺序无关,但从左到右解析起来会更简便)。
首先参考Group指令表查看对应指令(Mnemonic)。Group指令表是指操作码用户手册中的
Table A-6. Opcode Extentions for One- and Two Opcodes by Group Number,如图49-15所示。
图49-15中操作码为83,所以只看Group 1项目即可。并且,由于ModR/MC3的Reg值为000(二
进制),所以对应的指令(Mnemonic )为ADD。综合以上分析,指令83C312解析如下:
接下来,先确定第一个操作数。在ModR/M表中查找C3值。
由图49-16可知,ModR/M “C3” 的Ev对应的值为EBX。
从图49-14可知,第一个操作数的格式为Ev。在图49-13、图49-16的[E]区域中
查找寻址方法符号E对应的值,有EBX、BX、BL这3种,再加上操作数类型符号是
小写字母v,所以最终选择4个字节的EBX寄存器(该处的E只能为通用寄存器,不
可能为MM3与XMM3寄存器)。也不可以选择[G]区域中的EAX值,[G]区域要在寻
址方法符号为G时使用。ModR/M表比较复杂,必须准确理解其使用方法。
至此,指令83C312解析如下:
83C312 • ADD EBX, lb
第二个操作数的符号为Ib,表示1个字节大小的立即数,直接读取ModR/M后面的1个字节(12 )
即可。综上所述,指令83C312最终解析为如下形式:
83C312 - ADD EBX, 12
用IA-32指令格式表示如下:
[Prefixes][Opcode][ModR/M][SIB][Displacement][Immediate]
49.5.5前缀
本小节介绍含Prefix (前缀)的指令解析方法。有些前缀( 66,67 )对整条指令的解析有着重
要影响,所以必须掌握。
66:81FE 3412 CMP SI,1234
首先在Table A-2 Opcode Map中查找 “66”。
由图49-17可知,“66” 表示操作数大小(前缀)。更准确地说,它表示的是Operand-Size Override
Prefix, Prefix 66的含义为“将32位大小的操作数识别为16位大小(或者将16位大小的操作数识别
为32位大小)”。紧接在Prefix 66后面的1个字节81为操作码,在操作码映射中查找 “81”,如图
49-18所示0
Opcode 81为Group指令,带有2个操作数(Ev, Iz )。符号Ev—般表示32位的寄存器(或内存
地址),而符号Iv表示32位立即数(参考操作码用户手册A.2.1&A.2.2)。但因前面有Operand-Size
Override Prefix 66,故操作数的大小分别由32位变为16位。操作数格式中出现符号E,则表示后面
跟有ModR/M字节(FE)。综合以上分析,指令6681FE3412解析如下(请注意:Prefix 66使操作
数大小变为16位):
6681FE 3412 • Grpl Ev, Iz (Operand Size = 16 bit)
要想得到准确指令与操作数,还要分析ModR/M值FE代表的含义。
Ex) ModR/M = FE
FE的二进制表示=11111110
拆分ModR/M = 1111111110 (Mod: 11, Reg:111, R/M:110)
接着在Table A-6中查看Group指令,确定其代表的具体指令,如图49-19所示。
由图49-19中的Group表可知,Opcode 81 (Group 1 )中,ModR/M的REG (值为111 )对应的
实际指令为CMP。
6681FE 3412 CMP Ev, Iz (Operand Size = 16 bit)
接下来开始确定第一个操作数。在ModR/M表格中查找“FE”,如图49-20所示
从图49-20中可以看到,ModR/M “FE”的Ev符号对应值为ESI,但是受Prefix 66的限制,要
选择16位的SI。
6681FE 3412 - CMP SI, Iz (Operand Size = 16 bit)
第二个操作数的符号为Iz,原指4个字节(32位)的立即数值,但受Prefix 66的影响,其变为
2个字节(16位)大小,即获取ModR/M后面的2个字节(1234)。所以整条指令最终解析如下:
6681FE 3412 • CMP SI, 1234
用IA-32指令格式表示如下:
[Prefixes][Opcode][ModR/M][SIB][Displacement][Immediate]
49.5.6双字节操作码
本小节学习操作码为双字节时的指令解析方法。单字节操作码不够用时,可以将其扩展为双
字节。双字节操作码中第一个字节恒为0F,故其在操作码映射中的查找方式与单字节操作码是一
样的。
0F85 FA1F00O0 JNZ XXXXXXXX
先在单字节操作码映射中查找指令的第一个字节(0F),如图49-21所示。
由上表可知,“OF”为双字节操作码的Escape符号,指示继续在Table A-3中查找双字节操作
码。双字节操作码映射(即TableA-3 )在Intel用户手册中的分量是单字节的两倍,如图49-22所示。
从图49-22的表格标题可以看到,第一个字节为 “OF”。查找第二个字节85,可以看到它对应
的指令为JNE ( JNZ )。
提示————————————————————————–
Jcc为Conditional Jump (条件跳转)指令,一般这种条件跳转指令之前都有比较
语句(CMP、TEST),并根据比较的结果决定是否跳转。Jcc指令有多种形式,示例中
的0F85被解析为JNE ( Jump Not Equal)或JNZ ( Jump Not Zero )指令(两条指令含
义相同)。Jcc指令( 0F80〜0F8F )的操作数在图49-22中显示为“Long-displacement”。
操作数说明中,一般Long表示4个字节(32位),Short表示1个字节( 8位)。所以,
Jcc指令的操作数为4字节大小的Displacement (移位值)。
由于JNE指令的操作数为4个字节大小的移位值,继续读取操作码后面的4个字节
(00001FFA),整条指令解析如下:
以上指令中的移位值00001FFA为相对位移,加上当前的EIP才能准确算出跳转地
址。比如,上述指令的地址为401000,执行指令后,EIP值为401006 (增加6个字节
(指令长度)),那么实际跳转的地址为403000 (JNE 403000),它是EIP值( 401006 )
与移位值(1FFA )相加的结果。调试中会经常遇到“相对位移”这一术语,希望各位
理解其含义。
49.5.7移位值&立即数
若指令中同时存在移位值&立即数,该如何解析呢?下面学习这种指令的解析方法。
首先在操作码映射中查找“C7”,如图49-33所示。
Opcode C7对应Group 11 MOV指令,带有2个操作数(Ev,Iz )。所以上述指令解析如下:
岀现Group指令或操作数形式中有E、G时,操作码之后必跟着ModR/M选项,上述指令中
ModR/M的值为05。
接着在Group指令表(Table A-6)中查找其对应的实际指令(Mnemonic),如图49-24所示
Group 11中ModR/M的Reg值( 000 )对应的指令为MOV,且在Group 11中仅有一个MOV指令
(故图49-23中岀现了 “Groupll-MOV”的标识)。
C705 00CF4000 01000100 - MOV Ev, Iz
接下来确定第一个操作数(Ev),在ModR/M表(Table 2-2)中查找 “05”,如图49-25所示。
第一个操作数(Ev)为“disp32”,代表32位大小的移位值。ModR/M在00〜BF范围内时,Ev
形式的操作数表示内存地址(ModR/M在C0〜FF范围内时,Ev®式的操作数表示寄存器)。所以,
ModR/M之后的4个字节( 0040CF00 )为移位值,表示内存地址(表示地址时一定要用上[]中括号)。
C705 00CF4000 01000100 - MOV [0040CF00], Iz
最后,第二个操作数Iz为4个字节大小的立即数,从移位值之后读取4个字节(00010001 )
即可。
C705 0OCF4000 01000100 • MOV [0040CF00], 10001
上述指令表示向40CF00地址中放人10001值,使用IA-32指令格式表示如下:
[Prefixes][Opcode][ModR/M][SIB][Displacement][Immediate]
49.5.8 SIB
操作数指向内存地址时,SIB ( Scale, Index、Base)用来辅助寻址。指令中含有SIB时,其
本身会变得较为复杂。换言之,如果掌握了含有SIB的指令解析方法,你就达到了大师级别。
8B0C01 MOV ECX, [EAX+ECX]
首先在操作码映射中查找 “8B”,如图49-26所示。
由图49-26可知,Opcode 8B对应的指令为MOV Gv,Ev,带有2个操作数,第一个操作数为Gv
形式,是一个4字节的寄存器;第二个操作数为E4式,是一个4字节的寄存器或内存地址。操作
数的寻址方法为G、EB寸,操作码后会跟着ModR/M选项。解析出该ModR/M即可准确解析操作数。
上述指令中ModR/M为0C,在ModR/M表中查找,如图49-27所示。
第一个操作数Gv对应的值为ECX,第二个操作数Ev对应的值为符号表示需要
SIB字节来辅助表示准确地址。综合以上分析,上述指令解析如下:
第二个操作数的符号[-][-]指向内存地址,要准确解析它需要用到SIB字节,用更直观的方
式表示如下:
重写上述指令如下:
这种表7K方法更加直观。接下来在SIB表中查找Reg.A与Reg.B,如图49-28所示。
前面的指令中,SIB的值为01,SIB表就是操作码用户手册中的Table 2-3。
査看SIB表的方法与查看ModR/M表的方法类似,先找到SIB值,然后在表顶部的[Reg.A]区域
获取Reg.A寄存器,然后在表左侧的[Reg.B]区域获取Reg.B寄存器。图49-28中与SIB 01对应的
Reg.A为ECX,Reg.B为EAX。所以指令最终解析如下:
用IA-32指令格式表示如下:
操作数为(复杂形式的)内存地址时,SIB就像这样用来辅助寻址。接下来解析一条带有更
复杂的SIB选项的指令。
如图49-29所示,Opcode 8D对应的指令为LEA Gv,M。
LEA指令是“Load Effective Address”的缩写,是“取有效地址指令”。第一个操作数为Gv
形式,是4个字节大小的寄存器;第二个操作数为M形式,仅表示内存地址(参考表49-2)。
由于操作数的寻址方法为G、M,所以操作码后面跟着ModR/M字节,分析后面的ModR/M即
可得到准确的操作数值。上述指令中ModR/M值为84,在Table 2-2中查找它,如图49-30所示。
第一个操作数Gv形式为EAX,第二个操作数M形式为[–][–]+disp32。
各位现在已经非常熟悉ModR/M表格了吧?在表格上端求G形式的值,在表格左
侧求E或M形式的值。
综合以上分析,上述指令解析如下
第二个操作数中的[–][–]+disp32指的是内存地址,要想准确解析,需要使用后面的SIB字节
与32位的移位值。使用更直观的方式表示[–][–]+disp32如下:
用更直观的方式解析上述指令。
8D8428 B1354000 - LEA EAX, [(Reg. A)+(Reg. B) + disp32]
接下来查看SIB表以获取Reg.A与Reg.B对应的值。因SIB为28,故Reg.A为EAX, Reg.B为EBP
(参考图49-31 )。
上述指令中disp32的值为SIB后的004035B1。综合以上所有分析,整条指令最终解析如下:
8D8428 B1354000 LEA EAX, [EAX+EBP+4035B1]
49.6指令解析课外练习
若想完全掌握指令解析方法,需要大量练习。反复看前面的练习示例,熟悉解析方法后,在
OllyDbg调试器中打开并运行notepad.exe程序,如图49-32所示。
参考前面打印岀的操作码用户手册,将区域[1]中的指令逐条解析为反汇编代码(请先隐藏
OllyDbg调试器中的反汇编窗口)。指令解析的成功率达到99%以上后,再看区域[2]中的机器代码,
将它们解析为反汇编代码。通过这些训练,各位会迅速成长为IA-32指令解析高手。
49.7小结
本章主要讲解IA-32指令的解析方法,刚接触它的读者朋友可能会觉得有些难度。但若完全
掌握了指令解析方法,逆向技术水平就会达到中级以上。
我主要使用IA-32指令来编写查杀恶意代码的函数。检测变形病毒(Polymorphic Vims)时,
必须探测岀Polymorphic引擎中产生的指令类型,这时就需要分析人员具有丰富的指令知识。此外,
了解指令结构也有助于提高代码调试水平。掌握IA-32指令相关知识对于编写“打补丁”代码、
分析漏洞Shell代码都非常有帮助。当然,掌握IA-32指令解析方法对于提高各位的逆向分析技术
水平也有相当大的帮助。
以上内容仅涉及Intel用户手册中的极少部分。若想深入学习有关IA-32指令的知
识,请各位认真研读 Intel® 64 and IA-32 Architectures Software Developer,s Manuals (与
指令解析相关的部分为Vol. 2A-2.K Vol.2B-Appendix A )D