You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
log: ['action','free(P0) -- tcache bin for size 32: count = 1/7. The chunk\'s data area stores an fd pointer to NULL (end of list). Tcache entries form a singly-linked LIFO list.'],
@@ -115,6 +120,11 @@ const hint56: Exercise = {
115
120
log: ['warn','free(P7) -- tcache bin is full (7/7)! This chunk falls through to the fast bin instead. The fast bin has different security checks (and fewer in older glibc). Many exploits deliberately fill the tcache to force chunks into the more exploitable fast bin path.'],
log: ['warn','free(P8) -- also goes to fast bin (tcache still full). Fastbin[32]: P8 \u2192 P7 \u2192 NULL. Both P7 and P8 bypassed tcache and went straight to the classic fast bin path.'],
log: ['success','Tcache deep dive complete. Key facts: (1) 64 bins, one per size class, max 7 entries each. (2) LIFO singly-linked via fd pointer in the data area. (3) Minimal security checks in early glibc versions (no double-free detection until 2.29). (4) Filling the tcache forces chunks to the classic bin paths, which is a common exploit setup step.'],
Copy file name to clipboardExpand all lines: src/exercises/unit12-win-heap-internals/whint-60.ts
+28-14Lines changed: 28 additions & 14 deletions
Original file line number
Diff line number
Diff line change
@@ -28,41 +28,55 @@ const exercise: Exercise = {
28
28
},
29
29
mode: 'step',
30
30
vizMode: 'heap',
31
-
heapSize: 256,
31
+
heapSize: 1024,
32
32
steps: [
33
33
{
34
34
action: 'init',
35
-
log: ['info','The LFH is the front-end allocator for the NT Heap. It groups allocation sizes into 128 buckets (size classes). Bucket selection: sizes 1-256 use 8-byte granularity (bucket = ceil(size/8)), sizes 257-16384 use wider granularity. The LFH is NOT active by default -- it activates per-bucket.'],
35
+
log: ['info','The LFH is the front-end allocator for the NT Heap. It groups allocation sizes into 128 buckets (size classes). Bucket selection: sizes 1-256 use 8-byte granularity (bucket = ceil(size/8)), sizes 257-16384 use wider granularity. The LFH is NOT active by default -- it activates per-bucket after enough allocations of the same size.'],
log: ['action','HeapAlloc(h, 0, 32) -- first allocation of size 32. The back-end handles this (LFH not yet active). Internally, _HEAP.FrontEndHeapUsageData[] counts how many times each bucket is hit. This counter increments with each HeapAlloc call for this size class.'],
log: ['action','HeapAlloc(h, 0, 32) -- alloc #1. The back-end handles this (LFH not yet active). Internally, _HEAP.FrontEndHeapUsageData[] counts how many times each bucket is hit. This counter increments with each HeapAlloc call for this size class.'],
log: ['info','After 17 consecutive allocations of size class 32, the counter crosses the activation threshold. The heap manager calls RtlpActivateLowFragmentationHeap() for bucket 4 (32/8). This creates an _LFH_HEAP structure and links it to this bucket.'],
log: ['action','HeapAlloc(h, 0, 32) -- alloc #3. The counter is still below 17. All these are back-end allocations from the FreeLists or segment commit.'],
log: ['action','HeapAlloc × 13 more -- allocs #4 through #16 all served by the back-end. The usage counter climbs: 4... 8... 12... 16. Almost at the activation threshold.'],
log: ['warn','HeapAlloc(h, 0, 32) -- alloc #17! The usage counter hits the activation threshold. The heap manager calls RtlpActivateLowFragmentationHeap() for bucket 4 (32/8). This creates an _LFH_HEAP structure and links it to this bucket. LFH is now ACTIVE for size 32.'],
log: ['action','Allocation #18 (post-activation) -- now served by LFH. The LFH allocates a UserBlocks region: a contiguous memory block subdivided into fixed-size slots. Each slot is exactly (header + 32) bytes. The LFH picks a slot using a randomized bitmap scan, not FIFO.'],
log: ['action','HeapAlloc(h, 0, 32) -- alloc #18. Now served by LFH! The LFH allocates a UserBlocks region: a contiguous memory block subdivided into fixed-size slots. Each slot is exactly (header + 32) bytes. The LFH picks a slot using a randomized bitmap scan, not FIFO.'],
log: ['info','UserBlocks structure: _HEAP_USERDATA_HEADER at the start, followed by N fixed-size subsegment entries. A bitmap tracks which slots are busy/free. The LFH randomizes the starting position in the bitmap scan, so consecutive HeapAlloc calls do NOT return sequential addresses.'],
log: ['warn','LFH randomization was added in Windows 8 to make heap spraying harder. Before Win8, LFH returned slots in a deterministic pattern. The randomization means an attacker spraying 1000 objects cannot predict which slot index will be adjacent to a target -- but statistical attacks still work with enough spray.'],
log: ['success','LFH summary: activates per-bucket after 17+ allocs of the same size. Allocates from a UserBlocks region of fixed-size slots. Uses bitmap-based tracking with randomized slot selection (Win8+). Reduces fragmentation and raises the bar for heap manipulation attacks.'],
79
+
log: ['success','LFH activation demonstrated with all 18 allocations. Key facts: (1) LFH activates per-bucket after 17+ allocs of the same size. (2) Allocates from a UserBlocks region of fixed-size slots. (3) Uses bitmap-based tracking with randomized slot selection (Win8+). (4) Reduces fragmentation and raises the bar for heap manipulation attacks.'],
log: ['info','The Segment Heap (_SEGMENT_HEAP) is a complete redesign of the Windows heap introduced in Windows 10. It replaces _HEAP for modern apps. The core idea: route allocations to different backends depending on size, with stronger metadata validation and guard pages.'],
log: ['action','HeapAlloc(h, 0, 128) -- small allocation, handled by the LFH component. Segment Heap\'s LFH works similarly to NT Heap\'s LFH: fixed-size slots in UserBlocks, bitmap tracking, randomized selection. Sizes up to ~512 bytes go through this path once LFH is active for the bucket.'],
40
+
action: 'malloc',size: 32,name: 'S',srcLine: 8,
41
+
log: ['action','HeapAlloc(h, 0, 32) -- small allocation, handled by the LFH component. Segment Heap\'s LFH works similarly to NT Heap\'s LFH: fixed-size slots in UserBlocks, bitmap tracking, randomized selection. Sizes up to ~512 bytes go through this path once LFH is active for the bucket.'],
log: ['action','HeapAlloc(h, 0, 4096) -- medium allocation, routed to the Variable Size (VS) backend. VS allocations use _HEAP_VS_SUBSEGMENT structures within larger committed pages. Each VS block has its own encoded header. The VS backend handles sizes roughly 512 bytes to 128 KB.'],
45
+
action: 'malloc',size: 512,name: 'M',srcLine: 10,
46
+
log: ['action','HeapAlloc(h, 0, 512) -- medium allocation, routed to the Variable Size (VS) backend. VS allocations use _HEAP_VS_SUBSEGMENT structures within larger committed pages. Each VS block has its own encoded header. The VS backend handles sizes roughly 512 bytes to 128 KB.'],
log: ['action','HeapAlloc(h, 0, 512000) -- large allocation (> 128 KB). Routed directly to VirtualAlloc with dedicated pages. A _HEAP_LARGE_ALLOC_DATA record tracks the allocation. These are the easiest to find in memory -- each gets its own page-aligned region.'],
log: ['action','HeapAlloc(h, 0, 1024) -- large allocation. In a real system, allocations over 128 KB are routed directly to VirtualAlloc with dedicated pages. A _HEAP_LARGE_ALLOC_DATA record tracks the allocation. These are the easiest to find in memory -- each gets its own page-aligned region.'],
0 commit comments