Created
March 26, 2014 15:26
-
-
Save TheCjw/9785970 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* exception handling routines (xp 32-bit, partial/incomplete) | |
* | |
* ntdll 5.1.2600.5755 | |
* v2 (updated jan 2011) | |
* | |
* - hawkes <[email protected]> | |
* | |
* useful link: http://www.eeye.com/html/resources/newsletters/vice/VI20060830.html | |
* | |
*/ | |
#define DISPOSITION_DISMISS 0 | |
#define DISPOSITION_CONTINUE_SEARCH 1 | |
#define DISPOSITION_NESTED_EXCEPTION 2 | |
#define DISPOSITION_COLLIDED_UNWIND 3 | |
#define EH_NONCONTINUABLE 0x01 | |
#define EH_UNWINDING 0x02 | |
#define EH_EXIT_UNWIND 0x04 | |
#define EH_STACK_INVALID 0x08 | |
#define EH_NESTED_CALL 0x10 | |
#define STATUS_NONCONTINUABLE_EXCEPTION 0xC0000025 | |
#define STATUS_INVALID_DISPOSITION 0xC0000026 | |
#define EXCEPTION_CONTINUE_EXECUTION -1 | |
#define PAGE_EXEC_MASK (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY) | |
typedef struct _EXCEPTION_RECORD { | |
DWORD ExceptionCode; | |
DWORD ExceptionFlags; | |
struct _EXCEPTION_RECORD *ExceptionRecord; // var_CW | |
PVOID ExceptionAddress; | |
DWORD NumberParameters; | |
ULONG_PTR ExceptionInformation[15]; | |
} EXCEPTION_RECORD, *PEXCEPTION_RECORD; | |
typedef struct _EXCEPTION_REGISTRATION | |
{ | |
struct _EXCEPTION_REGISTRATION* prev; | |
PEXCEPTION_HANDLER handler; | |
} EXCEPTION_REGISTRATION, *PEXCEPTION_REGISTRATION; | |
typedef struct _VECTORED_EXCEPTION_NODE { | |
LIST_ENTRY ListEntry; | |
PVECTORED_EXCEPTION_HANDLER handler; | |
} VECTORED_EXCEPTION_NODE, *PVECTORED_EXCEPTION_NODE; | |
typedef enum _EXCEPTION_DISPOSITION | |
{ | |
ExceptionContinueExecution, | |
ExceptionContinueSearch, | |
ExceptionNestedException, | |
ExceptionCollidedUnwind | |
} EXCEPTION_DISPOSITION; | |
/* 7C97E3FA */ | |
UCHAR LogExceptions = 0; | |
/* 7C97E3C0 */ | |
LIST_ENTRY RtlpCalloutEntryList; | |
/* 7C9805E0 */ | |
RTL_CRITICAL_SECTION RtlpCalloutEntryLock; | |
RTL_CRITICAL_SECTION LdrpLoaderLock; | |
/* 7C90E47C */ | |
VOID KiUserExceptionDispatcher(__in PCONTEXT ContextRecord, __in PEXCEPTION_RECORD ExceptionRecord) { | |
NTSTATUS Status; | |
if (RtlDispatchException(ContextRecord, ExceptionRecord)) { | |
/* 7C90E48E modify the execution context of the current thread to whatever the chosen exception handler gives us */ | |
Status = ZwContinue(ContextRecord, 0); | |
} | |
else { | |
/* 7C90E49A no exception handler found so re-raise the exception for a debugger or the NT exception handler */ | |
Status = ZwRaiseException(ExceptionRecord, ContextRecord, 0); | |
} | |
/* 7C90E4A5 build an exception record with 20 bytes on the stack, second chance exception? */ | |
PEXCEPTION_RECORD exception = (PEXCEPTION_RECORD) alloca(0x14); | |
exception->ExceptionCode = Status; | |
exception->ExceptionFlags = 1; | |
exception->ExceptionRecord = ContextRecord; | |
exception->NumberParameters = 1; | |
return RtlRaiseException(exception); | |
} | |
/* 7C92A970 returns true if exception handler was found and used */ | |
BOOLEAN RtlDispatchException(PCONTEXT ContextRecord, PEXCEPTION_RECORD ExceptionRecord) { | |
BOOLEAN ret = 0; | |
LPVOID StackBase, StackLimit; | |
PEXCEPTION_REGISTRATION head; | |
DWORD kProcess_Flags; | |
DWORD dispatch, highest; | |
EXCEPTION_RECORD exRec; | |
if (RtlCallVectoredExceptionHandlers(ExceptionRecord, ContextRecord)) { | |
/* 7C95010A */ | |
ret = 1; | |
} | |
else { | |
/* 7C92A991 */ | |
RtlpGetStackLimits(&StackLimit, &StackBase); | |
/* 7C92A99F */ | |
head = RtlpGetRegistrationHead(); | |
highest = 0; | |
while (head != (PEXCEPTION_REGISTRATION) -1) { | |
/* 7C92A9B4 */ | |
if (head < StackLimit || head + sizeof(EXCEPTION_REGISTRATION) > StackBase || head & 3) { | |
/* 7C92A336 */ | |
ExceptionRecord->ExceptionFlags |= EH_STACK_INVALID; | |
goto exit; | |
} | |
if (head->handler >= StackLimit && head->handler < StackBase) { | |
/* 7C92A336 */ | |
ExceptionRecord->ExceptionFlags |= EH_STACK_INVALID; | |
goto exit; | |
} | |
/* 7C92A9E3 */ | |
if (!RtlIsValidHandler(head->handler)) { | |
/* 7C92A336 */ | |
ExceptionRecord->ExceptionFlags |= EH_STACK_INVALID; | |
goto exit; | |
} | |
else if (LogExceptions) { | |
/* 7C950113 */ | |
RtlpLogExceptionHandler(ContextRecord, ExceptionRecord, 0, head, 0x10); | |
} | |
/* 7C92A9FE */ | |
hret = RtlpExecuteHandlerForException(ContextRecord, head, ExceptionRecord, &dispatch, head->handler); | |
if (LogExceptions) { | |
/* 7C950129 there is a second parameter to this function that I don't understand yet */ | |
RtlpLogLastExceptionDisposition(highest, hret); | |
} | |
/* 7C92AA1E */ | |
if (head == NULL) { | |
ExceptionRecord->ExceptionFlags &= ~EH_NESTED_CALL; | |
} | |
/* 7C92AA27 */ | |
if (hret == DISPOSITION_DISMISS) { | |
if (ExceptionRecord->ExceptionFlags & EH_NONCONTINUABLE) { | |
/* 7C950181 */ | |
exRec.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION; | |
exRec.ExceptionFlags = EH_NONCONTINUABLE; | |
exRec.ExceptionRecord = ExceptionRecord; | |
exRec.NumberParameters = 0; | |
RtlRaiseException(&exRec); | |
/* 7C92A31C a little fudging with this block */ | |
if (ExceptionRecord->ExceptionFlags & EH_STACK_INVALID) { | |
goto exit; | |
} | |
} | |
else { | |
/* 77EDBD64 */ | |
ret = 1; | |
break; | |
} | |
} | |
else if (hret == DISPOSITION_CONTINUE_SEARCH) { | |
/* 7C92A31C a little fudging with this block */ | |
if (ExceptionRecord->ExceptionFlags & EH_STACK_INVALID) { | |
goto exit; | |
} | |
} | |
else if (hret == DISPOSITION_NESTED_EXCEPTION) { | |
/* 7C950169 */ | |
ExceptionRecord->ExceptionFlags |= EH_NESTED_CALL; | |
if (dispatch > highest) { | |
highest = dispatch; | |
} | |
} | |
else { | |
/* 7C950147 */ | |
exRec.ExceptionCode = STATUS_INVALID_DISPOSITION; | |
exRec.ExceptionFlags = EH_NONCONTINUABLE; | |
exRec.ExceptionRecord = ExceptionRecord; | |
exRec.NumberParameters = 0; | |
RtlRaiseException(&exRec); | |
} | |
/* 7C92A326 */ | |
head = head->prev; | |
} | |
} | |
exit: | |
/* 7C92AA43 */ | |
return ret; | |
} | |
/* 7C92A934 */ | |
BOOLEAN RtlCallVectoredExceptionHandlers(PEXCEPTION_RECORD ExceptionRecord, PCONTEXT ContextRecord) { | |
BOOLEAN ret = FALSE; | |
struct { | |
PEXCEPTION_RECORD eRec; | |
PCONTEXT cRec; | |
} Rec; | |
PVECTORED_EXCEPTION_NODE veh; | |
PVECTORED_EXCEPTION_HANDLER handler; | |
DWORD disposition | |
if (RtlpCalloutEntryList.Flink == &RtlpCalloutEntryList) { | |
return FALSE; | |
} | |
eRec = ExceptionRecord; | |
cRec = ExceptionRecord; | |
RtlEnterCriticalSection(&RtlpCalloutEntryLock); | |
for (veh = (PVECTORED_EXCEPTION_NODE) RtlpCalloutEntryList.Flink); | |
veh != (PVECTORED_EXCEPTION_NODE) RtlpCalloutEntryList; | |
veh = (PVECTORED_EXCEPTION_NODE) veh->ListEntry.Flink) { | |
handler = RtlDecodePointer(veh->handler); | |
disposition = handler(&Rec); | |
if (disposition == EXCEPTION_CONTINUE_EXECUTION) { | |
ret = 1; | |
break; | |
} | |
} | |
RtlLeaveCriticalSection(&RtlpCalloutEntryLock); | |
return ret; | |
} | |
/* 7C9033DC */ | |
VOID RtlpGetStackLimits(LPVOID **StackLimit, LPVOID **StackBase) { | |
PTEB teb = _TEB; // fs:18h | |
*StackLimit = teb->NtTib->StackLimit; | |
*StackBase = teb->NtTib->StackBase; | |
return; | |
} | |
/* 7C92AA50 */ | |
BOOLEAN RtlIsValidHandler(PEXCEPTION_HANDLER handler) { | |
DWORD table_sz; | |
LPVOID safeseh_table, base_addr; | |
DWORD ret, result_len, exec_flags, high, low; | |
MEMORY_BASIC_INFORMATION mbi; | |
safeseh_table = RtlLookupFunctionTable(handler, &base_addr, &table_sz); | |
if (safeseh_table == NULL || table_sz == 0) { | |
/* 7C9500D1 ProcessExecuteFlags*/ | |
if (ZwQueryInformationProcess(INVALID_HANDLE_VALUE, 22, &exec_flags, 4, NULL) >= 0) { | |
/* 7C92CF94 0x10 = ExecuteDispatchEnable */ | |
if (!(exec_flags & 0x10)) { | |
return 1; | |
} | |
} | |
/* 7C935E8E */ | |
if (NtQueryVirtualMemory(INVALID_HANDLE_VALUE, handler, NULL, &mbi, sizeof(MEMORY_BASIC_INFORMATION), &result_len) < 0) { | |
return 1; | |
} | |
/* 7C935EA9 */ | |
if (!(mbi.Protect & PAGE_EXEC_MASK)) { | |
RtlInvalidHandlerDetected(handler, -1, -1); | |
return 0; | |
} | |
else if (mbi.Type != SEC_IMAGE) { | |
return 1; | |
} | |
RtlCaptureImageExceptionValues(mbi.AllocationBase, &safeseh_table, &table_sz); | |
/* 7C935ED0 */ | |
if (var_10 == NULL || table_sz == 0) { | |
return 1; | |
} | |
return 0; | |
} | |
else if (safeseh_table == (LPVOID) -1 && table_sz == -1) { | |
return 0; | |
} | |
/* 7C9500A6 */ | |
if (table_sz < 0) { | |
RtlInvalidHandlerDetected(handler, safeseh_table, table_sz); | |
return 0; | |
} | |
rel_addr = handler - base_addr; | |
high = table_sz; | |
low = 0; | |
/* 7C9500B1 binary search through SafeSEH table */ | |
do { | |
idx = (high + low) / 2; | |
if (rel_addr < safeseh_table[idx]) { | |
if (idx == 0) { | |
RtlInvalidHandlerDetected(handler, safeseh_table, table_sz); | |
return 0; | |
} | |
high = idx - 1; | |
if (high < low) { | |
RtlInvalidHandlerDetected(handler, safeseh_table, table_sz); | |
return 0; | |
} | |
} | |
else if (rel_addr > safeseh_table[idx]) { | |
low = idx + 1; | |
if (high < low) { | |
RtlInvalidHandlerDetected(handler, safeseh_table, table_sz); | |
return 0; | |
} | |
} | |
else { | |
break; | |
} | |
} while(1); | |
return 1; | |
} | |
/* 7C92AAA4 */ | |
LPVOID RtlLookupFunctionTable(PEXCEPTION_HANDLER handler, DWORD *base_addr, DWORD *table_sz) { | |
MEMORY_BASIC_INFORMATION mbi; | |
LPVOID safeseh_table, base; | |
if (LdrpInLdrInit && RtlTryEnterCriticalSection(&LdrpLoaderLock) == 0) { | |
/* 7C92DD29 3 = MemoryBasicVlmInformation */ | |
if (NtQueryVirtualMemory((HANDLE) -1, handler, 3, &mbi, sizeof(MEMORY_BASIC_INFORMATION), NULL) < 0) { | |
return NULL; | |
} | |
else if (mbi.Type != SEC_IMAGE) { | |
return NULL; | |
} | |
base = mbi.BaseAddress; | |
/* 7C92DD51 */ | |
RtlCaptureImageExceptionValues(base, &safeseh_table, table_sz); | |
} | |
else { | |
if (_TEB != NULL) { | |
/* 7C92AAD9 */ | |
PLDR_MODULE node = _PEB->Ldr->InLoadOrderModuleList.Flink | |
if (_PEB->Ldr != 0 && node != NULL) { | |
while (node != &_PEB->Ldr->InLoadOrderModuleList) { | |
/* 7C92AB00 */ | |
if (handler < node->BaseAddr) { | |
node = node->InLoadOrderModuleList.Flink; | |
continue; | |
} | |
if (handler >= node->BaseAddr + node->SizeOfImage) { | |
node = node->InLoadOrderModuleList.Flink; | |
continue; | |
} | |
/* 7C92AB14 */ | |
base = node->BaseAddr; | |
RtlCaptureImageExceptionValues(base, &safeseh_table, table_sz); | |
if (safeseh_table == NULL && NtDllBase != NULL && (base = RtlNtImageHeader(NtDllBase)) != NULL) { | |
if (header > base && header < base + ((PIMAGE_NT_HEADER) base)->OptionalHeaders.SizeOfImage) { | |
/* 7C950D7D */ | |
RtlCaptureImageExceptionValues(base, &safeseh_table, table_sz); | |
} | |
} | |
break; | |
} | |
} | |
} | |
/* 7C92AB2C */ | |
if (LdrpInLdrInit) { | |
RtlLeaveCriticalSection(&LdrpLoaderLock); | |
} | |
} | |
*base_addr = base; | |
return safeseh_table; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Indeed the first argument of KiUserExceptionDispatcher should be PEXCEPTION_RECORD structure ptr, and follow by a PCONTEXT ptr. :-)