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