再探Win32 SEH

6 篇文章 0 订阅

很早之前就看了Matt Pietrek的A Crash Course on the Depths of Win32 Structured Exception Handling这篇巨作,真是前无古人,后有仿者。
今天突发奇想研究了一下VS2005的SEH,又看到些新的东西,下面说明一下。

 

看一下文中引用的EXSUP.INC中的异常注册的定义:

struct _EXCEPTION_REGISTRATION{   
     struct _EXCEPTION_REGISTRATION *prev;   
     void (*handler)(PEXCEPTION_RECORD,   
                     PEXCEPTION_REGISTRATION,   
                     PCONTEXT,   
                     PEXCEPTION_RECORD);   
     struct scopetable_entry *scopetable;   
     int trylevel;   
     int _ebp;   
     PEXCEPTION_POINTERS xpointers;   
};

 

ebp其实应该不是seh中的VC_EXCEPTION_REGISTRATION_RECORD的成员,因为在VC++中无论有否SEH都会用ebp去表示当前函数堆栈起始,参数和变量都是通过ebp访问的。


那这个ebp使用来干什么的呢?
编译器编译出来的函数代码中前两条语句基本上总是
push ebp
mov  esp,ebp
而在返回的时候将ebp直接送给esp,pop先前栈上的ebp的
mov  ebp, esp
pop  ebp

 

不知道大家记得这两条x86指令:

enter 等价于
push ebp
mov  esp,ebp

leave 等价于
mov  ebp, esp
pop  ebp

 

这两条指令就是设计来完成这个工作的,从这可以看出使用ebp作为esp的快照是普遍采用的,这样更说明ebp可能不是VC_EXCEPTION_REGISTRATION_RECORD的成员,可能恰巧在那里了。

 

PS : 可以看下GCC,GCC也是这样使用ebp的。

 

另外一个恰巧就是xpointers,大家知道call指令的作用就是将eip下条指令压入堆栈(当然,CPU在解析完call指令后,eip其实应该已经指向下条指令了,只要将eip压入即可),并将调用地址赋值给eip,这样下条执行就从新函数开始了。
所以这个xpointers也是所说的恰巧在那里。
 
所以实际的注册结构应该为:

struct _EH3_EXCEPTION_REGISTRATION
{
	_EH3_EXCEPTION_REGISTRATION * Next;
	void * ExceptionHandler;
	_SCOPETABLE_ENTRY * ScopeTable;
	DWORD TryLevel;
};

 

但是如果你自己跟踪堆栈就会发现,VC在这个注册结构的上方(堆栈上方)还放置了两个成员。
一个存储了当时的esp,这个esp就是这个函数使用的最高堆栈地址(不包括函数调用),而如果这个函数调用其它函数,则这个地址刚好指向函数调用堆栈的起始。
另外一个成员在构造这个注册结构的时候没有初始化,而且如果这个函数没有发生过异常,这个成员都不会被赋值,只有在发生异常时,这个成员才被写入,而且其值是_EXCEPTION_POINTERS *。

这个如果算上ebp和那个返回地址的话,整个的注册结构应该是这样的:

struct _EH_EXCEPTION_REGISTRATION_RECORD
{
	void * SavedESP;
	_EXCEPTION_POINTERS * ExceptionPointers;
	_EXCEPTION_REGISTRATION_RECORD SubRecord;
	INT_PTR ScopeTable;
	DWORD	TryLevel;
//	DWORD	_ebp;
//	PEXCEPTION_POINTERS xpointers;
};

 

VC的SEH从VC6到VS2003就没什么变化,只是增加了一些判断是否有效的函数,但是到了2005问题就来了。ShowSEHFrames 这个函数在VS2005下会在打印ScopeTable是触发异常。这是因为VS2005将C++EH的版本更新到了第四版。
第四版的EH修改了ScopeTable的数据结构,但是这次升级可不是单纯改个结构。因为VS2005加强了代码的安全性,Crt的内部指针都被加密了。

 

但是经过努力,终于将VS2005的EH结构分析出来,在此贡献给大家:

typedef _EXCEPTION_DISPOSITION (*EXCEPTION_HANDLER)(
	PEXCEPTION_RECORD,
	_EXCEPTION_REGISTRATION_RECORD *,
	PCONTEXT,
	PEXCEPTION_RECORD );

struct _EH_SCOPETABLE_RECORD
{
	DWORD EnclosingLevel;
	long (*FilterFunc)(void);
	union {
		void (*HandlerAddress)(void);
		void (*FinallyFunc)(void);
	} u;
};

struct _EH4_SCOPETABLE_RECORD : _EH_SCOPETABLE_RECORD {};
struct _EH3_SCOPETABLE_RECORD : _EH_SCOPETABLE_RECORD {};

struct _EH3_SCOPETABLE
{
	_EH3_SCOPETABLE_RECORD ScopeRecord[1];
};

struct _EH4_SCOPETABLE
{
	DWORD	GSCookieOffset;
	DWORD	GSCookieXOROffset;
	DWORD	EHCookieOffset;
	DWORD	EHCookieXOROffset;
	_EH4_SCOPETABLE_RECORD ScopeRecord[1];
};

struct _EXCEPTION_REGISTRATION_RECORD
{
	struct _EXCEPTION_REGISTRATION_RECORD *Next;
	EXCEPTION_HANDLER Handler;
};

struct _EH_EXCEPTION_REGISTRATION_RECORD
{
	void * SavedESP;
	_EXCEPTION_POINTERS * ExceptionPointers;
	_EXCEPTION_REGISTRATION_RECORD SubRecord;
	INT_PTR ScopeTable;
	DWORD	TryLevel;
//	DWORD	_ebp;
//	PEXCEPTION_POINTERS xpointers;
};

struct _EH4_EXCEPTION_REGISTRATION_RECORD
{
	void * SavedESP;
	_EXCEPTION_POINTERS * ExceptionPointers;
	_EXCEPTION_REGISTRATION_RECORD SubRecord;
	INT_PTR EncodedScopeTable;
	DWORD	TryLevel;
//	DWORD	_ebp;
//	PEXCEPTION_POINTERS xpointers;
};


还有操作系统的Shell32.dll内部的EH还是版本3。所以,已经不能简单的将操作系统BaseProcessStart中的注册记录 对应的ShowSEHFrames 程序应该修改如下:

//---------------------------------------------------------------------------- // Prototypes //---------------------------------------------------------------------------- // _except_handler3 在VS2005中还存在,不过他已经失去原有的作用了,取而代之的是 // _except_handler4 extern "C" _EXCEPTION_DISPOSITION _except_handler3( PEXCEPTION_RECORD, _EXCEPTION_REGISTRATION_RECORD *, PCONTEXT, PEXCEPTION_RECORD ); // _except_handler4 是VC++运行库函数。我们想使用它的地址,但是我们需要定义它 // 的参数和返回值,因为微软没有头文件提供它的定义。 extern "C" _EXCEPTION_DISPOSITION _except_handler4( PEXCEPTION_RECORD, _EXCEPTION_REGISTRATION_RECORD *, PCONTEXT, PEXCEPTION_RECORD ); //---------------------------------------------------------------------------- // Code //---------------------------------------------------------------------------- // // Display the information in one exception frame, along with its scopetable // void ShowSEHFrame( _EH_EXCEPTION_REGISTRATION_RECORD * pVCExcRec ) { VOID *pScopeTable = NULL; _EH_SCOPETABLE_RECORD *pScopeTableEntry = NULL; if( pVCExcRec->SubRecord.Handler == _except_handler4 ) { // pVCExcRec 是 _EH4_EXCEPTION_REGISTRATION_RECORD* _EH4_SCOPETABLE *pEH4ScopeTable; pEH4ScopeTable = (_EH4_SCOPETABLE *)pVCExcRec->ScopeTable;

// EH4的ScopeTable使用cookie加密过了 ((DWORD_PTR&)pEH4ScopeTable) ^= __security_cookie;

// EH3和EH4的ScopeTable位置不同,但是结构相同 pScopeTableEntry = pEH4ScopeTable->ScopeRecord; pScopeTable = pEH4ScopeTable; } else { _EH3_SCOPETABLE *pEH3ScopeTable; pEH3ScopeTable = (_EH3_SCOPETABLE *)pVCExcRec->ScopeTable; // EH3和EH4的ScopeTable位置不同,但是结构相同

pScopeTableEntry = pEH3ScopeTable->ScopeRecord; pScopeTable = pEH3ScopeTable; }//END_IF printf( "Frame: %p Handler: %p Prev: %p Scopetable: %p/n", pVCExcRec, pVCExcRec->SubRecord.Handler, pVCExcRec->SubRecord.Next, pScopeTable ); for( DWORD dwIndex = 0; dwIndex <= pVCExcRec->TryLevel; ++dwIndex ) { printf( " scopetable[%u] PrevTryLevel: %08X " "filter: %p __except: %p/n", dwIndex, pScopeTableEntry->EnclosingLevel, pScopeTableEntry->FilterFunc, pScopeTableEntry->u.HandlerAddress ); pScopeTableEntry++; }//END_FOR printf( "/n" ); } // // Walk the linked list of frames, displaying each in turn // void WalkSEHFrames( void ) { _EXCEPTION_REGISTRATION_RECORD *pExcRec; // Print out the location of the __except_handler3 function printf( "_except_handler3 is at address: %p/n", _except_handler3 ); printf( "_except_handler4 is at address: %p/n", _except_handler4 ); printf( "/n" ); // Get a pointer to the head of the chain at FS:[0] __asm mov eax, FS:[0]; __asm mov [pExcRec], EAX; // Walk the linked list of frames. 0xFFFFFFFF indicates the end of list while( DWORD_PTR(-1) != (DWORD_PTR)pExcRec ) { _EH_EXCEPTION_REGISTRATION_RECORD *pVCEcxRec = CONTAINING_RECORD( pExcRec, _EH_EXCEPTION_REGISTRATION_RECORD, SubRecord ); ShowSEHFrame( pVCEcxRec ); pExcRec = pExcRec->Next; } }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值