2017년 11월 13일 월요일

[Anti Memory Dumping Techniques]KiUserExceptionDispatcher 함수 I

1) 예외 체인 순회

KiUserExceptionDispatcher API가 호출되면, 인자로 받은 ExceptionRecord와 ContextRecord 값을 RtlDispatchException API의 인자로 넣게 된다. 

RtldispatchException API의 값이 True면 정상 실행하게 되고, False를 반환하게 되면 예외(Exception)이 발생하면서 예외 처리하게 된다.

TRUE = 현재 스레드 문맥을 담고 있는 ContextRecord를 매개변수로 전달하여 NtContinue 커널 함수 호출
FALSE = NtRaiseException함수를 통해 예외 발생

코드 구조는 다음과 같다.

BOOLEAN NTAPI RtlDispatchException
(
    IN PEXCEPTION_RECORD     ExceptionRecord,
    IN PCONTEXT              ContextRecord
)

RtlDispatchException 함수

SEH프레임이 등장하는 API로 해당 함수의 코드는 다음과 같다.

#define EXCEPTION_CHAIN_END (DWORD)-1

BOOLEAN RtlDispatchException(PEXCEPTION_RECORD ExceptionRecord, PCONTEXT ContextRecord)
{
    DISPATCHER_CONTEXT*    dc;
    EXCEPTION_DISPOSITION    Disposition;
    PEXCEPTION_REGISTRATION_RECORD pCurFrame, pNestedFrame = NULL
    ULOG HighAddress, HightLimit, LowLimit;
    EXCEPTION_RECORD    er;
    
    RtlpGetStackLimits(&LowLimit, &HighLimit);    //현재 스래드의 스택 상/하한 값을 획득
    
    pCurFrame = RtlpGetRegistrationHead();    //SEH 프레임의 포인터를 획득, FS[:0]을 반환하는 함수

    while (pCurFrame != EXCEPTION_CHAIN_END)    //SEH 프레임을 0xFFFFFFFF가 아닐 동안 돔
    //EXCEPTION_CHAIN_END = 0xFFFFFFFF
    {
        HighAddress = (ULONG)pCurFrame + sizeof(EXCEPTION_REGISTRATION_RECORD);
        if (((ULONG)pCurFrame < LowLomit || HighAddress > HighLimit) || ((ULONG)pCurFrame & 0x3) != 0)    //현재 프레임 영역이 스택의 상/하한 내에 있는지와 프레임의 주소가 4바이트 경계로 정렬되어 있는지 확인
        {
            ExceptionRecord->ExceptionFalgs |=  EXCEPTION_STACK_INVALID;
            return FALSE;
            // 만족 안될 경우 ExceptionFlags에 EXCEPTION_STACK_INVALID 플래그를 설정 후 false를 반환한다.(false는 예외를 발생하게 된다.)
        }
        Disposition = RtlpExecuteHandlerForException(ExceptionRecord, pCurFrame, ContextRecord, &dc, (PEXCEPTION_ROUTINE)pCurFrame->Handler);
        //RtlpExecuteHandlerForException 함수 호출, SEH 프레임의 Handler 필드에 설정된 예외 헨들러를 호출,pCurFrame에 현재 프레임의 포인터를 전달, 마지막 매게 변수는 현재 프레임의 예외 핸들러를 별도로 전달
        if (pNestedFrame == pCurFrame)    //예외 중첩 발생 처리
        {
            ExceptionRecord->ExceptionFlags &= (~EXCEPTION_NESTED_CALL);
            pNestedFrame = NULL;
        }
        switch (Disposion)    //RtlExecuteHandlerForException 반환값인 Disposition변수를 확인
        {
            case ExceptionContinueExecution:    //예외가 발생한 지점에서 부터 다시 실행
            if ((ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE) != 0)
            {
                er.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION;
                er.ExceptionFlags = EXCEPTION_NONCONTINUABLE;    // 실행을 계속 할 수 없음을 의미, 이경우 STATUS_NONCONTINUABLE_EXCEPTION 예외를 발생
                er.ExceptionRecord = ExceptionRecord;
                er.NumberParameters = 0;
                RtlRaiseException(&er);
            }
            else
            {
                return TRUE;
                //이 외에는 TRUE를 반환하여 계속 실행하도록 함.(NtContunue 함수 호출로 계속 실행
            }
            case ExceptionContinueSearch:    //현재 RegistrationPointer가 가리키는 프레임이 예외를 처리하지 않을 경우(예외 프레임을 계속 검색하라는 의미)
            break;

            case ExceptionNestedException:    //예외 중첩 발생 처리
                ExceptionRecord->ExceptionFlags |= EXCEPTION_NESTED_CALL;
                if (dc.RegistrationFrame > pNestedFrame)
                {
                    pNestedFrame = dc.RegistrationFrame;
                }
            break;

            default:    //이외의 경우, ExceptionFlags에 EXCEPTION_NONCONTINUABLE 플래그를 설정하고 STATUS_INVALID_DISPOSITION 예외를 발생
                er.ExceptionCode = STATUS_INVALID_DISPOSITION;
                er.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
                er.ExceptionRecord = ExceptionRecord;
                er.NumberParameters = 0;
            break;
        }
        pCurFrame = pCurFrame->Next;    //다음 예외 프레임 체크
    }
    return FALSE;    //전체르 봤는데 처리 못하면 false 반환
}


다음은 RtlpExecuteHandlerForException API(SEH 프레임을 읽어오는 API)에 대해서 정리한다!





댓글 없음:

댓글 쓰기