在《Visual C++异常处理机制原理与应用》部分,我们讲解了Visual C++提供的结构化异常处理机制,并对其中比较简单的终止型异常处理程序在部分条件下(自然执行、提前越出、__leave关键字等)进行了反汇编分析。下面我们继续对整个Visual C++结构化异常处理机制进行分析。
本次分析的目标环境是VC++ 6.0,这是一款已经有些“美人迟暮”的集成开发环境。使用它进行分析,主要原因如下:
仍有不少程序使用VC++ 6.0开发和编译(特别是恶意代码、灰产代码等)
Visual Studio的发展是一脉相承的,VC++ 6.0所使用的异常处理机制也为后续Visual Studio所借鉴并发展,大框架并没变。VC++ 6.0中的结构化异常机制相对比较简单,却囊括了所有关键部分。
因此,下面先在VC++ 6.0环境下进行相关分析,完毕后再分析Visual Studio 2017中C/C++结构化异常处理机制。
测试代码与分析
下面是用于测试的代码:
#include <windows.h>
#include <tchar.h>
int ExceptFilterInner(DWORD dwExceptionCode)
{
MessageBox(NULL, TEXT("Inner try`s filter running"), TEXT("Inner Try"), MB_OK);
if (dwExceptionCode == EXCEPTION_ACCESS_VIOLATION)
{
return EXCEPTION_CONTINUE_SEARCH;
}
else
{
return EXCEPTION_CONTINUE_EXECUTION;
}
}
int ExceptFilterOuter()
{
MessageBox(NULL, TEXT("Outer try`s filter running"), TEXT("Outer Try"), MB_OK);
//return EXCEPTION_CONTINUE_SEARCH;
return EXCEPTION_EXECUTE_HANDLER;
}
void RaiseExcept()
{
__try
{
_try
{
*(PDWORD)NULL = 0;
}
__finally
{
MessageBox(NULL, TEXT("Inner::Finally block execute"), TEXT("In Inner::finally"), MB_OK);
}
}
__except (ExceptFilterInner(GetExceptionCode()))
{
MessageBox(NULL, TEXT("Never execute"), TEXT("haha"), MB_OK);
}
}
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd)
{
__try
{
__try
{
RaiseExcept();
}
__finally
{
MessageBox(NULL, TEXT("Outer::Finally block execute"), TEXT("In Outer::finally"), MB_OK);
}
}
__except (ExceptFilterOuter())
{
MessageBox(NULL, TEXT("Outer try catched"), TEXT("haha"), MB_OK);
}
return 0;
}
在WinMain中设置了两个try块,其中:
- 内层的try块为try-finally终止异常处理
- 外层try块为try-except型异常处理,能处理任何异常程序
而在WinMain中被try块保护范围内,调用了一个名为RaiseExcept的函数,该函数被两层try块保护,其中执行了一段导致违规访问的代码:
内层try块为try-finally终止型异常处理
外层try块为try-except异常处理,但不处理违规访问类型的异常
按照之前的分析,执行的顺序是:
RaiseExcept中外层try-except中的过滤函数,而该异常处理块不处理此类型异常;
转而执行WinMain中外层try-except的过滤函数,并确定该异常处理块可以执行此类异常;
执行RaiseExcept内层finally块的代码
执行WinMain内层finally块的代码
执行WinMain中try-except的异常处理代码
异常处理块的安装和卸载
WinMain中的异常处理函数
43: int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd)
44: {
00401220 55 push ebp
00401221 8B EC mov ebp,esp
00401223 6A FF push 0FFh
00401225 68 38 21 42 00 push offset string "In Outer::finally"+18h (00422138)
0040122A 68 60 14 40 00 push offset __except_handler3 (00401460)
0040122F 64 A1 00 00 00 00 mov eax,fs:[00000000]
00401235 50 push eax
00401236 64 89 25 00 00 00 00 mov dword ptr fs:[0],esp
; 省略部分代码
45: __try
00401255 C7 45 FC 00 00 00 00 mov dword ptr [ebp-4],0 ; tryLevel置为0,表明进入外层try块
46: {
47: __try
0040125C C7 45 FC 01 00 00 00 mov dword ptr [ebp-4],1 ; tryLevel置为1,表明进入第二层try块