以下以windbg启动calc为调试结果:
1!heap
!heap 扩展显示堆使用信息,控制堆管理器中的断点,检测泄露的堆块,搜索堆块或者显示页堆(page heap)信息。
!heap -h列出当前进程的所有堆:
0:000> !heap -h
Index Address Name Debugging options enabled
1: 000a0000
Segment at 000a0000 to 001a0000 (00003000 bytes committed)
2: 001a0000
Segment at 001a0000 to 001b0000 (00006000 bytes committed)
3: 001b0000
Segment at 001b0000 to 001c0000 (00003000 bytes committed)
!heap -v可以观察堆的分配粒度和解除提交阈值:
0:000> !heap 000a0000 -v
Index Address Name Debugging options enabled
1: 000a0000
Segment at 000a0000 to 001a0000 (00003000 bytes committed)
Flags: 50000062
ForceFlags: 40000060
Granularity: 8 bytes
Segment Reserve: 00100000
Segment Commit: 00002000
DeCommit Block Thres: 00000200
DeCommit Total Thres: 00002000
Total Free Size: 000000d1
Max. Allocation Size: 7ffdefff
Lock Variable at: 000a0608
Next TagIndex: 0000
Maximum TagIndex: 0000
Tag Entries: 00000000
PsuedoTag Entries: 00000000
Virtual Alloc List: 000a0050
UCR FreeList: 000a0598
FreeList Usage: 00000000 00000000 00000000 00000000
FreeList[ 00 ] at 000a0178: 000a2980 . 000a2980 (1 block )
Segment at 000a0000 to 001a0000 (00003000 bytes committed)指明堆的内存范围和提交字节数
Granularity: 8 bytes指明堆块分配粒度
由于堆管理器使用HEAP结构来记录和维护堆的管理信息,因此我们把这个结构称为堆的管理结构,因为这个结构总是在每个堆的开始处,因此有时也被称为堆的头结构
下面显示了_HEAP的结构:
:000> dt ntdll!_HEAP 000a0000
+0x000 Entry : _HEAP_ENTRY
+0x008 Signature : 0xeeffeeff
+0x00c Flags : 0x50000062
+0x010 ForceFlags : 0x40000060
+0x014 VirtualMemoryThreshold : 0xfe00
+0x018 SegmentReserve : 0x100000
+0x01c SegmentCommit : 0x2000
+0x020 DeCommitFreeBlockThreshold : 0x200
+0x024 DeCommitTotalFreeThreshold : 0x2000
+0x028 TotalFreeSize : 0xd1
+0x02c MaximumAllocationSize : 0x7ffdefff
+0x030 ProcessHeapsListIndex : 1
+0x032 HeaderValidateLength : 0x608
+0x034 HeaderValidateCopy : (null)
+0x038 NextAvailableTagIndex : 0
+0x03a MaximumTagIndex : 0
+0x03c TagEntries : (null)
+0x040 UCRSegments : (null)
+0x044 UnusedUnCommittedRanges : 0x000a0598 _HEAP_UNCOMMMTTED_RANGE
+0x048 AlignRound : 0x17
+0x04c AlignMask : 0xfffffff8
+0x050 VirtualAllocdBlocks : _LIST_ENTRY [ 0xa0050 - 0xa0050 ]
+0x058 Segments : [64] 0x000a0640 _HEAP_SEGMENT
+0x158 u : __unnamed
+0x168 u2 : __unnamed
+0x16a AllocatorBackTraceIndex : 0
+0x16c NonDedicatedListLength : 1
+0x170 LargeBlocksIndex : (null)
+0x174 PseudoTagEntries : (null)
+0x178 FreeLists : [128] _LIST_ENTRY [ 0xa2980 - 0xa2980 ]
+0x578 LockVariable : 0x000a0608 _HEAP_LOCK
+0x57c CommitRoutine : (null)
+0x580 FrontEndHeap : 0x000a0688
+0x584 FrontHeapLockCount : 0
+0x586 FrontEndHeapType : 0x1 ''
+0x587 LastSegmentIndex : 0 ''
Segments字段用来记录堆中包含的所有段,它是一个数组,每个元素都指向一个HEAP_SEGMENT结构的指针,
+0x058 Segments : [64] 0x000a0640 _HEAP_SEGMENT
LastSegmentIndex的值加1就是段的总个数:
+0x587 LastSegmentIndex : 0 ''
说明就一个段:
0:000> dt _HEAP_SEGMENT 0x000a0640
ntdll!_HEAP_SEGMENT
+0x000 Entry : _HEAP_ENTRY
+0x008 Signature : 0xffeeffee
+0x00c Flags : 0
+0x010 Heap : 0x000a0000 _HEAP
+0x014 LargestUnCommittedRange : 0xfd000
+0x018 BaseAddress : 0x000a0000
+0x01c NumberOfPages : 0x100
+0x020 FirstEntry : 0x000a0680 _HEAP_ENTRY//第一个堆块
+0x024 LastValidEntry : 0x001a0000 _HEAP_ENTRY
+0x028 NumberOfUnCommittedPages : 0xfd
+0x02c NumberOfUnCommittedRanges : 1
+0x030 UnCommittedRanges : 0x000a0588 _HEAP_UNCOMMMTTED_RANGE
+0x034 AllocatorBackTraceIndex : 0
+0x036 Reserved : 0
+0x038 LastEntryInSegment : 0x000a2978 _HEAP_ENTRY//最后一个堆块
堆管理器使用_HEAP_ENTRY来描述每个堆块:
0:000> dt _HEAP_ENTRY 0x000a0680
ntdll!_HEAP_ENTRY
+0x000 Size : 0x303//堆块的大小,以分配粒度为单位
+0x002 PreviousSize : 8//前一个堆块的大小
+0x000 SubSegmentCode : 0x00080303
+0x004 SmallTagIndex : 0x60 '`'
+0x005 Flags : 0x7 ''//标志
+0x006 UnusedBytes : 0x18 ''//因为补齐而多分配的字节数
+0x007 SegmentIndex : 0 ''//这个堆块所在段的序号
其中Flags字段代表堆块的状态,其值是下列标志位的组合
标志 | 值 | 含义 |
HEAP_ENTRY_BUSY | 01 | 该块处于占用状态 |
HEAP_ENTRY_EXTRA_PRESENT | 02 | 该块存在额外的描述 |
HEAP_ENTRY_FILL_PATTERN | 04 | 使用固定模式填充堆块 |
HEAP_ENTRY_VIRTUAL_ALLOC | 08 | 虚拟分配 |
HEAP_ENTRY_LAST_ENTRY | 0X10 | 这是该段的最后一个块 |
_HEAP_ENTRY的长度固定为8字节长,位于堆块起始处,其后便是堆块的用户数据,也就是说,把HeapAlloc函数
返回的地址减去8,就是这个堆块的_HEAP_ENTRY结构的地址