终止处理SEH

#include <iostream>
#include <windows.h>


// 终结处理器: 由 __try 、 __finally 和 __leave 构成。
//  能够保证无论 __try 中的指令以何种方式退出,都必然会
//  执行 __finally 块。【不会处理异常,只做清理操作】
//  SEH 的使用范围是线程相关的,每个线程都有自己的函数

int main()
{
    __try 
    {
        // 被检查的代码块,通常是程序的逻辑部分
        printf("__try { ... }\n");

        // 如果当前以包括 return goto break 
        // 的跳转指令退出就会产生一些额外的函
        // 数调用,用于执行 __finally 块

        // 推荐是用 __leave 跳出当前的 __try
        __leave;
        //作用就是离开__try作用域块,在离开之前会去执行finally里的代码
        //https://blog.csdn.net/HXC_HUANG/article/details/70053039
        //exit(TRUE);
    }
    __finally 
    {
        // 终结处理块,通常编写的是用于清理当前程序的代码
        // 无论 __try 以何种方式退出,都会执行这里的指令
        printf("__finally { ... }\n");

        // 使用 AbnormalTermination 判断 __try 的退出方
        // 式,程序如果是正常退出的,那么返回值是 false
        if (AbnormalTermination())
            printf("异常退出\n");
        else
            printf("正常退出\n");
    }

ret_tag:
    return 0;
}

汇编代码

...
    __try 
00F3190F  mov         dword ptr [ebp-4],0  
00F31916  mov         dword ptr [ebp-0E0h],1  
    {

        printf("__try { ... }\n");
00F31920  push        offset string "__try { ... }\n" (0F37B30h)  
00F31925  call        _printf (0F3104Bh)  
00F3192A  add         esp,4  

        __leave;
    }
00F3192D  mov         dword ptr [ebp-4],0FFFFFFFEh  
00F31934  mov         dword ptr [ebp-0E0h],0  
00F3193E  call        main+85h (0F31945h)  //这里会跳转去执行finally中代码
00F31943  jmp         $LN10 (0F31978h)  
    __finally 
    {

        printf("__finally { ... }\n");
00F31945  push        offset string "__finally { ... }\n" (0F37B44h)  
00F3194A  call        _printf (0F3104Bh)  
00F3194F  add         esp,4  


        if (AbnormalTermination())
00F31952  cmp         dword ptr [ebp-0E0h],0  
00F31959  je          main+0AAh (0F3196Ah)  
            printf("异常退出\n");
00F3195B  push        offset string "\xd2\xec\xb3\xa3\xcd\xcb\xb3\xf6\n" (0F37B5Ch)  
00F31960  call        _printf (0F3104Bh)  
00F31965  add         esp,4  
        else
00F31968  jmp         main+0B7h (0F31977h)  
            printf("正常退出\n");
00F3196A  push        offset string "\xd5\xfd\xb3\xa3\xcd\xcb\xb3\xf6\n" (0F37B68h)  
00F3196F  call        _printf (0F3104Bh)  
            printf("正常退出\n");
00F31974  add         esp,4  
$LN11:
00F31977  ret  
    }

ret_tag:
    return 0;
00F31978  xor         eax,eax  
}
...

异常处理SEH

#include <iostream>
#include <windows.h>

// 异常处理器: 由关键字 __try 和 __except 构成,能够保证 __try
//  中如果产生了异常,会执行过滤表达式中的内容,应该在过滤表达式提
//  供的过滤函数中处理想要处理的异常。

// 异常过滤表达式中最常见的情况就是编写一个一场过滤函数,对异常进行处理
DWORD ExceptionFilter(EXCEPTION_POINTERS* ExceptionInfo, DWORD ExceptionCode)
{
    printf("ExceptionCode: %X\n", ExceptionCode);

    // 如果当前产生的异常是除零异常,那么就通过修改寄存器处理异常
    if (EXCEPTION_INT_DIVIDE_BY_ZERO == ExceptionCode)
    {
        // 通过查看汇编代码可以知道产生异常的指令是 idiv eax, ecx
        // 在这个位置对寄存器执行的所有修改都会直接被应用到程序中
        ExceptionInfo->ContextRecord->Eax = 30;
        ExceptionInfo->ContextRecord->Edx = 0;
        ExceptionInfo->ContextRecord->Ecx = 1;

        // 异常如果被处理了,那么就返回重新执行当前的代码
        return EXCEPTION_CONTINUE_EXECUTION;
    }

    // 如果不是自己能够处理的异常,就不处理只报告
    return EXCEPTION_EXECUTE_HANDLER;
}

int main()
{
    int number = 0;

    __try
    {
         // __try 中保存的是可能产生异常的代码
        number /= 0;
        //执行到这里的时候,会直接执行结束,好像不能调试异常处理块的代码
    }

    // __except 后的括号中会存在一个异常过滤表达式表达式的返回值必定是一下说明的几个之一
    //  - EXCEPTION_EXECUTE_HANDLER(1): 执行异常处理器报告异常,但是并不处理,不处理返回
    //  - EXCEPTION_CONTINUE_SEARCH(0): 将异常传递给上层的异常处理函数,通常无法处理返回
    //  - EXCEPTION_CONTINUE_EXECUTION(-1): 尝试重新执行指令,通常在处理了异常之后返回

    // 通常会为一场过滤表达式提供一个异常处理函数用于处理异常,并返回处理结果
    //  - GetExceptionCode: 用于获取异常的类型,能在过滤表达式和异常处理器中使用
    //  - GetExceptionInformation: 用于获取异常的信息,只能卸载过滤表达式中
    __except (ExceptionFilter(GetExceptionInformation(), GetExceptionCode()))
    {
        // 异常处理器,只有 __except 返回 EXCEPTION_EXECUTE_HANDLER 才会执行
        printf("__try 中 产生了异常,但是并没有处理异常 %x\n", GetExceptionCode());
    }

    printf("numebr = %d\n", number);

    return 0;
}
最后修改:2020 年 08 月 26 日
如果觉得我的文章对你有用,请随意赞赏