ring3下,fs寄存器的指向TEB,ring0下,fs寄存器指向KPCR,KPCR即Kernel Processor Control Region,处理器控制区域,因为Windows是需要支持多个CPU的,所以为每一个CPU在内核空间安排一个KPCR的结构。
KPCR结构如下:
nt!_KPCR
+0x000 NtTib : _NT_TIB
+0x01c SelfPcr : Ptr32 _KPCR ;指向自己的指针
+0x020 Prcb : Ptr32 _KPRCB ;指向Prcb在该结构体的位置的指针
+0x024 Irql : UChar
+0x028 IRR : Uint4B
+0x02c IrrActive : Uint4B
+0x030 IDR : Uint4B
+0x034 KdVersionBlock : Ptr32 Void ;指向DBGKD_GET_VERSION64的结构的指针
+0x038 IDT : Ptr32 _KIDTENTRY ;IDT的基址
+0x03c GDT : Ptr32 _KGDTENTRY ;GDT的基址
+0x040 TSS : Ptr32 _KTSS ;TSS段的基址
+0x044 MajorVersion : Uint2B
+0x046 MinorVersion : Uint2B
+0x048 SetMember : Uint4B
+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的结构
ntdll!_KPRCB
+0x000 MinorVersion : Uint2B
+0x002 MajorVersion : Uint2B
+0x004 CurrentThread : Ptr32 _KTHREAD
+0x008 NextThread : Ptr32 _KTHREAD
+0x00c IdleThread : Ptr32 _KTHREAD
+0x010 Number : Char
+0x011 Reserved : Char
+0x012 BuildType : Uint2B
+0x014 SetMember : Uint4B
+0x018 CpuType : Char
+0x019 CpuID : Char
+0x01a CpuStep : Uint2B
+0x01c ProcessorState : _KPROCESSOR_STATE
+0x33c KernelReserved : [16] Uint4B
+0x37c HalReserved : [16] Uint4B
+0x3bc PrcbPad0 : [92] UChar
+0x418 LockQueue : [16] _KSPIN_LOCK_QUEUE
+0x498 PrcbPad1 : [8] UChar
+0x4a0 NpxThread : Ptr32 _KTHREAD
+0x4a4 InterruptCount : Uint4B
+0x4a8 KernelTime : Uint4B
+0x4ac UserTime : Uint4B
+0x4b0 DpcTime : Uint4B
+0x4b4 DebugDpcTime : Uint4B
+0x4b8 InterruptTime : Uint4B
+0x4bc AdjustDpcThreshold : Uint4B
+0x4c0 PageColor : Uint4B
+0x4c4 SkipTick : Uint4B
+0x4c8 MultiThreadSetBusy : UChar
+0x4c9 Spare2 : [3] UChar
+0x4cc ParentNode : Ptr32 _KNODE
+0x4d0 MultiThreadProcessorSet : Uint4B
+0x4d4 MultiThreadSetMaster : Ptr32 _KPRCB
+0x4d8 ThreadStartCount : [2] Uint4B
+0x4e0 CcFastReadNoWait : Uint4B
+0x4e4 CcFastReadWait : Uint4B
+0x4e8 CcFastReadNotPossible : Uint4B
+0x4ec CcCopyReadNoWait : Uint4B
+0x4f0 CcCopyReadWait : Uint4B
+0x4f4 CcCopyReadNoWaitMiss : Uint4B
+0x4f8 KeAlignmentFixupCount : Uint4B
+0x4fc KeContextSwitches : Uint4B
+0x500 KeDcacheFlushCount : Uint4B
+0x504 KeExceptionDispatchCount : Uint4B
+0x508 KeFirstLevelTbFills : Uint4B
+0x50c KeFloatingEmulationCount : Uint4B
+0x510 KeIcacheFlushCount : Uint4B
+0x514 KeSecondLevelTbFills : Uint4B
+0x518 KeSystemCalls : Uint4B
+0x51c SpareCounter0 : [1] Uint4B
+0x520 PPLookasideList : [16] _PP_LOOKASIDE_LIST
+0x5a0 PPNPagedLookasideList : [32] _PP_LOOKASIDE_LIST
+0x6a0 PPPagedLookasideList : [32] _PP_LOOKASIDE_LIST
+0x7a0 PacketBarrier : Uint4B
+0x7a4 ReverseStall : Uint4B
+0x7a8 IpiFrame : Ptr32 Void
+0x7ac PrcbPad2 : [52] UChar
+0x7e0 CurrentPacket : [3] Ptr32 Void
+0x7ec TargetSet : Uint4B
+0x7f0 WorkerRoutine : Ptr32 void
+0x7f4 IpiFrozen : Uint4B
+0x7f8 PrcbPad3 : [40] UChar
+0x820 RequestSummary : Uint4B
+0x824 SignalDone : Ptr32 _KPRCB
+0x828 PrcbPad4 : [56] UChar
+0x860 DpcListHead : _LIST_ENTRY
+0x868 DpcStack : Ptr32 Void
+0x86c DpcCount : Uint4B
+0x870 DpcQueueDepth : Uint4B
+0x874 DpcRoutineActive : Uint4B
+0x878 DpcInterruptRequested : Uint4B
+0x87c DpcLastCount : Uint4B
+0x880 DpcRequestRate : Uint4B
+0x884 MaximumDpcQueueDepth : Uint4B
+0x888 MinimumDpcRate : Uint4B
+0x88c QuantumEnd : Uint4B
+0x890 PrcbPad5 : [16] UChar
+0x8a0 DpcLock : Uint4B
+0x8a4 PrcbPad6 : [28] UChar
+0x8c0 CallDpc : _KDPC
+0x8e0 ChainedInterruptList : Ptr32 Void
+0x8e4 LookasideIrpFloat : Int4B
+0x8e8 SpareFields0 : [6] Uint4B
+0x900 VendorString : [13] UChar
+0x90d InitialApicId : UChar
+0x90e LogicalProcessorsPerPhysicalProcessor : UChar
+0x910 MHz : Uint4B
+0x914 FeatureBits : Uint4B
+0x918 UpdateSignature : _LARGE_INTEGER
+0x920 NpxSaveArea : _FX_SAVE_AREA
+0xb30 PowerState : _PROCESSOR_POWER_STATE
+0x01c SelfPcr : Ptr32 _KPCR指向自身的一个指针
+0x034 KdVersionBlock : Ptr32 Void比较重要,它指向DBGKD_GET_VERSION64
+0x120 PrcbData : _KPRCB,KPCR的最后一项,全名为Kernel Processor Control Block,是KPCR的一个扩展。KPCR和KPRCB这两个结构保存与线程切换相关的全局信息。
nt!_DBGKD_GET_VERSION64
+0x000 MajorVersion : Uint2B ;主版本
+0x002 MinorVersion : Uint2B ;次版本号
+0x004 ProtocolVersion : Uint2B
+0x006 Flags : Uint2B
+0x008 MachineType : Uint2B
+0x00a MaxPacketType : UChar
+0x00b MaxStateChange : UChar
+0x00c MaxManipulate : UChar
+0x00d Simulation : UChar
+0x00e Unused : [1] Uint2B
+0x010 KernBase : Uint8B
+0x018 PsLoadedModuleList : Uint8B ;内核所有加载链表的一个指针,这里为表头,指向一个KLDR_DATA_TABLE_ENTRY
+0x020 DebuggerDataList : Uint8B
PsLoadedModuleList 是 Windows加载的所有内核模块构成的链表的表头的指针。其内容指向KLDR_DATA_TABLE_ENTRY结构,跟LBR遍历模块原理一致,有兴趣的可以移步去看。
可以通过KeNumberProcessors,KiProcessorBlock两个全局变量查看KPCR的信息
CCHAR KeNumberProcessors;
PKPRCB KiProcessorBlock[MAXIMUM_PROCESSORS];
我给虚拟机只分配了一个cpu,所以它这里只显示一个
查看PrcbData的地址,这里是ffdff120,这里要减去0x120,因为现在是指向Prcb的指针。
得到了ntoskrnl.exe的基地址。如果在64位上调试,则会出现ntkrnlpa.exe,因为ntkrnlpa.exe是ntoskrnl.exe的PAE版本。