Windows Heap Note

0x0 Enviroment:

  • Windows 7 SP1 32-bit
  • Windbg

0x1 A simple code:

0x2 HEAP Alloc

The code creates a heap with 0x1000 at first and can be 0x10000. Load the relase version into windbg

Check heap status: !heap -stat

Address 00880000 points to the heap that we created. Other one is process default heap.

we can see that Reserved bytes size is 0x10000 and Committed bytes 0x1000, which specified in hp = HeapCreate(0, 0x1000, 0x10000);.

Then !heap -a 00880000

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
0:001> !heap -stat
_HEAP 005b0000
     Segments            00000001
         Reserved  bytes 00100000
         Committed bytes 00017000
     VirtAllocBlocks     00000000
         VirtAlloc bytes 00000000
_HEAP 00020000
     Segments            00000001
         Reserved  bytes 00010000
         Committed bytes 00010000
     VirtAllocBlocks     00000000
         VirtAlloc bytes 00000000
_HEAP 00880000
     Segments            00000001
         Reserved  bytes 00010000
         Committed bytes 00001000
     VirtAllocBlocks     00000000
         VirtAlloc bytes 00000000
_HEAP 00010000
     Segments            00000001
         Reserved  bytes 00010000
         Committed bytes 00001000
     VirtAllocBlocks     00000000
         VirtAlloc bytes 00000000
0:001> !heap -a 00880000
Index   Address  Name      Debugging options enabled
  4:   00880000 
    Segment at 00880000 to 00890000 (00001000 bytes committed)
    Flags:                00001000
    ForceFlags:           00000000
    Granularity:          8 bytes
    Segment Reserve:      00100000
    Segment Commit:       00002000
    DeCommit Block Thres: 00000200
    DeCommit Total Thres: 00002000
    Total Free Size:      0000014b
    Max. Allocation Size: 7ffdefff
    Lock Variable at:     00880138
    Next TagIndex:        0000
    Maximum TagIndex:     0000
    Tag Entries:          00000000
    PsuedoTag Entries:    00000000
    Virtual Alloc List:   008800a0
    Uncommitted ranges:   00880090
            00881000: 0000f000  (61440 bytes)
    FreeList[ 00 ] at 008800c4: 00880590 . 00880590  
        00880588: 00588 . 00a58 [100] - free

    Segment00 at 00880000:
        Flags:           00000000
        Base:            00880000
        First Entry:     00880588
        Last Entry:      00890000
        Total Pages:     00000010
        Total UnCommit:  0000000f
        Largest UnCommit:00000000
        UnCommitted Ranges: (1)

    Heap entries for Segment00 in Heap 00880000
        00880000: 00000 . 00588 [101] - busy (587)
        00880588: 00588 . 00a58 [100]
        00880fe0: 00a58 . 00020 [111] - busy (1d)
        00881000:      0000f000      - uncommitted bytes.

In the result, there is a FreeList which is a linked list data structure. It stores addresses of free blocks. Right now it is in address 008800c4. Since there is no malloc operation yet, this freelist only point to a free block at 0x00880590.

The last part of the result shows heap information:

We can see that the memeory we committed is divied into 3 parts. 0x00880588 is the free block (0x00880590) pointed by freelist. The first 8 bytes stores heap header information.

Freelist table will point to the address (0x00880590) after heap header (0x00880588). When the block is free, there are 8 bytes right after heap header infomation which stores pre and next pointers (here both are 0x008800c4).

Next we want to analze the heap malloc and free process. Open the exe using IDA Pro

The base is 0x400000.

and we want to set breakpoint at the first heapalloc function, which is 0x00401026.

Check application’s start address in windbg: lmf m ConsoleApplication1

so the address we can set breakpoint is :

set the breakppoint and run.

before first allocation:

After first allocation:

As we can see before allocation. address 0x00880588 is one whole heap. After the allocation, it gives 0x10 heap, which contains 8 byte heap header information, 3 bytes user data and 5 bytes fills.

Lets check 0x00880588 heap header.

dt _HEAP_ENTRY 00880588

Clearly, the value is wrong. The reason is when the heapalloc funciton returns, that heap header values is done with xor operation.

Let’s find out

dt _HEAP 00880000

Encoding value is in offset 0x50

dd 0x00880000 + 0x50

found the encoding value is 0x64717779

Revisit the first 4 bytes of heap entry value:

it is 0x6770777b

? 64717779^6770777b

got value is 0x03010002.

0x0002 means this heap is 2 blocks; 0x01 means this heap is busy; 0x03 means SmallTagIndex; Notice: 32 bits system one block is 8 bytes, while 64 bits system one block is 16 bytes.

0x3 HEAP Free

After h3 = HeapAlloc(hp, HEAP_ZERO_MEMORY, 6); check heap !heap -a 0x00880000

FreeList at 008800c4 and points to block 008805c0. and this block’s pre and next pointers point to FreeList .

Notice, these pointers are stored after 8 bytes heap header information.

After a few heap free operations.

Freelist has 3 blocks:

These blocks are in order. When new heap alloc request comes in, function will go throught this free list and find the same size block, or find one to split it.

For example, code requests 0xc size, it will get 0x18 (0x08 + 0x0c) block.

When heap free the block, it may combine with its neighbor. In this case, we fre h1, h3, h5. since their neighbors are in busy status, they will not combine to one block.

But after h2 is freed, block will combine:

Note:

When debugging a heap code, you need to run the program first and then attach the windbg.