Exploit开发系列教程-Heap

author:P3nro5e

0x00 Heap

当进程开始运行时,堆管理器会创建一个新的堆,其被称为进程的默认堆。c/c++应用程序也会创建所谓的CRT堆 (在进行与new/delete、 malloc/free有关的操作及涉及他们的变量时会使用到)。通过HeapCreate API函数也可以创建其它的堆。堆管理器分为:前端分配器(Front End Allocator)和后端分配器 (Back End Allocator)。

0x01 前端分配器

前端分配器(Front End Allocator)是后端分配器(Back End Allocator)的一种抽象优化层。不同的使用案例会涉及到不同类型的前端分配器。前端分配器为:

  1. 旁视列表(Look aside list,LF)前端分配器
  2. 低碎片堆(Low fragmentation ,LF)前端分配器

LAL是一个由128个链表组成的表.每个列表含有特定大小的空闲块,其始于16字节。每个块的大小包含元数据的8个字节,它被使用于管理块。在决定所给出大小的列表中,求其索引值的公式为index = ceil((size + 8)/8) – 1,”+8”是用于元数据的计数。索引值总为正。

从Windows Vista开始,就没再使用LAL前端分配器,取而代之的是使用LF 前端分配器。 LF前端分配器非常复杂,主要是试图通过分配最小的内存块(具有足够大小来容纳被请求大小的数据)来减少堆碎片。

0x02 后端分配器

如果前端分配器不能满足分配的请求,则请求会被发送到后端分配器。

在Windows XP中,后端分配器使用一个与前端分配器相似的表。

索引0的链表含有空闲块,其大小大于1016字节并且小于或等于虚拟分配的限制大小(0x7FFF0字节)。

块在该列表中按大小升序排列。索引1闲置,通常索引x包含大小为8x的空闲块。当需要一个已给出大小但是无法得到的块时,后端分配器会试图将更大的块分割为所需大小的块。相对的处理操作,也被称作堆合并( heap coalescing):当某个块被释放时,堆管理器可能会检查它以及跟它邻近的块。并且如果其中一个块或两个块是空闲状态时,空闲块将可能被合并为单一的块。这样做将会减少堆碎片。堆管理器分配的堆块大小大于0x7FFF0字节时,堆管理器会将分配请求发送到虚拟内存管理器并让已分配的块持续存在于一个被称作虚拟分配列表(virtual allocation list)的表上。

在windows 7中,不再有特定大小的空闲列表。Windows 7使用一个单一的空闲列表,它含有所有按大小升序排列的块和另一节点(属于typeListHint)的列表(指向空闲列表中的节点)并且被使用来找到恰当大小的节点来满足分配请求。

0x03 堆(内存)段

堆管理器从Windows 虚拟内存管理器中请求其使用的所有内存。堆管理器请求大的堆块的虚拟内存,其被称作堆(内存)段。堆管理器使用那些段来分配所有的块和内部的bookkeeping结构。当新的段被创建时,只保留少部分被提交的内存。当需要更多的内存时,另一部分的内存会被提交。最后,在当前段中没有足够的未被提交的空间时,会创建新的段,其大小为前一个段的两倍。如果没有足够大的内存,那么将创建更小的段。如果没有用于创建最小段的空间,那么将会返回错误。

0x04 分析堆

偏移0x90的PEB中包含堆:

有意思的部分如下:

ProcessHeaps是存储指向HEAP 结构指针的数组。 我们来观察该数组:

我们可以展示出第一个堆的结构信息如下:

通过使用mona.py来获取关于堆的信息:

我们可以看到7个堆和使用mona脚本展示出的每个堆段。

我们也可以使用!heap来观察堆段:

“-m”选项也被用于展示段信息。

观察特定堆(0x5a0000)段,我们可以使用:

以下是我们要了解到的特定信息以及通过使用mona来展示的所有堆块的概要。我们也可以省略掉“-h 5a0000”选项的使用来得到所有堆段:

mona.py调用内存堆块中的已分配块。在段中观察堆块:

你也可以使用 !heap来观察:

添加”stat”选项来展示一些统计数据

使用mona.py可以观察到段的块/堆块中的字符串, BSTRINGs 和 vtable对象。使用“-t layout”选项来了解布局情况。该功能将数据写入到文件heaplayout.txt中。

你可以使用如下额外的选项:

• -v: write the data also in the log window • -fast: skip the discovery of object sizes • -size: skip strings that are smaller than • -after: ignore entries inside a chunk until either

可以发现一个字符串或vtable引用含有的值为;然后,输出当前堆块的相关信息。

范例:

分析以上输出内容中提取的两行信息:

其中的第二行内容告诉我们:

入口在起始的堆块地址+3a3字节的位置。

入口地址范围:005a0bab到005a0d73;

入口是454 字节或227个字符的Unicode字符串;

字符串为Path=C:\Program Files (x86)\Windows Kits\...

评论

RickGray2015-07-22 20:26:06

旁视列表(Look aside list,LF)前端分配器