How to distinguish between 1st and 2nd chances
Sometimes we look for Early Crash Dump pattern but information about whether an exception was first-chance or second-chance is missing from a crash dump file name or in a crash dump itself, for example:
This dump file has an exception of interest stored in it.
The stored exception information can be accessed via .ecxr.
(1254.1124): Access violation - code c0000005 (first/second chance not available)
TestDefaultDebugger64!CTestDefaultDebuggerDlg::OnBnClickedButton1:
00000000`00401570 c704250000000000000000 mov dword ptr [0],0 ds:00000000`00000000=????????
If we recall that first-chance exceptions don’t leave any traces on user space thread stacks (see Interrupts and Exceptions Explained, Part 6 for details) then we won’t see any exception codes on thread raw stack:
0:000> !teb
TEB at 000007fffffde000
ExceptionList: 0000000000000000
StackBase: 0000000000130000
StackLimit: 000000000012b000
SubSystemTib: 0000000000000000
FiberData: 0000000000001e00
ArbitraryUserPointer: 0000000000000000
Self: 000007fffffde000
EnvironmentPointer: 0000000000000000
ClientId: 0000000000001254 . 0000000000001124
RpcHandle: 0000000000000000
Tls Storage: 000007fffffde058
PEB Address: 000007fffffd5000
LastErrorValue: 0
LastStatusValue: c0000034
Count Owned Locks: 0
HardErrorMode: 0
0:000> s -d 000000000012b000 0000000000130000 c0000005
However, we would definitely see it on raw stack in second-chance exception crash dump:
0:000> s -d 000000000012b000 0000000000130000 c0000005
00000000`0012f000 c0000005 00000000 00000000 00000000 .
Using raw stack observations we can even tell when a crash dump was saved from a debugger handling a second-chance exception or saved by some postmortem debugger afterwards. For example, on my Vista x64 I see the following difference:
raw stack from a crash dump saved from WinDbg after receiving second-chance exception:
00000000`0012e278 00000000`00000000
00000000`0012e280 00000000`00000000
00000000`0012e288 00000000`7790032c kernel32!IsDebugPortPresent+0×2c
00000000`0012e290 00000000`00000000
00000000`0012e298 00000000`00000000
00000000`0012e2a0 00000000`00000000
00000000`0012e2a8 00000000`7790032c kernel32!IsDebugPortPresent+0×2c
00000000`0012e2b0 00000001`00010000
00000000`0012e2b8 00000000`00000000
00000000`0012e2c0 00000000`00000000
00000000`0012e2c8 00000000`77b63c94 ntdll! ?? ::FNODOBFM::`string’+0xbd14
00000000`0012e2d0 00000000`00000000
00000000`0012e2d8 00000000`0012e420
00000000`0012e2e0 00000000`77b63cb0 ntdll! ?? ::FNODOBFM::`string’+0xbd30
00000000`0012e2e8 00000000`7793cf47 kernel32!UnhandledExceptionFilter+0xb7
00000000`0012e2f0 ffffffff`ffffffff
00000000`0012e2f8 00000000`00000000
00000000`0012e300 00000000`00000000
00000000`0012e308 00000000`00000000
00000000`0012e310 00000000`00318718
00000000`0012e318 00000000`7799eb89 user32!ImeWndProcWorker+0×331
00000000`0012e320 00000000`00000000
00000000`0012e328 00000000`00000000
00000000`0012e330 00000000`00000000
00000000`0012e338 00000000`004189b0 TestDefaultDebugger64!_getptd_noexit+0×80
00000000`0012e340 00000000`01fb4b90
00000000`0012e348 00000000`0012e928
00000000`0012e350 00000000`00000000
00000000`0012e358 00000000`00418a50 TestDefaultDebugger64!_getptd+0×80
00000000`0012e360 00000000`01fb4b90
00000000`0012e368 00000000`0041776d TestDefaultDebugger64!_XcptFilter+0×1d
00000000`0012e370 00000000`0012e4f0
00000000`0012e378 00000000`77aa3cfa ntdll!RtlDecodePointer+0×2a
00000000`0012e380 00000000`00436fc4 TestDefaultDebugger64!__rtc_tzz+0×1f8c
00000000`0012e388 00000000`00000001
00000000`0012e390 00000000`0012f000
00000000`0012e398 00000000`77a60000 ntdll!`string’ <PERF> (ntdll+0×0)
00000000`0012e3a0 00000000`0004c6e1
00000000`0012e3a8 00000000`00000001
00000000`0012e3b0 00000000`0012ff90
00000000`0012e3b8 00000000`77afb1b5 ntdll!RtlUserThreadStart+0×95
00000000`0012e3c0 00000000`0012e420
00000000`0012e3c8 00000000`d4b99e6f
00000000`0012e3d0 00000000`00000000
00000000`0012e3d8 00000000`00000001
00000000`0012e3e0 00000000`0012e4f0
00000000`0012e3e8 00000000`77a8ae4b ntdll!_C_specific_handler+0×8c
raw stack from a crash dump saved by CDB installed as a default postmortem debugger (WER was used to launch it):
0:000> dqs 00000000`0012e278 00000000`0012e3e8
00000000`0012e278 00000000`00000000
00000000`0012e280 00000000`00000000
00000000`0012e288 00000000`00000000
00000000`0012e290 ffffffff`ffffffff
00000000`0012e298 00000000`779257ef kernel32!WerpReportExceptionInProcessContext+0×9f
00000000`0012e2a0 00000000`00000000
00000000`0012e2a8 00000000`0012ff90
00000000`0012e2b0 00000000`0012e420
00000000`0012e2b8 00000000`0041f4dc TestDefaultDebugger64!__CxxUnhandledExceptionFilter+0×5c
00000000`0012e2c0 00000000`00000000
00000000`0012e2c8 00000000`7a8b477b
00000000`0012e2d0 00000000`00000000
00000000`0012e2d8 fffff980`01000000
00000000`0012e2e0 00000000`00000001
00000000`0012e2e8 00000000`7793d07e kernel32!UnhandledExceptionFilter+0×1ee
00000000`0012e2f0 00000000`77b63cb0 ntdll! ?? ::FNODOBFM::`string’+0xbd30
00000000`0012e2f8 000007fe`00000000
00000000`0012e300 00000000`00000000
00000000`0012e308 00000000`00000001
00000000`0012e310 00000000`00000000
00000000`0012e318 00000000`00000000
00000000`0012e320 000007ff`00000000
00000000`0012e328 00000000`00000000
00000000`0012e330 00000000`00000000
00000000`0012e338 00000000`004189b0 TestDefaultDebugger64!_getptd_noexit+0×80
00000000`0012e340 00000000`023f4b90
00000000`0012e348 00000000`77ab8cf8 ntdll!RtlpFindNextActivationContextSection+0xaa
00000000`0012e350 00000000`00000000
00000000`0012e358 00000000`00418a50 TestDefaultDebugger64!_getptd+0×80
00000000`0012e360 00000000`023f4b90
00000000`0012e368 00000000`0041776d TestDefaultDebugger64!_XcptFilter+0×1d
00000000`0012e370 00000000`0012e4f0
00000000`0012e378 00000000`77aa3cfa ntdll!RtlDecodePointer+0×2a
00000000`0012e380 00000000`00436fc4 TestDefaultDebugger64!__rtc_tzz+0×1f8c
00000000`0012e388 00000000`00000001
00000000`0012e390 00000000`0012f000
00000000`0012e398 00000000`77a60000 ntdll!`string’ <PERF> (ntdll+0×0)
00000000`0012e3a0 00000000`0004c6e1
00000000`0012e3a8 00000000`00000001
00000000`0012e3b0 00000000`0012ff90
00000000`0012e3b8 00000000`77afb1b5 ntdll!RtlUserThreadStart+0×95
00000000`0012e3c0 00000000`0012e420
00000000`0012e3c8 00000000`7a8b477b
00000000`0012e3d0 00000000`00000000
00000000`0012e3d8 00000000`00000001
00000000`0012e3e0 00000000`0012e4f0
00000000`0012e3e8 00000000`77a8ae4b ntdll!_C_specific_handler+0×8c
- Dmitry Vostokov @ DumpAnalysis.org -
April 29th, 2008 at 11:28 am
[…] Exception Handling Residue we can use to check for hidden exceptions and differentiate between 1st and 2nd chance exceptions. Code residues are very powerful in reconstructing stack traces manually or looking for partial […]
June 24th, 2008 at 6:01 pm
[…] In one of my previous posts I wrote that in the case of a first-chance exception it is not possible to see it in a process crash dump because the entire exception processing was done in the kernel space (see the post How to distinguish between 1st and 2nd chances): […]
February 4th, 2010 at 11:54 pm
[…] We can double check the first-chance exception dump file to see if it is the right one. Indeed, there are no signs of exception processing on thread raw stack: […]