Dbgv.sys
(x86内核驱动程序)。它具有此sub_10D4A
函数,几乎在驱动程序DriverEntry
函数的开始处调用它。它是这样的:相关的拆卸件:
.text:00010D64 mov eax, ds:KeNumberProcessors
.text:00010D69 mov al, [eax]
.text:00010D6B cmp al, 40h ; '@'
.text:00010D6D movsx eax, al
.text:00010D70 jl short loc_10D74
.text:00010D72 mov eax, [eax] ; <-- line pointed out
我不需要了解的是红色箭头所指向的构造。如果
KeNumberProcessors
全局变量大于或等于64个CPU内核(或40h),它将执行mov eax, [eax]
指令,该指令将尝试从多个CPU的地址(例如40h
)读取DWORD,这没有任何意义。 /> 会不会导致蓝屏死机?
这里的目的是什么?
#1 楼
我仍然认为这会产生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
...我们通过移除对
*
取消引用的KeNumberProcessors
进行了修复,从而为我们提供了成功的编译和以下代码:KNPs.cpp(102) : error C2100: illegal indirection
相同。而且没有迹象表明像DebugView那样在
.sys
中会故意引起BSOD。永远不会发生。下面的原始答案:
我可以挖掘的信息是我同意的。此代码似乎会导致不可避免的BSOD。
让我们首先说明Windows 2000 Server(数据中心版)的最大处理器数量为32。
基于
KeNumberProcessors
的声明,该声明已弃用KeQueryActiveProcessors
: /> .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
该变量曾经是Windows XP之前的指针。查看上面链接的文档,您将发现(相关摘录):
KeNumberProcessors
内核变量在Windows Vista Service Pack 1(SP1)和Windows Server 2008中已废弃。和Windows的更高版本。从Windows Vista SP1开始,WDK
的WDK标头中没有出现
KeNumberProcessors
;但是,该变量仍从内核中导出,因此为较早平台构建的驱动程序不会中断...和:
从Windows XP开始,
KeNumberProcessors
是一个8位整数值。表示平台中的处理器数量。在Windows的早期版本中,
KeNumberProcessors
是一个指向8位整数值的指针,该整数值指示平台中的处理器数量。现在,而通过浏览Windows源代码,我当然可能不具备Sysinternals / Microsoft员工提供的所有答案,我的猜测如下。这本来是一个聪明的技巧-依靠环境知识-适应Windows XP和更高版本(变量为
CCHAR
)以及以前的Windows版本(即PCCHAR
)。旧MSDN文档更加冗长:
KeNumberProcessors定义的更改
内核变量
KeNumberProcessors
指示系统中驱动程序的活动CPU数量。在跑。在ntddk.h和wdm.h的Microsoft Windows XP版本中,
KeNumberProcessors
的定义已从指针更改为值。 Microsoft®Windows®2000 此变量的定义要求将
KeNumberProcessors
取消引用(例如,*KeNumberProcessors
)。由于更改了此变量的定义,因此Windows XP构建环境中构建的驱动程序不得取消引用此变量(例如,KeNumberProcessors
)。未能正确使用
KeNumberProcessors
的驱动程序将在编译时出现“非法间接”错误。
请注意,无论使用什么声明,正确
的驱动程序均会根据以下内容引用
KeNumberProcessors
到构建它们的环境将在Windows 2000和Windows XP上正常工作。因此,例如,在Windows 2000 build
环境中构建的,取消引用
KeNumberProcessors
的驱动程序将获得在Windows 2000或Windows XP上运行驱动程序时,此变量的正确值。问题似乎是他们似乎已经得到了它。错了,如果我不是所有人都误会了。但是,还有机会他们做对了,而我却缺少他们可以访问的一些重要信息:)
您可以使用以下方法使用MSVC来测试场景:
#if (NTDDI_VERSION >= NTDDI_VISTA)
extern NTSYSAPI volatile CCHAR KeNumberProcessors;
#elif (NTDDI_VERSION >= NTDDI_WINXP)
extern NTSYSAPI CCHAR KeNumberProcessors;
#else
extern PCCHAR KeNumberProcessors;
#endif
逐步执行它可以为您提供提示。我必须承认
movsx
让我失望了。这就是我认为它应该工作的方式
读取
KeQueryActiveProcessors
指定的地址处的字节。看看它的值是否小于0x40,如果是,则坚持将其解释为字节。如果大于0x40,则只要
KeQueryActiveProcessors
((Little Endian)地址的最低有效字节(也就是说,在XP之前的Windows上已经足够大了,我们可以假定这实际上是一个指针并取消引用它。注意:重新读取它:最低有效字节。因此,与此处无关紧要的不是内核基址中的0x80(内核加载的地址高于0x80000000)。对于Windows 2000,我发现最低有效字节的值如下:
0x70(ntkrnlpa.exe,SP4)
0xF0(ntoskrnl.exe,SP4)
0xC8(ntkrnlpa.exe,RTM)
0x30(ntoskrnl.exe,RTM)
我没有检查任何其他内核版本。这里的问题是,我们已经有一个小于0x40的值。
顺便说一句:通过使字节成为page结构的成员,可以合理地强制执行地址超出0x40的操作-aligned。
现在无论如何都会破坏这种情况的部分是
movsx
(符号扩展)。它会无条件地覆盖CCHAR
的地址,从而创建一个虚假的地址,该地址在所有实际情况下都应导致BSOD。资源ID可以是16位无符号整数,也可以是指向具有资源名称的零终止字符串的指针。#2 楼
根据定义,32位系统不能具有超过64个CPU内核(虚拟或物理)。由于未知的原因,这是对微软方面的硬性限制。可以假定这些限制与内核的实现细节有关,例如CPU内核结构的数量等。由于这是32位驱动程序,因此只能在32位中运行Windows版本。由于Microsoft的限制,驱动程序本身不需要支持64个以上的内核,从而允许/要求驱动程序使用BSOD来处理这些错误情况和意外情况。
KeNumberProcessors
全局变量大于或等于64个CPU内核(或40h),它将执行mov eax, [eax]
指令,该指令将尝试从多个CPU的地址(例如40h
)读取DWORD,这没有任何意义。 > 尽管您的描述大致准确,但还有一个较小的细节使您无法忽略该流程。没有它,该流程将无法正常运行。
这是比较和条件跳转之间的单个汇编指令:
movsx eax, al
。带符号的移动从al
扩展到eax
。这样可以确保EAX不是要读取的有效地址。#3 楼
是的,似乎会有BSOD,但是每个人都幸运的是KeNumberProcessors <= 0x40,所以它永远不会发生。评论
谢谢。还不能更新它:(因此,根据定义,我猜想如果一个系统有64个以上的内核,它们将分为几组,对吗?KeNumberProcessors虽然看起来像是一个用于确定内核数量的旧全局变量(现在但是推荐使用KeQueryActiveProcessors()函数。)但是KeNumberProcessors会在64位计数时中断计数还是该驱动程序中的错误? asm顺序?
–MikeF
18年6月18日在8:18
评论
谢谢。由于声誉而无法投票。想到的一个问题是,movsx符号不会将0x80扩展为'0xFFFFFF80'吗?这也不是一个“好的”内核地址。
–MikeF
18年6月18日在9:23
这才是重点。 movsx似乎是此代码看起来总是生成BSOD的实际原因。在Windows 2000和2003上的调试器中查看并比较这种机制会很有趣。
– 0xC0000022L♦
18年6月18日在9:25
是的我可以使用的最旧的操作系统是VM中的WinXP。虽然,这不是旧驱动程序。下载DbgView时,现在可以从Sysinternals页面上获取它。链接到我的OP中。
–MikeF
18 Jun 18'在9:27