使用mona.py分析堆对象的完整指南

本文详细介绍了如何使用mona.py工具在WinDBG环境中分析堆对象,包括dumpobj和dumplog两个新功能的用法。通过实际示例展示了如何识别堆对象中的有用信息,如指针、字符串和嵌套对象,帮助安全研究人员更高效地进行漏洞分析和利用开发。

引言

大家好, 在为Derbycon的高级漏洞利用开发课程做准备时,我一直在研究IE中的堆分配原语。在研究过程中,快速识别可能有用的对象是一个令人沮丧(或至少会拖慢研究进度)的问题。毕竟,我需要找到包含任意数据或指向任意数据指针的对象,但由于噪音干扰,这并不容易。

我决定为mona.py添加一些新功能,以便更快地找到有趣的对象。这些新功能仅在WinDBG下可用。

要获取最新版本的mona,只需运行!py mona up。由于我还升级到了最新版本的pykd,在运行最新版mona之前,您可能还需要更新pykd.pyd。(当您尝试使用过时版本的pykd运行mona时,应在WinDBG日志窗口中看到更新说明)

dumpobj (do)

第一个新功能是"dumpobj"。这个mona.py命令将转储对象的内容并提供(希望是)有用的内容信息。该命令接受以下参数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Usage of command 'dumpobj' :
-----------------------------
Dump the contents of an object.

Arguments:
    -a       : Address of object
    -s        : Size of object (default value: 0x28 or size of chunk)
Optional arguments:
    -l        : Recursively dump objects
    -m        : Size for recursive objects (default value: 0x28)

如上所示,我们需要至少提供2个参数:

  • -a:起始位置(对象地址,但您可以指定任何位置)
  • -s:对象大小。如果不指定-s参数,mona将尝试确定对象大小。如果无法确定,mona将转储对象的0x28字节。

此外,您还可以告诉mona同时转储链接对象。参数-l接受一个数字,表示递归转储的层级数。为了限制输出大小(和性能原因),仅打印链接对象的前0x28字节(除非使用参数-m覆盖此行为)。

当然,在WinDBG中转储对象内容相当简单。ddsdc命令将打印出对象并显示其内容的一些信息。但在某些情况下,dds/dc的输出不够充分,需要额外的工作来进一步分析对象及其内部链接的可选对象。

让我们看一个例子。假设我们在0x023a1bc0处有一个0x78字节的对象。当然,我们可以使用原生WinDBG命令转储对象内容:

1
2
3
4
5
0:001> dds 0x023a1bc0 L 0x78/4
023a1bc0  023a1d30
023a1bc4  023a1818
023a1bc8  00000000
...

使用mona,我们可以转储相同的对象,mona将尝试收集有关对象中每个dword的更多信息:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
0:001> !py mona do -a 0x023a1bc0
Hold on...
[+] No size specified, checking if address is part of known heap chunk
    Address found in chunk 0x023a1bb8, heap 0x00240000, (user)size 0x78

----------------------------------------------------
[+] Dumping object at 0x023a1bc0, 0x78 bytes

>> Object at 0x023a1bc0 (0x78 bytes):
Offset  Address      Contents    Info
------  -------      --------    -----
+00     0x023a1bc0 | 0x023a1d30  (Heap) ptr to ASCII '0::'
+04     0x023a1bc4 | 0x023a1818  (Heap) ptr to ASCII ':'
+08     0x023a1bc8 | 0x00000000  
...

显然,对象中的一些值指向字符串(ASCII和Unicode),另一个指针似乎链接到另一个对象(ADVAPI32!g_CodeLevelObjTable+0x4)。这比dds或dc更有用。但我们可以让它更好。我们可以告诉mona自动打印出链接对象,最多可达任意深度。

让我们重复mona命令,这次要求转储链接对象,最多一层深度:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
0:001> !py mona do -a 0x023a1bc0 -l 1
Hold on...
[+] No size specified, checking if address is part of known heap chunk
    Address found in chunk 0x023a1bb8, heap 0x00240000, (user)size 0x78

----------------------------------------------------
[+] Dumping object at 0x023a1bc0, 0x78 bytes
[+] Also dumping up to 1 levels deep, max size of nested objects: 0x28 bytes

>> Object at 0x023a1bc0 (0x78 bytes):
Offset  Address      Contents    Info
------  -------      --------    -----
+00     0x023a1bc0 | 0x023a1d30  (Heap) ptr to ASCII '0::'
...

如上所示,mona确定源对象包含对2个链接对象的引用,并决定也转储这些链接对象。重要的是要知道mona不会将字符串(ASCII或Unicode)视为对象,因为mona已经显示字符串,即使它们是在对象内部引用的。dumpobj命令的输出被写入名为"dumpobj.txt"的文本文件。

dumplog (dl)

很明显,dumpobj命令将使可视化对象内部的重要信息变得更加容易。如果您已经知道起始对象,这当然很有帮助。但是,如果您一直在记录应用程序中的所有堆分配和释放操作并将输出存储在日志文件中呢?即使几行javascript代码从堆的角度来看也可能相当嘈杂,使得识别有趣的对象变得不那么简单。

为了让我们的生活更轻松,我决定实现"dumplog",它将解析日志文件(基于特定语法)并对每个已分配但未释放的对象执行"dumpobj"。在当前版本中,dumplog不会转储链接对象,但我计划很快添加此功能。(可能明天)

Dumplog需要适当的设置。我们需要告诉WinDBG创建一个遵循特定约定的日志文件,并且显然必须在同一调试会话中运行mona dumplog(以确保记录的分配和释放操作仍然相关)。

“!py mona help dumplog"的输出显示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Usage of command 'dl' :
------------------------
Dump all objects recorded in an alloc/free log
Note: dumplog will only dump objects that have not been freed in the same logfile.
Expected syntax for log entries:
    Alloc : 'alloc(size in hex) = address'
    Free  : 'free(address)'
Additional text after the alloc & free info is fine.
Just make sure the syntax matches exactly with the examples above.
Arguments:
    -f <path/to/logfile> : Full path to the logfile

想法是将所有堆分配和释放操作记录到日志文件中。在WinDBG中,可以通过以下步骤实现:

在运行进程并触发要捕获和分析的分配/释放操作之前,告诉WinDBG将日志窗口的输出写入文本文件:

1
2
.logclose
.logopen c:\\allocs.txt

接下来设置2个日志记录断点:

1
2
bp !ntdll + 0002e12c ".printf \"alloc(0x%x) = 0x%p\", poi(esp+c), eax; .echo; g"
bp ntdll!RtlFreeHeap "j (poi(esp+c)!=0) '.printf \"free(0x%p)\", poi(esp+c); .echo; g'; 'g';"

(基于最新版本的kernel32.dll,Windows 7 SP1)。

这两个断点将在每次调用RtlAllocateHeap和RtlFreeHeap时向WinDBG日志窗口打印一条消息,打印出有关API调用的有价值信息。坚持这种格式很重要,但您可以自由地在消息字符串的末尾添加更多文本。在这两个断点处于活动状态,并且WinDBG配置为开始将日志窗口的输出写入文本文件后,我们可以运行应用程序。

当您准备好进行分析时,中断WinDBG。此时不要关闭它,但使用.logclose命令关闭日志文件。

我们现在可以使用mona解析日志文件,找到已分配但未释放的对象,并对每个对象执行mona dumpobj。

1
!py mona dl -f c:\allocs.txt

输出将写入dump_alloc_free.txt。

我希望您会喜欢这两个新功能。 保持安全并保重 干杯 -corelanc0d3r

更新(8月17日)- “dumplog"功能现在支持转储链接对象(选项-l)。链接对象将具有对父对象的引用。(这也适用于dumpobj)。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计