-
-
Save mgeeky/3628291b0e8e9c7dbe373cc9bd2efd9d to your computer and use it in GitHub Desktop.
WOW64 Callbacks
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
// | |
// How to locate the WOW64 Callback Table in ntdll.dll | |
// | |
// @modexpblog | |
// | |
#define PHNT_VERSION PHNT_VISTA | |
#include <phnt_windows.h> | |
#include <phnt.h> | |
#include <cstdio> | |
#include <cstdint> | |
#include <cstdlib> | |
#include <cstring> | |
typedef union _W64_T { | |
LPVOID p; | |
BYTE b[8]; | |
DWORD w[2]; | |
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 | |
// | |
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; | |
BOOL | |
IsReadOnlyPtr(LPVOID ptr) { | |
MEMORY_BASIC_INFORMATION mbi; | |
if (!ptr) return FALSE; | |
// query the pointer | |
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)); | |
} | |
PIMAGE_SECTION_HEADER | |
GetSectionFromRva(PIMAGE_NT_HEADERS NtHeaders, DWORD Rva) { | |
auto Section = IMAGE_FIRST_SECTION(NtHeaders); | |
for (DWORD i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++) { | |
if (Rva >= Section->VirtualAddress && | |
Rva < Section->VirtualAddress + Section->SizeOfRawData) | |
{ | |
return Section; | |
} | |
Section++; | |
} | |
return NULL; | |
} | |
// | |
// Determines if data resides within read-only section of dll. | |
// | |
BOOL | |
_IsReadOnlyPtr(PVOID dllbase, PVOID ptr) { | |
// | |
// null pointer? exit | |
// | |
if (!ptr) return FALSE; | |
auto nt = (PIMAGE_NT_HEADERS)((PBYTE)dllbase + ((PIMAGE_DOS_HEADER)dllbase)->e_lfanew); | |
ULONG_PTR img_start = (ULONG_PTR)dllbase; | |
ULONG_PTR img_end = (img_start + nt->OptionalHeader.SizeOfImage); | |
ULONG_PTR img_ptr = (ULONG_PTR)ptr; | |
// | |
// If the pointer is not within range of image, exit. | |
// | |
if (!(img_ptr > img_start && img_ptr < img_end)) return FALSE; | |
// | |
// If there's no section for RVA, exit | |
// | |
auto rva = (DWORD)(img_ptr - img_start); | |
auto s = GetSectionFromRva(nt, rva); | |
if (!s) return FALSE; | |
// | |
// If the name of section is ".rdata", we will assume it's initialized data with read access. | |
// | |
return (*(PDWORD)s->Name == *(PDWORD)".rdata"); | |
// | |
// if we want to check all sections. filter by characteristics. | |
// | |
//DWORD c = s->Characteristics; | |
//IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | |
} | |
// | |
// | |
// | |
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(m, (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); | |
} | |
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} | |
}; | |
int | |
main(void) { | |
GetWow64CallbackTable(&Wow64Table); | |
printf("Wow64LdrpInitialize : %p\n", Wow64Table.Wow64LdrpInitialize.Function.p); | |
printf("Wow64PrepareForException : %p\n", Wow64Table.Wow64PrepareForException.Function.p); | |
printf("Wow64ApcRoutine : %p\n", Wow64Table.Wow64ApcRoutine.Function.p); | |
printf("Wow64PrepareForDebuggerAttach : %p\n", Wow64Table.Wow64PrepareForDebuggerAttach.Function.p); | |
printf("Wow64SuspendLocalThread : %p\n", Wow64Table.Wow64SuspendLocalThread.Function.p); | |
printf("Wow64SuspendLocalProcess : %p\n", Wow64Table.Wow64SuspendLocalProcess.Function.p); | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment