Windows Heap Exploitation
Angelboy
Windows Heap Exploitation
• Windows Memory Allocator

• NT heap

• BackEnd

• Exploitation

• FrontEnd

• LowFragmentationHeap

• Exploitation
Windows memory allocator
• Memory allocator in Win10 is very complicated, there are mainly two kind
of memory allocator.

• Nt Heap

• Default memory allocator

• SegmentHeap

• New memory allocator in Win10

• Some system process and UWP will use.
Windows memory allocator
• Nt Heap can be divided into

• Back-End

• Front-End

• LowFragmentationHeap

• In order to prevent memory fragmentation problem, it will enable LFH
after allocating a certain number of the same size of chunk. (e.g.
Continuously allocate same size of chunks 18 times)

• Size <= 0x4000
Windows memory allocator
• The slide based on win10 (1809)

• OS build - 17763.379

• Later versions may not be completely correct

• 1903 is very smimilar

• The structure of heap often change a lot. In the new release version,
you need to trace the structure and workflow by yourself.
Windows memory allocator
• Overview
Kernel32.dll
HeapAlloc
HeapFree
msvcrt140.dll
malloc
free
ntdll.dll
RtlAllocateHeap
RtlFreeHeap
ntdll.dll
RtlpAllocateHeap
RtlpFreeHeap
ntdll.dll
RtlpLowFragHeapAlloc
RtlpLowFragHeapFree
Kernel
Front-End
Back-End
Windows memory allocator
• When we call malloc if LFH is disable.
Kernel32.dll
HeapAlloc
HeapFree
msvcrt140.dll
malloc
free
ntdll.dll
RtlAllocateHeap
RtlFreeHeap
ntdll.dll
RtlpAllocateHeap
RtlpFreeHeap
Kernel
Back-End
Windows memory allocator
• When we call malloc at the first time or when 

memory pool called userblock of LFH is used 

up if LFH is enable. 

• It will use back-end allocator to allocate 

userblock for LFH.
Kernel32.dll
HeapAlloc
HeapFree
msvcrt140.dll
malloc
free
ntdll.dll
RtlAllocateHeap
RtlFreeHeap
ntdll.dll
RtlpAllocateHeap
RtlpFreeHeap
ntdll.dll
RtlpLowFragHeapAlloc
RtlpLowFragHeapFree
Kernel
Front-End
Back-End
Windows memory allocator
• When we allocate the same size after enabling LFH,

it will use front-end allocator and use userblock in 

LFH
Kernel32.dll
HeapAlloc
HeapFree
msvcrt140.dll
malloc
free
ntdll.dll
RtlAllocateHeap
RtlFreeHeap
ntdll.dll
RtlpLowFragHeapAlloc
RtlpLowFragHeapFree
Front-End
Windows memory allocator
• HEAP can be divide into

• Process Heap

• Default heap

• The heap shared by the entire process, it will be used when you use
windows API 

• Stored in _PEB

• Used in CRT function

• Stored in crt_heap
Windows memory allocator
• HEAP can be divide into

• Private heap

• Create by HeapCreate
Windows memory allocator
• Core data structure

• _HEAP_ENTRY (chunk)

• The basic structure of memory allocator

• It is different in front-end and back-end, but it use same name.
Windows memory allocator
• _HEAP_ENTRY (chunk)
data
chunk header
chunk header
data
_HEAP
chunk header
.
.
.
malloc return value
Windows memory allocator
• Core data structure

• _HEAP

• The core structure of memory allocator, it is used to manage the
heap.

• Each heap correspond to a _HEAP structure, usually at the
beginning of the heap
Windows memory allocator
• _HEAP

• EncodeFlagMask

• It will be set to 0x100000 after
heap initialization. It’s used to
determine whether to encode the
header of the chunk in the heap

• Encoding (_HEAP_ENTRY)

• It’s used to do xor with chunk
header
…
EncodeFlagMask0x7c
0x80
0x138
Encoding
…
BlocksIndex
…
FreeList
…
FrontEndHeap
…
0x150
0x198
FrontEndHeapUsageData0x1a8
…
Windows memory allocator
• _HEAP

• BlocksIndex (_HEAP_LIST_LOOKUP_)

• The core structure in back-end
allocator used to manage chunks.

• FreeList (_HEAP_ENTRY)

• A linked list used to collect all freed
chunk in back-end

• Similar as unsorted bin in libc

• Sorted list
…
EncodeFlagMask0x7c
0x80
0x138
Encoding
…
BlocksIndex
…
FreeList
…
FrontEndHeap
…
0x150
0x198
FrontEndHeapUsageData0x1a8
…
Windows memory allocator
• _HEAP

• FrontEndHeap

• A pointer pointed to the structure of
front-end heap

• FrontEndHeapUsageData

• Record the number of chunks used
by various sizes.

• When it reaches a certain level, it
will enable the Front-End allocator
of the corresponding chunk.
…
EncodeFlagMask0x7c
0x80
0x138
Encoding
…
BlocksIndex
…
FreeList
…
FrontEndHeap
…
0x150
0x198
FrontEndHeapUsageData0x1a8
…
Windows memory allocator
• Nt Heap

• Back-End

• Front-End
Nt heap
• Nt Heap

• Back-End

• Data structure

• Memory allocation mechanism
Windows memory allocator
• _HEAP_ENTRY (chunk)

• Divided into

• Allocated chunk

• Freed chunk

• VirtualAlloc chunk
Nt heap
• _HEAP_ENTRY (chunk)

• PreviousBlockPrivateData

• The data of the previous chunk,
because chunk must be aligned to a
multiple of 0x10 

• Size

• The size of chunk.

• The value is right shift by 4 bits.
User Data
PreviousBlockPrivateData
(8byte)
Size (2byte)
Flag (1byte)
SmallTagIndex (1byte)
PreviousSize (2byte)
SegmentOffset (1byte)
Unusedbyte (1byte)
Inused
Nt heap
• _HEAP_ENTRY (chunk)

• Flag

• Indicates whether the chunk is busy

• SmallTagIndex

• Checksum is the xor of the first three
bytes

• It will be used to verify the header of
chunk
User Data
PreviousBlockPrivateData
(8byte)
Size (2byte)
Flag (1byte)
SmallTagIndex (1byte)
PreviousSize (2byte)
SegmentOffset (1byte)
Unusedbyte (1byte)
Inused
xor
Nt heap
• _HEAP_ENTRY (chunk)

• PreviousSize

• The size of previous chunk.

• The value is right shift by 4 bits.
User Data
PreviousBlockPrivateData
(8byte)
Size (2byte)
Flag (1byte)
SmallTagIndex (1byte)
PreviousSize (2byte)
SegmentOffset (1byte)
Unusedbyte (1byte)
Inused
Nt heap
• _HEAP_ENTRY (chunk)

• Unusedbyte

• The remaining size of chunk after
allocation

• It can be used to determine the status
of chunk (front-end or back-end)

• User Data

• The data used by user
User Data
PreviousBlockPrivateData
(8byte)
Size (2byte)
Flag (1byte)
SmallTagIndex (1byte)
PreviousSize (2byte)
SegmentOffset (1byte)
Unusedbyte (1byte)
Inused
Nt heap
• _HEAP_ENTRY (chunk)

• Flink

• Point to the next chunk in the linked list

• Blink

• Point to the previous chunk in the
linked list

• Unusedbyte 

• Must be zero
User Data
PreviousBlockPrivateData
(8byte)
Size (2byte)
Flag (1byte)
SmallTagIndex (1byte)
PreviousSize (2byte)
SegmentOffset (1byte)
Unusedbyte (1byte)
freed
Flink
Blink
Nt heap
• _HEAP_VIRTUAL_ALLOC_ENTRY (mmap
chunk)

• Flink

• Point to the next chunk of VirtualAlloc

• Blink

• Point to the previous chunk of
VirtualAlloc
User Data
Size (2byte)
Flag (1byte)
SmallTagIndex (1byte)
PreviousSize (2byte)
SegmentOffset (1byte)
Unusedbyte (1byte)
inused
Flink
Blink
…
Nt heap
• _HEAP_VIRTUAL_ALLOC_ENTRY (mmap
chunk)

• Size

• The size here refers to unused size
without shifting

• Unusedbyte

• Must be 4
User Data
Size (2byte)
Flag (1byte)
SmallTagIndex (1byte)
PreviousSize (2byte)
SegmentOffset (1byte)
Unusedbyte (1byte)
inused
Flink
Blink
…
Nt heap
• FreeLists (_HEAP_ENTRY)

• After free a chunk, it will be placed in FreeLists and will be inserted in
FreeLists according to the size.
Flink
Blink
Chunk header (0x70)
PreviousBlockPrivateData
Flink
Blink
Chunk header (0x110)
PreviousBlockPrivateData
Flink
Blink
Chunk header (0x160)
PreviousBlockPrivateData
Nt heap
Flink
Blink
Chunk header (0x70)
PreviousBlockPrivateData
Flink
Blink
Chunk header (0x110)
PreviousBlockPrivateData
Flink
Blink
Chunk header (0x160)
PreviousBlockPrivateDat
…
EncodeFlagMask0x7c
0x80
0x138
Encoding
…
BlocksIndex
…
FreeList
…
FrontEndHeap
…
0x150
0x198
FrontEndHeapUsageData0x1a8
…
Flink
Blink
_HEAP
Nt heap
• Remark

• About header encoding

• Every chunk header will be xor with _HEAP->Encoding

• It will be verified when it is decoded

• The verification method is to check whether the checksum is
correct.

• The xor of the first three bytes and compare with fourth byte.
Nt heap
• BlocksIndex (_HEAP_LIST_LOOKUP)

• It is mainly used to manage freed chunks of various sizes, so that it can
quickly find suitable chunks.
Nt heap
• BlocksIndex (_HEAP_LIST_LOOKUP)

• ExtendedLookup (_HEAP_LIST_LOOKUP)

• Point to next ExtendedLookup

• The next BlocksIndex will manage larger
chunks

• ArraySize

• The max chunk size that will be managed by
the BlocksIndex.

• The first BlocksIndex ArraySize will by 0x80
(Actually is 0x800)
BlocksIndex
ExtendedLookup0x0
ArraySize0x8
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
0x10
0x14
BaseIndex0x18
ListHead
ListsInUseUlong
ListHint
0x20
0x28
0x30
Nt heap
• BlocksIndex (_HEAP_LIST_LOOKUP)

• ItemCount

• The number of chunks in the
BlocksIndex

• OutofRangeItems

• The number of chunks that exceed
the size managed by this
BlocksIndex
BlocksIndex
ExtendedLookup0x0
ArraySize0x8
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
0x10
0x14
BaseIndex0x18
ListHead
ListsInUseUlong
ListHint
0x20
0x28
0x30
Nt heap
• BlocksIndex (_HEAP_LIST_LOOKUP)

• BaseIndex

• The starting index of the chunk in the
Blocksindex.

• It is used to find a suitable freed chunk in
ListHint

• The next BaseIndex of next BlocksIndex is the
maximum value of current BaseIndex.

• ListHead (_HEAP_ENTRY)

• FreeList 的 Head
BlocksIndex
ExtendedLookup0x0
ArraySize0x8
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
0x10
0x14
BaseIndex0x18
ListHead
ListsInUseUlong
ListHint
0x20
0x28
0x30
Nt heap
• BlocksIndex (_HEAP_LIST_LOOKUP)

• ListsInUseUlong

• Used to determine whether there is a suitable
chunk in ListHint, which is a bitmap

• ListHint 

• Important structure in back-end.

• A pointer array which point to the corresponding
size of chunk array.

• The goal is to find the suitable chunk faster.

• The interval of chunk size is 0x10
BlocksIndex
ExtendedLookup0x0
ArraySize0x8
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
0x10
0x14
BaseIndex0x18
ListHead
ListsInUseUlong
ListHint
0x20
0x28
0x30
…
EncodeFlagMask
Encoding
…
BlocksIndex
…
FreeList
…
FrontEndHeap
…
FrontEndHeapUsageDat
…
_HEAP
BlocksIndex
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Flink
Blink
Flink
Blink
Chunk header (0x70)
PreviousBlockPrivate
Flink
Blink
Chunk header (0x110)
PreviousBlockPrivate
Flink
Blink
Chunk header(0x160)
PreviousBlockPrivat
ListHint
Flink
Flink
Flink
ListHint[7]
ListHint[0x11]
ListHint[0x16]
000001…1…1000000000
ListsInUseUlong
Nt heap
• Nt Heap

• Back-End

• Data structure

• Memory allocation mechanism
Nt heap
• Allocate (RtlpAllocateHeap)

• It can be divided into

• Size <= 0x4000

• 0x4000 < size <= 0xff000

• Size > 0xff000
Nt heap
• Allocate (RtlpAllocateHeap)

• Size <= 0x4000

• The memory allocation is implemented in RtlpAllocateHeap

• First, it will check whether FrontEndHeapStatusBitmap corresponds to the
size is set (which indicates if LFH is enabled)

• If not, add 0x21 to the corresponding FrontEndHeapUsageData

• And check if the value exceeds 0xff00 or (& 0x1f) is larger than 0x10

• If true, it will enable LFH.
Nt heap
• Allocate (RtlpAllocateHeap)

• Size <= 0x4000

• Next, it will check whether ListHint corresponds to the size has a value. If true, it will take
the chunk in ListHint first.

• If there is a suitable chunk on the ListHint, remove the ListHint, and see whether the size
of chunk's Flink is the same size.

• If true, replace the ListHint with Flink

• If not, clear the ListHint.

• Finally, unlink the chunk to remove the chunk from the linked list and return it to user.
Nt heap
• Allocate (RtlpAllocateHeap)

• Size <= 0x4000

• If there is no suitable chunk

• Search from the larger ListHint. If it finds a large one, remove it
from the ListHint.

• Then split the chunk and insert remaining chunk into freelist and
put into ListHint.
Nt heap
• Allocate (RtlpAllocateHeap)

• Size <= 0x4000

• If there is no suitable chunk in FreeList

• Try ExtendHeap to increase heap space, then take the chunk from
it
Nt heap
• Allocate (RtlpAllocateHeap)

• 0x4000 < size <= 0xff000

• Everything is similar except without LFH-related operations
Nt heap
• Allocate (RtlpAllocateHeap)

• Size > 0xff000 (VirtualMemoryThreshold << 4)

• Use ZwAllocateVirtualMemory directly

• Similar to mmap, it will allocate large memory block , and will be
inserted into _HEAP->VirtualAllocdBlocks

• _HEAP->VirtualAllocdBlocks is linked list used to collect all
VirtualAllocate chunk in back-end
Nt heap
• Free (RtlpFreeHeap)

• Divide into

• Size <= 0xff000 

• Size > 0xff000
Nt heap
• Free (RtlpFreeHeap)

• Size <= 0xff000 

• It will check the alignment first and use the unused byte to determine the
chunk state

• If LFH is disable, decrease 1 to the corresponding
FrontEndHeapUsageData

• Then it will check whether the previous chunk or next chunk is freed. If it is
freed, then coalesce them.

• Unlink the coalesced chunk and remove from ListHint.
Nt heap
• Free (RtlpFreeHeap)

• Size <= 0xff000 

• After the coalescence, update size & prevsize, and check whether it
is the front or last of FreeList. If true, then insert into FreeList. If not,
insert into ListHint and update it. 

• It will check the linked list integrity when inserting

• But this check won’t terminate the process.
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Flink
Blink
Flink
Blink
Chunk header (0x70)
PreviousBlockPrivate
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x110)
PreviousBlockPrivat
FlinkListHint[7]
Flink
Blink
Chunk header(0x70)
PreviousBlockPrivat
merge chunk
P
Q
R
S
Free(Q)
Size (0x11)
Flag (0x1)
SmallTagIndex (0x10)
Prevsize (0x7)
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Flink
Blink
Flink
Blink
Chunk header (0x70)
PreviousBlockPrivate
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x110)
PreviousBlockPrivat
FlinkListHint[7]
Flink
Blink
Chunk header(0x70)
PreviousBlockPrivat
merge chunk
P
Q
R
S
Find Prevchunk
Size (0x11)
Flag (0x1)
SmallTagIndex (0x10)
Prevsize (0x7)
Prevchunk =
Chunk address
-
(prevsize << 4)
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Flink
Blink
Flink
Blink
Chunk header (0x70)
PreviousBlockPrivate
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x110)
PreviousBlockPrivat
FlinkListHint[7]
Flink
Blink
Chunk header(0x70)
PreviousBlockPrivat
merge chunk
P
Q
R
S
Find Prevchunk
Size (0x11)
Flag (0x1)
SmallTagIndex (0x10)
Prevsize (0x7)
Prevchunk =
chunk addr - 0x70
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Flink
Blink
Flink
Blink
Chunk header (0x70)
PreviousBlockPrivate
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x110)
PreviousBlockPrivat
FlinkListHint[7]
Flink
Blink
Chunk header(0x70)
PreviousBlockPrivat
merge chunk
P
Q
R
S
Decode prevchunk

header
Size (0x11)
Flag (0x1)
SmallTagIndex (0x10)
Prevsize (0x7)
Size (0x7)
Flag (0x0)
SmallTagIndex (0x7)
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Flink
Blink
Flink
Blink
Chunk header (0x70)
PreviousBlockPrivate
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x110)
PreviousBlockPrivat
FlinkListHint[7]
Flink
Blink
Chunk header(0x70)
PreviousBlockPrivat
merge chunk
P
Q
R
S
Check checksum
Size (0x11)
Flag (0x1)
SmallTagIndex (0x10)
Prevsize (0x7)
Size (0x7)
Flag (0x0)
SmallTagIndex (0x7)
SmallTagInex ==

(size & 0xff)
xor
(size >> 8)
xor
Flag
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Flink
Blink
Flink
Blink
Chunk header (0x70)
PreviousBlockPrivate
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x110)
PreviousBlockPrivat
FlinkListHint[7]
Flink
Blink
Chunk header(0x70)
PreviousBlockPrivat
merge chunk
P
Q
R
S
Check checksum
Size (0x11)
Flag (0x1)
SmallTagIndex (0x10)
Prevsize (0x7)
Size (0x7)
Flag (0x0)
SmallTagIndex (0x7)
7 ==

0x7^0x0^0x0
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Flink
Blink
Flink
Blink
Chunk header (0x70)
PreviousBlockPrivate
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x110)
PreviousBlockPrivat
FlinkListHint[7]
Flink
Blink
Chunk header(0x70)
PreviousBlockPrivat
merge chunk
P
Q
R
S
Check

linked list
Size (0x11)
Flag (0x1)
SmallTagIndex (0x10)
Prevsize (0x7)
Size (0x7)
Flag (0x0)
SmallTagIndex (0x7)
P->Flink->Blink == 

P->Blink->Flink == 

P
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Flink
Blink
Flink
Blink
Chunk header (0x70)
PreviousBlockPrivate
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x110)
PreviousBlockPrivat
FlinkListHint[7]
Flink
Blink
Chunk header(0x70)
PreviousBlockPrivat
merge chunk
P
Q
R
S
Find 

BlocksIndex
Size (0x11)
Flag (0x1)
SmallTagIndex (0x10)
Prevsize (0x7)
Size (0x7)
Flag (0x0)
SmallTagIndex (0x7)
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Flink
Blink
Flink
Blink
Chunk header (0x70)
PreviousBlockPrivate
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x110)
PreviousBlockPrivat
FlinkListHint[7]
Flink
Blink
Chunk header(0x70)
PreviousBlockPrivat
merge chunk
P
Q
R
S
Find suitable 

BlocksIndex
Size (0x11)
Flag (0x1)
SmallTagIndex (0x10)
Prevsize (0x7)
Size (0x7)
Flag (0x0)
SmallTagIndex (0x7)
Check
P->size < ArraySize
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Flink
Blink
Flink
Blink
Chunk header (0x70)
PreviousBlockPrivate
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x110)
PreviousBlockPrivat
FlinkListHint[7]
Flink
Blink
Chunk header(0x70)
PreviousBlockPrivat
merge chunk
P
Q
R
S
Check

ListHint
Size (0x11)
Flag (0x1)
SmallTagIndex (0x10)
Prevsize (0x7)
Size (0x7)
Flag (0x0)
SmallTagIndex (0x7)
Check
ListHint[7] ==
Prevchunk
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Flink
Blink
Flink
Blink
Chunk header (0x70)
PreviousBlockPrivate
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x110)
PreviousBlockPrivat
FlinkListHint[7]
Flink
Blink
Chunk header(0x70)
PreviousBlockPrivat
merge chunk
P
Q
R
S
Check

ListHint
Size (0x11)
Flag (0x1)
SmallTagIndex (0x10)
Prevsize (0x7)
Size (0x7)
Flag (0x0)
SmallTagIndex (0x7)
Check
prevchunk->Flink ==
ListHead
If true, update

ListHint
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Flink
Blink
Flink
Blink
Chunk header (0x70)
PreviousBlockPrivate
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x110)
PreviousBlockPrivat
FlinkListHint[7]
Flink
Blink
Chunk header(0x70)
PreviousBlockPrivat
merge chunk
P
Q
R
S
Check

ListHint[7]->Flink
Size (0x11)
Flag (0x1)
SmallTagIndex (0x10)
Prevsize (0x7)
Size (0x7)
Flag (0x0)
SmallTagIndex (0x7)
decode
prevchunk->Flink
And check
the checksum
Because the previous
slide is not true, it

will check the size

of Flink
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Flink
Blink
Flink
Blink
Chunk header (0x70)
PreviousBlockPrivate
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x110)
PreviousBlockPrivat
FlinkListHint[7]
Flink
Blink
Chunk header(0x70)
PreviousBlockPrivat
merge chunk
P
Q
R
S
Check

ListHint[7]->Flink
Size (0x11)
Flag (0x1)
SmallTagIndex (0x10)
Prevsize (0x7)
Size (0x7)
Flag (0x0)
SmallTagIndex (0x7)
Size == 

prevchunk->Flink->size
If true, replace ListHint

with Flink

ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Flink
Blink
Flink
Blink
Chunk header (0x70)
PreviousBlockPrivate
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x110)
PreviousBlockPrivat
FlinkListHint[7]
Flink
Blink
Chunk header(0x70)
PreviousBlockPrivat
merge chunk
P
Q
R
S
Update

ListHint[7]
Size (0x11)
Flag (0x1)
SmallTagIndex (0x10)
Prevsize (0x7)
Size (0x7)
Flag (0x0)
SmallTagIndex (0x7)
ListHint[7] =

prevchunk->Flink
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Flink
Blink
Flink
Blink
Chunk header (0x70)
PreviousBlockPrivate
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x110)
PreviousBlockPrivat
FlinkListHint[7]
Flink
Blink
Chunk header(0x70)
PreviousBlockPrivat
merge chunk
P
Q
R
S
Unlink

prevchunk
Size (0x11)
Flag (0x1)
SmallTagIndex (0x10)
Prevsize (0x7)
Size (0x7)
Flag (0x0)
SmallTagIndex (0x7)
Prevchunk->Blink->Flink

= Prevchunk->Flink

Prevchunk->Flink->Blink
= Prevchunk->Blink
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Flink
Blink
Chunk header(0x110)
PreviousBlockPrivat
FlinkListHint[7]
Flink
Blink
Chunk header(0x70)
PreviousBlockPrivat
merge chunk
P
Q
R
S
Update

prevchunk

and next chunk
Size (0x11)
Flag (0x1)
SmallTagIndex (0x10)
Prevsize (0x7)
Size (0x18)
Flag (0x0)
SmallTagIndex (0x7)
Prevchunk->size =

0x11 + Prevchunk->size(7)
R->Prevsize = 0x18
P
Flink
Blink
Chunk header (0x180)
PreviousBlockPrivate
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Flink
Blink
Chunk header(0x110)
PreviousBlockPrivat
FlinkListHint[7]
Flink
Blink
Chunk header(0x70)
PreviousBlockPrivat
merge chunk
P
Q
R
S
Check

next chunk
Size (0x11)
Flag (0x1)
SmallTagIndex (0x10)
Prevsize (0x7)
Size (0x18)
Flag (0x0)
SmallTagIndex (0x7)
Check if next chunk

is freed
P
Flink
Blink
Chunk header (0x180)
PreviousBlockPrivate
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Flink
Blink
Chunk header(0x110)
PreviousBlockPrivat
FlinkListHint[7]
Flink
Blink
Chunk header(0x70)
PreviousBlockPrivat
merge chunk
P
Q
R
S
Find suitable

BlocksIndex

For P
Size (0x11)
Flag (0x1)
SmallTagIndex (0x10)
Prevsize (0x7)
Size (0x18)
Flag (0x0)
SmallTagIndex (0x7)
Check

P->Size < ArraySize
P
Flink
Blink
Chunk header (0x180)
PreviousBlockPrivate
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Flink
Blink
Chunk header(0x110)
PreviousBlockPrivat
FlinkListHint[7]
Flink
Blink
Chunk header(0x70)
PreviousBlockPrivat
merge chunk
P
Q
R
S
Search

insert

point
Size (0x11)
Flag (0x1)
SmallTagIndex (0x10)
Prevsize (0x7)
Size (0x18)
Flag (0x0)
SmallTagIndex (0x7)
Check

P->size <
ListHead->Blink->Size

P->size <
ListHead->Flink->Size
P
Flink
Blink
Chunk header (0x180)
PreviousBlockPrivate
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Flink
Blink
Chunk header(0x110)
PreviousBlockPrivat
FlinkListHint[7]
Flink
Blink
Chunk header(0x70)
PreviousBlockPrivat
merge chunk
P
Q
R
S
Insert

linked list
Size (0x11)
Flag (0x1)
SmallTagIndex (0x10)
Prevsize (0x7)
Size (0x18)
Flag (0x0)
SmallTagIndex (0x7)
Check

S->Blink->Flink

== S
If not pass it will not 

abort

But it will not unlink
P
Flink
Blink
Chunk header (0x180)
PreviousBlockPrivate
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Flink
Blink
Chunk header(0x110)
PreviousBlockPrivat
FlinkListHint[7]
Flink
Blink
Chunk header(0x70)
PreviousBlockPrivat
merge chunk
P
Q
R
S
Update

ListHint
Size (0x11)
Flag (0x1)
SmallTagIndex (0x10)
Prevsize (0x7)
Size (0x18)
Flag (0x0)
SmallTagIndex (0x7)
If LintHint[0x18] == NULL

LintHint[0x18] = P
P
Flink
Blink
Chunk header (0x180)
PreviousBlockPrivate
FlinkListHint[0x18]
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Flink
Blink
Chunk header(0x110)
PreviousBlockPrivat
FlinkListHint[7]
Flink
Blink
Chunk header(0x70)
PreviousBlockPrivat
merge chunk
P
Q
R
S
Update

header
Size (0x11)
Flag (0x1)
SmallTagIndex (0x10)
Prevsize (0x7)
Size (0x18)
Flag (0x0)
SmallTagIndex (0x18)
SmallTagIndex = 

(size >> 8) ^

(size & 0xff) ^

Flag
P
Flink
Blink
Chunk header (0x180)
PreviousBlockPrivate
FlinkListHint[0x18]
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Flink
Blink
Chunk header(0x110)
PreviousBlockPrivat
FlinkListHint[7]
Flink
Blink
Chunk header(0x70)
PreviousBlockPrivat
merge chunk
P
Q
R
S
Encode

header
Size (0x11)
Flag (0x1)
SmallTagIndex (0x10)
Prevsize (0x7)
Size (0xda)
Flag (0xab)
SmallTagIndex (0x41)
P->header ^

_HEAP->Encoding
P
Flink
Blink
Chunk header (0x180)
PreviousBlockPrivate
FlinkListHint[0x18]
Nt heap
• Free (RtlpFreeHeap)

• Size > 0xff000 

• Check the linked list integrity and remove from _HEAP-
>VirtualAllocdBlocks

• Then use RtlpSecMemFreeVirtualMemory to munmap the chunk
Back-End Exploitation
• Unlink

• Similar bypass method in Linux.

• In short, use the behavior of removing nodes from linked list.

• It should be noted that the checksum must correct when decoding
chunk.

• The good thing is that Flink and Blink are directly point to UserData,
that is, you don’t have to forge chunk header
Back-End Exploitation
• Unlink

• Q->Blink->Flink = Q->Flink

• Q->Flink->Blink = Q->Blink
Flink
Blink
Chunk header (0x70)
PreviousBlockPrivateData
Flink
Blink
Chunk header (0x110)
PreviousBlockPrivateData
Flink
Blink
Chunk header (0x160)
PreviousBlockPrivateData
QP R
Back-End Exploitation
• Unlink

• Q->Blink->Flink = Q->Flink

• Q->Flink->Blink = Q->Blink
Flink
Blink
Chunk header (0x70)
PreviousBlockPrivateData
Flink
Blink
Chunk header (0x110)
PreviousBlockPrivateData
Flink
Blink
Chunk header (0x160)
PreviousBlockPrivateData
QP R
Back-End Exploitation
• Unlink

• Q->Blink->Flink = Q->Flink

• Q->Flink->Blink = Q->Blink
Flink
Blink
Chunk header (0x70)
PreviousBlockPrivateData
Flink
Blink
Chunk header (0x110)
PreviousBlockPrivateData
Flink
Blink
Chunk header (0x160)
PreviousBlockPrivateData
QP R
Back-End Exploitation
• Unlink

• Q->Blink->Flink = Q->Flink

• Q->Flink->Blink = Q->Blink
Flink
Blink
Chunk header (0x70)
PreviousBlockPrivateData
Flink
Blink
Chunk header (0x160)
PreviousBlockPrivateData
QP R
A
A
Chunk header (0x110)
PreviousBlockPrivate
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
Chunk header(0x110)
PreviousBlockPrivat
P
Q
R
S
P
Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
Data Pointer
ListHint
UAF
T
A
A
A
Chunk header (0x110)
PreviousBlockPrivate
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
Chunk header(0x110)
PreviousBlockPrivat
P
Q
R
S
P
Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
Data Pointer
ListHint
Free(Q)
T
A
Q
A
Chunk header (0x110)
PreviousBlockPrivate
Q
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
Chunk header(0x110)
PreviousBlockPrivat
P
Q
R
S
P
Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
Data Pointer
ListHint
T
A
A
ListHead
QListHint[0x11]
free(S)
Update ListHint
S
A
Chunk header (0x110)
PreviousBlockPrivate
Q
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
P
Q
R
S
P
Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
Data Pointer
ListHint
T
A
A
S
SListHint[0x11]
Q
ListHead
S
A
Chunk header (0x110)
PreviousBlockPrivate
Q
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
P
Q
R
S
P
Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
Data Pointer
ListHint
T
A
A
S
SListHint[0x11]
Q
ListHead
Let’s corrupt the heap !
S
A
Chunk header (0x110)
PreviousBlockPrivate
Q
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
P
Q
R
S
P
Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
Data Pointer
ListHint
T
A
&Q - 8
&Q
SListHint[0x11]
Q
ListHead
free(P)
S
A
Chunk header (0x110)
PreviousBlockPrivate
Q
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
P
Q
R
S
P
Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
Data Pointer
ListHint
T
A
&Q - 8
&Q
SListHint[0x11]
Q
ListHead
Check next chunk 

is free
S
A
Chunk header (0x110)
PreviousBlockPrivate
Q
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
P
Q
R
S
P
Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
Data Pointer
ListHint
T
A
&Q - 8
&Q
SListHint[0x11]
Q
ListHead
Decode 

chunk Q
S
A
Chunk header (0x110)
PreviousBlockPrivate
Q
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
P
Q
R
S
P
Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
Data Pointer
ListHint
T
A
&Q - 8
&Q
SListHint[0x11]
Q
ListHead(&Q-8)->Blink
==
(&Q)->Flink
== Q
S
A
Chunk header (0x110)
PreviousBlockPrivate
Q
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
P
Q
R
S
P
Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
Data Pointer
ListHint
T
A
&Q - 8
&Q
SListHint[0x11]
Q
ListHead(&Q-8)->Blink
==
(&Q)->Flink
== Q
Flink
Blink
S
A
Chunk header (0x110)
PreviousBlockPrivate
Q
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
P
Q
R
S
P
Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
Data Pointer
ListHint
T
A
&Q - 8
&Q
SListHint[0x11]
Q
ListHead(&Q-8)->Blink
==
(&Q)->Flink
== Q
Flink
Blink
S
A
Chunk header (0x110)
PreviousBlockPrivate
Q
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
P
Q
R
S
P
Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
Data Pointer
ListHint
T
A
&Q - 8
&Q
SListHint[0x11]
Q
ListHead*(&Q)
==
*(&Q)
== Q
S
A
Chunk header (0x110)
PreviousBlockPrivate
Q
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
P
Q
R
S
P
Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
Data Pointer
ListHint
T
A
&Q - 8
&Q
SListHint[0x11]
Q
ListHead*(&Q)
==
*(&Q)
== Q
Pass the check !!
S
A
Chunk header (0x110)
PreviousBlockPrivate
Q
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
P
Q
R
S
P
Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
Data Pointer
ListHint
T
A
&Q - 8
&Q
SListHint[0x11]
Q
ListHead
Find 

BlockIndex
S
A
Chunk header (0x110)
PreviousBlockPrivate
Q
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
P
Q
R
S
P
Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
Data Pointer
ListHint
T
A
&Q - 8
&Q
SListHint[0x11]
Q
ListHead
Check

ListHint[0x11]
S
A
Chunk header (0x110)
PreviousBlockPrivate
Q
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
P
Q
R
S
P
Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
Data Pointer
ListHint
T
A
&Q - 8
&Q
SListHint[0x11]
Q
ListHeadBecause S != Q

it will not replace

ListHint with Q
S
A
Chunk header (0x110)
PreviousBlockPrivate
Q
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
P
Q
R
S
P
Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
Data Pointer
ListHint
T
A
&Q - 8
&Q
SListHint[0x11]
Q
ListHead
Unlink !!
Q->Blink->Flink
= Q->Flink
Flink
Blink
S
A
Chunk header (0x110)
PreviousBlockPrivate
Q
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
P
Q
R
S
P
&Q - 8
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
ListHint
T
A
&Q - 8
&Q
SListHint[0x11]
Q
ListHeadUnlink !!
Q = &Q-8
S
A
Chunk header (0x110)
PreviousBlockPrivate
Q
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
P
Q
R
S
P
&Q - 8
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
ListHint
T
A
&Q - 8
&Q
SListHint[0x11]
Q
ListHead
Unlink !!
Q->Flink->Blink
= Q->Blink
Flink
Blink
S
A
Chunk header (0x110)
PreviousBlockPrivate
Q
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
P
Q
R
S
P
&Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
ListHint
T
A
&Q - 8
&Q
SListHint[0x11]
Q
ListHeadUnlink !!
Q = &Q
S
A
Chunk header (0x110)
PreviousBlockPrivate
Q
Chunk header (0x110)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
P
Q
R
S
P
&Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
ListHint
T
A
&Q - 8
&Q
SListHint[0x11]
Q
ListHeadUpdate 

chunk size
S
A
Chunk header (0x220)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
R
S
P
&Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
ListHint
T
A
SListHint[0x11]
Q
ListHeadUpdate 

chunk size
P
S
A
Chunk header (0x220)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
R
S
P
&Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
ListHint
T
A
SListHint[0x11]
Q
ListHead
Search insert point
P
S
A
Chunk header (0x220)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
R
S
P
&Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
ListHint
T
A
SListHint[0x11]
Q
ListHeadFind the last one
And decode it
P
S
A
Chunk header (0x220)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
R
S
P
&Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
ListHint
T
A
SListHint[0x11]
Q
ListHead
P
Find the first one
And decode it
S
A
Chunk header (0x220)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
R
S
P
&Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
ListHint
T
A
SListHint[0x11]
Q
ListHeadIt want to insert 

in front of A
P
S
A
Chunk header (0x220)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
R
S
P
&Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
ListHint
T
A
SListHint[0x11]
Q
ListHead
Check

A->Blink->Flink
== A
P
S
A
Chunk header (0x220)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
R
S
P
&Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
ListHint
T
A
SListHint[0x11]
Q
ListHead
Check

Q->Flink
== A
P
S
A
Chunk header (0x220)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
R
S
P
&Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
ListHint
T
A
SListHint[0x11]
Q
ListHeadFailed

but not aborting
It will not insert
P
S
A
Chunk header (0x220)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
R
S
P
&Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
ListHint
T
A
SListHint[0x11]
Q
ListHead
Update 

ListHint
P
S
A
Chunk header (0x220)
PreviousBlockPrivate
Chunk header(0x50)
PreviousBlockPrivat
S
Chunk header(0x110)
PreviousBlockPrivat
R
S
P
&Q
R
S
T
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
BlockIndex
ListHint
T
A
SListHint[0x11]
Q
ListHead
P
PListHint[0x22]
Back-End Exploitation
• Edit Q

• We can overwrite the pointer around
Q

• Finally, we can do arbitrary
memory reading and writing.
P
&Q
R
S
T
Back-End Exploitation
• Edit Q

• We can overwrite the pointer around
Q

• Finally, we can do arbitrary
memory reading and writing.

• We can overwrite Target 1 - 4
P
Target 1
Target 2
Target 3
Target 4
Back-End Exploitation
• After arbitrary memory reading and writing

• Leak

• ntdll.dll

• PebLdr

• binary address

• Kernel32.dll
Back-End Exploitation
• After arbitrary memory reading and writing

• Leak

• kernelbase

• KERNELBASE!BasepFilterInfo

• Stack address
Back-End Exploitation
• After arbitrary memory reading and writing

• Leak

• ntdll.dll

• _HEAP_LOCK

• _HEAP->LockVariable.Lock

• CriticalSection->DebugInfo

• Point to ntdll
Back-End Exploitation
• After arbitrary memory reading and writing

• Leak

• ntdll!PebLdr

• _PEB_LDR_DATA

• You can find all the location of dll

• However the drawback is that the last two byte may be 0.

• You can find the binary base first and find kernel32 from IAT
Back-End Exploitation
• After arbitrary memory reading and writing

• Leak

• BinaryBase

• Find kernel32 from IAT
Back-End Exploitation
• After arbitrary memory reading and writing

• Leak

• Kernel32

• Important dll

• Many useful function(CreateFile, ReadFile,WriteFile) 

• IAT

• Find kernelbase.dll
Back-End Exploitation
• After arbitrary memory reading and writing

• Leak

• kernelbase

• KERNELBASE!BasepFilterInfo

• Point to the structure on heap

• You have high probability to find stack pointer in the structure

• https://j00ru.vexillium.org/2016/07/disclosing-stack-data-from-the-default-
heap-on-windows/
Back-End Exploitation
• After arbitrary memory reading and writing

• Leak

• If there is no stack address in BasepFilterInfo

• You can find stack address in TEB which usually locate one page
before or after PEB
Back-End Exploitation
• After arbitrary memory reading and writing

• Write 

• Return address

• Control RIP

• ROP to VirtualProtect/VirtualAlloc

• Jmp to shellcode
Windows memory allocator
• Nt Heap

• Back-End

• Front-End
Windows memory allocator
• Nt Heap

• Front-End

• Commonly use in win10 

• LowFragmentationHeap

• Only enable in non-debug mode

• Size < 0x4000
Windows memory allocator
• Nt Heap

• Front-End

• Data Structure

• Memory allocation mechanism
Windows memory allocator
• Nt Heap

• Front-End

• Data Structure

• Memory allocation mechanism
LFH
• FrontEndHeap (_LFH_HEAP)

• HEAP (_HEAP)

• Point to the corresponding _HEAP

• Buckets (_HEAP_BUCKET)

• An array to find the malloc size
corresponding to the block size
…
Heap0x18
0x2a4
…
Buckets
SegmentInfoArray
…
…
LocalData
0x4a8
0xcc0
LFH
• FrontEndHeap (_LFH_HEAP)

• SegmentInfoArray
(_HEAP_LOCAL_SEGMENT_INFO)

• _HEAP_LOCAL_SEGMENT_INFO array

• Different sizes correspond to different
Segment_info structures, to manage the
information of the corresponding Subsegment.

• LocalData (_HEAP_LOCAL_DATA)

• One of the fields points to the LFH itself, which
is usually used to retrieve the LFH.
…
Heap0x18
…
Buckets
SegmentInfoArray
…
…
LocalData
0x2a4
0x4a8
0xcc0
LFH
• Buckets (_HEAP_BUCKET)

• BlockUnits

• The block size >> 4

• SizeIndex

• Required size >> 4
BlockUnits
Buckets
SizeIndex
…
BlockUnits
SizeIndex
…
…
…
Buckets[0]
Buckets[1]
LFH
• SegmentInfoArray
(_HEAP_LOCAL_SEGMENT_INFO)

• LocalData (_HEAP_LOCAL_DATA)

• Corresponding to _LFH_HEAP-
>LocalData to retrieve _LFH_HEAP
from SegmentInfo

• BucketIndex

• The index in Buckets[]
LocalData
SegmentInfoArray[x]
ActiveSubsegment
CachedItems
…
BucketIndex
…
LFH
• SegmentInfoArray
(_HEAP_LOCAL_SEGMENT_INFO)

• ActiveSubsegment (_HEAP_SUBSEGMENT)

• Very important structure

• Corresponding to the assigned Subsegment

• To maintain Userblock

• Record number of remaining chunk

• The maximum number of chunk in
Userblock
SegmentInfoArray[x]
LocalData
ActiveSubsegment
CachedItems
…
BucketIndex
…
LFH
• SegmentInfoArray
(_HEAP_LOCAL_SEGMENT_INFO)

• CachedItems (_HEAP_SUBSEGMENT)

• _HEAP_SUBSEGMENT array

• Stored the available Subsegment
correspond to the SegmentInfo

• When the ActiveSubsegment used
up, it will reload from CachedItems.
SegmentInfoArray[x]
LocalData
ActiveSubsegment
CachedItems
…
BucketIndex
…
LFH
…
Heap
…
…
…
LocalData
0x18
0x2a4
0x4a8
0xcc0
Buckets[x]
SegmentInfoArray[x]
BlockUnits
SizeIndex
…
_LFH_HEAP
_HEAP_BUCKET
_HEAP_LOCAL_SEGMENT_INFO
…
EncodeFlagMask
Encoding
…
BlocksIndex
…
FreeList
…
FrontEndHeap
…
FrontEndHeapUsageData
…
_HEAP
LocalData
ActiveSubsegment
CachedItems
…
BucketIndex
…
LFH
• ActiveSubsegment (_HEAP_SUBSEGMENT)

• LocalInfo
(_HEAP_LOCAL_SEGMENT_INFO)

• Point back to corresponding
_HEAP_LOCAL_SEGMENT_INFO
LocalInfo
ActiveSubsegment
UserBlock
…
AggregateExchg
BlockSzie
BlockCount
…
SizeIndex
…
0x0
0x8
0x20
0x24
0x28
0x2a
LFH
• ActiveSubsegment (_HEAP_SUBSEGMENT)

• UserBlock (_HEAP_USERDATA_HEADER)

• Memory pool of LFH

• That is, the location of chunk (Block)

• Some metadata will manage the
chunks at the beginning of UserBlock

• Important !
LocalInfo
ActiveSubsegment
UserBlock
…
AggregateExchg
BlockSzie
BlockCount
…
SizeIndex
…
0x0
0x8
0x20
0x24
0x28
0x2a
LFH
• ActiveSubsegment (_HEAP_SUBSEGMENT)

• AggregateExchg (_INTERLOCK_SEQ)

• To indicate remaining amount of
freed chunk in Userblock

• LFH uses it to determine if it should
allocate from this UserBlock
LocalInfo
ActiveSubsegment
UserBlock
…
AggregateExchg
BlockSzie
BlockCount
…
SizeIndex
…
0x0
0x8
0x20
0x24
0x28
0x2a
LFH
• ActiveSubsegment (_HEAP_SUBSEGMENT)

• BlockSize

• The size of each block (chunk) in the
UserBlock

• BlockCount

• The number of blocks in the UserBlock

• SizeIndex

• The SizeIndex corresponding to the
UserBlock
LocalInfo
ActiveSubsegment
UserBlock
…
AggregateExchg
BlockSzie
BlockCount
…
SizeIndex
…
0x0
0x8
0x20
0x24
0x28
0x2a
LFH
• AggregateExchg (_INTERLOCK_SEQ)

• Depth

• The remaining amount of freed chunk in
UserBlock

• Lock

• Just a Lock
AggregateExchg
Depth0x0
Hint (15bit)
Lock (1bit)
0x8
LFH
• UserBlock (_HEAP_USERDATA_HEADER)

• SubSegment

• Point to corresponding Subsegment

• EncodeOffsets

• To verify chunk header integrity

• BusyBitmap

• Indicate which chunk is being used
SubSegment
UserBlock
…
EncodedOffsets
BusyBitmap
0x8
0x18
0x20
chunk header
chunk header
chunk header
LFH
• UserBlock (_HEAP_USERDATA_HEADER)

• Block (chunk)

• The allocated memory return to user.
SubSegment
UserBlock
…
EncodedOffsets
BusyBitmap
0x8
0x18
0x20
chunk header
chunk header
chunk header
LFH
• _HEAP_ENTRY (chunk)

• SubSegmentCode

• Encoded metadata to retrieve the
location of UserBlock

• PreviousSize

• The index of the chunk in UserBlock
User Data
PreviousBlockPrivateData
(8byte)
SubSegmentCode (4byte)
PreviousSize (2byte)
SegmentOffset (1byte)
Unusedbyte (1byte)
Inused
LFH
• _HEAP_ENTRY (chunk)

• Unusedbyte

• Unusedbyte & 0x80 must be true

• To indicate it is LFH chunk
User Data
PreviousBlockPrivateData
(8byte)
SubSegmentCode (4byte)
PreviousSize (2byte)
SegmentOffset (1byte)
Unusedbyte (1byte)
Inused
LFH
• _HEAP_ENTRY (chunk)

• Unusedbytes

• Must be 0x80

• To indicate it is LFH freed chunk
User Data
PreviousBlockPrivateData
(8byte)
SubSegmentCode (4byte)
PreviousSize (2byte)
SegmentOffset (1byte)
Unusedbyte (1byte)
Freed
LFH
• Remark

• About EncodedOffsets

• EncodedOffsets is xor of the following values

• (sizeof(userblock header) | (BlockUnit*0x10 << 16))

• LFHkey

• Userblock address

• _LFH_HEAP address
LFH
• Remark

• About LFH header encoding

• The chunk header will xor with following values when initialization

• _HEAP address

• LFHkey

• Chunk address >> 4

• ((chunk address) - (UserBlock address)) << 12
LFH
…
Heap
…
…
…
LocalData
Buckets[x]
SegmentInfoArray[x]
BlockUnits
SizeIndex
…
_LFH_HEAP
_HEAP_BUCKET
_HEAP_LOCAL_SEGMENT_INFO
LocalInfo
UserBlock
…
AggregateExchg
BlockSzie
BlockCount
…
SizeIndex
…
Depth
Hint (15bit)
Lock (1bit)
_HEAP_SUBSEGMENT
SubSegment
…
EncodedOffsets
BusyBitmap
chunk header
chunk header
chunk header
_INTERLOCK_SEQ
_HEAP_USERDATA_HEADER
LocalData
ActiveSubsegment
CachedItems
…
BucketIndex
…
Windows memory allocator
• Nt Heap

• Front-End

• Data Structure

• Memory allocation mechanism
Windows memory allocator
• Nt Heap

• Front-End

• Initialization

• If FrontEndHeapUsageData[x] & 0x1f > 0x10, LFH will be initialized in next allocation

• It will ExtendFrontEndUsageData and create new BlocksIndex (0x80-0x400)

• Create FrontEndHeap

• Initialize SegmentInfoArrays[idx]

• It will start using front-end allocator from next allocation with same size
Windows memory allocator
• LFH(Initialization)

• malloc(0x40) * 16
FrontEndHeapUsageData
…
0x210
The range of LFH is index
from 0x0 to 0x80
Windows memory allocator
• LFH(Initialization)

• malloc(0x40) (17th)
FrontEndHeapUsageData
…
0x231 0x231 & 0x1f > 0x10
Windows memory allocator
• LFH(Initialization)
FrontEndHeapUsageData
…
0x231
heap->CompatibilityFlag |= 0x20000000
After setting this flag, it will initialize

LFH in the next allocation
Windows memory allocator
…
EncodeFlagMask
Encoding
…
BlocksIndex
…
FreeList
…
FrontEndHeap
…
FrontEndHeapUsageData
…
_HEAP
FrontEndHeapUsageData
0x231
BlocksIndex
ExtendedLookup
ArraySize
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex
ListHead
ListsInUseUlong
ListHint
Windows memory allocator
• LFH(Initialization)

• malloc(0x40) (18th)

• ExtendFrontEndUsageData and create new BlocksIndex
(0x80-0x400)

• The range of LFH is modified to index from 0x0 to
0x400

• Create and initialize FrontEndHeap (mmap)

• initialize SegmentInfoArrays[idx]

• Assign SegmentInfoArrays[BucketIndex] to
segmentInfo
FrontEndHeapUsageData
…
0x4
Windows memory allocator
…
EncodeFlagMask
Encoding
…
BlocksIndex
…
FreeList
…
FrontEndHeap
…
FrontEndHeapUsageData
…
_HEAP
FrontEndHeapUsageData
0x251 0x4
BlocksIndex
ExtendedLookup
ArraySize(0x80)
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex (0x0)
ListHead
ListsInUseUlong
ListHint
BlocksIndex
ExtendedLookup
ArraySize (0x400)
…
ItemCount (4 bytes)
OutofRangeItems (4 bytes)
BaseIndex (0x80)
ListHead
ListsInUseUlong
ListHint
Windows memory allocator
…
Heap
…
…
…
LocalData
Buckets[x]
SegmentInfoArray[x]
BlockUnits
SizeIndex
…
_LFH_HEAP
_HEAP_BUCKET
_HEAP_LOCAL_SEGMENT_INFO
LocalData
ActiveSubsegment
CachedItems
…
BucketIndex
…
…
EncodeFlagMask
Encoding
…
BlocksIndex
…
FreeList
…
FrontEndHeap
…
FrontEndHeapUsageData
…
_HEAP
FrontEndHeapUsageData
0x251 0x4
Windows memory allocator
• LFH(Initialization)

• malloc(0x40) (19th)

• Allocate Userblock and initialize every
chunk.

• Set the corresponding ActiveSubsegment
to the UserBlock

• Randomly return chunk to user
Userblock metadata
Chunk 00
Chunk 01
…
Chunk MaxBlockCount
…
Heap
…
…
…
LocalData
Buckets[x]
SegmentInfoArray[x]
BlockUnits
SizeIndex
…
_LFH_HEAP
_HEAP_BUCKET
_HEAP_LOCAL_SEGMENT_INFO
LocalInfo
UserBlock
…
AggregateExchg
BlockSzie
BlockCount
…
SizeIndex
…
Depth
Hint (15bit)
Lock (1bit)
_HEAP_SUBSEGMENT
SubSegment
…
EncodedOffsets
BusyBitmap
chunk header
chunk header
chunk header
_INTERLOCK_SEQ
_HEAP_USERDATA_HEADER
LocalData
ActiveSubsegment
CachedItems
…
BucketIndex
…
Windows memory allocator
• LFH

• Allocate (RtlpLowFragHeapAllocFromContext)

• It will check whether ActiveSubsegment has chunk available.

• Check ActiveSubsegment->depth

• If not, it will search from CachedItem, and replace ActiveSubsegment
with CachedItem’s SubSegment if available.
…
Heap
…
…
…
LocalData
Buckets[x]
SegmentInfoArray[x]
BlockUnits
SizeIndex
…
_LFH_HEAP
_HEAP_BUCKET
_HEAP_LOCAL_SEGMENT_INFO
LocalInfo
UserBlock
…
AggregateExchg
BlockSzie
BlockCount
…
SizeIndex
…
Depth
Hint (15bit)
Lock (1bit)
_HEAP_SUBSEGMENT
SubSegment
…
EncodedOffsets
BusyBitmap
chunk header
chunk header
chunk header
_INTERLOCK_SEQ
_HEAP_USERDATA_HEADER
LocalData
ActiveSubsegment
CachedItems
…
BucketIndex
…
Find SegementIfoArray by bucket->SizeIndex
…
Heap
…
…
…
LocalData
Buckets[x]
SegmentInfoArray[x]
BlockUnits
SizeIndex
…
_LFH_HEAP
_HEAP_BUCKET
_HEAP_LOCAL_SEGMENT_INFO
LocalInfo
UserBlock
…
AggregateExchg
BlockSzie
BlockCount
…
SizeIndex
…
Depth
Hint (15bit)
Lock (1bit)
_HEAP_SUBSEGMENT
SubSegment
…
EncodedOffsets
BusyBitmap
chunk header
chunk header
chunk header
_INTERLOCK_SEQ
_HEAP_USERDATA_HEADER
LocalData
ActiveSubsegment
CachedItems
…
BucketIndex
…
Find SegementIfoArray by bucket->SizeIndex
…
Heap
…
…
…
LocalData
Buckets[x]
SegmentInfoArray[x]
BlockUnits
SizeIndex
…
_LFH_HEAP
_HEAP_BUCKET
_HEAP_LOCAL_SEGMENT_INFO
LocalInfo
UserBlock
…
AggregateExchg
BlockSzie
BlockCount
…
SizeIndex
…
Depth
Hint (15bit)
Lock (1bit)
_HEAP_SUBSEGMENT
SubSegment
…
EncodedOffsets
BusyBitmap
chunk header
chunk header
chunk header
_INTERLOCK_SEQ
_HEAP_USERDATA_HEADER
LocalData
ActiveSubsegment
CachedItems
…
BucketIndex
…
Check ActiveSubsegment
…
Heap
…
…
…
LocalData
Buckets[x]
SegmentInfoArray[x]
BlockUnits
SizeIndex
…
_LFH_HEAP
_HEAP_BUCKET
_HEAP_LOCAL_SEGMENT_INFO
LocalInfo
UserBlock
…
AggregateExchg
BlockSzie
BlockCount
…
SizeIndex
…
Depth
Hint (15bit)
Lock (1bit)
_HEAP_SUBSEGMENT
SubSegment
…
EncodedOffsets
BusyBitmap
chunk header
chunk header
chunk header
_INTERLOCK_SEQ
_HEAP_USERDATA_HEADER
LocalData
ActiveSubsegment
CachedItems
…
BucketIndex
…
Check ActiveSubsegment
…
Heap
…
…
…
LocalData
Buckets[x]
SegmentInfoArray[x]
BlockUnits
SizeIndex
…
_LFH_HEAP
_HEAP_BUCKET
_HEAP_LOCAL_SEGMENT_INFO
LocalInfo
UserBlock
…
AggregateExchg
BlockSzie
BlockCount
…
SizeIndex
…
Depth
Hint (15bit)
Lock (1bit)
_HEAP_SUBSEGMENT
SubSegment
…
EncodedOffsets
BusyBitmap
chunk header
chunk header
chunk header
_INTERLOCK_SEQ
_HEAP_USERDATA_HEADER
LocalData
ActiveSubsegment
CachedItems
…
BucketIndex
…
Check ArgregateExchg.Depth not 0
…
Heap
…
…
…
LocalData
Buckets[x]
SegmentInfoArray[x]
BlockUnits
SizeIndex
…
_LFH_HEAP
_HEAP_BUCKET
_HEAP_LOCAL_SEGMENT_INFO
LocalInfo
UserBlock
…
AggregateExchg
BlockSzie
BlockCount
…
SizeIndex
…
Depth
Hint (15bit)
Lock (1bit)
_HEAP_SUBSEGMENT
SubSegment
…
EncodedOffsets
BusyBitmap
chunk header
chunk header
chunk header
_INTERLOCK_SEQ
_HEAP_USERDATA_HEADER
LocalData
ActiveSubsegment
CachedItems
…
BucketIndex
…
Check ArgregateExchg.Depth not 0
…
Heap
…
…
…
LocalData
Buckets[x]
SegmentInfoArray[x]
BlockUnits
SizeIndex
…
_LFH_HEAP
_HEAP_BUCKET
_HEAP_LOCAL_SEGMENT_INFO
LocalInfo
UserBlock
…
AggregateExchg
BlockSzie
BlockCount
…
SizeIndex
…
Depth
Hint (15bit)
Lock (1bit)
_HEAP_SUBSEGMENT
SubSegment
…
EncodedOffsets
BusyBitmap
chunk header
chunk header
chunk header
_INTERLOCK_SEQ
_HEAP_USERDATA_HEADER
LocalData
ActiveSubsegment
CachedItems
…
BucketIndex
…
Check Userblock
Windows memory allocator
• LFH

• Allocate (RtlpLowFragHeapAllocFromContext)

• Retrieve RtlpLowFragHeapRandomData[x] 

• It will retrieve the value from RtlpLowFragHeapRandomData[x+1] next
round.

• x is 1 byte, x = rand() %256 after 256 rounds

• RtlpLowFragHeapRandomData is a 256-byte array filled with random value

• The range of random value is 0x0 - 0x7f
Windows memory allocator
• LFH

• Allocate (RtlpLowFragHeapAllocFromContext)

• Finally, the index of chunk is

• RtlpLowFragHeapRandomData[x]*maxidx >> 7 

• Take the nearest chunk if collision

• Check (unused byte & 0x3f) !=0 (indicate chunk is freed)

• Modify index and unused byte in header and return to user
….
….
….
SubSegment
…
EncodedOffsets
BusyBitmap
chunk header
chunk header
chunk header
_HEAP_USERDATA_HEADER
• Get an index

• RtlpLowFragHeapRandomData[x]*maxidx >> 7

• Check if the BusyBitmap correspond to index is
0

• Return the chunk if true

• Otherwise take the next nearest chunk
chunk header
….
….
….
SubSegment
…
EncodedOffsets
BusyBitmap
chunk header
chunk header
chunk header
_HEAP_USERDATA_HEADER
• If the index is inused 

• Take next nearest chunk

• Set the bitmap to 1
chunk header
….
….
….
SubSegment
…
EncodedOffsets
BusyBitmap
chunk header
chunk header
chunk header
_HEAP_USERDATA_HEADER
chunk header
• If the index is inused 

• Take next nearest chunk

• Set the bitmap to 1
Windows memory allocator
• LFH

• Free (RtlFreeHeap)

• Update unused byte in chunk header

• Find the index of the Chunk and reset Userblock->BusyBitmap

• Update ActiveSubsegment->AggregateExchg

• If the free chunk is not belong to ActiveSubsegment, it will try to put
into cachedItems
LocalInfo
UserBlock
…
AggregateExchg
BlockSzie
BlockCount
…
SizeIndex
…
Depth
Hint (15bit)
Lock (1bit)
_HEAP_SUBSEGMENT
SubSegment
…
EncodedOffsets
BusyBitmap
chunk header
chunk header
chunk header
_INTERLOCK_SEQ
_HEAP_USERDATA_HEADER
• Free

• Find UserBlock by chunk
header
LocalInfo
UserBlock
…
AggregateExchg
BlockSzie
BlockCount
…
SizeIndex
…
Depth
Hint (15bit)
Lock (1bit)
_HEAP_SUBSEGMENT
SubSegment
…
EncodedOffsets
BusyBitmap
chunk header
chunk header
chunk header
_INTERLOCK_SEQ
_HEAP_USERDATA_HEADER
• Free

• Find UserBlock by chunk
header

• Find corresponding
SubSegment
LocalInfo
UserBlock
…
AggregateExchg
BlockSzie
BlockCount
…
SizeIndex
…
Depth
Hint (15bit)
Lock (1bit)
_HEAP_SUBSEGMENT
SubSegment
…
EncodedOffsets
BusyBitmap
chunk header
chunk header
chunk header
_INTERLOCK_SEQ
_HEAP_USERDATA_HEADER
• Free

• Find UserBlock by chunk
header

• Find corresponding
SubSegment

• Assign unused byte to
0x80

• Clear the bitmap

• Update AggregateExchg
LocalInfo
UserBlock
…
AggregateExchg
BlockSzie
BlockCount
…
SizeIndex
…
Depth
Hint (15bit)
Lock (1bit)
_HEAP_SUBSEGMENT
SubSegment
…
EncodedOffsets
BusyBitmap
chunk header
chunk header
chunk header
_INTERLOCK_SEQ
_HEAP_USERDATA_HEADER
• Free

• Find UserBlock by chunk
header

• Find corresponding
SubSegment

• Assign unused byte to
0x80

• Clear the bitmap

• Update AggregateExchg
LocalInfo
UserBlock
…
AggregateExchg
BlockSzie
BlockCount
…
SizeIndex
…
Depth
Hint (15bit)
Lock (1bit)
_HEAP_SUBSEGMENT
SubSegment
…
EncodedOffsets
BusyBitmap
chunk header
chunk header
chunk header
_INTERLOCK_SEQ
_HEAP_USERDATA_HEADER
• Free

• Find UserBlock by chunk
header

• Find corresponding
SubSegment

• Assign unused byte to
0x80

• Clear the bitmap

• Update AggregateExchg
LocalInfo
UserBlock
…
AggregateExchg
BlockSzie
BlockCount
…
SizeIndex
…
_HEAP_SUBSEGMENT
SubSegment
…
EncodedOffsets
BusyBitmap
chunk header
chunk header
chunk header
_HEAP_USERDATA_HEADERLocalData
ActiveSubsegment
CachedItems
…
BucketIndex
…
LocalInfo
UserBlock
…
AggregateExchg
BlockSzie
BlockCount
…
SizeIndex
…
_HEAP_SUBSEGMENT
Depth (1)
Hint (15bit)
Lock (1bit)
_INTERLOCK_SEQ
CachedItems
ActiveSubsegment 

is not equivalent to

SubSegment of free

chunk
LocalInfo
UserBlock
…
AggregateExchg
BlockSzie
BlockCount
…
SizeIndex
…
_HEAP_SUBSEGMENT
SubSegment
…
EncodedOffsets
BusyBitmap
chunk header
chunk header
chunk header
_HEAP_USERDATA_HEADERLocalData
ActiveSubsegment
CachedItems
…
BucketIndex
…
LocalInfo
UserBlock
…
AggregateExchg
BlockSzie
BlockCount
…
SizeIndex
…
_HEAP_SUBSEGMENT
Depth (1)
Hint (15bit)
Lock (1bit)
_INTERLOCK_SEQ
CachedItems
CachedItems[0]
LFH Exploitation
• Reuse attack

• If we have Use After Free

• Because of randomness of LFH, we can not predict the next chunk.

• We can fill up UserBlock and then free one of them, it will return
same chunk in next allocation with same size.
LFH Exploitation
• normal case

• malloc(sizeof(A))
Userblock header
LFH Exploitation
• normal case

• malloc(sizeof(B))
Userblock header
fptr
LFH Exploitation
• normal case

• free(A)
Userblock header B
fptr
LFH Exploitation
• normal case

• malloc(sizeof(A))
Userblock header B
fptr
LFH Exploitation
• normal case

• malloc(sizeof(B))
Userblock header B
fptr
LFH Exploitation
• normal case

• malloc(sizeof(B))
Userblock header B
fptr
Hard to use
LFH Exploitation
• Reuse attack 

• malloc(sizeof(A))
Userblock header
LFH Exploitation
• Reuse attack 

• malloc(sizeof(B)) x 6
Userblock header
fptr
LFH Exploitation
• Reuse attack 

• free(A)
Userblock header
B
B B B
fptr B B
LFH Exploitation
• Reuse attack 

• malloc(sizeof(B))
Userblock header
B
B B B
fptr B B
LFH Exploitation
• Reuse attack 

• A->fptr
Userblock header
B
B B B
fptr B B
LFH Exploitation
• Reuse attack 

• A->fptr
Userblock header
B
B B B
fptr B B
Hijack the control flow !
Reference
• https://github.com/saaramar/
35C3_Modern_Windows_Userspace_Exploitation

• http://illmatics.com/Understanding_the_LFH.pdf

• https://github.com/saaramar/Deterministic_LFH
Thank you for listening
angelboy@chroot.org @scwuaptx

Windows 10 Nt Heap Exploitation (English version)

  • 1.
  • 2.
    Windows Heap Exploitation •Windows Memory Allocator • NT heap • BackEnd • Exploitation • FrontEnd • LowFragmentationHeap • Exploitation
  • 3.
    Windows memory allocator •Memory allocator in Win10 is very complicated, there are mainly two kind of memory allocator. • Nt Heap • Default memory allocator • SegmentHeap • New memory allocator in Win10 • Some system process and UWP will use.
  • 4.
    Windows memory allocator •Nt Heap can be divided into • Back-End • Front-End • LowFragmentationHeap • In order to prevent memory fragmentation problem, it will enable LFH after allocating a certain number of the same size of chunk. (e.g. Continuously allocate same size of chunks 18 times) • Size <= 0x4000
  • 5.
    Windows memory allocator •The slide based on win10 (1809) • OS build - 17763.379 • Later versions may not be completely correct • 1903 is very smimilar • The structure of heap often change a lot. In the new release version, you need to trace the structure and workflow by yourself.
  • 6.
    Windows memory allocator •Overview Kernel32.dll HeapAlloc HeapFree msvcrt140.dll malloc free ntdll.dll RtlAllocateHeap RtlFreeHeap ntdll.dll RtlpAllocateHeap RtlpFreeHeap ntdll.dll RtlpLowFragHeapAlloc RtlpLowFragHeapFree Kernel Front-End Back-End
  • 7.
    Windows memory allocator •When we call malloc if LFH is disable. Kernel32.dll HeapAlloc HeapFree msvcrt140.dll malloc free ntdll.dll RtlAllocateHeap RtlFreeHeap ntdll.dll RtlpAllocateHeap RtlpFreeHeap Kernel Back-End
  • 8.
    Windows memory allocator •When we call malloc at the first time or when 
 memory pool called userblock of LFH is used 
 up if LFH is enable. • It will use back-end allocator to allocate 
 userblock for LFH. Kernel32.dll HeapAlloc HeapFree msvcrt140.dll malloc free ntdll.dll RtlAllocateHeap RtlFreeHeap ntdll.dll RtlpAllocateHeap RtlpFreeHeap ntdll.dll RtlpLowFragHeapAlloc RtlpLowFragHeapFree Kernel Front-End Back-End
  • 9.
    Windows memory allocator •When we allocate the same size after enabling LFH,
 it will use front-end allocator and use userblock in 
 LFH Kernel32.dll HeapAlloc HeapFree msvcrt140.dll malloc free ntdll.dll RtlAllocateHeap RtlFreeHeap ntdll.dll RtlpLowFragHeapAlloc RtlpLowFragHeapFree Front-End
  • 10.
    Windows memory allocator •HEAP can be divide into • Process Heap • Default heap • The heap shared by the entire process, it will be used when you use windows API • Stored in _PEB • Used in CRT function • Stored in crt_heap
  • 11.
    Windows memory allocator •HEAP can be divide into • Private heap • Create by HeapCreate
  • 12.
    Windows memory allocator •Core data structure • _HEAP_ENTRY (chunk) • The basic structure of memory allocator • It is different in front-end and back-end, but it use same name.
  • 13.
    Windows memory allocator •_HEAP_ENTRY (chunk) data chunk header chunk header data _HEAP chunk header . . . malloc return value
  • 14.
    Windows memory allocator •Core data structure • _HEAP • The core structure of memory allocator, it is used to manage the heap. • Each heap correspond to a _HEAP structure, usually at the beginning of the heap
  • 15.
    Windows memory allocator •_HEAP • EncodeFlagMask • It will be set to 0x100000 after heap initialization. It’s used to determine whether to encode the header of the chunk in the heap • Encoding (_HEAP_ENTRY) • It’s used to do xor with chunk header … EncodeFlagMask0x7c 0x80 0x138 Encoding … BlocksIndex … FreeList … FrontEndHeap … 0x150 0x198 FrontEndHeapUsageData0x1a8 …
  • 16.
    Windows memory allocator •_HEAP • BlocksIndex (_HEAP_LIST_LOOKUP_) • The core structure in back-end allocator used to manage chunks. • FreeList (_HEAP_ENTRY) • A linked list used to collect all freed chunk in back-end • Similar as unsorted bin in libc • Sorted list … EncodeFlagMask0x7c 0x80 0x138 Encoding … BlocksIndex … FreeList … FrontEndHeap … 0x150 0x198 FrontEndHeapUsageData0x1a8 …
  • 17.
    Windows memory allocator •_HEAP • FrontEndHeap • A pointer pointed to the structure of front-end heap • FrontEndHeapUsageData • Record the number of chunks used by various sizes. • When it reaches a certain level, it will enable the Front-End allocator of the corresponding chunk. … EncodeFlagMask0x7c 0x80 0x138 Encoding … BlocksIndex … FreeList … FrontEndHeap … 0x150 0x198 FrontEndHeapUsageData0x1a8 …
  • 18.
    Windows memory allocator •Nt Heap • Back-End • Front-End
  • 19.
    Nt heap • NtHeap • Back-End • Data structure • Memory allocation mechanism
  • 20.
    Windows memory allocator •_HEAP_ENTRY (chunk) • Divided into • Allocated chunk • Freed chunk • VirtualAlloc chunk
  • 21.
    Nt heap • _HEAP_ENTRY(chunk) • PreviousBlockPrivateData • The data of the previous chunk, because chunk must be aligned to a multiple of 0x10 • Size • The size of chunk. • The value is right shift by 4 bits. User Data PreviousBlockPrivateData (8byte) Size (2byte) Flag (1byte) SmallTagIndex (1byte) PreviousSize (2byte) SegmentOffset (1byte) Unusedbyte (1byte) Inused
  • 22.
    Nt heap • _HEAP_ENTRY(chunk) • Flag • Indicates whether the chunk is busy • SmallTagIndex • Checksum is the xor of the first three bytes • It will be used to verify the header of chunk User Data PreviousBlockPrivateData (8byte) Size (2byte) Flag (1byte) SmallTagIndex (1byte) PreviousSize (2byte) SegmentOffset (1byte) Unusedbyte (1byte) Inused xor
  • 23.
    Nt heap • _HEAP_ENTRY(chunk) • PreviousSize • The size of previous chunk. • The value is right shift by 4 bits. User Data PreviousBlockPrivateData (8byte) Size (2byte) Flag (1byte) SmallTagIndex (1byte) PreviousSize (2byte) SegmentOffset (1byte) Unusedbyte (1byte) Inused
  • 24.
    Nt heap • _HEAP_ENTRY(chunk) • Unusedbyte • The remaining size of chunk after allocation • It can be used to determine the status of chunk (front-end or back-end) • User Data • The data used by user User Data PreviousBlockPrivateData (8byte) Size (2byte) Flag (1byte) SmallTagIndex (1byte) PreviousSize (2byte) SegmentOffset (1byte) Unusedbyte (1byte) Inused
  • 25.
    Nt heap • _HEAP_ENTRY(chunk) • Flink • Point to the next chunk in the linked list • Blink • Point to the previous chunk in the linked list • Unusedbyte • Must be zero User Data PreviousBlockPrivateData (8byte) Size (2byte) Flag (1byte) SmallTagIndex (1byte) PreviousSize (2byte) SegmentOffset (1byte) Unusedbyte (1byte) freed Flink Blink
  • 26.
    Nt heap • _HEAP_VIRTUAL_ALLOC_ENTRY(mmap chunk) • Flink • Point to the next chunk of VirtualAlloc • Blink • Point to the previous chunk of VirtualAlloc User Data Size (2byte) Flag (1byte) SmallTagIndex (1byte) PreviousSize (2byte) SegmentOffset (1byte) Unusedbyte (1byte) inused Flink Blink …
  • 27.
    Nt heap • _HEAP_VIRTUAL_ALLOC_ENTRY(mmap chunk) • Size • The size here refers to unused size without shifting • Unusedbyte • Must be 4 User Data Size (2byte) Flag (1byte) SmallTagIndex (1byte) PreviousSize (2byte) SegmentOffset (1byte) Unusedbyte (1byte) inused Flink Blink …
  • 28.
    Nt heap • FreeLists(_HEAP_ENTRY) • After free a chunk, it will be placed in FreeLists and will be inserted in FreeLists according to the size. Flink Blink Chunk header (0x70) PreviousBlockPrivateData Flink Blink Chunk header (0x110) PreviousBlockPrivateData Flink Blink Chunk header (0x160) PreviousBlockPrivateData
  • 29.
    Nt heap Flink Blink Chunk header(0x70) PreviousBlockPrivateData Flink Blink Chunk header (0x110) PreviousBlockPrivateData Flink Blink Chunk header (0x160) PreviousBlockPrivateDat … EncodeFlagMask0x7c 0x80 0x138 Encoding … BlocksIndex … FreeList … FrontEndHeap … 0x150 0x198 FrontEndHeapUsageData0x1a8 … Flink Blink _HEAP
  • 30.
    Nt heap • Remark •About header encoding • Every chunk header will be xor with _HEAP->Encoding • It will be verified when it is decoded • The verification method is to check whether the checksum is correct. • The xor of the first three bytes and compare with fourth byte.
  • 31.
    Nt heap • BlocksIndex(_HEAP_LIST_LOOKUP) • It is mainly used to manage freed chunks of various sizes, so that it can quickly find suitable chunks.
  • 32.
    Nt heap • BlocksIndex(_HEAP_LIST_LOOKUP) • ExtendedLookup (_HEAP_LIST_LOOKUP) • Point to next ExtendedLookup • The next BlocksIndex will manage larger chunks • ArraySize • The max chunk size that will be managed by the BlocksIndex. • The first BlocksIndex ArraySize will by 0x80 (Actually is 0x800) BlocksIndex ExtendedLookup0x0 ArraySize0x8 … ItemCount (4 bytes) OutofRangeItems (4 bytes) 0x10 0x14 BaseIndex0x18 ListHead ListsInUseUlong ListHint 0x20 0x28 0x30
  • 33.
    Nt heap • BlocksIndex(_HEAP_LIST_LOOKUP) • ItemCount • The number of chunks in the BlocksIndex • OutofRangeItems • The number of chunks that exceed the size managed by this BlocksIndex BlocksIndex ExtendedLookup0x0 ArraySize0x8 … ItemCount (4 bytes) OutofRangeItems (4 bytes) 0x10 0x14 BaseIndex0x18 ListHead ListsInUseUlong ListHint 0x20 0x28 0x30
  • 34.
    Nt heap • BlocksIndex(_HEAP_LIST_LOOKUP) • BaseIndex • The starting index of the chunk in the Blocksindex. • It is used to find a suitable freed chunk in ListHint • The next BaseIndex of next BlocksIndex is the maximum value of current BaseIndex. • ListHead (_HEAP_ENTRY) • FreeList 的 Head BlocksIndex ExtendedLookup0x0 ArraySize0x8 … ItemCount (4 bytes) OutofRangeItems (4 bytes) 0x10 0x14 BaseIndex0x18 ListHead ListsInUseUlong ListHint 0x20 0x28 0x30
  • 35.
    Nt heap • BlocksIndex(_HEAP_LIST_LOOKUP) • ListsInUseUlong • Used to determine whether there is a suitable chunk in ListHint, which is a bitmap • ListHint • Important structure in back-end. • A pointer array which point to the corresponding size of chunk array. • The goal is to find the suitable chunk faster. • The interval of chunk size is 0x10 BlocksIndex ExtendedLookup0x0 ArraySize0x8 … ItemCount (4 bytes) OutofRangeItems (4 bytes) 0x10 0x14 BaseIndex0x18 ListHead ListsInUseUlong ListHint 0x20 0x28 0x30
  • 36.
    … EncodeFlagMask Encoding … BlocksIndex … FreeList … FrontEndHeap … FrontEndHeapUsageDat … _HEAP BlocksIndex ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems(4 bytes) BaseIndex ListHead ListsInUseUlong ListHint Flink Blink Flink Blink Chunk header (0x70) PreviousBlockPrivate Flink Blink Chunk header (0x110) PreviousBlockPrivate Flink Blink Chunk header(0x160) PreviousBlockPrivat ListHint Flink Flink Flink ListHint[7] ListHint[0x11] ListHint[0x16] 000001…1…1000000000 ListsInUseUlong
  • 37.
    Nt heap • NtHeap • Back-End • Data structure • Memory allocation mechanism
  • 38.
    Nt heap • Allocate(RtlpAllocateHeap) • It can be divided into • Size <= 0x4000 • 0x4000 < size <= 0xff000 • Size > 0xff000
  • 39.
    Nt heap • Allocate(RtlpAllocateHeap) • Size <= 0x4000 • The memory allocation is implemented in RtlpAllocateHeap • First, it will check whether FrontEndHeapStatusBitmap corresponds to the size is set (which indicates if LFH is enabled) • If not, add 0x21 to the corresponding FrontEndHeapUsageData • And check if the value exceeds 0xff00 or (& 0x1f) is larger than 0x10 • If true, it will enable LFH.
  • 40.
    Nt heap • Allocate(RtlpAllocateHeap) • Size <= 0x4000 • Next, it will check whether ListHint corresponds to the size has a value. If true, it will take the chunk in ListHint first. • If there is a suitable chunk on the ListHint, remove the ListHint, and see whether the size of chunk's Flink is the same size. • If true, replace the ListHint with Flink • If not, clear the ListHint. • Finally, unlink the chunk to remove the chunk from the linked list and return it to user.
  • 41.
    Nt heap • Allocate(RtlpAllocateHeap) • Size <= 0x4000 • If there is no suitable chunk • Search from the larger ListHint. If it finds a large one, remove it from the ListHint. • Then split the chunk and insert remaining chunk into freelist and put into ListHint.
  • 42.
    Nt heap • Allocate(RtlpAllocateHeap) • Size <= 0x4000 • If there is no suitable chunk in FreeList • Try ExtendHeap to increase heap space, then take the chunk from it
  • 43.
    Nt heap • Allocate(RtlpAllocateHeap) • 0x4000 < size <= 0xff000 • Everything is similar except without LFH-related operations
  • 44.
    Nt heap • Allocate(RtlpAllocateHeap) • Size > 0xff000 (VirtualMemoryThreshold << 4) • Use ZwAllocateVirtualMemory directly • Similar to mmap, it will allocate large memory block , and will be inserted into _HEAP->VirtualAllocdBlocks • _HEAP->VirtualAllocdBlocks is linked list used to collect all VirtualAllocate chunk in back-end
  • 45.
    Nt heap • Free(RtlpFreeHeap) • Divide into • Size <= 0xff000 • Size > 0xff000
  • 46.
    Nt heap • Free(RtlpFreeHeap) • Size <= 0xff000 • It will check the alignment first and use the unused byte to determine the chunk state • If LFH is disable, decrease 1 to the corresponding FrontEndHeapUsageData • Then it will check whether the previous chunk or next chunk is freed. If it is freed, then coalesce them. • Unlink the coalesced chunk and remove from ListHint.
  • 47.
    Nt heap • Free(RtlpFreeHeap) • Size <= 0xff000 • After the coalescence, update size & prevsize, and check whether it is the front or last of FreeList. If true, then insert into FreeList. If not, insert into ListHint and update it. • It will check the linked list integrity when inserting • But this check won’t terminate the process.
  • 48.
    ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems(4 bytes) BaseIndex ListHead ListsInUseUlong ListHint Flink Blink Flink Blink Chunk header (0x70) PreviousBlockPrivate Chunk header (0x110) PreviousBlockPrivate Chunk header(0x110) PreviousBlockPrivat FlinkListHint[7] Flink Blink Chunk header(0x70) PreviousBlockPrivat merge chunk P Q R S Free(Q) Size (0x11) Flag (0x1) SmallTagIndex (0x10) Prevsize (0x7)
  • 49.
    ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems(4 bytes) BaseIndex ListHead ListsInUseUlong ListHint Flink Blink Flink Blink Chunk header (0x70) PreviousBlockPrivate Chunk header (0x110) PreviousBlockPrivate Chunk header(0x110) PreviousBlockPrivat FlinkListHint[7] Flink Blink Chunk header(0x70) PreviousBlockPrivat merge chunk P Q R S Find Prevchunk Size (0x11) Flag (0x1) SmallTagIndex (0x10) Prevsize (0x7) Prevchunk = Chunk address - (prevsize << 4)
  • 50.
    ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems(4 bytes) BaseIndex ListHead ListsInUseUlong ListHint Flink Blink Flink Blink Chunk header (0x70) PreviousBlockPrivate Chunk header (0x110) PreviousBlockPrivate Chunk header(0x110) PreviousBlockPrivat FlinkListHint[7] Flink Blink Chunk header(0x70) PreviousBlockPrivat merge chunk P Q R S Find Prevchunk Size (0x11) Flag (0x1) SmallTagIndex (0x10) Prevsize (0x7) Prevchunk = chunk addr - 0x70
  • 51.
    ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems(4 bytes) BaseIndex ListHead ListsInUseUlong ListHint Flink Blink Flink Blink Chunk header (0x70) PreviousBlockPrivate Chunk header (0x110) PreviousBlockPrivate Chunk header(0x110) PreviousBlockPrivat FlinkListHint[7] Flink Blink Chunk header(0x70) PreviousBlockPrivat merge chunk P Q R S Decode prevchunk
 header Size (0x11) Flag (0x1) SmallTagIndex (0x10) Prevsize (0x7) Size (0x7) Flag (0x0) SmallTagIndex (0x7)
  • 52.
    ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems(4 bytes) BaseIndex ListHead ListsInUseUlong ListHint Flink Blink Flink Blink Chunk header (0x70) PreviousBlockPrivate Chunk header (0x110) PreviousBlockPrivate Chunk header(0x110) PreviousBlockPrivat FlinkListHint[7] Flink Blink Chunk header(0x70) PreviousBlockPrivat merge chunk P Q R S Check checksum Size (0x11) Flag (0x1) SmallTagIndex (0x10) Prevsize (0x7) Size (0x7) Flag (0x0) SmallTagIndex (0x7) SmallTagInex ==
 (size & 0xff) xor (size >> 8) xor Flag
  • 53.
    ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems(4 bytes) BaseIndex ListHead ListsInUseUlong ListHint Flink Blink Flink Blink Chunk header (0x70) PreviousBlockPrivate Chunk header (0x110) PreviousBlockPrivate Chunk header(0x110) PreviousBlockPrivat FlinkListHint[7] Flink Blink Chunk header(0x70) PreviousBlockPrivat merge chunk P Q R S Check checksum Size (0x11) Flag (0x1) SmallTagIndex (0x10) Prevsize (0x7) Size (0x7) Flag (0x0) SmallTagIndex (0x7) 7 ==
 0x7^0x0^0x0
  • 54.
    ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems(4 bytes) BaseIndex ListHead ListsInUseUlong ListHint Flink Blink Flink Blink Chunk header (0x70) PreviousBlockPrivate Chunk header (0x110) PreviousBlockPrivate Chunk header(0x110) PreviousBlockPrivat FlinkListHint[7] Flink Blink Chunk header(0x70) PreviousBlockPrivat merge chunk P Q R S Check
 linked list Size (0x11) Flag (0x1) SmallTagIndex (0x10) Prevsize (0x7) Size (0x7) Flag (0x0) SmallTagIndex (0x7) P->Flink->Blink == 
 P->Blink->Flink == 
 P
  • 55.
    ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems(4 bytes) BaseIndex ListHead ListsInUseUlong ListHint Flink Blink Flink Blink Chunk header (0x70) PreviousBlockPrivate Chunk header (0x110) PreviousBlockPrivate Chunk header(0x110) PreviousBlockPrivat FlinkListHint[7] Flink Blink Chunk header(0x70) PreviousBlockPrivat merge chunk P Q R S Find 
 BlocksIndex Size (0x11) Flag (0x1) SmallTagIndex (0x10) Prevsize (0x7) Size (0x7) Flag (0x0) SmallTagIndex (0x7)
  • 56.
    ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems(4 bytes) BaseIndex ListHead ListsInUseUlong ListHint Flink Blink Flink Blink Chunk header (0x70) PreviousBlockPrivate Chunk header (0x110) PreviousBlockPrivate Chunk header(0x110) PreviousBlockPrivat FlinkListHint[7] Flink Blink Chunk header(0x70) PreviousBlockPrivat merge chunk P Q R S Find suitable 
 BlocksIndex Size (0x11) Flag (0x1) SmallTagIndex (0x10) Prevsize (0x7) Size (0x7) Flag (0x0) SmallTagIndex (0x7) Check P->size < ArraySize
  • 57.
    ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems(4 bytes) BaseIndex ListHead ListsInUseUlong ListHint Flink Blink Flink Blink Chunk header (0x70) PreviousBlockPrivate Chunk header (0x110) PreviousBlockPrivate Chunk header(0x110) PreviousBlockPrivat FlinkListHint[7] Flink Blink Chunk header(0x70) PreviousBlockPrivat merge chunk P Q R S Check
 ListHint Size (0x11) Flag (0x1) SmallTagIndex (0x10) Prevsize (0x7) Size (0x7) Flag (0x0) SmallTagIndex (0x7) Check ListHint[7] == Prevchunk
  • 58.
    ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems(4 bytes) BaseIndex ListHead ListsInUseUlong ListHint Flink Blink Flink Blink Chunk header (0x70) PreviousBlockPrivate Chunk header (0x110) PreviousBlockPrivate Chunk header(0x110) PreviousBlockPrivat FlinkListHint[7] Flink Blink Chunk header(0x70) PreviousBlockPrivat merge chunk P Q R S Check
 ListHint Size (0x11) Flag (0x1) SmallTagIndex (0x10) Prevsize (0x7) Size (0x7) Flag (0x0) SmallTagIndex (0x7) Check prevchunk->Flink == ListHead If true, update
 ListHint
  • 59.
    ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems(4 bytes) BaseIndex ListHead ListsInUseUlong ListHint Flink Blink Flink Blink Chunk header (0x70) PreviousBlockPrivate Chunk header (0x110) PreviousBlockPrivate Chunk header(0x110) PreviousBlockPrivat FlinkListHint[7] Flink Blink Chunk header(0x70) PreviousBlockPrivat merge chunk P Q R S Check
 ListHint[7]->Flink Size (0x11) Flag (0x1) SmallTagIndex (0x10) Prevsize (0x7) Size (0x7) Flag (0x0) SmallTagIndex (0x7) decode prevchunk->Flink And check the checksum Because the previous slide is not true, it
 will check the size
 of Flink
  • 60.
    ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems(4 bytes) BaseIndex ListHead ListsInUseUlong ListHint Flink Blink Flink Blink Chunk header (0x70) PreviousBlockPrivate Chunk header (0x110) PreviousBlockPrivate Chunk header(0x110) PreviousBlockPrivat FlinkListHint[7] Flink Blink Chunk header(0x70) PreviousBlockPrivat merge chunk P Q R S Check
 ListHint[7]->Flink Size (0x11) Flag (0x1) SmallTagIndex (0x10) Prevsize (0x7) Size (0x7) Flag (0x0) SmallTagIndex (0x7) Size == 
 prevchunk->Flink->size If true, replace ListHint
 with Flink

  • 61.
    ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems(4 bytes) BaseIndex ListHead ListsInUseUlong ListHint Flink Blink Flink Blink Chunk header (0x70) PreviousBlockPrivate Chunk header (0x110) PreviousBlockPrivate Chunk header(0x110) PreviousBlockPrivat FlinkListHint[7] Flink Blink Chunk header(0x70) PreviousBlockPrivat merge chunk P Q R S Update
 ListHint[7] Size (0x11) Flag (0x1) SmallTagIndex (0x10) Prevsize (0x7) Size (0x7) Flag (0x0) SmallTagIndex (0x7) ListHint[7] =
 prevchunk->Flink
  • 62.
    ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems(4 bytes) BaseIndex ListHead ListsInUseUlong ListHint Flink Blink Flink Blink Chunk header (0x70) PreviousBlockPrivate Chunk header (0x110) PreviousBlockPrivate Chunk header(0x110) PreviousBlockPrivat FlinkListHint[7] Flink Blink Chunk header(0x70) PreviousBlockPrivat merge chunk P Q R S Unlink
 prevchunk Size (0x11) Flag (0x1) SmallTagIndex (0x10) Prevsize (0x7) Size (0x7) Flag (0x0) SmallTagIndex (0x7) Prevchunk->Blink->Flink
 = Prevchunk->Flink
 Prevchunk->Flink->Blink = Prevchunk->Blink
  • 63.
    ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems(4 bytes) BaseIndex ListHead ListsInUseUlong ListHint Flink Blink Chunk header(0x110) PreviousBlockPrivat FlinkListHint[7] Flink Blink Chunk header(0x70) PreviousBlockPrivat merge chunk P Q R S Update
 prevchunk
 and next chunk Size (0x11) Flag (0x1) SmallTagIndex (0x10) Prevsize (0x7) Size (0x18) Flag (0x0) SmallTagIndex (0x7) Prevchunk->size =
 0x11 + Prevchunk->size(7) R->Prevsize = 0x18 P Flink Blink Chunk header (0x180) PreviousBlockPrivate
  • 64.
    ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems(4 bytes) BaseIndex ListHead ListsInUseUlong ListHint Flink Blink Chunk header(0x110) PreviousBlockPrivat FlinkListHint[7] Flink Blink Chunk header(0x70) PreviousBlockPrivat merge chunk P Q R S Check
 next chunk Size (0x11) Flag (0x1) SmallTagIndex (0x10) Prevsize (0x7) Size (0x18) Flag (0x0) SmallTagIndex (0x7) Check if next chunk
 is freed P Flink Blink Chunk header (0x180) PreviousBlockPrivate
  • 65.
    ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems(4 bytes) BaseIndex ListHead ListsInUseUlong ListHint Flink Blink Chunk header(0x110) PreviousBlockPrivat FlinkListHint[7] Flink Blink Chunk header(0x70) PreviousBlockPrivat merge chunk P Q R S Find suitable
 BlocksIndex
 For P Size (0x11) Flag (0x1) SmallTagIndex (0x10) Prevsize (0x7) Size (0x18) Flag (0x0) SmallTagIndex (0x7) Check
 P->Size < ArraySize P Flink Blink Chunk header (0x180) PreviousBlockPrivate
  • 66.
    ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems(4 bytes) BaseIndex ListHead ListsInUseUlong ListHint Flink Blink Chunk header(0x110) PreviousBlockPrivat FlinkListHint[7] Flink Blink Chunk header(0x70) PreviousBlockPrivat merge chunk P Q R S Search
 insert
 point Size (0x11) Flag (0x1) SmallTagIndex (0x10) Prevsize (0x7) Size (0x18) Flag (0x0) SmallTagIndex (0x7) Check
 P->size < ListHead->Blink->Size
 P->size < ListHead->Flink->Size P Flink Blink Chunk header (0x180) PreviousBlockPrivate
  • 67.
    ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems(4 bytes) BaseIndex ListHead ListsInUseUlong ListHint Flink Blink Chunk header(0x110) PreviousBlockPrivat FlinkListHint[7] Flink Blink Chunk header(0x70) PreviousBlockPrivat merge chunk P Q R S Insert
 linked list Size (0x11) Flag (0x1) SmallTagIndex (0x10) Prevsize (0x7) Size (0x18) Flag (0x0) SmallTagIndex (0x7) Check
 S->Blink->Flink
 == S If not pass it will not 
 abort
 But it will not unlink P Flink Blink Chunk header (0x180) PreviousBlockPrivate
  • 68.
    ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems(4 bytes) BaseIndex ListHead ListsInUseUlong ListHint Flink Blink Chunk header(0x110) PreviousBlockPrivat FlinkListHint[7] Flink Blink Chunk header(0x70) PreviousBlockPrivat merge chunk P Q R S Update
 ListHint Size (0x11) Flag (0x1) SmallTagIndex (0x10) Prevsize (0x7) Size (0x18) Flag (0x0) SmallTagIndex (0x7) If LintHint[0x18] == NULL
 LintHint[0x18] = P P Flink Blink Chunk header (0x180) PreviousBlockPrivate FlinkListHint[0x18]
  • 69.
    ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems(4 bytes) BaseIndex ListHead ListsInUseUlong ListHint Flink Blink Chunk header(0x110) PreviousBlockPrivat FlinkListHint[7] Flink Blink Chunk header(0x70) PreviousBlockPrivat merge chunk P Q R S Update
 header Size (0x11) Flag (0x1) SmallTagIndex (0x10) Prevsize (0x7) Size (0x18) Flag (0x0) SmallTagIndex (0x18) SmallTagIndex = 
 (size >> 8) ^
 (size & 0xff) ^
 Flag P Flink Blink Chunk header (0x180) PreviousBlockPrivate FlinkListHint[0x18]
  • 70.
    ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems(4 bytes) BaseIndex ListHead ListsInUseUlong ListHint Flink Blink Chunk header(0x110) PreviousBlockPrivat FlinkListHint[7] Flink Blink Chunk header(0x70) PreviousBlockPrivat merge chunk P Q R S Encode
 header Size (0x11) Flag (0x1) SmallTagIndex (0x10) Prevsize (0x7) Size (0xda) Flag (0xab) SmallTagIndex (0x41) P->header ^
 _HEAP->Encoding P Flink Blink Chunk header (0x180) PreviousBlockPrivate FlinkListHint[0x18]
  • 71.
    Nt heap • Free(RtlpFreeHeap) • Size > 0xff000 • Check the linked list integrity and remove from _HEAP- >VirtualAllocdBlocks • Then use RtlpSecMemFreeVirtualMemory to munmap the chunk
  • 72.
    Back-End Exploitation • Unlink •Similar bypass method in Linux. • In short, use the behavior of removing nodes from linked list. • It should be noted that the checksum must correct when decoding chunk. • The good thing is that Flink and Blink are directly point to UserData, that is, you don’t have to forge chunk header
  • 73.
    Back-End Exploitation • Unlink •Q->Blink->Flink = Q->Flink • Q->Flink->Blink = Q->Blink Flink Blink Chunk header (0x70) PreviousBlockPrivateData Flink Blink Chunk header (0x110) PreviousBlockPrivateData Flink Blink Chunk header (0x160) PreviousBlockPrivateData QP R
  • 74.
    Back-End Exploitation • Unlink •Q->Blink->Flink = Q->Flink • Q->Flink->Blink = Q->Blink Flink Blink Chunk header (0x70) PreviousBlockPrivateData Flink Blink Chunk header (0x110) PreviousBlockPrivateData Flink Blink Chunk header (0x160) PreviousBlockPrivateData QP R
  • 75.
    Back-End Exploitation • Unlink •Q->Blink->Flink = Q->Flink • Q->Flink->Blink = Q->Blink Flink Blink Chunk header (0x70) PreviousBlockPrivateData Flink Blink Chunk header (0x110) PreviousBlockPrivateData Flink Blink Chunk header (0x160) PreviousBlockPrivateData QP R
  • 76.
    Back-End Exploitation • Unlink •Q->Blink->Flink = Q->Flink • Q->Flink->Blink = Q->Blink Flink Blink Chunk header (0x70) PreviousBlockPrivateData Flink Blink Chunk header (0x160) PreviousBlockPrivateData QP R
  • 77.
    A A Chunk header (0x110) PreviousBlockPrivate Chunkheader (0x110) PreviousBlockPrivate Chunk header(0x50) PreviousBlockPrivat Chunk header(0x110) PreviousBlockPrivat P Q R S P Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex Data Pointer ListHint UAF T A
  • 78.
    A A Chunk header (0x110) PreviousBlockPrivate Chunkheader (0x110) PreviousBlockPrivate Chunk header(0x50) PreviousBlockPrivat Chunk header(0x110) PreviousBlockPrivat P Q R S P Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex Data Pointer ListHint Free(Q) T A
  • 79.
    Q A Chunk header (0x110) PreviousBlockPrivate Q Chunkheader (0x110) PreviousBlockPrivate Chunk header(0x50) PreviousBlockPrivat Chunk header(0x110) PreviousBlockPrivat P Q R S P Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex Data Pointer ListHint T A A ListHead QListHint[0x11] free(S) Update ListHint
  • 80.
    S A Chunk header (0x110) PreviousBlockPrivate Q Chunkheader (0x110) PreviousBlockPrivate Chunk header(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat P Q R S P Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex Data Pointer ListHint T A A S SListHint[0x11] Q ListHead
  • 81.
    S A Chunk header (0x110) PreviousBlockPrivate Q Chunkheader (0x110) PreviousBlockPrivate Chunk header(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat P Q R S P Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex Data Pointer ListHint T A A S SListHint[0x11] Q ListHead Let’s corrupt the heap !
  • 82.
    S A Chunk header (0x110) PreviousBlockPrivate Q Chunkheader (0x110) PreviousBlockPrivate Chunk header(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat P Q R S P Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex Data Pointer ListHint T A &Q - 8 &Q SListHint[0x11] Q ListHead free(P)
  • 83.
    S A Chunk header (0x110) PreviousBlockPrivate Q Chunkheader (0x110) PreviousBlockPrivate Chunk header(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat P Q R S P Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex Data Pointer ListHint T A &Q - 8 &Q SListHint[0x11] Q ListHead Check next chunk 
 is free
  • 84.
    S A Chunk header (0x110) PreviousBlockPrivate Q Chunkheader (0x110) PreviousBlockPrivate Chunk header(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat P Q R S P Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex Data Pointer ListHint T A &Q - 8 &Q SListHint[0x11] Q ListHead Decode 
 chunk Q
  • 85.
    S A Chunk header (0x110) PreviousBlockPrivate Q Chunkheader (0x110) PreviousBlockPrivate Chunk header(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat P Q R S P Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex Data Pointer ListHint T A &Q - 8 &Q SListHint[0x11] Q ListHead(&Q-8)->Blink == (&Q)->Flink == Q
  • 86.
    S A Chunk header (0x110) PreviousBlockPrivate Q Chunkheader (0x110) PreviousBlockPrivate Chunk header(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat P Q R S P Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex Data Pointer ListHint T A &Q - 8 &Q SListHint[0x11] Q ListHead(&Q-8)->Blink == (&Q)->Flink == Q Flink Blink
  • 87.
    S A Chunk header (0x110) PreviousBlockPrivate Q Chunkheader (0x110) PreviousBlockPrivate Chunk header(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat P Q R S P Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex Data Pointer ListHint T A &Q - 8 &Q SListHint[0x11] Q ListHead(&Q-8)->Blink == (&Q)->Flink == Q Flink Blink
  • 88.
    S A Chunk header (0x110) PreviousBlockPrivate Q Chunkheader (0x110) PreviousBlockPrivate Chunk header(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat P Q R S P Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex Data Pointer ListHint T A &Q - 8 &Q SListHint[0x11] Q ListHead*(&Q) == *(&Q) == Q
  • 89.
    S A Chunk header (0x110) PreviousBlockPrivate Q Chunkheader (0x110) PreviousBlockPrivate Chunk header(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat P Q R S P Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex Data Pointer ListHint T A &Q - 8 &Q SListHint[0x11] Q ListHead*(&Q) == *(&Q) == Q Pass the check !!
  • 90.
    S A Chunk header (0x110) PreviousBlockPrivate Q Chunkheader (0x110) PreviousBlockPrivate Chunk header(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat P Q R S P Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex Data Pointer ListHint T A &Q - 8 &Q SListHint[0x11] Q ListHead Find 
 BlockIndex
  • 91.
    S A Chunk header (0x110) PreviousBlockPrivate Q Chunkheader (0x110) PreviousBlockPrivate Chunk header(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat P Q R S P Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex Data Pointer ListHint T A &Q - 8 &Q SListHint[0x11] Q ListHead Check
 ListHint[0x11]
  • 92.
    S A Chunk header (0x110) PreviousBlockPrivate Q Chunkheader (0x110) PreviousBlockPrivate Chunk header(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat P Q R S P Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex Data Pointer ListHint T A &Q - 8 &Q SListHint[0x11] Q ListHeadBecause S != Q
 it will not replace
 ListHint with Q
  • 93.
    S A Chunk header (0x110) PreviousBlockPrivate Q Chunkheader (0x110) PreviousBlockPrivate Chunk header(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat P Q R S P Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex Data Pointer ListHint T A &Q - 8 &Q SListHint[0x11] Q ListHead Unlink !! Q->Blink->Flink = Q->Flink Flink Blink
  • 94.
    S A Chunk header (0x110) PreviousBlockPrivate Q Chunkheader (0x110) PreviousBlockPrivate Chunk header(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat P Q R S P &Q - 8 R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex ListHint T A &Q - 8 &Q SListHint[0x11] Q ListHeadUnlink !! Q = &Q-8
  • 95.
    S A Chunk header (0x110) PreviousBlockPrivate Q Chunkheader (0x110) PreviousBlockPrivate Chunk header(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat P Q R S P &Q - 8 R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex ListHint T A &Q - 8 &Q SListHint[0x11] Q ListHead Unlink !! Q->Flink->Blink = Q->Blink Flink Blink
  • 96.
    S A Chunk header (0x110) PreviousBlockPrivate Q Chunkheader (0x110) PreviousBlockPrivate Chunk header(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat P Q R S P &Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex ListHint T A &Q - 8 &Q SListHint[0x11] Q ListHeadUnlink !! Q = &Q
  • 97.
    S A Chunk header (0x110) PreviousBlockPrivate Q Chunkheader (0x110) PreviousBlockPrivate Chunk header(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat P Q R S P &Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex ListHint T A &Q - 8 &Q SListHint[0x11] Q ListHeadUpdate 
 chunk size
  • 98.
    S A Chunk header (0x220) PreviousBlockPrivate Chunkheader(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat R S P &Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex ListHint T A SListHint[0x11] Q ListHeadUpdate 
 chunk size P
  • 99.
    S A Chunk header (0x220) PreviousBlockPrivate Chunkheader(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat R S P &Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex ListHint T A SListHint[0x11] Q ListHead Search insert point P
  • 100.
    S A Chunk header (0x220) PreviousBlockPrivate Chunkheader(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat R S P &Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex ListHint T A SListHint[0x11] Q ListHeadFind the last one And decode it P
  • 101.
    S A Chunk header (0x220) PreviousBlockPrivate Chunkheader(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat R S P &Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex ListHint T A SListHint[0x11] Q ListHead P Find the first one And decode it
  • 102.
    S A Chunk header (0x220) PreviousBlockPrivate Chunkheader(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat R S P &Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex ListHint T A SListHint[0x11] Q ListHeadIt want to insert 
 in front of A P
  • 103.
    S A Chunk header (0x220) PreviousBlockPrivate Chunkheader(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat R S P &Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex ListHint T A SListHint[0x11] Q ListHead Check
 A->Blink->Flink == A P
  • 104.
    S A Chunk header (0x220) PreviousBlockPrivate Chunkheader(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat R S P &Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex ListHint T A SListHint[0x11] Q ListHead Check
 Q->Flink == A P
  • 105.
    S A Chunk header (0x220) PreviousBlockPrivate Chunkheader(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat R S P &Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex ListHint T A SListHint[0x11] Q ListHeadFailed
 but not aborting It will not insert P
  • 106.
    S A Chunk header (0x220) PreviousBlockPrivate Chunkheader(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat R S P &Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex ListHint T A SListHint[0x11] Q ListHead Update 
 ListHint P
  • 107.
    S A Chunk header (0x220) PreviousBlockPrivate Chunkheader(0x50) PreviousBlockPrivat S Chunk header(0x110) PreviousBlockPrivat R S P &Q R S T ExtendedLookup ArraySize … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex ListHead ListsInUseUlong ListHint BlockIndex ListHint T A SListHint[0x11] Q ListHead P PListHint[0x22]
  • 108.
    Back-End Exploitation • EditQ • We can overwrite the pointer around Q • Finally, we can do arbitrary memory reading and writing. P &Q R S T
  • 109.
    Back-End Exploitation • EditQ • We can overwrite the pointer around Q • Finally, we can do arbitrary memory reading and writing. • We can overwrite Target 1 - 4 P Target 1 Target 2 Target 3 Target 4
  • 110.
    Back-End Exploitation • Afterarbitrary memory reading and writing • Leak • ntdll.dll • PebLdr • binary address • Kernel32.dll
  • 111.
    Back-End Exploitation • Afterarbitrary memory reading and writing • Leak • kernelbase • KERNELBASE!BasepFilterInfo • Stack address
  • 112.
    Back-End Exploitation • Afterarbitrary memory reading and writing • Leak • ntdll.dll • _HEAP_LOCK • _HEAP->LockVariable.Lock • CriticalSection->DebugInfo • Point to ntdll
  • 113.
    Back-End Exploitation • Afterarbitrary memory reading and writing • Leak • ntdll!PebLdr • _PEB_LDR_DATA • You can find all the location of dll • However the drawback is that the last two byte may be 0. • You can find the binary base first and find kernel32 from IAT
  • 114.
    Back-End Exploitation • Afterarbitrary memory reading and writing • Leak • BinaryBase • Find kernel32 from IAT
  • 115.
    Back-End Exploitation • Afterarbitrary memory reading and writing • Leak • Kernel32 • Important dll • Many useful function(CreateFile, ReadFile,WriteFile) • IAT • Find kernelbase.dll
  • 116.
    Back-End Exploitation • Afterarbitrary memory reading and writing • Leak • kernelbase • KERNELBASE!BasepFilterInfo • Point to the structure on heap • You have high probability to find stack pointer in the structure • https://j00ru.vexillium.org/2016/07/disclosing-stack-data-from-the-default- heap-on-windows/
  • 117.
    Back-End Exploitation • Afterarbitrary memory reading and writing • Leak • If there is no stack address in BasepFilterInfo • You can find stack address in TEB which usually locate one page before or after PEB
  • 118.
    Back-End Exploitation • Afterarbitrary memory reading and writing • Write • Return address • Control RIP • ROP to VirtualProtect/VirtualAlloc • Jmp to shellcode
  • 119.
    Windows memory allocator •Nt Heap • Back-End • Front-End
  • 120.
    Windows memory allocator •Nt Heap • Front-End • Commonly use in win10 • LowFragmentationHeap • Only enable in non-debug mode • Size < 0x4000
  • 121.
    Windows memory allocator •Nt Heap • Front-End • Data Structure • Memory allocation mechanism
  • 122.
    Windows memory allocator •Nt Heap • Front-End • Data Structure • Memory allocation mechanism
  • 123.
    LFH • FrontEndHeap (_LFH_HEAP) •HEAP (_HEAP) • Point to the corresponding _HEAP • Buckets (_HEAP_BUCKET) • An array to find the malloc size corresponding to the block size … Heap0x18 0x2a4 … Buckets SegmentInfoArray … … LocalData 0x4a8 0xcc0
  • 124.
    LFH • FrontEndHeap (_LFH_HEAP) •SegmentInfoArray (_HEAP_LOCAL_SEGMENT_INFO) • _HEAP_LOCAL_SEGMENT_INFO array • Different sizes correspond to different Segment_info structures, to manage the information of the corresponding Subsegment. • LocalData (_HEAP_LOCAL_DATA) • One of the fields points to the LFH itself, which is usually used to retrieve the LFH. … Heap0x18 … Buckets SegmentInfoArray … … LocalData 0x2a4 0x4a8 0xcc0
  • 125.
    LFH • Buckets (_HEAP_BUCKET) •BlockUnits • The block size >> 4 • SizeIndex • Required size >> 4 BlockUnits Buckets SizeIndex … BlockUnits SizeIndex … … … Buckets[0] Buckets[1]
  • 126.
    LFH • SegmentInfoArray (_HEAP_LOCAL_SEGMENT_INFO) • LocalData(_HEAP_LOCAL_DATA) • Corresponding to _LFH_HEAP- >LocalData to retrieve _LFH_HEAP from SegmentInfo • BucketIndex • The index in Buckets[] LocalData SegmentInfoArray[x] ActiveSubsegment CachedItems … BucketIndex …
  • 127.
    LFH • SegmentInfoArray (_HEAP_LOCAL_SEGMENT_INFO) • ActiveSubsegment(_HEAP_SUBSEGMENT) • Very important structure • Corresponding to the assigned Subsegment • To maintain Userblock • Record number of remaining chunk • The maximum number of chunk in Userblock SegmentInfoArray[x] LocalData ActiveSubsegment CachedItems … BucketIndex …
  • 128.
    LFH • SegmentInfoArray (_HEAP_LOCAL_SEGMENT_INFO) • CachedItems(_HEAP_SUBSEGMENT) • _HEAP_SUBSEGMENT array • Stored the available Subsegment correspond to the SegmentInfo • When the ActiveSubsegment used up, it will reload from CachedItems. SegmentInfoArray[x] LocalData ActiveSubsegment CachedItems … BucketIndex …
  • 129.
  • 130.
    LFH • ActiveSubsegment (_HEAP_SUBSEGMENT) •LocalInfo (_HEAP_LOCAL_SEGMENT_INFO) • Point back to corresponding _HEAP_LOCAL_SEGMENT_INFO LocalInfo ActiveSubsegment UserBlock … AggregateExchg BlockSzie BlockCount … SizeIndex … 0x0 0x8 0x20 0x24 0x28 0x2a
  • 131.
    LFH • ActiveSubsegment (_HEAP_SUBSEGMENT) •UserBlock (_HEAP_USERDATA_HEADER) • Memory pool of LFH • That is, the location of chunk (Block) • Some metadata will manage the chunks at the beginning of UserBlock • Important ! LocalInfo ActiveSubsegment UserBlock … AggregateExchg BlockSzie BlockCount … SizeIndex … 0x0 0x8 0x20 0x24 0x28 0x2a
  • 132.
    LFH • ActiveSubsegment (_HEAP_SUBSEGMENT) •AggregateExchg (_INTERLOCK_SEQ) • To indicate remaining amount of freed chunk in Userblock • LFH uses it to determine if it should allocate from this UserBlock LocalInfo ActiveSubsegment UserBlock … AggregateExchg BlockSzie BlockCount … SizeIndex … 0x0 0x8 0x20 0x24 0x28 0x2a
  • 133.
    LFH • ActiveSubsegment (_HEAP_SUBSEGMENT) •BlockSize • The size of each block (chunk) in the UserBlock • BlockCount • The number of blocks in the UserBlock • SizeIndex • The SizeIndex corresponding to the UserBlock LocalInfo ActiveSubsegment UserBlock … AggregateExchg BlockSzie BlockCount … SizeIndex … 0x0 0x8 0x20 0x24 0x28 0x2a
  • 134.
    LFH • AggregateExchg (_INTERLOCK_SEQ) •Depth • The remaining amount of freed chunk in UserBlock • Lock • Just a Lock AggregateExchg Depth0x0 Hint (15bit) Lock (1bit) 0x8
  • 135.
    LFH • UserBlock (_HEAP_USERDATA_HEADER) •SubSegment • Point to corresponding Subsegment • EncodeOffsets • To verify chunk header integrity • BusyBitmap • Indicate which chunk is being used SubSegment UserBlock … EncodedOffsets BusyBitmap 0x8 0x18 0x20 chunk header chunk header chunk header
  • 136.
    LFH • UserBlock (_HEAP_USERDATA_HEADER) •Block (chunk) • The allocated memory return to user. SubSegment UserBlock … EncodedOffsets BusyBitmap 0x8 0x18 0x20 chunk header chunk header chunk header
  • 137.
    LFH • _HEAP_ENTRY (chunk) •SubSegmentCode • Encoded metadata to retrieve the location of UserBlock • PreviousSize • The index of the chunk in UserBlock User Data PreviousBlockPrivateData (8byte) SubSegmentCode (4byte) PreviousSize (2byte) SegmentOffset (1byte) Unusedbyte (1byte) Inused
  • 138.
    LFH • _HEAP_ENTRY (chunk) •Unusedbyte • Unusedbyte & 0x80 must be true • To indicate it is LFH chunk User Data PreviousBlockPrivateData (8byte) SubSegmentCode (4byte) PreviousSize (2byte) SegmentOffset (1byte) Unusedbyte (1byte) Inused
  • 139.
    LFH • _HEAP_ENTRY (chunk) •Unusedbytes • Must be 0x80 • To indicate it is LFH freed chunk User Data PreviousBlockPrivateData (8byte) SubSegmentCode (4byte) PreviousSize (2byte) SegmentOffset (1byte) Unusedbyte (1byte) Freed
  • 140.
    LFH • Remark • AboutEncodedOffsets • EncodedOffsets is xor of the following values • (sizeof(userblock header) | (BlockUnit*0x10 << 16)) • LFHkey • Userblock address • _LFH_HEAP address
  • 141.
    LFH • Remark • AboutLFH header encoding • The chunk header will xor with following values when initialization • _HEAP address • LFHkey • Chunk address >> 4 • ((chunk address) - (UserBlock address)) << 12
  • 142.
  • 143.
    Windows memory allocator •Nt Heap • Front-End • Data Structure • Memory allocation mechanism
  • 144.
    Windows memory allocator •Nt Heap • Front-End • Initialization • If FrontEndHeapUsageData[x] & 0x1f > 0x10, LFH will be initialized in next allocation • It will ExtendFrontEndUsageData and create new BlocksIndex (0x80-0x400) • Create FrontEndHeap • Initialize SegmentInfoArrays[idx] • It will start using front-end allocator from next allocation with same size
  • 145.
    Windows memory allocator •LFH(Initialization) • malloc(0x40) * 16 FrontEndHeapUsageData … 0x210 The range of LFH is index from 0x0 to 0x80
  • 146.
    Windows memory allocator •LFH(Initialization) • malloc(0x40) (17th) FrontEndHeapUsageData … 0x231 0x231 & 0x1f > 0x10
  • 147.
    Windows memory allocator •LFH(Initialization) FrontEndHeapUsageData … 0x231 heap->CompatibilityFlag |= 0x20000000 After setting this flag, it will initialize
 LFH in the next allocation
  • 148.
  • 149.
    Windows memory allocator •LFH(Initialization) • malloc(0x40) (18th) • ExtendFrontEndUsageData and create new BlocksIndex (0x80-0x400) • The range of LFH is modified to index from 0x0 to 0x400 • Create and initialize FrontEndHeap (mmap) • initialize SegmentInfoArrays[idx] • Assign SegmentInfoArrays[BucketIndex] to segmentInfo FrontEndHeapUsageData … 0x4
  • 150.
    Windows memory allocator … EncodeFlagMask Encoding … BlocksIndex … FreeList … FrontEndHeap … FrontEndHeapUsageData … _HEAP FrontEndHeapUsageData 0x2510x4 BlocksIndex ExtendedLookup ArraySize(0x80) … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex (0x0) ListHead ListsInUseUlong ListHint BlocksIndex ExtendedLookup ArraySize (0x400) … ItemCount (4 bytes) OutofRangeItems (4 bytes) BaseIndex (0x80) ListHead ListsInUseUlong ListHint
  • 151.
  • 152.
    Windows memory allocator •LFH(Initialization) • malloc(0x40) (19th) • Allocate Userblock and initialize every chunk. • Set the corresponding ActiveSubsegment to the UserBlock • Randomly return chunk to user Userblock metadata Chunk 00 Chunk 01 … Chunk MaxBlockCount
  • 153.
  • 154.
    Windows memory allocator •LFH • Allocate (RtlpLowFragHeapAllocFromContext) • It will check whether ActiveSubsegment has chunk available. • Check ActiveSubsegment->depth • If not, it will search from CachedItem, and replace ActiveSubsegment with CachedItem’s SubSegment if available.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
    Windows memory allocator •LFH • Allocate (RtlpLowFragHeapAllocFromContext) • Retrieve RtlpLowFragHeapRandomData[x] • It will retrieve the value from RtlpLowFragHeapRandomData[x+1] next round. • x is 1 byte, x = rand() %256 after 256 rounds • RtlpLowFragHeapRandomData is a 256-byte array filled with random value • The range of random value is 0x0 - 0x7f
  • 163.
    Windows memory allocator •LFH • Allocate (RtlpLowFragHeapAllocFromContext) • Finally, the index of chunk is • RtlpLowFragHeapRandomData[x]*maxidx >> 7 • Take the nearest chunk if collision • Check (unused byte & 0x3f) !=0 (indicate chunk is freed) • Modify index and unused byte in header and return to user
  • 164.
    …. …. …. SubSegment … EncodedOffsets BusyBitmap chunk header chunk header chunkheader _HEAP_USERDATA_HEADER • Get an index • RtlpLowFragHeapRandomData[x]*maxidx >> 7 • Check if the BusyBitmap correspond to index is 0 • Return the chunk if true • Otherwise take the next nearest chunk chunk header
  • 165.
    …. …. …. SubSegment … EncodedOffsets BusyBitmap chunk header chunk header chunkheader _HEAP_USERDATA_HEADER • If the index is inused • Take next nearest chunk • Set the bitmap to 1 chunk header
  • 166.
    …. …. …. SubSegment … EncodedOffsets BusyBitmap chunk header chunk header chunkheader _HEAP_USERDATA_HEADER chunk header • If the index is inused • Take next nearest chunk • Set the bitmap to 1
  • 167.
    Windows memory allocator •LFH • Free (RtlFreeHeap) • Update unused byte in chunk header • Find the index of the Chunk and reset Userblock->BusyBitmap • Update ActiveSubsegment->AggregateExchg • If the free chunk is not belong to ActiveSubsegment, it will try to put into cachedItems
  • 168.
  • 169.
    LocalInfo UserBlock … AggregateExchg BlockSzie BlockCount … SizeIndex … Depth Hint (15bit) Lock (1bit) _HEAP_SUBSEGMENT SubSegment … EncodedOffsets BusyBitmap chunkheader chunk header chunk header _INTERLOCK_SEQ _HEAP_USERDATA_HEADER • Free • Find UserBlock by chunk header • Find corresponding SubSegment
  • 170.
    LocalInfo UserBlock … AggregateExchg BlockSzie BlockCount … SizeIndex … Depth Hint (15bit) Lock (1bit) _HEAP_SUBSEGMENT SubSegment … EncodedOffsets BusyBitmap chunkheader chunk header chunk header _INTERLOCK_SEQ _HEAP_USERDATA_HEADER • Free • Find UserBlock by chunk header • Find corresponding SubSegment • Assign unused byte to 0x80 • Clear the bitmap • Update AggregateExchg
  • 171.
    LocalInfo UserBlock … AggregateExchg BlockSzie BlockCount … SizeIndex … Depth Hint (15bit) Lock (1bit) _HEAP_SUBSEGMENT SubSegment … EncodedOffsets BusyBitmap chunkheader chunk header chunk header _INTERLOCK_SEQ _HEAP_USERDATA_HEADER • Free • Find UserBlock by chunk header • Find corresponding SubSegment • Assign unused byte to 0x80 • Clear the bitmap • Update AggregateExchg
  • 172.
    LocalInfo UserBlock … AggregateExchg BlockSzie BlockCount … SizeIndex … Depth Hint (15bit) Lock (1bit) _HEAP_SUBSEGMENT SubSegment … EncodedOffsets BusyBitmap chunkheader chunk header chunk header _INTERLOCK_SEQ _HEAP_USERDATA_HEADER • Free • Find UserBlock by chunk header • Find corresponding SubSegment • Assign unused byte to 0x80 • Clear the bitmap • Update AggregateExchg
  • 173.
    LocalInfo UserBlock … AggregateExchg BlockSzie BlockCount … SizeIndex … _HEAP_SUBSEGMENT SubSegment … EncodedOffsets BusyBitmap chunk header chunk header chunkheader _HEAP_USERDATA_HEADERLocalData ActiveSubsegment CachedItems … BucketIndex … LocalInfo UserBlock … AggregateExchg BlockSzie BlockCount … SizeIndex … _HEAP_SUBSEGMENT Depth (1) Hint (15bit) Lock (1bit) _INTERLOCK_SEQ CachedItems ActiveSubsegment 
 is not equivalent to
 SubSegment of free
 chunk
  • 174.
    LocalInfo UserBlock … AggregateExchg BlockSzie BlockCount … SizeIndex … _HEAP_SUBSEGMENT SubSegment … EncodedOffsets BusyBitmap chunk header chunk header chunkheader _HEAP_USERDATA_HEADERLocalData ActiveSubsegment CachedItems … BucketIndex … LocalInfo UserBlock … AggregateExchg BlockSzie BlockCount … SizeIndex … _HEAP_SUBSEGMENT Depth (1) Hint (15bit) Lock (1bit) _INTERLOCK_SEQ CachedItems CachedItems[0]
  • 175.
    LFH Exploitation • Reuseattack • If we have Use After Free • Because of randomness of LFH, we can not predict the next chunk. • We can fill up UserBlock and then free one of them, it will return same chunk in next allocation with same size.
  • 176.
    LFH Exploitation • normalcase • malloc(sizeof(A)) Userblock header
  • 177.
    LFH Exploitation • normalcase • malloc(sizeof(B)) Userblock header fptr
  • 178.
    LFH Exploitation • normalcase • free(A) Userblock header B fptr
  • 179.
    LFH Exploitation • normalcase • malloc(sizeof(A)) Userblock header B fptr
  • 180.
    LFH Exploitation • normalcase • malloc(sizeof(B)) Userblock header B fptr
  • 181.
    LFH Exploitation • normalcase • malloc(sizeof(B)) Userblock header B fptr Hard to use
  • 182.
    LFH Exploitation • Reuseattack • malloc(sizeof(A)) Userblock header
  • 183.
    LFH Exploitation • Reuseattack • malloc(sizeof(B)) x 6 Userblock header fptr
  • 184.
    LFH Exploitation • Reuseattack • free(A) Userblock header B B B B fptr B B
  • 185.
    LFH Exploitation • Reuseattack • malloc(sizeof(B)) Userblock header B B B B fptr B B
  • 186.
    LFH Exploitation • Reuseattack • A->fptr Userblock header B B B B fptr B B
  • 187.
    LFH Exploitation • Reuseattack • A->fptr Userblock header B B B B fptr B B Hijack the control flow !
  • 188.
  • 189.
    Thank you forlistening angelboy@chroot.org @scwuaptx