WoW64 (Windows 32-bit on Windows 64-bit) is a subsystem within Microsoft Windows that lets Windows run 32-bit programs on 64-bit hardware.
One way to glean what processes are currently running in WoW64 mode is by querying NtQuerySystemInformation
and checking whether IsWow64Process
returns true or not.
This returns a pointer to a value that is set to TRUE if the process is running under WOW64 on an Intel64, x64, or ARM64 processor.
typedef NTSTATUS(NTAPI* PNtQuerySystemInformation)(
ULONG SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);
BOOL IsProcessWow64(HANDLE hProcess) {
BOOL bIsWow64 = FALSE;
FARPROC pIsWow64Process = GetProcAddress(
GetModuleHandle(TEXT("kernel32")), "IsWow64Process");
if (pIsWow64Process) {
((BOOL(WINAPI*)(HANDLE, PBOOL))pIsWow64Process)(hProcess, &bIsWow64);
}
return bIsWow64;
}
With our type definition and the IsProcessWow64 function defined, we can implement the following logic in our main function like so: before doing anything, we pass GetCurrentProcess to our WoW64 check, thus checking whether the current process or environment we’re in is WoW64.
We then get a handle to ntdll
, set up a do-while loop and a buffer to store queried process information, and then iterate over them via NextEntryOffset
.
If the process is a normal 64-bit process, we simply print “x64,” the process name, and the related PID, like so: [x64] process.exe (PID: 1336)
.
Similarly, if the process returns true for the WOW64 check, we instead print WOW64, like so: [WOW64] process.exe (PID: 1337)
.
We iterate over the Windows process list after setting up a pointer to the process information buffer: PSYSTEM_PROCESS_INFORMATION procInfo = (PSYSTEM_PROCESS_INFORMATION)buffer
For each unique process ID we see, we call OpenProcess
which takes a “Desired Access” key represented by a DWORD, a BOOLEAN indicating whether or not to inherit the handle – we set this to false – and last, our process ID, represented by a DWORD.
HANDLE OpenProcess(
[in] DWORD dwDesiredAccess,
[in] BOOL bInheritHandle,
[in] DWORD dwProcessId
);
PROCESS_QUERY_LIMITED_INFORMATION
is the desired access we want and what we pass to NtQuerySystemInformation
. This way, as a standard user, we won’t get errors if we hit an elevated system process.
Using this method, if we want to enumerate elevated processes, we must run the binary under a user with elevated privileges.
We traverse the process list via NextEntryOffset
which is a part of the SYSTEM_PROCESS_INFORMATION
structure – we check each process ID against the WoW64 function, and then free our buffer and ntdll handle after completion.
int main() {
// Check if *this* program is running under WOW64
if (IsProcessWow64(GetCurrentProcess())) {
printf("[!] We appear to be running under WOW64 (32-bit on 64-bit Windows)\n\n");
}
else {
printf("[*] We appear to be running as native 64-bit\n\n");
}
// Load NtQuerySystemInformation
HMODULE ntdll = LoadLibraryA("ntdll.dll");
PNtQuerySystemInformation NtQuerySystemInformation =
(PNtQuerySystemInformation)GetProcAddress(ntdll, "NtQuerySystemInformation");
ULONG bufferSize = 0;
NTSTATUS status;
PVOID buffer = NULL;
// Query process info
do {
if (buffer) free(buffer);
buffer = malloc(bufferSize);
status = NtQuerySystemInformation(5, buffer, bufferSize, &bufferSize);
} while (status == 0xC0000004); // STATUS_INFO_LENGTH_MISMATCH
// Iterate processes
PSYSTEM_PROCESS_INFORMATION procInfo = (PSYSTEM_PROCESS_INFORMATION)buffer;
while (procInfo->NextEntryOffset) {
HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE,
(DWORD)(ULONG_PTR)procInfo->UniqueProcessId);
if (hProcess) {
BOOL isWow64 = IsProcessWow64(hProcess);
CloseHandle(hProcess);
printf("[%s] %.*S (PID: %d)\n",
isWow64 ? "WOW64" : "x64",
procInfo->ImageName.Length / 2,
procInfo->ImageName.Buffer,
(DWORD)(ULONG_PTR)procInfo->UniqueProcessId);
}
procInfo = (PSYSTEM_PROCESS_INFORMATION)((PBYTE)procInfo + procInfo->NextEntryOffset);
}
free(buffer);
FreeLibrary(ntdll);
return 0;
}
As seen below, one process is running in WoW64 mode on my vanilla Windows installation:
//snipped
[x64] vshost.exe (PID: 3300)
[WOW64] vcpkgsrv.exe (PID: 7144)
[x64] ServiceHub.Host.dotnet.x64.exe (PID: 7248)
WoW64Search on Github