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
}