自制内核数据结构/指令 快速查询

😊

_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
0x1B8 FreezeCount 该字段由 KeFreezeAllThreads函数和 KeThawAllThreads 操作。《软件调试第二版卷2 9.3.3 P175》
当一个线程的进程被中断到调试器之后,当前线程FreezeCount == 0,其余线程FreezeCount == 1。即该字段为 1 时表示这个线程处于冻结状态。
0x1B9 SuspendCount 当前线程被挂起的次数,次数小于1则会恢复执行线程。线程活动时该值为0。该字段加减次数由SuspendThreadResumeThread (对应于 NtSuspendThread 内核服务KeSuspendThread)操作。
0x1BA IdealProcessor 指明了在多处理器上该线程的理想处理器。

_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

成员:

偏移 名称 作用
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

_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而引入到系统中的

_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的重要成员/结构进行说明介绍。

可参考: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:未知类别

_KPCR

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

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结构) 这样设计的目的是为了查找方便。

_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

EFLAGS

Intel 提供的clisti指令:

cli:将EFLAGS寄存器的IF = 0,屏蔽可屏蔽的硬件中断

sti:将EFLAGS寄存器的IF = 1,响应可屏蔽的硬件中断。

Intel手册3A 2.3:控制寄存器CR4中的CPL、IOPL和VME标志决定着IF标志是可否可以由指令CLI、STTI、POPF、POPFD和IRET修改(可修改IF位的指令)。

58.png

1.png

_KTRAP_FRAME

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
kd> dt _KTRAP_FRAME -v
nt!_KTRAP_FRAME
struct _KTRAP_FRAME, 35 elements, 0x8c bytes
+0x000 DbgEbp : Uint4B
+0x004 DbgEip : Uint4B
+0x008 DbgArgMark : Uint4B
+0x00c DbgArgPointer : Uint4B
+0x010 TempSegCs : Uint4B
+0x014 TempEsp : Uint4B
+0x018 Dr0 : Uint4B
+0x01c Dr1 : Uint4B
+0x020 Dr2 : Uint4B
+0x024 Dr3 : Uint4B
+0x028 Dr6 : Uint4B
+0x02c Dr7 : Uint4B
+0x030 SegGs : Uint4B
+0x034 SegEs : Uint4B
+0x038 SegDs : Uint4B
+0x03c Edx : Uint4B
+0x040 Ecx : Uint4B
+0x044 Eax : Uint4B
+0x048 PreviousPreviousMode : Uint4B
+0x04c ExceptionList : Ptr32 to struct _EXCEPTION_REGISTRATION_RECORD, 2 elements, 0x8 bytes
+0x050 SegFs : Uint4B
+0x054 Edi : Uint4B
+0x058 Esi : Uint4B
+0x05c Ebx : Uint4B
+0x060 Ebp : Uint4B
+0x064 ErrCode : Uint4B
+0x068 Eip : Uint4B
+0x06c SegCs : Uint4B
+0x070 EFlags : Uint4B
+0x074 HardwareEsp : Uint4B
+0x078 HardwareSegSs : Uint4B
+0x07c V86Es : Uint4B
+0x080 V86Ds : Uint4B
+0x084 V86Fs : Uint4B
+0x088 V86Gs : Uint4B

40.png

_CONTEXT

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
kd> dt _CONTEXT -v
nt!_CONTEXT
struct _CONTEXT, 25 elements, 0x2cc bytes
+0x000 ContextFlags : Uint4B

//调试寄存器组,ContextFlags 设置为 CONTEXT_DEBUG_REGISTERS
//注意:CONTEXT_DEBUG_REGISTERS 没有被包含在 CONTEXT_FULL 中。
+0x004 Dr0 : Uint4B
+0x008 Dr1 : Uint4B
+0x00c Dr2 : Uint4B
+0x010 Dr3 : Uint4B
+0x014 Dr6 : Uint4B
+0x018 Dr7 : Uint4B

//浮点寄存器组,ContextFlags 设置为 CONTEXT_FLOATING_POINT
+0x01c FloatSave : struct _FLOATING_SAVE_AREA, 9 elements, 0x70 bytes

//段寄存器组,ContextFlags 字包含 CONTEXT_SEGMENTS
+0x08c SegGs : Uint4B
+0x090 SegFs : Uint4B
+0x094 SegEs : Uint4B
+0x098 SegDs : Uint4B

//通用数据寄存器(整型寄存器)组,ContextFlags 字包含 CONTEXT_INTEGER
+0x09c Edi : Uint4B
+0x0a0 Esi : Uint4B
+0x0a4 Ebx : Uint4B
+0x0a8 Edx : Uint4B
+0x0ac Ecx : Uint4B
+0x0b0 Eax : Uint4B

//控制寄存器组,ContextFlags 字包含 CONTEXT_CONTROL
+0x0b4 Ebp : Uint4B
+0x0b8 Eip : Uint4B
+0x0bc SegCs : Uint4B
+0x0c0 EFlags : Uint4B
+0x0c4 Esp : Uint4B
+0x0c8 SegSs : Uint4B

//拓展寄存器组,ContextFlags 字包含 CONTEXT_EXTENDED_REGISTERS
+0x0cc ExtendedRegisters : [512] UChar

_EXCEPTION_RECORD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
typedef struct _EXCEPTION_RECORD {
NTSTATUS ExceptionCode;
ULONG ExceptionFlags;
struct _EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress;
ULONG NumberParameters;
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;

kd> dt _EXCEPTION_RECORD -v
nt!_EXCEPTION_RECORD
struct _EXCEPTION_RECORD, 6 elements, 0x50 bytes
+0x000 ExceptionCode : Int4B //异常代码
+0x004 ExceptionFlags : Uint4B //异常标志
+0x008 ExceptionRecord : Ptr32 _EXCEPTION_RECORD //相关的下一个异常
+0x00c ExceptionAddress : Ptr32 Void //异常发生的地址
+0x010 NumberParameters : Uint4B //参数数组中的元素个数
+0x014 ExceptionInformation : [15] Uint4B //参数数组

_KOBJECTS

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
#define DISPATCHER_OBJECT_TYPE_MASK 0x7
typedef enum _KOBJECTS {
EventNotificationObject = 0, //通知类型对象
EventSynchronizationObject = 1, //事件同步对象
MutantObject = 2,
ProcessObject = 3,
QueueObject = 4,
SemaphoreObject = 5,
ThreadObject = 6,
Spare1Object = 7,
TimerNotificationObject = 8,
TimerSynchronizationObject = 9,
Spare2Object = 10,
Spare3Object = 11,
Spare4Object = 12,
Spare5Object = 13,
Spare6Object = 14,
Spare7Object = 15,
Spare8Object = 16,
Spare9Object = 17,
ApcObject, //18
DpcObject, //19
DeviceQueueObject, //20
EventPairObject, //21
InterruptObject, //22
ProfileObject //23
} KOBJECTS;

//
// Enumerated kernel types
//
// Kernel object types.
//
// N.B. There are really two types of event objects; NotificationEvent and
// SynchronizationEvent. The type value for a notification event is 0,
// and that for a synchronization event 1.
//
// N.B. There are two types of new timer objects; NotificationTimer and
// SynchronizationTimer. The type value for a notification timer is
// 8, and that for a synchronization timer is 9. These values are
// very carefully chosen so that the dispatcher object type AND'ed
// with 0x7 yields 0 or 1 for event objects and the timer objects.

ETHREAD.CrossThreadFlags

CrossThreadFlags域是一些针对跨线程访问的标志位(《Windows内核原理与实现3.4 P128》),包括:

  • Terminated(线程已执行终止操作)
  • DeadThread(创建失败)
  • HideFromDebugger(该线程对于调试器不可见)
  • Activelmpersonationlnfo(线程正在模仿)
  • SystemThread(是一个系统线程)
  • HardErrorsAreDisabled(对于该线程,硬件错误无效)
  • BreakOnTermination(调试器在线程终止时停下该线程,即要求退出进程时中断到调试器)
  • SkipCreationMsg(不向调试器发送创建消息)
  • SkipTerminationMsg(不向调试器发送终止消息)。

在创建线程时,经常将CrossThreadFlags.SystemThread设置为1,来伪装成系统线程,这样普通线程就没有办法将自己Kill了。

由定义可以知道,低1字节的8bit为常用位,其他高位为未定义或者不常用。

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
union {

ULONG CrossThreadFlags;
// The following fields are for the debugger only. Do not use.
// Use the bit definitions instead.以下字段仅适用于调试器。不使用。请改用位定义。
struct {
ULONG Terminated : 1;
ULONG DeadThread : 1;
ULONG HideFromDebugger : 1;
ULONG ActiveImpersonationInfo : 1;
ULONG SystemThread : 1;
ULONG HardErrorsAreDisabled : 1;
ULONG BreakOnTermination : 1;
ULONG SkipCreationMsg : 1;
ULONG SkipTerminationMsg : 1;
};
};
ACCESS_MASK GrantedAccess;
// Flags for cross thread access. Use interlocked operations
// via PS_SET_BITS etc.

// Used to signify that the delete APC has been queued or the
// thread has called PspExitThread itself.
#define PS_CROSS_THREAD_FLAGS_TERMINATED 0x00000001UL

// Thread create failed
#define PS_CROSS_THREAD_FLAGS_DEADTHREAD 0x00000002UL

// Debugger isn't shown this thread
#define PS_CROSS_THREAD_FLAGS_HIDEFROMDBG 0x00000004UL

// Thread is impersonating
#define PS_CROSS_THREAD_FLAGS_IMPERSONATING 0x00000008UL

// This is a system thread
#define PS_CROSS_THREAD_FLAGS_SYSTEM 0x00000010UL

// Hard errors are disabled for this thread
#define PS_CROSS_THREAD_FLAGS_HARD_ERRORS_DISABLED 0x00000020UL

// We should break in when this thread is terminated
#define PS_CROSS_THREAD_FLAGS_BREAK_ON_TERMINATION 0x00000040UL

// This thread should skip sending its create thread message
#define PS_CROSS_THREAD_FLAGS_SKIP_CREATION_MSG 0x00000080UL

// This thread should skip sending its final thread termination message
#define PS_CROSS_THREAD_FLAGS_SKIP_TERMINATION_MSG 0x00000100UL

MmUserProbeAddress、MmHighestUserAddress

MmUserProbeAddress是一个全局变量,指向一个划定用户空间和内核空间界限的地址。

与该值的比较用于确定地址是指向用户空间还是内核空间。

1
2
kd> dd MmUserProbeAddress
80563134 7fff0000 80000000 7ffeffff 00000000

可以看到MmUserProbeAddress == 0x7fff0000,则用户空间(3环)可以访问的最大地址是0x7fff0000

源码中有:PreviousSuspendCount >= MmUserProbeAddress,就会MmUserProbeAddress = NULL。就会抛出异常,因为NULL地址不可写。实际上源码中用了try-except,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
try {

Mode = KeGetPreviousMode();

if ( Mode != KernelMode ) {
if (ARGUMENT_PRESENT(PreviousSuspendCount)) {
ProbeForWriteUlong(PreviousSuspendCount);
}
}
} except (EXCEPTION_EXECUTE_HANDLER) {

return GetExceptionCode();
}

#define ARGUMENT_PRESENT(p) ((p) != NULL)

#define ProbeForWriteUlong(Address) {
if ((Address) >= (ULONG * const)MM_USER_PROBE_ADDRESS) {
*(volatile ULONG * const)MM_USER_PROBE_ADDRESS = 0;
}

*(volatile ULONG *)(Address) = *(volatile ULONG *)(Address);
}

可以看到,当try中的ProbeForWriteUlong(PreviousSuspendCount)执行到*(volatile ULONG * const)MM_USER_PROBE_ADDRESS = 0就会触发异常(因为NULL地址不可读写),然后执行except中的代码。

0环下大部分的Nt-函数都会对来自3环调用的参数进行检查,常用到MmUserProbeAddress

1
2
3
4
5
6
7
8
9
#define MM_HIGHEST_USER_ADDRESS *MmHighestUserAddress
#define MM_SYSTEM_RANGE_START *MmSystemRangeStart
#define MM_USER_PROBE_ADDRESS *MmUserProbeAddress
#define PTE_BASE 0xc0000000

MmHighestUserAddress = 0x7ffeffff
MmUserProbeAddress = 0x7fff0000
MmSystemRangeStart = 0x80000000
MmUserProbeAddress = MmHighestUserAddress + 1
  • 用户空间可以访问的最高地址是MmHighestUserAddress(0x7ffeffff),从MmUserProbeAddress(0x7fff0000)开始就不能访问了。一般文献中讲Windows系统中用户空间与系统空间的分界线是0x80000000,而这里之所以是0x7fff0000而不是0x80000000,是因为在分界下面留了64KB的空间不让访问,以此作为隔离区。(64KB = 2^6*2^10共16位)

  • MmUserProbeAddress是一个界限,该地址不可用

    • 3环可用地址<=MmHighestUserAddress
    • 3环可用地址<MmUserProbeAddress

这部分内容的讲解在《Windows内核情景分析5.2 P253》有、《Windows内核原理与实现4.2 P207》。

volatile类型修饰符

volatile关键字是一种类型修饰符。Volatile意思是“易变的”,应该解释为“直接存取原始内存地址”比较合适。这个关键词volatile总是与优化有关,多线程读取变量时也用到。

使用volatile修饰的变量,在编译这样的变量时,编译器一般都会作减少存取内存的优化,但有可能会读脏数据。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问

但是也不能频繁的使用volatile,否则会影响程序运行的性能。具体可以参考C/C++ 中 volatile 关键字详解

以下举个例子说明一下:

  1. 不使用volatile时编译Release版本(vc6编译器):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include "stdafx.h"

    int TestNum = 0;
    int main(int argc, char* argv[])
    {
    int temp = 0;

    TestNum = 2;
    temp = TestNum;
    TestNum = 5;

    getchar();
    return 0;
    }

    Release版本编译代码如下:

    1
    2
    3
    4
    5
    00401000	mov eax,dword ptr ds:[0x407034]
    00401005 mov dword ptr ds:[0x4098A8],0x5
    0040100F dec eax
    00401010 mov dword ptr ds:[0x407034],eax
    ...
    • 0x4098A8即为变量TestNum的地址,由于源代码最后一次赋值是TestNum = 5,则代码TestNum = 2是无用的被优化了。
    • 代码temp = TestNum中,赋值后temp没有被用到,在代码中也被优化了。
  2. 使用volatile修饰变量时编译Release版本(vc6编译器):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include "stdafx.h"

    volatile int TestNum = 0;
    int main(int argc, char* argv[])
    {
    int temp = 0;

    TestNum = 2;
    temp = TestNum;
    TestNum = 5;

    getchar();
    return 0;
    }

    Release版本编译代码如下:

    1
    2
    3
    4
    5
    6
    7
    00401000	mov dword ptr ds:[0x4098A8],0x2
    0040100A mov eax,dword ptr ds:[0x4098A8]
    0040100F mov dword ptr ds:[0x4098A8],0x5
    00401019 mov eax,dword ptr ds:[0x407034]
    0040101E dec eax
    0040101F mov dword ptr ds:[0x407034],eax
    ...

    可以看到,两次对TestNum的赋值都没有被优化,对于取地址0x4098A8中的值给temp赋值的代码也没有优化。

ExAcquireRundownProtection

ExAcquireRundownProtection函数:尝试获取共享对象的运行保护,以便调用方可以安全地访问该对象。

当运行保护生效时,驱动程序可以安全地访问对象,而不会有在访问完成之前删除该对象的风险。

1
2
3
NTKERNELAPI BOOLEAN FASTCALL ExAcquireRundownProtection (
IN PEX_RUNDOWN_REF RunRef //跟踪关联的共享对象的运行状态
)
  • 参数RunRef:指向先前对 ExInitializeRundownProtection 例程的调用初始化的EX_RUNDOWN_REF结构的指针。 运行保护例程使用此结构来跟踪关联的共享对象的运行状态。 此结构对驱动程序不透明。
  • 返回值:如果例程成功获取调用方的运行保护,ExAcquireRundownProtection 将返回 TRUE。 否则,它将返回 FALSEFALSE 的返回值指示对象的运行已启动,并且该对象必须被视为无效,不可再访问该对象。

停止运行保护(Run-Down Protection):从 Windows XP 开始,内核驱动就支持停运保护机制。驱动通过停运机制可以安全地访问在系统内存中的对象,通常这些对象是由其他内核驱动创建和销毁的。

拥有共享对象的驱动程序可以允许其他驱动程序获取和释放对象的运行时保护。 当停运保护生效时,除对象的所有者外,其他驱动可以访问该对象而不用担心在访问结束前该对象会被其所有者删除。在访问开始之前,要访问的驱动会提出对目标对象实施停运保护的请求。对于一个存活周期较长的对象来说,这类请求几乎都是被允许的。当访问结束时,执行访问的驱动会卸除之前对对象实施的停运保护。

参考:同步下的资源互斥:停运保护(Run-Down Protection)机制

_KPROCESS_STATE

_KPROCESS_STATE:表示进程当前的状态,共有以下5种。

1
2
3
4
5
6
7
8
9
10
11
12
13
typedef enum _KPROCESS_STATE {
ProcessInMemory, //0,表示进程的虚拟地址空间内容在物理内存中
ProcessOutOfMemory, //1,表示进程的虚拟地址空间内容已被换出物理内存
ProcessInTransition, //2,表示进程的虚拟地址空间内容不在物理内存中,但已请求换入
ProcessOutTransition, //3,表示进程的虚拟地址空间内容存在于物理内存中,但已请求换出
ProcessInSwap, //4,表示正在将进程的虚拟地址空间内容换入物理内存,换入完成后,状态将变更为 ProcessInMemory
ProcessOutSwap //5,表示正在将进程的虚拟地址空间内容换出物理内存,换出完成后,状态将变更为 ProcessOutOfMemory
} KPROCESS_STATE;

// Process State Enumerated Type Values
#define ProcessInMemory 0x0
#define ProcessOutOfMemory 0x1
#define ProcessInTransition 0x2

可参考Windows线程调度学习(一)Windows线程调度学习(一)Windows线程调度学习(一)

_KTHREAD_STATE

_KTHREAD_STATE:表示线程当前的调度状态

共有以下7种(WRK有9种)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef enum _KTHREAD_STATE {
Initialized,
Ready,
Running,
Standby,
Terminated,
Waiting,
Transition
} KTHREAD_STATE;

// Thread State Enumerated Type Values
#define Initialized 0x0
#define Ready 0x1
#define Running 0x2
#define Standby 0x3
#define Terminated 0x4
#define Waiting 0x5
状态 枚举值 说明
Initialized 0 已初始化(Initialized):说明一个线程对象的内部状态已经初始化,这是线程创建过程中的一个内部状态,此时线程尚未加入到进程的线程链表中,也没有启动。
Ready 1 就绪(Ready):代表该线程已经准备就绪,等待被调度执行。当线程调度器选择一个线程来执行时,它只考虑处于就绪状态的线程。此时,线程已被加入到某个处理器的就绪线程链表中。
Running 2 运行(Running):线程正在运行。该线程一直占有处理器,直至分到的时限结束,或者被一个更高优先级的线程抢占,或者线程终止,或者主动放弃处理器执行权,或者进入等待状态。
Standby 3 备用(Standby):处于备用状态的线程已经被选中作为某个处理器上下一个要运行的线程。对于系统中的每个处理器,只能有一个线程可以处于备用状态。然而,一个处于备用状态的线程在真正被执行以前,有可能被更高优先级的线程抢占。
Terminated 4 已终止(Terminated):表示线程已经完成任务,正在进行资源回收。KeTerminateThread``函数用于设置此状态。
Waiting 5 等待(Waiting):表示一个线程正在等待某个条件,比如等待一个分发器对象变成有信号状态,也可以等待多个对象。当等待的条件满足时,线程或者立即开始运行,或者回到就绪状态。
Transition 6 转移(Transition):处于转移状态的线程已经准备好运行,但是它的内核栈不在内存中。一旦它的内核栈被换人内存,则该线程进入就绪状态。
DeferredReady 7 延迟的就绪(DeferredReady):处于延迟的就绪状态的线程也已经准备好可以运行了,但是,与就绪状态不同的是,它尚未确定在哪个处理器上运行。当有机会被调度时,或者直接转人备用状态,或者转到就绪状态。因此,此状态是为了多处理器而引入的,对于单处理器系统没有意义。(WRK中)
GateWait 8 门等待(GateWait):线程正在等待一个门对象。此状态与等待状态类似,只不过它是专门针对门对象而设计。关于门对象和门等待,请参考5.4.3节。(WRK中)

与传统的操作系统中的调度状态相比,这里可能多了后三个状态。这些状态之间的转移图并不复杂,图3.5显示了WRK中的线程状态转移图。

1.png

可参考:

CMPXCHG

关于指令cmpxchg

Opcode/Instruction Op/En 64-Bit Mode Compat/Leg Mode Description
0F B1/r CMPXCHG r/m32, r32 MR Valid Valid* CMPXCHG r/m32, r32
Compare EAX with r/m32. If equal, ZF is set and r32 is loaded into r/m32. Else, clear ZF and load r/m32 into EAX.

ProbeForRead

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
NTKERNELAPI VOID NTAPI ProbeForRead(
IN CONST VOID *Address,
IN SIZE_T Length,
IN ULONG Alignment
)
/*++
Routine Description:
This function probes a structure for read accessibility and ensures
correct alignment of the structure. If the structure is not accessible
or has incorrect alignment, then an exception is raised.
Arguments:
Address - Supplies a pointer to the structure to be probed.
Length - Supplies the length of the structure.
Alignment - Supplies the required alignment of the structure expressed
as the number of bytes in the primitive datatype (e.g., 1 for char,
2 for short, 4 for long, and 8 for quad).
Return Value: None.
--*/
{
PAGED_CODE();

ASSERT(((Alignment) == 1) || ((Alignment) == 2) ||
((Alignment) == 4) || ((Alignment) == 8) ||
((Alignment) == 16));

if ((Length) != 0) {
if (((ULONG_PTR)(Address) & ((Alignment) - 1)) != 0) {
ExRaiseDatatypeMisalignment();

} else if ((((ULONG_PTR)(Address) + (Length)) < (ULONG_PTR)(Address)) ||
(((ULONG_PTR)(Address) + (Length)) > (ULONG_PTR)MM_USER_PROBE_ADDRESS)) {
ExRaiseAccessViolation();
}
}
}

ProbeForWrite

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
VOID ProbeForWrite (
IN PVOID Address,
IN SIZE_T Length,
IN ULONG Alignment
)
/*++
Routine Description:
This function probes a structure for write accessibility and ensures
correct alignment of the structure. If the structure is not accessible
or has incorrect alignment, then an exception is raised.
Arguments:
Address - Supplies a pointer to the structure to be probed.
Length - Supplies the length of the structure.
Alignment - Supplies the required alignment of the structure expressed
as the number of bytes in the primitive datatype (e.g., 1 for char,
2 for short, 4 for long, and 8 for quad).
Return Value: None.
--*/

{
ULONG_PTR EndAddress;
ULONG_PTR StartAddress;
#if defined(_WIN64)
ULONG_PTR PageSize;
#else
#define PageSize PAGE_SIZE
#endif

// If the structure has zero length, then do not probe the structure for write accessibility or alignment.
if (Length != 0) {

// If the structure is not properly aligned, then raise a data misalignment exception.
ASSERT((Alignment == 1) || (Alignment == 2) ||
(Alignment == 4) || (Alignment == 8) ||
(Alignment == 16));

StartAddress = (ULONG_PTR)Address;
if ((StartAddress & (Alignment - 1)) == 0) {

// Compute the ending address of the structure and probe for
// write accessibility.
EndAddress = StartAddress + Length - 1;
if ((StartAddress <= EndAddress) &&
(EndAddress < MM_USER_PROBE_ADDRESS)) {

// N.B. Only the contents of the buffer may be probed.
// Therefore the starting byte is probed for the
// first page, and then the first byte in the page
// for each succeeding page.

#if defined(_WIN64)
//
// If this is a Wow64 process, then the native page is 4K, which
// could be smaller than the native page size/
if (PsGetCurrentProcess()->Wow64Process != NULL) {
PageSize = PAGE_SIZE_X86NT;
} else {
PageSize = PAGE_SIZE;
}
#endif

EndAddress = (EndAddress & ~(PageSize - 1)) + PageSize;
do {
*(volatile CHAR *)StartAddress = *(volatile CHAR *)StartAddress;

StartAddress = (StartAddress & ~(PageSize - 1)) + PageSize;
} while (StartAddress != EndAddress);

return;

} else {
ExRaiseAccessViolation();
}

} else {
ExRaiseDatatypeMisalignment();
}
}

return;
}

neg、sbb

neg和sbb指令来判断函数返回值是0还是非0。

NEG、SBB 指令在Intel手册2b中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
NEG DEST;	// DEST = r/m8, r/m16, r/m32, r/m64
// Description
IF DEST = 0
THEN CF <- 0;
ELSE CF <- 1;
FI;
DEST <- [-(DEST)]

/*
neg X:对 X 求补 == 对 X 求反+1 == 求一个数的相反数
not X:对 X 求反

neg X:
X == 0,CF = 0
X != 0,CF = 1
*/

SBB DEST, SRC;
// Description
DEST <- (DEST-SRC-CF);

故常有这样的用法:

1
2
neg eax;
sbb eax, eax;

如果eax == 0,那么sbb之后eax的结果还是0。

如果eax != 0,那么sbb之后eax的结果是0xFFFFFFFF

地址对齐 (X+3)&~3

在内核中,经常会判断3环的一个指针是否已经4字节对齐,也常会配合ProbeForReadProbeForWrite一起使用。

4字节对齐的用法:

1
2
3
4
(pAddr + 3) &= (~3);	//对 pAddr 向上对齐。~ 是按位取反

//如果对齐前 pAddr == 123,对齐后 pAddr == 124
//如果对齐前 pAddr == 125,对齐后 pAddr == 128

如果判断一个地址是否已经4字节对齐:

1
2
3
4
5
6
7
if(pAddr & (~3))
{
//没有4字节对齐
}else
{
//已经4字节对齐
}

movsb、movsw、movsd

数据传送指令,将 ESI 地址指向的内容复制到 EDI 指向的地址中。

CF位决定传输方向:

  • DF = 0 时,表示正向传送,传送之后ESIEDI的值会增加。
  • DF = 1 时,表示反向传送,传送之后ESIEDI的值会减小。

MOVSB:传送一个字节,之后ESIEDI加/减1
MOVSW:传送一个字,之后ESIEDI加/减2
MOVSD:传送一个双字,之后ESIEDI加/减4

stosb、stosw、stosd

这三个指令把al/ ax/ eax的内容复制到EDI指向的内存单元中。

DF位影响EDI的加减。

CLC、CLD指令

CLC:Clears the CF flag in the EFLAGS register.

CLD:Clears the DF flag in the EFLAGS register.

1
2
clc;	//CF = 0
cld; //DF = 0

BT系列指令

BT系列指令:位测试和设置指令。

  1. 按照源操作指定的位号(最低位下标为0),测试目的操作数指定的位,并将被测试位拷贝复制到EFLAG得CF。
  2. 操作目的操作数指定的位。
指令 含义 实例测试
BT DEST,SRC 将 SRC 指定的 DEST 中一位的数值复制到 CF。 mov ax, 1 0000 0001b;
bt ax,0x7;
BT执行前:EAX = 0x101,EFLAG = 0x206,CF = 0。
BT执行前:EAX = 0x101,EFLAG = 0x206,CF = 0。

mov ax, 1 1000 0001b;
bt ax,0x7;
BT执行前:EAX = 0x181,EFLAG = 0x206,CF = 0。
BT执行后:EAX = 0x101,EFLAG = 0x207CF = 1
BTS DEST,SRC BTS 将 SRC 指定的 DEST 中一位的数值复制到 CF,且将 DEST 中该位置位。 mov ax, 1 0000 0001b;
bts ax,0x7;
BTS执行前:EAX = 0x101,EFLAG = 0x206,CF = 0。
BTS执行后:EAX = 0x181,EFLAG = 0x206,CF = 0。

mov ax, 1 1000 0001b;
bts ax,0x7;
BTS执行前:EAX = 0x181,EFLAG = 0x206,CF = 0。
BTS执行后:EAX = 0x181,EFLAG = 0x207CF = 1
BTC DEST,SRC BTC 将 SRC 指定的 DEST 中一位的数值复制到 CF,且将 DEST 中该位取反。 mov ax, 1 0000 0001b;
btc ax,0x7;
BTC执行前:EAX = 0x101,EFLAG = 0x206,CF = 0。
BTC执行后:EAX = 0x181,EFLAG = 0x206,CF = 0。

mov ax, 1 1000 0001b;
btc ax,0x7;
BTC执行前:EAX = 0x181,EFLAG = 0x206,CF = 0。
BTC执行后:EAX = 0x101,EFLAG = 0x207CF = 1
BTR DEST,SRC BTR将 SRC 指定的 DEST 中一位的数值复制到 CF,且将 DEST中该位复位。 mov ax, 1 0000 0001b;
btr ax,0x7;
BTR执行前:EAX = 0x101,EFLAG = 0x206,CF = 0。
BTR执行后:EAX = 0x101,EFLAG = 0x206,CF = 0。

mov ax, 1 1000 0001b;
btr ax,0x7;
BTR执行前:EAX = 0x181,EFLAG = 0x206,CF = 0。
BTR执行后:EAX = 0x101,EFLAG = 0x207CF = 1

JCC寄存器位

EFLAG寄存器(Intel 64 and IA-32 Architectures Software Developer’s Manual Volume 1:Basic Architecture 3.4.3),具体可参看该章节和文章x86—EFLAGS寄存器详解

1.png

2.png

变址寻址

$Imm[r_b,r_i,s] = Imm + r_b + r_i*s$

  1. 立即数偏移 $Imm$
  2. 基址寄存器$r_b$
  3. 变址寄存器$r_i$
  4. 比例因子$s$
1
lea  esi, ds:14h[10*4]	//esi = 0x54

此时的 esi 已经是一个地址了。lea 最重要的就是说明:目的寄存器已经是一个地址了

MOVZ/MOVS

这两类指令将较小的源值复制到较大的目的时使用,这两类指令都把源(在寄存器或内存中)复制到目的寄存器。上面的movl指令以寄存器为目的时,会将目的寄存器的高32位都置0。《CS:APP 深入理解计算机系统读书笔记

  • MOVZ将目的寄存器的剩余的字节填充为0用于无符号数的扩展
  • MOVS通过符号扩展来填充,将目的寄存器的剩余的字节填充为源寄存器的最高位用于有符号数的扩展