KPCR与进程
KPCR
- (Kernel) Processor Control Region
- a KPCR for each logical processor
- KPCR is the means through which the kernel manages them(processors).
- 由于Windows需要支持多个CPU, 因此Windows内核中为此定义了一套以处理器控制区(Processor Control Region)即KPCR为枢纽的数据结构, 使每个CPU都有个KPCR. 其中KPCR这个结构中有一个域KPRCB(Kernel Processor Control Block)结构, 这个结构扩展了KPCR. 这两个结构用来保存与线程切换相关的全局信息.
- 在和进程、线程相关的数据结构中,进程控制块和线程控制块是关键,通过这两个数据结构可以按图索骥找到其余的有关数据结构。Windows提供了一系列的获取进程控制块或者线程控制块的函数,
- In 32-bit Windows, entering kernel mode gets fs loaded with a GDT selector (0x0030) for a segment whose base address is that of the processor’s KPCR.
- The KPCR conveniently holds its own address in the SelfPcr or Self member, in 32-bit and 64-bit Windows respectively, so that reading just this one member using a segment register override makes the whole KPCR accessible without overrides(就是说KPCR提供了一个很方便的方式来获取其中的内容)
- Beware, though, that this is the address of the KPCR for the processor that the thread was running on at the time: it remains the address of the current KPCR only while the thread can ensure it is not switched to another processor. (所以就是说每一个进程都会有一个KPCR;除非是线程保证不会转移到其他的processor,才会保持当前的KPCR不变)
- Before version 6.0, space for the boot processor’s KPCR is allocated by the loader at the fixed address 0xFFDFF000.
KPCR与进程
在和进程、线程相关的数据结构中,进程控制块和线程控制块是关键,通过这两个数据结构可以按图索骥找到其余的有关数据结构。Windows提供了一系列的获取进程控制块或者线程控制块的函数
首先Windows内核提供了一个函数KeGetCurrentThread函数,用来或者当前线程的KTHREAD结构指针,这个函数的实现方式将在后文中介绍,windows其他获取控制块的函数都是以此函数为基础进行封装的。
1.KeGetCurrentThread – - > PsGetCurrentThread
后者只是前者定义的一个宏。并加上了强制类型转换,因为ETHREAD的一个成员是KTHREAD结构,所以两者的首地址相同。
#define PsGetCurrentThread() ((PETHREAD)KeGetCurrentThread())
2.PsGetCurrentThread – - >IoGetCurrentProcess
IoGetCurrentProcess是以PsGetCurrentThread为基础,封装的函数。
这样,在“常态”下IoGetCurrentProcess所返回的是当前线程所属进程的EPROCESS结构指针,而在挂靠状态下则所返回的是当前线程所挂靠进程的EPROCESS结构指针。
3.IoGetCurrentProcess – - > PsGetCurrentProcess
后者是前者定义的一个宏。
#defind PsGetCurrentProcess IoGetCurrentProcess
4.PsGetCurrentProcess – - > KeGetCurrentProcess
KeGetCurrentProcess是以PsGetCurrentProcess为基础封装的函数,返回值为KPROCESS指针。
PsGetCurrentThread –>IoGetCurrentProcess
KTHREAD(KPROCESS)有是ETHREAD(EPROCESS)结构中的第一个元素,所以两者的地址是一样的
thread和process之间的关系就是挂靠关系,也就是给一个thread可以找到他所依附的process的地址信息。
ETHREAD.Tcb.ApcState.Process指向了该线程对应(或者挂靠)的进程。
ETHREAD.Tcb指向的就是该线程的KTHREAD结构。
内核如何获得当前运行线程的KTHREAD结构
Windows内核中,为每个CPU分配了一套“处理器控制区(Processor Control Region)”,简称KPCR结构。该结构中存放着与线程切换相关的全局信息,包括所需的寄存器和其他的数据结构。
KPCR第三个变量是指向一个“处理器控制块”,即KPRCB数据结构.
KPRCB结构中的第三个成员变量是KTHREAD类型的CurrentThread变量,该变量指向了当前线程的KTHREAD数据结构.
当CPU运行于内核中时,段寄存器FS(所选择的段描述块)总是指向该CPU的KPCR结构的起点,而KPCR中位移为0x1c的字段为Self,指向其所在的KPCR结构的起点。找到CPU的KPCR结构,就可以找到其KPCRB结构,并进而找到其CurrentThread了。
KTHREAD(KPROCESS)有是ETHREAD(EPROCESS)结构中的第一个元素,所以两者的地址是一样的
thread和process之间的关系就是挂靠关系,也就是给一个thread可以找到他所依附的process的地址信息。
ETHREAD.Tcb.ApcState.Process指向了该线程对应(或者挂靠)的进程。
ETHREAD.Tcb指向的就是该线程的KTHREAD结构。
KPCR—KPCRB----current_thread
typedef struct _KPCR {
KPCR_TIB Tib; /* 00 */
struct _KPCR *Self; /* 1C */
struct _KPRCB *Prcb; /* 20 */
KIRQL Irql; /* 24 */
ULONG IRR; /* 28 */
ULONG IrrActive; /* 2C */
ULONG IDR; /* 30 */
PVOID KdVersionBlock; /* 34 */
PUSHORT IDT; /* 38 */
PUSHORT GDT; /* 3C */
struct _KTSS *TSS; /* 40 */
USHORT MajorVersion; /* 44 */
USHORT MinorVersion; /* 46 */
KAFFINITY SetMember; /* 48 */
ULONG StallScaleFactor; /* 4C */
UCHAR DebugActive; /* 50 */
UCHAR ProcessorNumber; /* 51 */
UCHAR Reserved; /* 52 */
UCHAR L2CacheAssociativity; /* 53 */
ULONG VdmAlert; /* 54 */
ULONG KernelReserved[14]; /* 58 */
ULONG L2CacheSize; /* 90 */
ULONG HalReserved[16]; /* 94 */
ULONG InterruptMode; /* D4 */
UCHAR KernelReserved2[0x48]; /* D8 */
KPRCB PrcbData; /* 120 */
} KPCR, *PKPCR;
/* ProcessoR Control Block */
typedef struct _KPRCB {
USHORT MinorVersion;
USHORT MajorVersion;
struct _KTHREAD *CurrentThread;
struct _KTHREAD *NextThread;
struct _KTHREAD *IdleThread;
. . . . . .
UCHAR CpuType;
UCHAR CpuID;
USHORT CpuStep;
KPROCESSOR_STATE ProcessorState;
. . . . . .
PVOID LockQueue[33]; // Used for Queued Spinlocks
struct _KTHREAD *NpxThread;
ULONG InterruptCount;
ULONG KernelTime;
ULONG UserTime;
. . . . . .
struct _KEVENT *DpcEvent;
UCHAR ThreadDpcEnable;
BOOLEAN QuantumEnd;
. . . . . .
LONG MmPageReadCount;
LONG MmPageReadIoCount;
LONG MmCacheReadCount;
LONG MmCacheIoCount;
LONG MmDirtyPagesWriteCount;
. . . . . .
FX_SAVE_AREA NpxSaveArea;
PROCESSOR_POWER_STATE PowerState;
} KPRCB, *PKPRCB;
kd> dt _KPRCB
nt!_KPRCB
+0x000 MinorVersion : Uint2B
+0x002 MajorVersion : Uint2B
+0x004 CurrentThread : Ptr32 _KTHREAD
+0x008 NextThread : Ptr32 _KTHREAD
+0x00c IdleThread : Ptr32 _KTHREAD
kd> dt _KTHREAD
nt!_KTHREAD
+0x000 Header : _DISPATCHER_HEADER
+0x010 MutantListHead : _LIST_ENTRY
+0x018 InitialStack : Ptr32 Void
+0x01c StackLimit : Ptr32 Void
+0x020 Teb : Ptr32 Void
+0x024 TlsArray : Ptr32 Void
+0x028 KernelStack : Ptr32 Void
+0x02c DebugActive : UChar
+0x02d State : UChar
+0x02e Alerted : [2] UChar
+0x030 Iopl : UChar
+0x031 NpxState : UChar
+0x032 Saturation : Char
+0x033 Priority : Char
+0x034 ApcState : _KAPC_STATE
kd> dt _KAPC_STATE
nt!_KAPC_STATE
+0x000 ApcListHead : [2] _LIST_ENTRY
+0x010 Process : Ptr32 _KPROCESS
+0x014 KernelApcInProgress : UChar
+0x015 KernelApcPending : UChar
+0x016 UserApcPending : UChar
展开KPRCB结构继续观察可以看到FS:[0x124]指向了KTHREAD结构
fs寄存器在Ring0中指向一个称为KPCR的数据结构,即FS段的起点与KPCR结构对齐。而在Ring0中fs寄存器一般为0x30
这样fs:[124]就指向KPRCB数据结构的第四个字节。由于KPRCB比较大,再此就不列出来了。查看其数据结构可以看到第四个字节指向CurrentThead(KTHREAD类型)。这样fs:[124]其实是指向当前线程的_KTHREAD。
一些名词
TSS
a Task State Segment (TSS), defining the current task on the processor;
ETHREAD
an ETHREAD, representing the system thread for the processor’s initial execution;
GDT
the Global Descriptor Table (GDT) for the processor;
IDT
the Interrupt Descriptor Table (IDT) for the processor.