Windows Heap Chunk Header Parsing and Size Calculation

前端 未结 2 1433
无人共我
无人共我 2021-01-13 10:57

How can I calculate heap chunk size from raw bytes read from memory. I tried below thing.

0:001> !heap
Index   Address  Name      Debugging options enable         


        
2条回答
  •  野的像风
    2021-01-13 11:57

    Summary: heap entries are now encoded, the key is in the heap itself.

    Let's say I have a heap at 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.
    

    There's a busy block at 0x00d60480: its allocated size is 0x118 (the size of the previous block is 0x480).

    If we dump this block we can see it's 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
    

    Back to the heap, pay a particular attention to the field named "Encoding " (at offset 0x50):

    0:000> dt _heap encoding
    ntdll!_HEAP
       +0x050 Encoding : _HEAP_ENTRY
    

    Dumping the whole _HEAP structure:

    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
    

    Dumping the encoding field as two DWORDs:

    0:000> dd 00d60000 + 0x50 L2
    00d60050  40f273a4 0000b9cd
    

    Now dumping the heap entry as two DWORDs:

    0:000> dd 00d60480 L2
    00d60480  64f57387 1800b95d
    

    Let's XOR them:

    0:000> ? 40f273a4 ^ 64f57387 
    Evaluate expression: 604438563 = 24070023
    
    0:000> ? 0000b9cd ^ 1800b95d 
    Evaluate expression: 402653328 = 18000090
    

    Now just writing a fake _HEAP_ENTRY so we can 'dt' it:

    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
    

    Size field is 0x23, granularity is 8 bytes (as reported by the !heap -a command output). The real size of the block is the Size field value multiplied by the granularity, so:

    0:000> ? 23 * 8
    Evaluate expression: 280 = 00000118
    

    It also works for the size of the previous block (reported to be 0x480):

    0:000> ? 0x90 * 8
    Evaluate expression: 11552 = 00000480
    

    We found the same sizes.

    Granularity

    Granularity (as given by the !heap -a command output) is not indicated by a specific field, it's just the size of a HEAP_ENTRY structure:

    8 bytes on x86 systems (or WOW64):

    0:000> ?? sizeof(_HEAP_ENTRY)
    unsigned int64 8
    

    16 bytes on x64 systems:

    0:000> ?? sizeof(_HEAP_ENTRY)
    unsigned int64 0x10
    

提交回复
热议问题