Win32 SEH异常深度探索_5 一个异常帧链表遍历例子

 If you're feeling a bit overwhelmed at this point by things like EXCEPTION_REGISTRATIONs, scopetables, trylevels, filter-expressions, and unwinding, so was I at first. The subject of compiler-level structured exception handling does not lend itself to learning incrementally. Much of it doesn't make sense unless you understand the whole ball of wax. When confronted with a lot of theory, my natural inclination is to write code that applies the concepts I'm learning. If the program works, I know that my understanding is (usually) correct.

前面有一大堆名词: EXCEPTION_REGISTRATIONs ( 异常注册 ), scopetables( 异常范围表 ), trylevels(try 块顺序索引 ), filter-expressions( 过滤表达式 ), unwinding( 回退 ) 。你可能会感到疑惑。而编译器生成的异常处理代码更是如此,你必须了解整个细节才能对此有所感觉。我通过一些可以工作的代码来说明我的理解。

 

Figure 10 is the source code for ShowSEHFrames.EXE. It uses _try/_except blocks to set up a list of several Visual C++ SEH frames. Afterwards, it displays information about each frame, as well as the scopetables that Visual C++ builds for each frame. The program doesn't generate or expect any exceptions. Rather, I included all the _try blocks to force Visual C++ to generate multiple EXCEPTION_ REGISTRATION frames, with multiple scopetable entries per frame.

下面一段代码生成了一系列的 SEH 帧并打印出来。 ( 注:这段代码在 VS2005 下需要做很多修改,因为 VS2005 编译器生成的代码和数据结构有了很多变化 )

 view plaincopy to clipboardprint?
//==================================================  
 // ShowSEHFrames - Matt Pietrek 1997  
 // Microsoft Systems Journal, February 1997  
 // FILE: ShowSEHFrames.CPP  
 // To compile: CL ShowSehFrames.CPP  
 //==================================================  
 #define WIN32_LEAN_AND_MEAN  
 #include <windows.h>  
 #include <stdio.h>  
 #pragma hdrstop  
   
 //----------------------------------------------------------------------------  
 // !!! WARNING !!!  This program only works with Visual C++, as the data  
 // structures being shown are specific to Visual C++.  
 //----------------------------------------------------------------------------  
   
 #ifndef _MSC_VER  
 #error Visual C++ Required (Visual C++ specific information is displayed)  
 #endif  
   
 //----------------------------------------------------------------------------  
 // Structure Definitions  
 //----------------------------------------------------------------------------  
   
 // The basic, OS defined exception frame  
   
 struct EXCEPTION_REGISTRATION  
 {  
     EXCEPTION_REGISTRATION* prev;  
     FARPROC                 handler;  
 };  
   
   
 // Data structure(s) pointed to by Visual C++ extended exception frame  
   
 struct scopetable_entry  
 {  
     DWORD       previousTryLevel;  
     FARPROC     lpfnFilter;  
     FARPROC     lpfnHandler;  
 };  
   
 // The extended exception frame used by Visual C++  
   
 struct VC_EXCEPTION_REGISTRATION : EXCEPTION_REGISTRATION  
 {  
     scopetable_entry *  scopetable;  
     int                 trylevel;  
     int                 _ebp;  
 };  
   
 //----------------------------------------------------------------------------  
 // Prototypes  
 //----------------------------------------------------------------------------  
   
 // __except_handler3 is a Visual C++ RTL function.  We want to refer to  
 // it in order to print it's address.  However, we need to prototype it since  
 // it doesn't appear in any header file.  
   
 extern "C" int _except_handler3(PEXCEPTION_RECORD, EXCEPTION_REGISTRATION *,  
                                 PCONTEXT, PEXCEPTION_RECORD);  
   
   
 //----------------------------------------------------------------------------  
 // Code  
 //----------------------------------------------------------------------------  
   
 //  
 // Display the information in one exception frame, along with its scopetable  
 //  
   
 void ShowSEHFrame( VC_EXCEPTION_REGISTRATION * pVCExcRec )  
 {  
     printf( "Frame: %08X  Handler: %08X  Prev: %08X  Scopetable: %08X/n",  
             pVCExcRec, pVCExcRec->handler, pVCExcRec->prev,  
             pVCExcRec->scopetable );  
   
     scopetable_entry * pScopeTableEntry = pVCExcRec->scopetable;  
   
     for ( unsigned i = 0; i <= pVCExcRec->trylevel; i++ )  
     {  
         printf( "    scopetable[%u] PrevTryLevel: %08X  " 
                 "filter: %08X  __except: %08X/n", i,  
                 pScopeTableEntry->previousTryLevel,  
                 pScopeTableEntry->lpfnFilter,  
                 pScopeTableEntry->lpfnHandler );  
   
         pScopeTableEntry++;  
     }  
   
     printf( "/n" );  
 }    
   
 //  
 // Walk the linked list of frames, displaying each in turn  
 //  
   
 void WalkSEHFrames( void )  
 {  
     VC_EXCEPTION_REGISTRATION * pVCExcRec;  
   
     // Print out the location of the __except_handler3 function  
     printf( "_except_handler3 is at address: %08X/n", _except_handler3 );  
     printf( "/n" );  
   
     // Get a pointer to the head of the chain at FS:[0]  
     __asm   mov eax, FS:[0]  
     __asm   mov [pVCExcRec], EAX  
   
     // Walk the linked list of frames.  0xFFFFFFFF indicates the end of list  
     while (  0xFFFFFFFF != (unsigned)pVCExcRec )  
     {  
         ShowSEHFrame( pVCExcRec );  
         pVCExcRec = (VC_EXCEPTION_REGISTRATION *)(pVCExcRec->prev);  
     }        
 }  
   
 void Function1( void )  
 {  
     // Set up 3 nested _try levels (thereby forcing 3 scopetable entries)  
     _try  
     {  
         _try  
         {  
             _try  
             {  
                 WalkSEHFrames();    // Now show all the exception frames  
             }  
             _except( EXCEPTION_CONTINUE_SEARCH )  
             {  
             }  
         }  
         _except( EXCEPTION_CONTINUE_SEARCH )  
         {  
         }  
     }  
     _except( EXCEPTION_CONTINUE_SEARCH )  
     {  
     }  
 }  
   
 int main()  
 {  
     int i;  
   
     // Use two (non-nested) _try blocks.  This causes two scopetable entries  
     // to be generated for the function.  
   
     _try  
     {  
         i = 0x1234;     // Do nothing in particular  
     }  
     _except( EXCEPTION_CONTINUE_SEARCH )  
     {  
         i = 0x4321;     // Do nothing (in reverse)  
     }  
   
     _try  
     {  
         Function1();    // Call a function that sets up more exception frames  
     }  
     _except( EXCEPTION_EXECUTE_HANDLER )  
     {  
         // Should never get here, since we aren't expecting an exception  
         printf( "Caught Exception in main/n" );  
     }  
   
     return 0;  
 }  
//==================================================
 // ShowSEHFrames - Matt Pietrek 1997
 // Microsoft Systems Journal, February 1997
 // FILE: ShowSEHFrames.CPP
 // To compile: CL ShowSehFrames.CPP
 //==================================================
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <stdio.h>
 #pragma hdrstop
 
 //----------------------------------------------------------------------------
 // !!! WARNING !!!  This program only works with Visual C++, as the data
 // structures being shown are specific to Visual C++.
 //----------------------------------------------------------------------------
 
 #ifndef _MSC_VER
 #error Visual C++ Required (Visual C++ specific information is displayed)
 #endif
 
 //----------------------------------------------------------------------------
 // Structure Definitions
 //----------------------------------------------------------------------------
 
 // The basic, OS defined exception frame
 
 struct EXCEPTION_REGISTRATION
 {
     EXCEPTION_REGISTRATION* prev;
     FARPROC                 handler;
 };
 
 
 // Data structure(s) pointed to by Visual C++ extended exception frame
 
 struct scopetable_entry
 {
     DWORD       previousTryLevel;
     FARPROC     lpfnFilter;
     FARPROC     lpfnHandler;
 };
 
 // The extended exception frame used by Visual C++
 
 struct VC_EXCEPTION_REGISTRATION : EXCEPTION_REGISTRATION
 {
     scopetable_entry *  scopetable;
     int                 trylevel;
     int                 _ebp;
 };
 
 //----------------------------------------------------------------------------
 // Prototypes
 //----------------------------------------------------------------------------
 
 // __except_handler3 is a Visual C++ RTL function.  We want to refer to
 // it in order to print it's address.  However, we need to prototype it since
 // it doesn't appear in any header file.
 
 extern "C" int _except_handler3(PEXCEPTION_RECORD, EXCEPTION_REGISTRATION *,
                                 PCONTEXT, PEXCEPTION_RECORD);
 
 
 //----------------------------------------------------------------------------
 // Code
 //----------------------------------------------------------------------------
 
 //
 // Display the information in one exception frame, along with its scopetable
 //
 
 void ShowSEHFrame( VC_EXCEPTION_REGISTRATION * pVCExcRec )
 {
     printf( "Frame: %08X  Handler: %08X  Prev: %08X  Scopetable: %08X/n",
             pVCExcRec, pVCExcRec->handler, pVCExcRec->prev,
             pVCExcRec->scopetable );
 
     scopetable_entry * pScopeTableEntry = pVCExcRec->scopetable;
 
     for ( unsigned i = 0; i <= pVCExcRec->trylevel; i++ )
     {
         printf( "    scopetable[%u] PrevTryLevel: %08X  "
                 "filter: %08X  __except: %08X/n", i,
                 pScopeTableEntry->previousTryLevel,
                 pScopeTableEntry->lpfnFilter,
                 pScopeTableEntry->lpfnHandler );
 
         pScopeTableEntry++;
     }
 
     printf( "/n" );
 } 
 
 //
 // Walk the linked list of frames, displaying each in turn
 //
 
 void WalkSEHFrames( void )
 {
     VC_EXCEPTION_REGISTRATION * pVCExcRec;
 
     // Print out the location of the __except_handler3 function
     printf( "_except_handler3 is at address: %08X/n", _except_handler3 );
     printf( "/n" );
 
     // Get a pointer to the head of the chain at FS:[0]
     __asm   mov eax, FS:[0]
     __asm   mov [pVCExcRec], EAX
 
     // Walk the linked list of frames.  0xFFFFFFFF indicates the end of list
     while (  0xFFFFFFFF != (unsigned)pVCExcRec )
     {
         ShowSEHFrame( pVCExcRec );
         pVCExcRec = (VC_EXCEPTION_REGISTRATION *)(pVCExcRec->prev);
     }     
 }
 
 void Function1( void )
 {
     // Set up 3 nested _try levels (thereby forcing 3 scopetable entries)
     _try
     {
         _try
         {
             _try
             {
                 WalkSEHFrames();    // Now show all the exception frames
             }
             _except( EXCEPTION_CONTINUE_SEARCH )
             {
             }
         }
         _except( EXCEPTION_CONTINUE_SEARCH )
         {
         }
     }
     _except( EXCEPTION_CONTINUE_SEARCH )
     {
     }
 }
 
 int main()
 {
     int i;
 
     // Use two (non-nested) _try blocks.  This causes two scopetable entries
     // to be generated for the function.
 
     _try
     {
         i = 0x1234;     // Do nothing in particular
     }
     _except( EXCEPTION_CONTINUE_SEARCH )
     {
         i = 0x4321;     // Do nothing (in reverse)
     }
 
     _try
     {
         Function1();    // Call a function that sets up more exception frames
     }
     _except( EXCEPTION_EXECUTE_HANDLER )
     {
         // Should never get here, since we aren't expecting an exception
         printf( "Caught Exception in main/n" );
     }
 
     return 0;
 } 

The important functions in ShowSEHFrames are WalkSEHFrames and ShowSEHFrame. WalkSEHFrames first prints out the address of __except_handler3, the reason for which will be clear in a moment. Next, the function obtains a pointer to the head of the exception list from FS:[0] and walks each node in the list. Each node is of type VC_EXCEPTION_REGISTRATION, which is a structure that I defined to describe a Visual C++ exception handling frame. For each node in the list, WalkSEHFrames passes a pointer to the node to the ShowSEHFrame function.

ShowSEHFrames 首先打印出 __except_handler3 的地址,然后通过 FS:[0] 获取异常链表的头指针,遍历链表并打印信息。

 

ShowSEHFrame starts by printing the address of the exception frame, the handler callback address, the address of the previous exception frame, and a pointer to the scopetable. Next, for each scopetable entry, the code prints out the previous trylevel, the filter-expression address, and the _except block address. How do I know how many entries are in a scopetable? I don't really. Rather, I assume that the current trylevel in the VC_EXCEPTION_REGISTRATION structure is one less than the total number of scopetable entries.

ShowSEHFrame 可以获取 scopetable 并打印里面信息。

不过这段代码获取 scopetable 的地方在 VS2005 下不能工作,因为 VS2005 是这样保存 scopetable 的:

004020E5  push        offset ___rtc_tzz+11Ch (403870h)          <== Scope table

004020FC  mov        eax,dword ptr [___security_cookie (405004h)]

00402101  xor         dword ptr [ebp-8],eax <== Scope table

也就是它会将 scopetable 的地址值与 ___security_cookie 做个异或,所以取得时候也得做些处理。

同样 scopetable 中有四个 int 空间保存了其他信息,从第五个 int 开始才是 scopetable 内的原素值。

 

You may be wondering why there are three exception frames using __except_handler3 as their callback since ShowSEHFrames plainly has only two functions that use SEH. The third frame comes from the Visual C++ runtime library. The code in CRT0.C from the Visual C++ runtime library sources shows that the call to main or WinMain is wrapped in an _try/_except block. The filter-expression code for this _try block is found in the WINXFLTR.C file.

你运行代码后可能会奇怪打印出来的信息怎么多了一个 SEH 。最后一个来自 VC 运行库,再 CRT0.C 中在调用 main/WinMain 时使用了一个 __try/__except 块包含,其相关 filter-expression 代码在 WINXFLTR.C 中。


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/hongjiqin/archive/2009/09/25/4593821.aspx

你可能感兴趣的:(Win32 SEH异常深度探索_5 一个异常帧链表遍历例子)