ntQuerySystemInformtion 和它的 struct - size not mismatch 等疑​​问

Posted

技术标签:

【中文标题】ntQuerySystemInformtion 和它的 struct - size not mismatch 等疑​​问【英文标题】:ntQuerySystemInformtion and it's struct - size not mismatch and other doubts 【发布时间】:2020-11-26 02:08:18 【问题描述】:

我很好奇为什么 API ntquerysysteminformtion 返回的大小不同于所有 nextEntryOffsets 的总和。我使用 SYSTEM_PROCESS_INFORMATION 结构调用 API,它返回按预期填充的结构的大小。

但是,如果我循环进入所有条目并对 NextEntryOffset 求和并与 API 返回的大小进行比较,它永远不会不匹配。

//call to API

int sum = 0;
pCurrent = nullptr;
pfw = (_SYSTEM_PROCESS_INFORMATION*)si;

do 

            pCurrent = pfw;
            pfw = (_SYSTEM_PROCESS_INFORMATION*)((PUCHAR)pCurrent + pfw->NextEntryOffset);

           sum += pCurrent->NextEntryOffset;

 while (pCurrent->NextEntryOffset != 0);

如果我打印 API 返回的 sum 变量和长度的值,它们总是不相等的。

如果结构上没有字段,如何发现/计算每个条目的正确大小?我的猜测是它不起作用,因为在最后一个条目上 NextEntryOffset 为空,但这很奇怪,因为我看不到一种方法来计算每个条目的实际大小,而不仅仅是相信返回的长度。我想应该有办法吧?

我正在阅读非官方文档,它描述了在给定特定信息类(如我的)时,其输出缓冲区的开始以及整个缓冲区的不规则。我不明白它是如何工作的,但我假设,例如,如果有一个指向另一个结构的指针,这个指针大小 + 指向的数据或其他结构的大小被计算为总大小的一部分,这可能是动态的、正确的?

我还尝试查看此非官方文档中的字段偏移量是否按预期工作,但我未能访问它的内容。例如,它可以查看 UniqueProcessId 的假定地址,但是我找不到查看此地址内的值以确认的方法。

PVOID tmp = pCurrent + 0x50;
wprintf(L"ID: %d", *tmp);

失败了。我可以调用像 pCurrent.UniqueProcessId 这样的结构,它可以工作。但是如何在不依赖于结构的情况下移动数据呢?非常欢迎示例代码。

非常感谢。

【问题讨论】:

【参考方案1】:

循环看起来正确。大小不匹配,因为您无法以这种方式测量返回缓冲区中最后一个结构的大小。发生了什么是结构是可变长度的。在现代 C 中,我们会将最后一个元素声明为 SYSTEM_THREAD_ENTRY 的灵活数组(其中大小在 NumberOfThreads 中给出),但来自 NTDLL 的东西是旧的。

编码它的“正确”方法(只要有任何权利)是将您需要的数据复制到您自己的数据结构中,然后丢弃原始缓冲区。

【讨论】:

感谢 Joshua 的快速回答,非常感谢。我不知道这种结构。有趣的是,我的声明中没有这个 SYSTEM_THREAD_ENTRY,我使用的是在互联网上找到的(来自 react OS?)。但是我有很多线程,但它没有成员。我不确定我失败的地方,是在结构定义上还是我需要对线程数和图像名进行一些手动求和,因为两者似乎都是可变的。您介意提供一个尺寸匹配的工作示例吗? :) 对不起,愚蠢的问题。如果我理解正确,当我调用 API 时,我并没有处理真实数据,它只是返回给我使用的副本,不是吗?只是为了理解和提高我与复制数据和丢弃原始数据相关的技能。我的意思是,即使我破坏了所有结构,我只会影响我自己的应用程序,因为原始结构从未发送给我,对吗? 所以要找出合适的尺寸,我应该像以前那样做我的总和 + pCurrent.numberofthreads * sizeof (struct SYSTEM_THREAD_ENTRY) 在每个条目中? @albfrk99:我认为你做不到。 API 可以返回一个有漏洞的数据结构。【参考方案2】:

你得到的sum 只计算第一个到倒数第二个结构的长度,不包括最后一个的长度。正如其他答案所指出的,这是一个可变长度结构,每个结构在内存中紧跟一个或多个 SYSTEM_THREAD_INFORMATION 结构,这些结构为前面进程中的每个线程提供信息。

还有ImageName.Buffer的内存空间(大小由系统决定),不用自己计算每个struct的大小,先通过NULL得到需要的大小:

NtQuerySystemInformation(SystemProcessInformation, NULL, 0, &length);

如果你想知道最后一个结构的大小,只需使用length-sum

另外,pCurrent + 0x50 等于&pCurrent[0x50],尝试用(char*)pCurrent + 0x50 测试。

变量数组见https://devblogs.microsoft.com/oldnewthing/20040826-00/?p=38043

【讨论】:

谢谢 Drake Wu - MSFT。我了解,以这种方式计算结构大小是不可能的。您说过调用 API 将第二个参数作为 NULL 传递,它将返回要分配的大小,但是如果在第二次调用时创建了一个新进程并且大小增加了怎么办?还是API保证当时的流程副本?关于在没有结构的情况下访问内容失败。我可以通过结构的成员和内存地址作为 pCurrent+offset 访问所有它们,但无论我用 (char *) 或类似的东西做什么,内容永远不会正确。

以上是关于ntQuerySystemInformtion 和它的 struct - size not mismatch 等疑​​问的主要内容,如果未能解决你的问题,请参考以下文章

第三十一节:扫盲并发和并行同步和异步进程和线程阻塞和非阻塞响应和吞吐等

shell中$()和 ` `${}${!}${#}$[] 和$(()),[ ] 和(( ))和 [[ ]]

Java基础8---面向对象代码块和继承和this和super和重写和重载和final

Java基础8---面向对象代码块和继承和this和super和重写和重载和final

JS中some()和every()和join()和concat()和pop(),push(),shift(),unshfit()和map()和filter()

malloc和free,brk和sbrk和mmap和munmap的使用和关系以及内存分配的原理