第49章 IA-32指令 - images.china-pub.com

23
49IA-32指令 本章将学习有关IA-32指令(或称x86指令)的内容。各位刚开始会觉得指令比较复杂,但若 理解了指令的解析方法与原理,就能够轻松地将指令转换为反汇编代码。掌握了这些内容后,各 位的逆向分析技术水平将提高到一个新的层次。 49.1 IA-32 指令 简言之,指令是指CPU能够识读的机器语言(Machine Language)。IA-32指令指IA-32Intel Architecture 32位)系列CPU使用的指令。 49-1 IA-32指令 如图49-1所示,粗线框中的每一行都是1条指令(E8 CC270000E9 A4FEFFFF8BFF55 等都是IA-32指令)。编程人员使用程序语言(C/C++JAVAPython等)编写程序,而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指令的过程中将用到下列术语,准确理解并运 用这些术语将有助于与他人进行顺畅的交流与沟通。 49-1 常用术语 Machine Language 机器语言,CPU可以解析的二进制代码(Binary01Instruction 一条机器指令(由OpCodeoperand等组成)

Transcript of 第49章 IA-32指令 - images.china-pub.com

502 第 49 章 IA-32 指令

第49章 IA-32指令

本章将学习有关IA-32指令(或称x86指令)的内容。各位刚开始会觉得指令比较复杂,但若

理解了指令的解析方法与原理,就能够轻松地将指令转换为反汇编代码。掌握了这些内容后,各

位的逆向分析技术水平将提高到一个新的层次。

49.1 IA-32 指令

简言之,指令是指CPU能够识读的机器语言(Machine Language)。IA-32指令指IA-32(Intel

Architecture 32位)系列CPU使用的指令。

图49-1 IA-32指令

如图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指令的过程中将用到下列术语,准确理解并运

用这些术语将有助于与他人进行顺畅的交流与沟通。

表49-1 常用术语

术 语 说 明 Machine Language 机器语言,CPU可以解析的二进制代码(Binary,0与1)

Instruction 一条机器指令(由OpCode与operand等组成)

49.2 常用术语 503

1

2

3

4

5

19

6

7

8

9

10

11

12

13

14

15

16

17

18

(续)

术 语 说 明 OpCode 操作码(Operation Code),指令内的实际指令

Assembly 汇编编程语言

Assemble 将汇编代码转换为机器语言(OpCode)(类似于C/C++的Compile)

Assembler 执行Assemble作业的程序(如:MASM、TASM、FASM等)

Disassemble 将机器语言转换为汇编语言(也用Unassemble一词)

Disassembler 执行Disassemble作业的程序(一般内嵌在调试器中-OllyDbg、IDA Pro等)

Disassembly 经过Disassemble生成的汇编语言(变量名、函数名等被替换为地址,可读性差)

*Compile 将C/C++等编写的源代码转换为机器代码(生成Obj文件)

*Link 将Obj文件链接为可执行文件(生成Exe/Dll文件 )

使用C/C++语言(或汇编语言)创建出PE文件后,源代码就被转换成了机器码。一名合格的

逆向分析人员必须能够解析这些机器码,并理解其工作原理。但机器码是用二进制(0与1)表示

的,我们很难读懂它。因此一般要把机器码转换为16进制代码,转换后可读性提高,但我们识读

16进制代码时仍然会感到吃力。所以,最后借助调试器内嵌的反汇编器将机器码转换为反汇编代

码,识读这些反汇编代码就容易多了。

49.2.1 反汇编器

图49-2显示的是我们熟悉的OllyDbg调试器的用户界面。OllyDbg调试器内嵌有IA-32反汇编

器(Disassembler)。

图49-2 OllyDbg用户界面

图49-2中,1行代码就是1条指令。(A)区域中是16进制表示的IA-32指令,(B)区域中是与之对

应的反汇编代码,(C)区域是指令在内存(或文件)中的实际形式。

504 第 49 章 IA-32 指令

提示 以地址 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所示。

图49-3 C语言源代码

49.2 常用术语 505

1

2

3

4

5

19

6

7

8

9

10

11

12

13

14

15

16

17

18

get_folder_count(LPCSTR szPath)是个非常简单的函数,用来计算参数给定路径(szPath)中

文件夹的个数。上述源代码经过编译后生成PE文件,在IDA Pro分析工具中使用Hex-Rays

Decompiler插件将生成的PE文件反编译(Decompile)为类C语言代码,如图49-4所示。

图49-4 反编译后的C语言代码

各位一定大吃一惊。从图49-4中可以看到,反编译得到的代码与程序的源代码非常相似,仅

函数名称(sub_401000)、变量名称(v1、v2、v3、v8)不同而已。程序的代码较长且较复杂时,

反编译得到的代码可读性可能下降,但是借助反编译代码我们能够快速把握程序的代码结构,从

这个意义来说,反编译仍然是个非常棒的功能。

拥有这种强大功能的IDA Pro分析工具与Hex-Rays Decompiler插件都是付费的商业软件,且

价格昂贵,一般人难以承受,大部分都由公司购买使用。使用OllyDbg调试器打开上面的程序文

件,可以看到反汇编后的代码,如图49-5所示。

从图49-5中可以看到,反汇编代码看上去比较复杂,不如反编译后的代码更容易阅读,由此

可见反编译器多么有用。

提示 请注意,反编译器也不是万能的。若使用保护器等工具故意打乱程序代码,或在

程序运行中使用一些操作技术,就不能反编译,或者使反编译后的代码更加复杂。所

以,学习高级逆向分析技术时一定要掌握反汇编代码和指令。

506 第 49 章 IA-32 指令

图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 IA-32 指令格式 507

1

2

3

4

5

19

6

7

8

9

10

11

12

13

14

15

16

17

18

图49-6 IA-32指令格式

49.3.1 指令前缀

指令前缀(Instruction Prefixes)是一个可选项目,后面出现特定操作码时将补充说明其含义。

下面举个简单的例子。

66:81FE 4746 CMP SI,4647 66:C703 33D2 MOV WORD PTR DS:[EBX],0D233 F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI] 以上指令最前面的黑体部分为前缀

前缀项大小为1个字节(后面讲解“指令解析方法”(借助操作码映射解析)时会详细说明

Prefix 66的含义)。

49.3.2 操作码

Opcode(Operation Code,操作码)是必不可少的部分,用来表示实际的指令。

66:81FE 4746 CMP SI,4647 66:C703 33D2 MOV WORD PTR DS:[EBX],0D233 F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI] 8BF1 MOV ESI,ECX E8 04400000 CALL 00405040 56 PUSH ESI E9 4A020000 JMP 00401366 33C0 XOR EAX,EAX 0F95C1 SETNE CL C3 RETN CC INT3 以上指令黑体部分为操作码

操作码长度为1~3个字节,我们在常见的应用程序调试中遇到的操作码大部分都是1个字节,

有时也会遇到2个字节的操作码。3个字节长度的操作码主要用在MMX(MultiMedia eXtension,

多媒体扩展)相关指令中,一般很少有机会接触到。操作码通常都带有操作数(Operand),操作

数种类有寄存器、内存地址、常量(Contanst)。指令中出现的ModR/M与SIB选项辅助操作码确

定操作数。

操作码种类很多,解析时一般需要查看Intel用户手册的操作码映射。后面讲解“指令解析方

法”时会做一些解析操作码的练习。

49.3.3 ModR/M

ModR/M是个可选项,主要用来辅助说明操作码的操作数(操作数的个数、种类[寄存器、地

址、常量])。

508 第 49 章 IA-32 指令

66:81FE 4746 CMP SI,4647 66:C703 33D2 MOV WORD PTR DS:[EBX],0D233 8BF1 MOV ESI,ECX 33C0 XOR EAX,EAX 0F95C1 SETNE CL 以上指令黑粗体部分为ModR/M

ModR/M项拥有1个字节(8位)长度,分为3部分,各部分含义如图49-7所示。

图49-7 ModR/M

简单的ModR/M计算练习 Ex1. ModR/M = FE FE的2进制表示 = 11111110 ModR/M拆分 = 11|111|110(Mod:11, Reg:111, R/M:110) Ex2. ModR/M = 03 03的2进制表示 = 00000011 ModR/M拆分= 00|000|011(Mod:00, Reg:000, R/M:011)

49.3.4 SIB SIB(Scale-Index-Base)也是一个可选项,用来辅助说明ModR/M。操作码的操作数为内存

地址时,需要与ModR/M项一起使用。

898424 50020000 MOV [ESP+250], EAX C68424 5C010000 00 MOV [ESP+15C], 00 8D8428 B1354000 LEA EAX, [EAX+EBP+4035B1] 8B0C01 MOV ECX, [EAX+ECX] 以上指令黑体部分为SIB

SIB项也拥有1个字节(8位)长度,分为3部分,各部分含义如图49-8所示。

图49-8 SIB

*简单的SIB计算练习 Ex1. SIB = 24 24的2进制表示 = 00100100 SIB拆分 = 00|100|100(Scale:00, Index:100, Base:100) Ex2. SIB = 01 01的2进制表示 = 00000001 SIB拆分 = 00|000|001(Scale:00, Index:000, Base:001)

49.3.5 位移

位移(Displacement)也是可选项,操作码的操作数为内存地址时,用来表示位移操作。

81B8 00004000 50450000 CMP DWORD PTR DS:[EAX+400000],4550 C705 68CF4000 01000100 MOV DWORD PTR DS:[40CF68],10001 833D 60CF4000 00 CMP DWORD PTR DS:[40CF60],0 814E 0C 00800000 OR DWORD PTR DS:[ESI+C],8000 以上指令黑粗体部分为位移

49.4 指令解析手册 509

1

2

3

4

5

19

6

7

8

9

10

11

12

13

14

15

16

17

18

位移的长度为1、2、4字节。

49.3.6 立即数

立即数(Immediate)也是一个可选项,操作码的操作数为常量时,该常量就被称为立即数。

81B8 00004000 50450000 CMP DWORD PTR DS:[EAX+400000],4550 C705 68CF4000 01000100 MOV DWORD PTR DS:[40CF68],10001 833D 60CF4000 00 CMP DWORD PTR DS:[40CF60],0 814E 0C 00800000 OR DWORD PTR DS:[ESI+C],8000 以上指令黑粗体部分为立即数

立即数的长度为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用户手册后,其中有些表格是解析指令时需要参考的,请将下面列出的表格打印

出来。

Vol. 2A Chapter 2 Instruction Format Table 2-2. 32-Bit Addressing Forms with the ModR/M Byte Table 2-3. 32-Bit Addressing Forms with the SIB Byte Vol. 2B Appendix A Opcode map A.2 Key to Abbreviations A.2.1 Codes for Addressing Method A.2.2 Codes for Operand Type Table A-1. Superscripts Utilized in Opcode Tables A.3 One, Two, Three-byte Opcode Maps Table A-2. One-byte Opcode Map Table A-3. Two-byte Opcode Map A.4 Opcode Extensions for One-byte and Two-byte Opcodes Table A-6. Opcode Extentions for One- and Two Opcodes by Group Number

仅打印粗斜体部分即可

打印上表后,下面通过练习来学习使用操作码映射、ModR/M表、SIB表等表格解析指令。

510 第 49 章 IA-32 指令

提示 上面打印的资料用得多了、泛黄的时候,各位也就成了解析 IA-32 指令的高手。

我几年间一直使用一部自制的指令手册,有人问有关指令解析的问题时,我就会翻阅

它并给出解答。翻阅得多了,就有了感觉,哪些内容在哪页一清二楚,所以查起来非

常快。我尊敬的一位前辈也有这样的参考资料,当时看上去都用旧了。从某种意义上

说,这种使用痕迹就像一把测量逆向分析人员“年轮”的尺子。希望各位也打印一份

这样的资料,需要的时候随时翻阅查看(参考图 49-9)。

图49-9 OpCode用户手册

49.5 指令解析练习

我们从本节开始学习IA-32指令解析方法。

49.5.1 操作码映射

首先解析1条长度为1个字节的操作码指令。

41 INC ECX

查看前面打印出的操作码手册(或者Intel Manual Vol.2B)中的“Table A-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指

令格式表示如下:

[Prefixes][Opcode][ModR/M][SIB][Displacement][Immediate]

提示 图 49-10 中 INC 指令的上标为 i64,REX 指令的上标为 o64,它们的含义在操作码

用户手册的“Table A-1. Superscripts Utilized in Opcode Tables”中给出了讲解。根据表

格中的说明,i64 表示不在 64 位模式中使用,o64 表示只在 64 位模式中使用。所以操

作码 40~47 在 32 位模式中作为 INC 指令使用,在 64 位模式(o64)中用作 REX 前缀。

由于这里解析的是 IA-32 指令,所以应该解析为 INC 指令。操作数亦是如此,在 32 位

模式中要选择 ECX,在 64 位模式中要选择 REX.B。

49.5 指令解析练习 511

1

2

3

4

5

19

6

7

8

9

10

11

12

13

14

15

16

17

18

图49-10 Opcode 41 in Table A-2

49.5.2 操作数

下面继续通过练习来学习操作数的形态结构。

68 A0B44000 PUSH 0040B4A0

使用前面的方法在Table A-2操作码映射中查找指令的第一个字节68,如图所示。

从图49-11中可以看到,操作码68对应于PUSH Iz,为带有1个操作数的PUSH指令。

提示 Table A-2 One-byte Opcode Map 内容繁多,在 Intel 用户手册中占了不少分量,图

49-11 是其中一部分。

图49-11 Opcode 68 in Table A-2

512 第 49 章 IA-32 指令

Iz用来表示操作数的类型,把握其含义即可准确解析整条指令。大写字母I指寻址方法

(Addressing Method),小写字母z指操作数类型(Operand Type),A.2.1 Codes for Addressing Method

与A.2.2 Codes for Operand Type中分别对它们进行了说明。

常用寻址方法整理如表49-2所示。

表49-2 部分Vol.2B A.2.1. Codes for Addressing Method(出处:Intel官方用户手册)

E A ModR/M byte follows the opcode and specifies the operand. The operand is either a general-purpose register or a memory address.

G The reg field of the ModR/M byte selects a general register (for example, AX (000)).

I Immediate data: the operand value is encoded in subsequent bytes of the instruction.

J The instruction contains a relative offset to be added to the instruction pointer register (for example, JMP (0E9), LOOP).

M The ModR/M byte may refer only to memory (for example, BOUND, LES, LDS, LSS, LFS, LGS, CMPXCHG8B).

X Memory addressed by the DS:rSI register pair (for example, MOVS, CMPS, OUTS, or LODS).

Y Memory addressed by the ES:rDI register pair (for example, MOVS, CMPS, INS, STOS, or SCAS).

指示寻址方法的大写字母I代表Immediate(立即数)。若想知道立即数的大小,还要参考操作

数类型。

常用操作数类型整理如表49-3所示。

表49-3 部分Vol.2B A.2.2. Codes for Operand Type(出处:Intel官方用户手册)

b Byte, regardless of operand-size attribute.

d Doubleword, regardless of operand-size attribute.

v Word, doubleword or quadword (in 64-bit mode), depending on operand-size attribute.

z Word for 16-bit operand-size or doubleword for 32 or 64-bit operand-size.

指示操作数类型的小写字母z在32位模式下表示的大小为DWORD(32位,4个字节)。综合

以上信息,操作码68对应的PUSH Iz指令中,Iz(操作数格式)表示大小为4个字节(32位)的立

即数,所以继续读取68之后的4个字节(0040B440),整条指令最终解析为PUSH 0040B4A0,用

IA-32指令格式表示如下:

[Prefixes][Opcode][ModR/M][SIB][Displacement][Immediate]

以上只是解析指令的“热身”练习,下面正式学习指令解析方法。

49.5.3 ModR/M

首先学习包含ModR/M的指令解析方法。

89C1 MOV ECX,EAX

先在操作码映射中查找Opcode 89,如图49-12所示。

从操作码映射中可以看到,Opcode 89对应MOV Ev,Gv指令,带有2个操作数,第一个操作数

的格式为Ev,第二个操作数格式为Gv。

49.5 指令解析练习 513

1

2

3

4

5

19

6

7

8

9

10

11

12

13

14

15

16

17

18

图49-12 Opcode 89 in Table A-2

由表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个字节C1即为ModR/M选项。ModR/M表

格是指操作码手册中的Table 2-2. 32-Bit Addressing Forms with the ModR/M Byte,如图49-13所示。

图49-13 Table 2-2. 32-bit ModR/M Byte

514 第 49 章 IA-32 指令

图49-13中的ModR/M表看上去比操作码映射复杂得多,但熟悉原理后就能很容易地掌握其使

用方法。ModR/M长度为1个字节,在图49-13ModR/M表中的[ModR/M]区域内,从00开始到FF结

束。前面讲解ModR/M时提到过,它按比特位分为3个部分,分别为Mod、Reg、R/M(参考ModR/M

说明)。下面以ModR/M C1为例讲解。

Ex) ModR/M = C1 C1的二进制形式为 = 11000001 拆分ModR/M = 11|000|001 (Mod:11, Reg:000, R/M:001)

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 ECX,EAX。

提示 大写字母 E 与 G 表示寻址方法,在图 49-13 中分别出现在[E]区域与[G]区域。小写

字母 v 表示操作数类型,32 位计算中操作数的大小也为 32 位。根据这些信息在图 49-13

中查找 ModR/M 值 C1,可知 Ev、Gv 分别为 ECX、EAX。

用IA-32指令格式表示如下:

[Prefixes][Opcode][ModR/M][SIB][Displacement][Immediate]

49.5.4 Group Group指令语句将操作码与ModR/M组合起来,使操作码最多可以表示出8种形式的指令

(Mnemonic)。灵活使用Group指令虽然会使解析变得有些复杂,却能较好地扩展操作码映射。

83C3 12 ADD EBX,12

首先在操作码映射中查找83对应的指令形式。

从图49-14可知,Opcode 83对应的指令为Grp 1 Ev,Ib,带有2个操作数,形式分别为Ev、Ib。

由前面的讲解可知,Ev代表4个字节的寄存器(或者内存地址),Ib代表1个字节的立即数(根据

表49-3可知,操作数类型的b代表字节大小)。

49.5 指令解析练习 515

1

2

3

4

5

19

6

7

8

9

10

11

12

13

14

15

16

17

18

图49-14 Opcode 83 in Table A-2

提示 到现在为止,解析上述语句时仍未出现指令(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解析如下:

83C312 - Grp1 Ev, Ib

到现在还是无法确切知道指令(Mnemonic)与操作数的内容。分析ModR/M“C3”后即可

准确解析上述指令(解析与顺序无关,但从左到右解析起来会更简便)。

Ex) ModR/M = C3 C3的二进制形式为 = 11000011 拆分ModR/M = 11|000|011 (Mod:11, Reg:000, R/M:011)

首先参考Group指令表查看对应指令(Mnemonic)。Group指令表是指操作码用户手册中的

Table A-6. Opcode Extentions for One- and Two Opcodes by Group Number,如图49-15所示。

图49-15 Opcode 83 in Table A-6

图49-15中操作码为83,所以只看Group 1项目即可。并且,由于ModR/M C3的Reg值为000(二

进制),所以对应的指令(Mnemonic)为ADD。综合以上分析,指令83C312解析如下:

83C312 - ADD Ev, Ib

接下来,先确定第一个操作数。在ModR/M表中查找C3值。

由图49-16可知,ModR/M“C3”的Ev对应的值为EBX。

516 第 49 章 IA-32 指令

图49-16 ModR/M“C3”in Table 2-2

提示 从图 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, Ib

第二个操作数的符号为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所示。

49.5 指令解析练习 517

1

2

3

4

5

19

6

7

8

9

10

11

12

13

14

15

16

17

18

图49-17 Prefix 66 in Table A-2

图49-18 Opcode 81 in Table A-2

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 - Grp1 Ev, Iz (Operand Size = 16 bit)

要想得到准确指令与操作数,还要分析ModR/M值FE代表的含义。

Ex) ModR/M = FE FE的二进制表示 = 11111110 拆分ModR/M = 11|111|110 (Mod:11, Reg:111, R/M:110)

接着在Table A-6中查看Group指令, 确定其代表的具体指令,如图49-19所示。

图49-19 Opcode 81 in Table A-6

由图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所示。

518 第 49 章 IA-32 指令

图49-20 ModR/M FE in Table 2-2

从图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 FA1F0000 JNZ XXXXXXXX

先在单字节操作码映射中查找指令的第一个字节(0F),如图49-21所示。

图49-21 Opcode 0F in Table A-2

由上表可知,“0F”为双字节操作码的Escape符号,指示继续在Table A-3中查找双字节操作

码。双字节操作码映射(即Table A-3)在Intel用户手册中的分量是单字节的两倍,如图49-22所示。

图49-22 Opcode 0F85 in Table A-3

49.5 指令解析练习 519

1

2

3

4

5

19

6

7

8

9

10

11

12

13

14

15

16

17

18

从图49-22的表格标题可以看到,第一个字节为“0F”。查找第二个字节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),整条指令解析如下:

0F85 FA1F0000 - JNE XXXXXXXX

用IA-32指令格式表示如下:

[Prefixes][Opcode][ModR/M][SIB][Displacement][Immediate]

提示 以上指令中的移位值 00001FFA 为相对位移,加上当前的 EIP 才能准确算出跳转地

址。比如,上述指令的地址为 401000,执行指令后,EIP 值为 401006(增加 6 个字节

(指令长度)),那么实际跳转的地址为 403000(JNE 403000),它是 EIP 值(401006)

与移位值(1FFA)相加的结果。调试中会经常遇到“相对位移”这一术语,希望各位

理解其含义。

49.5.7 移位值&立即数

若指令中同时存在移位值&立即数,该如何解析呢?下面学习这种指令的解析方法。

C705 00CF4000 01000100 MOV DWORD PTR DS:[40CF00], 10001

首先在操作码映射中查找“C7”,如图49-33所示。

图49-23 Opcode C7 in Table A-2

Opcode C7对应Group 11 MOV指令,带有2个操作数(Ev,Iz)。所以上述指令解析如下:

C705 00CF4000 01000100 - Grp11 Ev, Iz

出现Group指令或操作数形式中有E、G时,操作码之后必跟着ModR/M选项,上述指令中

ModR/M的值为05。

Ex. ModR/M = 05

520 第 49 章 IA-32 指令

05的二进制形式 = 00000101 拆分ModR/M = 00|000|101 (Mod:00, Reg:000, R/M:101)

接着在Group指令表(Table A-6)中查找其对应的实际指令(Mnemonic),如图49-24所示。

图49-24 Opcode C7 in Table A-6

Group 11中ModR/M的Reg值(000)对应的指令为MOV,且在Group 11中仅有一个MOV指令

(故图49-23中出现了“Group11 - MOV”的标识)。

C705 00CF4000 01000100 - MOV Ev, Iz

接下来确定第一个操作数(Ev),在ModR/M表(Table 2-2)中查找“05”,如图49-25所示。

图49-25 ModR/M 05 in Table 2-2

第一个操作数(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 00CF4000 01000100 - MOV [0040CF00], 10001

上述指令表示向40CF00地址中放入10001值,使用IA-32指令格式表示如下:

[Prefixes][Opcode][ModR/M][SIB][Displacement][Immediate]

49.5.8 SIB 操作数指向内存地址时,SIB(Scale、Index、Base)用来辅助寻址。指令中含有SIB时,其

49.5 指令解析练习 521

1

2

3

4

5

19

6

7

8

9

10

11

12

13

14

15

16

17

18

本身会变得较为复杂。换言之,如果掌握了含有SIB的指令解析方法,你就达到了大师级别。

8B0C01 MOV ECX, [EAX+ECX]

首先在操作码映射中查找“8B”,如图49-26所示。

图49-26 Opcode 8B in Table A-2

由图49-26可知,Opcode 8B对应的指令为MOV Gv,Ev,带有2个操作数,第一个操作数为Gv

形式,是一个4字节的寄存器;第二个操作数为Ev形式,是一个4字节的寄存器或内存地址。操作

数的寻址方法为G、E时,操作码后会跟着ModR/M选项。解析出该ModR/M即可准确解析操作数。

上述指令中ModR/M为0C,在ModR/M表中查找,如图49-27所示。

第一个操作数Gv对应的值为ECX,第二个操作数Ev对应的值为[--][--]。符号[--][--]表示需要

SIB字节来辅助表示准确地址。综合以上分析,上述指令解析如下:

8B0C01 - MOV ECX, [--][--]

第二个操作数的符号[--][--]指向内存地址,要准确解析它需要用到SIB字节,用更直观的方

式表示如下:

[--][--] = [(Reg. A) + (Reg. B)] * 寄存器A、B二者可缺其一!

重写上述指令如下:

8B0C01 - MOV ECX, [(Reg. A) + (Reg. B)]

图49-27 ModR/M 0C in Table 2-2

这种表示方法更加直观。接下来在SIB表中查找Reg.A与Reg.B,如图49-28所示。

前面的指令中,SIB的值为01,SIB表就是操作码用户手册中的Table 2-3。

522 第 49 章 IA-32 指令

图49-28 Table 2-3. 32-bit SIB Byte

查看SIB表的方法与查看ModR/M表的方法类似,先找到SIB值,然后在表顶部的[Reg.A]区域

获取Reg.A寄存器,然后在表左侧的[Reg.B]区域获取Reg.B寄存器。图49-28中与SIB 01对应的

Reg.A为ECX,Reg.B为EAX。所以指令最终解析如下:

8B0C01 - MOV ECX, [ECX+EAX]

用IA-32指令格式表示如下:

[Prefixes][Opcode][ModR/M][SIB][Displacement][Immediate]

操作数为(复杂形式的)内存地址时,SIB就像这样用来辅助寻址。接下来解析一条带有更

复杂的SIB选项的指令。

8D8428 B1354000 LEA EAX, [EAX+EBP+4035B1]

如图49-29所示,Opcode 8D对应的指令为LEA Gv,M。

图49-29 Opcode 8D in Table A-2

LEA指令是“Load Effective Address”的缩写,是“取有效地址指令”。第一个操作数为Gv

形式,是4个字节大小的寄存器;第二个操作数为M形式,仅表示内存地址(参考表49-2)。

8D8428 B1354000 - LEA Gv, M

49.5 指令解析练习 523

1

2

3

4

5

19

6

7

8

9

10

11

12

13

14

15

16

17

18

由于操作数的寻址方法为G、M,所以操作码后面跟着ModR/M字节,分析后面的ModR/M即

可得到准确的操作数值。上述指令中ModR/M值为84,在Table 2-2中查找它,如图49-30所示。

第一个操作数Gv形式为EAX,第二个操作数M形式为[--][--]+disp32。

提示 各位现在已经非常熟悉 ModR/M 表格了吧?在表格上端求 G 形式的值,在表格左

侧求 E 或 M 形式的值。

综合以上分析,上述指令解析如下:

8D8428 B1354000 - LEA EAX, [--][--]+disp32

第二个操作数中的[--][--]+disp32指的是内存地址,要想准确解析,需要使用后面的SIB字节

与32位的移位值。使用更直观的方式表示[--][--]+disp32如下:

[--][--]+disp32 = [(Reg. A) + (Reg. B) + disp32] * 寄存器A、B两者可缺其一!

图49-30 ModR/M 84 in Table 2-2

用更直观的方式解析上述指令。

8D8428 B1354000 - LEA EAX, [(Reg. A)+(Reg. B) + disp32]

接下来查看SIB表以获取Reg.A与Reg.B对应的值。因SIB为28,故Reg.A为EAX,Reg.B为EBP

(参考图49-31)。

图49-31 SIB 28 in Table 2-3

上述指令中disp32的值为SIB后的004035B1。综合以上所有分析,整条指令最终解析如下:

8D8428 B1354000 - LEA EAX, [EAX+EBP+4035B1]

524 第 49 章 IA-32 指令

用IA-32指令格式表示如下:

[Prefixes][Opcode][ModR/M][SIB][Displacement][Immediate]

49.6 指令解析课外练习 若想完全掌握指令解析方法,需要大量练习。反复看前面的练习示例,熟悉解析方法后,在

OllyDbg调试器中打开并运行notepad.exe程序,如图49-32所示。

图49-32 通过OllyDbg做指令解析练习

参考前面打印出的操作码用户手册,将区域[1]中的指令逐条解析为反汇编代码(请先隐藏

OllyDbg调试器中的反汇编窗口)。指令解析的成功率达到99%以上后,再看区域[2]中的机器代码,

将它们解析为反汇编代码。通过这些训练,各位会迅速成长为IA-32指令解析高手。

49.7 小结

本章主要讲解IA-32指令的解析方法,刚接触它的读者朋友可能会觉得有些难度。但若完全

掌握了指令解析方法,逆向技术水平就会达到中级以上。

我主要使用IA-32指令来编写查杀恶意代码的函数。检测变形病毒(Polymorphic Virus)时,

必须探测出Polymorphic引擎中产生的指令类型,这时就需要分析人员具有丰富的指令知识。此外,

了解指令结构也有助于提高代码调试水平。掌握IA-32指令相关知识对于编写“打补丁”代码、

分析漏洞Shell代码都非常有帮助。当然,掌握IA-32指令解析方法对于提高各位的逆向分析技术

水平也有相当大的帮助。

提示 以上内容仅涉及 Intel 用户手册中的极少部分。若想深入学习有关 IA-32 指令的知

识,请各位认真研读 Intel® 64 and IA-32 Architectures Software Developer’ s Manuals(与

指令解析相关的部分为 Vol. 2A-2.1、Vol.2B-Appendix A)。