SEH链和展开操作

SEH链和展开操作


每次我们定义了一个新的SEH异常处理回调函数,EXCEPTION_REGISTRATION结构的prev字段都被要求填写上一个EXCEPTION_REGISTRATION结构的地址,随着应用程序对模块的调用一层层深入下去的时候,那么最后回调函数会形成一个SEH链

 

当程序中有多个线程在运行的时候,每个线程中都会存在各自的SEH链,这些SEH链中指定了多个回调函数,除他们以外,系统中可能还会存在一个全局性的筛选器,再者如果进程被调试,调试器进程也相当于一个异常处理的程序存在.那么当一个异常发生的时候,系统究竟该听谁的呢?

 

在这种情况下,系统按一定的步骤选择一个回调函数并执行他,如果这个回调函数可以执行这个异常,那么其他的回调函数都不会执行,否则系统执行下一个回调函数

(1).系统查看产生异常的进程是否正在被调试,如果正在被调试的话,那么向调试器发送EXCEPTION_DEBUG_EVENT事件

(2).如果进程没有被调试或者调试器不去处理这个异常,那么系统 检查异常所处的线程,并在这个线程环境中查看fs:[0]来确定是否安装有SEH异常处理回调函数,如果有则调用它.

(3).回调函数尝试处理这个异常,如果可以正确处理的话,则修正错误并将返回值设置为ExceptionContinueExecution,这时系统将结束整个查找过程

(4).如果回调函数返回ExceptionContinueSearch,告知系统他无法处理这个异常,那么系统将根据SEH链中的prev字段得到上一个回调函数地址,并重复步骤(3)过程,直到链中某个回调函数返回ExceptionContinueExecution为止,查找结束 

(5).如果到了SEH链的尾部,却没有一个回调函数愿意处理这个异常,那么系统将再次检测进程是否正在被调试,如果被调试的话,则再次通知调试器

(6).如果调试器还是不去处理这个异常或者进程没有被调试 ,那么系统检查有没有安装筛选器回调函数,如果有,则去调用他,筛选器回调函数返回的时候,系统默认的异常处理程序根据这个返回值将做出相应的动作

(7).如果没有安装筛选器回调函数,系统直接 调用默认的异常处理程序终止进程

一个 比较形象的比喻就是:

Windows拿着一份异常处理的活挨个问每个回调函数
"你干不干" ,"不干","你呢","我也不干"... ... 当问到某一个的时候,
他说:"那我来干好了!"那么Windows就不会在去问其他人了,于是相安无事

有时,问完一圈后,谁都不愿意干(当然是干不了)Windows大怒:"谁都不干,看我炒了你们!"于是就把整个进程终止掉,所有的异常处理回调函数全部完蛋啦
(筛选器-全局的-进程的  SEH-线程的)

 完整的异常处理回调函数

[plain]   view plain copy print ?
  1. <span style="font-family:Microsoft YaHei;font-size:13px;">_Handler1   proc    C _lpExceptionRecord,_lpSEH,_lpContext,_lpDispatcherContext  
  2.         ;C调用方式-调用者自己平衡堆栈  
  3.         .if (异常代码 == 0c0000027h) || (异常标志 & EXCEPTION_UNWINDING) || (异常标志 & EXCEPTION_UNWINDING_FOR_EXIT)  
  4.             ;进行资源释放等扫尾工作  
  5.             mov eax,ExceptionContinueSearch  
  6.         .elseif 异常代码 == 可以处理的异常代码  
  7.             ;处理异常,对CONTEXT进行修正  
  8.             ;进行展开操作  
  9.             mov eax,ExceptionContinueExecution  
  10.         .else  
  11.             ;其他无法处理的异常代码  
  12.             mov eax,ExceptionContinueSearch  
  13.         .endif  
  14.         ret  
  15.   
  16. _Handler1   endp</span>  


 

二.展开操作(Unwinding)

如果把第一个SEH处理的函数的返回值改成ExceptionContinueSearch这个时候函数将会进行循环查找

这个时候回调函数应该进行一些扫尾工作,因为其将被卸载

注册 Unwinding操作可以使用RtlUnwinding函数

invoke RtlUnwinding,lpLastStackFrame,lpCodeLabel,lpExceptionRecord,dwRet

(1).lpLastStackFrame:这个参数设置为NULL,那么他将对所有的SEH链进行展开操作,这时所有的回调函数参数中的异常标志在带有EXCEPTION_UNWINDING的同时也带有EXCEPTION_UNWINDING_FOR_EXIT标志位,这种方式称为展开退出

指定为当前回调函数的EXCEPTION_REGISTRATION结构的地址的话,表示对当前回调函数之后的所有其他回调函数进行展开操作,这样RtlUnwinding函数调用的每一个回调函数时,异常标记位都会带有EXCEPTION_UNWINDING标记位

(2).lpCodeLabel参数指明函数将要返回的位置,如果位NULL,那么RtlUnwinding函数将返回到其后面的一条指令,否则函数直接返回到lpCodeLabel指定的位置

(3).lpExceptionRecord指定一个EXCEPTION_RECORD结构,这个结构将在调用的时候传给每一个回调函数

(4).dwRet参数一般不被使用,它可以指明为NULL

 

使用RtlUnwinding函数时要注意的是:这个函数并不像其他API函数一样保存esi,edi和ebx寄存器的值,在函数返回时候这些寄存器的值可能会改变,所以如果程序用到这些寄存器的话,必须手动去保存和恢复他们

 

 

[plain]   view plain copy print ?
  1. <span style="font-family:Microsoft YaHei;font-size:13px;">        .386  
  2.         .model flat,stdcall  
  3.         option casemap:none  
  4. ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
  5. ; Include 文件定义  
  6. ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
  7. include     windows.inc  
  8. include     user32.inc  
  9. includelib  user32.lib  
  10. include     kernel32.inc  
  11. includelib  kernel32.lib  
  12.   
  13. L macro var:VARARG  
  14.     LOCAL @lbl  
  15.     .const  
  16.     @lbl db var,0  
  17.     .code  
  18.     exitm <offset @lbl>  
  19. endm  
  20. ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
  21. ; 数据段  
  22. ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
  23.         .data  
  24. szMsg1      db  '这是外层异常处理程序(将处理异常)',0dh,0ah  
  25.         db  '异常发生位置:%08X,异常代码:%08X,标志:%08X',0  
  26. szMsg2      db  '这是内层异常处理程序(对异常不进行处理)',0dh,0ah  
  27.         db  '异常发生位置:%08X,异常代码:%08X,标志:%08X',0  
  28. szCaption   db  '提示信息',0  
  29. szBeforeUnwind  db  '现在将开始 Unwind,当前的 FS:[0] = %08X',0  
  30. szAfterUnwind   db  'Unwind 返回,当前的 FS:[0] = %08X',0  
  31. szSafe1     db  '回到了外层子程序的安全位置!',0  
  32. szSafe2     db  '回到了内层子程序的安全位置!',0  
  33. ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
  34. ; 代码段  
  35. ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
  36.         .code  
  37. ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
  38. ; 外层错误 Handler,将处理异常  
  39. ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
  40. _Handler1   proc    C _lpExceptionRecord,_lpSEH,_lpContext,_lpDispatcherContext  
  41.         local   @szBuffer[256]:byte  
  42.   
  43.         pushad  
  44.         mov esi,_lpExceptionRecord  
  45.         mov edi,_lpContext  
  46.         assume  esi:ptr EXCEPTION_RECORD,edi:ptr CONTEXT,fs:nothing  
  47.         invoke  wsprintf,addr @szBuffer,addr szMsg1,\  
  48.             [edi].regEip,[esi].ExceptionCode,[esi].ExceptionFlags  
  49.         invoke  MessageBox,NULL,addr @szBuffer,NULL,MB_OK  
  50. ;********************************************************************  
  51. ; 将 EIP 指向安全的位置并恢复堆栈  
  52. ;********************************************************************  
  53.         mov eax,_lpSEH  
  54.         push    [eax + 8]  
  55.         pop [edi].regEip  
  56.         push    _lpSEH  
  57.         pop [edi].regEsp  
  58. ;********************************************************************  
  59. ; 对前面的 Handler 进行 Unwind 操作  
  60. ;********************************************************************  
  61.         invoke  wsprintf,addr @szBuffer,addr szBeforeUnwind,dword ptr fs:[0]  
  62.         invoke  MessageBox,NULL,addr @szBuffer,addr szCaption,MB_OK  
  63.   
  64.         invoke  RtlUnwind,_lpSEH,NULL,NULL,NULL  
  65.   
  66.         invoke  wsprintf,addr @szBuffer,addr szAfterUnwind,dword ptr fs:[0]  
  67.         invoke  MessageBox,NULL,addr @szBuffer,addr szCaption,MB_OK  
  68. ;********************************************************************  
  69.         assume  esi:nothing,edi:nothing  
  70.         popad  
  71.         mov eax,ExceptionContinueExecution  
  72.         ret  
  73.   
  74. _Handler1   endp  
  75. ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
  76. ; 内层错误 Handler,不处理异常  
  77. ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
  78. _Handler2   proc    C _lpExceptionRecord,_lpSEH,_lpContext,_lpDispatcherContext  
  79.         local   @szBuffer[256]:byte  
  80.   
  81.         pushad  
  82.         mov esi,_lpExceptionRecord  
  83.         mov edi,_lpContext  
  84.         assume  esi:ptr EXCEPTION_RECORD,edi:ptr CONTEXT  
  85.         invoke  wsprintf,addr @szBuffer,addr szMsg2,\  
  86.             [edi].regEip,[esi].ExceptionCode,[esi].ExceptionFlags  
  87.         invoke  MessageBox,NULL,addr @szBuffer,NULL,MB_OK  
  88.         assume  esi:nothing,edi:nothing  
  89.         popad  
  90.         mov eax,ExceptionContinueSearch  
  91.         ret  
  92.   
  93. _Handler2   endp  
  94. ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
  95. _Test2      proc  
  96.   
  97.         assume  fs:nothing  
  98.         push    offset _SafePlace  
  99.         push    offset _Handler2  
  100.         push    fs:[0]  
  101.         mov fs:[0],esp  
  102. ;********************************************************************  
  103. ; 会引发异常的指令  
  104. ;********************************************************************  
  105.         pushad  
  106.         xor eax,eax  
  107.         mov dword ptr [eax],0  
  108.         popad       ;这一句将无法被执行  
  109. _SafePlace:  
  110.         invoke  MessageBox,NULL,L("回到了内层子程序的安全位置!"),L("提示信息"),MB_OK  
  111.         pop fs:[0]  
  112.         add esp,8  
  113.         ret  
  114.   
  115. _Test2      endp  
  116. ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
  117. _Test1      proc  
  118.   
  119.         assume  fs:nothing  
  120.         push    offset _SafePlace  
  121.         push    offset _Handler1  
  122.         push    fs:[0]  
  123.         mov fs:[0],esp  
  124.         invoke  _Test2  
  125. _SafePlace:  
  126.         invoke  MessageBox,NULL,L("回到了外层子程序的安全位置!"),L("提示信息"),MB_OK  
  127.         pop fs:[0]  
  128.         add esp,8  
  129.         ret  
  130.   
  131. _Test1      endp  
  132. ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
  133. start:  
  134.         invoke  _Test1  
  135.         invoke  ExitProcess,NULL  
  136. ;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
  137.         end start  
  138. </span>  

你可能感兴趣的:(操作)