SYSTEM_INFORMATION_CLASS is an enum. For Thread & Process enumeration we will use SystemProcessInformation
To use SYSTEM_PROCESS_INFORMATION we need to allocate a buffer. We can get the size by running pNtQuerySystemInformation
with NULL values except for dwSizeWritten
.
Copy PSYSTEM_PROCESS_INFORMATION pSystemProcessInfo;
// Allocate Buffer for SYSTEM_PROCESS_INFORMATION
pSystemProcessInfo = (PSYSTEM_PROCESS_INFORMATION)HeapAlloc(GetProcessHeap(), 0, dwSize1);
if (pSystemProcessInfo == NULL) {
wprintf(L"[!] HeapAlloc Failed %d\n", GetLastError());
}
SYSTEM_THREAD_INFORMATION is located at the last undocumented parameter of SYSTEM_PROCESS_INFORMATION. To acccess it use SystemProcInfo->Threads
Copy typedef struct _SYSTEM_THREAD_INFORMATION
{
LARGE_INTEGER KernelTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER CreateTime;
ULONG WaitTime;
PVOID StartAddress;
CLIENT_ID ClientId;
KPRIORITY Priority;
KPRIORITY BasePriority;
ULONG ContextSwitches;
KTHREAD_STATE ThreadState;
KWAIT_REASON WaitReason;
} SYSTEM_THREAD_INFORMATION, *PSYSTEM_THREAD_INFORMATION;
Copy typedef struct _CLIENT_ID
{
HANDLE UniqueProcess;
HANDLE UniqueThread;
} CLIENT_ID, *PCLIENT_ID;
Below is the example of using NtQuerySystemInformation to enumerate threads.
Copy #include <windows.h>
#include <wchar.h>
#include <stdio.h>
#include <winternl.h>
typedef NTSTATUS (NTAPI* fnNtQuerySystemInformation)(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);
typedef struct _SYSTEM_PROC_INFO
{
ULONG NextEntryOffset;
ULONG NumberOfThreads; // Size of the Threads member
LARGE_INTEGER WorkingSetPrivateSize;
ULONG HardFaultCount;
ULONG NumberOfThreadsHighWatermark;
ULONGLONG CycleTime;
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ImageName;
KPRIORITY BasePriority;
HANDLE UniqueProcessId;
HANDLE InheritedFromUniqueProcessId;
ULONG HandleCount;
ULONG SessionId;
ULONG_PTR UniqueProcessKey;
SIZE_T PeakVirtualSize;
SIZE_T VirtualSize;
ULONG PageFaultCount;
SIZE_T PeakWorkingSetSize;
SIZE_T WorkingSetSize;
SIZE_T QuotaPeakPagedPoolUsage;
SIZE_T QuotaPagedPoolUsage;
SIZE_T QuotaPeakNonPagedPoolUsage;
SIZE_T QuotaNonPagedPoolUsage;
SIZE_T PagefileUsage;
SIZE_T PeakPagefileUsage;
SIZE_T PrivatePageCount;
LARGE_INTEGER ReadOperationCount;
LARGE_INTEGER WriteOperationCount;
LARGE_INTEGER OtherOperationCount;
LARGE_INTEGER ReadTransferCount;
LARGE_INTEGER WriteTransferCount;
LARGE_INTEGER OtherTransferCount;
SYSTEM_THREAD_INFORMATION Threads[1]; // Threads member
} SYSTEM_PROC_INFO, *PSYSTEM_PROC_INFO;
#define STATUS_SUCCESS 0x00000000
#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004
int wmain() {
NTSTATUS STATUS;
PSYSTEM_PROC_INFO SystemProcInfo;
PVOID pValueToFree = NULL;
ULONG uReturnLen1 = 0;
ULONG uReturnLen2 = 0;
// Fetching NtQuerySystemInformation's address from ntdll.dll
fnNtQuerySystemInformation pNtQuerySystemInformation = (fnNtQuerySystemInformation)GetProcAddress(GetModuleHandle(L"NTDLL.DLL"), "NtQuerySystemInformation");
if (pNtQuerySystemInformation == NULL) {
printf("[!] GetProcAddress Failed With Error : %d\n", GetLastError());
return -1;
}
// First NtQuerySystemInformation call - retrieve the size of the return buffer (uReturnLen1)
if ((STATUS = pNtQuerySystemInformation(SystemProcessInformation, NULL, 0, &uReturnLen1)) != STATUS_SUCCESS && STATUS != STATUS_INFO_LENGTH_MISMATCH) {
printf("[!] NtQuerySystemInformation [1] Failed With Error : 0x%0.8X \n", STATUS);
return -1;
}
// Allocating buffer of size "uReturnLen1"
SystemProcInfo = (PSYSTEM_PROC_INFO)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (SIZE_T)uReturnLen1);
if (SystemProcInfo == NULL) {
printf("[!] HeapAlloc Failed With Error : %d\n", GetLastError());
return -1;
}
// Setting a fixed variable to be used later to free, because "SystemProcInfo" will be modefied
pValueToFree = SystemProcInfo;
// Second NtQuerySystemInformation call - returning the SYSTEM_PROCESS_INFORMATION array (SystemProcInfo)
if ((STATUS = pNtQuerySystemInformation(SystemProcessInformation, SystemProcInfo, uReturnLen1, &uReturnLen2)) != STATUS_SUCCESS) {
printf("[!] NtQuerySystemInformation [2] Failed With Error : 0x%0.8X \n", STATUS);
return -1;
}
while (TRUE) {
wprintf(L"[+] %ls\n", SystemProcInfo->ImageName.Buffer);
if (wcsncmp(L"query.exe", SystemProcInfo->ImageName.Buffer, SystemProcInfo->ImageName.Length / sizeof(WCHAR)) == 0) {
for (int i=0; i<=SystemProcInfo->NumberOfThreads; i++) {
wprintf(L"TID: %d\n", SystemProcInfo->Threads[i].ClientId.UniqueThread);
}
}
// If we reached the end of the SYSTEM_PROCESS_INFORMATION structure
if (!SystemProcInfo->NextEntryOffset)
break;
// Calculate the next SYSTEM_PROCESS_INFORMATION element in the array
SystemProcInfo = (PSYSTEM_PROC_INFO)((ULONG_PTR)SystemProcInfo + SystemProcInfo->NextEntryOffset);
}
getchar();
return 0;
}