[工学]毕业设计异常处理.doc

上传人:sccc 文档编号:4533123 上传时间:2023-04-26 格式:DOC 页数:59 大小:962.50KB
返回 下载 相关 举报
[工学]毕业设计异常处理.doc_第1页
第1页 / 共59页
[工学]毕业设计异常处理.doc_第2页
第2页 / 共59页
[工学]毕业设计异常处理.doc_第3页
第3页 / 共59页
[工学]毕业设计异常处理.doc_第4页
第4页 / 共59页
[工学]毕业设计异常处理.doc_第5页
第5页 / 共59页
点击查看更多>>
资源描述

《[工学]毕业设计异常处理.doc》由会员分享,可在线阅读,更多相关《[工学]毕业设计异常处理.doc(59页珍藏版)》请在三一办公上搜索。

1、漳州师范学院 毕业论文(设计)异常处理 EXCEPTION HANDLING姓 名: 张妙平 学 号: 050811131 系 别: 计算机科学与工程系 专 业: 计算机科学与技术 年 级: 05级 指导教师: 王桃发 2009 年 3 月 28 日摘要异常处理是程序开发的一个重要内容,异常处理的好坏关系到程序的友好程序和系统的稳定性.C+是一种纯面向对象的编程语言,其异常处理机制和普通的编程语言有很多不同的地方,有力地增强了C+程序的健壮性和容错性.关键字: C+;异常处理;面向对象编程;健壮性;容错性Abstract Exception handling plays a significa

2、nt role in programming development.Its performance will influence the friendliness and stability of the system.As a complete object obriented programming language, the exception handling mechanism of C+ is very different from other programming language,and effectively improves robustness and toleran

3、ce of the programs.Key words:C+;Exception handling;object obriented programming;robustness;toleranceII目录中英文摘要(I)1.前言(1)1.180386的中断和异常(1)1.2Windows结构化异常处理(SEH)(1)1.3C+中异常处理(2)1.4病毒的分类(3)1.5计算机病毒的命名(4)1.6计算机病毒的工作过程(4)1.7计算机病毒的传染(4)2.蠕虫病毒(7)2.1蠕虫病毒的定义(7)2.2网络蠕虫病毒分析和防范(9)2.3经典蠕虫病毒尼姆达(Nimda)解析(12)3.尼姆达(N

4、IMDA)蠕虫病毒的查杀(18)3.1前言(18)3.2杀毒软件的扫描方法(18)3.3反病毒技术(21)3.4尼姆达(Nimda)蠕虫病毒嗅探器的设计(25)3.5 结果(31)3.6尼姆达(Nimda)蠕虫病毒解决方法(33)3.7防范措施(33)4.总结(35)致 谢(36)参考文献(37)附录(38)1.前言1.180386的中断和异常 8086/8088把中断内部中断和外部中断两大类.为了支持多任务和虚拟存储器等功能.80386把外部中断称为”中断”,把内部中断称为”异常”.与8086/8088一样,80386通常在两条指令之间响应中断或异常;80386最多处理256种中断或异常.1

5、.1.1中断对80386而言,中断是由异步的外部事件引起的.外部事件及中断响应与正执行的指令没有关系.通常,中断用于指示I/0设备的一次操作已完成.与8086/8088一样,80386有两根引脚INTR和NMI接受外部中断请求信号.INTR接受可屏蔽中断请求.NMI接受不可屏蔽中断请求.在80386中,标志寄存器EFLAGS中的IF标志决定是否屏蔽可屏蔽中断请求. 外部硬件在通过INTR发出中断请求信号的同时,还要向处理器给出一个8位(28=256)的中断向量号.处理处理器在响应可屏蔽中断请求时,读取这个由外部硬件给出的中断向量号.处理器对这个中断向量号并没有规定.但在具体的微机系统中,系统必

6、须通过软件和硬件的配合设置,使得给出的这个中断向量号不仅与外部中断源对应,而且要避免中断向量号使用冲突情况的出现.可编程中断控制器芯片8259A可配合80386工作,能够根据设置向处理器提供上述这个中断向量号和还能处理中断请求的优先级.每个8259A芯片可以支持8路中断请求信号,如果使用9个8259A芯片,就可使80386在单个引脚INTR上接受多达64个中断源的中断请求信号. 处理器不屏蔽来自NMI的中断请求.处理器在响应NMI中断时,不从外部硬件接受中断向量号.与8086/8088一样,80386中,不可屏蔽中断所对应的中断向量号固定为2.为了避免不可屏蔽中断的嵌套,每当接受一个NMI中断

7、,处理器就在内部屏蔽了在次响应NMI,这一屏蔽过程直到执行中断返回指令IRET后才结束.所以,NMI处理程序应以IRET指令结束. 1.1.2 异常异常是80386在执行指令期间检测到不正常的或非法的条件所引起的.异常与正执行的指令有直接的联系.例如:执行除法指令时,除数为0.再如,执行指令时发现特权级不正确.当发生这些情况时,指令就不能成功完成.软中断指令”INT n”和”INTO”也归类于异常而不称为中断,这是因为执行这些指令产生异常事件.80386识别多种不同类别的异常,并赋予每一种类别不同的中断向量号.异常发生后,处理器就象响应中断那样处理异常.也即,根据中断向量号,转响应的中断处理程

8、序.把这种中断处理程序称为异常处理程序可能更合适.根据引起异常的的程序是否可被恢复和恢复点不同,把异常进一步分类为故障(Fault),陷阱(Trap),中止(Abort).我们把对应的异常处理程序分别称为故障处理程序,陷阱处理程序和中止处理程序.故障是在引起异常的指令之前,把异常情况通知给系统的一种异常.80386认为故障是可排除的.当控制转移到故障处理程序时,所保存的断点CS及EIP的值指向引起故障的指令.这样,在故障处理程序把故障排除后,执行IRET返回到引起鼓掌的程序继续执行时,刚才引起故障的指令可重新得到执行.这种重新执行,不需要操作系统软件的额外参与.故障的发现可能在指令开始执行之前

9、,也可能在指令执行期间.如果在执行指令期间检测到故障,那么中止故障指令,并把指令的源操作数恢复为指令开始执行之前的值.这可保证故障指令的重新执行得到正确的结果.例如,在一条指令的执行期间,如果发现段不存在,那么停止该指令的执行,并通知系统产生段故障.对应的段故障处理程序可通过加载该段的方法来排除故障,之后,原指令就可以成功执行,至少不在发生段不存在故障.陷阱是在引起异常的指令之后,把异常情况通知给系统的一种异常.当控制转移到异常处理程序时.所保存的断点CS和EIP的值指向引起陷阱的指令的下一条要执行的指令.下一条要执行的指令,不一定就是下一条指令.因此,陷阱处理程序并不是总能根据保存的断点,反

10、推确定出产生异常的指令.在转入陷阱处理程序时,引起陷阱的指令应正常完成,它有可能改变了寄存器或存储单元.软中断指令,单步异常是陷阱的例子.中止是在系统出现严重情况时,通知系统的一种异常.引起中止的指令是无法确定的.产生中止时正执行的程序不能被恢复执行.系统接受中止后,处理程序要重新建立各种系统表格,并可能需要重新启动操作系统.硬件故障和系统表中出现非法值或不一致值是中止的例子.1.1.3 优先级 在一条指令执行期间,如检测到不止一个中断或异常,那么按表10.6所列优先级通知系统.把优先级最高的中断或异常通知系统,其他优先级较低的异常被废止,而优先级较低的中断则保持悬挂.1.1.4 异常类型 象

11、中断分为多种类型一样,异常也分为多种类型.80386识别的多种不同类型的异常及赋予的对应中断向量号列于表10.7.某些异常还以出错码的形式提供一些附加的信息传递给异常处理程序,出错代码列中的”无”表示没有出错代码,”有”表示有出错代码.8088中断向量号和80386中断向量号发生冲突.实际上,Intel 在宣布8086/8088时,保留了这些发生冲突的中断向量号.尽管发生这样的冲突,但以80386为CPU的微机系统仍可保持与以8088为CPU的微机系统的兼容.原因是在80386的实方式下,几乎不会发生那些中断向量号与外部硬件在提出中断请求时所提供的中断向量号存在冲突的异常.故障类异常当发生故障

12、,控制转移到故障处理程序时,所保存的断点CS及EIP的值指向引起故障的指令,以便在排除故障后恢复执行.(1)除法出错故障(异常0) 除法出错是一种故障,当执行DIV指令或IDIV指令时,如果除数等于0,或者商太大,以至于存放商的操作数容纳不下,那么产生这一故障.除法出错不提供出错码.(2)边界检查故障(异常5) 如果BOUND指令发现被测试的值超出了指令中给顶的范围,那么发生边界检查故障,边界检查故障不提供出错码.(3)非法操作码故障(异常6) 如果80386不能把CS及EIP所指存储单元处的位模式识别为某条指令的部分,那么就发生非法操作码故障.当出现下列情况时,发生这样的故障:1.操作码字段

13、的内容不是一个合法的80386指令的代码.2.要求使用存储器操作数的场合,使用了寄存器操作数.3.不能被加锁的指令使用了LOCK前缀. 非法操作码故障不提供出错码.(4)设备不可用故障(异常7) 设备不可用故障支持80387数字协处理器.在没有80387协处理器硬件的系统中,可用该异常的处理程序代替协处理器的软件模拟器.在发生任务切换时,使得只有在新任务使用浮点指令时,才进行80387寄存器状态的切换.设备不可用故障不提供出错码.该故障在下列情况下产生:1.在执行浮点指令时,控制寄存器CR0中的EM位或TS为1;2.在执行WAIT指令时,控制寄存器CR0中TS位及EM位都为1.(5)段不存在故

14、障(异常0BH) 处理器在把描述符装入非SS段寄存器的高速缓冲器时,如果发现描述符其他发面有效,而P位为0(表示对应段不存在),那么就发生段不存在故障.有关SS段的情形纳入堆栈段故障.在进入故障处理程序时,保存的CS及EIP指向发生故障的指令;或者该故障作为任务切换的一部分发生时,指向任务的第一条指令. 段不存在故障提供一个包含引起该异常的段选择子的出错码.出错码的格式如图: 16位出错码的主要成分是选择子,高13位是选择子的索引部分,TI位是描述符表指示位.图所示出错码格式是段异常出错码的一般格式.从图中可见出错码中不含选择子的RPL,而由IDT位和EXT位代替.当处理某一异常或外部中断时,

15、又发生了某种异常,那么EXT位置1.当从中断描述符表IDT读出表项并产生异常时,IDT位置1.这只在中断或异常的处理期间才会发生.当没有选择子时,构成出错码选择子部分的值为0.(6)堆栈段故障(异常0CH)当处理器检测到用SS寄存器进行寻址的与段有关的某种问题时,就发生堆栈段故障.在进入故障处理程序时,保存的CS和EIP指向发生故障的指令;或者该故障作为任务切换的一部分发生时,指向任务的第一条指令.堆栈段故障提供一个出错码,出错码的一般格式也如图所示. 具体地说,当出现下列三种情况时,将引起堆栈段故障.1.在堆栈操作时,偏移超出段界限所规定的范围,这种情况下的出错码是0.例如,PUSH操作时,

16、堆栈溢出.2.在由特权级变换所引起的对内层堆栈的操作时,偏移超出段界限所规定的范围. 这种情况下的出错码包含有有内层堆栈的选择子.3装入到SS寄存器(高速缓冲寄存器)的描述符的存在位为0.这种情况下的出错码包含有对应的选择子. 上述的第一种情况是最容易辨别的.第二和第三种情况的辨别要通过出错码所含选择子所示的描述符中的存在位进行.如果存在位为1,那么是第二种情况;否则是第三种情况.(7)无效TSS故障(异常0AH)当正从任务状态TSS装入选择子时,如果发生触了不存在故障以外的段异常时,就发生无效TSS故障.在进行故障处理程序时,保存的CS及EIP指向发生故障的指令,或者该故障作为任务切换的一部

17、分发生时,指向任务的第一条指令.无效TSS故障提供一个出错码,出错码的格式如图.其中选择子部分是指向引起故障TSS的选择子.一些引起无效TSS故障的原因如下:TSS描述符中的段限长小于103.无效的LDT描述符,或者LDT未出现;堆栈段不是一个可写段;堆栈段选择子索引的描述符超出描述符表界限.堆栈段DPL与新的CPL不匹配;堆栈段选择子RPL不等于CPL.代码段选择子索引的描述符超出描述符表界限.代码段选择子不指向代码段;非一致代码段的DPL不等于新的CPL.一直代码段DPL大于新的CPL.对应DS,ES,FS或GS的选择子指向一个不可读段;对应DS,ES,FS或GS的选择子索引的描述符超出描

18、述符表界限.(8)通用保护故障(异常0DH)除了明确列出的段异常外,其他的段异常都被视为通用保护故障.在进入故障处理程序时,保存的CS及EIP指向发生故障的指令;或者作为该故障作为任务切换的一部分发生时,指向任务的第一条指令.通用保护故障提供一个出错码,出错码的一般也如图:根据处理程序可能进行的响应,通用保护故障可分为如下两类:1.违反保护方式,但程序无须终止的异常.这类故障提供的出错码是0.这种异常在应用程序执行特权指令或I/0访问时发生,支持虚拟8086程序的系统或支持虚拟I/0访问的系统,需要模拟这些指令,并在模拟完成产生故障的指令后,重新执行被中断的程序.2.违反保护方式,并导致程序终

19、止的异常.这类故障提供的出错码可能为0,也可能不为0(能确定选择子时).引起这类故障的一些原因如下:向某个只读数据段或代码段写;从某个只能执行的代码段读出.将某个系统段描述符装入到数据段寄存器DS,ES,FS,GS或SS;将控制转移到一个不可执行的段.在通过段寄存器CS,DS,ES,FS或GS访问内存时,偏移越出段界限;当访问某个描述符表时,超过描述符表段界限;把PG为为1但PE位为0的控制信息装入到CR0;切换到一个正忙的任务.对上述两类通用保护故障的辨别,可通过检查引起故障的指令和出错码进行.如果出错码非0,那么肯定是第二类通用保护故障.如果出错码是0,那么需进一步检查引起故障的指令,以确

20、定它是否是系统支持的可以模拟的指令.(9)页故障(异常0EH)(10)协处理器出错(异常10H)协处理器出错故障指示协处理器发生了未被屏蔽的数字错误,如上溢或下溢.在引起故障的浮点指令之后的下一条浮点指令或WAIT指令,把协处理器出错作为一个故障通知给系统.协处理器出错故障不提供出错码.陷阱类异常(1)调试陷阱(异常1) 调试异常有故障类型,也有陷阱类型.调试程序可以访问调试寄存器DR6,以确定调试异常的原因和类型.调试异常不提供出错码.(2)单字节INT3(异常3)INT3是一条特别的一字节”INT n”指令.调试程序可个该指令支持程序断点.INT 3指令被看成是一种陷阱,而不是一个中断.当

21、由于执行INT 3指令而进入异常3处理程序时,被保存的CS和EIP指向紧跟INT3的指令,也即INT 3指令后面的一个字节.INT3陷阱不提供出错码.(3)溢出(异常4)INTO指令提供条件陷阱,如果OF标志为1,那么INTO指令产生陷阱;否则不产生陷阱,继续执行INTO后面的指令.在进入溢出处理程序时,被保存的CS和EIP指向INTO指令的下一条指令.溢出陷阱不提供出错码.中止类异常(1)双重故障异常(异常8) 当系统正在处理一个异常时,如果又检测到一个异常,处理器试图向系统通知一个双重故障,而不是通知第二个异常.双重故障被分在中止异常那一类,所以在转入双重故障处理程序时,被保存的CS和EI

22、P可能不指向引起双重故障指令,而且指令的重新启动不支持双重故障.双重故障提供的出错码是0. 当正处理一个段故障异常时,有可能又产生一个页故障.在这种情况下,通知给系统的是页故障异常而不是双重故障异常.但是,如果正处理一个段或页故障时,又一个段故障被检测到;或者如果正处理一个页故障时,又一个页故障被检测到,那么就引起双重故障.当正处理一个双重故障时,又一个段或页故障被检测到,那么处理器暂停执行指令,并进入关机方式.关机方式类似与处理器执行一条HLT指令后的状态:处理器空转,并维持到处理器接受到一个NMI中断请求或者被重新启动为止.在关机方式下,处理器不响应INTR中断请求.双重故障通常指示系统表

23、出现严重的问题.例如段描述符表,页表,或中断描述符表出现问题.双重故障处理程序在重建系统表后,可能不得不重新启动操作系统.(2)协处理器段越界(异常9)协处理器段越界被分在中止异常这一类.当浮点指令操作数超出段界限时,产生该中止异常.协处理器段越界异常不提供出错码.1.1.5中断异常的转移方法这里介绍的中断和异常的转移方法是指80386在保护方式下响应中断和处理异常时所采用的转移方法中断描述符表IDT.与8086/8088一样,在响应中断或者处理异常时,80386根据中断向量号转对应的处理程序.但是,在保护方式下80386不再使用实方式下的中断向量表,而是使用中断描述符表IDT(Interru

24、pt Descriptor Table).在保护方式下,80386把中断向量号作为中断描述符表IDT中描述符的索引,而不在是中断向量表中的中断向量的索引.像全局描述符表GDT一样,中断描述符表IDT只有一个.中断描述符表寄存器IDTR指示IDT在内存中的位置.由于80386只识别256个中断向量号.所以IDT最大长度是2K.中断描述符表IDT所含的描述符只能是中断门,陷阱门和任务门.也就是说,在保护方式下,80386只有通过中断门,陷阱门,或任务门才能转移到对应的中断或异常处理程序.中断响应和异常处理的步骤由硬件自动实现的中断响应和异常处理的步骤如下:首先,判中断向量号要索引的门描述符是否超出

25、IDT的界限.如果超出界限,就引起了通用保护故障,出错码是中断向量号乘8再加2.其次,从IDT中取得对应的门描述符,分解出选择子,偏移量和描述符属性类型,并进行有关检查.描述符只能是任务门,286中断门,286陷阱门,386中断门或386陷阱门,否则,就引起通用保护故障,出错码是中断向量号盛8再加2.如果是由于INT n指令或者 INTO指令引起转移,还要检查中断门或陷阱门描述父中的DPL是否满足CPL=DPL.这种检查可以避免应用程序执行INT n指令时,使用分配给各种设备用的中断向量号.如果检查不通过,就引起通用保护故障,出错码是中断向量号乘8,在加2.门描述父中的P位必须是1,表示门描述

26、父是一个有效项,否则就引起段不存在故障,出错码是中断向量号乘8再加2.最后,根据门描述符类型,分情况转入中断或异常处理程序.对于异常处理,在开始上述步骤之前,还要根据异常类型确定返回点;如果有出错码,则形成符合出错码格式的出错码.对于异常处理,如果有出错码,在实际执行异常处理程序之前,还要把出错码压入堆栈.为了保证堆栈的双字边界对齐,16位的出错码以32位的值压入,其中高16位的值未作定义.通过中断门或陷阱门的转移如果中断向量号所指示门描述符是386中断门或386陷阱门,那么控制转移到当前任务的一个处理程序过程,并且可以变换特权级.与通过调用门的CALL指令一样,从中断门或陷阱门中获取指向处理

27、程序的48位全指针.其中,16位选择子是对应处理程序代码段的选择子,它指示GDT或LDT中的描述符;32为偏移指示处理程序入口点在代码段内的偏移.通过中断门或陷阱门的转移过程如下图.该过程由硬件自动进行. 图中”开始”处表示接上述分情况转移,所以此时已对由中断向量号所索引的IDT中的中断门或陷阱门描述符进行过必要的检查,并从中取得指示处理程序的由选择子和偏移构成的48位全指针.”结束”处表示转入实际的中断或陷阱处理程序.从图可见,中断门或陷阱门指示处理程序的选择子必须指向描述一个可执行代码段的描述符.如果选择子为空,就引起通用保护故障,出错码是0.如果描述符不是代码段描述符,就引起通用保护故障

28、,出错码含选择子.中断或异常可以转移到同一特权级或内层特权级.上述指定处理程序段的描述符中的类型及DPL字段,决定了这种同一任务内的转移是否要发生特权级的变换.如果是一个非一致的代码段,并且DPLCPL,那么要发生特权级的变换,堆栈也要切换成内层堆栈,但不复制堆栈中的参数.图中的”把描述符装入CS”是指把上述指定处理程序段的描述符装入CS的高速缓冲寄存器中,在这一步骤中对描述符进行检查,包括是否是代码段描述符和代码段是否存在等,因此可能在发生异常.在对该描述父进行检查时,通过调整RPL=0的方法,实现只考虑DPL,而不考虑门中选择子的RPL.在把描述符装入CS之后,还要检查门描述父中给出的表示

29、处理程序代码段入口的偏移是否越界,即是否超出段界限.如果越界,就引起出错码为0的通用保护故障.从图可见,把标志寄存器和断点压入堆栈的做法和顺序与实方式是相同的,但这里每一次堆栈操作是一个双字,CS被扩展成32位.把TF置成0,表示不允许处理程序单步执行.把NT置成0,表示处理程序在利用中断返回指令IRET返回时,返回到同一任务而不是一个嵌套任务.从图可以看出,通过中断门的转移和通过陷阱门的转移之间的差别只是对IF标志的处理.对于中断门,在转移过程中,把IF置成0,使得在处理程序执行期间,屏蔽掉INTR中断;对于陷阱门,在转移过程中,保持IF位不变,即如果IF为原是1,那么通过陷阱门转移到处理程

30、序之后仍允许INTR中断.因此,中断门最适宜与处理中断,而陷阱门适宜于处理异常.从图可以看出,在有出错码的情况下,在转入处理程序之前,还要把出错码压入堆栈.只有异常处理才有可能有出错码.以下一些图给出了中断门或陷阱门转移时的堆栈情况.(a)是没有变换特权级和没有出错码的情形;(b)是没有变换特权级和有出错码的情形;(C)是变换特权级和没有出错码的内层堆栈情形.(d)是变换特权级和有出错码的内层堆栈情况.注意图中每一项为双字. 通过任务门的转移如果中断向量号所指示的门描述符是任务门描述符,那么控制转移到一个作为独立的任务方式出现的处理程序.任务门中含48位全指针.这时,16位选择子是指向描述对应

31、程序任务的TSS段的选择子,也即该选择子指示一个可用的286TSS,或386TSS.通过任务门的转移与通过任务门到一个可用的386TSS的CALL指令的转移很相似,主要的区别是,对于提供出错码的异常处理,在完成任务切换之后,把出错码压入新任务的堆栈中.通过任务门的转移,在进入中断或异常处理程序时,标志寄存器EFLAGS中的NT位被置1,表示是嵌套任务.在响应中断或处理异常时,使用任务门可提供一个处理程序任务的自动调度.这种任务调度由硬件直接执行,并且越过包含在操作系统中的软件任务切换,这就为处理程序提供了一个快速的任务切换.中断或异常处理后的返回 中断返回指令IRET用于从中断或异常处理程序的

32、返回.该指令的执行根据任务嵌套标志NT是否为1,分两种情况:1.NT为1,表示是嵌套任务的返回.2.NT为0,表示当前任务内的返回.对于提供出错代码的异常的处理程序,必须先从堆栈中弹出出错代码,然后在执行IRET指令.中断返回指令IRET不仅能够用于由中断/异常引起的嵌套任务的返回,而且也适用于由段间调用指令CALL通过任务门引起的嵌套任务的返回.1.2Windows结构化异常处理(SEH)SEH是WINDOWS系统中处理计算机异常行为的一种方式,这种方式的特点就是用入栈的方式把断点保存起来,所以他的效率很高,往往能够处理很多的异常状态。Windows的异常处理机制是依靠80X86处理器的保护

33、机制来主动捕获异常,所以WIN32下异常处理程序的用途不仅仅局限于防止程序被Windows野蛮地终止,合理利用他们可以让有些功能的实现方式变得更加简单,一般来说,可以在下面这些情况下使用异常处理程序.1.用来处理非致命的错误.2.处理”计划内”的异常.3.处理致命错误.Windows中的用户自定义的异常处理函数以”回调函数”的方式来实现.Windows下的异常处理有两种方式:筛选异常处理和SEH异常处理.1.2.1使用筛选异常处理异常 筛选异常处理程序的方式是由程序指定一个异常处理回调函数.当发生异常的时候,系统将调回这个回调函数,并根据回调函数的返回值决定如何进行下一个操作.在进程范围内,筛

34、选器异常处理回调函数是惟一的. 可以使用SetUnhandledExceptionFilter函数来设置一个筛选异常处理回调函数.这个回调函数不是替换了系统默认的异常处理程序,而是在它面前进行了一些预处理,操作的结果还是会被送到系统默认的异常处理程序中去,函数使用方法如下:Invoke SetUnhandledExceptionFilter,offset _HandlerMov lpPrevHandler, eax筛选器异常处理回调函数的格式如下:_handlerproc pExceptionInfo 回调函数带一个参数,这个参数是个指针,指向一个包含所发生异常详细信息的EXCEPTION_P

35、OINTERS,结构定义如下:EXCEPTION_POINTERS STRUCTpExceptionRecord DWORD ?/指向异常原因,位置等EXCEPTION_RECORDContextRecord DWORD ?/指向记录异常产生时刻运行环境CONTEXTEXCEPTION_POINTERS ENDSEXCEPTION_RECORD 的结构如下:EXCEPTION_RECORD STRUCTExceptionCode DWORD ? ;异常事件码ExceptionFlags DWORD ? ;标志PexceptionRecord DWORD ? ;下一个EXCEPTION_RECO

36、RD结构地址ExceptionAddress DWORD ?NumberParameters DWORD ? ExceptionInformation DWORD EXCEPTION_MAXIMUM_PARAMETERS dup(?)EXCEPTION_RECORD ENDSCONTEXT的结构如下:CONTEXT STRUCTContextFlags DWORD ? iDr0 DWORD ?iDr1 DWORD ? iDr2 DWORD ?iDr3 DWORD ? iDr6 DWORD ?iDr7 DWORD ? FloatSave FLOATING_SAVE_AREA regGs DWOR

37、D ? regFs DWORD ?regEs DWORD ? regDs DWORD ?regEdi DWORD ? regEsi DWORD ?regEbx DWORD ? regEdx DWORD ?regEcx DWORD ? regEax DWORD ?regEbp DWORD ? regEip DWORD ?regCs DWORD ? regFlag DWORD ?regEsp DWORD ? regSs DWORD ?ExtendedRegisterers db MAXIMUM_SUPPTORED_EXTENSION dup(?)CONTEXT ENDS回调函数返回后,Window

38、s执行默认的异常处理程序,这个程序会根据回调函数的返回值决定如何处理下一步动作.回调函数可以有三种返回值:1.EXCEPTION_EXECUTE_HANDLER(定义为1):进程将被终止,在终止之前系统不会显示出错提示对话框;2.EXCEPTION_CONTINUE_SEARCH(定义为0),系统同样将终止程序,但是在终止前首先出错提示对话框.使用这两种返回值的时候,异常处理程序完成的工作一般是退出前的扫尾工作.3.EXCEPTION_CONTINUE_EXCEPTION,系统将CONTEXT设置回去并继续执行,例子程序中就是这样使用的.注意当异常标志中包含了EXCEPITON_NONCONT

39、INUALBE标志时,不应该使用这个返回值,否则引发新异常. 筛选器异常处理程序是最简单的方法,不便于模块的封装,(由于是全局性的).1.2.2 使用SEH处理异常 Windows系统中还提供了另一种在每个线程之间独立的异常处理方法结构化异常处理(Structured Exception Handling,简称SEH ).SEH和筛选器异常处理之间有一些共同点:首先是两者的异常处理程序都是以回调函数的方式提供的,另外,系统都会根据回调函数的返回值选择不同的操作.他们之间也存在许多的不同点:1.两者的回调函数的参数定义和返回值的定义都是不同的.2.SEH使用了与硬件平台相关的数据指针,所以在不同

40、硬件平台中使用SEH的方法会有所不同.引用14章中SEH处理异常的例子 SEH异常处理回调函数的设置由下面三条指令完成: push offset _Handler ;将回调函数的地址推入堆栈 push fs:0 ;将当前使用的EXCEPTION_REGISTRATION结构地址推入堆栈;即TIB中的ExceptionList字段,现在堆栈指针esp指向的地方刚;好是一个新的EXCEPTION_REGISTRATION结构-esp等于原结构地址,也就是PREV字段,而esp+4等于回调函数地址,也就是handler字段 mov fs:0,esp ;TIB中ExceptionList(fs:0)-

41、EXCEPTION_REGISTRATIONWin32为每个线程定义了一个线程信息块(Thread Information Block/TIB).线程信息块的格式被定义为NT_TIB结构:NT_TIB STRUCT ExceptionList dd ? ;SEH链入口 StackBase dd ? ;堆栈基址 StackLimit dd ? ;堆栈大小 SubSystemTib dd ? FiberData dd ? ArbitraryOserPointer dd ? Self dd ? ;本NT_TIB结构自身的线性地址NT_TIB ENDSNT_TIB结构的第一个字段ExceptionLi

42、st指向一个EXCEPTION_REGISTRATION结构,SEH异常处理回调函数的入口地址就是由EXCEPTION_REGISTRATION结构指定的.这个结构定义如下:EXCEPTION_REGISTRATION STRUCTPrev dd ? ; 前一个EXCEPTION_REGISTRATION结构的地址Handle dd ? ;r 异常处理回调函数地址EXCEPTION_REGISTRATION ENDS当异常发生时,系统从TIB中取出ExceptionList字段,然后从ExceptionList字段指定的EXCEPTION_REGSITRATION结构中取出handler字段.

43、并根据其中的地址去调用回调函数.整个过程如图:所以只要构建一个含有回调函数地址的EXCEPTION_REGISTRATION结构,然后修改TIB中的ExceptionList字段,指向这个结构就可以注册一个SEH异常处理回调函数.到哪里找TIB呢?答案是:TIB永远放在 Fs段选择器指定的数据段的0偏移处,所以,fs:0的地方就是TIB结构的ExceptionList字段,这个答案对于Windows9x系统和Windows NT系统都是有效的由于一个进程中的不同线程可以有不同的环境,所以,在不同线程中fs段选择器可以使用不同的值,这种特征使每个线程都可以设置不同的回调函数.当不再需要这个回调函

44、数的时候,只要将fs:0的值恢复为原来的EXCEPTION_REGISTRATION结构地址就可以了.例子程序使用下面的恢复代码:pop fs:0 ;从堆栈中prev字段中弹出原来的fs:0值pop eax ;为了堆栈平衡,没有实际用途.这种用法使构建异常处理程序的数据可以存放在一个子程序私有空间,有利于程序结构的模块化.所有的高级语言在使用SEH时都将数据结构建立在堆栈中.SEH异常处理回调函数和回调函数的返回值SEH异常处理回调函数定义如下:_Handler proc _lpExceptionRecord,_lpSEH,_lpContext,_lpDispatcherContext._lpSEH参数指向的数据就是我们自己定义的EXCEPTION_REGISTRATION结构.Windows下的许多高级语言都是在EXCEPTION_REGISTRATION结构的后面添加自定义的数据:比如,Microsoft SDK的except.inc中是这样定义的:_EXCEPTIONREGISTRATIONRECORD STRUCT prev_structure dd ? ExceptionHandler dd ? ExceptionFilter dd ? ;附加数据 FilterFrame d

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 教育教学 > 成人教育


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号