线程
3.线程
线程介绍
线程是需要上下文环境的
线程一定绑定在某个进程上的
内核线程只有一个堆栈(在内核中创建线程如果不指定进程的话,默认是绑定在system进程)
R3中线程有2个堆栈,在R3进R0的时候会切换堆栈,这时候用的就不是R3的堆栈而是R0的(R0和R3的上下文环境)
线程没有cr3的概念,只有绑定在哪个进程上面的概念
内核空间是所有进程共享的
在R3
下,描述线程用的是TEB
在R0
下,描述线程用的是KTHREAD
或者ETHREAD
线程在内核态的时候是可以跑在其他的进程上
线程在WinXP
的时候是32
个链表
线程在Win7
之后的时候不是32
个链表,而是每一个核
32个链表,放到KPCR
上
线程在单核情况下本质就是32个链表组成的数组
线程在就绪状态下会插入11号链表中
线程结构
windbg查看进程线程
获得指定进程的线程
!process 0 0
!process 进程地址
THREAD 88a04538 Cid 0d78.0cb4 Teb: 7ffdd000 Win32Thread: 00000000 WAIT: (UserRequest) UserMode Alertable
86df6b20 SynchronizationTimer
86e13d58 SynchronizationTimer
86e65290 SynchronizationTimer
因为是R3(UserMode)
的所以有TEB
dt _kthread线程地址
kd> dt _kthread 88a04538
ntdll!_KTHREAD
+0x000 Header : _DISPATCHER_HEADER
+0x010 CycleTime : 0x827a0
+0x018 HighCycleTime : 0
+0x020 QuantumTarget : 0x11d245d4//总耗时的时间碎片
+0x028 InitialStack : 0x838c4ed0 Void//栈底
+0x02c StackLimit : 0x838c2000 Void/栈顶
+0x030 KernelStack : 0x838c4648 Void/栈保存的切换前的ESP
+0x034 ThreadLock : 0//锁
+0x038 WaitRegister : _KWAIT_STATUS_REGISTER
+0x039 Running : 0 ''//是否在运行中的状态,运行中是1
+0x03a Alerted : [2] ""//可警惕
+0x03c KernelStackResident : 0y0//线程是否可以扩张
+0x03c ReadyTransition : 0y0
+0x03c ProcessReadyQueue : 0y0
+0x03c WaitNext : 0y0
+0x03c SystemAffinityActive : 0y0
+0x03c Alertable : 0y1//唤醒,标注当前线程是否可以被唤醒
+0x03c GdiFlushActive : 0y0//GDI是否活动刷新
+0x03c UserStackWalkActive : 0y0//用户层的堆栈活动
+0x03c ApcInterruptRequest : 0y0//是否允许apc中断,如果1的话,硬件中断不会触发apc,有成员的时候才会触发apc
+0x03c ForceDeferSchedule : 0y0
+0x03c QuantumEndMigrate : 0y0
+0x03c UmsDirectedSwitchEnable : 0y0
+0x03c TimerActive : 0y0
+0x03c SystemThread : 0y0//判断自己是否是内核线程,1代表内核线程
+0x03c Reserved : 0y000000000000000000 (0)
+0x03c MiscFlags : 0n32
+0x040 ApcState : _KAPC_STATE//apc状态
+0x040 ApcStateFill : [23] "xE???"
+0x057 Priority : 10 ''//优先级,当前线程的优先级的级别
+0x058 NextProcessor : 0//根据亲核性,下次切换线程线程跑在哪个核上
+0x05c DeferredProcessor : 0//如果没有指定下一次泡在那个核上,那么就按照这个默认的
+0x060 ApcQueueLock : 0
+0x064 ContextSwitches : 0xb//当前线程切换了多少次
+0x068 State : 0x5 ''//状态,线程的状态
+0x069 NpxState : 0 ''
+0x06a WaitIrql : 0 ''//等待相关的
+0x06b WaitMode : 1 ''//等待相关的
+0x06c WaitStatus : 0n2//等待相关的
+0x070 WaitBlockList : 0x88a045f8 _KWAIT_BLOCK//等待相关的
+0x074 WaitListEntry : _LIST_ENTRY [ 0x87e7ddbc - 0x80b9e300 ]//等待相关的
+0x074 SwapListEntry : _SINGLE_LIST_ENTRY
+0x07c Queue : (null) //队列
+0x080 WaitTime : 0x390dc
+0x084 KernelApcDisable : 0n0
+0x086 SpecialApcDisable : 0n0
+0x084 CombinedApcDisable : 0
+0x088 Teb : 0x7ffdd000 Void//TEB
+0x090 Timer : _KTIMER//定时器
+0x0b8 AutoAlignment : 0y0
+0x0b8 DisableBoost : 0y0
+0x0b8 EtwStackTraceApc1Inserted : 0y0
+0x0b8 EtwStackTraceApc2Inserted : 0y0
+0x0b8 CalloutActive : 0y0
+0x0b8 ApcQueueable : 0y1
+0x0b8 EnableStackSwap : 0y1
+0x0b8 GuiThread : 0y0
+0x0b8 UmsPerformingSyscall : 0y0
+0x0b8 VdmSafe : 0y0
+0x0b8 UmsDispatched : 0y0
+0x0b8 ReservedFlags : 0y000000000000000000000 (0)
+0x0b8 ThreadFlags : 0n96
+0x0bc ServiceTable : 0x84185b00 Void
+0x0c0 WaitBlock : [4] _KWAIT_BLOCK
+0x120 QueueListEntry : _LIST_ENTRY [ 0x0 - 0x0 ]
+0x128 TrapFrame : 0x838c4c34 _KTRAP_FRAME
+0x12c FirstArgument : 0x00000003 Void
+0x130 CallbackStack : (null)
+0x130 CallbackDepth : 0
+0x134 ApcStateIndex : 0 ''
+0x135 BasePriority : 8 ''
+0x136 PriorityDecrement : 2 ''
+0x136 ForegroundBoost : 0y0010
+0x136 UnusualBoost : 0y0000
+0x137 Preempted : 0 ''//抢占(windows的抢占机制),标记为1可以抢占其他线程
+0x138 AdjustReason : 0 ''
+0x139 AdjustIncrement : 0 ''
+0x13a PreviousMode : 1 ''
+0x13b Saturation : 0 ''
+0x13c SystemCallNumber : 0x185
+0x140 FreezeCount : 0
+0x144 UserAffinity : _GROUP_AFFINITY
+0x150 Process : 0x88b3f030 _KPROCESS//谁创建了这个线程
+0x154 Affinity : _GROUP_AFFINITY
+0x160 IdealProcessor : 0
+0x164 UserIdealProcessor : 0
+0x168 ApcStatePointer : [2] 0x88a04578 _KAPC_STATE
+0x170 SavedApcState : _KAPC_STATE
+0x170 SavedApcStateFill : [23] "???"
+0x187 WaitReason : 0x6 ''
+0x188 SuspendCount : 0 ''
+0x189 Spare1 : 0 ''
+0x18a OtherPlatformFill : 0 ''
+0x18c Win32Thread : (null) //win32线程,如果是UI线程,那么会多一个Win32结构体
+0x190 StackBase : 0x838c5000 Void
+0x194 SuspendApc : _KAPC
+0x194 SuspendApcFill0 : [1] "???"
+0x195 ResourceIndex : 0 ''
+0x194 SuspendApcFill1 : [3] "???"
+0x197 QuantumReset : 0x12 ''
+0x194 SuspendApcFill2 : [4] "???"
+0x198 KernelTime : 0
+0x194 SuspendApcFill3 : [36] "???"
+0x1b8 WaitPrcb : (null)
+0x194 SuspendApcFill4 : [40] "???"
+0x1bc LegoData : (null)
+0x194 SuspendApcFill5 : [47] "???"
+0x1c3 LargeStack : 0 ''
+0x1c4 UserTime : 0
+0x1c8 SuspendSemaphore : _KSEMAPHORE
+0x1c8 SuspendSemaphorefill : [20] "???"
+0x1dc SListFaultCount : 0
+0x1e0 ThreadListEntry : _LIST_ENTRY [ 0x88b3f05c - 0x86df4f28 ]//创建这个线程的进程创建的所有线程的列表
+0x1e8 MutantListHead : _LIST_ENTRY [ 0x88a04720 - 0x88a04720 ]
+0x1f0 SListFaultAddress : (null)
+0x1f4 ThreadCounters : (null)
+0x1f8 XStateSave : (null)
内核态的线程的堆栈
+0x028 InitialStack : 0x838c4ed0 Void
+0x02c StackLimit : 0x838c2000 Void
+0x030 KernelStack : 0x838c4648 Void
内核线程的栈
图
为什么这个栈底并不在底部,看下面的KiFastCallEntry
将ebp
减去了29ch
每一个线程在创建堆栈的时候预留了29c
个位置
这预留的29c = trap_frame + 浮点
所以如果要求出整个栈的栈底的话需要InitialStack + 29c
trap_frame
作用:fastcall
进内核的时候需要提供一个存储环境的地方
浮点
线程切换的时候使用
KernelStack
并不是常用的pop/push
的那个ESP
,而是线程切换的时候将老线程的esp保存在这个里,线程换恢复再从这里取出,恢复堆栈(类似Linux
中的prev ebp
)
Alerted 可警惕
+0x03a Alerted : [2] ""//可警惕
数组是0
代表是0环的可警惕,数组是1
是3环的可警惕
可警惕的程度代表当线程正处在一种可以被唤醒的假等待
Sleep
是死等,SleepEx
是带有警惕性的等待
KernelStackResident 线程扩张
默认情况下线程的栈被创建的时候只有12k(0x3000)
但是整个太小了,再调用UI的时候不够,需要扩张
而这个KernelStackResident
记录了线程是否允许拓张,为1
代表可以扩张
Alertable 是否可以唤醒
这个相当于一个开关,当他置为1的时候,不一定可以唤醒,在此情况下加上了可警惕就绝对可以唤醒
ApcState apc状态
kd> dt _kapc_state
ntdll!_KAPC_STATE
+0x000 ApcListHead : [2] _LIST_ENTRY
+0x010 Process : Ptr32 _KPROCESS//当先线程的上下文环境是谁
+0x014 KernelApcInProgress : UChar
+0x015 KernelApcPending : UChar
+0x016 UserApcPending : UChar
Process
Process
:当前线程的进程所属是谁
假设A
进程的0x1234
指向的是A
,B
进程的0x1234
指向的是B
,那么如何正确的让A
进程读B
进程的地址,也就是如何知道读的是不是B
进程
CR3
是绑定在进程上的,可以通过改变CR3
来让改变线程的上下文环境
,让线程跑到其他进程
上
Priority 线程优先级
线程初始优先级是8
数字越大,优先级越低