<2024年12月>
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

文章分类

导航

订阅

解读编码后的HEAP_ENTRY结构

好久没有写博客了,很是惭愧。看到论坛上有朋友询问无法解读HEAP_ENTRY结构,觉得这个问题非常好,正好也略有空闲,就聊聊这个问题吧。

事情还是源于“广被误解”的Vista,说起话来,已经是2006年前的事了,当年Vista团队不辞劳苦对NT做了很多大刀阔斧的改革,但因为延期,和来不及打磨的楞楞角角,遭到的批评远比好评多,功劳都被后来的Win7给占了,真是比窦娥还冤。

Vista引入了很多新的东西,直到今天,还有很多东西需要Windows阵营里的各色人群慢慢认识和理解消化。比如对堆的改造就是其中之一。

长话短说,在堆的若干新特征中, 比较易让人感觉到的便是对堆块的块头结构(HEAP_ENTRY)编码。编码的目的是引入随机性,增强堆的安全性,防止黑客轻易就可以预测堆的数据内容而实施攻击。

首先,在_HEAP结构中新增(相对于XP)了如下两个字段:

   +0x07c EncodeFlagMask   : 0x100000
   +0x080 Encoding         : _HEAP_ENTRY

其中的EncodeFlagMask用来指示是否启用编码功能;Encoding字段是用来编码的,编码的方法就是用这个Encoding结构与每个堆块的头结构做异或(XOR)。

不妨通过实际例子来加深理解,辛苦notepad出来作下靶子,然后请WinDBG出场。先看32位的情况:

0:001> !heap 01670000  -A
Index   Address  Name      Debugging options enabled
  6:   01670000
    Segment at 01670000 to 016b0000 (00019000 bytes committed)

...

        0167f7b0: 00048 . 00460 [101] - busy (458)
        0167fc10: 00460 . 00060 [101] - busy (58)
        0167fc70: 00060 . 09370 [100]
        01688fe0: 09370 . 00020 [111] - busy (1d)
        01689000:      00027000      - uncommitted bytes.

不妨就以起始地址为0167fc10的这个堆块为例,先使用扩展命令观察:

0:001> !heap -x 0167fc10
Entry     User      Heap      Segment       Size  PrevSize  Unused    Flags
-----------------------------------------------------------------------------
0167fc10  0167fc18  01670000  01670000        60       460         8  busy

其总长度是0x60字节,包含头结构的8个字节。

再观察原始的结构内容:

0:001> dt _heap_entry  0167fc10
ntdll!_HEAP_ENTRY
   +0x000 Size             : 0x1922
   +0x002 Flags            : 0x37 '7'
   +0x003 SmallTagIndex    : 0x23 '#'
   +0x000 SubSegmentCode   : 0x23371922
   +0x004 PreviousSize     : 0xe768
   +0x006 SegmentOffset    : 0 ''
   +0x006 LFHFlags         : 0 ''
   +0x007 UnusedBytes      : 0x8 ''
   +0x000 FunctionIndex    : 0x1922
   +0x002 ContextValue     : 0x2337
   +0x000 InterceptorValue : 0x23371922
   +0x004 UnusedBytesLength : 0xe768
   +0x006 EntryOffset      : 0 ''
   +0x007 ExtendedBlockSignature : 0x8 ''
   +0x000 Code1            : 0x23371922
   +0x004 Code2            : 0xe768
   +0x006 Code3            : 0 ''
   +0x007 Code4            : 0x8 ''
   +0x000 AgregateCode     : 0x800e768`23371922

Size字段是1922,这显然不对,是编码过的。

下面尝试用手工解码,先要读取Encoding结构的数据:

0:001> dt _HEAP 01670000 
ntdll!_HEAP
   +0x000 Entry            : _HEAP_ENTRY
   +0x008 SegmentSignature : 0xffeeffee
   +0x00c SegmentFlags     : 0
   +0x010 SegmentListEntry : _LIST_ENTRY [ 0x16700a8 - 0x16700a8 ]
   +0x018 Heap             : 0x01670000 _HEAP
   +0x01c BaseAddress      : 0x01670000
   +0x020 NumberOfPages    : 0x40
   +0x024 FirstEntry       : 0x01670588 _HEAP_ENTRY
   +0x028 LastValidEntry   : 0x016b0000 _HEAP_ENTRY
   +0x02c NumberOfUnCommittedPages : 0x27
   +0x030 NumberOfUnCommittedRanges : 1
   +0x034 SegmentAllocatorBackTraceIndex : 0
   +0x036 Reserved         : 0
   +0x038 UCRSegmentList   : _LIST_ENTRY [ 0x1688ff0 - 0x1688ff0 ]
   +0x040 Flags            : 0x1002
   +0x044 ForceFlags       : 0
   +0x048 CompatibilityFlags : 0
   +0x04c EncodeFlagMask   : 0x100000
   +0x050 Encoding         : _HEAP_ENTRY
   +0x058 PointerKey       : 0x6db0dffc
   +0x05c Interceptor      : 0
   +0x060 VirtualMemoryThreshold : 0xfe00
   +0x064 Signature        : 0xeeffeeff
   +0x068 SegmentReserve   : 0x100000
   +0x06c SegmentCommit    : 0x2000
   +0x070 DeCommitFreeBlockThreshold : 0x200
   +0x074 DeCommitTotalFreeThreshold : 0x2000
   +0x078 TotalFreeSize    : 0x1a28
   +0x07c MaximumAllocationSize : 0x7ffdefff
   +0x080 ProcessHeapsListIndex : 6
   +0x082 HeaderValidateLength : 0x138
   +0x084 HeaderValidateCopy : (null)
   +0x088 NextAvailableTagIndex : 0
   +0x08a MaximumTagIndex  : 0
   +0x08c TagEntries       : (null)
   +0x090 UCRList          : _LIST_ENTRY [ 0x1688fe8 - 0x1688fe8 ]
   +0x098 AlignRound       : 0xf
   +0x09c AlignMask        : 0xfffffff8
   +0x0a0 VirtualAllocdBlocks : _LIST_ENTRY [ 0x16700a0 - 0x16700a0 ]
   +0x0a8 SegmentList      : _LIST_ENTRY [ 0x1670010 - 0x1670010 ]
   +0x0b0 AllocatorBackTraceIndex : 0
   +0x0b4 NonDedicatedListLength : 0
   +0x0b8 BlocksIndex      : 0x01670150
   +0x0bc UCRIndex         : 0x01670590
   +0x0c0 PseudoTagEntries : (null)
   +0x0c4 FreeLists        : _LIST_ENTRY [ 0x167f770 - 0x167fc78 ]
   +0x0cc LockVariable     : 0x01670138 _HEAP_LOCK
   +0x0d0 CommitRoutine    : 0x6db0dffc     long  +6db0dffc
   +0x0d4 FrontEndHeap     : (null)
   +0x0d8 FrontHeapLockCount : 0
   +0x0da FrontEndHeapType : 0 ''
   +0x0dc Counters         : _HEAP_COUNTERS
   +0x130 TuningParameters : _HEAP_TUNING_PARAMETERS
读取偏移为0x50的Encoding子结构:

0:001> dd 01670000+50
01670050  2e36192e 0000e7e4 6db0dffc 00000000
01670060  0000fe00 eeffeeff 00100000 00002000
01670070  00000200 00002000 00001a28 7ffdefff
01670080  01380006 00000000 00000000 00000000
01670090  01688fe8 01688fe8 0000000f fffffff8
016700a0  016700a0 016700a0 01670010 01670010
016700b0  00000000 00000000 01670150 01670590
016700c0  00000000 0167f770 0167fc78 01670138

再显示堆块头结构的原始数据:
0:001> dd 0167fc10 
0167fc10  23371922 0800e768 01ed3a00 01e9ca90
0167fc20  01c0fd08 0167edb8 00000001 00008c4e
0167fc30  000015f6 00000262 0000090c 00010000
0167fc40  01678a60 0000003c 00010001 00000000
0167fc50  ffffffff 00000000 00000000 ffffffff
0167fc60  00000000 00000000 ffffffff 00000000
0167fc70  52360b40 0000e7e8 016700c4 0167b5b8
0167fc80  0300d400 62617261 79631400 32006c72

做异或运算进行解码:
0:001> ? 23371922^2e36192e 
Evaluate expression: 218169356 = 0d01000c

低地址的word是Size字段,所以Size字段是0xC,因为是以粒度8为单位的,所以字节大小是:

0:001> ? c*8
Evaluate expression: 96 = 00000060

也就是0x60,与前面扩展命令显示出的刚好一致。

下面再看一下64位系统的情况,64位中,堆块头结构扩展为16字节,堆块的分配粒度也增大为16.

0:002> !heap 02680000                -A
Index   Address  Name      Debugging options enabled
  8:   02680000

...

        0000000002686ee0: 01010 . 010e0 [100]
        0000000002687fc0: 010e0 . 00040 [111] - busy (3d)
        0000000002688000:      00008000      - uncommitted bytes.

0:002> !heap -x 0000000002687fc0
Entry             User              Heap              Segment               Size  PrevSize  Unused    Flags
-------------------------------------------------------------------------------------------------------------
0000000002687fc0  0000000002687fd0  0000000002680000  0000000002680000        40      10e0         3  busy last

总长度是0x40字节

0:002> dt _HEAP_ENTRY 0000000002687fc0 
ntdll!_HEAP_ENTRY
   +0x000 PreviousBlockPrivateData : (null)
   +0x008 Size             : 0x98ce
   +0x00a Flags            : 0x55 'U'
   +0x00b SmallTagIndex    : 0x72 'r'
   +0x00c PreviousSize     : 0x40ec
   +0x00e SegmentOffset    : 0 ''
   +0x00e LFHFlags         : 0 ''
   +0x00f UnusedBytes      : 0x3 ''
   +0x008 CompactHeader    : 0x30040ec`725598ce
   +0x000 Reserved         : (null)
   +0x008 FunctionIndex    : 0x98ce
   +0x00a ContextValue     : 0x7255
   +0x008 InterceptorValue : 0x725598ce
   +0x00c UnusedBytesLength : 0x40ec
   +0x00e EntryOffset      : 0 ''
   +0x00f ExtendedBlockSignature : 0x3 ''
   +0x000 ReservedForAlignment : (null)
   +0x008 Code1            : 0x725598ce
   +0x00c Code2            : 0x40ec
   +0x00e Code3            : 0 ''
   +0x00f Code4            : 0x3 ''
   +0x008 AgregateCode     : 0x30040ec`725598ce

注意Size字段是从偏移8开始的两个字节,不再是从偏移0开始。

0:002> dt _HEAP 02680000               
ntdll!_HEAP
   +0x000 Entry            : _HEAP_ENTRY
   +0x010 SegmentSignature : 0xffeeffee
   +0x014 SegmentFlags     : 0
   +0x018 SegmentListEntry : _LIST_ENTRY [ 0x00000000`02690018 - 0x2680128 ]
   +0x028 Heap             : 0x00000000`02680000 _HEAP
   +0x030 BaseAddress      : 0x00000000`02680000
   +0x038 NumberOfPages    : 0x10
   +0x040 FirstEntry       : 0x00000000`02680a80 _HEAP_ENTRY
   +0x048 LastValidEntry   : 0x00000000`02690000 _HEAP_ENTRY
   +0x050 NumberOfUnCommittedPages : 8
   +0x054 NumberOfUnCommittedRanges : 1
   +0x058 SegmentAllocatorBackTraceIndex : 0
   +0x05a Reserved         : 0
   +0x060 UCRSegmentList   : _LIST_ENTRY [ 0x00000000`02687fe0 - 0x2687fe0 ]
   +0x070 Flags            : 0x1002
   +0x074 ForceFlags       : 0
   +0x078 CompatibilityFlags : 0
   +0x07c EncodeFlagMask   : 0x100000
   +0x080 Encoding         : _HEAP_ENTRY
   +0x090 PointerKey       : 0x4d3b4656`7fc766cd
   +0x098 Interceptor      : 0
   +0x09c VirtualMemoryThreshold : 0xff00
   +0x0a0 Signature        : 0xeeffeeff
   +0x0a8 SegmentReserve   : 0x200000
   +0x0b0 SegmentCommit    : 0x2000
   +0x0b8 DeCommitFreeBlockThreshold : 0x400
   +0x0c0 DeCommitTotalFreeThreshold : 0x1000
   +0x0c8 TotalFreeSize    : 0x315
   +0x0d0 MaximumAllocationSize : 0x7ff`fffdefff
   +0x0d8 ProcessHeapsListIndex : 8
   +0x0da HeaderValidateLength : 0x208
   +0x0e0 HeaderValidateCopy : (null)
   +0x0e8 NextAvailableTagIndex : 0
   +0x0ea MaximumTagIndex  : 0
   +0x0f0 TagEntries       : (null)
   +0x0f8 UCRList          : _LIST_ENTRY [ 0x00000000`02687fd0 - 0x26e0fd0 ]
   +0x108 AlignRound       : 0x1f
   +0x110 AlignMask        : 0xffffffff`fffffff0
   +0x118 VirtualAllocdBlocks : _LIST_ENTRY [ 0x00000000`02680118 - 0x2680118 ]
   +0x128 SegmentList      : _LIST_ENTRY [ 0x00000000`02680018 - 0x2690018 ]
   +0x138 AllocatorBackTraceIndex : 0
   +0x13c NonDedicatedListLength : 0
   +0x140 BlocksIndex      : 0x00000000`02680230
   +0x148 UCRIndex         : 0x00000000`02680a90
   +0x150 PseudoTagEntries : (null)
   +0x158 FreeLists        : _LIST_ENTRY [ 0x00000000`02684e80 - 0x26dfc70 ]
   +0x168 LockVariable     : 0x00000000`02680208 _HEAP_LOCK
   +0x170 CommitRoutine    : 0x4d3b4656`7fc766cd     long  +4d3b46567fc766cd
   +0x178 FrontEndHeap     : 0x00000000`02690080
   +0x180 FrontHeapLockCount : 0
   +0x182 FrontEndHeapType : 0x2 ''
   +0x188 Counters         : _HEAP_COUNTERS
   +0x1f8 TuningParameters : _HEAP_TUNING_PARAMETERS
0:002> dd 02680000+80               
00000000`02680080  00000000 00000000 775498ca 000041e2
00000000`02680090  7fc766cd 4d3b4656 00000000 0000ff00
再显示堆块头结构的原始数据:

0:002> dd 0000000002687fc0 
00000000`02687fc0  00000000 00000000 725598ce 030040ec
00000000`02687fd0  026e0fd0 00000000 026800f8 00000000
00000000`02687fe0  02680060 00000000 02680060 00000000
00000000`02687ff0  02688000 00000000 00008000 00000000

做异或解码:

0:002> ? 775498ca^725598ce
Evaluate expression: 83951620 = 00000000`05010004

低地址的WORD是0x4,乘上堆的分配力度0x10,是0x40,与扩展命令显示出的也刚好相符。

就写到这吧,突然想起还有封邮件要写,下次再聊...

posted on 2011年11月10日 22:41 由 Raymond

# re: 解读编码后的HEAP_ENTRY结构 @ 2011年11月11日 9:32

这篇文章不得不顶!!!!!!!!!!!!!!!!

lee0ne

Powered by Community Server Powered by CnForums.Net