Windows Heap Internals: NT Heap and Segment Heap Architecture
Objective: Dissect the two Windows user-mode heap managers, the classic NT Heap and the modern Segment Heap, down to their root descriptors, allocation routing, size-class components, and cookie/encoding mitigations, so you can navigate both in WinDbg and reason about how corruption bugs interact with the metadata.
The heap is where most memory-corruption bugs actually live. Stacks get all the tutorial love because the primitive is clean, but real-world client exploitation (browsers, document parsers, script engines) is overwhelmingly a heap game. If you want to read those bugs, or catch someone else reading them, you have to know what the allocator writes into memory and why. Two allocators matter on a modern box: the NT Heap that has shipped since NT 3.1 and still backs most legacy processes, and the Segment Heap that Windows 10 introduced and made default for UWP, Edge, and a growing list of system binaries. They expose the same HeapAlloc surface and share almost none of their internals.
This is a survey and instrumentation tutorial. We build a small self-authored lab target, walk both heaps in the debugger, and pair every offensive primitive with the mitigation and telemetry that answer it. No live-CVE weaponization here; the exploitation mechanics belong in the Exploit Dev section.
Contents
- 1 1. The Heap Between the Page and the Byte
- 2 2. NT Heap: Backend Architecture
- 3 3. NT Heap: The Low Fragmentation Heap Frontend
- 4 4. Segment Heap: Architecture Overview and Opt-In
- 5 5. Segment Heap: Backend and Variable Size Allocator
- 6 6. Segment Heap: LFH and Large Block Components
- 7 7. Security Mitigations Deep-Dive
- 8 8. Lab Target and WinDbg Inspection
- 9 9. Common Attacker Techniques
- 10 10. Defensive Strategies & Detection
- 11 11. Tools for Heap Analysis
- 12 12. MITRE ATT&CK Mapping
- 13 Summary
- 14 Related Tutorials
- 15 References
1. The Heap Between the Page and the Byte
The Virtual Memory Manager deals in pages: 4 KB granular, reserved or committed through NtAllocateVirtualMemory. Applications want bytes: a 24-byte node, a 96-byte struct, a 300-byte string. The heap manager is the shim between those two worlds. It reserves large regions from the VMM, then carves them into byte-granular blocks, tracks which are busy and free, and recycles them.
Every public allocation API bottoms out here. malloc, operator new, LocalAlloc, and GlobalAlloc all funnel through HeapAlloc, which calls ntdll!RtlAllocateHeap. GetProcessHeap hands you the default process heap; HeapCreate makes a private one. From the allocator’s point of view there is no difference between a new from the CRT and a raw HeapAlloc, which is exactly why heap grooming works across abstraction layers.
Whether a given heap is an NT Heap or a Segment Heap is decided at heap creation time and is invisible to the caller. Same API, different machinery underneath.
2. NT Heap: Backend Architecture
The NT Heap is split into two layers. The backend owns fundamental memory: segments, free lists, commit and decommit, coalescing. The frontend is an optimization cache for small, hot allocations, and the only frontend that modern Windows ships is the Low Fragmentation Heap.
Everything hangs off the root descriptor, ntdll!_HEAP.
| Struct / Symbol | Description |
|---|---|
ntdll!_HEAP | Root descriptor. Holds Flags, ForceFlags, Encoding (the XOR cookie), SegmentList, FreeLists[128], FrontEndHeap, FrontEndHeapType. |
ntdll!_HEAP_SEGMENT | A contiguous committed range. SegmentSignature is 0xffeeffee; also SegmentListEntry, Heap back-pointer, BaseAddress, NumberOfPages, FirstEntry, LastValidEntry, NumberOfUnCommittedPages. |
ntdll!_HEAP_ENTRY | Per-block header. 16 bytes on x64. Size, Flags (0x01 busy, 0x08 internal/LFH), SmallTagIndex, PreviousSize, UnusedBytes. |
ntdll!_HEAP_FREE_ENTRY | Free-block overlay on _HEAP_ENTRY; adds a FreeList _LIST_ENTRY for doubly-linked free-list membership. |
ntdll!_HEAP_LIST_LOOKUP | The 128-slot free-list index; each slot is a _LIST_ENTRY keyed by allocation granularity units. |
The header layout is worth internalizing because it is what an overflow smashes first. Here is the x64 shape:
typedef struct _HEAP_ENTRY {
USHORT Size; // block size in allocation units
UCHAR Flags; // 0x01 = busy, 0x08 = LFH/internal
UCHAR SmallTagIndex; // checksum byte
USHORT PreviousSize; // previous block size (coalescing)
UCHAR SegmentOffset; // / LFHFlags on LFH blocks
UCHAR UnusedBytes; // padding accounting
} HEAP_ENTRY, *PHEAP_ENTRY;
Granularity is architecture-dependent: 8 bytes on x86, 16 bytes on x64. That is the quantum the allocator rounds every request up to, and the unit the Size field counts.
Allocation path: RtlAllocateHeap first checks whether a frontend (LFH) is active for the requested size. If not, it drops into the backend RtlpAllocateHeap, which walks FreeLists for a block that fits, splits it if it is larger than needed, and marks the header busy. Free path: RtlFreeHeap decodes the header, validates the cookie, and returns the block to the appropriate free list, coalescing with physically adjacent free neighbors to fight fragmentation. When free space passes the decommit threshold the backend hands pages back to the VMM.
| Function | Role |
|---|---|
RtlCreateHeap | Creates a heap; reserves the initial region via NtAllocateVirtualMemory. |
RtlAllocateHeap | Public alloc entry; dispatches to LFH frontend or backend. |
RtlFreeHeap | Frees a block; decodes and validates the header, then returns it to a free list or LFH slab. |
RtlpAllocateHeap | Backend allocator; walks the 128 free lists. |
RtlpLowFragHeapAllocFromContext | Internal LFH allocation from a subsegment. |
The single most important defensive detail here: on modern Windows the second 8-byte word of _HEAP_ENTRY is XOR-encoded against _HEAP.Encoding. The Size, Flags, and checksum are never stored in cleartext. When the allocator decodes a header it also verifies a byte checksum, so a corrupted or attacker-forged header almost never decodes to a self-consistent value, and the heap raises a corruption exception instead of trusting the garbage.

3. NT Heap: The Low Fragmentation Heap Frontend
The LFH exists to kill fragmentation for small, frequently repeated allocations. Instead of splitting general free blocks, it pre-carves a slab (a subsegment) for one size class and hands out fixed slots.
The activation rule trips up more people than any other heap detail: the LFH does not turn on for a size class until 18 consecutive allocations of a similar small size. Allocate a 0x50-byte chunk once and you are in the backend. Allocate it 18 times and the LFH bucket for that class wakes up. The ceiling is roughly 16 KB (0x4000); anything larger bypasses LFH entirely and stays in the backend.
| Struct / Symbol | Description |
|---|---|
ntdll!_LFH_HEAP / _HEAP_LOCAL_DATA | LFH manager state, reached via _HEAP.FrontEndHeap. |
ntdll!_HEAP_LFH_CONTEXT | Core LFH state: Buckets[129], Callbacks, Config. Bucket pointers are XOR-encoded with RtlpHpHeapGlobals.LfhKey. |
ntdll!_HEAP_SUBSEGMENT / _HEAP_LFH_SUBSEGMENT | Pre-carved slab for one size class. FreeHint locates the next free block. |
ntdll!_HEAP_USERDATA_HEADER | Header of a subsegment’s user-data block: SubSegment, SizeIndex, EncodedOffsets. |
Two mitigations live specifically in the LFH. First, since Windows 8 the slot chosen inside a subsegment is randomized, so a groomed allocation does not deterministically land next to its predecessor. Second, the callback and bucket pointers in _HEAP_LFH_CONTEXT are encoded with LfhKey, so leaking one does not directly hand an attacker a function pointer to overwrite.
4. Segment Heap: Architecture Overview and Opt-In
Windows 10 shipped a ground-up rewrite. The Segment Heap keeps the HeapAlloc API and throws away nearly everything below it. Where the NT Heap tracks frees with linked lists, the Segment Heap uses red-black trees; where the NT Heap does first-fit-ish list walks, the Segment Heap does a best-fit search that prefers the most-committed block. It routes requests across four components by size.
| Component | Size Range | Notes |
|---|---|---|
| LFH | 1 – 16,368 bytes (0x3FF0) | Only when the size is detected as popular (adaptive). |
| VS allocator | 1 – ~128 KB | Variable-sized blocks carved from VS subsegments. |
| Backend | ~128 KB – 508 KB | Page-range descriptor based; commits via NtAllocateVirtualMemory. |
| Large Block | > 508 KB | Direct NT Memory Manager call, 64 KB aligned. |
The root descriptor is ntdll!_SEGMENT_HEAP:
| Field | Meaning |
|---|---|
Signature / EnvHandle / AllocatedBase | Identity and base bookkeeping. |
SegContexts[2] | Backend segment contexts (each _HEAP_SEG_CONTEXT). |
VsContext | _HEAP_VS_CONTEXT, the VS allocator state. |
LfhContext | _HEAP_LFH_CONTEXT, the LFH state. |
LargeAllocMetadata / LargeReservedPages / LargeCommittedPages | Large-block accounting. |
Keys for the whole heap live in one global structure, ntdll!_RTLP_HP_HEAP_GLOBALS, which stores HeapKey (8 bytes, VS and segment encoding) and LfhKey (8 bytes, LFH pointer encoding).
Who gets a Segment Heap? UWP apps, Edge, and a set of system binaries are opted in by default. Everything else is NT Heap unless it opts in. The two opt-in levers:
IMAGE_LOAD_CONFIG_DIRECTORY.HeapFlagsin the PE header.- The per-image IFEO key
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\<exe>\FrontEndHeapDebugOptions, value0x08to force Segment Heap.

5. Segment Heap: Backend and Variable Size Allocator
The backend services allocations that are too big for VS but not large-block huge. It manages page ranges inside its segments through descriptors and tracks free ranges in a red-black tree.
| Struct | Role |
|---|---|
ntdll!_HEAP_SEG_CONTEXT | Backend segment context: SegmentListHead, FreePageRanges (a _RTL_RB_TREE). |
ntdll!_HEAP_PAGE_RANGE_DESCRIPTOR | Describes a page range in a segment; tracks committed vs free state. |
The VS allocator is the interesting middle tier. Its state lives in _HEAP_VS_CONTEXT:
typedef struct _HEAP_VS_CONTEXT {
RTL_SRWLOCK Lock;
RTL_RB_TREE FreeChunkTree; // red-black tree of free VS blocks
LIST_ENTRY SubsegmentList;
SIZE_T TotalCommittedUnits;
SIZE_T FreeCommittedUnits;
PVOID BackendCtx; // -> _SEGMENT_HEAP
HEAP_SUBALLOCATOR_CALLBACKS Callbacks; // encoded
} HEAP_VS_CONTEXT;
VS blocks come from subsegments (_HEAP_VS_SUBSEGMENT), each with a CommitBitmap, a Size in 16-byte units, and a Signature computed as Size XOR 0xABED. That signature is a cheap integrity check: corrupt the size, and the signature no longer matches.
Every VS block carries a _HEAP_VS_CHUNK_HEADER encoding Size, offset fields, and an Allocated flag. On free, RtlpHpVsContextFree first checks that Allocated flag (a double-free guard), then calls RtlpHpVsChunkCoalesce to merge adjacent free chunks, then reinserts the coalesced block into FreeChunkTree. Allocation runs RtlpHpVsContextAllocateInternal, which does the best-fit walk of the RB tree.
| Function | Role |
|---|---|
RtlpHpAllocate | Top-level dispatcher; routes to LFH, VS, backend, or large block. |
RtlpHpVsContextAllocateInternal | VS alloc; best-fit search of FreeChunkTree. |
RtlpHpVsContextFree | VS free; checks Allocated, coalesces, reinserts. |
RtlpHpSegAlloc | Backend segment allocation path. |
RtlpHpVsSubsegmentCreate | Creates and initializes a VS subsegment. |
6. Segment Heap: LFH and Large Block Components
The Segment Heap LFH serves the same purpose as its NT Heap cousin, prevent fragmentation of hot small sizes, but with new structures. It buckets similarly-sized blocks out of larger pre-allocated slabs, and a bucket is enabled only when its size is detected as popular. The slab is _HEAP_LFH_SUBSEGMENT, which carries a FreeHint (an index hint into the block bitmap), a BlockCount, and a BlockBitmap marking each slot allocated or free. Allocation runs through RtlpHpLfhContextAllocate, using FreeHint to jump to a likely-free slot.
Above 508 KB the Segment Heap stops carving and asks the NT Memory Manager directly. RtlpHpLargeAlloc reserves 64 KB-aligned memory and records it in _HEAP_LARGE_ALLOC_DATA, whose backing store is itself allocated by RtlpHpMetadataAlloc. Large allocations are tracked in a bitmap rather than inline headers, which is part of the broader Segment Heap philosophy of keeping metadata away from user data.
7. Security Mitigations Deep-Dive
The Segment Heap’s mitigations are largely carried over from the NT Heap, and Microsoft’s own analysis rates the two as comparable in applied protections. The one structural advantage the Segment Heap has is out-of-line metadata: much of its bookkeeping lives away from the user buffer, so a linear overflow does not immediately land on an inline header the way it does in the classic NT Heap.
| Mitigation | What it defeats |
|---|---|
| Heap base ASLR | Predicting heap addresses for absolute-address overwrites. |
_HEAP_ENTRY XOR cookie | Forging a self-consistent NT heap header after an overflow. |
VS block size + 0xABED signature encoding | Silent corruption of VS chunk sizes. |
| LFH slot randomization (Win8+) | Deterministic adjacency grooming inside a subsegment. |
| Out-of-line Segment Heap metadata | Inline-header smashing via linear overflow. |
Function pointer encoding (HeapKey, LfhKey + context address) | Direct overwrite of Callbacks in _HEAP_LFH_CONTEXT / _HEAP_VS_CONTEXT. |
| Guard pages between segments | Overflow running off the end of a region unnoticed. |
| Safe unlinking (Win8+) | The classic unlink write-what-where on free-list pointers. |
None of these is bulletproof alone. The point of the stack is that a single leak or a single overflow rarely completes a chain; you need to defeat encoding, survive randomization, and locate metadata, and each layer costs the attacker an extra primitive.

8. Lab Target and WinDbg Inspection
Build the lab target. It is a deliberately unbounded heap toy: allocate, free, write past the end, read back, and a spray helper that fires 20 same-size allocations to trip the LFH.
// heap_lab.c - intentionally-vulnerable heap playground (self-made lab target)
// Compile: cl /Od /Zi heap_lab.c /link /out:heap_lab.exe
#include <windows.h>
#include <stdio.h>
#include <string.h>
#define MAX_CHUNKS 32
HANDLE g_heap;
LPVOID g_chunks[MAX_CHUNKS];
static void do_alloc(int i, SIZE_T sz) {
g_chunks[i] = HeapAlloc(g_heap, 0, sz);
printf("[+] chunk[%d] = %p (0x%zx bytes)\n", i, g_chunks[i], sz);
}
static void do_free(int i) {
HeapFree(g_heap, 0, g_chunks[i]); g_chunks[i] = NULL;
printf("[-] freed chunk[%d]\n", i);
}
static void do_write(int i, SIZE_T n, const char *d) {
memcpy(g_chunks[i], d, n); // intentional: no bounds check = overflow primitive
}
static void do_read(int i, SIZE_T n) {
fwrite(g_chunks[i], 1, n, stdout); putchar('\n');
}
int main(void) {
int op, i; SIZE_T sz; char buf[256];
g_heap = HeapCreate(0, 0, 0);
printf("[*] private heap @ %p\n", g_heap);
for (;;) {
printf("\n1=alloc 2=free 3=write 4=read 5=spray 0=quit > ");
if (scanf("%d", &op) != 1) break;
switch (op) {
case 1: scanf("%d %zx", &i, &sz); do_alloc(i, sz); break;
case 2: scanf("%d", &i); do_free(i); break;
case 3: scanf("%d %zx %255s", &i, &sz, buf); do_write(i, sz, buf); break;
case 4: scanf("%d %zx", &i, &sz); do_read(i, sz); break;
case 5: for (i = 0; i < 20; i++) do_alloc(i, 0x50); break; // trip LFH
case 0: return 0;
}
}
return 0;
}
Point WinDbg at Microsoft symbols first: srv*c:\symbols*https://msdl.microsoft.com/download/symbols. Then work these commands.
| Command | What it shows |
|---|---|
!heap -s | Inventory of every heap, with type (NT vs Segment). |
!heap -h <addr> | One heap’s segments and free lists. |
!heap -x <addr> | Which heap block contains a suspect address. |
!heap -stat -h <addr> | Per-bucket LFH statistics; confirms activation. |
dt ntdll!_HEAP <addr> | Root NT heap descriptor. |
dt ntdll!_SEGMENT_HEAP <addr> | Root Segment Heap descriptor. |
Exercise 1, walk the NT heap. Launch under the debugger and dump the private heap:
windbg -g heap_lab.exe
0:000> !heap -s
0:000> !heap -h <private_heap_addr>
0:000> dt ntdll!_HEAP <private_heap_addr>
0:000> dt ntdll!_HEAP_ENTRY <first_entry_addr>
0:000> dt ntdll!_HEAP_SEGMENT <segment_addr>
Exercise 2, trip the LFH. Send 5 to the lab menu (20 allocations of 0x50), then:
0:000> !heap -stat -h <heap_addr>
0:000> dt ntdll!_HEAP_SUBSEGMENT <subsegment_addr>
You should see a bucket for the 0x50 class flip active only after the run crosses the 18-allocation line, not before.
Exercise 3, decode a header by hand. Read _HEAP.Encoding, then XOR it against the raw 8-byte second word of a _HEAP_ENTRY and confirm Size, Flags, and the checksum. This is the exercise that cost me an hour the first time: I XORed the first eight bytes, got nonsense, and blamed my symbols. The encoded word on x64 is the second 8-byte word of the header. Get the offset right and the decode is exact. Then flip one byte of a live header and step the next free to watch the corruption exception fire.
Exercise 4, walk the Segment Heap. Attach to a default-Segment-Heap process:
windbg -pn notepad.exe
0:000> !heap -s
0:000> dt ntdll!_SEGMENT_HEAP <heap_addr>
0:000> dt ntdll!_HEAP_VS_CONTEXT <heap_addr+0x280>
0:000> dt ntdll!_HEAP_LFH_CONTEXT <heap_addr+0x340>
0:000> dt ntdll!_RTLP_HP_HEAP_GLOBALS ntdll!RtlpHpHeapGlobals
Those +0x280 / +0x340 offsets drift between Windows builds. Do not hardcode them; run dt ntdll!_SEGMENT_HEAP with no address to read the current field offsets, then add them to the base. Newer notepad ships as a Store app, so if !heap -s shows NT Heap, use an older desktop notepad or a UWP app to see the Segment Heap.
Exercise 5, page heap. This is the single most useful heap-debugging aid, full stop. It places each allocation at the end of its own page followed by a guard page, so a one-byte overflow faults immediately at the exact instruction instead of corrupting silently and crashing minutes later.
gflags /p /enable heap_lab.exe /full
windbg -g heap_lab.exe
; alloc a 0x30 chunk, then write 0x40 bytes -> instant AV at the memcpy
Run the same overflow without page heap and you get silence, then a delayed, misleading crash somewhere unrelated. That contrast is the whole lesson.
9. Common Attacker Techniques
Heap bugs interact directly with the structures above. An overflow smashes the adjacent header or the neighbor’s data; a use-after-free hands the attacker a dangling pointer that the allocator happily re-hands to a controlled object; a double-free corrupts free-list or tree linkage.
| Technique | Description |
|---|---|
| Heap overflow | Unbounded write past a chunk corrupts the next _HEAP_ENTRY or VS chunk header. |
| Use-after-free | Freed block reused; attacker reclaims the slot with a controlled object of the same size class. |
| Double-free | Freeing twice corrupts free-list / FreeChunkTree linkage into a write primitive. |
| LFH grooming | Allocate and free precise counts of one size to shape subsegment slots and control adjacency. |
| Heap spray | Flood the heap with attacker-sized blocks to make target-object placement deterministic. |
| Callback overwrite | Corrupt encoded Callbacks pointers to redirect control flow (requires defeating the XOR key). |
Grooming is the connective tissue. Because LFH serves fixed size classes, an attacker who allocates 18-plus objects of the victim’s size forces a fresh subsegment, then frees a hole and reallocs the vulnerable object into a predictable neighbor. Slot randomization since Windows 8 is precisely the mitigation that makes this less deterministic and more expensive.

10. Defensive Strategies & Detection
Heap allocations are user-mode, in-process activity; Sysmon does not log a HeapAlloc. Detection targets the consequences of heap exploitation, the crash, the injection, the cross-process memory access, not the allocation itself.
| Event ID | Name | Relevance |
|---|---|---|
Event ID 1 | Process Create | Baseline; correlate unusual processes spawning post-crash shells. |
Event ID 8 | CreateRemoteThread | Heap-staged shellcode often followed by a remote thread. |
Event ID 10 | ProcessAccess | Cross-process ReadProcessMemory/WriteProcessMemory against a target heap. |
Event ID 17/18 | PipeCreated / PipeConnected | Some post-exploitation primitives stage through named pipes. |
Event ID 255 | Error | Heap corruption exceptions can surface here. |
Heap corruption crashes generate Windows Error Reporting events (Application Error, Event ID 1000/1001 in the Application log). ETW gives finer signal: Microsoft-Windows-Heap-Snapshot (GUID 901d2863-5b44-4b5c-9b50-9e2a5a9e6b28) captures heap-state snapshots and, via the HeapAPI keyword, per-process HeapAlloc/HeapFree calls (high-rate, use selectively). The NT Kernel Logger PERF_HEAP keyword tracks kernel-side HeapRangeCreate/HeapRangeReserve. Recurring HEAP_CORRUPTION stop codes from one binary are the clearest signal of active probing.
title: Potential Heap Spray via Unusual Process Memory Access
logsource:
product: windows
service: sysmon
detection:
selection:
EventID: 10 # ProcessAccess
GrantedAccess|contains:
- '0x1fffff' # PROCESS_ALL_ACCESS
- '0x1010' # VM read + query
TargetImage|endswith:
- '\notepad.exe'
- '\calc.exe' # common hollowed/sprayed targets
condition: selection
fields:
- SourceImage
- TargetImage
- GrantedAccess
- CallTrace
level: high
Hardening that actually moves the needle:
- Enable page heap (
gflags /p /enable <app>.exe /full) in dev/test and Application Verifier in staging. - Verify ASLR and DEP/NX are enforced via
SetProcessMitigationPolicy; confirm in Process Hacker’s Mitigations tab. - Turn on terminate-on-corruption:
HeapSetInformation(heap, HeapEnableTerminationOnCorruption, NULL, 0), or per-appSet-ProcessMitigation -Name <app>.exe -Enable HeapTerminateOnCorruption. - Compile with Control Flow Guard (
/guard:cf) to blunt hijacked encoded callback pointers. - Opt sensitive processes into the Segment Heap for out-of-line metadata.
- Capture WER LocalDumps (
HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps) for post-incident heap forensics.
11. Tools for Heap Analysis
| Tool | Description | Link |
|---|---|---|
| WinDbg Preview | !heap, dt struct walks, live and post-mortem heap inspection. | microsoft.com |
| gflags | Enables page heap and other NT global flags. | (WDK) |
| Application Verifier | Full-page heap plus handle/lock checks in staging. | (WDK) |
| Process Hacker | Live heap and mitigation-policy inspection per process. | processhacker.sourceforge.io |
| x64dbg | User-mode debugging supplement for allocation tracing. | x64dbg.com |
| Volatility | Memory-forensic heap reconstruction from a dump. | volatilityfoundation.org |
12. MITRE ATT&CK Mapping
| Technique | MITRE ID | Detection |
|---|---|---|
| Exploitation for Client Execution | T1203 | WER Event 1000/1001 on browsers/parsers; recurring HEAP_CORRUPTION faults. |
| Exploitation for Privilege Escalation | T1068 | Kernel-pool corruption crashes; unexpected token changes post-crash. |
| Process Injection | T1055 | Sysmon Event ID 8 (CreateRemoteThread), Event ID 10 (ProcessAccess). |
| Process Hollowing | T1055.012 | Suspended-process create then remote write/thread into a hollowed image. |
| DLL Injection | T1055.001 | Remote WriteProcessMemory plus thread into a foreign module region. |
Summary
- The heap manager is the byte-granular layer over the page-granular VMM, and it is where most client-side memory corruption actually lands.
- The NT Heap uses a backend of segments and 128 free lists plus an LFH frontend that activates after 18 same-size allocations under 0x4000, with XOR-encoded
_HEAP_ENTRYheaders guarding integrity. - The Segment Heap is a Windows 10 rewrite routing by size across LFH, VS (RB-tree, best-fit), backend page ranges, and large blocks, keeping much metadata out-of-line.
- Mitigations (header cookies, pointer encoding with
HeapKey/LfhKey, LFH slot randomization, safe unlinking, guard pages) each cost the attacker an extra primitive rather than closing the door outright. - Detect the consequences, not the allocation: WER/Event 1000 crashes, Sysmon
Event ID 8andEvent ID 10, and page heap plus terminate-on-corruption as the defender’s front line.
Related Tutorials
- Windows OS Architecture
- Access Tokens and Privileges: The Kernel’s Security Context
- SIDs and Security Descriptors: Identity in Windows Security
- Fibers: User-Mode Cooperative Threads
- Jobs and Silos: Process Grouping and Resource Limits
References
- Heap Functions – Win32 Apps | Microsoft Learn
- HeapCreate function (heapapi.h) – Win32 Apps | Microsoft Learn
- Windows 10 Segment Heap Internals (Black Hat USA 2016 Slides) – Mark Vincent Yason, IBM X-Force
- Windows 10 Segment Heap Internals (White Paper) – Mark Vincent Yason, IBM X-Force
- Exploitation for Privilege Escalation, Technique T1068 – MITRE ATT&CK Enterprise
- Windows Kernel Internals: User-mode Heap Manager (LFH Design) – David B. Probert, Microsoft Corporation
Get new drops in your inbox
Windows internals, exploit dev, and red-team write-ups - no spam, unsubscribe anytime.