我们都知道SEH异常处理机制,那VEH、TopLevelEH呢?他们执行的先后顺序是怎样的呢?当这些机制都不使用的情况下,会发生什么情况呢?异常处理器是怎么工作的?如果你对此感兴趣,那我们就一起来扒开异常处理机制的面纱吧

术语:
SEH: 结构化异常处理
VEH: 向量化异常处理
TopLevelEH:顶层异常处理

EXCEPTION_EXECUTE_HANDLER :该异常被处理。从异常处下一条指令继续执行
EXCEPTION_CONTINUE_SEARCH:不能处理该异常,让别人处理它吧
EXCEPTION_CONTINUE_EXECUTION:该异常被忽略。从异常处处继续执行

//调试器返回值:
DBG_CONTINUE : 等同于EXCEPTION_CONTINUE_EXECUTION
DBG_EXCEPTION_NOT_HANDLED :等同于EXCEPTION_CONTINUE_SEARCH

想想对我这等语文是体育老师教出来的童鞋来说,想把这个主题将透彻,还真是有点难度的~,我们还是按异常处理器执行顺序来吧,废话不多说,开始。。。

异常处理器其实包含 内核异常处理R3异常处理内核异常处理比较简单,我也对它没兴趣,所以这里就把它给忽略了。我们只讲R3程序产生异常时,异常处理器是怎么工作的。

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

大致分上面几步把,下面咱就详细讨论一下各个步骤都干了哪些细活

1. 第一次交给调试器
如果该出现异常的程序正在被调试,则该异常首先交给调试器处理(通过DebugPort)。
调试器拿到这个异常后,需要判断是否要处理该异常,如果处理该异常返回DBG_CONTINUE,否则返回DBG_EXCEPTION_NOT_HANDLED

代码:

    while(!bExit) {DWORD dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;DEBUG_EVENT debugEvent;WaitForDebugEvent(&debugEvent, INFINITE);switch ( debugEvent.dwDebugEventCode ){case EXCEPTION_DEBUG_EVENT:{EXCEPTION_DEBUG_INFO* pExcpInfo = &debugEvent.u.Exception;if ( MessageBox(0,_T("处理该异常?"), _T("我是调试器"),MB_YESNO)==IDYES ){dwContinueStatus = DBG_CONTINUE;//...}}break;//...}ContinueDebugEvent(debugEvent.dwProcessId,  debugEvent.dwThreadId, dwContinueStatus); }

2. 执行VEH
这里就不讲Veh的概念了,有兴趣的去Google一下。
如果没有被调试,或者调试器返回DBG_EXCEPTION_NOT_HANDLED,则就会检查是否存在VEH。如果存在VEH,则把异常交给他们处理。
VEH是个链表,可以存在多个Veh。每个VEH按顺序被调用
一个VEH可以返回连个值:EXCEPTION_CONTINUE_SEARCHEXCEPTION_CONTINUE_EXECUTION。返回EXCEPTION_EXECUTE_HANDLER无效的,等同于EXCEPTION_CONTINUE_SEARCH。
当一个Veh返回EXCEPTION_CONTINUE_SEARCH,则把异常交给下一个VEH处理。
如果返回EXCEPTION_CONTINUE_EXECUTION,认为已经被处理,退出异常处理器在异常指令处继续执行
从执行顺序来看,VEH是在SEH之前执行的,并且不依赖某一线程,本进程中任何线程出现异常都可以被VEH处理,所以在有些时候是很有用处的。
怎么添加一个VEH呢?

代码:

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

3. 执行SEH
SEH应该是大家都比较熟悉的了。当所有的VEH都不处理该异常,该异常就会让SEH处理。
SEH是基于线程栈的异常处理机制,所以它只能处理自己线程的异常。
先看一个示例代码:

代码:

LONG FirstSEHer( PEXCEPTION_POINTERS pExcepInfo )
{TCHAR* pTitle = _T("第一个SEH处理器");_tprintf( _T("[EH.Exe] [SEH][1] in \n") );LONG nRet = ShowSelectMessageBox(pTitle);_tprintf( _T("[EH.Exe] [SEH][1] out \n") );return nRet;
}
LONG SecondSEHer( PEXCEPTION_POINTERS pExcepInfo )
{TCHAR* pTitle = _T("第二个SEH处理器");_tprintf( _T("[EH.Exe] [SEH][2] in \n") );LONG nRet = ShowSelectMessageBox(pTitle);_tprintf( _T("[EH.Exe] [SEH][2] out \n") );;return nRet;
}
void ExcepFunction()
{__try{__try{__try{_tprintf( _T("[EH.Exe] *[CALL] int 3\n") );__asm int 3;}__finally{printf( "[EH.Exe] *[SEH][0] finally call...\n" );}}__except( FirstSEHer(GetExceptionInformation()) ){_tprintf( _T("[EH.Exe] [SEH][1] 被俺处理了~(只有返回EXCEPTION_EXECUTE_HANDLER才会走到这里)\n"));}}__except( SecondSEHer(GetExceptionInformation()) ){_tprintf( _T("[EH.Exe] [SEH][2] 被俺处理了(只有返回EXCEPTION_EXECUTE_HANDLER才会走到这里)\n"));}
}

ExcepFunction函数有三个SEH,但是有两个Headler。当__asm int 3;被执行时就会被SEH捕获。捕获后,首先交给FirstSEHer处理,如果FirstSEHer返回EXCEPTION_CONTINUE_SEARCH则才会交给SecondSEHer处理。
FirstSEHer可以返回三个值:EXCEPTION_CONTINUE_SEARCHEXCEPTION_EXECUTE_HANDLEREXCEPTION_CONTINUE_EXECUTION
当返回EXCEPTION_CONTINUE_SEARCH,执行上一层SEH,这里执行SecondSEHer
返回EXCEPTION_EXECUTE_HANDLER时则表示异常被处理,会先把内部的__finally块执行完,再跳到自身的__except块中执行。
返回EXCEPTION_CONTINUE_EXECUTION时表示该异常被忽略,会再次执行__asm int 3处指令。如果该条汇编不被修正成其他指令(如nop),则会再次产生一个异常。

另外,如果想在 try catch的C++异常中捕获系统异常,必须让C++支持SEH异常处理。设置方法: Vc-〉项目属性-->配置属性-->c/C++-->代码生成-->启用C++异常,选中"是,但有SEH异常(/EHa)"。

4. TopLevelEH
顶层异常处理,这个其实是利用SEH实现的。在最顶层的SEH中,可以注册一个顶层异常处理器。虽然他是基于SEH实现的,但是它可以处理所有线程抛出的异常。
当SEH都处理不了该异常,在最顶层的SEH中就会检查是否注册了顶层异常处理,如果注册了,则执行顶层异常处理。
注意:如果该进程正在调试状态,顶层异常处理会被忽略,不会被执行
顶层异常处理函数也可以返回三个值:EXCEPTION_CONTINUE_SEARCHEXCEPTION_EXECUTE_HANDLEREXCEPTION_CONTINUE_EXECUTION
返回EXCEPTION_CONTINUE_EXECUTION时,和SEH一样。
返回EXCEPTION_EXECUTE_HANDLER时,则直接杀死该进程
返回EXCEPTION_CONTINUE_SEARCH时,会查注册表,检查是否存在实时调试器。注册表路径:KLM\software\microsoft\windows nt\currentvsrsion\aedebug。如果Auto==1,Debugger!=NULL则根据Debugger中指示的参数启动实时调试器,让调试器处理该异常。(如果不存在顶层异常且进程没被调试,也会检查并启动实时调试器)
注册方法:

代码:

LONG NTAPI TopLevelExcepFilter( PEXCEPTION_POINTERS pExcepInfo )
{TCHAR* pTitle = _T("*顶级* 异常处理器");_tprintf( _T("[EH.Exe] [TOP] in \n") );LONG nRet = ShowSelectMessageBox(pTitle);_tprintf( _T("[EH.Exe] [TOP] out \n") );;return nRet;
}
//注册
SetUnhandledExceptionFilter( &TopLevelExcepFilter );

顶层异常处理通常用来生成程序Dump文件。供开发人员分析。

5. 再次交给调试器
如果上述的异常处理机制都没有处理该异常,则调试器会再次接收该异常。
调试器这个时候返回DBG_CONTINUE,则和第一次相同。
返回DBG_EXCEPTION_NOT_HANDLED,则直接杀死该进程

6. 调用异常端口通知csrss.exe
当上面提到的都没有处理该异常,则调用ExceptionPort通知csrss.exe。csrss.exe的做法是会弹出一个对话框:

这个时候还有一次修复异常并让程序继续运行的机会,就是点击“调试”按钮。其他按钮都很导致异常进程被终止。

终于写完了,为了完成该文,特意写了个小软件。里面包含一个异常产生程序(ExcepHandler)和一个简单调试器(MyDbg)源码在此:
TestException.rar.
(调试器参考了超然兄的代码,特此感谢)

白话windows之四 异常处理机制(VEH、SEH、TopLevelEH...)相关推荐

  1. [网络安全自学篇] 五十四.Windows系统安全之基于SEH异常处理机制的栈溢出攻击及防御解析

    这是作者的网络安全自学教程系列,主要是关于安全工具和实践操作的在线笔记,特分享出来与博友们学习,希望您们喜欢,一起进步.前文分享了XP和Kali环境搭建,通过Windows漏洞实现栈溢出攻击,通过Me ...

  2. 攻击windows异常处理机制SEH

    转载自个人博客0pt1mus 0x00 简介 本文主要有两个部分.第一部分介绍windows异常处理机制中的SEH,详细介绍SEH的工作原理.第二部分介绍如何通过栈溢出实现利用SEH来绕过GS. 0x ...

  3. Windows异常世界历险记(五)——VC6中结构化异常处理机制的反汇编分析(下)

    在本系列的上一篇文章Windows异常世界历险记(四)--VC6中结构化异常处理机制的反汇编分析(中)中,给出了针对VC6的异常处理机制进行逆向后得到的伪码.在本文中,我们仍然以之前写的小程序为例,通 ...

  4. Windows消息响应机制之四:PostQuitMessage和GetMessage函数

    Windows是消息驱动的操作系统.在Windows环境下编程必须熟练掌握Windows消息响应机制.  今天在练习Win32编程时碰到一个关于GetMessage函数的问题.这个问题之前一直没有引起 ...

  5. C++ 异常处理机制的实现

    http://blog.jobbole.com/103925/ 本文深入讨论了VC++编译器异常处理的实现机制.附件源代码包含了一个VC++的异常处理库. 下载源代码 – 19 Kb 介绍 相对于传统 ...

  6. 异常处理第三讲,SEH(结构化异常处理),异常展开问题

    异常处理第三讲,SEH(结构化异常处理),异常展开问题 作者:IBinary 出处:http://www.cnblogs.com/iBinary/ 版权所有,欢迎保留原文链接进行转载:) 不知道昨天有 ...

  7. 全面介绍Windows内存管理机制及C++内存分配实例(六):堆栈

    本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ...

  8. 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件

    本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ...

  9. 全面介绍Windows内存管理机制及C++内存分配实例(三):虚拟内存

    本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ...

  10. kotlin协程硬核解读(5. Java异常本质协程异常传播取消和异常处理机制)

    版权声明:本文为openXu原创文章[openXu的博客],未经博主允许不得以任何形式转载 文章目录 1. 异常的本质 1.1 操作系统.程序.JVM.进程.线程 1.2 异常方法调用栈 1.3 ja ...

最新文章

  1. 摆脱 FM!这些推荐系统模型真香
  2. 20162318 2016-2017-2 《程序设计与数据结构》第7周学习总结
  3. LeetCode-剑指 Offer 14- I. 剪绳子
  4. 联想v3500存储Linux配置手册,OEL6.X IBM v3500存储多路径配置
  5. al-khaser 调试器,虚拟机检测工具
  6. 图像的存储格式转化(python实现)
  7. 广告中oCPX到底是如何进行优化的?
  8. sqllite开发安卓项目_【兼职项目】预算3万开发无线温度电流传感,2万开发直流电机打磨机控制...
  9. android闹钟唤醒不准的原因_俄罗斯睡眠专家:闹钟铃声选错了,可能扰乱你的内分泌...
  10. 史上最牛的文科生:法学出身,却发明出十进制计算器,折磨无数人的微积分符号,跨界40多个领域惊艳学术圈
  11. dmp只导数据不导结构_今日头条快消食品推广CVR为何高达4.40%?原来DMP定向这么好...
  12. python2.7 tab,自动补全
  13. matlab直扩序列生成,基于matlab的直接序列扩频通信系统仿真毕业论文
  14. 单片机编程软件很简单(10),Keil单片机编程软件常见文件介绍
  15. 19个免费的UI界面设计工具及资源
  16. java 开发之商品规格属性(SKU)组合算法
  17. Soul源码总结-01-15
  18. android qq隐藏功能,90﹪的人都不知道--手机QQ这些隐藏的功能!
  19. web-sso 系统集成 单点登录
  20. 会议服务与管理【2】

热门文章

  1. editplus java acp_EditPlus配置Java编译器
  2. 37.0.概率论与数理统计-常用统计量及其分布,次序统计量,(样本均值,样本方差,样本标准差,样本k阶矩,样本k阶中心矩)
  3. 我的日程安排系列问题(区间重叠问题)
  4. 深度:嵌入式系统的软件架构设计
  5. 关于ansys19.0安装问题
  6. python基础第二章:流程控制
  7. 信庭嵌入式工作室-ARM应用技术之体系结构应用(上)
  8. 转行学习IT技术要做哪些准备
  9. 74HC595 的使用
  10. 数据驱动测试(DDT)入门