首页 > 解决方案 > _SYSTEM_PROCESS_INFORMATION 结构向后兼容

问题描述

目标

编写一个查询进程线程状态的函数。

解决方案

使用这篇有用的帖子:迭代流程的独特技术并​​制定初始函数:

bool IterateOverThreads() {
    NTSTATUS status;
    PSYSTEM_PROCESS_INFORMATION spi;
    ULONG lBufferSize = 0;

    status = ::NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS::SystemProcessInformation, 0, 0,  & lBufferSize);
    if (0xC0000004L != status || 0 == lBufferSize)
        return false;

    unique_ptr<byte[]> pMemory(new byte[lBufferSize]);

    spi = (PSYSTEM_PROCESS_INFORMATION)pMemory.get();

    // get System Information
    if (!NT_SUCCESS(status = ::NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS::SystemProcessInformation, spi, lBufferSize,  & lBufferSize)))
        return false;

    // Loop over the list until we reach the last entry
    while (spi->NextEntryDelta) {

        // Calculate the address of the next entry.
        spi = (PSYSTEM_PROCESS_INFORMATION)((LPBYTE)spi + spi->NextEntryDelta);

        // iterate over threads
        for (size_t ii = 0; ii < spi->ThreadCount; ++ii) {
            // do whatever with thread attributes
            spi->Threads[ii].State;
            spi->Threads[ii].WaitReason;
        }
    }
    return true;
}

问题 1

我的解决方案/项目必须使用 Microsoft SDK 7.1 版。

StructSYSTEM_PROCESS_INFORMATION在 SDK 版本之间发生了如下变化:

Microsoft SDKs\Windows\v7.1A\Include\winternl.h

typedef struct _SYSTEM_PROCESS_INFORMATION {
    ULONG NextEntryOffset;
    BYTE Reserved1[52];
    PVOID Reserved2[3];
    HANDLE UniqueProcessId;
    PVOID Reserved3;
    ULONG HandleCount;
    BYTE Reserved4[4];
    PVOID Reserved5[11];
    SIZE_T PeakPagefileUsage;
    SIZE_T PrivatePageCount;
    LARGE_INTEGER Reserved6[6];
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;

记录在NtQuerySystemInformation

typedef struct _SYSTEM_PROCESS_INFORMATION {
    ULONG NextEntryOffset;
    ULONG NumberOfThreads;
    BYTE Reserved1[48];
    UNICODE_STRING ImageName;
    KPRIORITY BasePriority;
    HANDLE UniqueProcessId;
    PVOID Reserved2;
    ULONG HandleCount;
    ULONG SessionId;
    PVOID Reserved3;
    SIZE_T PeakVirtualSize;
    SIZE_T VirtualSize;
    ULONG Reserved4;
    SIZE_T PeakWorkingSetSize;
    SIZE_T WorkingSetSize;
    PVOID Reserved5;
    SIZE_T QuotaPagedPoolUsage;
    PVOID Reserved6;
    SIZE_T QuotaNonPagedPoolUsage;
    SIZE_T PagefileUsage;
    SIZE_T PeakPagefileUsage;
    SIZE_T PrivatePageCount;
    LARGE_INTEGER Reserved7[6];
} SYSTEM_PROCESS_INFORMATION;

所以,我不能“享受”使用像NumberOfThreads(或其他)这样的成员。

修复问题1:

根据文档SYSTEM_PROCESS_INFORMATION在我的代码中定义自己

问题 2

我的应用程序在所有大于等于 XP 的 Windows 上运行。

问题

我的代码安全吗?意思是,访问spi->ThreadCount安全吗?我可以假设那里的字节是有效的吗?在旧 Windows 版本上从我自己定义的结构中读取字节会有风险吗?

标签: c++winapic++14

解决方案


目前是最好的(我认为)定义之一SYSTEM_PROCESS_INFORMATION

现在它适用于所有当前的 Windows 版本(包括 xp)。

访问 spi->ThreadCount 安全吗?

是的,安全。所有当前版本的最小值。(说XP已经不会改变)。这在未来的构建中是否安全(结构没有改变)已经是另一个问题。

我的代码安全吗?

不,它错了。2分的最小值。首先,在你lBufferSize第一次调用之后NtQuerySystemInformation和在第二次调用中使用它之前 - 所需的大小可以改变(增长) - 所以需要真正做一次调用,NtQuerySystemInformation但在循环中直到你得到STATUS_INFO_LENGTH_MISMATCH. 您的代码可以工作,但有时会失败。

// Loop over the list until we reach the last entry  
while (spi->NextEntryDelta) {

这总是错误的——你丢失了最后一个条目,它有NextEntryDelta == 0

return bool 不是函数的最佳主意,更好的 return NTSTATUS

最小的正确代码看起来像

NTSTATUS IterateOverThreads()
{
    NTSTATUS status;

    PVOID buf;
    ULONG cb = 0x1000;
    do 
    {
        if (buf = LocalAlloc(0, cb))
        {
            if (0 <= (status = NtQuerySystemInformation(SystemProcessInformation, buf, cb, &cb)))
            {
                union {
                    PVOID pv;
                    PBYTE pb;
                    PSYSTEM_PROCESS_INFORMATION spi;
                };

                pv = buf;

                ULONG NextEntryOffset = 0;

                do 
                {
                    pb += NextEntryOffset;

                    DbgPrint("%wZ\n", &spi->ImageName);

                    if (ULONG NumberOfThreads = spi->NumberOfThreads)
                    {
                        PSYSTEM_THREAD_INFORMATION TH = spi->Threads;
                        do 
                        {
                            DbgPrint("\t%p %x %x\n", TH->ClientId.UniqueThread, TH->ThreadState, TH->WaitReason);
                        } while (TH++, --NumberOfThreads);
                    }

                } while (NextEntryOffset = spi->NextEntryOffset);
            }
            LocalFree(buf);
        }
        else
        {
            status = STATUS_INSUFFICIENT_RESOURCES;
        }

    } while (status == STATUS_INFO_LENGTH_MISMATCH);

    return status;
}

推荐阅读