我正在尝试反转DbgView工具的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位无符号整数,也可以是指向具有资源名称的零终止字符串的指针。

评论


谢谢。由于声誉而无法投票。想到的一个问题是,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



#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