Recently a colleague wrote an application which on purpose caused an access violation. He took a memory dump of the process when it access violated and checked the call stack. To his surprise he didn’t see a direct call stack which would point him to the exact location in his source file but the call stack showed as if it was ready for throwing the exception but not yet thrown. This post is not for analyzing why this happened but how can we make sense of such call stacks.
For reference this is how the callstack looks…
WARNING: Frame IP not in any known module. Following frames may be wrong.
To get around this call stack its good to know that for every exception there is an exception context that get saved to memory, which in simple terms means this context record will point to the code that caused the exception. So in our case if we look at the parameters passed to first two functions at the top of the stack ZwRaiseException and KiUserExceptionDispatcher, they get a reference to the context record. By trial and error I’ve found that it’s the second parameter while the first one is the exception record…
0:000> kb 2
ChildEBP RetAddr Args to Child
004eec0c 76fe014d 004eec20 004eec70 00000000 ntdll!ZwRaiseException+0x12
004eec0c 00392d2d 004eec20 004eec70 00000000 ntdll!KiUserExceptionDispatcher+0x29
This is what is stored inside exception record, contains useful information about the exception that happened. It shows us the exception address, exception code etc. But our guy of interest is the context record.
0:000> .exr 004eec20
ExceptionAddress: 00392d2d (CrashingApp!CCrashingAppDlg::OnBnClickedOk+0x0000002d)
ExceptionCode: c0000005 (Access violation)
Attempt to write to address 00000000
The key to retrieve the actual call stack is the context record: 004eec70. The command to set context record or context of execution is .cxr. This command is used as follows…
0:000> .cxr 004eec70
eax=00000000 ebx=00000000 ecx=004efa80 edx=00000000 esi=0065e748 edi=004ef1b8
eip=00392d2d esp=004ef0d4 ebp=004ef1b8 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202
00392d2d c60063 mov byte ptr [eax],63h ds:002b:00000000=??
So what does a context contain. As the above output says, a context is identified by eip, esp, eax, ecx, edx etc. In fact context means registers! When an exception happens all the values in the registers get’s saved and that is what we call as context record. This is because a program in execution always uses the registers for its execution, just change the register values to make the program to do something else. I won't post the structure internal details here since I'm not sure if that's permitted.
Once the above command completes the context is now changed to the actual exception context, take a look at the call stack now…
*** Stack trace for last set context - .thread/.cxr resets it
Bingo! right at the top we now have the culprit, the call stack now makes perfect sense.
Ok what happens if the function KiUserExceptionDispatcher is not always on the call stack, what happens if this exception caused another exception? In such scenarios I normally check the ‘thread’ stack for calls made to KiUserExceptionDispatcher/ZwRaiseException. I also check for well known exception codes like: c0000005. In this case the function addresses on this threads’ stack is as follows…
0x004eeb10 0x77047519 ntdll!_except_handler4+000000cc
0x004eeb28 0x76ffc540 ntdll! ?? ::FNODOBFM::`string'+00000b6e
0x004eeb38 0x7702b459 ntdll!ExecuteHandler2+00000026
0x004eeb50 0x7702b46d ntdll!ExecuteHandler2+0000003a
0x004eeb5c 0x7702b42b ntdll!ExecuteHandler+00000024
0x004eeb80 0x77033c67 ntdll!RtlCallVectoredContinueHandlers+00000012
0x004eeb94 0x77033c48 ntdll!RtlDispatchException+000001b5
0x004eec0c 0x76ff15de ntdll!ZwRaiseException+00000012
0x004eec10 0x76fe014e ntdll!KiUserExceptionDispatcher+0000002a
Now I know this is something interesting, so start exploring the values around the above function’s address: 0x004eec10. I see the following…
0:000> dds 0x004eec10
004eec10 76fe014e ntdll!KiUserExceptionDispatcher+0x2a
004eec14 004eec20 <--- exception record
004eec18 004eec70 <--- context record
004eec20 c0000005 <----- the exception code
Again if we use .cxr on above context record we’ll end up having the correct call stack again. At any point of time if you would like to return back to original state as and when the dump was taken, use .cxr without any parameters.
Any questions, let me know.