Windows SEH异常处理讨论
Windows C++程序异常的类型
在Windows C++异常的场景中,我们需要理解以下两种类型的异常:
- C++语言抛出的异常。
- 这是利用C++ throw抛出的exception,利用C++ try-catch即可捕获。
- 即便是来自于另一个DLL的C++ exception,仍然能利用C++ try-catch捕获。
- 访问操作系统受保护内存导致的异常。
- 利用Windows API函数SetUnhandledExceptionFilter()可捕获。
- 利用C++ try-catch无法捕获。
试验与分析
C++语言抛出的异常
即用C++ throw关键字抛出的C++ exception,则利用C++ try-catch即能捕获。已用如下样例代码证明:
// This is the main program, which links to DLL1
// main.cpp
#include <iostream>
int main()
{
try
{
ThrowCppException();
}
catch (std::exception& e)
{
std::cout << e.what() << "\n";
}
std::cout << "Program finished normally\n";
return 0;
}
// DLL1, this is a Dynamic Link Library(DLL)
// DLLTest.h
#pragma once
#if defined(DLL1_EXPORTS)
#define DLL1_SYMBOL_EXPORT __declspec(dllexport)
#else
#define DLL1_SYMBOL_EXPORT __declspec(dllimport)
#endif
DLL1_SYMBOL_EXPORT void ThrowCppException();
// DLL1, this is a Dynamic Link Library(DLL)
// DLLTest.cpp
#include "DLLTest.h"
#include <exception>
void ThrowCppException()
{
throw std::exception("This is an C++ exception");
}
输出如下:
This is an C++ exception
Program finished normally
...\ConsoleApplication2\x64\Debug\ConsoleApplication2.exe (process 52160) exited with code 0.
Press any key to close this window . . .
程序可以对C++异常进行处理,然后再继续正常地运行,或者正常地退出。
内存访问异常
首先,我们来证明,这类异常是无法通过C++ try-catch捕获的。已用如下样例代码证明:
// This is the main program, which links to DLL1
// main.cpp
#include <iostream>
int main()
{
try
{
WriteOSProtectedMemory();
}
catch (std::exception& e)
{
std::cout << e.what() << "\n";
}
std::cout << "Program finished normally\n";
return 0;
}
// DLL1, this is a Dynamic Link Library(DLL)
// DLLTest.h
#pragma once
#if defined(DLL1_EXPORTS)
#define DLL1_SYMBOL_EXPORT __declspec(dllexport)
#else
#define DLL1_SYMBOL_EXPORT __declspec(dllimport)
#endif
DLL1_SYMBOL_EXPORT void ThrowCppException();
// DLL1, this is a Dynamic Link Library(DLL)
// DLLTest.cpp
#include "DLLTest.h"
#include <exception>
void ThrowCppException()
{
throw std::exception("This is an C++ exception");
}
输出如下:
...\ConsoleApplication2\x64\Debug\ConsoleApplication2.exe (process 39044) exited with code -1073741819.
Press any key to close this window . . .
程序直接退出了,没有输出"Program finished normally"。另外,输出"exited with code -1073741819",意味着程序不是正常地结束。
应用层捕获DLL层的内存访问异常
应用层利用Windows API函数SetUnhandledExceptionFilter(),可对DLL层触发的内存访问异常进行捕获。已用以下样例代码证明:
#include "DLLTest.h"
#include <iostream>
#include <Windows.h>
// 试图在App层面捕获全部OS Exception
LONG NTAPI OSExceptionHandlerInApp(EXCEPTION_POINTERS* pExcepInfo)
{
// Do something, for example save the data to local disk
std::cout << "OSExceptionHandlerInApp: SEH handler in App caught unhandled exception\n";
std::cout << "Program could not finish normally\n";
return EXCEPTION_EXECUTE_HANDLER;
}
int main()
{
SetUnhandledExceptionFilter(OSExceptionHandlerInApp);
WriteOSProtectedMemory();
std::cout << "Program finished normally\n";
return 0;
}
// DLL1, this is a Dynamic Link Library(DLL)
// DLLTest.h
#pragma once
#if defined(DLL1_EXPORTS)
#define DLL1_SYMBOL_EXPORT __declspec(dllexport)
#else
#define DLL1_SYMBOL_EXPORT __declspec(dllimport)
#endif
DLL1_SYMBOL_EXPORT void WriteOSProtectedMemory();
// DLL1, this is a Dynamic Link Library(DLL)
// DLLTest.cpp
#include "DLLTest.h"
void WriteOSProtectedMemory()
{
char* p = (char*)0x0000000000000078;
*p = 32;
}
输出如下:
OSExceptionHandlerInApp: SEH handler in App caught unhandled exception
Program could not finish normally
...\ConsoleApplication2\x64\Debug\ConsoleApplication2.exe (process 38176) exited with code -1073741819.
Press any key to close this window . . .
可以看出,应用层利用ExceptionHander捕获了DLL触发的内存异常,然后程序退出。
DLL层调用SetUnhandledExceptionFilter()导致应用层无法捕获内存异常
如果DLL层调用了SetUnhandledExceptionFilter(),则导致应用层调用SetUnhandledExceptionFilter()无效。实际上,是DLL层把应用层注册的ExceptionFilter改写了。已用以下样例代码证明:
#include "DLLTest.h"
#include <iostream>
#include <Windows.h>
// 试图在App层面捕获全部OS Exception
LONG NTAPI OSExceptionHandlerInApp(EXCEPTION_POINTERS* pExcepInfo)
{
// Do something, for example save the data to local disk
std::cout << "OSExceptionHandlerInApp: SEH handler in App caught unhandled exception\n";
std::cout << "Program could not finish normally\n";
return EXCEPTION_EXECUTE_HANDLER;
}
int main()
{
SetUnhandledExceptionFilter(OSExceptionHandlerInApp);
OverwriteOSExceptionHander();
WriteOSProtectedMemory();
std::cout << "Program finished normally\n";
}
// DLL1, this is a Dynamic Link Library(DLL)
// DLLTest.h
#pragma once
#if defined(DLL1_EXPORTS)
#define DLL1_SYMBOL_EXPORT __declspec(dllexport)
#else
#define DLL1_SYMBOL_EXPORT __declspec(dllimport)
#endif
DLL1_SYMBOL_EXPORT void WriteOSProtectedMemory();
// DLL1, this is a Dynamic Link Library(DLL)
// DLLTest.cpp
#include "DLLTest.h"
#include <iostream>
#include <Windows.h>
void WriteOSProtectedMemory()
{
char* p = (char*)0x0000000000000078;
*p = 32;
}
// 在DLL中截获OS Exception
LONG NTAPI OSExceptionHandlerInDLL(EXCEPTION_POINTERS* pExcepInfo)
{
std::cout << "OSExceptionHandlerInDLL: SEH handler in DLL caught unhandled exception\n";
return EXCEPTION_EXECUTE_HANDLER;
}
void OverwriteOSExceptionHander()
{
SetUnhandledExceptionFilter(OSExceptionHandlerInDLL);
}
输出如下:
OSExceptionHandlerInDLL: SEH handler in DLL caught unhandled exception
...\ConsoleApplication2\x64\Debug\ConsoleApplication2.exe (process 20108) exited with code -1073741819.
Press any key to close this window . . .
可以看出,应用层已经无法利用ExceptionHander捕获DLL触发的内存异常,直接退出。