2021-09-20

windows下的异常处理机制

SEH(通过 __try __except)

// 02_SEH异常处理.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <Windows.h>
// 编写异常过滤函数用于绝对返回哪个值
DWORD ExceptionFilter(DWORD ExceptionCode, PEXCEPTION_POINTERS ExceptionPointers)
{
    printf("开始处理异常\n");
    if (ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
    {
        // 修复除零异常,然后程序继续执行
        // 修复的原理就是产生异常时的寄存器我们都能获取到,
        // 然后将产生错误的位置,修复以下,比如这个异常的指令是
        //  idiv  eax,dword ptr [ebp-2Ch] 
        int* p = (int*)(ExceptionPointers->ContextRecord->Ebp -0x2c);
        *p = 1;
        return EXCEPTION_CONTINUE_EXECUTION;
    }
    else if (ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
    {
        // 内存访问异常,我无法处理,写入日志之后,直接不回去执行
        return EXCEPTION_EXECUTE_HANDLER;
    }
    else// 异常被修复,返回继续执行
    {
        //其他的异常,我都处理不了,让别人处理吧
        return EXCEPTION_CONTINUE_SEARCH;
    }
}

void Fun()
{
    //被保护的代码
    __try
    {
        //int a = 10,b =0;
        //printf("我要产生异常了\n");
        //int c = a / b;
        //printf("异常被我处理了\n");
        int* p = (int*)0x32432434;
        printf("我要产生异常了\n");
        *p = 100;
        printf("异常被我处理了\n");
    }
    //1  从except的后面继续执行  EXCEPTION_EXECUTE_HANDLER
    //0  处理不了                EXCEPTION_CONTINUE_SEARCH
    //-1 从异常处继续执行        EXCEPTION_CONTINUE_EXECUTION
    //GetExceptionCode()能够获取到是什么异常
    //GetExceptionInformation() 是能够获取到异常的信息 最主要的是寄存器信息
    __except (ExceptionFilter(GetExceptionCode(), GetExceptionInformation()))
    {
        int a = 0;
        a++;
        printf("嘿嘿 掠过一个异常\n");
    }
}


int main()
{
    __try
    {
        Fun();
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        printf("还是让爸爸来处理吧\n");
    }
    system("pause");
}

UEH

#include <iostream>
#include <Windows.h>

// 编写异常过滤函数用于绝对返回哪个值
DWORD ExceptionFilter(DWORD ExceptionCode, PEXCEPTION_POINTERS ExceptionPointers)
{
    printf("开始处理异常\n");
    //if (ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
    //{
    //     修复除零异常,然后程序继续执行
    //     修复的原理就是产生异常时的寄存器我们都能获取到,
    //     然后将产生错误的位置,修复以下,比如这个异常的指令是
    //      idiv  eax,dword ptr [ebp-2Ch] 
    //    int* p = (int*)(ExceptionPointers->ContextRecord->Ebp - 0x2c);
    //    *p = 1;
    //    return EXCEPTION_CONTINUE_EXECUTION;
    //}
    if (ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
    {
        // 内存访问异常,我无法处理,写入日志之后,直接不回去执行
        return EXCEPTION_EXECUTE_HANDLER;
    }
    else// 异常被修复,返回继续执行
    {
        //其他的异常,我都处理不了,让别人处理吧
        return EXCEPTION_CONTINUE_SEARCH;
  
    }
}

void Fun()
{
    //被保护的代码
    __try
    {
        int a = 10,b =0;
        printf("我要产生异常了\n");
        int c = a / b;
        printf("异常被我处理了\n");
        //int* p = (int*)0x32432434;
        //printf("我要产生异常了\n");
        //*p = 100;
        //printf("异常被我处理了\n");
    }
    //1  从except的后面继续执行  EXCEPTION_EXECUTE_HANDLER
    //0  处理不了                EXCEPTION_CONTINUE_SEARCH
    //-1 从异常处继续执行        EXCEPTION_CONTINUE_EXECUTION
    //GetExceptionCode()能够获取到是什么异常
    //GetExceptionInformation() 是能够获取到异常的信息 最主要的是寄存器信息
    __except (ExceptionFilter(GetExceptionCode(), GetExceptionInformation()))
    {
        int a = 0;
        a++;
        printf("嘿嘿 掠过一个异常\n");
    }
}

// 自己定义的顶层异常处理程序
LONG WINAPI TopLevelExceptionHandler(_EXCEPTION_POINTERS* ExceptionInfo)
{
    printf("UEH处理\n");

    // 修正被除数
    int* p = (int*)(ExceptionInfo->ContextRecord->Ebp - 0x2c);
    *p = 1;
    // 修正除数后重新执行产生异常的指令
    return EXCEPTION_CONTINUE_EXECUTION;
    // 处理不了
    return EXCEPTION_CONTINUE_SEARCH;
}

int main()
{
    SetUnhandledExceptionFilter(TopLevelExceptionHandler);
    __try
    {
        Fun();

    }
    __except (EXCEPTION_CONTINUE_SEARCH)
    {
        printf("还是让爸爸来处理吧\n");
    }
    system("pause");
}

VEH(向量化异常处理) AddVectoredExceptionHandler
在程序一开始的地方注册一个回调函数,在程序的任意位置产生异常,都会调用回调函数,这种异常处理是全局的,注册后全进程有效。


VCH(主要做收尾工作)AddVectoredContinueHandler


SEH原理解析

在这里插入图片描述
FS:[0]在用户层,永远是一个线程的TEB结构
什么是TEB(Thread Environment Block)线程环境块。
是一个结构体,里面存储了很多线程相关的信息。
在这里插入图片描述
在这里插入图片描述
TEB的第一个字段是 _NT_TIB _NT_TIB的第一个字段是 ExceptionList。ExceptionList是一个指针
在这里插入图片描述
FS:[0] 是TEB的地址 TEB的第一个字段就是 ExceptionList指针。
所以FS:[0] 是异常链的指针。
SEH异常处理是怎么处理的。
通过FS:[0]找到 异常链,遍历异常链中的 Handler 挨个调用一次。

遍历SHE 处理函数

// 05_遍历SEH处理函数.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <Windows.h>
// 遍历SEH异常处理链
void EnumSEH()
{
    // 获取头节点
    PEXCEPTION_REGISTRATION_RECORD ExceptionList = nullptr;
    __asm push fs : [0]
    __asm pop ExceptionList

    // 遍历 ExceptionList 中的所有函数
    while (ExceptionList != (PEXCEPTION_REGISTRATION_RECORD)-1)
    {
        printf("Address: %p\n", ExceptionList->Handler);
        ExceptionList = ExceptionList->Next;
    }
}

void Fun()
{
    //被保护的代码
    __try
    {

        //遍历SEH
        EnumSEH();
    }
    __except (1)
    {
        printf("嘿嘿 掠过一个异常\n");
    }
}

int main()
{
    __try
    {
        Fun();
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        printf("还是让爸爸来处理吧\n");
    }
}


自己挂载SEH

// 06_自己挂载SEH.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <Windows.h>
// 自定义的 SEH 处理函数
EXCEPTION_DISPOSITION NTAPI ExceptionRoutine(
	_Inout_ struct _EXCEPTION_RECORD* ExceptionRecord,
	_In_ PVOID EstablisherFrame,
	_Inout_ struct _CONTEXT* ContextRecord,
	_In_ PVOID DispatcherContext)
{
	printf("进入到我安装的异常处理器中\n");
	ContextRecord->Ecx = 1;

	return ExceptionContinueExecution;
}

int main()
{
	// 保存原始的处理函数,方便还原
	PEXCEPTION_REGISTRATION_RECORD ExceptionList = nullptr;
	//备份头节点
	__asm push fs : [0]
	__asm pop ExceptionList

	// 安装自己的异常处理函数
	__asm push ExceptionRoutine //在栈上放置异常处理函数地址
	__asm push fs : [0]         //在栈上放置头节点地址
	//	
	//ESP   头节点地址,头节点此时就变为了下一个结点
	//      异常处理函数地址
	__asm mov fs : [0] , esp

	// 主动触发一个异常
	int number = 10;
	number /= 0;

	// 恢复以前的异常处理函数
	__asm add esp, 0x08
	__asm mov eax, ExceptionList
	__asm mov fs : [0] , eax
	return 0;
}

总结:
当异常交由用户处理时,按照一下顺序调用异常处理方式:VEH---->SHE—>UEH—>VCH

UEH 和 VCH 他们是后面出现的异常处理机制,他们比SEH使用起来更为简单,他们只需要在程序一开始的,注册一个回调函数,然后在程序的任何一个位置产生异常,都会调用回调函数,进行异常的处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值