Figure 12: RtlUnwind Pseudocode
void _RtlUnwind( PEXCEPTION_REGISTRATION pRegistrationFrame,
PVOID returnAddr, // Not used! (At least on i386)
PEXCEPTION_RECORD pExcptRec,
DWORD _eax_value )
{
DWORD stackUserBase;
DWORD stackUserTop;
PEXCEPTION_RECORD pExcptRec;
EXCEPTION_RECORD exceptRec;
CONTEXT context;
// Get stack boundaries from FS:[4] and FS:[8]
RtlpGetStackLimits( &stackUserBase, &stackUserTop );
if ( 0 == pExcptRec ) // The normal case
{
pExcptRec = &excptRec;
pExcptRec->ExceptionFlags = 0;
pExcptRec->ExceptionCode = STATUS_UNWIND;
pExcptRec->ExceptionRecord = 0;
// Get return address off the stack
pExcptRec->ExceptionAddress = RtlpGetReturnAddress();
pExcptRec->ExceptionInformation[0] = 0;
}
if ( pRegistrationFrame )
pExcptRec->ExceptionFlags |= EXCEPTION_UNWINDING;
else
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(); // Retrieve FS:[0]
// Begin traversing the list of EXCEPTION_REGISTRATION
while ( -1 != pExcptRegHead )
{
EXCEPTION_RECORD excptRec2;
if ( pExcptRegHead == pRegistrationFrame )
{
_NtContinue( &context, 0 );
}
else
{
// If there's an exception frame, but it's lower on the stack
// then the head of the exception list, something's wrong!
if ( pRegistrationFrame && (pRegistrationFrame <= pExcptRegHead) )
{
// Generate an exception to bail out
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)
if ( (stackUserBase <= pExcptRegHead ) // Make sure that
&& (stackUserTop >= pStack ) // pExcptRegHead is in
&& (0 == (pExcptRegHead & 3)) ) // range, and a multiple
{ // of 4 (i.e., sane)
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 // The stack looks goofy! Raise an exception to bail out
{
excptRec2.ExceptionRecord = pExcptRec;
excptRec2.NumberParameters = 0;
excptRec2.ExceptionCode = STATUS_BAD_STACK;
excptRec2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
RtlRaiseException( &excptRec2 );
}
}
// If we get here, we reached the end of the EXCEPTION_REGISTRATION list.
// This shouldn't happen normally.
if ( -1 == pRegistrationFrame )
NtContinue( &context, 0 );
else
NtRaiseException( pExcptRec, &context, 0 );
}
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 = return address of the caller of the caller of this function
pContext->Ebp = EBP of the caller of the caller of this function
pContext->Esp = Context.Ebp + 8
}