自学内容网 自学内容网

Windows SEH异常处理讨论

Windows C++程序异常的类型

在Windows C++异常的场景中,我们需要理解以下两种类型的异常:

  1. C++语言抛出的异常。
    • 这是利用C++ throw抛出的exception,利用C++ try-catch即可捕获。
    • 即便是来自于另一个DLL的C++ exception,仍然能利用C++ try-catch捕获。
  2. 访问操作系统受保护内存导致的异常。
    • 利用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触发的内存异常,直接退出。


原文地址:https://blog.csdn.net/Zhanglin_Wu/article/details/143437132

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!