Skip to content

Instantly share code, notes, and snippets.

@conioh
Forked from TheCjw/seh_xp.c
Created August 26, 2018 18:38
Show Gist options
  • Save conioh/56b3573646ff97bee556d94290dc68f0 to your computer and use it in GitHub Desktop.
Save conioh/56b3573646ff97bee556d94290dc68f0 to your computer and use it in GitHub Desktop.
/*
* exception handling routines (xp 32-bit, partial/incomplete)
*
* ntdll 5.1.2600.5755
* v2 (updated jan 2011)
*
* - hawkes <hawkes@sota.gen.nz>
*
* 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