一、内容回顾
内容回顾
进程在内核中对应结构体:EPROCESS
线程在内核中对应结构体:ETHREAD
CPU在内核中也有一个对应的结构体:KPCR
二、KPCR结构
kd> dt _KPCR
nt!_KPCR
+0x000 NtTib : _NT_TIB
+0x01c SelfPcr : Ptr32 _KPCR
+0x020 Prcb : Ptr32 _KPRCB
+0x024 Irql : UChar
+0x028 IRR : Uint4B
+0x02c IrrActive : Uint4B
+0x030 IDR : Uint4B
+0x034 KdVersionBlock : Ptr32 Void
+0x038 IDT : Ptr32 _KIDTENTRY
+0x03c GDT : Ptr32 _KGDTENTRY
+0x040 TSS : Ptr32 _KTSS
+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
kd> dt _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
三、重要成员介绍
1、KPCR介绍
-
当线程进入0环时,FS:[0]指向KPCR(3环时FS:[0] -> TEB)
-
每个CPU都有一个KPCR结构体(一个核一个)
-
KPCR中存储了CPU本身要用的一些重要数据:GDT、IDT以及线程相关的一些信息。
struct _NT_TIB
{
+0x000 ExceptionList : Ptr32_EXCEPTION_REGISTRATION_RECORD //当前线程内核异常链表(SEH),但你使用try/except语句会往这里加一个
+0x004 StackBase : Ptr32 Void
+0x008 StackLimit : Ptr32 Void
//当前线程内核栈的基址和大小
+0x018 Self : Ptr32 _NT_TIB //指向自己(也就是指向KPCR结构) 这样设计的目的是为了查找方便,
}
struct KPCR
{
+0x01c SelfPcr : Ptr32 _KPCR
//指向自己,方便寻址,通过IDA分析可以知道不少系统dll都通过fs[0x1c]来指向自己
+0x020 Prcb : Ptr32 _KPRCB
//指向拓展结构体PRCB
+0x038 IDT : Ptr32 _KIDTENTRY
//IDT表基址
+0x03c GDT : Ptr32 _KGDTENTRY
//GDT表基址
+0x040 TSS : Ptr32 _KTSS
//指针,指向TSS,每个CPU都有一个TSS.
+0x051 Number : UChar
//CPU编号:0 1 2 3 4 5。。。
+0x120 PrcbData : _KPRCB
//拓展结构体
}
struct KPRCB
{
+0x004 CurrentThread : Ptr32 _KTHREAD // 当前线程
+0x008 NextThread : Ptr32 _KTHREAD // 即将切换的下一个线程
+0x00c IdleThread : Ptr32 _KTHREAD // 空闲线程 这个类型应该是一个函数,指向IdleThread函数
}
四、等待/调度链表
上一节课我们讲了进程结构体EPROCESS(0x50和0x190)是2个链表,里面圈着当前进程所有的线程。
对进程断链,程序可以正常运行,原因是CPU执行与调度是基于线程的,进程断链只是影响一些遍历系统进程的API,并不会影响程序执行。
对线程断链也是一样的,断链后在Windbg或者OD中无法看到被断掉的线程,但并不影响其执行(仍然再跑)。
为什么线程断链之后程序还在运行?理由很简单,因为操作系统没有使用这个链表来调度线程,这个链表只是给我们看的,不是给系统看的!
1、33个链表
线程有3种状态:就绪、等待、运行
正在运行中的线程存储在KPCR中,就绪和等待的线程全在另外的33个链表中。
一个等待链表,32个就绪链表:
这些链表都使用了_KTHREAD(0x060)这个位置,也就是说,线程在某一时刻,只能属于其中一个圈。
2、等待链表
kd> dd KiWaitListHead
比如:线程调用了Sleep() 或者 WaitForSingleObject()等函数时,就挂到这个链表
3、调度链表
调度链表有32个圈,就是优先级:0 - 31 0最低 31最高 默认优先级一般是8
改变优先级就是从一个圈里面卸下来挂到另外一个圈上
这32个圈是正在调度中的线程:包括正在运行的和准备运行的
比如:只有一个CPU但有10个线程在运行,那么某一时刻,正在运行的线程在KPCR中,其他9个在这32个圈中。
4、查看调度链表
既然有32个链表,就要有32个链表头。
kd> dd KiDispatcherReadyListHead L70
5、版本差异
XP只有一个33个圈,也就是说上面这个数组只有一个,多核也只有一个.
Win7也是一样的只有一个圈,如果是64位的,那就有64个圈.
服务器版本:KiWaitListHead整个系统只有一个,但KiDispatcherReadyListHead这个数组有几个CPU就有几组
6、总结:
1、正在运行的线程在KPCR中
2、准备运行的线程在32个调度链表中(0 - 31级),KiDispatcherReadyListHead 是个数组存储了这32个链表头.
3、等待状态的线程存储在等待链表中,KiWaitListHead存储链表头.
4、这些圈都挂一个相同的位置:_KTHREAD(0x060)