我仍然认为这会造成 BSOD,而且我认为这是故意的。一旦将所有拼图拼凑在一起,假设这是故意的,这是完全有道理的。源代码不兼容将不可避免地迫使开发人员注意到KeNumberProcessors
从PCCHAR
到类型的变化CCHAR
。最可能的错误是:error C2100: illegal indirection
。
根据 PE 标头,该文件是使用 WDK 7600.16385.1(操作系统版本)创建的,目标是 XP(子系统版本),假设我们可以信任用于创建驱动程序的最佳实践。但是,正如我所验证的,该驱动程序确实可以在 Windows 2000 Professional(带有 SP4)上运行。只要作者足够小心,不使用在 Windows 2000 上不可用的函数,但在名义目标的编译和链接时可用,这是可能的。
好的,假设您有一个针对 XP 之前的 Windows 版本的旧驱动程序。你会写这个(需要更多的代码来强制这个代码不被优化掉):
CCHAR nCpus = *KeNumberProcessors;
PVOID lpBuf = ExAllocatePool(NonPagedPool, nCpus * 7);
当为 Windows 2000(免费)作为目标(使用 WDK 6001.18002)构建它时,我们得到了行分配nCpus
和后续的:
.text:00010519 mov eax, ds:__imp__KeNumberProcessors
; opcodes unrelated to processor number
.text:0001052E movsx eax, byte ptr [eax]
.text:00010531 imul eax, 7
.text:00010534 push edi
.text:00010535 push eax ; NumberOfBytes
.text:00010536 push ebx ; PoolType
.text:00010537 call ds:__imp__ExAllocatePool@8 ; ExAllocatePool(x,x)
.text:0001053D mov [ebp+lpBuf], eax
当使用 WDK 7600.16385.1 以 Windows XP 为目标时,我们在编译期间收到错误:
KNPs.cpp(102) : error C2100: illegal indirection
...我们通过删除*
that dereferences 来修复KeNumberProcessors
它,然后给我们一个成功的编译和以下代码:
.text:00010519 mov eax, ds:__imp__KeNumberProcessors
; opcodes unrelated to processor number
.text:0001052E movsx eax, byte ptr [eax]
.text:00010531 imul eax, 7
.text:00010534 push edi
.text:00010535 push eax ; NumberOfBytes
.text:00010536 push ebx ; PoolType
.text:00010537 call ds:__imp__ExAllocatePool@8 ; ExAllocatePool(x,x)
.text:0001053D mov [ebp+lpBuf], eax
那是相同的。并且没有任何类似于.sys
for DebugView 的迹象会故意导致 BSOD。
所以我的钱是:这意味着导致蓝屏死机,完全知道这种情况实际上永远不会发生。
原答案如下:
根据我可以挖掘的信息,我同意。这段代码似乎会导致不可避免的蓝屏死机。
让我们首先说明 Windows 2000 Server(数据中心版)的最大可用处理器数量是 32。
基于 for 的声明KeNumberProcessors
,顺便说一下,它已被弃用而支持KeQueryActiveProcessors
:
#if (NTDDI_VERSION >= NTDDI_VISTA)
extern NTSYSAPI volatile CCHAR KeNumberProcessors;
#elif (NTDDI_VERSION >= NTDDI_WINXP)
extern NTSYSAPI CCHAR KeNumberProcessors;
#else
extern PCCHAR KeNumberProcessors;
#endif
在 Windows XP 之前,该变量曾经是一个指针。查看上面链接的文档,您会发现(相关摘录):
该KeNumberProcessors
内核变量是在Windows Vista的Service Pack 1(SP1)时,Windows Server 2008和Windows的更高版本过时。 KeNumberProcessors
不会出现在从 Windows Vista SP1 开始的 WDK 版本的 WDK 标头中;但是,该变量仍然从内核导出,因此为早期平台构建的驱动程序不会中断
... 和:
从 Windows XP 开始,KeNumberProcessors
是一个 8 位整数值,表示平台中的处理器数量。在早期版本的 Windows 中,KeNumberProcessors
是一个指向 8 位整数值的指针,该值指示平台中的处理器数量。
现在,虽然我当然可能无法通过浏览 Windows 源代码获得 Sysinternals/Microsoft 的人的所有答案,但我的猜测如下。这本来是一个聪明的伎俩-依靠间接知识-以适应这两个Windows XP和更新(其中变量是CCHAR
)以及之前的Windows版本,它是一个PCCHAR
。
较旧的 MSDN 文档更加冗长:
KeNumberProcessors 定义的变化
内核变量KeNumberProcessors
表示系统中运行驱动程序的活动 CPU 的数量。在 Microsoft Windows XP 版本的 ntddk.h 和 wdm.h 中, 的定义KeNumberProcessors
已从指针更改为值。此变量的 Microsoft® Windows® 2000 定义要求KeNumberProcessors
取消引用(例如,*KeNumberProcessors
)。由于此变量的定义已更改,因此在 Windows XP 构建环境中构建的驱动程序不得取消引用此变量(例如,
KeNumberProcessors
)。
未能KeNumberProcessors
正确使用的驱动程序将在编译时收到“非法间接访问”错误。
请注意,无论使用何种声明,KeNumberProcessors
根据构建它们的环境正确引用的驱动程序将在 Windows 2000 和 Windows XP 上正常工作。因此,例如,KeNumberProcessors
当驱动程序在 Windows 2000 或 Windows XP 上运行时,在 Windows 2000 构建环境中构建的驱动程序取消引用将获得此变量的正确值。
问题似乎是他们似乎弄错了,如果我没有弄错的话。然而,他们也有可能做对了,而我缺乏他们可以访问的一些重要信息:)
您可以使用 MSVC 测试场景:
DWORD_PTR KeNumberProcessors = 0x80564321;
DWORD_PTR dsKeNumberProcessors = (DWORD_PTR)&KeNumberProcessors;
__asm {
push eax
mov eax, dsKeNumberProcessors
mov al, [eax] // only affects AL -> AL := 0x21
cmp al, 0x40
movsx eax, al // garbles EAX
pop eax
};
逐步完成它可以为您提供有关正在发生的事情的线索。我不得不承认这movsx
让我失望了。
这就是我认为它可能意味着工作的方式
读取指定地址处的字节KeQueryActiveProcessors
。看看它是否是一个小于 0x40 的值,如果是,坚持解释为字节。
如果大于 0x40,每当包含在(即在 XP 之前的 Windows 上)地址 的最低有效字节足够大时就会发生这种情况,我们可以假设这实际上是一个指针并取消引用它。 KeQueryActiveProcessors
注意:重新阅读它:最低有效字节。因此,这里重要的不是内核基地址中的 0x80(内核加载到高于 0x80000000 的地址)。对于 Windows 2000,我发现最低有效字节的以下值:
- 0x70 (ntkrnlpa.exe, SP4)
- 0xF0(ntoskrnl.exe,SP4)
- 0xC8 (ntkrnlpa.exe, RTM)
- 0x30(ntoskrnl.exe,RTM)
不过,我没有检查任何其他内核版本。这里的问题是我们已经有一个小于 0x40 的值。
顺便说一句:通过让字节成为页面对齐的结构的成员,可以合理地强制执行超过 0x40 的地址。
现在无论如何都会破坏这个场景的部分是movsx
(符号扩展)。它会CCHAR
无条件地覆盖可能是地址的内容,从而创建一个虚假地址,出于所有实际目的,该地址应该会导致 BSOD。
无论如何,这背后的机制可能意味着类似于资源 ID,它可以是 16 位无符号整数或指向具有资源名称的以零结尾的字符串的指针。