zoukankan      html  css  js  c++  java
  • Windows调试1.WinDbg基本使用-异常基础知识

    WinDbg 的基本使用

    • WinDbg 支持的调试方式

      • 直接调试(打开一个 exe 程序) 附加调试

      • (附加到一个已经在运行的进程上)

        • 入侵式:可以改变代码的执行流程和寄存器的内容。

        • 非入侵式:不可以改变代码的执行流程,实际上就是挂起了目标进程,对目标进程的线 程环境和内存进行远程访问操作。

      • 基础指令:

        • dd dw dt da du 等:

        • 查看内存信息 ed ew ea 等:

        • 编辑内存信息 t p g 等: 流程控制

    • 中断与异常

      • 中断: 通常由外部硬件产生,属于异步事件,处理器可以不进行处理 异常:

      • 通常是由内部主动触发,属于同步事件,必须要进行处理

      • 中断和异常被统一管理,可以在 WinDbg 使用 !idt 查看中断描述符表。

    • 异常的种类

      • 错误:已经执行了,但是没有执行成功,eip 指向了当前的错误指令

        • 内存访问错误,除零错误,分页错误,硬件执行断点

    • 陷阱:已经执行了,并且执行成功,eip 指向了当前指令的下一条

      • 软件断点(int 3) 单步异常(TF) 硬件读写断点

      • 终止:一旦产生,无法修复,会直接崩溃

       

    • SEH(结构化异常处理)

      • 关键字: 需要和 C++(try throw catch) 异常处理的关键字区分开来

      • 终止处理程序:

        • 使用的关键字有 __ try __ finally __ leave ,保证__ finally块绝对被执行,通常被用作清理工 作

      • 异常处理程序: 使用关键字 __ try 和 __ except,当 __ try 有异常产生时,进行相应的处理。

      • 过滤表达式中存放的可以是任何表达式,但是返回值必须是 0 1 -1

        • EXCEPTION_CONTINUE_EXECUTION: 表示重新执行,修复了才会用

        • EXCEPTION_CONTINUE_SEARCH: 表示无法修复,继续向下以 VEH SEH UEH 的顺序寻 找

        • EXCEPTION_EXECUTE_HANDLER:表示执行 __ except 中的代码,影响执行流程

      • GetExceptionCode:过滤表达式和 except 块,获取异常类型

      • GetExceptionInfomation:只能个用于过滤表达式,获取寄存器和异常信息

    • 终止异常处理程序和普通异常处理程序都是线程相关的,可以有多个

       

      UEH(顶层异常处理程序)

    • 是异常处理的最后一道关卡,通常用于执行错误信息的收集工作,也被用于反调试

    • 设置异常处理的函数是 SetUnhandledExceptionFilter()

    • 进程相关,被存放在一个全局变量中,只能有一个

    • UEH的调用位于SEH之后,如果所有SEH都无法处理才会被调用

    • 如果程序处于被调试状态,那么UEH函数不会被调用

       

      VEHVCH(向量化异常处理程序)

    • VEH 和 VCH 都被存放在一个全局的链表中,可以设置多个

    • VEH的调用是最先的,如果所有VEH都无法处理异常,则会调用UEH

    • VCH只会在异常被处理的时候被调用,如果异常没有被处理程序会崩溃

       

    终结处理器

    // 终结处理器: 保证程序在执行的过程中,一定会执行 _finally 块的代码
    //  - 保证无论 __try 是以何种方式退出的,最终都会执行 __finally
    //  - 不能够处理异常,通常只能用于执行清理工作
    // __try: 保存的通常是需要进行检测的代码
    // __finally: 保存的是一定会执行的一段代码
    // __leave: 用于正常退出 __try 块
    //goto :非正常退出
    int main()
    {
        __try 
        {
            printf("__try { ... }
    ");
    ​
            // 推荐使用 __leave 退出代码块,使用跳转语句会产生多余的函数调用
            // __leave 对应实际是一条 jmp 语句,执行更加的迅速,用于正常退出__try块
            __leave;
    ​
            // 使用跳转指令退出 __try 块,例如 continue break goto return,属于非正常退出
            goto label_exit;
        }
        __finally
        {
            // 通常用于执行某一些特定的清理工作,比如关闭句柄或释放内存
            printf("__finally { ... }
    ");
    ​
            // 使用 AbnormalTermination 判断是否是异常退出的
            if (AbnormalTermination())
                printf("异常退出代码块");
            else
                printf("正常退出代码块");
        }
    ​
    label_exit:
    ​
        return 0;
    }

     

    异常处理SEH

    // SEH 的两种实现方式是不能同时存在的,但是可以嵌套
    //      两种功能指__try__finanly跟__try__except
    // SEH 的处理函数被保存在了栈中,所以不同的线程拥有各自的处理函数
    // 异常处理程序(SEH):  可以用于捕获产生的异常,并且对它执行相应的处理
    // __try: 是需要被保护(可能产生异常)的代码
    // __except: 存放过滤表达式和异常处理块
    ​
    ​
    // 保存异常信息和线程环境的异常结构体
    // typedef struct _EXCEPTION_POINTERS {
    //    PEXCEPTION_RECORD ExceptionRecord;        // 保存了[异常类型]和[产生异常的指令所在的位置]
    //    PCONTEXT ContextRecord;                   // 保存的是异常发生时的寄存器环境,通过修改可以修复异常
    // } EXCEPTION_POINTERS, * PEXCEPTION_POINTERS;
    ​
    ​
    // 过滤函数: 用于根据不同的情况,返回不同类型的值
    //  GetExceptionCode()返回值是 unsigned long
    //  typedef unsigned long       DWORD;
    DWORD FilterHandler(DWORD ExceptionCode, PEXCEPTION_POINTERS ExceptionInfo)
    { 
        // 假设产生的是一个整数除零异常,尝试对异常进行处理,并返回继续执行
        if (ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
        {
            ExceptionInfo->ContextRecord->Ecx = 1;
            return EXCEPTION_CONTINUE_EXECUTION;
            //这个是-1
        }
    ​
        // 否则其它的异常不做处理,向上传递,这个是0
        return EXCEPTION_CONTINUE_SEARCH;
    }
    ​
    ​
    int main()
    {
        __try
        {
            printf("__try { ... }
    ");
    ​
            // 可能会产生异常的指令,只有产生了异常,才会执行 ___except
            __asm mov eax, 100
            __asm xor edx, edx
            __asm xor ecx, ecx
            __asm idiv ecx
    ​
            // 处理异常有意义么? 没有意义的,具体需要分析目标的处理函数
            printf("异常已经被处理类!
    ");
    ​
            // 一个触发内存访问异常的代码
            // *(DWORD*)0 = 0;
        }
    ​
        // __except() 过滤表达式的内容可以是任意形式的,但是它的值必须是下面的三个之一,通常是函数调用
        //  - EXCEPTION_EXECUTE_HANDLER(1): 表示捕获到了异常,需要执行异常处理块的代码,并继续往下执行
        //  - EXCEPTION_CONTINUE_SEARCH(0): 表示无能为力,交给其它异常处理函数,通常没有处理的返回这一个
        //  - EXCEPTION_CONTINUE_EXECUTION(-1): 表示不相信不能执行,需要重新执行一遍,只有处理了的异常才会使用
        // 异常过滤函数通常要用到的两个函数调用
        //  - GetExceptionCode: 获取产生的异常的类型,只能在过滤表达式和异常处理块中调用
        //  - GetExceptionInformation: 获取异常的信息和异常产生时的线程环境,只能在过滤表达式中使用
        __except(FilterHandler(GetExceptionCode(), GetExceptionInformation()))
        {
            // 只会在异常过滤表达式的值为 EXCEPTION_EXECUTE_HANDLER 才会调用
            printf("__except(EXCEPTION_EXECUTE_HANDLER) { ... }
    ");
        }
    ​
        return 0;
    }
    顶层异常处理
    // 顶层异常处理(UEH): 是应用程序的最后一道防线,如果所有的 SEH 都没有能够处理异常,就会执行它
    //  - UEH 通常被用于执行内存转储操作,将收集的错误信息(异常类型,线程上下文和内存)提交到服务器
    //  - UEH 在 64位系统 下的调试器内是永远不会执行的,需要单独的进行运行
    ​
    ​
    // 自定义的顶层异常处理函数,即使没有自定义,也会有一个默认的处理函数,且只有一个
    // 它的返回值类型和 SEH 是相同的,但是缺少了 EXCEPTION_EXECUTE_HANDLER
    LONG WINAPI TopLevelExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo)
    {
        printf("TopLevelExceptionHandler(): %08X
    ", ExceptionInfo->ExceptionRecord->ExceptionCode);
    ​
        // 假设产生的是一个整数除零异常,尝试对异常进行处理,并返回继续执行
        if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
        {
            ExceptionInfo->ContextRecord->Ecx = 1;
            return EXCEPTION_CONTINUE_EXECUTION;    // 0
        }
    ​
        // 否则其它的异常不做处理,向上传递
        return EXCEPTION_CONTINUE_SEARCH;           // 1
    }
    ​
    ​
    int main()
    {
        // 顶层异常处理的设置依赖于一个函数
        SetUnhandledExceptionFilter(TopLevelExceptionHandler);
    ​
        __try
        {
            // 产生除零异常
            __asm mov eax, 100
            __asm xor edx, edx
            __asm xor ecx, ecx
            __asm idiv ecx
        }
        __except (EXCEPTION_CONTINUE_SEARCH)
        {
            // 这里永远不会执行,因为不是 EXCEPTION_EXECUTE_HANDLER(1)
            printf("__except (EXCEPTION_CONTINUE_SEARCH)
    ");
    ​
        }
    ​
        printf("异常处理成功!");
        system("pause");
    ​
        return 0;
    }

    向量化异常处理程序(VEH)

    // 向量化异常处理程序(VEH): 用户层支持的一种机制,在 SEH 之前被执行
    //  - 保存在一个全局的链表中,整个进行都可以访问到
    ​
    ​
    // 自定义的 VEH 异常处理函数,它的执行位于 SEH 之前,如果 VEH 没有处理成功,才会调用 SEH
    LONG WINAPI VectoredExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo)
    {
        printf("VectoredExceptionHandler(): %08X
    ", ExceptionInfo->ExceptionRecord->ExceptionCode);
    ​
        // 假设产生的是一个整数除零异常,尝试对异常进行处理,并返回继续执行
        if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
        {
            ExceptionInfo->ContextRecord->Ecx = 1;
            return EXCEPTION_CONTINUE_EXECUTION;    // 0
        }
    ​
        // 否则其它的异常不做处理,向上传递
        return EXCEPTION_CONTINUE_SEARCH;           // 1
    }
    ​
    ​
    int main()
    {
        // 设置一个向量化异常处理函数(VEH),参数一表示添加到异常处理函数链表的位置
        AddVectoredExceptionHandler(TRUE, VectoredExceptionHandler);
    ​
        __try
        {
            // 产生除零异常
            __asm mov eax, 100
            __asm xor edx, edx
            __asm xor ecx, ecx
            __asm idiv ecx
        }
        __except (EXCEPTION_EXECUTE_HANDLER)
        {
            // 这里永远不会执行,因为不是 EXCEPTION_EXECUTE_HANDLER(1)
            printf("__except (EXCEPTION_EXECUTE_HANDLER)
    ");
    ​
        }
    ​
        printf("异常处理成功!");
        system("pause");
    ​
        return 0;
    }
     

    向量化异常处理程序(VCH)

    // 向量化异常处理程序(VCH): 用户层支持的一种机制,在 最后 被执行
    //  - 保存在一个全局的链表中,整个进程都可以访问到,和 VEH 在同一个表中,只是标志位不同
    //  - VCH 只会在异常被处理的情况下,最后被执行。
    // VEH -> SEH -> UEH -> [VCH]
    ​
    ​
    // 自定义 UEH 函数,在 SEH 之后执行
    LONG WINAPI TopLevelExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo)
    {
        printf("TopLevelExceptionHandler(): %08X
    ", ExceptionInfo->ExceptionRecord->ExceptionCode);
    ​
        // 假设产生的是一个整数除零异常,尝试对异常进行处理,并返回继续执行
        if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
        {
            ExceptionInfo->ContextRecord->Ecx = 1;
            return EXCEPTION_CONTINUE_EXECUTION;
        }
    ​
        // 否则其它的异常不做处理,向上传递
        return EXCEPTION_CONTINUE_SEARCH;
    }
    ​
    // 自定义的 VEH 异常处理函数,它的执行位于 SEH 之前,如果 VEH 没有处理成功,才会调用 SEH
    LONG WINAPI VectoredExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo)
    {
        printf("VectoredExceptionHandler(): %08X
    ", ExceptionInfo->ExceptionRecord->ExceptionCode);
    ​
        // 假设产生的是一个整数除零异常,尝试对异常进行处理,并返回继续执行
        //if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
        //{
        //  ExceptionInfo->ContextRecord->Ecx = 1;
        //  return EXCEPTION_CONTINUE_EXECUTION;
        //}
    // 否则其它的异常不做处理,向上传递
        return EXCEPTION_CONTINUE_SEARCH;
    }
    ​
    // 自定义的 VCH 异常处理函数,只有在异常处理成功的情况下,最后才会被调用
    LONG WINAPI VectoredContinueHandler(EXCEPTION_POINTERS* ExceptionInfo)
    {
        printf("VectoredContinueHandler(): %08X
    ", ExceptionInfo->ExceptionRecord->ExceptionCode);
        return EXCEPTION_CONTINUE_SEARCH;
    }
    ​
    ​
    // 过滤函数: 用于根据不同的情况,返回不同类型的值
    DWORD FilterHandler(DWORD ExceptionCode, PEXCEPTION_POINTERS ExceptionInfo)
    {
        printf("FilterHandler(): %08X
    ", ExceptionInfo->ExceptionRecord->ExceptionCode);
    ​
        //// 假设产生的是一个整数除零异常,尝试对异常进行处理,并返回继续执行
        //if (ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
        //{
        //  ExceptionInfo->ContextRecord->Ecx = 1;
        //  return EXCEPTION_CONTINUE_EXECUTION;
        //}
    // 否则其它的异常不做处理,向上传递
        return EXCEPTION_CONTINUE_SEARCH;
    }
    ​
    int main()
    {
        // 设置一个向量化异常处理函数(VEH)
        AddVectoredExceptionHandler(TRUE, VectoredExceptionHandler);
        // 设置一个向量化异常处理函数(VCH)
        AddVectoredContinueHandler(TRUE, VectoredContinueHandler);
        // UEH 处理函数
        SetUnhandledExceptionFilter(TopLevelExceptionHandler);
    ​
        __try
        {
            // 产生除零异常
            __asm mov eax, 100
            __asm xor edx, edx
            __asm xor ecx, ecx
            __asm idiv ecx
        }
        __except (FilterHandler(GetExceptionCode(), GetExceptionInformation()))
        {
            // 这里永远不会执行,因为不是 EXCEPTION_EXECUTE_HANDLER(1)
            printf("__except (EXCEPTION_EXECUTE_HANDLER)
    ");
        }
    ​
        printf("异常处理成功!");
        system("pause");
    ​
        return 0;
    }

     

     

  • 相关阅读:
    用Xamarin + VS 编写Android程序体验及其与Android Studio的比较
    【Android】XML文件的解析
    【Ubuntu】您没有查看“sf_VirtualDisk”的内容所需的权限。
    Android酷炫实用的开源框架(UI框架)
    Linux下安装gcc 、g++ 、gfortran编译器
    Ubuntu 分辨率调整及操作问题解决
    “this kernel requires an x86-64 CPU, but only detects an i686 CPU, unable to boot” 问题解决
    【Android】沉浸式状态栏实现
    【Android】基于TCP协议的网络通信
    C#中string和byte[]相互转换问题解决
  • 原文地址:https://www.cnblogs.com/ltyandy/p/11153773.html
Copyright © 2011-2022 走看看