_SYSTEM_PROCESS_INFORMATION结构向后兼容性

_SYSTEM_PROCESS_INFORMATION struct backward compatibility

本文关键字:兼容性 INFORMATION SYSTEM PROCESS 结构      更新时间:2023-10-16

Goal

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

溶液

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

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。

SDK 版本之间的结构SYSTEM_PROCESS_INFORMATION已按以下方式更改:

Microsoft SDKsWindowsv7.1AIncludewinternl.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 版本上从我自己定义的结构中读取字节是否有风险?

目前最好的

(我认为)SYSTEM_PROCESS_INFORMATION定义之一

现在它对所有当前的Windows版本(包括XP)都有效。

访问 spi->线程计数安全?

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

我的代码安全吗?

不,错了。 2分的最小值。 首先,在您第一次调用NtQuerySystemInformation时获得lBufferSize之后,在第二次调用中使用它之前 - 所需的大小可以更改(增长) - 所以真的需要对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("%wZn", &spi->ImageName);
                    if (ULONG NumberOfThreads = spi->NumberOfThreads)
                    {
                        PSYSTEM_THREAD_INFORMATION TH = spi->Threads;
                        do 
                        {
                            DbgPrint("t%p %x %xn", 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;
}