Windows XP 进程线程(一)

ʕ •ᴥ•ʔ ɔ:

1 进程与线程

1.1 进程线程说明

3期时就已经知道,进程实际上就是提供一个空间,线程是进程中一个个运行起来跑起来的实例(一个进程得到CPU执行时一行行运行的代码就是线程在跑)。进程有了线程才有活力,否则啥也不是。

本节内容可参考:Windows内核学习笔记之进程(上)

从0环的角度来看,无论是进程还是线程,就是两个结构体操作系统创建进程或线程时,就是分配一块内存,然后填充结构体,进程线程也就创建出来了。所以一定要对进程/线程结构体进行了解。

进程最重要的就是提供了一个CR3,分配线性地址空间。

进程的切换:实质上是线程的切换。进程就提供了一个CR3。A:x,y,z,B:m,n(AB进程,xyzmn线程)。x、z线程切换不会切换CR3的值,但是x、m线程切换时会切换CR3的值。本来是没有进程切换这种说法的,但是CR3切换时也就叫做进程切换了。 attach 和 .process 本质也是切换CR3。

OpenProcess得到某进程结构体Eprocess,但是不能让3环使用,所以返回一个句柄(结构体EPROCESS临时的编号),也就得到句柄值。

1.2 _EPROCESS结构

EPROCESS称为进程结构体,每个Windows进程在0环都有一个对应的结构体:EPROCESS ,这个结构体包含了进程所有重要的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
kd> dt _EPROCESS
nt!_EPROCESS
+0x000 Pcb : _KPROCESS
+0x06c ProcessLock : _EX_PUSH_LOCK
+0x070 CreateTime : _LARGE_INTEGER
+0x078 ExitTime : _LARGE_INTEGER
+0x080 RundownProtect : _EX_RUNDOWN_REF
+0x084 UniqueProcessId : Ptr32 Void
+0x088 ActiveProcessLinks : _LIST_ENTRY
+0x090 QuotaUsage : [3] Uint4B
+0x09c QuotaPeak : [3] Uint4B
+0x0a8 CommitCharge : Uint4B
+0x0ac PeakVirtualSize : Uint4B
+0x0b0 VirtualSize : Uint4B
+0x0b4 SessionProcessLinks : _LIST_ENTRY
+0x0bc DebugPort : Ptr32 Void
+0x0c0 ExceptionPort : Ptr32 Void
+0x0c4 ObjectTable : Ptr32 _HANDLE_TABLE
+0x0c8 Token : _EX_FAST_REF
+0x0cc WorkingSetLock : _FAST_MUTEX
+0x0ec WorkingSetPage : Uint4B
+0x0f0 AddressCreationLock : _FAST_MUTEX
+0x110 HyperSpaceLock : Uint4B
+0x114 ForkInProgress : Ptr32 _ETHREAD
+0x118 HardwareTrigger : Uint4B
+0x11c VadRoot : Ptr32 Void
+0x120 VadHint : Ptr32 Void
+0x124 CloneRoot : Ptr32 Void
+0x128 NumberOfPrivatePages : Uint4B
+0x12c NumberOfLockedPages : Uint4B
+0x130 Win32Process : Ptr32 Void
+0x134 Job : Ptr32 _EJOB
+0x138 SectionObject : Ptr32 Void
+0x13c SectionBaseAddress : Ptr32 Void
+0x140 QuotaBlock : Ptr32 _EPROCESS_QUOTA_BLOCK
+0x144 WorkingSetWatch : Ptr32 _PAGEFAULT_HISTORY
+0x148 Win32WindowStation : Ptr32 Void
+0x14c InheritedFromUniqueProcessId : Ptr32 Void
+0x150 LdtInformation : Ptr32 Void
+0x154 VadFreeHint : Ptr32 Void
+0x158 VdmObjects : Ptr32 Void
+0x15c DeviceMap : Ptr32 Void
+0x160 PhysicalVadList : _LIST_ENTRY
+0x168 PageDirectoryPte : _HARDWARE_PTE
+0x168 Filler : Uint8B
+0x170 Session : Ptr32 Void
+0x174 ImageFileName : [16] UChar
+0x184 JobLinks : _LIST_ENTRY
+0x18c LockedPagesList : Ptr32 Void
+0x190 ThreadListHead : _LIST_ENTRY
+0x198 SecurityPort : Ptr32 Void
+0x19c PaeTop : Ptr32 Void
+0x1a0 ActiveThreads : Uint4B
+0x1a4 GrantedAccess : Uint4B
+0x1a8 DefaultHardErrorProcessing : Uint4B
+0x1ac LastThreadExitStatus : Int4B
+0x1b0 Peb : Ptr32 _PEB
+0x1b4 PrefetchTrace : _EX_FAST_REF
+0x1b8 ReadOperationCount : _LARGE_INTEGER
+0x1c0 WriteOperationCount : _LARGE_INTEGER
+0x1c8 OtherOperationCount : _LARGE_INTEGER
+0x1d0 ReadTransferCount : _LARGE_INTEGER
+0x1d8 WriteTransferCount : _LARGE_INTEGER
+0x1e0 OtherTransferCount : _LARGE_INTEGER
+0x1e8 CommitChargeLimit : Uint4B
+0x1ec CommitChargePeak : Uint4B
+0x1f0 AweInfo : Ptr32 Void
+0x1f4 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO
+0x1f8 Vm : _MMSUPPORT
+0x238 LastFaultCount : Uint4B
+0x23c ModifiedPageCount : Uint4B
+0x240 NumberOfVads : Uint4B
+0x244 JobStatus : Uint4B
+0x248 Flags : Uint4B
+0x248 CreateReported : Pos 0, 1 Bit
+0x248 NoDebugInherit : Pos 1, 1 Bit
+0x248 ProcessExiting : Pos 2, 1 Bit
+0x248 ProcessDelete : Pos 3, 1 Bit
+0x248 Wow64SplitPages : Pos 4, 1 Bit
+0x248 VmDeleted : Pos 5, 1 Bit
+0x248 OutswapEnabled : Pos 6, 1 Bit
+0x248 Outswapped : Pos 7, 1 Bit
+0x248 ForkFailed : Pos 8, 1 Bit
+0x248 HasPhysicalVad : Pos 9, 1 Bit
+0x248 AddressSpaceInitialized : Pos 10, 2 Bits
+0x248 SetTimerResolution : Pos 12, 1 Bit
+0x248 BreakOnTermination : Pos 13, 1 Bit
+0x248 SessionCreationUnderway : Pos 14, 1 Bit
+0x248 WriteWatch : Pos 15, 1 Bit
+0x248 ProcessInSession : Pos 16, 1 Bit
+0x248 OverrideAddressSpace : Pos 17, 1 Bit
+0x248 HasAddressSpace : Pos 18, 1 Bit
+0x248 LaunchPrefetched : Pos 19, 1 Bit
+0x248 InjectInpageErrors : Pos 20, 1 Bit
+0x248 VmTopDown : Pos 21, 1 Bit
+0x248 Unused3 : Pos 22, 1 Bit
+0x248 Unused4 : Pos 23, 1 Bit
+0x248 VdmAllowed : Pos 24, 1 Bit
+0x248 Unused : Pos 25, 5 Bits
+0x248 Unused1 : Pos 30, 1 Bit
+0x248 Unused2 : Pos 31, 1 Bit
+0x24c ExitStatus : Int4B
+0x250 NextPageColor : Uint2B
+0x252 SubSystemMinorVersion : UChar
+0x253 SubSystemMajorVersion : UChar
+0x252 SubSystemVersion : Uint2B
+0x254 PriorityClass : UChar
+0x255 WorkingSetAcquiredUnsafe :

下面对进程结构体EPROCESS的重要成员/结构进行说明介绍。

1.3 _KPROCESS结构

_KPROCESS_EPROCESS结构的第一个成员,大小为0x6C字节。

可参考、:EPROCESS 进程/线程优先级 句柄表 GDT LDT 页表 《寒江独钓》内核学习笔记(2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
kd> dt _KPROCESS
nt!_KPROCESS
+0x000 Header : _DISPATCHER_HEADER
+0x010 ProfileListHead : _LIST_ENTRY
+0x018 DirectoryTableBase : [2] Uint4B
+0x020 LdtDescriptor : _KGDTENTRY
+0x028 Int21Descriptor : _KIDTENTRY
+0x030 IopmOffset : Uint2B
+0x032 Iopl : UChar
+0x033 Unused : UChar
+0x034 ActiveProcessors : Uint4B
+0x038 KernelTime : Uint4B
+0x03c UserTime : Uint4B
+0x040 ReadyListHead : _LIST_ENTRY
+0x048 SwapListEntry : _SINGLE_LIST_ENTRY
+0x04c VdmTrapcHandler : Ptr32 Void
+0x050 ThreadListHead : _LIST_ENTRY
+0x058 ProcessLock : Uint4B
+0x05c Affinity : Uint4B
+0x060 StackCount : Uint2B
+0x062 BasePriority : Char
+0x063 ThreadQuantum : Char
+0x064 AutoAlignment : UChar
+0x065 State : UChar
+0x066 ThreadSeed : UChar
+0x067 DisableBoost : UChar
+0x068 PowerState : UChar
+0x069 DisableQuantum : UChar
+0x06a IdealNode : UChar
+0x06b Flags : _KEXECUTE_OPTIONS
+0x06b ExecuteOptions : UChar
偏移 名称 含义
0x000 Header “可等待”对象,比如Mutex互斥体、Event事件等(WaitForSingleObject)。内核结构体中,凡是有成员是_DISPATCHER_HEADER就说明该内核对象可以使用WaitForSingleObject等函数进行等待。
0x010 ProfileListHead 当该进程参与性能分析时,作为一个节点加入到全局性能分析进程列表(内核全局变量KiProfileListHead)
0x018 DirectoryTableBase 页目录表的基址,CR3。是一个只有两项的数组,第一项指向该进程的页目录表地址,第二项指向进程的超空间的页目录表地址
0x020 LdtDescriptor 该进程LDT(局部描述发表)的描述符,历史遗留,16位Windows 段选择子不够,每个进程都有一个LDT表。
0x028 Int21Descriptor 兼容DOS程序,允许它们通过int 21H指令来调用DOS系统功能
0x30 IopmOffset 指定IOPM(I/O权限表)的位置,内核通过IOPM可控制进程的用户模式I/O访问权限
0x32 Iopl 定义进程的I/O优先级
0x34 ActiveProcessors 记录当前进程正在哪些处理器上运行,这和进程创建时的CPU处理器的亲和性有关。
0x38 KernelTime 记录一个进程对象在内核模式下所花的时间(已经运行了多久)
0x3C UserTime 记录一个进程对象在用户模式下所花的时间(已经运行了多久)
0x40 ReadListHead 是一个双向链表头,记录了进程中处于就绪状态但尚未被加入全局就绪链表的线程,这个域的意义在于,当一个进程被换出内存后,它所属的线程一旦就绪,则被挂到此链表中,并要求换入该进程。此后,当该进程被换入内存时,ReadyListHead中的所有线程被加入到系统全局的就绪线程链表中。
链表中的每一项都是一个指向KTHREAD对象的WaitListEntry域的地址,所以,从链表中的每一项都可以定位到对应的线程对象。OutThread = (PKTHREAD)(Process->ReadyListHead.Flink - 0x60);
《Windows内核原理与实现3.3.1 P108》
0x048 SwapListEntry 单链表项,当一个进程要被换出内存时,它(进程)通过此域加入到KiProcessOutSwapListHead为链头的单链表中;当一个进程被换入内存时,将进程加入到以KiProcessInSwapListHead为链头的单链表中。
KiProcessInSwapListHead - This is the list of processes that are waiting to be inswapped.
KiProcessOutSwapListHead - This is the list of processes that are waiting to be outswapped.
上面英语部分是从源码中拷贝到的,也就是说KiProcessOutSwapListHead上挂着的进程实际还在内存里。
Windbg中看了一下这两个变量的地址:KiProcessInSwapListHead - 4 = KiProcessOutSwapListHead
还可参考《Windows内核原理与实现4.5.5 P284》
0x050 ThreadListHead 指向一个链表头,此链表包含了该进程的所有当前进程。当一个线程被创建的时候,被加入到此链表中,在终止的时候被从链表中移除。双向链表,属于本进程的所有线程,都可以遍历出来。
0x058 ProcessLock 自旋锁对象,用途是保护此进程中的数据成员
0x05C Affinity 指定该进程的线程可以在哪些处理器上运行,其类型是KAFFINITY,这是一个32位或64位整数,其二进制表示的每一位分别对应于当前机器上的一个处理器(核)。规定进程里面的所有线程能在哪个CPU上跑:
- Affinity = 00000001,值为1,那这个进程的所以线程只能在0号CPU上跑。
- Affinity = 00000011,值为3,那这个进程的所以线程能在0、1号CPU上跑。
- Affinity = 00000100,值为4,那这个进程的所以线程能在2号CPU上跑。
- Affinity = 00000101,值为5,那这个进程的所以线程能在0,2号CPU上跑。
该成员占4个字节共32位 ,所以最多32核。Windows64位就64核。
如果只有一个CPU 把这个设置为4 或其他非00000001的值,那么这个进程就死了。
0x060 StackCount 记录当前进程有多少个线程栈位于内存中
0x062 BasePriority 指定进程中的线程优先级,所有线程在启动时都会继承进程的BasePriority(基础优先级或最低优先级,该进程中的所有线程最起码的优先级)
0x064 AutoAlignment 用于进程中的内存访问对齐设置,此标志位会被传递到线程的数据结构中,当一个线程的对齐检查开关打开时,该线程中的未对齐数据访问将会导致对齐错误(Alignment Fault)
0x065 State 说明进程是否在内存中,共有六种可能状态:
- ProcessInMemory
- ProcessOutOfMemory
- ProcessInTransition
- ProcessOutTransition
- ProcessInSwap
- ProcessOutSwap
0x066 ThreadSeed 用于为该进程的线程指定理想处理器
0x067 DisableBoost 与线程调度过程中的优先级提升和时限分配有关
0x068 PowerState 用于记录电源状态
0x069 DisableQuantum 与线程调度过程中的优先级提升和时限分配有关
0x06A IdealNode 用于为一个进程选择优先的处理节点,这是在进程初始化时设定
0x06B ExecuteOptions 用于设置一个进程的执行选项,这是为了支持NX而引入到系统中的

1.4 其他成员

可参考、:EPROCESS 进程/线程优先级 句柄表 GDT LDT 页表 《寒江独钓》内核学习笔记(2)

偏移 名称 含义
0x000 Pcb 即上面介绍的KPROCESS内嵌结构体,因此,在系统内部,一个进程的EPROCESS对象地址和KPROCESS对象的地址是相同的
0x06C ProcessLock 与KPROCESS的自旋锁同名,但是它们类型不同,保护成员也不同。这里的ProcessLock域是一个推锁对象,用于保护EPROCESS中的数据成员
0x070 CreateTime 进程创建时间
0x078 ExitTime 进程退出时间
0x080 RundownProtect 进程停止保护锁,当一个进程到最后被销毁时,它要等到所有其他进程和线程已经释放此锁才可以继续进行
0x084 UniqueProcessId 进程的编号,任务管理器中的PID
0x088 ActiveProcessLinks 双向链表,所有正在运行的进程。所有的活动进程都连接在一起**,构成了一个链表。全局变量PsActiveProcessHead指向链表头。
图片1.png下一个EPROCESS = PsActiveProcessHead->Flink - 0x88
0x090
0x09c
QuotaUsage
QuotaPeak
物理页相关的统计信息。
0x0a8
0x0ac
0x0b0
CommitCharge
PeakVirtualSize
VirtualSize
虚拟内存相关的统计信息。
0x0B4 SessionProcessLinks 双链表节点,当一个进程加入到一个系统会话中,其SessionProcessLinks域将作为一个节点加入到该会话的进程链表中
0x0BC DebugPort 指向调试端口的句柄。DebugPort是一个指针,充当调试程序和被调试程序之间的桥梁。A – B – C。
- A:调试器(程序)
- B:DebugPort存的地址
- C:被调试程序
不断地将该值清为0,可以在一定程度上防止程序被调试。要想彻底一些防御,还需要清楚ETHREAD里面的DebugPort成员。
⚠️注意:DebugPort是被调试程序的DebugPort。
0x0C0 Exception 指向异常端口的句柄
0x0C4 ObjectTable 指向进程的句柄表
0x0C8 Token 是一个快速引用,指向该进程的访问令牌
0x1A0 ActiveThreads 记录了当前进程有多少活动线程。当该值减为0时,所有的线程将退出,于是进程也退出。(分析NtTerminateThread(IN HANDLE ThreadHandle OPTIONAL,IN NTSTATUS ExitStatus)时逆得的。)
0x11C VadRoot 虚拟地址描述符二叉树根节点
0x130 Win32Process 指向由Windows子系统管理的进程区域,如果此指不为NULL,说明这是一个Windows子系统进程(GUI进程)
0x140 QuotaBlock 指向进程的配额块,进程的配额块类型为: EPROCESS_QUOTA_BLOCK。Windows系统中的”配额块”相互串起来构成了一个双链表,每个配额块都可以被多个进程共享,所以有一个引用计数指用来说明当前有多少个进程正在使用这一配额块。配额块中主要定义了非换页内存池、换页内存池、交换文件中的内存配额限制。这里要注意的是,所有配额块构成的双链表的表头为PspQuotaBlockList。系统的默认配额块PspDefaultQuotaBlock。
0x170 Session 所属会话对象
0x174 ImageName 指向进程名,进程镜像文件名,最多16个字节。
0x190 ThreadListHead 双向链表,属于本进程的所有线程,都可以遍历出来
0x1A0 ActiveThreads 活动线程的数量。当该值为0,所有线程都将退出,进程也就会退出
0x1A4 GrantedAccess 进程的访问权限
0x1B0 Peb PEB(Process Environment Block 进程环境块),进程在3环的一个结构体,里面包含了进程的模块列表、是否处于调试状态等信息。PEB是给3环用的,ETHREAD是给操作系统用的。
0x24C ExitStatus 包含了进程的退出状态,从进程的退出状态通常可以获知进程非正常退出的大致原因
0x252 SubSystemMinorVersion 子系统次版本号
0x253 SubSystemMajorVersion 子系统主版本号
0x254 PriorityClass 单字节,表明进程的优先级程度,分别有以下几个类别:1:空闲类别2:普通类别3:高优先级类别4:实时类别5:普通之下类别6:普通之上类别0:未知类别

1.5 如何找到EPROCESS地址

在0环获取EPROCESS常用的有以下几种方法。

方法一(常用)

不使用API的其中一种做法是通过 KPCR(FS:[0])找当前线程,然后+0x220找当前进程,示例代码

1
2
3
4
5
6
7
8
9
kd> r fs
fs=00000030
kd> r gdtr
gdtr=8003f000
kd> dq 8003f000+8*6
8003f030 ffc093df`f0000001 0040f300`00000fff
8003f040 0000f200`0400ffff 00000000`00000000
8003f050 80008955`27000068 80008955`27680068
8003f060 00009302`2f40ffff 0000920b`80003fff
  1. ffc093df`f0000001:FS.Base = 0xFFDFF000,即为FS:[0] == KPCR结构。

  2. FS:[0x124]_KTHREAD,有如下:

    1
    2
    3
    4
    5
    6
    __asm
    {
    mov eax, fs:[0x124];
    mov eax, [eax + 0x220];
    mov pEprocess, eax;
    }

方法二

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
PROCESS 8636e570 SessionId: 0 Cid: 07a4 Peb: 7ffdc000 ParentCid: 067c
DirBase: 06e404e0 ObjectTable: 00000000 HandleCount: 0.
Image: notepad.exe

kd> dt _EPROCESS 0x8636e570
nt!_EPROCESS
+0x000 Pcb : _KPROCESS
+0x06c ProcessLock : _EX_PUSH_LOCK
+0x070 CreateTime : _LARGE_INTEGER 0x1d832f3`55cfd438
+0x078 ExitTime : _LARGE_INTEGER 0x1d832f3`5c6a377a
+0x080 RundownProtect : _EX_RUNDOWN_REF
...
+0x174 ImageFileName : [16] "notepad.exe"
...

PEPROCESS pEprocess = (PEPROCESS)0x8636e570;

方法三:在0环通过API。

1
2
3
PEPROCESS pEprocess = (PEPROCESS)PsGetCurrentProcess();

PEPROCESS PsGetCurrentProcess(VOID);

方法四:在0环通过API,ObReferenceObjectByHandle

1
2
3
4
5
6
7
8
9
10
11
12
PEPROCESS pEprocess = NULL;
NTSTATUS status = 0;
status = ObReferenceObjectByHandle(hProcess,FILE_ANY_ACCESS,*PsProcessType,KernelMode,(PVOID*)&pEprocess,NULL);

NTSTATUS ObReferenceObjectByHandle(
IN HANDLE Handle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_TYPE ObjectType OPTIONAL,
IN KPROCESSOR_MODE AccessMode,
OUT PVOID *Object,
OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL
);

方法五:使用函数PsLookupProcessByProcessId((HANDLE)uPID,&pEprocess),参见Windows XP 驱动开发(三)1.9驱动代码部分。

1
2
3
4
NTSTATUS PsLookupProcessByProcessId(
[in] HANDLE ProcessId, //指定进程的进程 ID。
[out] PEPROCESS *Process //返回指向由 ProcessId指定的进程的 EPROCESS 结构的引用指针。
);

1.6 擦除DebugPort实验

在Windows XP中先使用OD调试一个程序ipmsg.exe,如下图:

28.png

按一下步骤操作:

  1. 找到EPROCESS结构地址。8637ada0

    1
    2
    3
    4
    5
    6
    7
    8
    kd> !process 0 0
    PROCESS 860eb020 SessionId: 0 Cid: 0af4 Peb: 7ffde000 ParentCid: 067c
    DirBase: 06e40380 ObjectTable: e115e230 HandleCount: 72.
    Image: ollydbg.exe

    PROCESS 8637ada0 SessionId: 0 Cid: 0f50 Peb: 7ffd9000 ParentCid: 0af4
    DirBase: 06e403e0 ObjectTable: e2253080 HandleCount: 21.
    Image: ipmsg.exe
  2. 找到EPROCESS中的DebugPort。0x8629eaf0

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    kd> dt _EPROCESS 8637ada0
    ntdll!_EPROCESS
    +0x000 Pcb : _KPROCESS
    +0x06c ProcessLock : _EX_PUSH_LOCK
    +0x070 CreateTime : _LARGE_INTEGER 0x1d83c36`daaaca9a
    ...
    +0x0bc DebugPort : 0x8629eaf0 Void
    ...
    +0x174 ImageFileName : [16] "ipmsg.exe"
    ...
  3. 将DebugPort清零。ed 0x8637ada0+0xbc 0

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    kd> ed 0x8637ada0+0xbc 0
    kd> dt _EPROCESS 8637ada0
    ntdll!_EPROCESS
    +0x000 Pcb : _KPROCESS
    +0x06c ProcessLock : _EX_PUSH_LOCK
    +0x070 CreateTime : _LARGE_INTEGER 0x1d83c36`daaaca9a
    ...
    +0x0bc DebugPort : (null)
    ...
    +0x174 ImageFileName : [16] "ipmsg.exe"
    ...

    回到XP的OD中随便F8试试,发现程序崩了,说明DebugPort清零会影响到程序的调试。

    29.png

1.7 进程隐藏:ActiveProcessLinks断链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <ntddk.h>

NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path);
VOID DriverUnload(PDRIVER_OBJECT driver);

NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
{
PEPROCESS pEprocess;
PEPROCESS pCurProcess;
PCHAR ImageFileName = NULL;
__asm
{
mov eax, fs:[0x124];
mov eax, [eax + 0x220];
mov pEprocess, eax;
}

pCurProcess = pEprocess;
do
{
ImageFileName = (PCHAR)pCurProcess + 0x174;
if(!strcmp(ImageFileName,"notepad.exe"))
{
PLIST_ENTRY curNode;
curNode = (PLIST_ENTRY)((ULONG)pCurProcess + 0x88);
curNode->Flink->Blink = curNode->Blink;
curNode->Blink->Flink = curNode->Flink;
}
pCurProcess = (PEPROCESS)(*(PULONG)((ULONG)pCurProcess + 0x88) - 0x88);
}
while(pCurProcess != pEprocess);

driver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}

VOID DriverUnload(PDRIVER_OBJECT driver)
{
DbgPrint("驱动卸载成功\n");
}

驱动运行前:

30.png

驱动运行后,进程notepad.exe已隐藏:

31.png

2 线程结构体

学习了EPROCESS里的部分成员,其中 ActiveProcessLinks 是进程链表,我们可以通过对其断链实现进程隐藏,DebugPort 是调试端口,抹除它的值可以使三环调试器调试崩溃。

每个Windows线程在0环都有一个对应的结构体:ETHREAD ,这个结构体包含了线程所有重要的信息。

2.1 _ETHREAD结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
kd> dt _ETHREAD
ntdll!_ETHREAD
+0x000 Tcb : _KTHREAD
+0x1c0 CreateTime : _LARGE_INTEGER
+0x1c0 NestedFaultCount : Pos 0, 2 Bits
+0x1c0 ApcNeeded : Pos 2, 1 Bit
+0x1c8 ExitTime : _LARGE_INTEGER
+0x1c8 LpcReplyChain : _LIST_ENTRY
+0x1c8 KeyedWaitChain : _LIST_ENTRY
+0x1d0 ExitStatus : Int4B
+0x1d0 OfsChain : Ptr32 Void
+0x1d4 PostBlockList : _LIST_ENTRY
+0x1dc TerminationPort : Ptr32 _TERMINATION_PORT
+0x1dc ReaperLink : Ptr32 _ETHREAD
+0x1dc KeyedWaitValue : Ptr32 Void
+0x1e0 ActiveTimerListLock : Uint4B
+0x1e4 ActiveTimerListHead : _LIST_ENTRY
+0x1ec Cid : _CLIENT_ID
+0x1f4 LpcReplySemaphore : _KSEMAPHORE
+0x1f4 KeyedWaitSemaphore : _KSEMAPHORE
+0x208 LpcReplyMessage : Ptr32 Void
+0x208 LpcWaitingOnPort : Ptr32 Void
+0x20c ImpersonationInfo : Ptr32 _PS_IMPERSONATION_INFORMATION
+0x210 IrpList : _LIST_ENTRY
+0x218 TopLevelIrp : Uint4B
+0x21c DeviceToVerify : Ptr32 _DEVICE_OBJECT
+0x220 ThreadsProcess : Ptr32 _EPROCESS
+0x224 StartAddress : Ptr32 Void
+0x228 Win32StartAddress : Ptr32 Void
+0x228 LpcReceivedMessageId : Uint4B
+0x22c ThreadListEntry : _LIST_ENTRY
+0x234 RundownProtect : _EX_RUNDOWN_REF
+0x238 ThreadLock : _EX_PUSH_LOCK
+0x23c LpcReplyMessageId : Uint4B
+0x240 ReadClusterSize : Uint4B
+0x244 GrantedAccess : Uint4B
+0x248 CrossThreadFlags : Uint4B
+0x248 Terminated : Pos 0, 1 Bit
+0x248 DeadThread : Pos 1, 1 Bit
+0x248 HideFromDebugger : Pos 2, 1 Bit
+0x248 ActiveImpersonationInfo : Pos 3, 1 Bit
+0x248 SystemThread : Pos 4, 1 Bit
+0x248 HardErrorsAreDisabled : Pos 5, 1 Bit
+0x248 BreakOnTermination : Pos 6, 1 Bit
+0x248 SkipCreationMsg : Pos 7, 1 Bit
+0x248 SkipTerminationMsg : Pos 8, 1 Bit
+0x24c SameThreadPassiveFlags : Uint4B
+0x24c ActiveExWorker : Pos 0, 1 Bit
+0x24c ExWorkerCanWaitUser : Pos 1, 1 Bit
+0x24c MemoryMaker : Pos 2, 1 Bit
+0x250 SameThreadApcFlags : Uint4B
+0x250 LpcReceivedMsgIdValid : Pos 0, 1 Bit
+0x250 LpcExitThreadCalled : Pos 1, 1 Bit
+0x250 AddressSpaceOwner : Pos 2, 1 Bit
+0x254 ForwardClusterOnly : UChar
+0x255 DisablePageFaultClustering : UChar

_KTHREAD_ETHREAD结构的第一个成员,大小为0x1c0字节。

2.2 _KTHREAD结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
kd> dt _KTHREAD
ntdll!_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
+0x04c ContextSwitches : Uint4B
+0x050 IdleSwapBlock : UChar
+0x051 Spare0 : [3] UChar
+0x054 WaitStatus : Int4B
+0x058 WaitIrql : UChar
+0x059 WaitMode : Char
+0x05a WaitNext : UChar
+0x05b WaitReason : UChar
+0x05c WaitBlockList : Ptr32 _KWAIT_BLOCK
+0x060 WaitListEntry : _LIST_ENTRY
+0x060 SwapListEntry : _SINGLE_LIST_ENTRY
+0x068 WaitTime : Uint4B
+0x06c BasePriority : Char
+0x06d DecrementCount : UChar
+0x06e PriorityDecrement : Char
+0x06f Quantum : Char
+0x070 WaitBlock : [4] _KWAIT_BLOCK
+0x0d0 LegoData : Ptr32 Void
+0x0d4 KernelApcDisable : Uint4B
+0x0d8 UserAffinity : Uint4B
+0x0dc SystemAffinityActive : UChar
+0x0dd PowerState : UChar
+0x0de NpxIrql : UChar
+0x0df InitialNode : UChar
+0x0e0 ServiceTable : Ptr32 Void
+0x0e4 Queue : Ptr32 _KQUEUE
+0x0e8 ApcQueueLock : Uint4B
+0x0f0 Timer : _KTIMER
+0x118 QueueListEntry : _LIST_ENTRY
+0x120 SoftAffinity : Uint4B
+0x124 Affinity : Uint4B
+0x128 Preempted : UChar
+0x129 ProcessReadyQueue : UChar
+0x12a KernelStackResident : UChar
+0x12b NextProcessor : UChar
+0x12c CallbackStack : Ptr32 Void
+0x130 Win32Thread : Ptr32 Void
+0x134 TrapFrame : Ptr32 _KTRAP_FRAME //从3环进入0环后,用于保存3环的寄存器
+0x138 ApcStatePointer : [2] Ptr32 _KAPC_STATE
+0x140 PreviousMode : Char
+0x141 EnableStackSwap : UChar
+0x142 LargeStack : UChar
+0x143 ResourceIndex : UChar
+0x144 KernelTime : Uint4B
+0x148 UserTime : Uint4B
+0x14c SavedApcState : _KAPC_STATE
+0x164 Alertable : UChar
+0x165 ApcStateIndex : UChar
+0x166 ApcQueueable : UChar
+0x167 AutoAlignment : UChar
+0x168 StackBase : Ptr32 Void
+0x16c SuspendApc : _KAPC
+0x19c SuspendSemaphore : _KSEMAPHORE
+0x1b0 ThreadListEntry : _LIST_ENTRY
+0x1b8 FreezeCount : Char
+0x1b9 SuspendCount : Char
+0x1ba IdealProcessor : UChar
+0x1bb DisableBoost : UChar
偏移 名称 作用
0x000 Header “可等待”对象,比如Mutex互斥体、Event事件等(WaitForSingleObject)。内核结构体中,凡是有成员是_DISPATCHER_HEADER就说明该内核对象可以使用WaitForSingleObject等函数进行等待。
0x018 InitialStack 记录了原始栈的位置(高地址)
0x01C StackLimit 栈的大小
0x020 Teb TEB,Thread Environment Block,线程环境块。大小4KB,位于用户地址空间。3环可以通过 FS:[0] 找到TEB。0环时FS执行KPCR。
0x028 KernelStack 记录了真正内核调用栈的开始位置,由于在内核栈的顶部区域还记录了浮点处理器保存区和一个异常陷阱帧,所以,KernelStack的位置比InitialStack要低一些。线程切换时,旧线程的esp0存到它的KernelStack,然后把新线程的KernelStack写到TSS。
以上三个成员与线程切换相关。
0x02C DedugActive 如果值为-1,不能使用调试寄存器:Dr0 - Dr7
0x02D State 线程状态:就绪、等待还是运行
0x02E Alerted 用来说明线程在指定模式下是否为”已经被吵醒” 。
BOOLEAN Alerted[MaximumMode];
typedef enum _MODE { KernelMode,UserMode,MaximumMode } MODE;
0x031 NpxState 反映了浮点处理器的状态
0x033 Priority 说明线程优先级,这里是指它的动态优先级,即执行过程中可能由于某些原因而调整过的优先级
0x034 ApcState 指定一个线程的APC信息
0x054 WaitStatus 等待的结果状态,当前对象等待的结果是什么?
KeWaitForSingleObject函数结束等待时反映着结束的原因。
0x058 WaitIrql 记录了原先的IRQL值。Waitlrql域与WaitNext一起使用,当WaitNext为TRUE时,WaitIrql记录了原先的IRQL 值。
0x059 WaitMode 线程等待时的处理器模式,即内核模式或用户模式的等待。0:内核,1:用户
0x05A WaitNext WaitNext域也是一个布尔值,TRUE值表示这个线程马上要调用一个内核等待函数,它的用途是,在发出了一个信号(比如释放了一个信号量对象)以后,接下来该线程会马上调用等待函数,所以,它不必解除线程调度器锁。
0x05B WaitReason 等待的理由,因为什么原因在等待?WaitReason域记录了一个线程的等待理由,其值的含义见base\ntos\inc\ke.h中的KWAIT_REASON枚举类型。VaitReason基本上只是记录了等待的理由,而并不参与到线程调度或决策中
0x60 WaitListEntry 双向链表节点,当一个线程正在等待被执行时,该域作为一个线程节点加入到某个链表中
0x60 SwapListEntry 单链表节点,当线程的内核栈需要被换入时,插入到以全局变量KiStackInSwapListHead为链表头的单链表中
0x06C BasePriority 线程的静态优先级,其初始值是所属进程的BasePriority值(KPROCESS->BasePriority),以后可以通过KeSetBasePriorityThread()函数重新设定。
015是”普通线程”的优先级, 1631是”实时线程”的优先级。
0x70 WaitBlock 指定等待的对象,等待哪个对象(WaitForSingleObject)
0x0E0 ServiceTable 指向系统服务描述符表基址(SSDT)
0x0E8 ApcQueueable 自旋锁,用户保护APC
0x124 Affinity 指定了线程的处理器亲和性,此值初始继承自进程对象的Affinity值
0x128 Preempted 布尔值,说明这个线程是否被高优先级的线程抢占了,只有当一个线程正在运行或者正在等待运行而被高优先级线程抢占的时候,此值才会是TRUE
0x129 ProcessReadyQueue 布尔值,说明线程是否在所属进程的KPROCESS对象的ReadyListEntry(ReadyListHead)链表中,TRUE表示在此链表中,FALSE表示不在此链表中
0x12A KernelStackResident 布尔值,说明该线程的内核栈是否驻留在内存中,当内核栈被换出内存时,此值将被设置成FALSE;当换入内存时,在设置成TRUE
0x12B NextProcessor 下一次切换线程完毕后运行在哪个核上
0x134 TrapFrame 线程中最关键的部分,记录了控制流状态,线程堆栈结构
0x138 ApcStatePointer 两个元数的数组,指向KAPC_STATE指针,两个元数分别指向线程对象的ApcState和SavedApcState
0x140 PreviousMode 原先模式,某些内核函数会判断程序是0环调用还是3环调用的
0x14C SavedApcState 保存的APC
0x164 Alertable 说明一个线程是否可以被吵醒。Alertable是个布尔量,表示是否允许本次等待因用户空间APC而中断,或者说被“吵醒(Alert)”。 吵醒与唤醒是不同的,唤醒是因为所等待的条件得到了满足(仓库到货),而吵醒是因为别的原因而醒来(与仓库无关)。
0x165 ApcStateIndex 索引值,表明当前APC状态在ApcStatePointer域中的索引
0x168 StateBase 记录当前栈的基址。当线程初始化时,InitialStack和StackBase是相等的
0x1B0 ThreadListEntry 双向链表,一个进程所有的线程都挂在一个链表中,挂的就是这个位置。_ETHREAD一共有两个这样的链表。
32.png
0x1BA IdealProcessor 指明了在多处理器上该线程的理想处理器

2.3 其他成员

偏移 名称 作用
0x000 Tcb 内嵌的KTHREAD结构体
0x1D0 ExitStatus 线程退出状态
0x1EC Cid 包含进程ID、线程ID:
kd> dt _CLIENT_ID
ntdll!_CLIENT_ID
+0x000 UniqueProcess : Ptr32 Void
+0x004 UniqueThread : Ptr32 Void
0x220 ThreadsProcess 指向线程所属的进程
0x22C ThreadListEntry 双向链表,一个进程所有的线程都挂在一个链表中,挂的就是这个位置,一共有两个这样的链表。
32.png

2.4 如何找到ETHREAD地址

方法一:通过EPROCESS得ThreadListHead链来获取,具体见2.5。

方法二:该方法较麻烦,不推荐:在0环通过API,ObReferenceObjectByHandle

1
2
NTSTATUS status = 0;
status = ObReferenceObjectByHandle(hProcess,FILE_ANY_ACCESS,*PsThreadType,KernelMode,(PVOID*)&pEthread,NULL);

而参数hProcess的获取可以通过ObOpenObjectByPointer()函数,PID可以通过PEPROCESS->UniqueProcessId得到。

2.5 将进程的线程全部隐藏

如果要将进程的线程全部隐藏,需要将两个串着活动线程的双向链表都断开

在EPROCESS偏移的+0x050 ThreadListHead: _LIST_ENTRY+0x190 ThreadListHead: _LIST_ENTRY双向链表,属于本进程的所有线程,都可以遍历出来

关系如下:

1
2
3
4
pCurThread = (PLIST_ENTRY)(*(PULONG)((ULONG)pCurProcess + 0x50));
pEthread = (PETHREAD)(*(PULONG)((ULONG)pCurProcess + 0x50) - 0x1b0);

pCurThread->Blink == *(PULONG)((ULONG)pEthread + 0x1b0;

32.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <ntddk.h>
#include <ntstatus.h>

NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path);
VOID DriverUnload(PDRIVER_OBJECT driver);

NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
{
PEPROCESS pCurProcess,pEprocess = NULL;
PETHREAD pEthread;
NTSTATUS status;
PLIST_ENTRY pCurThread;
ANSI_STRING ImageFileName = {0};
ANSI_STRING NOTEPAD;
ULONG PID = 0;
RtlInitAnsiString(&NOTEPAD,"explorer.exe");

__asm
{
mov eax, fs:[0x124];
mov eax, [eax + 0x220];
mov pEprocess, eax;
}

pCurProcess = pEprocess;
do
{
RtlInitAnsiString(&ImageFileName,(PCHAR)((ULONG)pCurProcess + 0x174));
if(!RtlCompareString(&ImageFileName,&NOTEPAD,TRUE))
{
*(PULONG)((ULONG)pCurProcess+0x50) = ((ULONG)pCurProcess + 0x50);
*(PULONG)((ULONG)pCurProcess+0x54) = ((ULONG)pCurProcess + 0x54);

*(PULONG)((ULONG)pCurProcess+0x190) = ((ULONG)pCurProcess + 0x190);
*(PULONG)((ULONG)pCurProcess+0x194) = ((ULONG)pCurProcess + 0x194);
/* 以下方法始终会存在一个线程
pCurThread = (PLIST_ENTRY)(*(PULONG)((ULONG)pCurProcess + 0x50));
pEthread = (PETHREAD)(*(PULONG)((ULONG)pCurProcess + 0x50) - 0x1b0);

pCurThread->Blink = (PLIST_ENTRY)((ULONG)pCurProcess + 0x50);
pCurThread->Flink = (PLIST_ENTRY)((ULONG)pCurProcess + 0x50);

pCurThread = (PLIST_ENTRY)(*(PULONG)((ULONG)pCurProcess + 0x190));
pCurThread->Blink = (PLIST_ENTRY)((ULONG)pCurProcess + 0x190);
pCurThread->Flink = (PLIST_ENTRY)((ULONG)pCurProcess + 0x190);
*/

//DbgPrint("pCurThread:%x,pEthread->0x1b0:%x\n",pCurThread->Blink,*(PULONG)((ULONG)pEthread \
//+ 0x1b0));
//PID = (*(PULONG)((ULONG)pCurProcess + 0x84)); //是一个地址(VOID),该地址保存PID
}
pCurProcess = (PEPROCESS)(*(PULONG)((ULONG)pCurProcess + 0x88) - 0x88);
}
while(pCurProcess != pEprocess);


driver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}

VOID DriverUnload(PDRIVER_OBJECT driver)
{
DbgPrint("驱动卸载成功\n");
}

33.png

即使explorer.exe线程全被隐藏起来了,但是其依然正常运行。将在下面的章节进行解析为什么。

3 CPU控制区

KPCR:CPU控制区(Processor Control Region)。KPCR是个结构体,用来描述CPU状态。实际上这部分内容在Windows XP 系统调用(一)已经讲解过啦。

CPU也有自己的控制块,每一个CPU有一个,叫KPCR。

  1. 进程在内核中对应结构体:EPROCESS
  2. 线程在内核中对应结构体:ETHREAD
  3. CPU在内核中也有一个对应的结构体:KPCR

3.1 _KPCR结构

  1. 当线程进入0环时,FS:[0]指向KPCR(3环时FS:[0] -> TEB)。

  2. 每个CPU都有一个KPCR结构体(一个核一个0)。

  3. KPCR中存储了CPU本身要用的一些重要数据:GDT、IDT以及线程相关的一些信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
kd> dt _KPCR
nt!_KPCR
+0x000 NtTib : _NT_TIB
+0x01c SelfPcr : Ptr32 _KPCR //指向自己,方便寻址
+0x020 Prcb : Ptr32 _KPRCB //指向拓展结构体_KPRCB
+0x024 Irql : UChar
+0x028 IRR : Uint4B
+0x02c IrrActive : Uint4B
+0x030 IDR : Uint4B
+0x034 KdVersionBlock : Ptr32 Void
+0x038 IDT : Ptr32 _KIDTENTRY //IDT表基址
+0x03c GDT : Ptr32 _KGDTENTRY //GDT表基址
+0x040 TSS : Ptr32 _KTSS //指针,指向TSS,每个CPU都有一个TSS
+0x044 MajorVersion : Uint2B
+0x046 MinorVersion : Uint2B
+0x048 SetMember : Uint4B
+0x04c StallScaleFactor : Uint4B
+0x050 DebugActive : UChar
+0x051 Number : UChar //CPU编号:0 1 2 3 4 5...
+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 //拓展结构体

_NT_TIB_KPCR的前0x1c字节。

3.2 _NT_TIB结构

1
2
3
4
5
6
7
8
9
10
kd>  dt _NT_TIB
nt!_NT_TIB
+0x000 ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD//当前线程内核异常链表(SEH)。
+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 //指向自己(也就是指向KPCR结构) 这样设计的目的是为了查找方便。

3.3 _KPRCB结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
kd> dt _KPRCB
nt!_KPRCB
+0x000 MinorVersion : Uint2B
+0x002 MajorVersion : Uint2B
+0x004 CurrentThread : Ptr32 _KTHREAD //指向当前线程
+0x008 NextThread : Ptr32 _KTHREAD //即将切换的下一个线程
+0x00c IdleThread : Ptr32 _KTHREAD //CPU空闲时跑的线程
+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 //系统调用计数,即记录KiFastCallEntry被调用了多少次
+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

3.4 如何找到KPCR地址

通过fs:[0]即可找到KPCR结构的地址,通过KPCR -> PrcbData(_KPRCB) -> CurrentThread(_KTHREAD) -> TrapFrame(_KTRAP_FRAME)找到TrapFrame。

1
2
3
4
5
6
7
kd> r fs
fs=00000030 //0030 -- 0011 0000 -- 00110 0 00 -- index == 0x6,TI == 0(GDT), RPL == 00
kd> r gdtr
gdtr=8003f000
kd> dq 8003f000+6*8
8003f030 ffc093df`f0000001 0040f300`00000fff
8003f040 0000f200`0400ffff 00000000`00000000

FS:[0] = ffc093df`f0000001,FS.Base = 0xFFDFF000。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
kd> dt _KPCR 0xFFDFF000
nt!_KPCR
+0x000 NtTib : _NT_TIB
+0x01c SelfPcr : 0xffdff000 _KPCR
+0x020 Prcb : 0xffdff120 _KPRCB
+0x024 Irql : 0 ''
+0x028 IRR : 0
+0x02c IrrActive : 0
+0x030 IDR : 0xffffffff
+0x034 KdVersionBlock : 0x8054e2b8 Void
+0x038 IDT : 0x8003f400 _KIDTENTRY
+0x03c GDT : 0x8003f000 _KGDTENTRY
+0x040 TSS : 0x80042000 _KTSS
+0x044 MajorVersion : 1
+0x046 MinorVersion : 1
+0x048 SetMember : 1
+0x04c StallScaleFactor : 0x960
+0x050 DebugActive : 0 ''
+0x051 Number : 0 ''
+0x052 Spare0 : 0 ''
+0x053 SecondLevelCacheAssociativity : 0 ''
+0x054 VdmAlert : 0
+0x058 KernelReserved : [14] 0
+0x090 SecondLevelCacheSize : 0
+0x094 HalReserved : [16] 0
+0x0d4 InterruptMode : 0
+0x0d8 Spare1 : 0 ''
+0x0dc KernelReserved2 : [17] 0
+0x120 PrcbData : _KPRCB

上次学习了 ETHREAD 的部分属性,其中有一个 ThreadListEntry 属性,存储了线程链表,我们可以把它断链处理,然后任务管理器,OD,windbg就会认为进程里没有线程了,但实际上程序还在正常运行,这说明了操作系统线程调度并不通过 ThreadListEntry 链表

接下来将学习调度链表和等待链表,了解操作系统通过调度链表调度线程的过程。