Windbg学习19(!heap)
以下以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结构的地址