Windows Server 2008中核心堆结构的变化

前阵子一个偶然的机会碰到了一个wow64的crash dump,是在Windows Server 2008 R2上产生的,硬件平台是x64。在分析过程中发现,原来2008的核心堆结构有了很大的变化,所以这里总结一下。


在Windows Server 2003上,核心堆结构如下:

lkd> dt nt!_heap
   +0x000 Entry            : _HEAP_ENTRY
   +0x008 Signature        : Uint4B
   +0x00c Flags            : Uint4B
   +0x010 ForceFlags       : Uint4B
   +0x014 VirtualMemoryThreshold : Uint4B
   +0x018 SegmentReserve   : Uint4B
   +0x01c SegmentCommit    : Uint4B
   +0x020 DeCommitFreeBlockThreshold : Uint4B
   +0x024 DeCommitTotalFreeThreshold : Uint4B
   +0x028 TotalFreeSize    : Uint4B
   +0x02c MaximumAllocationSize : Uint4B
   +0x030 ProcessHeapsListIndex : Uint2B
   +0x032 HeaderValidateLength : Uint2B
   +0x034 HeaderValidateCopy : Ptr32 Void
   +0x038 NextAvailableTagIndex : Uint2B
   +0x03a MaximumTagIndex  : Uint2B
   +0x03c TagEntries       : Ptr32 _HEAP_TAG_ENTRY
   +0x040 UCRSegments      : Ptr32 _HEAP_UCR_SEGMENT
   +0x044 UnusedUnCommittedRanges : Ptr32 _HEAP_UNCOMMMTTED_RANGE
   +0x048 AlignRound       : Uint4B
   +0x04c AlignMask        : Uint4B
   +0x050 VirtualAllocdBlocks : _LIST_ENTRY
   +0x058 Segments         : [64] Ptr32 _HEAP_SEGMENT
   +0x158 u                : __unnamed
   +0x168 u2               : __unnamed
   +0x16a AllocatorBackTraceIndex : Uint2B
   +0x16c NonDedicatedListLength : Uint4B
   +0x170 LargeBlocksIndex : Ptr32 Void
   +0x174 PseudoTagEntries : Ptr32 _HEAP_PSEUDO_TAG_ENTRY
   +0x178 FreeLists        : [128] _LIST_ENTRY
   +0x578 LockVariable     : Ptr32 _HEAP_LOCK
   +0x57c CommitRoutine    : Ptr32     long
   +0x580 FrontEndHeap     : Ptr32 Void
   +0x584 FrontHeapLockCount : Uint2B
   +0x586 FrontEndHeapType : UChar
   +0x587 LastSegmentIndex : UChar

 

而在Windows Server 2008 R2上,核心堆结构如下:

lkd> dt nt!_heap
   +0x000 Entry            : _HEAP_ENTRY
   +0x010 SegmentSignature : Uint4B
   +0x014 SegmentFlags     : Uint4B
   +0x018 SegmentListEntry : _LIST_ENTRY
   +0x028 Heap             : Ptr64 _HEAP
   +0x030 BaseAddress      : Ptr64 Void
   +0x038 NumberOfPages    : Uint4B
   +0x040 FirstEntry       : Ptr64 _HEAP_ENTRY
   +0x048 LastValidEntry   : Ptr64 _HEAP_ENTRY
   +0x050 NumberOfUnCommittedPages : Uint4B
   +0x054 NumberOfUnCommittedRanges : Uint4B
   +0x058 SegmentAllocatorBackTraceIndex : Uint2B
   +0x05a Reserved         : Uint2B
   +0x060 UCRSegmentList   : _LIST_ENTRY

   +0x070 Flags            : Uint4B
   +0x074 ForceFlags       : Uint4B
   +0x078 CompatibilityFlags : Uint4B
   +0x07c EncodeFlagMask   : Uint4B
   +0x080 Encoding         : _HEAP_ENTRY
   +0x090 PointerKey       : Uint8B
   +0x098 Interceptor      : Uint4B
   +0x09c VirtualMemoryThreshold : Uint4B
   +0x0a0 Signature        : Uint4B
   +0x0a8 SegmentReserve   : Uint8B
   +0x0b0 SegmentCommit    : Uint8B
   +0x0b8 DeCommitFreeBlockThreshold : Uint8B
   +0x0c0 DeCommitTotalFreeThreshold : Uint8B
   +0x0c8 TotalFreeSize    : Uint8B
   +0x0d0 MaximumAllocationSize : Uint8B
   +0x0d8 ProcessHeapsListIndex : Uint2B
   +0x0da HeaderValidateLength : Uint2B
   +0x0e0 HeaderValidateCopy : Ptr64 Void
   +0x0e8 NextAvailableTagIndex : Uint2B
   +0x0ea MaximumTagIndex  : Uint2B
   +0x0f0 TagEntries       : Ptr64 _HEAP_TAG_ENTRY
   +0x0f8 UCRList          : _LIST_ENTRY
   +0x108 AlignRound       : Uint8B
   +0x110 AlignMask        : Uint8B
   +0x118 VirtualAllocdBlocks : _LIST_ENTRY
   +0x128 SegmentList      : _LIST_ENTRY
   +0x138 AllocatorBackTraceIndex : Uint2B
   +0x13c NonDedicatedListLength : Uint4B
   +0x140 BlocksIndex      : Ptr64 Void
   +0x148 UCRIndex         : Ptr64 Void
   +0x150 PseudoTagEntries : Ptr64 _HEAP_PSEUDO_TAG_ENTRY
   +0x158 FreeLists        : _LIST_ENTRY
   +0x168 LockVariable     : Ptr64 _HEAP_LOCK
   +0x170 CommitRoutine    : Ptr64     long
   +0x178 FrontEndHeap     : Ptr64 Void
   +0x180 FrontHeapLockCount : Uint2B
   +0x182 FrontEndHeapType : UChar
   +0x188 Counters         : _HEAP_COUNTERS
   +0x1f8 TuningParameters : _HEAP_TUNING_PARAMETERS

lkd> dt nt!_heap_segment
   +0x000 Entry            : _HEAP_ENTRY
   +0x010 SegmentSignature : Uint4B
   +0x014 SegmentFlags     : Uint4B
   +0x018 SegmentListEntry : _LIST_ENTRY
   +0x028 Heap             : Ptr64 _HEAP
   +0x030 BaseAddress      : Ptr64 Void
   +0x038 NumberOfPages    : Uint4B
   +0x040 FirstEntry       : Ptr64 _HEAP_ENTRY
   +0x048 LastValidEntry   : Ptr64 _HEAP_ENTRY
   +0x050 NumberOfUnCommittedPages : Uint4B
   +0x054 NumberOfUnCommittedRanges : Uint4B
   +0x058 SegmentAllocatorBackTraceIndex : Uint2B
   +0x05a Reserved         : Uint2B
   +0x060 UCRSegmentList   : _LIST_ENTRY

 

从上面的列表结构可以看出:

 

1. Windows Server 2003上:

a) _heap数据结构完全占据一个_heap_entry,这点可以通过比较_heap_entry中的size和_heap结构的尺寸得出。

b) _heap_segment最多有64个,以数组方式存在_heap数据结构中。见列表中标红的部分。进一步研究表明,堆块的初始尺寸是1M,然后每次增加到原来的2倍尺寸,如果没有这么大的连续空间就回退一半,直到找到满足条件的连续空间或者失败。这样的算法使得在虚存空间分段严重的情况下,一个堆只能管理很小的内存空间。

c) FreeList是一个拥有128元素的数组,每个数组元素包含一个链表的头结点,用于链接相同大小的堆块。

 

2. Windows Server 2008 R2上:

a) _heap数据结构包含第一个_heap_segment,这点可以通过比较列表中标成蓝色的部分得到。

b) _heap_segment从数组变成链表了,进一步研究表明,初始对快大小是1M,然后对堆块每次增长到原来的2倍,但是堆块最大尺寸是0xfd00,即15MB。这样的好处有两个:

- 对虚存的连续性要求大大降低,内存的使用率就会大大提高。这样可以让系统只用一个堆就能管理所有的内存空间。

- 支持堆的安全特性。

c) FreeList变成了只有一个链表,感觉已经不再按照堆块大小区分不同链表了。

 

Windows Server 2008 R2的_heap结构中容易迷惑的是SegmentList成员和SegmentListEntry成员。实际上SegmentList才是链接所有_heap_segment的头结点。SegmentListEntry是第一个堆段链接到堆的头结点的成员。

另外两个结构中的VirtualAllocdBlocks结构都没有改变。该结构用于管理大于512kB的内存。

 

备注:

在用windbg amd64版调试wow64程序时,由于wow64.dll没有导出_TEB32和_PEB32数据结构,所以扩展命令!heap,!peb, !teb都不能用,可以用!info得到32位和64为的_peb和_teb,然后用dt ntdll32!_peb,dt ntdll32!_teb的到相应信息,包括32位堆的首地址。

另外一个解决方案就是直接使用windbg x86版调试wow64程序,这样就和32位完全一样,非常方便,但是缺点就是不能访问wow64所在的原生64位进程的信息。

你可能感兴趣的:(数据结构,windows,list,server,Interceptor,encoding)