Ken Johnson (otherwise known as Skywing) first talked about the KiUserExceptionDispatcher back in 2007 . Since then, scattered around the internet are various posts talking about it, but for some reason nobody demonstrating how to use it. It’s been documented that FinFisher misuses the function pointers as part of its virtual machine functionality, so let’s take a look at how to find the table before doing anything creative with it…The code to locate the table didn’t take long and didn’t require looking at FinFisher internals or existing code. It’s a simple heuristic based search.
If you take a look at ntdll!LdrpLoadWow64, that’s called during initialization of a WOW64 process, you’ll see it loading wow64.dll and resolving the address of six exports. This process has been better documented in the posts mentioned above.
A closer look at how this works will provide you with an array of function names stored in STRING format and a pointer to a variable that holds each address resolved. The following is my attempt at recreating the same structure.
typedef union _W64_T { LPVOID p; DWORD64 q; LPVOID *pp; } W64_T; typedef struct _WOW64_CALLBACK { STRING Name; W64_T Function; } WOW64_CALLBACK, *PWOW64_CALLBACK; // // Structure based on 64-bit version of NTDLL on Windows 10 // typedef struct _WOW64_CALLBACK_TABLE { WOW64_CALLBACK Wow64LdrpInitialize; WOW64_CALLBACK Wow64PrepareForException; WOW64_CALLBACK Wow64ApcRoutine; WOW64_CALLBACK Wow64PrepareForDebuggerAttach; WOW64_CALLBACK Wow64SuspendLocalThread; WOW64_CALLBACK Wow64SuspendLocalProcess; } WOW64_CALLBACK_TABLE, *PWOW64_CALLBACK_TABLE; WOW64_CALLBACK_TABLE Wow64Table = { {RTL_CONSTANT_STRING("Wow64LdrpInitialize"), NULL}, {RTL_CONSTANT_STRING("Wow64PrepareForException"), NULL}, {RTL_CONSTANT_STRING("Wow64ApcRoutine"), NULL}, {RTL_CONSTANT_STRING("Wow64PrepareForDebuggerAttach"), NULL}, {RTL_CONSTANT_STRING("Wow64SuspendLocalThread"), NULL}, {RTL_CONSTANT_STRING("Wow64SuspendLocalProcess"), NULL} };
There could be a number of ways to do this. In the following example, we search the .rdata section for STRING structures that equal the function pointer we wish to find. Since these strings are constant and unlikely to change, it works reasonably well.
BOOL IsReadOnlyPtr(LPVOID ptr) { MEMORY_BASIC_INFORMATION mbi; if (!ptr) return FALSE; DWORD res = VirtualQuery(ptr, &mbi, sizeof(mbi)); if (res != sizeof(mbi)) return FALSE; return ((mbi.State == MEM_COMMIT ) && (mbi.Type == MEM_IMAGE ) && (mbi.Protect == PAGE_READONLY)); } BOOL GetWow64FunctionPointer(PWOW64_CALLBACK Callback) { auto m = (PBYTE)GetModuleHandleW(L"ntdll"); auto nt = (PIMAGE_NT_HEADERS)(m + ((PIMAGE_DOS_HEADER)m)->e_lfanew); auto sh = IMAGE_FIRST_SECTION(nt); for (DWORD i=0; i<nt->FileHeader.NumberOfSections; i++) { if (*(PDWORD)sh[i].Name == *(PDWORD)".rdata") { auto rva = sh[i].VirtualAddress; auto cnt = (sh[i].Misc.VirtualSize - sizeof(STRING)) / sizeof(ULONG_PTR); auto ptr = (PULONG_PTR)(m + rva); for (DWORD j=0; j<cnt; j++) { if (!IsReadOnlyPtr((LPVOID)ptr[j])) continue; auto api = (PSTRING)ptr[j]; if (api->Length == Callback->Name.Length && api->MaximumLength == Callback->Name.MaximumLength) { if (!strncmp(api->Buffer, Callback->Name.Buffer, Callback->Name.Length)) { Callback->Function.p = (PVOID)ptr[j + 1]; return TRUE; } } } break; } } return FALSE; } void GetWow64CallbackTable(PWOW64_CALLBACK_TABLE Table) { GetWow64FunctionPointer(&Table->Wow64LdrpInitialize); GetWow64FunctionPointer(&Table->Wow64PrepareForException); GetWow64FunctionPointer(&Table->Wow64ApcRoutine); GetWow64FunctionPointer(&Table->Wow64PrepareForDebuggerAttach); GetWow64FunctionPointer(&Table->Wow64SuspendLocalThread); GetWow64FunctionPointer(&Table->Wow64SuspendLocalProcess); }
This type of code isn’t useful to a 32-Bit WOW process without jumping to 64-Bit since the function pointers are stored in the 64-Bit version of NTDLL. There are potentially other uses though like intercepting APCs, anti-debugging and processing exceptions before VEH or SEH, which FinFisher did successfully for many many years….