Windows Heap Internals: NT Heap and Segment Heap Architecture

By Debraj Basak·Aug 22, 2026·17 min readWindows Internals

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.


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 / SymbolDescription
ntdll!_HEAPRoot descriptor. Holds Flags, ForceFlags, Encoding (the XOR cookie), SegmentList, FreeLists[128], FrontEndHeap, FrontEndHeapType.
ntdll!_HEAP_SEGMENTA contiguous committed range. SegmentSignature is 0xffeeffee; also SegmentListEntry, Heap back-pointer, BaseAddress, NumberOfPages, FirstEntry, LastValidEntry, NumberOfUnCommittedPages.
ntdll!_HEAP_ENTRYPer-block header. 16 bytes on x64. Size, Flags (0x01 busy, 0x08 internal/LFH), SmallTagIndex, PreviousSize, UnusedBytes.
ntdll!_HEAP_FREE_ENTRYFree-block overlay on _HEAP_ENTRY; adds a FreeList _LIST_ENTRY for doubly-linked free-list membership.
ntdll!_HEAP_LIST_LOOKUPThe 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.

FunctionRole
RtlCreateHeapCreates a heap; reserves the initial region via NtAllocateVirtualMemory.
RtlAllocateHeapPublic alloc entry; dispatches to LFH frontend or backend.
RtlFreeHeapFrees a block; decodes and validates the header, then returns it to a free list or LFH slab.
RtlpAllocateHeapBackend allocator; walks the 128 free lists.
RtlpLowFragHeapAllocFromContextInternal 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.


Hierarchy diagram of NT Heap structures from root _HEAP descriptor down to segments, busy and free block entries, free lists, and the LFH frontend
The NT Heap root descriptor branches into backend segments and free lists plus the LFH frontend; every allocation slot is a _HEAP_ENTRY carved from a segment.

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 / SymbolDescription
ntdll!_LFH_HEAP / _HEAP_LOCAL_DATALFH manager state, reached via _HEAP.FrontEndHeap.
ntdll!_HEAP_LFH_CONTEXTCore LFH state: Buckets[129], Callbacks, Config. Bucket pointers are XOR-encoded with RtlpHpHeapGlobals.LfhKey.
ntdll!_HEAP_SUBSEGMENT / _HEAP_LFH_SUBSEGMENTPre-carved slab for one size class. FreeHint locates the next free block.
ntdll!_HEAP_USERDATA_HEADERHeader 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.

ComponentSize RangeNotes
LFH1 – 16,368 bytes (0x3FF0)Only when the size is detected as popular (adaptive).
VS allocator1 – ~128 KBVariable-sized blocks carved from VS subsegments.
Backend~128 KB – 508 KBPage-range descriptor based; commits via NtAllocateVirtualMemory.
Large Block> 508 KBDirect NT Memory Manager call, 64 KB aligned.

The root descriptor is ntdll!_SEGMENT_HEAP:

FieldMeaning
Signature / EnvHandle / AllocatedBaseIdentity 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 / LargeCommittedPagesLarge-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.HeapFlags in the PE header.
  • The per-image IFEO key HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\<exe>\FrontEndHeapDebugOptions, value 0x08 to force Segment Heap.

Flow diagram showing how RtlpHpAllocate dispatches a request to one of four Segment Heap components based on allocation size: LFH, VS allocator, backend, or large block
The Segment Heap dispatcher routes every request to one of four size-tiered components, keeping metadata out-of-line at each tier.

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.

StructRole
ntdll!_HEAP_SEG_CONTEXTBackend segment context: SegmentListHead, FreePageRanges (a _RTL_RB_TREE).
ntdll!_HEAP_PAGE_RANGE_DESCRIPTORDescribes 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.

FunctionRole
RtlpHpAllocateTop-level dispatcher; routes to LFH, VS, backend, or large block.
RtlpHpVsContextAllocateInternalVS alloc; best-fit search of FreeChunkTree.
RtlpHpVsContextFreeVS free; checks Allocated, coalesces, reinserts.
RtlpHpSegAllocBackend segment allocation path.
RtlpHpVsSubsegmentCreateCreates 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.

MitigationWhat it defeats
Heap base ASLRPredicting heap addresses for absolute-address overwrites.
_HEAP_ENTRY XOR cookieForging a self-consistent NT heap header after an overflow.
VS block size + 0xABED signature encodingSilent corruption of VS chunk sizes.
LFH slot randomization (Win8+)Deterministic adjacency grooming inside a subsegment.
Out-of-line Segment Heap metadataInline-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 segmentsOverflow 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.


Illustration of layered vault doors as a metaphor for stacked heap security mitigations each requiring a separate bypass
Heap mitigations stack independently – defeating header cookies, pointer encoding, and slot randomization each demands a separate attacker 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.

CommandWhat it shows
!heap -sInventory 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.

TechniqueDescription
Heap overflowUnbounded write past a chunk corrupts the next _HEAP_ENTRY or VS chunk header.
Use-after-freeFreed block reused; attacker reclaims the slot with a controlled object of the same size class.
Double-freeFreeing twice corrupts free-list / FreeChunkTree linkage into a write primitive.
LFH groomingAllocate and free precise counts of one size to shape subsegment slots and control adjacency.
Heap sprayFlood the heap with attacker-sized blocks to make target-object placement deterministic.
Callback overwriteCorrupt 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.


Flow diagram tracing a heap exploitation chain from initial vulnerability through heap grooming, metadata corruption, information leak, and final control-flow hijack
Each mitigation layer forces the attacker to chain an additional primitive – grooming, leaking the encoding key, and forging a pointer before control flow can be hijacked.

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 IDNameRelevance
Event ID 1Process CreateBaseline; correlate unusual processes spawning post-crash shells.
Event ID 8CreateRemoteThreadHeap-staged shellcode often followed by a remote thread.
Event ID 10ProcessAccessCross-process ReadProcessMemory/WriteProcessMemory against a target heap.
Event ID 17/18PipeCreated / PipeConnectedSome post-exploitation primitives stage through named pipes.
Event ID 255ErrorHeap 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-app Set-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

ToolDescriptionLink
WinDbg Preview!heap, dt struct walks, live and post-mortem heap inspection.microsoft.com
gflagsEnables page heap and other NT global flags.(WDK)
Application VerifierFull-page heap plus handle/lock checks in staging.(WDK)
Process HackerLive heap and mitigation-policy inspection per process.processhacker.sourceforge.io
x64dbgUser-mode debugging supplement for allocation tracing.x64dbg.com
VolatilityMemory-forensic heap reconstruction from a dump.volatilityfoundation.org

12. MITRE ATT&CK Mapping

TechniqueMITRE IDDetection
Exploitation for Client ExecutionT1203WER Event 1000/1001 on browsers/parsers; recurring HEAP_CORRUPTION faults.
Exploitation for Privilege EscalationT1068Kernel-pool corruption crashes; unexpected token changes post-crash.
Process InjectionT1055Sysmon Event ID 8 (CreateRemoteThread), Event ID 10 (ProcessAccess).
Process HollowingT1055.012Suspended-process create then remote write/thread into a hollowed image.
DLL InjectionT1055.001Remote 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_ENTRY headers 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 8 and Event ID 10, and page heap plus terminate-on-corruption as the defender’s front line.

Related Tutorials

References

Get new drops in your inbox

Windows internals, exploit dev, and red-team write-ups - no spam, unsubscribe anytime.