Windows内核:进程和线程
进程结构体
EPROCESS结构体
+0x000 Pcb : _KPROCESS
- +0x018 DirectoryTableBase : [2] Uint4B 进程页目录表基址
- +0x038 KernelTime : Uint4B 0环运行时间
- +0x03c UserTime : Uint4B 三环运行时间
- +0x05c Affinity : Uint4B 规定该进程可以跑在哪个cpu上 比如11B表示可以再0,1CPU上跑 100B表示只能在2号CPU上跑
- +0x062 BasePriority : Char 该进程所有线程最起码的优先级
+0x070 CreateTime : _LARGE_INTEGER 进程创建时间
+0x078 ExitTime : _LARGE_INTEGER 进程退出时间
+0x084 UniqueProcessId : Ptr32 Void 进程的编号(任务管理器中的PID)
+0x088 ActiveProcessLinks : _LIST_ENTRY 所有的活动进程连接在一起构成双向链表 PsActiveProcessHead全局符号指向链表头部,需要注意的是这里链表节点指向的都是EPROCESS+0x88的位置
- ```c
_LIST_ENTRY
+0x000 Flink : Ptr32 _LIST_ENTRY//指向下一结点,尾部结点的Flink则指向头部
+0x004 Blink : Ptr32 _LIST_ENTRY//Blink指向前一结点,首部节点指向尾部结点
![image-20231109115500052](Windows-Kernel-X86-process-thread/image-20231109115500052.png)
* +0x0bc DebugPort : Ptr32 Void +0x0c0 ExceptionPort : Ptr32 Void 调试相关
* +0x0c4 ObjectTable : Ptr32 _HANDLE_TABLE 句柄表,记录了被当前进程打开的所有内核对象句柄,是用来标识某个内核对象的一个`id`,同一个对象的该`id`对于每个进程是不同的。
* +0x11c VadRoot : Ptr32 Void 一颗平衡二叉树,标识0-2G哪些地址没占用了,记录了模块映射信息。
* +0x174 ImageFileName : [16] UChar 进程镜像文件名
* +0x1a0 ActiveThreads : Uint4B 当前进程活动线程的数量
* +0x1b0 Peb : Ptr32 _PEB 进程在3环的一个结构体,里面包含了进程的模块列表、是否处于调试状态等信息,减少频繁的进入内核层查询数据
* +0x002 BeingDebugged : UChar 当前进程被调试为1,否则为0
* +0x008 ImageBaseAddress : Ptr32 Void 进程基址
* +0x00c Ldr : Ptr32 _PEB_LDR_DATA 当前进程加载模块的相关信息
## 线程结构体
**ETHREAD结构体**
* +0x000 Tcb : _KTHREAD
* +0x018 InitialStack : Ptr32 Void 当前线程栈底
+0x01c StackLimit : Ptr32 Void 当前线程栈边界(栈可以到达的最低点)
+0x028 KernelStack : Ptr32 Void 当前线程栈顶
上面三个成员都与线程切换相关
* +0x020 Teb : Ptr32 Void 线程环境块 大小4KB,位于用户地址空间 用户态fs:0指向Teb
* +0x02c DebugActive : UChar 如果值为-1,将不能使用调试寄存器:Dr0 - Dr7
* +0x034 ApcState : _KAPC_STATE
+0x0e8 ApcQueueLock : Uint4B
+0x138 ApcStatePointer : [2] Ptr32 _KAPC_STATE
+0x14c SavedApcState : _KAPC_STATE//与APC相关
* +0x02d State : UChar 线程状态:就绪/等待/运行 waiting/ready/running
* +0x06c BasePriority : Char 初始值是所属进程的BasePriority值(KPROCESS->BasePriority),可以通过KeSetBasePriorityThread()函数重新设定
* +0x070 WaitBlock : [4] _KWAIT_BLOCK 当前线程在进行同步时等待了哪些个对象(WaitForSingleObject)
* +0x0e0 ServiceTable : Ptr32 Void 系统服务表基址
* +0x134 TrapFrame : Ptr32 _KTRAP_FRAME 保存进0环时的环境
* +0x140 PreviousMode : Char 先前模式,指示程序是0环调用还是3环调用的。某些内核函数会判断程序是0环调用还是3环调用的
* +0x1b0 ThreadListEntry : _LIST_ENTRY **和** **ETHREAD** +0x22c ThreadListEntry : _LIST_ENTRY 一个进程所有的线程都挂在这个链表中,两个链表是一样的(之所以有两个只是为了方便),验证两个链表是否是同一个时要注意链表节点指向结构体的偏移不同(对于ETHREAD结构体第一个链表减去0x1B0得到ETHREAD开头,第二个链表需要减去0x22c)
![image-20231109194134624](Windows-Kernel-X86-process-thread/image-20231109194134624.png)
* +0x1ec Cid : _CLIENT_ID
* ```c
_CLIENT_ID
+0x000 UniqueProcess : Ptr32 Void //进程ID
+0x004 UniqueThread : Ptr32 Void //线程ID
- ```c
+0x220 ThreadsProcess : Ptr32 _EPROCESS 指向自己所属进程的EPROCESS结构体地址
CPU控制区结构体
KPCR结构体 内核态fs:0指向KPCR 一个核一个kpcr,存储了CPU
常用数据的副本(主要是为了快),用的时候直接查kpcr,而不是线程进程结构体,每次切换线程时会把线程信息更新到当前kpcr结构体里
+0x000 NtTib : _NT_TIB
+0x000 ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD//当前线程内核异常链表
+0x004 StackBase : Ptr32 Void//当前线程内核栈的基址
+0x008 StackLimit : Ptr32 Void//当前线程栈边界(栈可以到达的最低点)
+0x00c SubSystemTib : Ptr32 Void
+0x010 FiberData : Ptr32 Void
+0x010 Version : Uint4B
+0x014 ArbitraryUserPointer : Ptr32 Void
+0x018 Self : Ptr32 _NT_TIB//指向NtTib自己,类似于c++的this指针+0x01c SelfPcr : Ptr32 _KPCR 指向KPCR自己,类似于c++的this指针
+0x020 Prcb : Ptr32 _KPRCB 指向下面拓展结构体PrcbData的指针
+0x120 PrcbData : _KPRCB
+0x038 IDT : Ptr32 _KIDTENTRY IDT表基址
+0x03c GDT : Ptr32 _KGDTENTRY GDT表基址
+0x040 TSS : Ptr32 _KTSS 指向
TSS
的指针,每个CPU
都有一个TSS
。+0x051 Number : UChar CPU编号
+0x120 PrcbData : _KPRCB
- +0x004 CurrentThread : Ptr32 _KTHREAD 当前正在运行线程结构体指针
- +0x008 NextThread : Ptr32 _KTHREAD 即将切换的下一个线程结构体指针
- +0x00c IdleThread : Ptr32 _KTHREAD 如果没有要即将切换线程要跑的空闲线程结构体指针
kd> dd KiProcessorBlock l1 |
等待链表与调度链表
等待链表
对EPROCESS和ETHREAD结构体进行进程断链或者线程断链,只是遍历系统进程的API遍历不到了,但是进程线程依旧可以执行,对于进程来说CPU执行与调度是基于线程的,对于线程说明CPU调度线程的时候压根不用这些链表
等待链表由全局变量KiWaitListHead指定位置存储链表,指向的位置就是ETHREAD.KTHREAD+0x060 WaitListEntry**:**_LIST_ENTRY
kd> dd KiWaitListHead |
调度链表
调度链表有32个圈,圈中包括正在运行的和准备运行的。,就是优先级是0-31
,0为最低优先级,31为最高,默认优先级一般是8。