windows异常处理机制

相关术语

  • SEH:结构化异常处理

  • VEH向量化异常处理

  • TopLevelEH顶层异常处理

每种处理方法都可以三种值,在不同处理方法下意义不同

  • EXCEPTION_EXECUTE_HANDLER

    该异常被处理,从异常处下一条指令开始执行

  • EXCEPTION_CONTINUE_SEARCH

    不处理该异常

  • EXCEPTION_CONTINUE_EXECUTIOIN

    忽略异常,从异常处继续执行

如果当前进程正在被调试,调试器也可以返回值

  • DBG_CONTINUE

    等同于EXCEPTION_CONTINUE_EXECUTION

  • DBG_EXCEPTION_NOT_HANDLED

    等同于EXCEPTION_CONTINUE_SEARCH

异常处理器包含内核异常处理和R3异常处理

R3程序产生异常时,异常处理器处理顺序:

  1. 交给调试器

  2. 执行VEH

  3. 执行SEH

  4. TopLevelEH

  5. 交给调试器

  6. 调试异常端口通知csrss.exe

第一次交给调试器

判断是否处理该异常

  • 处理返回DBG_CONTINUE

  • 否则返回DBG_EXECUTE_NOT_HANDLED

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
while(!bExit)
{
DWORD dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
DEBUG_EVENT debugEvent;
WaitForDebugEvent(&debugEvent, INFINITE);
switch ( debugEvent.dwDebugEventCode )
{
case EXCEPTION_DEBUG_EVENT:
{
EXCEPTION_DEBUG_INFO* pExcpInfo = &debugEvent.u.Exception;
if ( MessageBox(0,_T("处理该异常?"), _T("我是调试器"),MB_YESNO)==IDYES )
{
dwContinueStatus = DBG_CONTINUE;
//...
}
}
break;
//...
}
ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, dwContinueStatus);
}

执行VEH

如果没有被调试,或者调试器不处理该异常(返回DBG_EXCEPTION_NOT_HANDLED),就会把异常交给VEH

VEH是个链表,每个VEH按顺序调用

VEH在SEH之前执行,不依赖某一线程,返回值:

  • EXCEPTION_CONTINUE_SEARCH 交给下一个VEH

  • EXCEPTION_CONTINUE_EXECUTION 认为已被处理,退出异常处理器在异常指令处继续执行

  • EXCEPTION_EXECUTE_HANDLED 无效,等同于EXCEPTION_CONTINUE_SEARCH

1
2
3
4
5
6
7
8
9
10
LONG NTAPI FirstVectExcepHandler( PEXCEPTION_POINTERS pExcepInfo )
{
if( ... )
{
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}
//参数1=1表示插入Veh链的头部,=0表示插入到VEH链的尾部
AddVectoredExceptionHandler( 1, &FirstVectExcepHandler );

执行SEH

当所有的VEH都不处理该异常,该异常就会让SEH处理,SEH只能处理自己线程的异常,基于线程栈的异常处理机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
LONG FirstSEHer( PEXCEPTION_POINTERS pExcepInfo )
{
TCHAR* pTitle = _T("第一个SEH处理器");
_tprintf( _T("[EH.Exe] [SEH][1] in \n") );
LONG nRet = ShowSelectMessageBox(pTitle);
_tprintf( _T("[EH.Exe] [SEH][1] out \n") );
return nRet;
}
LONG SecondSEHer( PEXCEPTION_POINTERS pExcepInfo )
{
TCHAR* pTitle = _T("第二个SEH处理器");
_tprintf( _T("[EH.Exe] [SEH][2] in \n") );
LONG nRet = ShowSelectMessageBox(pTitle);
_tprintf( _T("[EH.Exe] [SEH][2] out \n") );;
return nRet;
}
void ExcepFunction()
{
__try
{
__try
{
__try
{

_tprintf( _T("[EH.Exe] *[CALL] int 3\n") );
__asm int 3;
}
__finally
{
printf( "[EH.Exe] *[SEH][0] finally call...\n" );
}
}
__except( FirstSEHer(GetExceptionInformation()) )
{
_tprintf( _T("[EH.Exe] [SEH][1] 被俺处理了~(只有返回EXCEPTION_EXECUTE_HANDLER才会走到这里)\n"));
}
}
__except( SecondSEHer(GetExceptionInformation()) )
{
_tprintf( _T("[EH.Exe] [SEH][2] 被俺处理了(只有返回EXCEPTION_EXECUTE_HANDLER才会走到这里)\n"));
}
}

执行TopLevelEH

利用SEH实现,但是可以处理所有线程抛出的异常。如果正在被调试,则顶层异常会被忽略。

返回值:

  • EXCEPTION_CONTINUE_SEARCH

    查注册表,如果存在调试器,则抛给调试器

    注册表路径:KLM\software\microsoft\windows nt\currentvsrsion\aedebug。如果Auto==1,Debugger!=NULL

  • EXCEPTION_EXECUTE_HANDLER

    杀死该进程

  • EXCEPTION_CONTINUE_EXECUTION

    和SEH一样

1
2
3
4
5
6
7
8
9
10
LONG NTAPI TopLevelExcepFilter( PEXCEPTION_POINTERS pExcepInfo )
{
TCHAR* pTitle = _T("*顶级* 异常处理器");
_tprintf( _T("[EH.Exe] [TOP] in \n") );
LONG nRet = ShowSelectMessageBox(pTitle);
_tprintf( _T("[EH.Exe] [TOP] out \n") );;
return nRet;
}
//注册
SetUnhandledExceptionFilter( &TopLevelExcepFilter );

再次交给调试器

返回值

  • DBG_CONTINUE

    和第一次相同

  • DBG_EXCEPTION_NOT_HANDLED

    直接杀死进程

调试异常端口通知csrss.exe

上面的异常处理机制都没有处理该异常,则调用csrss.exe,弹出一个对话框

参考资料

[1] [原创]白话windows之四 异常处理机制(VEH、SEH、TopLevelEH…)