Jump to content
Nytro

Exception handling routines (ntdll)

Recommended Posts

Posted

Exception handling routines (ntdll)

/* 
* 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;
}

Vectored exception handling:

typedef struct _LdrpVectorHandlerList {
struct _LdrpVectorHandlerList *Prev;
struct _LdrpVectorHandlerList *Next;
DWORD Depth;
PVECTORED_EXCEPTION_HANDLER VectoredHandler;
} VECTORED_HANDLER_LIST, *PVECTORED_HANDLER_LIST;

VECTORED_HANDLER_LIST LdrpVectorHandlerList[2];

BOOLEAN RtlpCallVectoredHandlers(PEXCEPTION_RECORD ExceptionRecord, PCONTEXT ContextRecord, BOOL flag) {
BOOLEAN ret;
DWORD hret;
PVECTORED_HANDLER_LIST freeList, head, next;

PTEB teb = _TEB; // fs:18h
PPEB peb = teb->ProcessEnvironmentBlock; // teb:30h

ret = 0;

head = &LdrpVectorHandlerList[flag];

/* there are two possible bit flags with CrossProcessFlags that it checks here
flag = 0 corresponds to ProcessUsingVEH
flag = 1 corresponds to ProcessUsingVCH
*/
if (peb->CrossProcessFlags & (1 << (flag + 2)) {
freeList = NULL;

/* get a slim reader/writer lock on the vectorhandlerlist */
RtlAcquireSRWLockExclusive(head);

next = head->Next;

while (next != head) {
next->Depth++;
RtlReleaseSRWLockExclusive(head);

handler = RtlDecodePointer(next->VectoredHandler);
hret = handler(ExceptionRecord);

RtlAcquireSRWLockExclusive(head);
next->Depth--;

if (next->Depth == 0) {
next->Next->Prev = next->Prev;
next->Prev->Next = next->Next;

if (next->Prev == next->Next) {
/* this is actuall an atomic bit reset (lock btr) instruction
* basically they are clearing either the ProcessUsingVEH or ProcessUsingVCH flags
* when the list becomes empty */
peb->CrossProcessFlags &= ~(1 << (flag + 2));
}

next->Prev = freeList;
freeList = next;
}

next = next->Prev;

if (hret == EXCEPTION_CONTINUE_EXECUTION) {
ret = 1;
break;
}
}

RtlReleaseSRWLockExclusive(head);

while (freeList) {
LPVOID p = freeList;
freeList = freeList->Prev;
RtlFreeHeap(peb->ProcessHeap, 0, p);
}
}

return ret;
}

Probabil reverse engineering (pe XP) la aceste functii, in cel mai rau caz o implementare particulara.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...