⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 结构化异常处理.htm

📁 结构化异常处理是一种操作系统提供的机制,用来优化程序的结构,提供更加健壮的程序执行环境.
💻 HTM
📖 第 1 页 / 共 2 页
字号:
<br>   };
<br>
<br>  明白了handler的任务以后,看它实际的代码吧.
<br>  int __except_handler3(_EXCEPTION_RECORD * pExceptionRecord,EXCEPTION_REGISTRATION * pRegistrationFrame,_CONTEXT     
<br>              *pContextRecord,void * pDispatcherContext )
<br>  {
<br>    LONG filterFuncRet
<br>    LONG trylevel
<br>    EXCEPTION_POINTERS exceptPtrs
<br>    PSCOPETABLE pScopeTable
<br>
<br>    CLD // Clear the direction flag (make no assumptions!),这个是c语言编译器默认的操作方式
<br>
<br>    // if neither the EXCEPTION_UNWINDING nor EXCEPTION_EXIT_UNWIND bit
<br>    // is set... This is true the first time through the handler (the
<br>    // non-unwinding case)
<br>
<br>    // 检查是不是要进行unwind
<br>    if ( ! (pExceptionRecord->ExceptionFlags & (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND)) )
<br>    {
<br>      // 设置[ebp-14]的值,记得上面说的[ebp-14]放的是什么么,这里的ExceptionRecord是在handler的堆栈里面的
<br>      // 所以它的生存期是有限的,handler函数返回了,这个就不存在了,[ebp-14]这个指针也就指向了未知区域了,所以msdn里面限制
<br>      // 了GetExceptionXXX函数的调用地点,明白了么?
<br>      // Build the EXCEPTION_POINTERS structure on the stack
<br>      exceptPtrs.ExceptionRecord = pExceptionRecord;
<br>      exceptPtrs.ContextRecord = pContextRecord;
<br>
<br>      // Put the pointer to the EXCEPTION_POINTERS 4 bytes below the
<br>      // establisher frame. See ASM code for GetExceptionInformation
<br>      // 想想看,-4指向了什么地方?
<br>      *(PDWORD)((PBYTE)pRegistrationFrame - 4) = &exceptPtrs;
<br>
<br>      // Get initial &#34;trylevel&#34; value,看看布局再看看上面那个结构的定义
<br>      trylevel = pRegistrationFrame->trylevel 
<br>
<br>      // Get a pointer to the scopetable array
<br>      scopeTable = pRegistrationFrame->scopetable;
<br>
<br>search_for_handler: 
<br>
<br>      if ( pRegistrationFrame->trylevel != TRYLEVEL_NONE/*-1*/ )
<br>      {
<br>        // 如果是空,就表示这个是一个finally语句,finally是用来作unwind的
<br>        if ( pRegistrationFrame->scopetable[trylevel].lpfnFilter )
<br>        {
<br>           PUSH EBP // Save this frame EBP
<br>
<br>           // !!!Very Important!!! Switch to original EBP. This is
<br>           // what allows all locals in the frame to have the same
<br>           // value as before the exception occurred.
<br>           // ebp是一个函数的frame pointer,对于一个函数的执行非常的重要,这里既然是要执行filter(except后面小括号
<br>           // 里面的语句),就必须要恢复ebp的值,ebp是怎么恢复的呢?,上面的代码里面可以看到是一个mov ebp,esp,这个esp又
<br>           // 是什么呢?对比下上面的内存布局,好好体会这句话的含义,看清楚前面有个取地址符.
<br>           EBP = &pRegistrationFrame->_ebp 
<br>
<br>           // Call the filter function调用except小括号里面的语句,检查这个返回值
<br>           filterFuncRet = scopetable[trylevel].lpfnFilter();
<br>
<br>           POP EBP // Restore handler frame EBP
<br>
<br>           if ( filterFuncRet != EXCEPTION_CONTINUE_SEARCH )
<br>           {
<br>             if ( filterFuncRet < 0 ) // EXCEPTION_CONTINUE_EXECUTION
<br>               return ExceptionContinueExecution; // 依靠操作系统完成continue execution 
<br>
<br>             // If we get here, EXCEPTION_EXECUTE_HANDLER was specified
<br>             scopetable == pRegistrationFrame->scopetable
<br>
<br>             // Does the actual OS cleanup of registration frames
<br>             // Causes this function to recurse
<br>             // 进行unwind,操作系统会变量当前registration record以前的handler一一调用他们,然后断开这些record链表
<br>             __global_unwind2( pRegistrationFrame );
<br>
<br>             // Once we get here, everything is all cleaned up, except
<br>             // for the last frame, where we&#39;ll continue execution
<br>             EBP = &pRegistrationFrame->_ebp
<br>
<br>             // 操作系统帮我们完成前面的unwind,当前record的unwind要自己来完成
<br>             __local_unwind2( pRegistrationFrame, trylevel );
<br>
<br>             // 这里是setjmp/longjmp支持代码
<br>             // NLG == &#34;non-local-goto&#34; (setjmp/longjmp stuff)
<br>             __NLG_Notify( 1 ); // EAX == scopetable->lpfnHandler
<br>
<br>             // Set the current trylevel to whatever SCOPETABLE entry
<br>             // was being used when a handler was found
<br>             // 修改当前的trylevel为prevtrylevel,很显然,从当前的try block出来了自然就到了上一个try block 
<br>             pRegistrationFrame->trylevel = scopetable->previousTryLevel;
<br>
<br>             // Call the _except {} block. Never returns.
<br>             // goto except语句,这里并不返回,因为编译器并没有在except语句最后生成一个ret代码
<br>             pRegistrationFrame->scopetable[trylevel].lpfnHandler();
<br>           }
<br>         }
<br>
<br>         scopeTable = pRegistrationFrame->scopetable;
<br>         trylevel = scopeTable->previousTryLevel
<br>
<br>         goto search_for_handler;
<br>       }
<br>       else // trylevel == TRYLEVEL_NONE
<br>       {
<br>          retvalue == DISPOSITION_CONTINUE_SEARCH;
<br>       } 
<br>     }
<br>   }
<br>   else // EXCEPTION_UNWINDING or EXCEPTION_EXIT_UNWIND flags are set
<br>   {
<br>     // 进行unwind(由__global_unwind2函数触发)
<br>     PUSH EBP // Save EBP
<br>     EBP = &pRegistrationFrame->_ebp // Set EBP for __local_unwind2
<br>
<br>     __local_unwind2( pRegistrationFrame, TRYLEVEL_NONE )
<br>
<br>     POP EBP // Restore EBP
<br>
<br>     retvalue == DISPOSITION_CONTINUE_SEARCH;
<br>   }
<br> }
<br>
<br>  这里也不能不提到编译器为你生成代码的样子
<br>
<br>  __try 
<br>  {
<br>    i = 0;
<br>  } 
<br>  __except(EXCEPTION_EXECUTE_HANDLER)
<br>  {
<br>    i = 1;
<br>  } 
<br>
<br>  这里假设i放在 ebp-20的地方,同时省略fs:[0]的设置
<br>
<br>__try: 
<br>  mov [ebp-4],0 ; trylevel = 0
<br>  mov [ebp-18h],esp ; 保存esp 
<br>  mov [ebp-20h],0 ; 执行i = 0
<br>  jmp __finish ; 跳出try语句
<br>__except_filter: 
<br>  mov eax,EXCEPTION_EXECUTE_HANDLER ; 返回
<br>  ret
<br>__except_body:
<br>  mov esp,[ebp-18h] ; 首先恢复esp值,也就是回复运行栈
<br>  mov [ebp-20h],1 ; 执行 i = 1;
<br>__finish: 
<br>  mov [ebp-4],0ffffffffh ; trylevel = -1
<br>
<br>  到这里差不多我要讲的就结束了,更为详细的可以参考我多次提到的msj里面的那个文章.
<br>  http://www.microsoft.com/msj/0197/exception/exception.aspx
<br>  如果你对vc生成的代码更加的感兴趣,你可以使用ida+softice动态静态跟踪看看.
<br>
<br></td>
      </tr>
    </table>
		
		
		
    </td>
    <td bgcolor="#CCCCCC"><img src="image/001.gif" width="1" height="1"></td>
  </tr>
</table>

</body>
</html>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -