😊
_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 +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一共有两个这样的链表。
0x1B8
FreezeCount
该字段由 KeFreezeAllThreads
函数和 KeThawAllThreads
操作。《软件调试第二版卷2 9.3.3 P175》 当一个线程的进程被中断到调试器之后,当前线程FreezeCount == 0
,其余线程FreezeCount == 1
。即该字段为 1
时表示这个线程处于冻结状态。
0x1B9
SuspendCount
当前线程被挂起的次数,次数小于1则会恢复执行线程。线程活动时该值为0。该字段加减次数由SuspendThread
和 ResumeThread
(对应于 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
双向链表,一个进程所有的线程都挂在一个链表中,挂的就是这个位置,一共有两个这样的链表。
_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
指向链表头。 下一个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 +0x024 Irql : UChar +0x028 IRR : Uint4B +0x02c IrrActive : Uint4B +0x030 IDR : Uint4B +0x034 KdVersionBlock : Ptr32 Void +0x038 IDT : Ptr32 _KIDTENTRY +0x03c GDT : Ptr32 _KGDTENTRY +0x040 TSS : Ptr32 _KTSS +0x044 MajorVersion : Uint2B +0x046 MinorVersion : Uint2B +0x048 SetMember : Uint4B +0x04c StallScaleFactor : Uint4B +0x050 DebugActive : UChar +0x051 Number : UChar +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 +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
_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 +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 +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 提供的cli
与sti
指令:
cli
:将EFLAGS寄存器的IF = 0
,屏蔽可屏蔽的硬件中断 。
sti
:将EFLAGS寄存器的IF = 1
,响应可屏蔽的硬件中断。
Intel手册3A 2.3:控制寄存器CR4中的CPL、IOPL和VME标志决定着IF标志是可否可以由指令CLI、STTI、POPF、POPFD和IRET修改(可修改IF位的指令)。
_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
_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 +0x004 Dr0 : Uint4B +0x008 Dr1 : Uint4B +0x00c Dr2 : Uint4B +0x010 Dr3 : Uint4B +0x014 Dr6 : Uint4B +0x018 Dr7 : Uint4B +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 +0x09c Edi : Uint4B +0x0a0 Esi : Uint4B +0x0a4 Ebx : Uint4B +0x0a8 Edx : Uint4B +0x0ac Ecx : Uint4B +0x0b0 Eax : Uint4B +0x0b4 Ebp : Uint4B +0x0b8 Eip : Uint4B +0x0bc SegCs : Uint4B +0x0c0 EFlags : Uint4B +0x0c4 Esp : Uint4B +0x0c8 SegSs : Uint4B +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, DpcObject, DeviceQueueObject, EventPairObject, InterruptObject, ProfileObject } KOBJECTS;
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; 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; #define PS_CROSS_THREAD_FLAGS_TERMINATED 0x00000001UL #define PS_CROSS_THREAD_FLAGS_DEADTHREAD 0x00000002UL #define PS_CROSS_THREAD_FLAGS_HIDEFROMDBG 0x00000004UL #define PS_CROSS_THREAD_FLAGS_IMPERSONATING 0x00000008UL #define PS_CROSS_THREAD_FLAGS_SYSTEM 0x00000010UL #define PS_CROSS_THREAD_FLAGS_HARD_ERRORS_DISABLED 0x00000020UL #define PS_CROSS_THREAD_FLAGS_BREAK_ON_TERMINATION 0x00000040UL #define PS_CROSS_THREAD_FLAGS_SKIP_CREATION_MSG 0x00000080UL #define PS_CROSS_THREAD_FLAGS_SKIP_TERMINATION_MSG 0x00000100UL
MmUserProbeAddress、MmHighestUserAddress MmUserProbeAddress
是一个全局变量,指向一个划定用户空间和内核空间界限的地址。
与该值的比较用于确定地址是指向用户空间还是内核空间。
1 2 kd> dd MmUserProbeAddress 80563134 7f ff0000 80000000 7f feffff 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 关键字详解 。
以下举个例子说明一下:
不使用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
没有被用到,在代码中也被优化了。
使用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 0040100 A mov eax,dword ptr ds:[0x4098A8 ]0040100F mov dword ptr ds:[0x4098A8 ],0x5 00401019 mov eax,dword ptr ds:[0x407034 ]0040101 E 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 。 否则,它将返回 FALSE 。 FALSE 的返回值指示对象的运行已启动,并且该对象必须被视为无效,不可再访问该对象。
停止运行保护 (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, ProcessOutOfMemory, ProcessInTransition, ProcessOutTransition, ProcessInSwap, ProcessOutSwap } KPROCESS_STATE; #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; #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中的线程状态转移图。
可参考:
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 ) { 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 ) { ULONG_PTR EndAddress; ULONG_PTR StartAddress; #if defined(_WIN64) ULONG_PTR PageSize; #else #define PageSize PAGE_SIZE #endif if (Length != 0 ) { ASSERT((Alignment == 1 ) || (Alignment == 2 ) || (Alignment == 4 ) || (Alignment == 8 ) || (Alignment == 16 )); StartAddress = (ULONG_PTR)Address; if ((StartAddress & (Alignment - 1 )) == 0 ) { EndAddress = StartAddress + Length - 1 ; if ((StartAddress <= EndAddress) && (EndAddress < MM_USER_PROBE_ADDRESS)) { #if defined(_WIN64) 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; IF DEST = 0 THEN CF <- 0 ; ELSE CF <- 1 ; FI; DEST <- [-(DEST)] SBB DEST, SRC; DEST <- (DEST-SRC-CF);
故常有这样的用法:
如果eax == 0
,那么sbb之后eax的结果还是0。
如果eax != 0
,那么sbb之后eax的结果是0xFFFFFFFF
。
地址对齐 (X+3)&~3 在内核中,经常会判断3环的一个指针是否已经4字节对齐,也常会配合ProbeForRead
、ProbeForWrite
一起使用。
4字节对齐的用法:
如果判断一个地址是否已经4字节对齐:
1 2 3 4 5 6 7 if (pAddr & (~3 )){ }else { }
movsb、movsw、movsd 数据传送指令,将 ESI
地址指向的内容复制到 EDI
指向的地址中。
CF位决定传输方向:
当DF = 0
时,表示正向传送,传送之后ESI
和EDI
的值会增加。
当DF = 1
时,表示反向传送,传送之后ESI
和EDI
的值会减小。
MOVSB
:传送一个字节,之后ESI
和EDI
加/减1
MOVSW
:传送一个字,之后ESI
和EDI
加/减2
MOVSD
:传送一个双字,之后ESI
和EDI
加/减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.
BT系列指令 BT系列指令 :位测试和设置指令。
按照源操作指定的位号(最低位下标为0),测试目的操作数指定的位,并将被测试位拷贝复制到EFLAG得CF。
操作目的操作数指定的位。
指令
含义
实例测试
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 = 0x207 ,CF = 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 = 0x18 1,EFLAG = 0x206,CF = 0。mov ax, 1 1000 0001b;
bts ax,0x7;
BTS执行前:EAX = 0x181,EFLAG = 0x206,CF = 0。 BTS执行后:EAX = 0x181,EFLAG = 0x207 ,CF = 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 = 0x18 1,EFLAG = 0x206,CF = 0。mov ax, 1 1000 0001b;
btc ax,0x7;
BTC执行前:EAX = 0x181,EFLAG = 0x206,CF = 0。 BTC执行后:EAX = 0x10 1,EFLAG = 0x207 ,CF = 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 = 0x10 1,EFLAG = 0x207 ,CF = 1 。
JCC寄存器位 EFLAG寄存器(Intel 64 and IA-32 Architectures Software Developer’s Manual Volume 1:Basic Architecture 3.4.3 ),具体可参看该章节和文章x86—EFLAGS寄存器详解 。
变址寻址 $Imm[r_b,r_i,s] = Imm + r_b + r_i*s$
立即数偏移 $Imm$
基址寄存器$r_b$
变址寄存器$r_i$
比例因子$s$
此时的 esi 已经是一个地址了。lea
最重要的就是说明:目的寄存器已经是一个地址了 。
MOVZ/MOVS 这两类指令将较小的源值复制到较大的目的时使用,这两类指令都把源(在寄存器或内存中)复制到目的寄存器 。上面的movl
指令以寄存器为目的时,会将目的寄存器的高32位都置0。《CS:APP 深入理解计算机系统读书笔记 》
MOVZ 将目的寄存器的剩余的字节填充为0 用于无符号数的扩展
MOVS 通过符号扩展来填充,将目的寄存器的剩余的字节填充为源寄存器的最高位 用于有符号数的扩展