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)에 대해서 정리한다!
댓글 없음:
댓글 쓰기