第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


第49章 IA-32指令
https://m0ck1ng-b1rd.github.io/1999/04/09/逆向工程核心原理/第49章 IA-32指令/
作者
何语灵
发布于
1999年4月9日
许可协议