多处理器下内核通过KPCR获取KdVersionBlock指针,以及KdVersionBlock为NULL的问题

Windows C/C++ 浅灬笑 3089℃ 0评论

本文以WinXP为例子,其余版本有可能有不同之处。

KPCR

KPCR 也就是 Kernel Processor Control Region 内核处理器控制区。Windows需要支持多个CPU, 为此Windows内核中定义了一套以处理器控制区,使每个CPU都有个KPCR。其中KPCR这个结构中有一个域KPRCB(Kernel Processor Control Block)结构, 这个结构扩展了KPCR.,这两个结构用来保存与线程切换相关的全局信息。

通常在内核模式下段寄存器FS指向的是KPCR结构,用户模式下指向的是TEB结构。

在Windows XP中KPCR的结构如下:

0: kd> dt _KPCR
nt!_KPCR
 +0x000 NtTib : _NT_TIB
 +0x01c SelfPcr : Ptr32 _KPCR //指向_KPCR自身的指针
 +0x020 Prcb : Ptr32 _KPRCB   //指向_KPRCB的指针
 +0x024 Irql : UChar
 +0x028 IRR : Uint4B
 +0x02c IrrActive : Uint4B
 +0x030 IDR : Uint4B
 +0x034 KdVersionBlock : Ptr32 Void   //指向DBGKD_GET_VERSION64
 +0x038 IDT : Ptr32 _KIDTENTRY
 +0x03c GDT : Ptr32 _KGDTENTRY
 +0x040 TSS : Ptr32 _KTSS
 +0x044 MajorVersion : Uint2B
 +0x046 MinorVersion : Uint2B
 +0x048 SetMember : Uint4B            //当前处理器编号1 2 3 4
 +0x04c StallScaleFactor : Uint4B
 +0x050 DebugActive : UChar
 +0x051 Number : UChar
 +0x052 Spare0 : UChar
 +0x053 SecondLevelCacheAssociativity : UChar
 +0x054 VdmAlert : Uint4B
 +0x058 KernelReserved : [14] Uint4B
 +0x090 SecondLevelCacheSize : Uint4B
 +0x094 HalReserved : [16] Uint4B
 +0x0d4 InterruptMode : Uint4B
 +0x0d8 Spare1 : UChar
 +0x0dc KernelReserved2 : [17] Uint4B
 +0x120 PrcbData : _KPRCB    //KPRCB结构

其中所需要的数据都已经标注,下面来做一个实验看一下KdVersionBlock 的地址变化。

查阅不同处理器下的KPCR获取到的KdVersionBlock 地址(内核下)

获取到当前处理器的数量

0: kd> dd KeNumberProcessors l 1
80556a60 00000004

可以看到当前系统有4个处理器。

获取到相应处理器的_KPRCB的地址

0: kd> dd KiProcessorBlock l4
8055d5a0 ffdff120 ba338120 ba340120 ba348120

从结果种可以看出四个_KPRCB的地址分别为0xffdff120、0xba338120、0xba340120、0xba348120,在前面的_KPCR的结构种已经可以知道,_KPRCB的偏移位置为0x120,所以将得到的_KPRCB的地址减去0x120即可获取到对应处理器的_KPCR地址,为了简单起见,这里就实验两个处理器的数据。

查看处理器 1 的_KPCR数据

0: kd> dt _KPCR 0xffdff120-0x120
nt!_KPCR
 +0x000 NtTib : _NT_TIB
 +0x01c SelfPcr : 0xffdff000 _KPCR       //指向了自身的地址
 +0x020 Prcb : 0xffdff120 _KPRCB         //指向了_KPRCB
 +0x024 Irql : 0 ''
 +0x028 IRR : 0
 +0x02c IrrActive : 0
 +0x030 IDR : 0xffffffff
 +0x034 KdVersionBlock : 0x8054e2b8 Void //KdVersionBlock的地址
 +0x038 IDT : 0x8003f400 _KIDTENTRY
 +0x03c GDT : 0x8003f000 _KGDTENTRY
 +0x040 TSS : 0x80042000 _KTSS
 +0x044 MajorVersion : 1
 +0x046 MinorVersion : 1
 +0x048 SetMember : 1                    //处理器编号 1
 +0x04c StallScaleFactor : 0x95b
 +0x050 DebugActive : 0 ''
 +0x051 Number : 0 ''
 +0x052 Spare0 : 0 ''
 +0x053 SecondLevelCacheAssociativity : 0 ''
 +0x054 VdmAlert : 0
 +0x058 KernelReserved : [14] 0
 +0x090 SecondLevelCacheSize : 0
 +0x094 HalReserved : [16] 0
 +0x0d4 InterruptMode : 0
 +0x0d8 Spare1 : 0 ''
 +0x0dc KernelReserved2 : [17] 0
 +0x120 PrcbData : _KPRCB

从结果种可以看出,处理器1的数据都是完整的,KdVersionBlock 的值不为NULL,并且SelfPcr 、Prcb 所指向的结果都是正确的。

查看处理器 2 的_KPCR数据

0: kd> dt _KPCR 0xba338120-0x120
nt!_KPCR
 +0x000 NtTib : _NT_TIB
 +0x01c SelfPcr : 0xba338000 _KPCR
 +0x020 Prcb : 0xba338120 _KPRCB
 +0x024 Irql : 0 ''
 +0x028 IRR : 0
 +0x02c IrrActive : 0
 +0x030 IDR : 0xffffffff
 +0x034 KdVersionBlock : (null)
 +0x038 IDT : 0xba33c590 _KIDTENTRY
 +0x03c GDT : 0xba33c190 _KGDTENTRY
 +0x040 TSS : 0xba338d70 _KTSS
 +0x044 MajorVersion : 1
 +0x046 MinorVersion : 1
 +0x048 SetMember : 2                     //处理器编号为2
 +0x04c StallScaleFactor : 0x95b
 +0x050 DebugActive : 0 ''
 +0x051 Number : 0x1 ''
 +0x052 Spare0 : 0 ''
 +0x053 SecondLevelCacheAssociativity : 0 ''
 +0x054 VdmAlert : 0
 +0x058 KernelReserved : [14] 0
 +0x090 SecondLevelCacheSize : 0
 +0x094 HalReserved : [16] 1
 +0x0d4 InterruptMode : 0
 +0x0d8 Spare1 : 0 ''
 +0x0dc KernelReserved2 : [17] 0
 +0x120 PrcbData : _KPRCB

从上面的信息可以看出,处理器2种的KdVersionBlock 是null,3 4号处理器的结果省略,经过实验可以看出只有1号处理器种才会记录KdVersionBlock的值,其余的处理器所对应的_KPCR中的KdVersionBlock均为NULL。

分析结果

对于多处理器的环境下,如果要想正确的获取KdVersionBlock的值,必要要保证当前的线程在1号处理器上运行。

KdVersionBlock地址获取代码

PULONG ptr_KPCR=0,
       ptr_KPRCB=0,
       ptr_KDVERSIONBLOCK=0;
 //CPU1
 KeSetSystemAffinityThread(1);
 _asm{
     mov eax , fs:[0x1C]
     mov ptr_KPCR,eax           //指向 _KPCR 指针
     mov eax, fs:[0x20]
     mov ptr_KPRCB , eax        //指向 _KPRCB的指针
     mov eax, fs:[0x34]
     mov ptr_KDVERSIONBLOCK,eax //指向 KdVersionBlock的指针
 }
 KeRevertToUserAffinityThread();

FS段寄存器在内核下指向的是KPCR结构。

MSDN documentation suggests to Windows Vista and later developpers to use KeSetSystemAffinityThreadEx instead of KeSetSystemAffinityThread and to use KeRevertToUserAffinityThreadEx instead of KeRevertToUserAffinityThread.

Even if there is no entry for KeRevertToUserAffinityThread inside the MSDN there is a blogpost from Windows Driver Kit (WDK) Documentation Blog about Windows Kernel Routine Name Conventions that says.

转载请注明:悠然品鉴 » 多处理器下内核通过KPCR获取KdVersionBlock指针,以及KdVersionBlock为NULL的问题

喜欢 (4)or分享 (0)
发表我的评论
取消评论

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址