展开
在挖掘展开(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的
|