You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

枚举进程时_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,需确保结构体定义严格匹配目标系统的实际布局:

  1. 针对64位系统补充结构体大小验证,例如64位Windows 10+下SYSTEM_PROCESS_INFORMATION的大小约为0x118字节(需根据具体系统版本调整)。
  2. 修正字段的对齐规则,确保和内核结构体一致(内核默认使用8字节对齐,部分字段可能存在隐式填充)。
  3. 注意:未公开API可能随Windows版本更新而变化,此方案存在兼容性风险,不建议用于生产环境。

内容的提问来源于stack exchange,提问作者kot123

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 15:09:51