「基础学习」Windows异常处理简介

admin 2023年11月14日14:25:14评论18 views字数 6720阅读22分24秒阅读模式

    异常机制,就是为了让计算机能够更好的处理程序运行期间产生的错误,从编程的角度来看,能够将错误的处理与程序的逻辑分隔开。使得我们可以集中精力开发关键功能,而把程序可能出现的异常统一管理。Windows提供了异常处理的机制,使得你有机会挽救自己即将崩溃的程序。

  • SEH-结构化异常处理
  • VEH-向量化异常处理
  • VCH-向量化异常处理

结构化异常处理(SEH)

    Structed Exception Handler(结构化异常处理)简称SEH,是微软提供的一种处理异常的机制。在VC++中,通过提供四个微软关键字使得程序员能够良好的使用这一机制,分别是:__try、 __finally、 __except、 __leave接下来简要说明一下用法。

终结处理器

    由 __try、 __finally 和 __leave构成。能够保证无论 __try 块中的指令以何种方式退出,都必然会执行 __finally 块。[不会进行异常处理,只进行清理操作]SEH 的使用范围是线程相关的,每个线程都有自己的函数(SEH链表是局部链表,在堆栈中)

  1. __try
    {
        // 被检查的代码块,通常是程序的逻辑部分
        printf("__try { ... }n");
     
        // 使用 __leave 跳出当前的 __try
        __leave;
    }
    __finally
    {
        // 终结处理块,通常用于清理当前程序
        // 无论 __try 以何种方式退出,都会执行这里的指令
        printf("__finally { ... }n");
     
        // 使用 AbnormalTermination 判断 __try 的退出方式
        // 正常退出,返回值是 false
        if (AbnormalTermination())
            printf("异常退出n");
        else
            printf("正常退出n");
    }
异常处理器

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

  • EXCEPTION_EXECUTE_HANDLER(1):表示该异常被处理,从异常处下一条指令继续执行
  • EXCEPTION_CONTINUE_SEARCH(0):表示异常不能被处理,交给下一个SEH
  • EXCEPTION_CONTINUE_EXECUTION(-1):表示异常被忽略,从异常处继续执行
  1. // 异常处理器: 由关键字 __try 和 __except 构成
    // 如果 __try 中产生了异常,会执行过滤表达式中的内容
    // 应该在过滤表达式提供的过滤函数中处理想要处理的异常
     
    // 异常过滤表达式中最常见的情况就是编写一个异常过滤函数,对异常进行处理
    DWORD ExceptionFilter(DWORD ExceptionCode, PEXCEPTION_POINTERS ExceptionInfo)
    {
        printf("ExceptionCode: %Xn", ExceptionCode);
     
        // 如果当前产生的异常是除零异常,那么就通过修改寄存器处理异常
        if (ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
        {
            // 在这里对寄存器执行的所有修改都会直接被应用到程序中
            ExceptionInfo->ContextRecord->Eax = 1;
            ExceptionInfo->ContextRecord->Ecx = 1;
     
            // 如果异常被处理了,那么就返回重新执行当前的代码
            return EXCEPTION_CONTINUE_EXECUTION;
        }
     
        // 如果不是自己能够处理的异常,就不处理只报告
        return EXCEPTION_EXECUTE_HANDLER;
    }
     
    int main()
    {
        int number = 0;
     
        __try
        {
            // __try 中的是可能产生异常的代码
            // idiv eax, ecx
            number /= 0;
        }
     
        // 通常会为异常过滤表达式提供一个异常处理函数用于处理异常,并返回处理结果
        // GetExceptionCode: 用于获取异常的类型,能在过滤表达式和异常处理器中使用
        // GetExceptionInformation: 用于获取异常的信息,只能写在过滤表达式中
     
        // 异常过滤表达式
        __except (ExceptionFilter(GetExceptionCode(), GetExceptionInformation()))
        {
            // 异常处理器,只有 __except 返回 EXCEPTION_EXECUTE_HANDLER 才会执行
            printf("__try 中产生了异常,但是并没有处理异常 %Xn", GetExceptionCode());
        }
     
        printf("numebr = %dn", number);
     
        return 0;
    }
顶层异常处理器(UEF)

    TopLevelEH 全称顶层异常处理器(UEF),这个函数只能有一个,被保存在全局变量中。由于只会被系统默认的最底层 SEH 调用,所以又会被称作是 SEH 的一种,是整个异常处理的最后一环。所以通常都不会再此执行异常处理操作,而是进行内存 dump ,将消息发送给服务器,进行异常分析。在win7 之后,只有在非调试模式下才会被调用,可以用来反调试。

  1. LONG WINAPI TopLevelExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo)
    {
        printf("ExceptionCode: %Xn", ExceptionInfo->ExceptionRecord->ExceptionCode);
     
        // 如果当前的异常是除零异常,那么就通过修改寄存器处理异常
        if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
        {
            ExceptionInfo->ContextRecord->Eax = 1;
            ExceptionInfo->ContextRecord->Ecx = 1;
     
            // 异常如果被处理了,那么就返回重新执行当前的代码
            return EXCEPTION_CONTINUE_EXECUTION;
        }
     
        // 如果不是自己能够处理的异常,就不处理只报告
        return EXCEPTION_EXECUTE_HANDLER;
    }
     
     
    int main()
    {
        int number = 0;
     
        // 通过一个函数可以直接的安装 UEF
        SetUnhandledExceptionFilter(TopLevelExceptionFilter);
     
        __try
        {
            number /= 0;
        }
        // 异常一旦被 SEH 处理,就不会再传递给 UEF
        __except (EXCEPTION_CONTINUE_SEARCH)
        {
            printf("不会被执行n");
        }
     
        printf("number = %dn", number);
     
        system("pause");
        return 0;
    }

向量化异常处理(VEH)

    Vectored Exception Handler 向量化异常处理的一种,被保存在一个全局的链表中,进程内的所有线程都可以使用这个函数,是第一个处理异常的函数。

  1. LONG WINAPI VectoredExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
    {
        printf("ExceptionCode: %Xn", ExceptionInfo->ExceptionRecord->ExceptionCode);
     
        if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
        {
            ExceptionInfo->ContextRecord->Eax = 1;
            ExceptionInfo->ContextRecord->Ecx = 1;
     
            return EXCEPTION_CONTINUE_EXECUTION;
        }
     
        return EXCEPTION_EXECUTE_HANDLER;
    }
     
    int main()
    {
        int number = 0;
     
        // 通过一个API可以直接安装VEH
        // 参数一是布尔值,如果为 TRUE,就将当前的函数添加到全局 VEH 函数的链表头部
        // 否则则为尾部
        AddVectoredExceptionHandler(TRUE, VectoredExceptionHandler);
     
        __try
        {
            number /= 0;
        }
        // 异常首先被 VEH 接收到,如果无法处理才会传递给 SEH
        __except (EXCEPTION_EXECUTE_HANDLER)
        {
            printf("永远不会被执行n");
        }
     
        printf("number = %dn", number);
     
        system("pause");
        return 0;
    }

添加VEH

  1. LONG NTAPI FirstVectExcepHandler( PEXCEPTION_POINTERS pExcepInfo )
    {
      ``if``( ... )
      ``{
        ``return` `EXCEPTION_CONTINUE_EXECUTION;
      ``}
      ``return` `EXCEPTION_CONTINUE_SEARCH;
    }
    //``参数1=1表示插入Veh链的头部,=0表示插入到VEH链的尾部
    AddVectoredExceptionHandler( 1, &FirstVectExcepHandler );

向量化异常处理(VCH)

VCH:和 VEH 类似,但是只会在异常被处理的情况下最后调用。

异常的传递过程

  1. LONG WINAPI VectoredExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo)
    {
        printf("VEH: ExceptionCode: %Xn", ExceptionInfo->ExceptionRecord->ExceptionCode);
     
        if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
        {
            ExceptionInfo->ContextRecord->Eax = 1;
            ExceptionInfo->ContextRecord->Ecx = 1;
     
            return EXCEPTION_CONTINUE_SEARCH;
        }
     
        return EXCEPTION_EXECUTE_HANDLER;
    }
     
    DWORD StructedExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo)
    {
        printf("SEH: ExceptionCode: %Xn", ExceptionInfo->ExceptionRecord->ExceptionCode);
     
        if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
        {
            ExceptionInfo->ContextRecord->Eax++;
            ExceptionInfo->ContextRecord->Ecx = 1;
     
            return EXCEPTION_CONTINUE_SEARCH;
        }
     
        return EXCEPTION_EXECUTE_HANDLER;
    }
     
    LONG WINAPI TopLevelExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo)
    {
        printf("UEF: ExceptionCode: %Xn", ExceptionInfo->ExceptionRecord->ExceptionCode);
     
        if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
        {
            ExceptionInfo->ContextRecord->Eax++;
            ExceptionInfo->ContextRecord->Ecx = 1;
     
            return EXCEPTION_CONTINUE_EXECUTION;
        }
     
        return EXCEPTION_EXECUTE_HANDLER;
    }
     
    LONG WINAPI VectoredContinueHandler(PEXCEPTION_POINTERS ExceptionInfo)
    {
        // VCH 不会对异常进行处理,调用的时机和异常处理的情况有关
        printf("VCH: ExceptionCode: %Xn", ExceptionInfo->ExceptionRecord->ExceptionCode);
     
        return EXCEPTION_CONTINUE_SEARCH;
    }
     
    int main()
    {
        int number = 0;
     
        AddVectoredExceptionHandler(TRUE, VectoredExceptionHandler);
        AddVectoredContinueHandler(TRUE, VectoredContinueHandler);
        SetUnhandledExceptionFilter(TopLevelExceptionFilter);
     
        __try
        {
            number /= 0;
        }
        __except (StructedExceptionFilter(GetExceptionInformation()))
        {
            printf("SEH: 异常处理器n");
        }
     
        printf("number = %dn", number);
     
        system("pause");
        return 0;
    }

总结

异常的传递过程:VEH -> SEH -> UEH -> VCH

异常处理器处理顺序流程

  1. 交给调试器(进程必须被调试)
  2. 执行VEH
  3. 执行SEH
  4. TopLevelEH(进程被调试时不会被执行)
  5. 交给调试器(上面的异常处理都说处理不了,就再次交给调试器)
  6. 调用异常端口通知csrss.exe

从执行顺序来看,VEH是在SEH之前执行的,并且不依赖某一线程,本进程中任何线程出现异常都可以被VEH处理


本星球作为若干人从基础到进阶到高手的黄埔军校,涵盖了红队对抗几乎大部分知识,欢迎加入阅读历史信息,提升自己。

「基础学习」Windows异常处理简介


原文始发于微信公众号(黑客在思考):「基础学习」Windows异常处理简介

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年11月14日14:25:14
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   「基础学习」Windows异常处理简介https://cn-sec.com/archives/2199008.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息