_SYSTEM_PROCESS_INFORMATION结构向后兼容

时间:2018-12-25 11:49:10

标签: c++ winapi c++14

目标

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

解决方案

使用此有用的文章:screenshot of GUI app running并制定一个初始函数:

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。

SYSTEM_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;

记录在Unique Technique for Iterating Through Processes

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:

根据NtQuerySystemInformation

在代码中定义SYSTEM_PROCESS_INFORMATION我自己

问题2

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

问题

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

1 个答案:

答案 0 :(得分:1)

目前是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的条目

返回布尔不是实现函数的最佳方法,最好返回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->TH;
                        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;
}