Win32构造化非常处理(SEH)探秘(下)[VC/C++编程]
本文“Win32构造化非常处理(SEH)探秘(下)[VC/C++编程]”是由七道奇为您精心收集,来源于网络转载,文章版权归文章作者所有,本站不对其观点以及内容做任何评价,请读者自行判断,以下是其具体内容:
展开
在发掘展开(Unwinding)的实现代码之前让我们先来搞清楚它的意思.我在前面已经讲过全部大概的非常处理程序是若何被组织在一个由线程信息块的第一个DWORD(FS:[0])所指向的链表中的.由于针对某个特定非常的处理程序大概不在这个链表的开首,因此就需求从链表中顺次移除实际处理非常的那个非常处理程序之前的全部非常处理程序.
正如你在Visual C++的__except_handler3函数中看到的那样,展开是由__global_unwind2这个运行时库(RTL)函数来完成的.这个函数只是对RtlUnwind这个未公开的API举行了非常简单的封装.(目前这个API已经被公开了,但给出的信息极端简单,具体信息可以参考最新的Platform SDK文档.)
__global_unwind2(void * pRegistFrame)
{
_RtlUnwind( pRegistFrame, &__ret_label, 0, 0 );
__ret_label:
}
固然从技术上讲RtlUnwind是一个KERNEL32函数,但它只是转发到了NTDLL.DLL中的同名函数上.下面是我为此函数写的伪代码.
RtlUnwind 函数的伪代码:
void _RtlUnwind( PEXCEPTION_REGISTRATION pRegistrationFrame,
PVOID returnAddr, // 并未利用!(至少是在i386机械上)
PEXCEPTION_RECORD pExcptRec,
DWORD _eax_value)
{
DWORD stackUserBase;
DWORD stackUserTop;
PEXCEPTION_RECORD pExcptRec;
EXCEPTION_RECORD exceptRec;
CONTEXT context;
// 从FS:[4]和FS:[8]处获得仓库的边界
RtlpGetStackLimits( &stackUserBase, &stackUserTop );
if ( 0 == pExcptRec ) // 正常情形
{
pExcptRec = &excptRec;
pExcptRec->ExceptionFlags = 0;
pExcptRec->ExceptionCode = STATUS_UNWIND;
pExcptRec->ExceptionRecord = 0;
pExcptRec->ExceptionAddress = [ebp+4]; // RtlpGetReturnAddress()—获得返回地址
pExcptRec->ExceptionInformation[0] = 0;
}
if ( pRegistrationFrame )
pExcptRec->ExceptionFlags |= EXCEPTION_UNWINDING;
else // 这两个标志合起来被定义为EXCEPTION_UNWIND_CONTEXT
pExcptRec->ExceptionFlags|=(EXCEPTION_UNWINDING|EXCEPTION_EXIT_UNWIND);
context.ContextFlags =( CONTEXT_i486 | CONTEXT_CONTROL |
CONTEXT_INTEGER | CONTEXT_SEGMENTS);
RtlpCaptureContext( &context );
context.Esp += 0x10;
context.Eax = _eax_value;
PEXCEPTION_REGISTRATION pExcptRegHead;
pExcptRegHead = RtlpGetRegistrationHead(); // 返回FS:[0]的值
// 开始遍历EXCEPTION_REGISTRATION构造链表
while ( -1 != pExcptRegHead )
{
EXCEPTION_RECORD excptRec2;
if ( pExcptRegHead == pRegistrationFrame )
{
NtContinue( &context, 0 );
}
else
{
// 假如存在某个非常帧在仓库上的位置比非常链表的头部还低
// 阐明一定呈现了错误
if ( pRegistrationFrame && (pRegistrationFrame <= pExcptRegHead) )
{
// 生成一个非常
excptRec2.ExceptionRecord = pExcptRec;
excptRec2.NumberParameters = 0;
excptRec2.ExceptionCode = STATUS_INVALID_UNWIND_TARGET;
excptRec2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
RtlRaiseException( &exceptRec2 );
}
}
PVOID pStack = pExcptRegHead + 8; // 8 = sizeof(EXCEPTION_REGISTRATION)
// 确保pExcptRegHead在仓库范围内,并且是4的倍数
if ( (stackUserBase <= pExcptRegHead )
&& (stackUserTop >= pStack )
&& (0 == (pExcptRegHead & 3)) )
{
DWORD pNewRegistHead;
DWORD retValue;
retValue = RtlpExecutehandlerForUnwind(pExcptRec, pExcptRegHead, &context,
&pNewRegistHead, pExceptRegHead->handler );
if ( retValue != DISPOSITION_CONTINUE_SEARCH )
{
if ( retValue != DISPOSITION_COLLIDED_UNWIND )
{
excptRec2.ExceptionRecord = pExcptRec;
excptRec2.NumberParameters = 0;
excptRec2.ExceptionCode = STATUS_INVALID_DISPOSITION;
excptRec2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
RtlRaiseException( &excptRec2 );
}
else
pExcptRegHead = pNewRegistHead;
}
PEXCEPTION_REGISTRATION pCurrExcptReg = pExcptRegHead;
pExcptRegHead = pExcptRegHead->prev;
RtlpUnlinkHandler( pCurrExcptReg );
}
else // 仓库已经被破坏!生成一个非常
{
excptRec2.ExceptionRecord = pExcptRec;
excptRec2.NumberParameters = 0;
excptRec2.ExceptionCode = STATUS_BAD_STACK;
excptRec2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
RtlRaiseException( &excptRec2 );
}
}
// 假如履行到这里,阐明已经到了EXCEPTION_REGISTRATION
// 构造链表的末尾,正常情形下不该该发生这种情形.
//(因为正常情形下非常应当被处理,这样就不会到链表末尾)
if ( -1 == pRegistrationFrame )
NtContinue( &context, 0 );
else
NtRaiseException( pExcptRec, &context, 0 );
}
RtlUnwind函数的伪代码到这里就完毕了,以下是它调用的几个函数的伪代码:
PEXCEPTION_REGISTRATION RtlpGetRegistrationHead( void )
{
return FS:[0];
}
RtlpUnlinkHandler( PEXCEPTION_REGISTRATION pRegistrationFrame )
{
FS:[0] = pRegistrationFrame->prev;
}
void RtlpCaptureContext( CONTEXT * pContext )
{
pContext->Eax = 0;
pContext->Ecx = 0;
pContext->Edx = 0;
pContext->Ebx = 0;
pContext->Esi = 0;
pContext->Edi = 0;
pContext->SegCs = CS;
pContext->SegDs = DS;
pContext->SegEs = ES;
pContext->SegFs = FS;
pContext->SegGs = GS;
pContext->SegSs = SS;
pContext->EFlags = flags; // 它对应的汇编代码为__asm{ PUSHFD / pop [xxxxxxxx] }
pContext->Eip = 此函数的调用者的调用者的返回地址 // 读者看一下这个函数的
pContext->Ebp = 此函数的调用者的调用者的EBP // 汇编代码就会清楚这一点
pContext->Esp = pContext->Ebp + 8;
}
固然 RtlUnwind 函数的规模看起来很大,但是假如你按一定办法把它脱离,其实并不难理解.它首先从FS:[4]和FS:[8]处获得当前线程仓库的边界.它们关于背面要举行的合理性查抄非常重要,以确保全部将要被展开的非常帧都在仓库范围内.
RtlUnwind 接着在仓库上成立了一个空的EXCEPTION_RECORD构造并把STATUS_UNWIND赋给它的ExceptionCode域,同时把 EXCEPTION_UNWINDING标志赋给它的 ExceptionFlags 域.指向这个构造的指针作为此中一个参数被传送给每个非常回调函数.然后,这个函数调用RtlCaptureContext函数来成立一个空的CONTEXT构造,这个构造也变成了在展开阶段调用每个非常回调函数时传送给它们的一个参数.
RtlUnwind函数的别的部份遍历EXCEPTION_REGISTRATION构造链表.关于此中的每个帧,它都调用 RtlpExecuteHandlerForUnwind 函数,背面我会讲到这个函数.恰是这个函数带 EXCEPTION_UNWINDING 标志调用了非常处理回调函数.每次回调之后,它调用RtlpUnlinkHandler 移除呼应的非常帧.
RtlUnwind 函数的第一个参数是一个帧的地址,当它遍历到这个帧时就终止展开非常帧.上面所说的这些代码之间还有一些安全性查抄代码,它们用来确保不出问题.假如呈现任何问题,RtlUnwind 就引发一个非常,指导出了什么问题,并且这个非常带有EXCEPTION_NONCONTINUABLE 标志.当一个进程被设置了这个标志时,它就不答应再运行,必须终止.
以上是“Win32构造化非常处理(SEH)探秘(下)[VC/C++编程]”的内容,如果你对以上该文章内容感兴趣,你可以看看七道奇为您推荐以下文章:
本文地址: | 与您的QQ/BBS好友分享! |