Skip to content

Instantly share code, notes, and snippets.

@odzhan
Last active January 1, 2024 16:21
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save odzhan/b4898fa96f36b131973f62b797c4f639 to your computer and use it in GitHub Desktop.
Save odzhan/b4898fa96f36b131973f62b797c4f639 to your computer and use it in GitHub Desktop.
WOW64 Callbacks
//
// 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