Windows Heap Overrun Monitoring


Heap is one of the most important memory structures used by almost every programs. Windows Heap Manager is at core of heap management. In this blog, I would like to focus on the heap monitoring feature provided by Windows Heap Manager and some other useful tools in the context of detecting heap overrun. All the examples are based on 64-bit Windows 7 Operating System. The memory structures and layouts in 32-bit OS are similar to 64-bit ones, usually it’s just  the size difference (which is half of the 64-bit ones).

Note: A major revamp has done to Windows Heap Manager in Windows Vista and continued in Windows 7. The purpose of those changes is to improve Heap Manager’s performance, security and error resilience and monitoring. There are many great books and articles online about the details of latest Windows Heap Manager, such like [AWD07] and [WININT09]

Standard Heap Monitoring by Windows Heap Manager

Windows Heap Manager provides some basic heap monitoring functionalities for every programs using heap.  It performs limited heap validation in many heap operations. The normal heap block layout managed by Windows Heap Manager looks like this:

Heap Block |Next Heap Block
Heap Header User Allocation Heap Header User Allocation
16 bytes Multiple of 16 bytes 16 bytes Multiple of 16 bytes

As you can imagine, if the memory-write overruns the user allocated portion, then the next heap block’s header will be overwritten. Windows Heap Manager validates the heap header every time when a heap block is used or freed. If it finds the header has been damaged, then it announces a heap overrun has happened. Because of this detection mechanism used by Windows Heap Manager, it is not able to catch the overrun at the time it happens, but only detect it afterwards when the corrupted block is involved in some other operations.

Another important aspect here is that the size of user allocation space is always a multiple of 16 bytes. This means, even if the size of memory requested by user is just 1 byte, the Windows Heap manager will still allocate 16 bytes for that request, in which 15 bytes are marked as unused. In this case, if the overrun happens within the unused user allocation, then obviously the next heap block’s header will not be damaged. Thus, the Windows Heap Manager is not able to detect this kind of overrun. 

Example 1:

The following code request a 1 byte memory using malloc function of Microsoft C Runtime Library (Release Build). malloc internally calls the Windows Heap Manager APIs to full fill the request. Once the memory is obtained, it initializes the 1 byte value to ‘a’, and after that, it overruns the heap with a wild assignment. 

    char* a = (char *) malloc(1);
    a[0] = 'a'; a[24] = 'a';

Debugger was attached once the program starts, and the following debugger output shown is after the malloc call:

Note: Do not use the debugger to launch the example program, the reason will be explained shortly. Also, the debugger needs to able to find the correct symbols for some of system DLLs, and the easiest way is to add Microsoft Public Symbol Server to the sympath

example1!wmain+0x91:
00000001`3ff010c1 ff1571100000    call    qword ptr [example1!_imp_malloc (00000001`3ff02138)] ds:00000001`3ff02138={MSVCR100!malloc (00000000`510c89ec)}
0:000> p
rax=00000000003897b0 rbx=00000000003ae590 rcx=0000000000000003
rdx=00000000003897b0 rsi=00000000003ae570 rdi=00000000003ae5b0
rip=000000013fb410c7 rsp=000000000030fa40 rbp=0000000000000000
r8=0000000000000000  r9=0000000000000000 r10=0000000000000080
r11=0000000000000000 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei pl nz na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206

example1!wmain+0x97:
00000001`3ff010c7 b910000000      mov     ecx,18h

In Windows 64 bit calling convention, the rax stores the return address of malloc function, i.e. address to the memory requested.

Note: For the completed guide of calling conversion, please see [FOGA10] 

To find more information about the heap block the returned address belongs to, use !heap –x command on the address in rax. it will find the heap block contains that particular address:

0:000> !heap -x 00000000003897b0
Entry             User              Heap              Segment               Size  PrevSize  Unused    Flags
-------------------------------------------------------------------------------------------------------------
00000000003897a0  00000000003897b0  0000000000380000  0000000000380000        20      1010        1f  busy

So we can see now, this heap block starting at 00000000003897a0 with its header, then followed by the user allocation portion from 00000000003897b0, which is also the result address in rax. And the size of this block is 20 bytes and only 1 bytes is used so there are 1f bytes unused. We will see in a bit how those numbers come around. And the block is marked as busy because it is allocated.

We can even get much more information about the heap block and its neighboring heap blocks by using !heap –i command on the block entry address:

0:000> !heap -i 00000000003897a0 
Detailed information for block entry 00000000003897a0
Assumed heap       : 0x0000000000380000 (Use !heap -i NewHeapHandle to change)
Header content     : 0x791E4849 0x1F00F699 (decoded : 0x03010002 0x1F000101)
Owning segment     : 0x0000000000380000 (offset 0)
Block flags        : 0x1 (busy )
Total block size   : 0x2 units (0x20 bytes)
Requested size     : 0x1 bytes (unused 0x1f bytes)
Previous block size: 0x101 units (0x1010 bytes)
Block CRC          : OK - 0x3 
Previous block     : 0x0000000000388790
Next block         : 0x00000000003897c0

It shows additional stuff like header content and next heap block (at address 0x00000000003897c0).  In order to get a better view of the heap block’s memory layout, let’s do a direct memory dump at the beginning of the heap block:

0:000> dc 00000000003897a0 
00000000`003897a0  00000000 00000000 791e4849 1f00f699  ........IH.y....
00000000`003897b0  003ae5d0 00000000 00380158 00000000  ..:.....X.8.....
00000000`003897c0  00000000 00000000 fa1f48cb 0000f79a  .........H......
00000000`003897d0  003ae5d0 00000000 00380158 00000000  ..:.....X.8.....

16 bytes value start from address 00000000003897a0 is the head header. You can see the same memory content 791e4849 1f00f699 is shown in the result of !heap –i command above as header content. After the header, it follows with 16 bytes user allocation space (starting at 00000000`003897b0). Although only 1 byte is requested by the user, the Windows Heap Manager still reserves 16 bytes memory space for that request. After the user allocation space, the next memory address 00000000`003897c0 is actually the address of next heap block (the 16 bytes value from 00000000`003897c0 contains the next block’s header). So, we can do the following math:

  • Total block size = size of header + size of user allocation = 0x20 (32) bytes
  • User allocation size = 0x10 (16) bytes, in which, 1 bytes is used (requested), 0x20 – 1 = 0x1F (31) bytes are unused. The unused bytes actually include the unused part of user allocation plus the block header which is not accessible to the user.

Let’s continue with the code execution,  a[0] = 'a'; this is a valid heap usage because it matches the 1 byte requested space. After this assignment, the memory looks like this:

0:000> dc 00000000003897a0 
00000000`003897a0  00000000 00000000 791e4849 1f00f699  ........IH.y....
00000000`003897b0  003ae561 00000000 00380158 00000000  a.:.....X.8.....
00000000`003897c0  00000000 00000000 fa1f48cb 0000f79a  .........H......
00000000`003897d0  003ae5d0 00000000 00380158 00000000  ..:.....X.8.....

So far so good. However, the upcoming assignment, a[24] = 'a';, is totally out of line. It is way beyond the requested memory size. After executing this wild code, the memory looks like this now:

0:000> dc 00000000003897a0 
00000000`003897a0  00000000 00000000 791e4849 1f00f699  ........IH.y....
00000000`003897b0  003ae561 00000000 00380158 00000000  a.:.....X.8.....
00000000`003897c0  00000000 00000000 fa1f4861 0000f79a  ........aH......
00000000`003897d0  003ae5d0 00000000 00380158 00000000  ..:.....X.8.....

As you can see,  the last assignment overrides the header value of next heap block. It broke the coherence of heap structures, and later on if the damaged heap block is involved in some other memory operation, the Windows Heap Manager will detect this error. The following error occurs once the program is allowed to continue execution:

0:000> g
Critical error detected c0000374
(c78.19c8): Break instruction exception - code 80000003 (first chance)
ntdll!RtlReportCriticalFailure+0x2f:
00000000`77a26c9f cc              int     3

0:000> !heap
Details:

Error address: 00000000003897c0
Heap handle: 0000000000380000
Error type heap_failure_buffer_overrun (6)
Parameter 1: 000000000000000a
Last known valid blocks: before - 00000000003897a0, after - 0000000000389fc0
Stack trace:
                0000000077a2a4a8: ntdll!RtlpAnalyzeHeapFailure+0x00000000000003a8
                00000000779bea71: ntdll!RtlpAllocateHeap+0x0000000000002176
                00000000779b29ac: ntdll!RtlAllocateHeap+0x000000000000016c
                00000000779936ba: ntdll!RtlpAllocateUserBlock+0x0000000000000145
                0000000077992b5b: ntdll!RtlpLowFragHeapAllocFromContext+0x00000000000004e7
                00000000779b1c58: ntdll!RtlAllocateHeap+0x00000000000000e4
                0000000078d48a47: MSVCR100!malloc+0x000000000000005b
                000000013fb410dc: example1!wmain+0x00000000000000ac
                000000013fb4139e: example1!__tmainCRTStartup+0x000000000000011a
                000000007785f56d: kernel32!BaseThreadInitThunk+0x000000000000000d
                0000000077993281: ntdll!RtlUserThreadStart+0x000000000000001d
Index   Address  Name      Debugging options enabled
  1:   00080000               
  2:   00010000               
  3:   00020000               
  4:   00380000

Therefore, the heap manager found this heap overrun error later on down the road, while it was trying to use the damaged heap block (00000000003897c0) to full fill another memory allocation request.

Note: error code 0xc0000374 means  STATUS_HEAP_CORRUPTION. It is a new error code added in Windows Vista. It is defined in header file ntstatus.h included in Windows SDK. The completed guild of Windows Error Code can be found at [MS-ERREF].

However, if we change the last assignment from  a[24] = 'a'; to a[1] = 'a';, What then? It is still a heap overrun error because a[1] exceeds the 1 bytes limit as well, although it is not as wild as a[24].  Can the Windows Heap Manager still catch this error?

0:000> dc 00000000003897a0 
00000000`003897a0  00000000 00000000 791e4849 1f00f699  ........IH.y....
00000000`003897b0  003a6161 00000000 00380158 00000000 aa:.....X.8.....
00000000`003897c0  00000000 00000000 fa1f48cb 0000f79a  .........H......
00000000`003897d0  003ae5d0 00000000 00380158 00000000  ..:.....X.8.....

The answer is no. Windows Heap Manager cannot detect this variant of overrun error because the overrun is within the unused user allocation of heap block. As shown above, the next heap block’s header is still intact and there is no way for Windows Heap Manager to notice any problems. For this simple example, it got lucky and it can continue execution to exit without any more problems, but for many other applications, this kind of heap overrun just like a time bomb waiting there to cause bigger problems.

Is there a way to catch this kind of just off by 1 overrun?

Special Debug Heap Monitoring

Instead of using the standard heap, Windows Heap Manager uses a special debug heap for the processes that launched by debugger. This debug heap has a different heap block layout:

Heap Block |Another Heap Block
Heap Header User Allocation Tail Checking Pattern Heap Extra Heap Header User Allocation Tail Checking Pattern Heap Extra
16 bytes Multiple of 16 bytes 16 bytes 16–31 bytes 16 bytes Multiple of 16 bytes 16 bytes 16– 31 bytes

Note: You can force a debugger launched process to use the standard heap instead of the special debug heap by setting _NO_DEBUG_HEAP environment variable to 1, or the -hd debugger command-line option. By just attaching the debugger to an existing process, it will NOT change from the standard heap to the debugger heap. That’s why the debugger must be attached after the example program started in Example 1.

Comparing to the basic heap block created by standard heap, the debug heap added more data after user allocation portion of each heap block. First data after user allocation is the Tail Checking Pattern. The pattern is a 16 bytes of ab with potential padding which aligns on 16-byte boundary. After that, it is followed by heap extra data (it is 16  bytes or more depends on padding). The Heap Manager will validate the Tail Checking Pattern on release of heap block to check if it has been changed by any heap overruns.

In the last example, the heap overrun within unused user heap space has slipped through the detection of Windows Heap Manager, but it will be caught by this special debugger heap because the Tail Checking Pattern covers unused portion of user space as well. The detection is still not in real time: The overrun can only be found when that heap block is released, not at the time it actually happened.

This detection mechanism works well if the overrun is a continuous one from user allocated space. However, if the memory-write jumps over the Tail Checking Pattern and lands before next head header (i.e. writes in the padding area or heap extra area), then the Tail Checking Pattern is left unchanged by this overrun. Therefore, for this kind of overrun, it cannot be detected by using the special debug heap.

Example 2:

The following code request a 24 byte memory using malloc function. After that, it tries to perform some heap operation to cause a heap overrun error. This time, we use debugger to launch this program.

    char* b = (char *) malloc(24); 
    b[24] = 'b'; free(b);

The memory layout of the heap block returned by malloc looks like this:

0:000> !heap -i 0000000000234bf0 
Detailed information for block entry 0000000000234bf0
Assumed heap       : 0x0000000000230000 (Use !heap -i NewHeapHandle to change)
Header content     : 0x60B63EB2 0x380068BC (decoded : 0x02070005 0x38000103)
Owning segment     : 0x0000000000230000 (offset 0)
Block flags        : 0x7 (busy extra fill )
Total block size   : 0x5 units (0x50 bytes)
Requested size     : 0x18 bytes (unused 0x38 bytes)
Previous block size: 0x103 units (0x1030 bytes)
Block CRC          : OK - 0x2 
Previous block     : 0x0000000000233bc0
Next block         : 0x0000000000234c40

0:000> dc 0000000000234bf0 
00000000`00234bf0  feeefeee feeefeee 60b63eb2 380068bc  .........>.`.h.8
00000000`00234c00  baadf00d baadf00d baadf00d baadf00d  ................
00000000`00234c10  baadf00d baadf00d
abababab abababab  ................
00000000`00234c20  abababab abababab
feeefeee feeefeee  ................
00000000`00234c30  00000000 00000000 00000000 00000000  ................
00000000`00234c40  feeefeee feeefeee b3b53e62 000069ba  ........b>...i..
00000000`00234c50  00237ee0 00000000 00230158 00000000  .~#.....X.#.....
00000000`00234c60  feeefeee feeefeee feeefeee feeefeee  ................

The heap header is still 16 bytes starting from address 00000000`00234bf0. Then it is followed by 32 bytes user allocation starting from 00000000`00234c00. The heap manager always allocated in multiple of 16 bytes, even if the user only requested 24 bytes. Next, the debug heap put 16-byte Tail Checking Pattern immediately after the last byte of used portion of user allocation space, which is from 00000000`00234c18 to 00000000`00234c27. In order to get aligned at 16 bytes boundary, 8 bytes of padding is added after the Tail Checking Pattern. Lastly, there is another 16 bytes of heap extra starting from 00000000`00234c30. The next heap block starts next 16 bytes boundary (at address 00000000`00234c40).

  • Total block size = size of header + size of user allocation + Tail Checking Pattern + Padding + Heap Extra  = 0x50 (80) bytes
  • User allocation size = requested size + extra filling = 0x40 (64) bytes, in which, 0x18 (24) bytes is used (requested), 0x50 – 0x18 = 0x38 (56) bytes are unused.

Let’s continue executing the next assignment statement a[24] = 'a';. This assignment overruns the allocated heap by 1 byte, hence, it modified the first byte of Tail Checking Pattern. The memory layout after the assignment looks like this:

00000000`00234bf0  feeefeee feeefeee 60b63eb2 380068bc  .........>.`.h.8
00000000`00234c00  baadf00d baadf00d baadf00d baadf00d  ................
00000000`00234c10  baadf00d baadf00d
ababab62 abababab  ........b.......
00000000`00234c20  abababab abababab
feeefeee feeefeee  ................
00000000`00234c30  00000000 00000000 00000000 00000000  ................
00000000`00234c40  feeefeee feeefeee b3b53e62 000069ba  ........b>...i..
00000000`00234c50  00237ee0 00000000 00230158 00000000  .~#.....X.#.....
00000000`00234c60  feeefeee feeefeee feeefeee feeefeee  ................

We have created a heap overrun just by 1 byte, just like we did in the last part of example 1. It is well within the unused user allocation space. Let the program to continue:

0:000> g
HEAP: Heap block at 0000000000234BF0 modified at 0000000000234C18 past requested size of 18
(2a54.1af0): Break instruction exception - code 80000003 (first chance)
ntdll!RtlpBreakPointHeap+0x21:
00000000`77a13741 cc              int     3

0:000> k
Child-SP          RetAddr           Call Site
00000000`001cf678 00000000`77a225a1 ntdll!RtlpBreakPointHeap+0x21
00000000`001cf680 00000000`779c887e ntdll!RtlpCheckBusyBlockTail+0x211
00000000`001cf6c0 00000000`77a2c499 ntdll!RtlpValidateHeapEntry+0x45f0e
00000000`001cf700 00000000`779cd0fe ntdll!RtlDebugFreeHeap+0xb9
00000000`001cf760 00000000`779b2075 ntdll!RtlpFreeHeap+0x1b07e
00000000`001cfaa0 00000000`77862c7a ntdll!RtlFreeHeap+0x1a2
00000000`001cfb20 00000000`78d68ac4 kernel32!HeapFree+0xa
00000000`001cfb50 00000001`3fd31152 MSVCR100!free+0x1c
00000000`001cfb80 00000001`3fd3138e example2!wmain+0x122
00000000`001cfbb0 00000000`7785f56d example2!__tmainCRTStartup+0x11a
00000000`001cfbe0 00000000`77993281 kernel32!BaseThreadInitThunk+0xd
00000000`001cfc10 00000000`00000000 ntdll!RtlUserThreadStart+0x1d

As you see, The debug heap has detected this off-by-1 overrun. The call stack shows that the error is found when heap is being released. Hence, the debug heap is able to catch overruns which are within the unused user allocation area. By the way, this detection is also postmortem in nature as you might already notice.

Let’s try something else now. If we change the assignment from a[24] = 'a'; to a[40] = 'a';, then the memory-write skips over the Tail Checking Pattern and lands on the padding area, as shown on the memory layout below:

00000000`00234bf0  feeefeee feeefeee 60b63eb2 380068bc  .........>.`.h.8
00000000`00234c00  baadf00d baadf00d baadf00d baadf00d  ................
00000000`00234c10  baadf00d baadf00d
abababab abababab  ................
00000000`00234c20  abababab abababab
feeefe62 feeefeee  ........b.......
00000000`00234c30  00000000 00000000 00000000 00000000  ................
00000000`00234c40  feeefeee feeefeee b3b53e62 000069ba  ........b>...i..
00000000`00234c50  00237ee0 00000000 00230158 00000000  .~#.....X.#.....
00000000`00234c60  feeefeee feeefeee feeefeee feeefeee  ................

In this case, the Tail Checking Pattern is unchanged by the overrun, Thus, it cannot be detected by the debug heap. If the program is allowed to continuous, it will finish without rising any problems.

More interestingly, if the debug heap is not used (i.e. the program is not spawned by the debugger), this overrun, a[40] = 'a';, would have landed on the heap header of next heap block. And for that, the Windows Heap Manager could detect it. This should give you a basic idea on why having the debugger could make some of the heap corruption bug to disappear.

PageHeap Monitoring

So far we have seen the default Windows Heap Manager monitoring and detection ability, as well as the the ability of the special debug heap. However, both of them have certain limitations on detecting heap overruns.

To overcome those limitations, Microsoft has a tool called PageHeap which provides better heap monitoring and detection features than last two. As time of this writing, the PageHeap tool has been integrated into Gflags tool as well as AppVerifier tool. Its usage are well documented in MSDN and widely discussed in the community. Both books [AWD07] and [WININT09] have chapters on it too.

Using either Gflags or AppVerifier, a Full Page Heap layout can be enabled for each heap block as shown below

Full page heap block structure 

 

 

 

 

 

 

Diagram source: [MSDN-TSPHB]

And the actually memory layout for such a heap block looks like this (the debug output is showing a busy heap block): 

0:000> dc 3e50ff0-40
00000000`03e50fb0  abcdbbbb 00000000 03d21000 00000000  ................
00000000`03e50fc0  00000001 00000000 00001000 00000000  ................
00000000`03e50fd0  00000000 00000000 00000000 00000000  ................
00000000`03e50fe0  00227e70 00000000 00000000 dcbabbbb  p~".............
00000000`03e50ff0  d0d0d0c0 d0d0d0d0 d0d0d0d0 d0d0d0d0  ................
00000000`03e51000  ???????? ???????? ???????? ????????  ????????????????
00000000`03e51010  ???????? ???????? ???????? ????????  ????????????????
00000000`03e51020  ???????? ???????? ???????? ????????  ????????????????

The green part is the Prefix Pattern for that heap block starting with abcdbbbb and ending with dcbabbbb. Next, the purple part is the user allocation. There is 1 byte of c0 and 15 bytes of d0 in the user allocation, and that means the user only requested 1 byte of memory. The rest 15 bytes of d0 are the suffix pattern. Lastly, it is the non-accessible page (the red part) after the last d0 .

Let’s assume char* a is pointed to the valid address of that 1 byte allocation. A heap overrun, such as a memory write to the address at a[24], will immediately cause a access violation exception:

(297c.62c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
example1!wmain+0x19:

00000001`3f9a1019 c6401862        mov     byte ptr [rax+18h],62h ds:00000000`03e51008=??

You can see the access violation happens at exact the same time the mov instruction trying to access an address in the red part of the memory layout. So first time ever, the heap overrun is caught right at when it happens.

But what if the heap overrun is within the suffix pattern, such as a off-by-1 overrun (a memory write to address at a[1])? In that case, the heap overrun will be detected only when that block is released. That’s when the suffix pattern validation is done, just like the debug heap. If the program is under debugger, the following error is shown when the program is trying to free(a)

===========================================================
VERIFIER STOP 000000000000000F: pid 0x2898: corrupted suffix pattern

    0000000003F31000 : Heap handle
    0000000004060FF0 : Heap block
    0000000000000001 : Block size
    0000000004060FF1 : corruption address

Note: When PageHeap is enabled, the Heap Stack Trace is also enable for every heap block. The Stack Trace records the entire call stack of last heap operation done to that heap block. For example, the !heap –p –a memory_address command finds the heap block contains the specific memory address, and it also displays the stack trace:

0:000> !heap -p -a 0000000003E50FF0
    address 0000000003e50ff0 found in
    _DPH_HEAP_ROOT @ 3d21000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                 3d24888:          3e50ff0                1 -          3e50000             2000
    000007fef3198513 verifier!AVrfDebugPageHeapAllocate+0x000000000000026f
    0000000077a39901 ntdll!RtlDebugAllocateHeap+0x0000000000000031
    00000000779fe6ea ntdll!RtlpAllocateHeap+0x000000000004bd2a
    00000000779b29ac ntdll!RtlAllocateHeap+0x000000000000016c
    000000006e5f8a47 MSVCR100!malloc+0x000000000000005b
    000000013fff1011 testprogram!wmain+0x0000000000000011
    000000013fff1262 testprogram!__tmainCRTStartup+0x000000000000011a
    000000007785f56d kernel32!BaseThreadInitThunk+0x000000000000000d
    0000000077993281 ntdll!RtlUserThreadStart+0x000000000000001d

From the stack trace, you can see the last operation is the allocation of that heap block. 

Gflags and AppVerifier also provide a slimmed down version of Full Page Heap called Normal Page Heap. The difference is that Normal Page Heap doesn’t have the non-accessible page appended after the suffix pattern. Therefore, using Normal Page Heap, it is not possible to catch heap overrun instantly.

Microsoft Visual Studio C Runtime (Debug Version)

The debug version of Microsoft Visual Studio C Runtime library (msvcrtXXd) also has its own Heap monitoring feature. The mechanism it uses is similar to PageHeap.

  

Takeaways

Windows Heap Manager provides different levels of heap monitoring in junction with debugger, gflags or AppVerifier. With PageHeap, a few overruns can be caught at the exact moment when it happens, but in most of time, the detection is only postmortem (i.e. the overrun is detected after it has happened). This nature of heap overrun make it become one of most difficult bug to solve. [AWD07] has more information on how to deal with it and how to avoid the overrun at the first place.

Furthermore, having debugger might trigger Windows heap Manager to use debug heap, which has different heap block layout. This is a side effect of debugger. As demonstrated above, if the debugger is in the picture, this side effect could actually delays the exposure of certain heap corruption bugs and even prevents them from happening at all. Therefore, be careful, and always be aware of the implication of the environment the program is running under.

 

References:

1. [AWD07] Advanced Windows Debugging Chapter 6, Chapter 15

2. [WININT09] Windows Internals, Fifth Edition Chapter 9

3. [MS-ERREF] Windows Error Codes

4. [FOGA10] Calling conventions for different C++ compilers and operating systems

5. [MSDN-DTFW] Debugging Tools For Windows - !heap

6. [MSDN-TSPHB] The Structure of a Page Heap Block


Comments (3)
  1. Adrián says:

    This information was great to understand Win7 heap that I'm trying to debug right now. I'm in some trouble though. I'm using immunity debugger, and the crash I'm trying to debug occurs when the application reads the configuration file, so I cannot attach at a later time, and I'm not able to make Immunity use the default heap instead of the special debug one 🙁 Is there any trick here?

    Thanks!

  2. Ls says:

    very easy to understand! thanks.

  3. George says:

    Nice article, even though someone should have edited it for grammar. The major problem is that the !heap command is completely useless because the public symbol files do not contain the full type information, as detailed by the debugger.

Comments are closed.

Skip to main content