How to Find the context record for user mode exception on X64

I see a KERNELBASE!UnhandledExceptionFilter on the stack. That seems like a good thing to focus on.

If this were x86, you could easily get an EXCEPTION_POINTERS struct out of the first parameter to KERNELBASE!UnhandledExceptionFilter. From there, you would have access to the EXCEPTION_RECORD and CONTEXT. The procedure is described in this KB article.

The same method works for x64 processes with one caveat. Due to the nature of the x64 calling convention, it is harder to retrieve the actual argument to KERNELBASE!UnhandledExceptionFilter since it is stored in a register rather than on the stack.

I recently found a debugger extension called CMKD that automates the task of hunting for the first 4 args in the x64 calling convention rather than blindly displaying stack values like kb and kv. This can be done by hand but it is a rather lengthy and error-prone process -- better to let an extension take a crack at it first.

With it, you can do something like this:

0:000> !cmkd.stack -p
Call Stack : 15 frames
## Stack-Pointer    Return-Address   Call-Site       
[...]
03 000000aea3dae7e0 00007fff1e906b14 KERNELBASE!UnhandledExceptionFilter+196 
    Parameter[0] = 000000aea3dae930
    Parameter[1] = (unknown)       
    Parameter[2] = (unknown)       
    Parameter[3] = (unknown)       
[...]

And, now we have an EXCEPTION_POINTERS* in Parameter[0].

0:000> dt 000000ae`a3dae930 EXCEPTION_POINTERS
ConsoleApplication2!EXCEPTION_POINTERS
   +0x000 ExceptionRecord  : 0x000000ae`a3daf850 _EXCEPTION_RECORD
   +0x008 ContextRecord    : 0x000000ae`a3daf240 _CONTEXT

We can see in my example that a C++ exception was thrown...

0:000> .exr 000000ae`a3daf850
ExceptionAddress: 00007fff1bfeab78 (KERNELBASE!RaiseException+0x0000000000000068)
   ExceptionCode: e06d7363 (C++ EH exception)
  ExceptionFlags: 00000001
NumberParameters: 4
   Parameter[0]: 0000000019930520
   Parameter[1]: 000000aea3daf9b0
   Parameter[2]: 00007ff6f50024a8
   Parameter[3]: 00007ff6f5000000
  pExceptionObject: 000000aea3daf9b0
  _s_ThrowInfo    : 00007ff6f50024a8

Hopefully this helps. Good luck. :)


Another method fox x64 case doesn't require extension but is relying on two unstable facts:

  • windbg ability to reconstruct registers for a specific frame
  • the fact that WerpReportFault stores EXCEPTION_POINTERS address in rdi before passing it to WerpReportFaultInternal (it is the case at least for kernel32.dll 6.1.7601.23915 (win7sp1_ldr.170913-0600)

Exception pointer can be extracted as an rdi value of the WerpReportFault's frame:

0:007> k
 # Child-SP          RetAddr           Call Site
00 00000000`0868dcd8 000007fe`fcf61430 ntdll!NtWaitForMultipleObjects+0xa
01 00000000`0868dce0 00000000`76fb16e3 KERNELBASE!WaitForMultipleObjectsEx+0xe8
02 00000000`0868dde0 00000000`7702b8b5 kernel32!WaitForMultipleObjectsExImplementation+0xb3
03 00000000`0868de70 00000000`7702ba37 kernel32!WerpReportFaultInternal+0x215
04 00000000`0868df10 00000000`7702ba8f kernel32!WerpReportFault+0x77
05 00000000`0868df40 00000000`7702bcac kernel32!BasepReportFault+0x1f
06 00000000`0868df70 00000000`77230108 kernel32!UnhandledExceptionFilter+0x1fc
07 00000000`0868e050 00000000`771c7958 ntdll! ?? ::FNODOBFM::`string'+0x2025
08 00000000`0868e080 00000000`771d812d ntdll!_C_specific_handler+0x8c
09 00000000`0868e0f0 00000000`771c855f ntdll!RtlpExecuteHandlerForException+0xd
0a 00000000`0868e120 00000000`771fbcb8 ntdll!RtlDispatchException+0x45a
0b 00000000`0868e800 000007fe`fe03df54 ntdll!KiUserExceptionDispatch+0x2e
0c 00000000`0868ef00 000007fe`fe03e1b6 gdi32!pmfAllocMF+0x2b0
0d 00000000`0868ef70 000007fe`fb10a646 gdi32!GetEnhMetaFileW+0x32
0e 00000000`0868efb0 000007fe`fb0c4959 GdiPlus!GpMetafile::GpMetafile+0x1c6
0f 00000000`0868f150 00000001`40001c35 GdiPlus!GdipCreateBitmapFromFile+0xc5

0:007> .frame /r 04
    04 00000000`0868df10 00000000`7702ba8f kernel32!WerpReportFault+0x77
    rax=00000000c0000001 rbx=0000000000000000 rcx=0000000002660000
    rdx=0000000000000001 rsi=0000000000000001 rdi=000000000868e0b0
    rip=000000007702ba37 rsp=000000000868df10 rbp=000000000868ff90
     r8=000000000868d3f8  r9=000000000868d560 r10=0000000000000000
    r11=0000000000000246 r12=000000000868e0b0 r13=0000000000000000
    r14=0000000000000002 r15=0000000000000000
    iopl=0         nv up ei pl zr na po nc
    cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000244
    kernel32!WerpReportFault+0x77:
    00000000`7702ba37 8b0d27ff0600    mov     ecx,dword ptr [kernel32!RestrictedUserHandle+0xc (00000000`7709b964)] ds:00000000`7709b964=00000000

0:007> .exptr 000000000868e0b0

----- Exception record at 00000000`0868ecf0:
ExceptionAddress: 000007fefe03df54 (gdi32!pmfAllocMF+0x00000000000002b0)
   ExceptionCode: c0000006 (In-page I/O error)
  ExceptionFlags: 00000000
NumberParameters: 3
   Parameter[0]: 0000000000000000
   Parameter[1]: 0000000002610028
   Parameter[2]: 00000000c00000be
Inpage operation failed at 0000000002610028, due to I/O error 00000000c00000be

----- Context record at 00000000`0868e800:
rax=0000000002610000 rbx=000000000e5fe7c0 rcx=0000000000006894
rdx=0000000000000000 rsi=0000000000000000 rdi=0000000000000000
rip=000007fefe03df54 rsp=000000000868ef00 rbp=0000000000000104
 r8=000000000868ee38  r9=0000000000000104 r10=0000000000000000
r11=0000000000000286 r12=0000000000000001 r13=000000006d9cf760
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=00010206
gdi32!pmfAllocMF+0x2b0:
000007fe`fe03df54 81782820454d46  cmp     dword ptr [rax+28h],464D4520h ds:00000000`02610028=????????

I did some research and found two ways of getting it without any plugins, relying on WinDBG magic, etc.

First, invoke k command in WinDBG. Find a portion of stack like this:

Child-SP          RetAddr
00000000`0ab7d9d0 00007ff9`98baed2d exception handler
00000000`0ab7da10 00007ff9`98b16c86 ntdll!RtlpExecuteHandlerForException+0xd
00000000`0ab7da40 00007ff9`98badc5e ntdll!RtlDispatchException+0x3c6
00000000`0ab7e140 00007ff9`98b5b48a ntdll!KiUserExceptionDispatch+0x2e
00000000`0ab7e860 00007ff9`96925531 Function that crashed

Now you can find what you want in local variables:

Option 1: Use EXCEPTION_POINTERS structure saved on stack

.exptr 00000000`0ab7da10 - 0x20

Option 2: Use CONTEXT and EXCEPTION_RECORD separately

.cxr 00000000`0ab7e140
.exr 00000000`0ab7e140 + @@c++(sizeof(ntdll!_CONTEXT)) + 0x20

Tags:

64 Bit

Windbg