分类: WINDOWS
2007-10-12 11:49:34
一、SEH背景知识
SEH("Structured Exception Handling"),即结构化异常处理.是操作系统提供给程序设计者的强有力的处理程序错误或异常的武器.在VISUAL C++中你或许已经熟悉了_try{} _finally{} 和_try{} _except {} 结构,这些并不是编译程序本身所固有的,本质上只不过是对windows内在提供的结构化异常处理的包装,不用这些高级语言编译器所提供的包装 ,照样可以利用系统提供的强大seh处理功能,在后面你将可以看到,用系统本身提供seh结构和规则以及ASM语言, 我们将对SEH的机制以及实现有一个彻底的了解.
发生异常时系统的处理顺序(by Jeremy Gordon):
1.系统首先判断异常是否应发送给目标程序的异常处理例程,如果决定应该发送,并且目标程序正在被调试,则系统挂起程序并向调试器发送EXCEPTION_DEBUG_EVENT消息.呵呵,这不是正好可以用来探测调试器的存在吗?
2.如果你的程序没有被调试或者调试器未能处理异常,系统就会继续查找你是否安装了线程相关的异常处理例程,如果你安装了线程相关的异常处理例程,系统就把异常发送给你的程序seh处理例程,交由其处理.
3.每个线程相关的异常处理例程可以处理或者不处理这个异常,如果他不处理并且安装了多个线程相关的异常处理例程, 可交由链起来的其他例程处理.
4.如果这些例程均选择不处理异常,如果程序处于被调试状态,操作系统仍会再次挂起程序通知debugger.
5.如果程序未处于被调试状态或者debugger没有能够处理,并且你调用SetUnhandledExceptionFilter安装了最后异常处理例程的话,系统转向对它的调用.
6.如果你没有安装最后异常处理例程或者他没有处理这个异常,系统会调用默认的系统处理程序,通常显示一个对话框,你可以选择关闭或者最后将其附加到调试器上的调试按钮.如果没有调试器能被附加于其上或者调试器也处理不了,系统就调用ExitProcess终结程序.
7.不过在终结之前,系统仍然对发生异常的线程异常处理句柄来一次展开,这是线程异常处理例程最后清理的机会. 如果你看了上面的步骤一头雾水的话,别着急,化点时间慢慢理解或者进入下一部分实例操作.
在实际程序设计过程中,不可能只有一个异常处理例程,这就产生了异常处理程序嵌套的问题,可能很多处理例程分别监视若干子程序并处理其中某种异常,另外一个监视所有子程序可能产生的共性异常,这作起来实际很容易,也方便调试.你只要依次建立异常处理框架就可以了.
关于VC++异常处理可以嵌套很多人可能比较熟悉,用起来更容易不过实现比这里也就复杂得多,在VC++ 中一个程序所有异常只指向一个相同的处理句例程,然后在这个处理例程里再实现对各个子异常处理例程的调用,他的大致方法是建立一个子异常处理例程入口的数组表,然后根据指针来调用子处理例程,过程比较烦琐,原来打算大致写一点,现在发现自己对C/C++了解实在太少,各位有兴趣还是自己参考MSDN Matt Pietrek 1996年写的一篇文章
有异常嵌套就涉及到异常展开的问题,也许你注意到了如果按照我前面的例子包括final都不处理异常的话, 最后系统在终结程序之前会来一次展开,在试验之后发现,展开不会调用final只是对per_thread例程展开(right?).什么是堆栈展开?为什么要进行堆栈展开?如何进行堆栈展开?
我曾经为堆栈展开迷惑过,原因是各种资料的描述很不一致,Matt Pietrek说展开后前面的ERR结构被释放, 并且好像链后面如果决定处理必须展开,很多C/C++讲述异常处理的书也如斯说这使人很迷惑,我们再来看看Jeremy Gordon的描述,堆栈展开是处理异常的例程自愿进行的.呵呵,究竟事实如何?
在迷惑好久之后我终于找到了答案:Matt Pietrek讲的没有错,那是VC++以及系统kernel的处理方法,Jeremy Gordon说的也是正确的,那是我门asm Fans的自由!
好了现在来说堆栈展开,堆栈展开是异常处理例程在决定处理某个异常的时候给前面不处理这个异常的处理例程的一个清洗的机会,前面拒绝处理这个异常的例程可以释放必要的句柄对象或者释放堆栈或者干点别的工作...
那完全是你的自由,叫stack unwind似乎有点牵强.堆栈展开有一个重要的标志就是
EXCEPTION_RECORD.ExceptionFlag为2,表示正在展开,你可以进行相应的处理工作,但实际上经常用的是6这是因为还有一个UNWIND_EXIT什么的,具体含义我也没有搞明白,不过对我们的工作好像没有什么影响.
注意在自己的异常处理例程中,unwind不是的,必须你自己自觉地引发,如果所有例程都不处理系统最后的展开是注定的,当然如果没有必要你也可以不展开.
Structured Exception Handling |
责任编辑:ncic 更新日期:2007-4-5 |
在所有Win32操作系统提供的机制中,使用最广泛的未公开的机制恐怕就要数结构化异常处理(structuredexceptionhandling,SEH)了。一提到结构化异常处理,可能就会令人想起_try、_finally和_except之类的词儿。在任何一本不错的Win32书中都会有对SEH详细的介绍。甚至连Win32SDK里都对使用_try、_finally和_except进行结构化异常处理作了完整的介绍。既然有这么多地放都提到了SEH,那我为什么还要说它是未公开的呢?本质上讲,Win32结构化异常处理是操作系统提供的一种。编译器的运行时库对这种操作系统实现进行了封装,而所有能找到的介绍SEH的文档讲的都是针对某一特定编译器的运行时库。关键字_try、_finally和_except并没有什么神秘的。微软的OS和编译器定义了这些关键字以及它们的行为。其它的C++编译器厂商也只需要尊从它们定好的语义就行了。在编译器的SEH层减少了直接使用纯操作系统的SEH所带来的危害的同时,也将纯操作系统的SEH从大家的面前隐藏了起来。 我收到过大量的电子邮件说他们都需要实现编译器级的SEH但却找不到公开的文档。本来,我可以指着VisualC++和BorlangC++的运行时库的源代码说看一下它们就行了。但是,不知道是什么原因,编译器级的SEH仍是个天大的秘密。微软和Borland都没有提供SEH最内层的源代码。 在本文中,我会从最基本的概念上讲解结构化异常处理。在讲解的时候,我会将操作系统所提供的与编译器代码生成和运行时库支持的分离开来。当深入关键性操作系统程序的代码时,我基于的都是Intel版的WindowsNT4.0。然而。我所讲的大部分内容同样适用于其它的处理器。 我会避免提及实际的C++的异常处理,C++下用的是catch()而不是_except。其实,真正的C++异常处理的实现方式和我所讲的方式也是极为相似的。但是,真正C++异常处理特有的复杂性会影响到我这里所讲的概念。对于深挖那些晦涩的.H和.INC文件并拼凑出Win32SEH的相关代码,最好的一个信息来源就是IBMOS/2的头文件(特别是BSEXCPT.H)。这对有相关经验的人并没什么可希奇的,这里讲的SEH机制在微软开发OS/2时就定义了。因此,Win32的SEH与OS/2的极为相似。 SEHintheBuff 若将SEH的细节都放到一起讨论,任务实在艰巨,因此,我会从简单的开始,一层一层往深里讲。如果之前从未使用过结构化异常处理,则正好心无杂念。若是用过,那就要努力将 _try、GetExceptionCode和 EXCEPTION_EXECUTE_HANDLER从脑子中扫出,假装这是一个全新的概念。Areyouready?Good。 |
SEH 结构化异常处理(1) | |||
责任编辑:ncic 更新日期:2007-4-5 | |||
[工具]flyod1.10 [目的]学习SEH的手法,另书中是用SoftICE调试的,看起来不习惯.根据原文内容重新整理一下,便于和我一样的菜鸟们一起学习. 今天下决心,好好学习,这是就算是个开始吧!感觉学明白的确很不容易! [注释]?--为不能理解的地方,请大侠们指点一下.学习过程中,有理解错误的地方,肯请大侠们多多指教. [练习对象]加密与加密二版第10章,光盘配套的练习软件:seh.exeseh2.exe [writer] ytcswb 2005.2.1 感谢看学及论坛的大侠们为我们提供这么好的学习资料。 1.例子seh.exe学习: 00401000>$ 8D4424F8 leaeax,dwordptrss:[esp-8]//程序入口!根据下面的代码分析,这里显然可以
在看看EXCEPTION_RECORD结构:
|