c++ - _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 版本上从我自己定义的结构中读取字节会有风险吗?
解决方案
目前是最好的(我认为)定义之一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;
}
推荐阅读
- amazon-web-services - 在 AWS 云开发工具包 (CDK) 中传播标签有困难
- php - 将图像文件上传到数据库中的文件夹+链接而不使用表单输入问题
- c++ - 有人可以告诉我为什么会出现此错误 - 模板 LinkedList
- java - 如何在没有tomcat重启的情况下在spring boot中重新加载外部属性文件?
- python - Dataframe:循环调用一些变量,同时手动调用其他变量
- python - Python tkinter 画布动画
- javascript - 如何在具有特定不同值对的对象数组中找到值的总和?
- wso2 - WSO2 API Manager 添加hal+json
- django - 如何保存在docker中创建的数据库?
- apache-storm - Apache Storm 集群未将拓扑的组件分配给所有可用的工作人员