Disclaimer: The experimental environment is Windows 2000
Source: Moeomu’s blog
Introduction to heap
Difference with stack
- A heap is a piece of memory space requested from the operating system by the programmer using functions such as malloc. Whether it succeeds or not has a great deal to do with the state of the operating system, unlike the neatly managed stack, its management and allocation algorithm are very peculiar
- The heap is freed by the programmer using free or delete, while the stack is automatically freed by the system.
- The address range of the heap varies greatly, while the memory address of the stack is always
0x0012XXXX
. - Heap addresses move from low to high and stack addresses move from high to low
Heap safety
- The heap is cluttered, so its use will be much more difficult compared to the stack, and the management of the heap has never been disclosed by Microsoft, so it is difficult to study
- In Windows 2000 - Windows XP SP1, heap management does not take security into account and is easy to exploit
- In Windows XP SP2 - Windows 2003, cookies and pointer validation at the head of the block were added.
- Windows Vista - Windows 7, the security, stability and efficiency of heap management have changed dramatically
Heap data structures and management policies
Two types of heap structures
- Heap blocks: The memory in the heap area is organized into blocks of different sizes, identified by heap blocks.
- Block head: the size of this block, whether it is occupied or not
- Block body: data area
- Heap table: Located at the beginning of the heap area, it can index all important information about the heap area, including the size, location, and occupancy or not of the heap block. The heap table is often represented by more than one data structure.
- In Windows, heap blocks in the occupied state are only indexed by the program that occupies them, and the heap table only indexes heap blocks in the idle state.
- Important heap tables in Windows.
- Idle bidirectional linked table: (Freelist) (Empty table)
- The empty table contains 128 arrays, the second array
freelist[1]
identifies 8 bytes of empty heap space, after which each item is incremented by 8 bytes one by one free heap block size (including heap head) = index item * 8(bytes)
freelist[0]
identifies all heap blocks larger than 1024 bytes (less than or equal to 512KB), which are sorted in ascending order from smallest to largest
- The empty table contains 128 arrays, the second array
- Fast one-way linked table (Lookaside) (fast table)
- The fast table contains 128 entries and is organized similarly to the empty table, but the single-linked table
- Always initialized to null, each fast table has up to 4 nodes
- Each node is initialized as occupied, so no heap block merging occurs
- Idle bidirectional linked table: (Freelist) (Empty table)
Management Policy
- Heap Block Allocation
- Zero empty table allocation: chaining free blocks of different sizes in ascending order, looking for the last block in reverse from
free[0]
, and then searching forward for the smallest free heap block that can meet the requirements for allocation - Ordinary free table allocation: find the best free space allocation, followed by the next best
- Fast table allocation: find the table with matching size, offload it from the heap table, and return a pointer to the heap block to the program
- When the empty table cannot find the optimal heap block, a slightly larger block is used for allocation. This is a suboptimal allocation, where a block is first cut out from the larger block to the exact size requested for allocation, and then the remaining part is relabeled with the block header and concatenated into the empty table.
- The fast table is only allocated when it is an exact match, so there is no such phenomenon
- Zero empty table allocation: chaining free blocks of different sizes in ascending order, looking for the last block in reverse from
- Heap block release
- Change the heap block status to free and chain to the appropriate heap table. All freed blocks will be chained to the end of the heap table, and the allocation will be taken from the end of the heap table first.
- Heap block merge
- Repeatedly requesting and releasing heap areas will create a lot of memory fragments, so in order to use memory wisely and efficiently some heap blocks will be merged
- This operation consists of unloading two blocks from the free table, merging the heap blocks, adjusting the block head information of the merged block, and re-chaining the new block into the free table.
- The heap area will also undergo a memory crunch (shrink the compact) performed by
RtlCompactHeap
, which will adjust the entire heap and try to merge the available pieces
- Heap block allocation and release strategy
- Small blocks (SIZE<1KB)
- Allocation
- Fast table allocation first, mechanical energy ordinary empty table allocation
- If it fails, use heap cache allocation
- If the heap cache allocation fails, try to allocate after memory crunch
- If allocation is not possible, return NULL
- Release
- Priority chaining to fast table (only 4 free blocks can be chained)
- If the fast table is full, chain to the corresponding empty table
- Allocation
- Large blocks (1KB<=SIZE<512KB)
- Allocation
- Allocate using heap cache
- If the heap cache allocation fails, use the big block in free[0] for allocation
- Release
- Put it into heap cache first
- If heap cache is full, chain into freelists[0]
- Allocation
- Jumbo block (SIZE>=512KB)
- Allocation: dummy allocation (not from heap area)
- Release: direct release, no heap table operation
- Small blocks (SIZE<1KB)
Practice
Lesson learned in blood: whether empty or fast table, its
Blink/Flink
pointer points to always theBlink/Flink
of the next/previous node
Testing empty tables
code
|
|
Watch
INT exception calls up debugger, not running
- After
HeadCreate()
creates the heap area, give the heap area pointer to EAX, and observe that the address is0x360000
at this point - Check the memory area
0x360000
, the information backward is (copied, I don’t know how big these structures are anyway) segment table index (SegmentList), virtual table index (VirtualAllocationList), empty table usage mark (freelist usage bitmap) and empty table index area - The empty table index is found at offset
0x178
, and its content is0x00360688
, which meansfreelist[0]
points to the offset0x688
, let’s congratulate what is stored in this place - This place stores
0x00360178
, wonderful, it points tofreelist[0]
, it goes around and points to itself, and thisfreelist[0]
seems to point to the only free heap area, generally known as the “tail block” - According to the structure of the heap block (below), the actual block starts at
0x00360680
, and it looks like the heap block pointer crosses the heap block head and points directly to the data area 0x1-0x2
bytes is its own size, at this time the value is0x0130
, indicating that the size of the heap is0x130
bytes- The
0x3-0x4
bytes are the previous heap block size, this value is0x08
(?????). Isn’t it said to be unique???) - The
0x5
byte is the index, which is 0 at this point 0x6
byte is Flag, at this point this is 10x7
byte is reserved byte, which is 00x8
byte is the tag index (debug state), I don’t know what it does, it is 00x9-0xC
(empty block exclusive) byte is the address of the previous empty block, it is0x00360178
- The
0xD-0x10
(empty heap block exclusive) byte is the address of the next empty heap block, again0x00360178
runs six allocations
0x00360680-0x00360688
is the h1 block header,0x00360689-0x0036068F
is the 8 byte block body with00 00 00 00 78 01 36 00
0x00360690-0x00360698
is the h2 block header,0x00360699-0x0036069F
is the 8-byte block body with00 00 00 00 00 01 36 00
0x003606A0-0x003606A8
is the h3 block header,0x003606A9-0x003606AF
is the 8-byte block body with00 00 00 00 00 00 00 36 00
0x003606B0-0x003606B8
is the h4 block header,0x003606B9-0x003606BF
is the 8-byte block body with00 00 00 00 00 00 00 00 00
0x003606C0-0x003606C8
is the h5 block header,0x003606C9-0x003606DF
is a 24 byte block body with `00 00 00 00 00 00 00 00 00 00 000x003606E0-0x003606E8
is the h5 block header and0x003606E9-0x003606FF
is the 24 byte block body with00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Freeing the heap
- The first time the heap is freed the block size is 16 bytes, so it is connected to
freeList[2]
, which is the location of0x188
, at this time the content is0x00360688
- The second freed heap is also connected to
freeList[2]
, which is not described in detail - The third released heap is also connected to
freeList[2]
, which is not described in detail - The fourth time it is freed, h3, h4 and h5 are adjacent to each other, so they are merged, where h3h4 is 2 heap units each and h5 is 4, so they are merged to a total of 8 heap units, excluding the heap head, they are left with 7 heap units, so they are put into
freeList[8]
Conclusion
- The information contained in the heap table is SegmentList, VirtualAllocationList, freelist usage bitmap and empty index area in that order.
- When a heap is just initialized, its heap block status
- There is only one large block in the idle state, which is called the “tail block”
- This is followed by the fast table
- Freelist[0] points to the “tail block”
- Each index of the region points to itself, except for the zero freelist index, which means that there are no free blocks in all the rest of the free-table
- First occupied block
|
|
- Idle state block head
|
|
- Heap block allocation
- The size of the heap block includes the block header, so the application for 32 bytes will allocate 40 bytes.
- The unit of heap block is 8 bytes, less than 8 bytes are allocated according to 8 bytes, so the minimum actual allocation is 16 bytes
- In the initial state, the fast table and the empty table are empty, there is no exact allocation, the request will be allocated using the suboptimal block
- Due to the occurrence of suboptimal allocation, the allocation function will cut away some small blocks from the tail block, modify the size at the beginning of the tail block, and finally point
freelist[0]
to the new tail block
Test the fast table
code
|
|
Conclusion
- The block first identification bit is 0x01
- Only the pointer to the next block in the stack is stored, there is no pointer to the previous block in the stack
- The address of
freeList[0]
at offset0x178
becomes0x00361E90
and the original0x00360688
is occupied by the fast table - The fast table starts at
0x688
, each structure has a total of0x30
bytes, and the first four bytes of content are the fast table chain - Although the 0Day security book says that the 8-byte heap area is inserted as
lookaside[1]
, it seems to me that it is the one at0x688
that islookaside[0]
, the one at0x6B8
that islookaside[1]
, and the one at0x0E8
that can be calledlookaside[2]
, which The size of the heap block with the block header is 16 bytes in total