KPCR
KPCR:CPU控制区(Process Control Region)
在逆向操作系统内核中存在三个关键的结构体,
分别是EPRCOESS(进程结构体)、ETHREAD(线程结构体)、KPCR(CPU控制区)
每一个CPU都有一个KPCR结构体用于存储一些数据。
KPCR类似于线程结构体的一个副本用于快速查询。
KPCR介绍
1.当线程进入0环时,FS:[0]指向KPCR(3环时指向FS:[0]->TEB)
2.每个CPU都有一个KPCR结构体(一个核心一个)
3.KPCR中存储了CPU本身要用的一些重要数据:GDT/IDT以及线程相关的信息。
KPCR重要成员
+0x00 NtTibe :_NT_TIB(跟3环TEB成员时一样的)
NtTib->0x00 ExceptionList:Ptr32_EXCEPTION_REGISTRATION_RECORD(0环当前线程内核异常链表SEH)
3环中FS是3环的0环是0环自己的。
+0x01c SelfPcr :Ptr32_KPCR
指向自己,方便寻址
+0x020 Prcb :Ptr32_KPRCB
指向拓展结构体PRCB
+0x038 IDT :Ptr32_KIDTENTRY
IDT表基址
+0x03c GDT :Ptr32_KGDTENTRY
GDT表基址
需要注意的GDT和IDT每个CPU核心都有一套。
+0x040 TSS :Ptr32_KTSS
指针,指向TSS,每个CPU都有一个TSS
+0x51 Number :UChar
CPU编号:0 1 2 3 4 5 6.....
+0x120 PrcbData :_KPRCB
_KPRCB+0x004 CurrentThread:Ptr32_KTHREAD 当前CPU核心执行的线程
_KPRCB+0x008 NextThread:Ptr32__KTHREAD 当线程切换后执行的线程
_KPRCB+0x00C IdleThread:Ptr32_KTHREAD 如果没有线程要执行的空闲线程。
等待链表_调用链表
进程断链和线程断链
对于进程断链,程序可以正常运行,原因是CPU执行与调度是基于线程的,
进程断链只是影响一些遍历系统进程的API,并不会影响程序执行。
对线程断链也是一样的,断链后Windbg或者OD中无法看到被断掉的线程,
但并不影响其被执行(仍然再跑)。
等待链表/调度链表
等待链表
windbg:dd KiWaitListHead
比如:线程调用了Sleep或者WaitForSingleObject等待函数时,
就挂到这个链表(查看等待线程)。
线程总共有3种状态分别是:阻塞、等待(随时要跑处于调度状态)、运行
33个链表:
正在运行的线程存储在KPCR中,就绪和等待的线程全在另外的33个链表中。
一个等待链表,32个就绪链表。
这些链表都使用了_KTHREAD(0X60)这个位置,也就是说,线程在某一时刻,
只能属于其中一个圈。
查看调度链表(也成为了就绪链表)
既然有32个链表,就要有32个链表头。
dd KiDispatcherReadyListHead L70
之所以要有32个调度链表是因为线程是有线程级别的,
每一个双向链表存储了不同等级的线程链表,比如0级线程级别
,1级线程级别,2级线程级别都是调度链表。
通过调度线程链表可以找到当前操作系统所有的等待线程。
破解ETHREAD和EPROCESS断链的进程或线程进程技术
通过断链方式的ETHREAD和EPROCESS,
虽然在windbg或OD中无法找到蛛丝马迹,但是可以通过
遍历所有调度链表找到被断链隐藏线程或者通过对应的,
调度链表成员偏移找到进程名称找到被隐藏的进程。
版本差异
XP只有33个圈,也就是说上面的数组只有一个,多核也只有一个。
Win7也是一样只有一个圈,如果是64位,那就是64和圈
服务器版本:
KiWaitListHead(等待链表)整个系统只有一个,但KiDispatcherReadyListHead(调度链表)整个数组
有几个CPU就有几组。
总结
1.正在运行的线程在KPCR中
2.准备运行的线程在32个调度链表中(0-31级),KiDispatcherReadyListHead是个数组存储了32个链表头。
3.等待状态的线程存储在等待链表中,KiWaitListHead存储链表头。
4.这些圈都挂一个相同的位置:KTHREAD(0x60)