Modeling Exception Handling

Dmitry Vostokov, 15th June 2009
http://www.dumpanalysis.org

Once we got a process memory dump with a stack overflow pattern[5]. There were hundreds of repeated frames involving exception dispatch:

(d70.111c): Stack overflow - code c00000fd (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00113354 ebx=00113068 ecx=0011307c edx=7c9485ec esi=001133c8 edi=00000000
eip=7c95153e esp=00112ff4 ebp=00113050 iopl=0 nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000 efl=00210212
ntdll!RtlDispatchException+0x8:
7c95153e 56              push    esi

0:000> k 1000
ChildEBP RetAddr  
00113050 7c94855e ntdll!RtlDispatchException+0x8
00113050 7c978002 ntdll!KiUserExceptionDispatcher+0xe
001133b0 7c94855e ntdll!RtlDispatchException+0xf7
001133b0 7c978002 ntdll!KiUserExceptionDispatcher+0xe
00113710 7c94855e ntdll!RtlDispatchException+0xf7
00113710 7c978002 ntdll!KiUserExceptionDispatcher+0xe
00113a70 7c94855e ntdll!RtlDispatchException+0xf7
00113a70 7c978002 ntdll!KiUserExceptionDispatcher+0xe
00113dd0 7c94855e ntdll!RtlDispatchException+0xf7
00113dd0 7c978002 ntdll!KiUserExceptionDispatcher+0xe
00114130 7c94855e ntdll!RtlDispatchException+0xf7
00114130 7c978002 ntdll!KiUserExceptionDispatcher+0xe
00114490 7c94855e ntdll!RtlDispatchException+0xf7
[...]
0020f830 7c978002 ntdll!KiUserExceptionDispatcher+0xe
0020fb90 7c94863c ntdll!RtlDispatchException+0xf7
0020fe70 7c80bee7 ntdll!RtlRaiseException+0x3d
0020fed0 00058dc9 kernel32!RaiseException+0x53
WARNING: Stack unwind information not available. Following frames may be wrong.
0020ff34 000a7d5a Application+0x48dc9
0020ff54 000a7bf2 Application+0x97d5a
0020ff78 000a7de2 Application+0x97bf2
0020ffc0 7c82f23b Application+0x97de2
0020fff0 00000000 kernel32!BaseProcessStart+0x23

If we look at raw stack we could see repeated hidden exception pattern[6]:

0:000> dds esp
00112ff4  00000000
00112ff8  00000000
00112ffc  00000000
00113000  00000000
00113004  00000000
00113008  00000000
0011300c  00000000
00113010  00000000
00113014  00000000
00113018  00000000
0011301c  00000000
00113020  00000000
00113024  00000000
00113028  00000000
0011302c  00000000
00113030  00000000
00113034  00000000
00113038  00000000
0011303c  00000000
00113040  00000000
00113044  00000000
00113048  00000000
0011304c  00000000
00113050  001133b0
00113054  7c94855e ntdll!KiUserExceptionDispatcher+0xe
00113058  00113068
0011305c  0011307c ; .cxr
00113060  00113068 ; .exr 
00113064  0011307c
00113068  c0000025
0011306c  00000001
00113070  001133c8

0:000> .exr 00113068
ExceptionAddress: 7c978002 (ntdll!RtlDispatchException+0x000000f7)
   ExceptionCode: c0000025
  ExceptionFlags: 00000001
NumberParameters: 0

0:000> !error c0000025
Error code: (NTSTATUS) 0xc0000025 (3221225509) - {EXCEPTION}  
    Cannot Continue  Windows cannot continue from this exception.

Looking closer at RaiseException call we noticed that its second parameter is 1:

0:000> kv 100
ChildEBP RetAddr  Args to Child 
[...]
0020fed0 00058dc9 0eedfade 00000001 00000007 kernel32!RaiseException+0x53
[...]

This function has the following prototype[7]:

void RaiseException(
  DWORD dwExceptionCode,
  DWORD dwExceptionFlags,
  DWORD nNumberOfArguments,
  const DWORD* lpArguments
);

What caught our attention was the fact that dwExceptionFlags paramater can have the value EXCEPTION_NONCONTINUABLE and if we continue to execute the code afterwards another exception is raised:

EXCEPTION_NONCONTINUABLE_EXCEPTION

Therefore, to test our hypothesis that the code of Application module attempted to continue code excution after raising non-continuable exception we created a small C++ application:

#include "stdafx.h"
#include <excpt.h> 
#include <windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
    __try
    {
       RaiseException (0xdeaddead, EXCEPTION_NONCONTINUABLE, NULL, NULL);
    }
    __except (EXCEPTION_CONTINUE_EXECUTION)
    {
    }

    return 0;
}

The application raises a non-continuable exception but SEH exception filter attempts to continue execution. If the second parameter is 0 then the program quietly exits as expected. However if this parameter is set to 1 (EXCEPTION_NONCONTINUABLE) then the program crashes:

We then attached WinDbg non-invasively to SEHTest.exe:

0:000> k
ChildEBP RetAddr  
00081128 00000000 ntdll!RtlRaiseException+0xa
 
0:000> r
eax=00081140 ebx=0017ff34 ecx=00000000 edx=ffffffd8 esi=000811bc edi=00000000
eip=77052fb2 esp=00080e58 ebp=00081128 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
ntdll!RtlRaiseException+0xa:
77052fb2 54              push    esp

Unfortunately WinDbg was unable to reconstruct stack trace so we did that manually[8]:

0:000> dds ebp
00081128  000811a4
0008112c  77096fb7 ntdll!RtlDispatchException+0x18a
00081130  00081140
00081134  00000000
00081138  000811bc
0008113c  00081560
00081140  c0000025
00081144  00000001
00081148  000811bc
0008114c  00000000
00081150  00000000
00081154  00000000
00081158  00000000
0008115c  00000000
00081160  00000000
00081164  00000000
00081168  00000000
0008116c  00000000
00081170  00000000
00081174  00000000
00081178  00000000
0008117c  00000000
00081180  00000000
00081184  00000000
00081188  00000000
0008118c  00000000
00081190  00000000
00081194  00000000
00081198  00180000
0008119c  00081000
000811a0  00000000
000811a4  00081548

0:000> k L=00081128 00081128 77052fb2 1000
ChildEBP RetAddr  
00081128 77096fb7 ntdll!RtlRaiseException+0xa
000811a4 77052eff ntdll!RtlDispatchException+0x18a
000811a4 77096fb7 ntdll!KiUserExceptionDispatcher+0xf
00081548 77052eff ntdll!RtlDispatchException+0x18a
00081548 77096fb7 ntdll!KiUserExceptionDispatcher+0xf
000818ec 77052eff ntdll!RtlDispatchException+0x18a
000818ec 77096fb7 ntdll!KiUserExceptionDispatcher+0xf
00081c90 77052eff ntdll!RtlDispatchException+0x18a
00081c90 77096fb7 ntdll!KiUserExceptionDispatcher+0xf
00082034 77052eff ntdll!RtlDispatchException+0x18a
00082034 77096fb7 ntdll!KiUserExceptionDispatcher+0xf
000823d8 77052eff ntdll!RtlDispatchException+0x18a
000823d8 77096fb7 ntdll!KiUserExceptionDispatcher+0xf
0008277c 77052eff ntdll!RtlDispatchException+0x18a
0008277c 77096fb7 ntdll!KiUserExceptionDispatcher+0xf
00082b20 77052eff ntdll!RtlDispatchException+0x18a
00082b20 77096fb7 ntdll!KiUserExceptionDispatcher+0xf
00082ec4 77052eff ntdll!RtlDispatchException+0x18a
00082ec4 77096fb7 ntdll!KiUserExceptionDispatcher+0xf
00083268 77052eff ntdll!RtlDispatchException+0x18a
[...]
0017f7dc 77096fb7 ntdll!KiUserExceptionDispatcher+0xf
0017fb80 77052eff ntdll!RtlDispatchException+0x18a
0017fb80 75b7f328 ntdll!KiUserExceptionDispatcher+0xf
0017ff04 0040104b kernel32!RaiseException+0x58
0017ff44 004011d8 SEHTest!wmain+0x4b
0017ff88 75bde4a5 SEHTest!__tmainCRTStartup+0x10f
0017ff94 770acfed kernel32!BaseThreadInitThunk+0xe
0017ffd4 770ad1ff ntdll!__RtlUserThreadStart+0x23
0017ffec 00000000 ntdll!_RtlUserThreadStart+0x1b

0:000> kv L=00081128 00081128 77052fb2 1000
ChildEBP RetAddr  Args to Child
[...]
0017ff04 0040104b deaddead 00000001 00000000 kernel32!RaiseException+0x58 (FPO: [4,20,0])
[...]

We can also see the same hidden exception on raw stack: c0000025. Therefore we conclude that we sucessfully modeled the problem.

The SEHTest application and its symbols can be downloaded from:

http://www.dumpanalysis.org/Debugged/Jun09/download/SEHTest.zip

[5] http://www.dumpanalysis.org/blog/index.php/2008/06/10/crash-dump-analysi...
[6] http://www.dumpanalysis.org/blog/index.php/2007/02/02/crash-dump-analysi...
[7] http://msdn.microsoft.com/en-us/library/aa909203.aspx
[8] http://www.dumpanalysis.org/blog/index.php/2007/07/25/reconstructing-sta...