枚举进程时_SYSTEM_PROCESS_INFORMATION的VirtualSize值异常偏大问题
问题描述
尝试枚举所有进程并通过_SYSTEM_PROCESS_INFORMATION结构体获取进程虚拟地址空间(VAS)大小时,得到如2203412733952这类异常偏大的值,已使用size_t对应的格式说明符%zu,但问题仍存在。
自定义结构体定义
// ensure proper alignment for 64-bit systems #pragma pack(push, 8) // align structure to 8-byte boundaries typedef struct _SYSTEM_PROCESS_INFORMATION { ULONG NextEntryOffset; ULONG NumberOfThreads; LARGE_INTEGER WorkingSetPrivateSize; //VISTA ULONG HardFaultCount; //WIN7 ULONG NumberOfThreadsHighWatermark; //WIN7 ULONGLONG CycleTime; //WIN7 LARGE_INTEGER CreateTime; LARGE_INTEGER UserTime; LARGE_INTEGER KernelTime; UNICODE_STRING ImageName; KPRIORITY BasePriority; HANDLE UniqueProcessId; HANDLE InheritedFromUniqueProcessId; ULONG HandleCount; ULONG SessionId; ULONG_PTR PageDirectoryBase; // // This part corresponds to VM_COUNTERS_EX. // NOTE: *NOT* THE SAME AS VM_COUNTERS! // 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; // // This part corresponds to IO_COUNTERS // LARGE_INTEGER ReadOperationCount; LARGE_INTEGER WriteOperationCount; LARGE_INTEGER OtherOperationCount; LARGE_INTEGER ReadTransferCount; LARGE_INTEGER WriteTransferCount; LARGE_INTEGER OtherTransferCount; } SYSTEM_PROCESS_INFORMATION, * PSYSTEM_PROCESS_INFORMATION; #pragma pack(pop) // Validate structure size #ifndef _WIN64 C_ASSERT(sizeof(SYSTEM_PROCESS_INFORMATION) == 0xB8); // Must be 8-byte aligned #endif
枚举进程代码
BOOL enumAllProcesses() { fnNtQuerySystemInformation pNtQuerySystemInformation = (fnNtQuerySystemInformation)GetProcAddress(GetModuleHandle(L"NTDLL.dll"), "NtQuerySystemInformation"); if (pNtQuerySystemInformation == NULL) { printf("Failed to get NtQuerySystemInformation function address \n"); return FALSE; } PSYSTEM_PROCESS_INFORMATION sysProcessInfo; // buffer DWORD dwProcInfoSize; unsigned long RetLen; NTSTATUS STATUS; // first call to NtQuerySystemInformation to get the size needed for all processes // not providing any buffer for the process information, which is fine because the goal is to find out how much memory you need to allocate. STATUS = pNtQuerySystemInformation(SystemProcessInformation, NULL, 0, &dwProcInfoSize); if (STATUS != STATUS_INFO_LENGTH_MISMATCH) { printf("First call to NtQuerySystemInformation was not STATUS_INFO_LENGTH_MISMATCH: %d \n", STATUS); return FALSE; } // allocate buffer that can hold process information sysProcessInfo = (PSYSTEM_PROCESS_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (size_t)dwProcInfoSize); if (sysProcessInfo == NULL) { printf("Memory allocation failed (sysProcessInfo) \n"); return FALSE; } // actual call to NtQuerySystemInformation // RetLen will store the total size of the data block that contains information about all processes. STATUS = pNtQuerySystemInformation(SystemProcessInformation, sysProcessInfo, dwProcInfoSize, &RetLen); if (STATUS != 0x0) { printf("pNtQuerySystemInformation Failed %ld \n", STATUS); HeapFree(GetProcessHeap(), 0, sysProcessInfo); return FALSE; } // iterate through the SYSTEM_PROCESS_INFORMATION structures to extract details about each process. do { printf("\n------------------------------\n"); if (sysProcessInfo->ImageName.Buffer != NULL) { wprintf(L"Process Name: %s \n", sysProcessInfo->ImageName.Buffer); } if (sysProcessInfo->UniqueProcessId != NULL) { printf("Process ID: %lu \n", (DWORD)(ULONG_PTR)sysProcessInfo->UniqueProcessId); } printf("Number of Threads: %lu \n", sysProcessInfo->NumberOfThreads); printf("Virtual Address Space Size: %zu \n", sysProcessInfo->VirtualSize); printf("------------------------------\n"); // done iterating if (sysProcessInfo->NextEntryOffset == 0) { break; } // move to next structure in the buffer sysProcessInfo = (PSYSTEM_PROCESS_INFORMATION)((PUCHAR)sysProcessInfo + sysProcessInfo->NextEntryOffset); } while (TRUE); HeapFree(GetProcessHeap(), 0, sysProcessInfo); return TRUE; }
问题原因与解决方法
核心原因
自定义的_SYSTEM_PROCESS_INFORMATION结构体与Windows内核实际的字段布局不匹配,尤其是64位系统下的字段偏移错误,导致读取VirtualSize时取到了错误内存区域的垃圾数据。
具体问题点:
SYSTEM_PROCESS_INFORMATION是未公开的内核结构体,不同Windows版本、32/64位平台的字段布局存在差异。手动定义的结构体在64位环境下,PageDirectoryBase之后的VM_COUNTERS_EX相关字段偏移与内核返回的数据不对应。- 仅针对32位系统做了结构体大小验证(
C_ASSERT(sizeof(SYSTEM_PROCESS_INFORMATION) == 0xB8)),64位系统下没有验证,而实际64位结构体大小远大于该值,直接导致后续字段读取错位。
解决方案
方案1:使用官方公开API(推荐,无兼容性风险)
放弃未公开的NtQuerySystemInformation,改用微软官方支持的EnumProcesses+GetProcessMemoryInfo组合,直接获取准确的进程内存信息:
#include <Windows.h> #include <Psapi.h> #pragma comment(lib, "Psapi.lib") BOOL enumAllProcesses() { DWORD processIDs[1024]; DWORD cbNeeded; if (!EnumProcesses(processIDs, sizeof(processIDs), &cbNeeded)) { printf("EnumProcesses failed\n"); return FALSE; } DWORD numProcesses = cbNeeded / sizeof(DWORD); for (DWORD i = 0; i < numProcesses; i++) { if (processIDs[i] == 0) continue; HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processIDs[i]); if (hProcess == NULL) continue; PROCESS_MEMORY_COUNTERS_EX pmc; if (GetProcessMemoryInfo(hProcess, (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc))) { WCHAR szProcessName[MAX_PATH] = L"<unknown>"; HMODULE hMod; DWORD cbModName; if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbModName)) { GetModuleBaseNameW(hProcess, hMod, szProcessName, sizeof(szProcessName)/sizeof(WCHAR)); } printf("\n------------------------------\n"); wprintf(L"Process Name: %s\n", szProcessName); printf("Process ID: %lu\n", processIDs[i]); printf("Number of Threads: %lu\n", pmc.ProcessCount); printf("Virtual Address Space Size: %zu KB\n", pmc.VirtualSize / 1024); printf("------------------------------\n"); } CloseHandle(hProcess); } return TRUE; }
方案2:修正未公开结构体定义(仅适合必须使用NtQuerySystemInformation的场景)
若必须使用未公开API,需确保结构体定义严格匹配目标系统的实际布局:
- 针对64位系统补充结构体大小验证,例如64位Windows 10+下
SYSTEM_PROCESS_INFORMATION的大小约为0x118字节(需根据具体系统版本调整)。 - 修正字段的对齐规则,确保和内核结构体一致(内核默认使用8字节对齐,部分字段可能存在隐式填充)。
- 注意:未公开API可能随Windows版本更新而变化,此方案存在兼容性风险,不建议用于生产环境。
内容的提问来源于stack exchange,提问作者kot123
相关产品推荐
相关产品推荐

