kernel 模式 (windows x64) 中的 GS 段指向内核处理器控制区域 (KPCR)。
您可以使用!pcr 命令转储它:
kd> !pcr
KPCR for Processor 0 at fffff802fbd73000:
Major 1 Minor 1
NtTib.ExceptionList: fffff802fd6d8000
NtTib.StackBase: fffff802fd6d9070
NtTib.StackLimit: 0000000000b0e968
NtTib.SubSystemTib: fffff802fbd73000
NtTib.Version: 00000000fbd73180
NtTib.UserPointer: fffff802fbd737f0
NtTib.SelfTib: 000000007f005000
SelfPcr: 0000000000000000
Prcb: fffff802fbd73180
Irql: 0000000000000000
IRR: 0000000000000000
IDR: 0000000000000000
InterruptMode: 0000000000000000
IDT: 0000000000000000
GDT: 0000000000000000
TSS: 0000000000000000
CurrentThread: ffffe001e41a3080
NextThread: 0000000000000000
IdleThread: fffff802fbde9740
DpcQueue: Unable to read nt!_KDPC_DATA.DpcListHead.Flink @ fffff802fbd75f00
您可以通过读取名为IA32_GS_BASE(其值为0xc0000101)的MSR(模型特定寄存器)来确认KPCR确实由GS段寄存器指向:
kd> rdmsr 0xc0000101
msr[c0000101] = fffff802`fbd73000
如您所见,在我的示例中,它们都指向 0xfffff802fbd73000。
PCR 由KPCR 结构描述:
kd> dt nt!_kpcr
+0x000 NtTib : _NT_TIB
+0x000 GdtBase : Ptr64 _KGDTENTRY64
+0x008 TssBase : Ptr64 _KTSS64
+0x010 UserRsp : Uint8B
+0x018 Self : Ptr64 _KPCR
+0x020 CurrentPrcb : Ptr64 _KPRCB //points to the _KPRCB member at 0x180
+0x028 LockArray : Ptr64 _KSPIN_LOCK_QUEUE
+0x030 Used_Self : Ptr64 Void
+0x038 IdtBase : Ptr64 _KIDTENTRY64
+0x040 Unused : [2] Uint8B
+0x050 Irql : UChar
+0x051 SecondLevelCacheAssociativity : UChar
+0x052 ObsoleteNumber : UChar
+0x053 Fill0 : UChar
+0x054 Unused0 : [3] Uint4B
+0x060 MajorVersion : Uint2B
+0x062 MinorVersion : Uint2B
+0x064 StallScaleFactor : Uint4B
+0x068 Unused1 : [3] Ptr64 Void
+0x080 KernelReserved : [15] Uint4B
+0x0bc SecondLevelCacheSize : Uint4B
+0x0c0 HalReserved : [16] Uint4B
+0x100 Unused2 : Uint4B
+0x108 KdVersionBlock : Ptr64 Void
+0x110 Unused3 : Ptr64 Void
+0x118 PcrAlign1 : [24] Uint4B
+0x180 Prcb : _KPRCB
如您所见,KPCR 结构的最后一个字段是另一个名为KPRCB(代表内核处理器控制块)的结构(不是指针,而是结构本身),偏移量为 0x180 .
这是这个结构的开始:
kd> dt nt!_kprcb
+0x000 MxCsr : Uint4B
+0x004 LegacyNumber : UChar
+0x005 ReservedMustBeZero : UChar
+0x006 InterruptRequest : UChar
+0x007 IdleHalt : UChar
+0x008 CurrentThread : Ptr64 _KTHREAD
+0x010 NextThread : Ptr64 _KTHREAD
+0x018 IdleThread : Ptr64 _KTHREAD
+0x020 NestingLevel : UChar
+0x021 ClockOwner : UChar
+0x022 PendingTickFlags : UChar
+0x022 PendingTick : Pos 0, 1 Bit
+0x022 PendingBackupTick : Pos 1, 1 Bit
+0x023 IdleState : UChar
+0x024 Number : Uint4B
...
为简洁起见,以上输出被截断,因为此结构(因此 PCR)非常大:Windows 10 x64 中的 PCR 大小为 0x8040 字节(KPRCB 为 0x7EC0)。
鉴于 GS 中的 0x2eb8 偏移量(指向 PCR),我们可以从 PCR (0x180) 中减去 KPRCB 偏移量:
kd> ? 0x2eb8 - 0x180
Evaluate expression: 11576 = 00000000`00002d38
然后检查 KPRCB 中的偏移量 0x2d38 是哪个字段:
0: kd> .shell -ci "dt nt!_kprcb" findstr /i 0x2d38
+0x2d38 KeSystemCalls : Uint4B
(注意:您可以只 dt nt!_kprcb 并查看偏移量 0x2d38)。
因此,示例中的递增字段名为 KeSystemCalls,是一个 32 位字段 (Uint4B),如您的代码所示。
字段使用情况
在 IDA 反汇编程序 (ntoskrnel.exe windows 10 - x64) 中搜索,我们在 0x2eb8 值上有 2 个命中:
KiSystemCall64
VslpDispatchIumSyscall
第一个是“普通”系统调用调度程序,而第二个是IUM processes(又名 Trustlets)的系统调用调度程序。
在这两个函数中,字段的使用完全相同(以 KiSystemCall64 为例):
.text:0000000140187360 call r10 ; perform syscall
.text:0000000140187363
.text:0000000140187363 loc_140187363:
.text:0000000140187363 inc dword ptr gs:2EB8h ; increment syscall counter
所以这个字段只是一个单调计数器,用于记录自系统启动以来发生的系统调用次数。