一、SEH
1、终止处理的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;}__finally {// 终结处理块,通常编写的是用于清理当前程序的代码// 无论 __try 以何种方式退出,都会执行这里的指令printf("__finally { ... }\n");// 使用 AbnormalTermination 判断 __try 的退出方// 式,程序如果是正常退出的,那么返回值是 falseif (AbnormalTermination())printf("异常退出\n");elseprintf("正常退出\n");}
ret_tag:return 0;
}
使用__leave进行退出时的汇编代码:当我们需要使用跳转其他跳转指令的时候,使用__leave会直接跳转到__finally的第一条语句中。
使用其他(goto,return,break)跳转指令退出时的汇编代码如果使用其他的跳转指令会产生额外的代码
2、异常处理的SEH
// Exception.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#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->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;
}
3.顶层异常UEH
TopLevelEH 全称顶层异常处理器(UEF),这个函数只能有一个,被保存在全局变量中
由于只会被系统默认的最底层 SEH 调用,所以又会被称作是 SEH 的一种,是整个异常处理的最后一环。所以通常都不会再此执行异常处理操作,而是进行内存 dump ,将消息发送给服务器,进行异常分析
#include <iostream>
#include <windows.h>
// UEH: 全称顶层异常处理器,这个函数只能有一个,被保存在全局变量中,
//  由于只会被系统默认的最底层 SEH 调用,所以又会被称作是 SEH 的一
//  种,是整个异常处理中的最后一环,所以通常都不会再次执行异常处理操
//  作,而是进行内存 dump,将信息发送给服务器,进行异常的分析。
//  [注意: UEH 在 win7 之后,只有在非调试模式下才会被调用,可以反调试]
LONG WINAPI TopLevelExceptionFilter(EXCEPTION_POINTERS* ExceptionInfo)
{printf("ExceptionCode: %X\n", ExceptionInfo->ExceptionRecord->ExceptionCode);// 如果当前产生的异常是除零异常,那么就通过修改寄存器处理异常if (EXCEPTION_INT_DIVIDE_BY_ZERO == ExceptionInfo->ExceptionRecord->ExceptionCode){// 通过查看汇编代码可以知道产生异常的指令是 idiv eax, ecx// 在这个位置对寄存器执行的所有修改都会直接被应用到程序中ExceptionInfo->ContextRecord->Eax = 30;ExceptionInfo->ContextRecord->Ecx = 1;// 异常如果被处理了,那么就返回重新执行当前的代码return EXCEPTION_CONTINUE_EXECUTION;}// 如果不是自己能够处理的异常,就不处理只报告return EXCEPTION_EXECUTE_HANDLER;
}
int main()
{int number = 0;// 通过一个函数可以直接的安装 UEHSetUnhandledExceptionFilter(TopLevelExceptionFilter);// 安装一个 SEH 处理器__try{number /= 0;}// 异常一旦被 SEH 处理,就不会再传递给 UEH__except (EXCEPTION_CONTINUE_SEARCH){printf("这个地方永远不会执行\n");}printf("number = %d\n", number);system("pause");return 0;
}
只有在win7非调试模式下才能被调用,可以利用这个特性来做反调试。
二、向量异常VEH
#include "pch.h"
#include <iostream>
#include <windows.h>
// VEH: 向量化异常处理的一种,被保存在一个全局的链表中,进程内的所有线程都可
//  以使用这个函数,是第一个处理异常的函数。
LONG WINAPI VectoredExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo)
{printf("ExceptionCode: %X\n", ExceptionInfo->ExceptionRecord->ExceptionCode);// 如果当前产生的异常是除零异常,那么就通过修改寄存器处理异常if (EXCEPTION_INT_DIVIDE_BY_ZERO == ExceptionInfo->ExceptionRecord->ExceptionCode){// 通过查看汇编代码可以知道产生异常的指令是 idiv eax, ecx// 在这个位置对寄存器执行的所有修改都会直接被应用到程序中ExceptionInfo->ContextRecord->Eax = 30;ExceptionInfo->ContextRecord->Ecx = 1;// 异常如果被处理了,那么就返回重新执行当前的代码return EXCEPTION_EXECUTE_HANDLER;}// 如果不是自己能够处理的异常,就不处理只报告return EXCEPTION_EXECUTE_HANDLER;
}
int main()
{int number = 0;// 通过一个函数可以直接的安装 VEH,参数一是布尔值,如果为 TRUE//  的话,就将当前的函数添加到全局 VEH 函数的链表头部,否则尾部AddVectoredExceptionHandler(TRUE, VectoredExceptionHandler);// 安装一个 SEH 处理器__try{number /= 0;}// 异常首先被 VEH 接收到,如果无法处理才会传递给 SEH__except (EXCEPTION_STACK_OVERFLOW){printf("这个地方永远不会执行\n");}printf("number = %d\n", number);system("pause");return 0;
}
三、向量异常VCH#include <iostream>
#include <windows.h>
// 异常的传递过程: VEH -> SEH -> UEH -> VCH
// VCH: 和 VEH 类似,但是只会在异常被处理的情况下最后调用
LONG WINAPI VectoredContinueHandler(EXCEPTION_POINTERS* ExceptionInfo)
{// VEH 不会对异常进行处理,调用的时机和异常处理的情况有关printf("VCH: ExceptionCode: %X\n", ExceptionInfo->ExceptionRecord->ExceptionCode);return EXCEPTION_CONTINUE_SEARCH;
}
LONG WINAPI VectoredExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo)
{printf("VEH: ExceptionCode: %X\n", ExceptionInfo->ExceptionRecord->ExceptionCode);// 如果当前产生的异常是除零异常,那么就通过修改寄存器处理异常if (EXCEPTION_INT_DIVIDE_BY_ZERO == ExceptionInfo->ExceptionRecord->ExceptionCode){// 通过查看汇编代码可以知道产生异常的指令是 idiv eax, ecx// 在这个位置对寄存器执行的所有修改都会直接被应用到程序中ExceptionInfo->ContextRecord->Eax = 30;ExceptionInfo->ContextRecord->Ecx = 1;// 异常如果被处理了,那么就返回重新执行当前的代码return EXCEPTION_CONTINUE_SEARCH;}// 如果不是自己能够处理的异常,就不处理只报告return EXCEPTION_EXECUTE_HANDLER;
}
LONG WINAPI TopLevelExceptionFilter(EXCEPTION_POINTERS* ExceptionInfo)
{printf("UEH: ExceptionCode: %X\n", ExceptionInfo->ExceptionRecord->ExceptionCode);// 如果当前产生的异常是除零异常,那么就通过修改寄存器处理异常if (EXCEPTION_INT_DIVIDE_BY_ZERO == ExceptionInfo->ExceptionRecord->ExceptionCode){// 通过查看汇编代码可以知道产生异常的指令是 idiv eax, ecx// 在这个位置对寄存器执行的所有修改都会直接被应用到程序中ExceptionInfo->ContextRecord->Eax = 30;ExceptionInfo->ContextRecord->Ecx = 1;// 异常如果被处理了,那么就返回重新执行当前的代码return EXCEPTION_CONTINUE_SEARCH;}// 如果不是自己能够处理的异常,就不处理只报告return EXCEPTION_EXECUTE_HANDLER;
}
DWORD StructedExceptionFilter(EXCEPTION_POINTERS* ExceptionInfo)
{printf("SEH: ExceptionCode: %X\n", ExceptionInfo->ExceptionRecord->ExceptionCode);// 如果当前产生的异常是除零异常,那么就通过修改寄存器处理异常if (EXCEPTION_INT_DIVIDE_BY_ZERO == ExceptionInfo->ExceptionRecord->ExceptionCode){// 通过查看汇编代码可以知道产生异常的指令是 idiv eax, ecx// 在这个位置对寄存器执行的所有修改都会直接被应用到程序中ExceptionInfo->ContextRecord->Eax = 30;ExceptionInfo->ContextRecord->Ecx = 1;// 异常如果被处理了,那么就返回重新执行当前的代码return EXCEPTION_CONTINUE_EXECUTION;}// 如果不是自己能够处理的异常,就不处理只报告return EXCEPTION_EXECUTE_HANDLER;
}
int main()
{int number = 0;AddVectoredExceptionHandler(TRUE, VectoredExceptionHandler);AddVectoredContinueHandler(TRUE, VectoredContinueHandler);SetUnhandledExceptionFilter(TopLevelExceptionFilter);// 安装一个 SEH 处理器__try{number /= 0;}__except (StructedExceptionFilter(GetExceptionInformation())){printf("SEH: 异常处理器\n");}printf("number = %d\n", number);system("pause");return 0;
}
四、探究原理SEH
#include <iostream>
#include <windows.h>
// 带有异常处理函数的函数
void test1()
{// 在 VS 的同一个函数中无论编写了多少个 SEH, 编译// 器实际上只会安装一个叫做 except_handler4 的函数__try{printf("__try { ... }\n");__try{printf("__try { ... }\n");}__except (1){printf("__except (1) { ... }\n");}}__except (1){printf("__except (1) { ... }\n");}
}
// 没有异常处理函数的函数
void test2() { }
// 遍历当前程序中已经存在的异常处理函数
void ShowSEH()
{// 定义一个结构体指针,用于保存 SEH 链表的头节点EXCEPTION_REGISTRATION_RECORD* header = nullptr;// 通过 FS:[0] 找到 ExceptionList 的头节点__asm push fs:[0]__asm pop header// 遍历异常处理链表,链表以 -1 结尾while (header != (EXCEPTION_REGISTRATION_RECORD*)-1){printf("function: %08X\n", header->Handler);header = header->Next;}printf("\n");
}
EXCEPTION_DISPOSITION NTAPI ExceptionRoutine(// 产生的异常信息_Inout_ struct _EXCEPTION_RECORD* ExceptionRecord,_In_ PVOID EstablisherFrame,// 产生异常时的线程上下文_Inout_ struct _CONTEXT* ContextRecord,_In_ PVOID DispatcherContext
)
{printf("自定义SEH: ExceptionCode: %X\n", ExceptionRecord->ExceptionCode);// 如果当前产生的异常是除零异常,那么就通过修改寄存器处理异常if (EXCEPTION_INT_DIVIDE_BY_ZERO == ExceptionRecord->ExceptionCode){// 通过查看汇编代码可以知道产生异常的指令是 idiv eax, ecx// 在这个位置对寄存器执行的所有修改都会直接被应用到程序中ContextRecord->Eax = 30;ContextRecord->Ecx = 1;// 异常如果被处理了,那么就返回重新执行当前的代码return ExceptionContinueExecution;}// 如果不是自己能够处理的异常,就不处理只报告return ExceptionContinueSearch;
}
int main()
{test1();test2();// 遍历到了异常处理函数ShowSEH();// 手动的安装一个异常处理函数,操作 FS:[0]__asm push ExceptionRoutine         // 新的异常处理函数__asm push fs : [0]                 // 上一个节点__asm mov fs : [0] , esp            // esp 指向创建的结构体首地址int number = 0;number /= 0;// 遍历到了异常处理函数ShowSEH();// 卸载一个异常处理函数__asm mov eax, fs:[0]               // 获取到了安装完之后的节点 __asm mov eax, [eax]                // 上一个 SEH 节点,修改前的__asm mov fs:[0], eax               // 修改前的重新设置为 SEH 头节点__asm add esp, 8// 遍历到了异常处理函数ShowSEH();return 0;
}
/*PEXCEPTION_REGISTRATION_RECORD ExceptionList = nullptr;__asm push fs:[0]__asm pop ExceptionList__asm push ExceptionRoutine__asm push fs : [0]__asm mov fs : [0], esp__asm mov eax, ExceptionList__asm mov fs:[0], eax__asm add esp, 0x08
*/

异常与调试之SEH、UEH、VEH、VCH以及SEH原理相关推荐

  1. nvidia nx平台GStreamer pipeline异常问题调试记录2

    1.前言 承接上文 nvidia nx平台GStreamer pipeline异常问题调试记录1_bingdund的博客-CSDN博客 2. 设置gst调试级别为3 这是我在崩溃发生前得到的唯一消息 ...

  2. vs 调试时异常 托管调试助手 “NonComVisibleBaseClass“:“执行了 QueryInterface 调用

    vs调试时异常 托管调试助手 "NonComVisibleBaseClass":"执行了 QueryInterface 调用, 请求提供 COM 可见的托管类" ...

  3. STM32G431+P-NUCLEO-IHM03开发套件调试3(p-nucleo-ihm03套件)-FOC基础原理介绍

    STM32G431+P-NUCLEO-IHM03开发套件调试3(p-nucleo-ihm03套件) 文章目录 STM32G431+P-NUCLEO-IHM03开发套件调试3(p-nucleo-ihm0 ...

  4. Crash Dump调试:Symbol Server/Source Server、PDB原理分析

    Crash Dump调试:Symbol Server/Source Server.PDB原理分析 Crash Dump调试:Symbol Server/Source Server.PDB原理分析 - ...

  5. windows 异常处理中VEH、SEH、UEH、VCH 之间的关系

    1.当异常交由用户处理时,按照以下顺序调用异常处理方式VEH-〉SEH-〉VCH . 2.当VEH表示处理了异常,就不会传递给SEH,但是会传递异常给VCH . 3.当VEH没有处理了,就会传递给SE ...

  6. 发生一个未处理的异常 脚本调试 错误号2912

    我已经找到问题的 答案了, 是删除注册表里的两个键值就行了. 在msdn中 是这样解释的: 关键词"实时调试" 实时调试 是这样一种功能,当在 Visual Studio 外运行的 ...

  7. 一起看视频打开后显示服务器异常,VS调试异常问题解决(一)

    一.无法在web服务器上启动调试,您没有调试web服务器进程的权限 具体:无法在web服务器上启动调试,您没有调试web服务器进程的权限,您需要以web服务器的用户账户身份运行,或者具有管理员权限. ...

  8. sx1308一些异常波形调试

    板子开源https://oshwhub.com/jhx275816/yi-ge-jian-dan-de-li-dian-chi-chong-dian-mo-kuai-yi-jing-ce-shi 小学 ...

  9. 小程序微信授权登录服务器异常,解决调试腾讯云微信小程序Demo错误“登录失败:调用鉴权服务失败#40029_WEIXIN_CODE_ERR”...

    此文章解决大家有可能遇到的"登录失败:调用鉴权服务失败#40029的问题"~~ 很多人出现上面的问题,那是因为:如果在购买解决方案时,把AppId 和 AppSecret 填写错误 ...

  10. 记一次悲催的软件异常崩溃调试解决历程,错误0xc0000417,无效参数,_set_invalid_parameter_handler

    这个问题其实分析了好几天,一步一步,最终找到答案,写这个文章的意义,不在于本次解决问题,在于总结,也是反思.

最新文章

  1. 写代码做副业月入50K+的方法都藏在这几个抖音、拼多多大佬的公众号里
  2. winserver2008 Oracle 11g 安装
  3. python统计个数的函数_Python
  4. 基础数据仓库环境搭建(一)数据仓库概述
  5. Http / Put 和 Post 区别
  6. 【MySQl】MyISAM和InnoDB索引对比
  7. Android Studio最全插件整理
  8. 一个关于sql server索引与填充因子的链接
  9. RUNOOB python练习题2
  10. Windiws环境安装轻量级文件服务器ftpserver
  11. 9-3:C++多态之多态的实现原理之虚函数表,虚函数表指针静态绑定和动态绑定
  12. 一张图读懂哔哩哔哩2019Q1财报
  13. js 操作java对象_js对象复制
  14. 使用redux和axios获取api数据
  15. 第四章——SQLServer2008-2012资源及性能监控(1)
  16. Linux下汇编语言学习笔记76 ---
  17. [CareerCup] 18.4 Count Number of Two 统计数字2的个数
  18. linux能运行关关采集器吗,杰奇linux采集器,基本能用且速度还行
  19. kobo glo原系统设置
  20. 39页大数据治理运营整体解决方案

热门文章

  1. 跟熊浩学沟通30讲读后感_跟熊浩学沟通
  2. 有向图的邻接矩阵的平方运算的含义
  3. Excel阳历转农历VBA函数
  4. 免费申请阿里云服务器
  5. PHP生成短链接的方法
  6. 读书笔记:《明朝那些事第三部:妖孽宫廷》
  7. 自考招生工商学院类网站织梦模板
  8. 001.Heartbeat简介
  9. 太难了 | 2019年互联网寒冬,如何防止自己被裁员失业?
  10. html 指针图表,HTML5 canvas 指针时钟