从 C# 崩溃异常 中研究页堆布局( 二 )

稍微解读下上面的输出 。

  1. + 56e0000**
表示 页堆 的堆句柄 。
  1. NormalHeap - 5aa0000
表示 页堆 关联的 NT堆,可能有朋友要问了 , 既然都开启页堆了, 还要弄一个 ntheap 干嘛? 大家不要忘了,windows 的一些系统api会用到这个堆 。
接下来有一个问题,如何观察这两个 heap 之间的关联关系呢? 要回答这个问题,需要了解 页堆 的布局结构,画个简图如下:
从 C# 崩溃异常 中研究页堆布局

文章插图
从图中可以看到,离句柄偏移 4k 的位置有一个 DPH_HEAP_ROOT 结构,它相当于 NTHEAP 的_HEAP , 我们拿 56e0000 举个例子 。
0:000> dt nt!_DPH_HEAP_ROOT 56e0000+0x1000ntdll!_DPH_HEAP_ROOT   ...   +0x0b4 NormalHeap       : 0x05aa0000 Void   +0x0b8 CreateStackTrace : 0x042f4d94 _RTL_TRACE_BLOCK   +0x0bc FirstThread      : (null)上面输出的 NormalHeap: 0x05aa0000 就是它关联的 ntheap 句柄 。
3. 堆块布局研究对页堆 有了一个整体认识,接下来继续研究堆块句柄,我们发现 ptr=0x56e5ff0 是落在 56e0000 这个页堆上,接下来我们导出这个页堆的详细信息 。
0:000> !heap -p -h 56e0000    _DPH_HEAP_ROOT @ 56e1000    Freed and decommitted blocks      DPH_HEAP_BLOCK : VirtAddr VirtSize    Busy allocations      DPH_HEAP_BLOCK : UserAddr  UserSize - VirtAddr VirtSize        056e1f70 : 056e5ff0 00000009 - 056e5000 00002000          unknown!fillpattern    _HEAP @ 5aa0000      No FrontEnd      _HEAP_SEGMENT @ 5aa0000       CommittedRange @ 5aa04a8      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state        05aa04a8 0167 0000  [00]   05aa04b0    00b30 - (free)      * 05aa0fe0 0004 0167  [00]   05aa0fe8    00018 - (busy)       VirtualAllocdBlocks @ 5aa009c上面的信息如何解读呢?我们逐一来聊一下吧 。
  1. _DPH_HEAP_ROOT @ 56e1000
这个已经和大家聊过了 , 它和 _HEAP 结构是一致的 。
  1. DPH_HEAP_BLOCK :
从字面意思就能看出来和 ntheap 的 heap_entry 是一致的 , 都是用来描述堆块信息,不过有一点要注意 , 这个堆块是落在上图中的 DPH_HEAP_BLOCK Pool 池链表结构中的,言外之意就是它不会作为 heap_entry 的头部附加信息,接下来我们 dt 导出来看看 。
0:000> dt ntdll!_DPH_HEAP_BLOCK 056e1f70   +0x000 pNextAlloc       : 0x056e1020 _DPH_HEAP_BLOCK   +0x000 AvailableEntry   : _LIST_ENTRY [ 0x56e1020 - 0x0 ]   +0x000 TableLinks       : _RTL_BALANCED_LINKS   +0x010 pUserAllocation  : 0x056e5ff0  "???"   +0x014 pVirtualBlock    : 0x056e5000  "???"   +0x018 nVirtualBlockSize : 0x2000   +0x01c nVirtualAccessSize : 0x20   +0x020 nUserRequestedSize : 9   +0x024 nUserActualSize  : 0x56e1f60   +0x028 UserValue        : 0x056e1fc8 Void   +0x02c UserFlags        : 0x3f18   +0x030 StackTrace       : 0x042f4dcc _RTL_TRACE_BLOCK   +0x034 AdjacencyEntry   : _LIST_ENTRY [ 0x56e1010 - 0x56e1010 ]   +0x03c pVirtualRegion   : (null)从字段信息看,它记录了堆块的分配首地址 , 栈信息等等,比如用 dds 观察一下 StackTrace 。

推荐阅读