📄 结构化异常处理.htm
字号:
<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 "trylevel" 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'll continue execution
<br> EBP = &pRegistrationFrame->_ebp
<br>
<br> // 操作系统帮我们完成前面的unwind,当前record的unwind要自己来完成
<br> __local_unwind2( pRegistrationFrame, trylevel );
<br>
<br> // 这里是setjmp/longjmp支持代码
<br> // NLG == "non-local-goto" (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 + -