Windows调试1.WinDbg基本使用-异常基础知识
Posted ltyandy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 __ ?nally __ leave ,保证__ ?nally块绝对被执行,通常被用作清理工 作
-
-
异常处理程序: 使用关键字 __ 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函数不会被调用
VEH\\VCH(向量化异常处理程序)
-
VEH 和 VCH 都被存放在一个全局的链表中,可以设置多个
-
VEH的调用是最先的,如果所有VEH都无法处理异常,则会调用UEH
-
VCH只会在异常被处理的时候被调用,如果异常没有被处理程序会崩溃
终结处理器
// 终结处理器: 保证程序在执行的过程中,一定会执行 _finally 块的代码 // - 保证无论 __try 是以何种方式退出的,最终都会执行 __finally // - 不能够处理异常,通常只能用于执行清理工作 ? // __try: 保存的通常是需要进行检测的代码 // __finally: 保存的是一定会执行的一段代码 // __leave: 用于正常退出 __try 块 //goto :非正常退出 int main() __try printf("__try ... \\n"); ? // 推荐使用 __leave 退出代码块,使用跳转语句会产生多余的函数调用 // __leave 对应实际是一条 jmp 语句,执行更加的迅速,用于正常退出__try块 __leave; ? // 使用跳转指令退出 __try 块,例如 continue break goto return,属于非正常退出 goto label_exit; __finally // 通常用于执行某一些特定的清理工作,比如关闭句柄或释放内存 printf("__finally ... \\n"); ? // 使用 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 ... \\n"); ? // 可能会产生异常的指令,只有产生了异常,才会执行 ___except __asm mov eax, 100 __asm xor edx, edx __asm xor ecx, ecx __asm idiv ecx ? // 处理异常有意义么? 没有意义的,具体需要分析目标的处理函数 printf("异常已经被处理类!\\n"); ? // 一个触发内存访问异常的代码 // *(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) ... \\n"); ? return 0;
顶层异常处理
// 顶层异常处理(UEH): 是应用程序的最后一道防线,如果所有的 SEH 都没有能够处理异常,就会执行它 // - UEH 通常被用于执行内存转储操作,将收集的错误信息(异常类型,线程上下文和内存)提交到服务器 // - UEH 在 64位系统 下的调试器内是永远不会执行的,需要单独的进行运行 ? ? // 自定义的顶层异常处理函数,即使没有自定义,也会有一个默认的处理函数,且只有一个 // 它的返回值类型和 SEH 是相同的,但是缺少了 EXCEPTION_EXECUTE_HANDLER LONG WINAPI TopLevelExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo) printf("TopLevelExceptionHandler(): %08X\\n", 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)\\n"); ? ? printf("异常处理成功!"); system("pause"); ? return 0;
向量化异常处理程序(VEH)
// 向量化异常处理程序(VEH): 用户层支持的一种机制,在 SEH 之前被执行 // - 保存在一个全局的链表中,整个进行都可以访问到 ? ? // 自定义的 VEH 异常处理函数,它的执行位于 SEH 之前,如果 VEH 没有处理成功,才会调用 SEH LONG WINAPI VectoredExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo) printf("VectoredExceptionHandler(): %08X\\n", 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)\\n"); ? ? printf("异常处理成功!"); system("pause"); ? return 0;
// 向量化异常处理程序(VCH): 用户层支持的一种机制,在 最后 被执行 // - 保存在一个全局的链表中,整个进程都可以访问到,和 VEH 在同一个表中,只是标志位不同 // - VCH 只会在异常被处理的情况下,最后被执行。 ? // VEH -> SEH -> UEH -> [VCH] ? ? // 自定义 UEH 函数,在 SEH 之后执行 LONG WINAPI TopLevelExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo) printf("TopLevelExceptionHandler(): %08X\\n", 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\\n", 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\\n", ExceptionInfo->ExceptionRecord->ExceptionCode); return EXCEPTION_CONTINUE_SEARCH; ? ? // 过滤函数: 用于根据不同的情况,返回不同类型的值 DWORD FilterHandler(DWORD ExceptionCode, PEXCEPTION_POINTERS ExceptionInfo) printf("FilterHandler(): %08X\\n", 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)\\n"); ? printf("异常处理成功!"); system("pause"); ? return 0;
以上是关于Windows调试1.WinDbg基本使用-异常基础知识的主要内容,如果未能解决你的问题,请参考以下文章
背水一战 Windows 10 (73) - 控件(控件基类): UIElement - 拖放的基本应用, 手动开启 UIElement 的拖放操作