尝试在 Dbgv.sys 驱动程序中了解此构造以用于 DbgView 工具

逆向工程 视窗 调试 内核模式 核心
2021-06-14 03:56:03

我正在尝试反转DbgView 工具Dbgv.sys(x86 内核驱动程序)它有这个函数,几乎在驱动程序函数的开头就被调用它是这样的:sub_10D4ADriverEntry

sub_10D4A 片段

相关拆解:

.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 的地址读取 DWORD,例如40h,这是没有意义的。

它不会导致蓝屏吗?那里的意图是什么?

3个回答

我仍然认为这会造成 BSOD,而且我认为这是故意的一旦将所有拼图拼凑在一起,假设这是故意的,这是完全有道理的。源代码不兼容将不可避免地迫使开发人员注意到KeNumberProcessorsPCCHAR类型的变化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

那是相同的并且没有任何类似于.sysfor 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 位无符号整数或指向具有资源名称的以零结尾的字符串的指针。

根据定义,32 位系统不能有超过 64 个 CPU 内核(虚拟或物理)。由于未知原因,这是微软方面的硬限制。人们可以假设这些限制与内核的实现细节有关,例如 CPU 内核结构的数量等。

由于这是一个 32 位驱动程序,因此只能在 32 位 Windows 版本中运行。由于 Microsoft 的限制,驱动程序本身不需要支持超过 64 个内核,允许/要求驱动程序处理这些错误情况和意外情况以使用 BSOD。

如果KeNumberProcessors全局变量大于或等于 64 个 CPU 内核(或 40h),它将执行mov eax, [eax]指令,该指令将尝试从多个 CPU 的地址读取 DWORD,例如40h,这是没有意义的。

尽管您的描述大致准确,但还有一个小细节使您无法注意到此流程。没有它,这个流程可能会起作用。

那是比较和条件跳转之间的单个汇编指令:movsx eax, al. 带符号的移动从 aaleax确保EAX 不是一个有效的读取地址。

是的,似乎会有蓝屏死机,但每个人都很幸运拥有 KeNumberProcessors <= 0x40,所以它永远不会发生。