Win32结构化异常处理(SEH)探秘(上)
cRec ); pVCExcRec = (VC_EXCEPTION_REGISTRATION *)(pVCExcRec->prev); } } void Function1( void ) { // 嵌套3层__try块以便强制为scopetable数组产生3个元素 __try { __try { __try { WalkSEHFrames(); // 现在显示所有的异常帧的信息 } __except( EXCEPTION_CONTINUE_SEARCH ) {} } __except( EXCEPTION_CONTINUE_SEARCH ) {} } __except( EXCEPTION_CONTINUE_SEARCH ) {} } int main() { int i; // 使用两个__try块(并不嵌套),这导致为scopetable数组生成两个元素 __try { i = 0x1234; } __except( EXCEPTION_CONTINUE_SEARCH ) { i = 0x4321; } __try { Function1(); // 调用一个设置更多异常帧的函数 } __except( EXCEPTION_EXECUTE_HANDLER ) { // 应该永远不会执行到这里,因为我们并没有打算产生任何异常 printf( "Caught Exception in main\n" ); } return 0; } ShowSEHFrames程序中比较重要的函数是WalkSEHFrames和ShowSEHFrame。WalkSEHFrames函数首选打印出 __except_handler3的地址,打印它的原因很快就清楚了。接着,它从FS:[0]处获取异常链表的头指针,然后遍历该链表。此链表中每个结点都是一个VC_EXCEPTION_REGISTRATION类型的结构,它是我自己定义的,用于描述Visual C++的异常处理帧。对于这个链表中的每个结点,WalkSEHFrames都把指向这个结点的指针传递给ShowSEHFrame函数。 ShowSEHFrame函数一开始就打印出异常处理帧的地址、异常处理回调函数的地址、前一个异常处理帧的地址以及scopetable的地址。接着,对于每个 scopetable数组中的元素,它都打印出其priviousTryLevel、过滤器表达式的地址以及相应的__except块的地址。我是如何知道scopetable数组中有多少个元素的呢?其实我并不知道。但是我假定VC_EXCEPTION_REGISTRATION结构中的当前trylevel域的值比scopetable数组中的元素总数少1。 图十一是 ShowSEHFrames 的运行结果。首先检查以“Frame:”开头的每一行,你会发现它们显示的异常处理帧在堆栈上的地址呈递增趋势,并且在前三个帧中,它们的异常处理程序的地址是一样的(都是004012A8)。再看输出的开始部分,你会发现这个004012A8不是别的,它正是 Visual C++运行时库函数__except_handler3的地址。这证明了我前面所说的单个回调函数处理所有异常这一点。 图十一 ShowSEHFrames运行结果 你可能想知道为什么明明 ShowSEHFrames 程序只有两个函数使用SEH,但是却有三个异常处理帧使用__except_handler3作为它们的异常回调函数。实际上第三个帧来自 Visual C++ 运行时库。Visual C++ 运行时库源代码中的 CRT0.C 文件清楚地表明了对 main 或 WinMain 的调用也被一个__try/__except 块封装着。这个__try 块的过滤器表达式代码可以在 WINXFLTR.C文 件中找到。 回到 ShowSEHFrames 程序,注意到最后一个帧的异常处理程序的地址是 77F3AB6C,这与其它三个不同。仔细观察一下,你会发现这个地址在 KERNEL32.DLL 中。这个特别的帧就是由 KERNEL32.DLL 中的 BaseProcessStart 函数安装的,这在前面我已经说过。 |
凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢! |