摘要:堆条目现在已编码,关键在堆本身。
假设我在 0x00d60000 有一个堆:
0:000> !heap -a 00d60000
Index Address Name Debugging options enabled
2: 00d60000
Segment at 00d60000 to 00d70000 (00001000 bytes committed)
Flags: 40000061
ForceFlags: 40000061
Granularity: 8 bytes
Segment Reserve: 00100000
Segment Commit: 00002000
DeCommit Block Thres: 00000200
DeCommit Total Thres: 00002000
Total Free Size: 00000149
Max. Allocation Size: 7ffdefff
Lock Variable at: 00000000
Next TagIndex: 0000
Maximum TagIndex: 0000
Tag Entries: 00000000
PsuedoTag Entries: 00000000
Virtual Alloc List: 00d6009c
Uncommitted ranges: 00d6008c
00d61000: 0000f000 (61440 bytes)
FreeList[ 00 ] at 00d600c0: 00d605a0 . 00d605a0
00d60598: 00118 . 00a48 [104] - free
Segment00 at 00d60000:
Flags: 00000000
Base: 00d60000
First Entry: 00d60480
Last Entry: 00d70000
Total Pages: 00000010
Total UnCommit: 0000000f
Largest UnCommit:00000000
UnCommitted Ranges: (1)
Heap entries for Segment00 in Heap 00d60000
address: psize . size flags state (requested size)
00d60000: 00000 . 00480 [101] - busy (47f)
00d60480: 00480 . 00118 [107] - busy (100), tail fill
00d60598: 00118 . 00a48 [104] free fill
00d60fe0: 00a48 . 00020 [111] - busy (1d)
00d61000: 0000f000 - uncommitted bytes.
0x00d60480处有一个繁忙块:其分配的大小是0x118(前一个块的大小是0x480)。
如果我们转储这个块,我们可以看到它encoded:
0:000> dt _heap_entry 00d60480
ntdll!_HEAP_ENTRY
+0x000 Size : 0x7387
+0x002 Flags : 0xf5 ''
+0x003 SmallTagIndex : 0x64 'd'
+0x000 SubSegmentCode : 0x64f57387
+0x004 PreviousSize : 0xb95d
+0x006 SegmentOffset : 0 ''
+0x006 LFHFlags : 0 ''
+0x007 UnusedBytes : 0x18 ''
+0x000 FunctionIndex : 0x7387
+0x002 ContextValue : 0x64f5
+0x000 InterceptorValue : 0x64f57387
+0x004 UnusedBytesLength : 0xb95d
+0x006 EntryOffset : 0 ''
+0x007 ExtendedBlockSignature : 0x18 ''
+0x000 Code1 : 0x64f57387
+0x004 Code2 : 0xb95d
+0x006 Code3 : 0 ''
+0x007 Code4 : 0x18 ''
+0x004 Code234 : 0x1800b95d
+0x000 AgregateCode : 0x1800b95d`64f57387
回到堆,特别注意名为“Encoding”的字段(位于偏移量 0x50 处):
0:000> dt _heap encoding
ntdll!_HEAP
+0x050 Encoding : _HEAP_ENTRY
转储整个 _HEAP 结构:
0:000> dt _heap 00d60000
ntdll!_HEAP
+0x000 Entry : _HEAP_ENTRY
+0x008 SegmentSignature : 0xffeeffee
+0x00c SegmentFlags : 0
+0x010 SegmentListEntry : _LIST_ENTRY [ 0xd600a4 - 0xd600a4 ]
+0x018 Heap : 0x00d60000 _HEAP
+0x01c BaseAddress : 0x00d60000 Void
+0x020 NumberOfPages : 0x10
+0x024 FirstEntry : 0x00d60480 _HEAP_ENTRY
+0x028 LastValidEntry : 0x00d70000 _HEAP_ENTRY
+0x02c NumberOfUnCommittedPages : 0xf
+0x030 NumberOfUnCommittedRanges : 1
+0x034 SegmentAllocatorBackTraceIndex : 0
+0x036 Reserved : 0
+0x038 UCRSegmentList : _LIST_ENTRY [ 0xd60ff0 - 0xd60ff0 ]
+0x040 Flags : 0x40000061
+0x044 ForceFlags : 0x40000061
+0x048 CompatibilityFlags : 0
+0x04c EncodeFlagMask : 0x100000
+0x050 Encoding : _HEAP_ENTRY
+0x058 Interceptor : 0
+0x05c VirtualMemoryThreshold : 0xfe00
+0x060 Signature : 0xeeffeeff
+0x064 SegmentReserve : 0x100000
+0x068 SegmentCommit : 0x2000
+0x06c DeCommitFreeBlockThreshold : 0x200
+0x070 DeCommitTotalFreeThreshold : 0x2000
+0x074 TotalFreeSize : 0x149
+0x078 MaximumAllocationSize : 0x7ffdefff
+0x07c ProcessHeapsListIndex : 2
+0x07e HeaderValidateLength : 0x248
+0x080 HeaderValidateCopy : (null)
+0x084 NextAvailableTagIndex : 0
+0x086 MaximumTagIndex : 0
+0x088 TagEntries : (null)
+0x08c UCRList : _LIST_ENTRY [ 0xd60fe8 - 0xd60fe8 ]
+0x094 AlignRound : 0x17
+0x098 AlignMask : 0xfffffff8
+0x09c VirtualAllocdBlocks : _LIST_ENTRY [ 0xd6009c - 0xd6009c ]
+0x0a4 SegmentList : _LIST_ENTRY [ 0xd60010 - 0xd60010 ]
+0x0ac AllocatorBackTraceIndex : 0
+0x0b0 NonDedicatedListLength : 0
+0x0b4 BlocksIndex : 0x00d60248 Void
+0x0b8 UCRIndex : (null)
+0x0bc PseudoTagEntries : (null)
+0x0c0 FreeLists : _LIST_ENTRY [ 0xd605a0 - 0xd605a0 ]
+0x0c8 LockVariable : (null)
+0x0cc CommitRoutine : 0x7944d754 long +7944d754
+0x0d0 FrontEndHeap : (null)
+0x0d4 FrontHeapLockCount : 0
+0x0d6 FrontEndHeapType : 0 ''
+0x0d7 RequestedFrontEndHeapType : 0 ''
+0x0d8 FrontEndHeapUsageData : (null)
+0x0dc FrontEndHeapMaximumIndex : 0
+0x0de FrontEndHeapStatusBitmap : [257] ""
+0x1e0 Counters : _HEAP_COUNTERS
+0x23c TuningParameters : _HEAP_TUNING_PARAMETERS
将编码字段转储为两个 DWORD:
0:000> dd 00d60000 + 0x50 L2
00d60050 40f273a4 0000b9cd
现在将堆条目转储为两个 DWORD:
0:000> dd 00d60480 L2
00d60480 64f57387 1800b95d
让我们对它们进行异或:
0:000> ? 40f273a4 ^ 64f57387
Evaluate expression: 604438563 = 24070023
0:000> ? 0000b9cd ^ 1800b95d
Evaluate expression: 402653328 = 18000090
现在只需编写一个假的 _HEAP_ENTRY,这样我们就可以“dt”它:
0:000> ed 00d604b0
00d604b0 00000000 24070023
24070023
00d604b4 00000000 18000090
18000090
00d604b8 00000000
0:000> dt _HEAP_ENTRY 00d604b0
ntdll!_HEAP_ENTRY
+0x000 Size : 0x23
+0x002 Flags : 0x7 ''
+0x003 SmallTagIndex : 0x24 '$'
+0x000 SubSegmentCode : 0x24070023
+0x004 PreviousSize : 0x90
+0x006 SegmentOffset : 0 ''
+0x006 LFHFlags : 0 ''
+0x007 UnusedBytes : 0x18 ''
+0x000 FunctionIndex : 0x23
+0x002 ContextValue : 0x2407
+0x000 InterceptorValue : 0x24070023
+0x004 UnusedBytesLength : 0x90
+0x006 EntryOffset : 0 ''
+0x007 ExtendedBlockSignature : 0x18 ''
+0x000 Code1 : 0x24070023
+0x004 Code2 : 0x90
+0x006 Code3 : 0 ''
+0x007 Code4 : 0x18 ''
+0x004 Code234 : 0x18000090
+0x000 AgregateCode : 0x18000090`24070023
大小字段为 0x23,粒度为 8 字节(如!heap -a
命令输出)。这real块的大小是Size
字段值乘以粒度,因此:
0:000> ? 23 * 8
Evaluate expression: 280 = 00000118
它也适用于前一个块的大小(据报道为 0x480):
0:000> ? 0x90 * 8
Evaluate expression: 11552 = 00000480
我们发现了相同的尺寸。
粒度
粒度(由!heap -a
命令输出)不是由特定字段指示的,它只是一个的大小HEAP_ENTRY
结构:
x86 系统(或 WOW64)上为 8 个字节:
0:000> ?? sizeof(_HEAP_ENTRY)
unsigned int64 8
x64 系统上为 16 字节:
0:000> ?? sizeof(_HEAP_ENTRY)
unsigned int64 0x10