-
-
[原创]Windows内核学习笔记之线程(上)
-
2021-12-20 17:18 10694
-
一.线程基本概念
在操作系统中,进程只是提供资源,它并没有执行指令的作用。真正来执行指令的是线程,线程是一个指令执行序列,它可以直接访问所属进程中的资源。每个进程至少要有一个线程,这样才可以使用进程的资源来执行各种指令,而每个线程在任一时刻一定属于某一特定的进程。
线程不仅仅是一个控制流,它还有更多的内容。线程的调用栈记录了它作为控制流的状态信息,包括每一层函数调用和返回指令地址。线程一定隶属于某个进程,其控制流可以访问这个进程中的资源,包括所有的内存数据以及系统分配给此进程的其他资源。一个进程可以有多个线程,由于这些线程隶属于同一进程,所以它们之间相互通信要方便很多,毕竟几乎所有的资源(不是全部)对它们来说都是共享的。因此,线程概念的引入有时候也称为轻量进程。
二.线程数据结构
为了保存线程的各种信息,在用户层和内核层都有相应的数据结构用来存储线程的信息。
1.用户层数据结构
与描述进程用户空间信息的PEB类型,NT内核定义了线程环境块(TEB)来描述线程的用户空间信息,包括用户栈,异常处理,错误码,线程局部存储等,该结构定义如下:
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 | kd> dt _TEB nt!_TEB + 0x000 NtTib : _NT_TIB + 0x01c EnvironmentPointer : Ptr32 Void + 0x020 ClientId : _CLIENT_ID + 0x028 ActiveRpcHandle : Ptr32 Void + 0x02c ThreadLocalStoragePointer : Ptr32 Void + 0x030 ProcessEnvironmentBlock : Ptr32 _PEB + 0x034 LastErrorValue : Uint4B + 0x038 CountOfOwnedCriticalSections : Uint4B + 0x03c CsrClientThread : Ptr32 Void + 0x040 Win32ThreadInfo : Ptr32 Void + 0x044 User32Reserved : [ 26 ] Uint4B + 0x0ac UserReserved : [ 5 ] Uint4B + 0x0c0 WOW32Reserved : Ptr32 Void + 0x0c4 CurrentLocale : Uint4B + 0x0c8 FpSoftwareStatusRegister : Uint4B + 0x0cc SystemReserved1 : [ 54 ] Ptr32 Void + 0x1a4 ExceptionCode : Int4B + 0x1a8 ActivationContextStack : _ACTIVATION_CONTEXT_STACK + 0x1bc SpareBytes1 : [ 24 ] UChar + 0x1d4 GdiTebBatch : _GDI_TEB_BATCH + 0x6b4 RealClientId : _CLIENT_ID + 0x6bc GdiCachedProcessHandle : Ptr32 Void + 0x6c0 GdiClientPID : Uint4B + 0x6c4 GdiClientTID : Uint4B + 0x6c8 GdiThreadLocalInfo : Ptr32 Void + 0x6cc Win32ClientInfo : [ 62 ] Uint4B + 0x7c4 glDispatchTable : [ 233 ] Ptr32 Void + 0xb68 glReserved1 : [ 29 ] Uint4B + 0xbdc glReserved2 : Ptr32 Void + 0xbe0 glSectionInfo : Ptr32 Void + 0xbe4 glSection : Ptr32 Void + 0xbe8 glTable : Ptr32 Void + 0xbec glCurrentRC : Ptr32 Void + 0xbf0 glContext : Ptr32 Void + 0xbf4 LastStatusValue : Uint4B + 0xbf8 StaticUnicodeString : _UNICODE_STRING + 0xc00 StaticUnicodeBuffer : [ 261 ] Uint2B + 0xe0c DeallocationStack : Ptr32 Void + 0xe10 TlsSlots : [ 64 ] Ptr32 Void + 0xf10 TlsLinks : _LIST_ENTRY + 0xf18 Vdm : Ptr32 Void + 0xf1c ReservedForNtRpc : Ptr32 Void + 0xf20 DbgSsReserved : [ 2 ] Ptr32 Void + 0xf28 HardErrorsAreDisabled : Uint4B + 0xf2c Instrumentation : [ 16 ] Ptr32 Void + 0xf6c WinSockData : Ptr32 Void + 0xf70 GdiBatchCount : Uint4B + 0xf74 InDbgPrint : UChar + 0xf75 FreeStackOnTermination : UChar + 0xf76 HasFiberData : UChar + 0xf77 IdealProcessor : UChar + 0xf78 Spare3 : Uint4B + 0xf7c ReservedForPerf : Ptr32 Void + 0xf80 ReservedForOle : Ptr32 Void + 0xf84 WaitingOnLoaderLock : Uint4B + 0xf88 Wx86Thread : _Wx86ThreadState + 0xf94 TlsExpansionSlots : Ptr32 Ptr32 Void + 0xf98 ImpersonationLocale : Uint4B + 0xf9c IsImpersonating : Uint4B + 0xfa0 NlsCache : Ptr32 Void + 0xfa4 pShimData : Ptr32 Void + 0xfa8 HeapVirtualAffinity : Uint4B + 0xfac CurrentTransactionHandle : Ptr32 Void + 0xfb0 ActiveFrame : Ptr32 _TEB_ACTIVE_FRAME + 0xfb4 SafeThunkCall : UChar + 0xfb5 BooleanSpare : [ 3 ] UChar |
偏移 | 名称 | 作用 |
---|---|---|
0x000 | NtTib | 包含异常,线程栈等信息 |
0x020 | ClientId | 线程所属进程的PID |
0x030 | ProcessEnvironmentPointer | 保存了进程PEB地址 |
其中NtTib的结构如下:
1 2 3 4 5 6 7 8 9 10 | kd> dt _NT_TIB ntdll!_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 |
偏移 | 名称 | 作用 |
---|---|---|
0x000 | ExceptionList | 执行当前线程用户异常链表 |
0x004 | StackBase | 用户栈基址 |
0x008 | StackLimit | 用户栈边界 |
0x018 | Self | 指向本身NtTib地址 |
在用户层fs:[0]保存的就是TEB的地址,但由于TEB偏移0x18处的指针也是指针自己。所以,会在代码中看到fs:[0x18],此时获得的依然是TEB的地址,而fs:[0x30]获得的则是PEB的地址
2.内核层数据结构
与创建进程类似,当成功创建了一个线程的时候,Windows内核中就会创建相应的线程内核对象,该内核对象保存了线程的各种信息。
Windows内核的执行体层负责与管理策略相关的功能,而内核层(或微内核)实现了操作系统的核心机制,线程在这两个层上都有相应的数据结构。
A.微内核层数据结构
保存在微内核层的KTHREAD是线程最基本的数据结构,每个KTHREAD对象都代表了一个线程,反之也成立,即每个线程都有一个KTHREAD对象。
由于在Windows中,线程是系统处理器调度的基本单元,而且线程调度是在内核层完成的,所以,KTHREAD的许多域都和Windows线程调度机制有关。
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 76 | kd> dt _KTHREAD nt!_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 VdmSafe : UChar + 0x052 Spare0 : [ 2 ] 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 | 说明了内核层的线程对象是一个分发器对象,线程可以被等待,当线程结束时,在该对象上的等待可被满足 |
0x018 | InitialStack | 记录了原始栈的位置(高地址) |
0x01C | StackLimit | 记录了栈的低地址 |
0x020 | Teb | 指向线程的TEB地址 |
0x028 | KernelStack | 记录了真正内核调用栈的开始位置,由于在内核栈的顶部区域还记录了浮点处理器保存区和一个异常陷阱帧,所以,KernelStack的位置比InitialStack要低一些 |
0x02C | DedugActive | 如果值为-1不能使用调试寄存器 |
0x02D | State | 反映当前线程的状态 |
0x030 | Priority | 说明线程优先级,这里是指它的动态优先级,即执行过程中可能由于某些原因而调整过的优先级 |
0x034 | ApcState | 指定一个线程的APC信息 |
0x06C | BasePriority | 线程的静态优先级,其初始值是所属进程的BasePriority值,以后可通过KeSetBasePriorityThread函数重新设定 |
0x70 | WaitBlock | 指定等待的对象 |
0x0E0 | ServiceTable | 指向系统服务表基址 |
0x0E8 | ApcQueueable | 自旋锁,用户保护APC |
0x134 | TrapFrame | 线程中最关键的部分,记录了控制流状态 |
0x138 | ApcStatePointer | 两个元数的数组,指向KAPC_STATE指针,两个元数分别指向线程对象的ApcState和SavedApcState |
0x140 | PreviousMode | 用于内核函数判断程序是用户层调用还是内核层调用 |
0x14C | SavedApcState | 保存的APC |
0x164 | Alertable | 说明一个线程是否可以被唤醒 |
0x165 | ApcStateIndex | 索引值,表明当前APC状态在ApcStatePointer域中的索引 |
0x168 | StateBase | 记录当前栈的基址。当线程初始化时,InitialStack和StackBase是相等的 |
0x1B0 | ThreadListEntry | 双向链表,将一个进程中的所有线程全部连接起来 |
B.执行体数据结构
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 57 | kd> dt _ETHREAD nt!_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 + 0x258 KernelStackReference : Uint4B |
偏移 | 名称 | 作用 |
---|---|---|
0x000 | Tcb | 内嵌的KTHREAD结构体 |
0x1D0 | ExitStatus | 线程退出状态 |
0x1EC | Cid | 线程ID |
0x220 | ThreadsProcess | 指向线程所属的进程 |
0x22C | ThreadListEntry | 双向链表,一个进程中的所有链表都通过该链表进行连接 |
三.Windows的线程管理
1.线程的创建
最简单的线程创建函数就是CreateThread,该函数可以在当前进程中创建一个线程。
可以看到该函数是通过调用CreateRemoteThread函数,而传入的第一个进程句柄参数通过使用-1来表示当前进程
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 | .text: 7C8106D7 ; HANDLE __stdcall CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId) .text: 7C8106D7 public _CreateThread@ 24 .text: 7C8106D7 _CreateThread@ 24 proc near ; DATA XREF: .text:off_7C802654↑o .text: 7C8106D7 .text: 7C8106D7 lpThreadAttributes = dword ptr 8 .text: 7C8106D7 dwStackSize = dword ptr 0Ch .text: 7C8106D7 lpStartAddress = dword ptr 10h .text: 7C8106D7 lpParameter = dword ptr 14h .text: 7C8106D7 dwCreationFlags = dword ptr 18h .text: 7C8106D7 lpThreadId = dword ptr 1Ch .text: 7C8106D7 .text: 7C8106D7 mov edi, edi .text: 7C8106D9 push ebp .text: 7C8106DA mov ebp, esp .text: 7C8106DC push [ebp + lpThreadId] ; lpThreadId .text: 7C8106DF push [ebp + dwCreationFlags] ; dwCreationFlags .text: 7C8106E2 push [ebp + lpParameter] ; lpParameter .text: 7C8106E5 push [ebp + lpStartAddress] ; lpStartAddress .text: 7C8106E8 push [ebp + dwStackSize] ; dwStackSize .text: 7C8106EB push [ebp + lpThreadAttributes] ; lpThreadAttributes .text: 7C8106EE push 0FFFFFFFFh ; 当前进程 .text: 7C8106F0 call _CreateRemoteThread@ 28 ; CreateRemoteThread(x,x,x,x,x,x,x) .text: 7C8106F5 pop ebp .text: 7C8106F6 retn 18h .text: 7C8106F6 _CreateThread@ 24 endp |
在CreateRemoteThread中首先会对局部变量进程初始化
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 | .text: 7C8104CC ; HANDLE __stdcall CreateRemoteThread(HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId) .text: 7C8104CC public _CreateRemoteThread@ 28 .text: 7C8104CC _CreateRemoteThread@ 28 proc near ; CODE XREF: CreateThread(x,x,x,x,x,x) + 19 ↓p .text: 7C8104CC ; BaseCreateThreadPoolThread(x,x,x) + 3F ↓p .text: 7C8104CC ; DATA XREF: ... .text: 7C8104CC .text: 7C8104CC var_420 = byte ptr - 420h .text: 7C8104CC ThreadInformation = byte ptr - 408h .text: 7C8104CC Teb = dword ptr - 404h .text: 7C8104CC Cookie = dword ptr - 3ECh .text: 7C8104CC var_3E8 = dword ptr - 3E8h .text: 7C8104CC SuspendCount = dword ptr - 3E4h .text: 7C8104CC UserStack = _INITIAL_TEB ptr - 3E0h .text: 7C8104CC var_lpParameter = dword ptr - 3CCh .text: 7C8104CC var_lpThreadId = dword ptr - 3C8h .text: 7C8104CC ClientId = _CLIENT_ID ptr - 3C4h .text: 7C8104CC ProcessHandle = dword ptr - 3BCh .text: 7C8104CC pvBuffer = dword ptr - 3B8h .text: 7C8104CC var_3B4 = byte ptr - 3B4h .text: 7C8104CC ThreadHandle = dword ptr - 3B0h .text: 7C8104CC ExitStatus = dword ptr - 3ACh .text: 7C8104CC ThreadContext = CONTEXT ptr - 3A8h .text: 7C8104CC ApiMessage = byte ptr - 0DCh .text: 7C8104CC var_BC = dword ptr - 0BCh .text: 7C8104CC var_ThreadHandle = dword ptr - 0B4h .text: 7C8104CC var_UniqueProcess = dword ptr - 0B0h .text: 7C8104CC var_UniqueThread = dword ptr - 0ACh .text: 7C8104CC var_Cookie = dword ptr - 1Ch .text: 7C8104CC ms_exc = CPPEH_RECORD ptr - 18h .text: 7C8104CC hProcess = dword ptr 8 .text: 7C8104CC lpThreadAttributes = dword ptr 0Ch .text: 7C8104CC dwStackSize = dword ptr 10h .text: 7C8104CC lpStartAddress = dword ptr 14h .text: 7C8104CC lpParameter = dword ptr 18h .text: 7C8104CC dwCreationFlags = dword ptr 1Ch .text: 7C8104CC lpThreadId = dword ptr 20h .text: 7C8104CC push 410h .text: 7C8104D1 push offset stru_7C8106A8 .text: 7C8104D6 call __SEH_prolog .text: 7C8104DB mov eax, ___security_cookie .text: 7C8104E0 mov [ebp + var_Cookie], eax .text: 7C8104E3 mov ecx, [ebp + hProcess] ; 将进程句柄赋给ecx .text: 7C8104E6 mov [ebp + ProcessHandle], ecx .text: 7C8104EC mov esi, [ebp + lpThreadAttributes] .text: 7C8104EF mov ebx, [ebp + lpStartAddress] .text: 7C8104F2 mov eax, [ebp + lpParameter] .text: 7C8104F5 mov [ebp + var_lpParameter], eax .text: 7C8104FB mov eax, [ebp + lpThreadId] .text: 7C8104FE mov [ebp + var_lpThreadId], eax .text: 7C810504 xor edx, edx .text: 7C810506 mov [ebp + pvBuffer], edx .text: 7C81050C xor eax, eax .text: 7C81050E lea edi, [ebp + var_3B4] .text: 7C810514 stosd |
接下来需要调用BaseCreateStack来创建线程栈
1 2 3 4 5 6 7 8 9 10 | .text: 7C810515 lea eax, [ebp + UserStack] .text: 7C81051B push eax ; int .text: 7C81051C test byte ptr [ebp + dwCreationFlags + 2 ], 1 ; 是否带有STACK_SIZE_PARAM_IS_A_RESERVATION标志 .text: 7C810520 jnz loc_7C83AB6E .text: 7C810526 push edx ; RegionSize .text: 7C810527 push [ebp + dwStackSize] ; UINT_PTR .text: 7C81052A .text: 7C81052A loc_7C81052A: ; CODE XREF: CreateRemoteThread(x,x,x,x,x,x,x) + 2A6A6 ↓j .text: 7C81052A push ecx ; ProcessHandle .text: 7C81052B call _BaseCreateStack@ 16 |
调用BaseInitializeContext来初始化线程CONTEXT
1 2 3 4 5 6 7 8 9 | .text: 7C810538 xor edi, edi .text: 7C81053A inc edi .text: 7C81053B push edi .text: 7C81053C push [ebp + UserStack.StackBase] .text: 7C810542 push ebx .text: 7C810543 push [ebp + var_lpParameter] .text: 7C810549 lea eax, [ebp + ThreadContext] .text: 7C81054F push eax .text: 7C810550 call _BaseInitializeContext@ 20 |
通过系统调用NtCreateThread在内核中创建线程内核对象,此时是以挂起的方式来创建线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | .text: 7C810565 push edi ; CreateSuspended .text: 7C810566 lea ecx, [ebp + UserStack] .text: 7C81056C push ecx ; UserStack .text: 7C81056D lea ecx, [ebp + ThreadContext] .text: 7C810573 push ecx ; ThreadContext .text: 7C810574 lea ecx, [ebp + ClientId] .text: 7C81057A push ecx ; ClientId .text: 7C81057B mov esi, [ebp + ProcessHandle] .text: 7C810581 push esi ; ProcessHandle .text: 7C810582 push eax ; ObjectAttributes .text: 7C810583 push THREAD_ALL_ACCESS ; DesiredAccess .text: 7C810588 lea eax, [ebp + ThreadHandle] .text: 7C81058E push eax ; ThreadHandle .text: 7C81058F call ds:__imp__NtCreateThread@ 32 ; NtCreateThread(x,x,x,x,x,x,x,x) .text: 7C810595 mov [ebp + ExitStatus], eax |
调用CsrClientCallServer来给Windows子系统(Csrss)发送消息
1 2 3 4 5 6 7 8 9 10 11 12 | .text: 7C81060B mov eax, [ebp + ThreadHandle] .text: 7C810611 mov [ebp + var_ThreadHandle], eax .text: 7C810617 mov eax, [ebp + ClientId.UniqueProcess] .text: 7C81061D mov [ebp + var_UniqueProcess], eax .text: 7C810623 mov eax, [ebp + ClientId.UniqueThread] .text: 7C810629 mov [ebp + var_UniqueThread], eax .text: 7C81062F push 0Ch ; DataLength .text: 7C810631 push 10001h ; ApiNumber .text: 7C810636 push ebx ; CaptureBuffer .text: 7C810637 lea eax, [ebp + ApiMessage] .text: 7C81063D push eax ; ApiMessage .text: 7C81063E call ds:__imp__CsrClientCallServer@ 16 |
判断是否带有CREATE_SUSPENDED标志,如果没有,则会调用NtResumeThread来恢复线程的执行
1 2 3 4 5 6 | .text: 7C81066E test byte ptr [ebp + dwCreationFlags], 4 ; 是否带有CREATESUSPENDED .text: 7C810672 jnz short loc_7C810687 .text: 7C810674 lea eax, [ebp + SuspendCount] .text: 7C81067A push eax ; SuspendCount .text: 7C81067B push [ebp + ThreadHandle] ; ThreadHandle .text: 7C810681 call ds:__imp__NtResumeThread@ 8 |
2.系统调用NtCreateThread
在NtCreateThread中会首先判断是否是内核模式传递的调用
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 | PAGE: 004ADD59 ; NTSTATUS __stdcall NtCreateThread(PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, HANDLE ProcessHandle, PCLIENT_ID ClientId, PCONTEXT ThreadContext, PINITIAL_TEB InitialTeb, BOOLEAN CreateSuspended) PAGE: 004ADD59 _NtCreateThread@ 32 proc near ; DATA XREF: .text: 0040D8F4 ↑o PAGE: 004ADD59 PAGE: 004ADD59 var_InitialTeb = dword ptr - 38h PAGE: 004ADD59 var_34 = dword ptr - 34h PAGE: 004ADD59 var_ClientId = dword ptr - 24h PAGE: 004ADD59 var_CurETHREAD = dword ptr - 20h PAGE: 004ADD59 var_1C = dword ptr - 1Ch PAGE: 004ADD59 ms_exc = CPPEH_RECORD ptr - 18h PAGE: 004ADD59 ThreadHandle = dword ptr 8 PAGE: 004ADD59 DesiredAccess = dword ptr 0Ch PAGE: 004ADD59 ObjectAttributes = dword ptr 10h PAGE: 004ADD59 ProcessHandle = dword ptr 14h PAGE: 004ADD59 ClientId = dword ptr 18h PAGE: 004ADD59 ThreadContext = dword ptr 1Ch PAGE: 004ADD59 InitialTeb = dword ptr 20h PAGE: 004ADD59 CreateSuspended = byte ptr 24h PAGE: 004ADD59 push 28h PAGE: 004ADD5B push offset stru_423B30 PAGE: 004ADD60 call __SEH_prolog PAGE: 004ADD65 and [ebp + ms_exc.registration.TryLevel], 0 PAGE: 004ADD69 mov eax, large fs: 124h PAGE: 004ADD6F mov [ebp + var_CurETHREAD], eax PAGE: 004ADD72 cmp byte ptr [eax + 140h ], 0 ; 判断PreviousMode是否为 0 PAGE: 004ADD79 jz loc_4E902E |
如果是内核模式,则直接参数InitialTeb赋值给ebx
1 2 3 | PAGE: 004E902E loc_4E902E: ; CODE XREF: NtCreateThread(x,x,x,x,x,x,x,x) + 20 ↑j PAGE: 004E902E mov ebx, [ebp + InitialTeb] PAGE: 004E9031 jmp loc_4ADDF3 |
如果不是内核模式,就会对输出参数ThreadHandle, ClientId和输入参数ThreadContext以及InitialTeb进行可写检查
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 | PAGE: 004ADD7F mov eax, _MmUserProbeAddress PAGE: 004ADD84 mov ecx, [ebp + ThreadHandle] PAGE: 004ADD87 cmp ecx, eax PAGE: 004ADD89 jnb loc_52E0D8 PAGE: 004ADD8F PAGE: 004ADD8F loc_4ADD8F: ; CODE XREF: NtCreateThread(x,x,x,x,x,x,x,x) + 80385 ↓j PAGE: 004ADD8F mov eax, [ecx] PAGE: 004ADD91 mov [ecx], eax PAGE: 004ADD93 mov ebx, [ebp + ClientId] PAGE: 004ADD96 test ebx, ebx PAGE: 004ADD98 jz short loc_4ADDBD PAGE: 004ADD9A mov [ebp + var_ClientId], ebx PAGE: 004ADD9D mov eax, _MmUserProbeAddress PAGE: 004ADDA2 cmp ebx, eax PAGE: 004ADDA4 jnb loc_52E0E3 PAGE: 004ADDAA PAGE: 004ADDAA loc_4ADDAA: ; CODE XREF: NtCreateThread(x,x,x,x,x,x,x,x) + 8038D ↓j PAGE: 004ADDAA test bl, 3 PAGE: 004ADDAD jnz loc_52E0EB PAGE: 004ADDB3 PAGE: 004ADDB3 loc_4ADDB3: ; CODE XREF: PAGE: 0052E0F0 ↓j PAGE: 004ADDB3 mov al, [ebx] PAGE: 004ADDB5 mov [ebx], al PAGE: 004ADDB7 mov al, [ebx + 4 ] PAGE: 004ADDBA mov [ebx + 4 ], al PAGE: 004ADDBD PAGE: 004ADDBD loc_4ADDBD: ; CODE XREF: NtCreateThread(x,x,x,x,x,x,x,x) + 3F ↑j PAGE: 004ADDBD cmp [ebp + ThreadContext], 0 PAGE: 004ADDC1 jz loc_52E129 PAGE: 004ADDC7 test byte ptr [ebp + ThreadContext], 3 PAGE: 004ADDCB jnz loc_52E0F5 PAGE: 004ADDD1 PAGE: 004ADDD1 loc_4ADDD1: ; CODE XREF: PAGE: 0052E0FA ↓j PAGE: 004ADDD1 mov eax, _MmUserProbeAddress PAGE: 004ADDD6 cmp [ebp + ThreadContext], eax PAGE: 004ADDD9 jnb loc_52E0FF PAGE: 004ADDDF PAGE: 004ADDDF loc_4ADDDF: ; CODE XREF: NtCreateThread(x,x,x,x,x,x,x,x) + 803B1 ↓j PAGE: 004ADDDF mov ebx, [ebp + InitialTeb] ; 将InitialTeb赋给ebx PAGE: 004ADDE2 test bl, 3 PAGE: 004ADDE5 jnz loc_52E10F PAGE: 004ADDEB PAGE: 004ADDEB loc_4ADDEB: ; CODE XREF: PAGE: 0052E119 ↓j PAGE: 004ADDEB cmp ebx, eax PAGE: 004ADDED jnb loc_52E11E |
把传入的参数InitialTeb赋值给局部变量
1 2 3 4 5 6 | PAGE: 004ADDF3 loc_4ADDF3: ; CODE XREF: NtCreateThread(x,x,x,x,x,x,x,x) + 3B2D8 ↓j PAGE: 004ADDF3 ; NtCreateThread(x,x,x,x,x,x,x,x) + 803CB ↓j PAGE: 004ADDF3 mov eax, [ebx] PAGE: 004ADDF5 mov [ebp + var_InitialTeb], eax PAGE: 004ADDF8 mov ecx, [ebx + 4 ] PAGE: 004ADDFB mov [ebp + var_InitialTebPlus4], ecx |
调用PspCreateThread来完成创建,再次之前edx被清0,所以最后两个参数都为NULL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | PAGE: 004ADDFE xor edx, edx .... PAGE: 004ADE16 push edx ; StartContext PAGE: 004ADE17 push edx ; StartRoutine PAGE: 004ADE18 push dword ptr [ebp + CreateSuspended] ; CreateSuspended PAGE: 004ADE1B lea eax, [ebp + var_InitialTeb] PAGE: 004ADE1E push eax ; InitialTeb PAGE: 004ADE1F push [ebp + ThreadContext] ; ThraedContext PAGE: 004ADE22 push [ebp + ClientId] ; ClientId PAGE: 004ADE25 push edx ; ProcessPointer PAGE: 004ADE26 push [ebp + ProcessHandle] ; ProcessHandle PAGE: 004ADE29 push [ebp + ObjectAttributes] ; ObjectAttributes PAGE: 004ADE2C push [ebp + DesiredAccess] ; AccessMask PAGE: 004ADE2F push [ebp + ThreadHandle] ; ThreadHandle PAGE: 004ADE32 call _PspCreateThread@ 44ateThread @ 44 |
线程是属于进程的,所以参数中必须得有个进程句柄。参数ClientId和ThradHandle是用来返回CID和句柄的。参数ThreadContext用来给定一个线程的原始上下文,实际上就是各个寄存器的初值。另一个参数CreateSuspended则说明新创的线程是否要被挂起,等到对其调用NtResumeThread时才开始运行。
相比于NtCreateThread,PspCreateThread多了三个参数,这三个参数是为创建内核线程而设的,内核线程都属于系统初始进程PsInitialSystemProcess,但是这个进程没有句柄,所以只好使用EPROCESS结构指针ProcessPointer。同样,StartRoutine和StartContext也是用来给定内核线程的程序入口和上下文。非内核线程的程序入口是有ThreadContext寄存器中的EIP来决定的,所以对于来自用户空间的系统调用这三个参数都是NULL。
在PspCreateThraed中首先会判断是否存在StartRoutine,如果存在就会对局部变量var_PreviousMode赋值为0
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 | PAGE: 0049ED4C ; int __stdcall PspCreateThread(PHANDLE ThreadHandle, ACCESS_MASK AccessMask, POBJECT_ATTRIBUTES ObjectAttributes, HANDLE ProcessHandle, PEPROCESS ProcessPointer, PCLIENT_ID ClientId, PCONTEXT ThraedContext, PINITIAL_TEB InitialTeb, BOOLEAN CreateSuspended, PKSTART_ROUTINE StartRoutine, PVOID StartContext) PAGE: 0049ED4C _PspCreateThread@ 44 proc near ; CODE XREF: PsCreateSystemThread(x,x,x,x,x,x,x) + 2E ↓p PAGE: 0049ED4C ; NtCreateThread(x,x,x,x,x,x,x,x) + D9↓p PAGE: 0049ED4C PAGE: 0049ED4C var_AccessState = _ACCESS_STATE ptr - 124h PAGE: 0049ED4C var_B0 = dword ptr - 0B0h PAGE: 0049ED4C SubjectSecurityContext = _SECURITY_SUBJECT_CONTEXT ptr - 94h PAGE: 0049ED4C var_84 = dword ptr - 84h PAGE: 0049ED4C var_EProcess = dword ptr - 80h PAGE: 0049ED4C Resource = dword ptr - 7Ch PAGE: 0049ED4C var_78 = dword ptr - 78h PAGE: 0049ED4C var_RundownProtectAddress = dword ptr - 74h PAGE: 0049ED4C var_ActiveThreads = dword ptr - 70h PAGE: 0049ED4C var_CidEntry = dword ptr - 6Ch PAGE: 0049ED4C var_CidEntryPlus4 = dword ptr - 68h PAGE: 0049ED4C CurrentTime = LARGE_INTEGER ptr - 64h PAGE: 0049ED4C var_5C = dword ptr - 5Ch PAGE: 0049ED4C var_58 = dword ptr - 58h PAGE: 0049ED4C AccessStatus = dword ptr - 54h PAGE: 0049ED4C var_50 = dword ptr - 50h PAGE: 0049ED4C MemoryAllocated = byte ptr - 4Ch PAGE: 0049ED4C P = dword ptr - 48h PAGE: 0049ED4C var_44 = dword ptr - 44h PAGE: 0049ED4C var_Process = dword ptr - 40h PAGE: 0049ED4C var_Thread = dword ptr - 3Ch PAGE: 0049ED4C SecurityDescriptor = dword ptr - 38h PAGE: 0049ED4C var_34 = dword ptr - 34h PAGE: 0049ED4C BugCheckParameter1 = dword ptr - 30h PAGE: 0049ED4C var_2C = dword ptr - 2Ch PAGE: 0049ED4C var_25 = byte ptr - 25h PAGE: 0049ED4C var_PreviousMode = byte ptr - 24h PAGE: 0049ED4C var_Teb = dword ptr - 20h PAGE: 0049ED4C var_CurETHREAD = dword ptr - 1Ch PAGE: 0049ED4C ms_exc = CPPEH_RECORD ptr - 18h PAGE: 0049ED4C ThreadHandle = dword ptr 8 PAGE: 0049ED4C AccessMask = dword ptr 0Ch PAGE: 0049ED4C ObjectAttributes = dword ptr 10h PAGE: 0049ED4C ProcessHandle = dword ptr 14h PAGE: 0049ED4C ProcessPointer = dword ptr 18h PAGE: 0049ED4C ClientId = dword ptr 1Ch PAGE: 0049ED4C ThreadContext = dword ptr 20h PAGE: 0049ED4C InitialTeb = dword ptr 24h PAGE: 0049ED4C CreateSuspended = byte ptr 28h PAGE: 0049ED4C StartRoutine = dword ptr 2Ch PAGE: 0049ED4C StartContext = dword ptr 30h PAGE: 0049ED4C push 114h PAGE: 0049ED51 push offset stru_415050 PAGE: 0049ED56 call __SEH_prolog PAGE: 0049ED5B mov eax, large fs: 124h ; 将当前线程ETHREAD赋给eax PAGE: 0049ED61 mov [ebp + var_CurETHREAD], eax PAGE: 0049ED64 xor esi, esi PAGE: 0049ED66 cmp [ebp + StartRoutine], esi ; 判断StartRoutine是否为NULL PAGE: 0049ED69 jz loc_4ADE9A PAGE: 0049ED6F mov [ebp + var_PreviousMode], 0 |
如果不存在,则取出当前线程的PreviousMode赋值给局部变量
1 2 3 4 | PAGE: 004ADE9A loc_4ADE9A: ; CODE XREF: PspCreateThread(x,x,x,x,x,x,x,x,x,x,x) + 1D ↑j PAGE: 004ADE9A mov al, [eax + _KTHREAD.PreviousMode] ; 取出PreviousMode赋给al PAGE: 004ADEA0 mov [ebp + var_PreviousMode], al PAGE: 004ADEA3 jmp loc_49ED73 |
对局部变量进程赋值,判断是否存在进程句柄
1 2 3 4 5 6 | PAGE: 0049ED73 loc_49ED73: ; CODE XREF: PspCreateThread(x,x,x,x,x,x,x,x,x,x,x) + F157↓j PAGE: 0049ED73 mov [ebp + var_Teb], esi PAGE: 0049ED76 xor ebx, ebx PAGE: 0049ED78 mov [ebp + var_Process], ebx PAGE: 0049ED7B cmp [ebp + ProcessHandle], esi ; 判断是否存在进程句柄 PAGE: 0049ED7E jnz loc_49F298 |
如果存在进程句柄,就会调用ObReferenceObjectByHandle来获得进程EPROCESS,将其解析得到的EPROCESS赋给局部变量var_Process和寄存器ebx
1 2 3 4 5 6 7 8 9 10 11 12 | PAGE: 0049F298 loc_49F298: ; CODE XREF: PspCreateThread(x,x,x,x,x,x,x,x,x,x,x) + 32 ↑j PAGE: 0049F298 push esi PAGE: 0049F299 lea eax, [ebp + var_EProcess] PAGE: 0049F29C push eax ; Object PAGE: 0049F29D push dword ptr [ebp + var_PreviousMode] ; AccessMode PAGE: 0049F2A0 push _PsProcessType ; ObjectType PAGE: 0049F2A6 push 2 ; DesiredAccess PAGE: 0049F2A8 push [ebp + ProcessHandle] ; Handle PAGE: 0049F2AB call _ObReferenceObjectByHandle@ 24 ; ObReferenceObjectByHandle(x,x,x,x,x,x) PAGE: 0049F2B0 mov ebx, [ebp + var_EProcess] ; 将解析得到的var_EPROCESS赋给ebx PAGE: 0049F2B3 mov [ebp + var_Process], ebx PAGE: 0049F2B6 jmp loc_49ED9C |
如果不存在进程句柄,则会继续判断是否存在StartRoutine
1 2 | PAGE: 0049ED84 cmp [ebp + StartRoutine], esi ; 判断是否存在StartRoutine PAGE: 0049ED87 jz loc_509149 |
如果不存在,则返回句柄错误的返回值,结束函数
1 2 3 | PAGE: 00509149 loc_509149: ; CODE XREF: PspCreateThread(x,x,x,x,x,x,x,x,x,x,x) + 3B ↑j PAGE: 00509149 mov eax, STATUS_INVALID_HANDLE PAGE: 0050914E jmp loc_49ED9C |
如果存在则调用函数ObfReferenceObject来获得EPROCESS并将其赋值给局部变量var_Process
1 2 3 4 | PAGE: 0049ED8D mov ebx, [ebp + ProcessPointer] ; 将ProcessPointer赋给ebx PAGE: 0049ED90 mov ecx, ebx ; Object PAGE: 0049ED92 call @ObfReferenceObject@ 4 ; ObfReferenceObject(x) PAGE: 0049ED97 mov [ebp + var_Process], ebx |
继续判断先前模式是否是内核模式
1 2 | PAGE: 0049EDA4 cmp [ebp + var_PreviousMode], 0 ; 是否处在内核模式 PAGE: 0049EDA8 jnz loc_4AE060 |
如果不是内核模式,则会判断是否是系统进程
1 2 3 4 | PAGE: 004AE060 loc_4AE060: ; CODE XREF: PspCreateThread(x,x,x,x,x,x,x,x,x,x,x) + 5C ↑j PAGE: 004AE060 cmp ebx, _PsInitialSystemProcess ; 判断是否是系统进程 PAGE: 004AE066 jnz loc_49EDAE PAGE: 004AE06C jmp loc_52DAB9 |
如果是的话,则返回句柄错误
1 2 3 | PAGE: 0052DAB9 loc_52DAB9: ; CODE XREF: PspCreateThread(x,x,x,x,x,x,x,x,x,x,x) + F320↑j PAGE: 0052DAB9 mov esi, STATUS_INVALID_HANDLE PAGE: 0052DABE jmp short loc_52DAC2 |
如果不是系统进程或者先前模式是内核模式,则会调用ObCreateObject来创建线程内核对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | PAGE: 0049EDAE loc_49EDAE: ; CODE XREF: PspCreateThread(x,x,x,x,x,x,x,x,x,x,x) + F31A↓j PAGE: 0049EDAE lea eax, [ebp + var_Thread] PAGE: 0049EDB1 push eax ; Object PAGE: 0049EDB2 push esi ; NonPagedPoolCharge PAGE: 0049EDB3 push esi ; PagePoolCharge PAGE: 0049EDB4 push 260h ; ObjectBodySize PAGE: 0049EDB9 push esi ; ParentContext PAGE: 0049EDBA push dword ptr [ebp + var_PreviousMode] ; OwnershipMode PAGE: 0049EDBD push [ebp + ObjectAttributes] ; ObjectAttributes PAGE: 0049EDC0 push _PsThreadType ; ObjectType PAGE: 0049EDC6 push dword ptr [ebp + var_PreviousMode] ; PreviousMode PAGE: 0049EDC9 call _ObCreateObject@ 36 ; ObCreateObject(x,x,x,x,x,x,x,x,x) PAGE: 0049EDCE cmp eax, esi PAGE: 0049EDD0 jl loc_52DAC0 |
将ETHREAD结构地址清0
1 2 3 4 5 | PAGE: 0049EDD6 mov ecx, 98h PAGE: 0049EDDB xor eax, eax PAGE: 0049EDDD mov esi, [ebp + var_Thread] ; 将ETHREAD赋给esi PAGE: 0049EDE0 mov edi, esi PAGE: 0049EDE2 rep stosd |
为线程对象的一些成员赋值
1 2 3 4 5 | PAGE: 0049EDE4 and [esi + _ETHREAD.RundownProtect.___u0.Ptr], eax ; 将RundownProtect赋值为 0 PAGE: 0049EDEA mov [esi + _ETHREAD.ThreadsProcess], ebx ; 为ThreadProcess赋值 PAGE: 0049EDF0 lea edi, [esi + _ETHREAD.Cid] ; 将Cid的地址赋给edi PAGE: 0049EDF6 mov eax, [ebx + _EPROCESS.UniqueProcessId] ; 将EPROCESS中的UniqueProcessId赋给eax PAGE: 0049EDFC mov [edi], eax ; 将PID赋值edi保存的地址中 |
调用ExCreateHandle在全局句柄表中插入线程对象
1 2 3 4 5 6 7 8 9 | PAGE: 0049EDFE mov [ebp + var_CidEntry], esi PAGE: 0049EE01 and [ebp + var_CidEntryPlus4], 0 PAGE: 0049EE05 lea eax, [ebp + var_CidEntry] PAGE: 0049EE08 push eax PAGE: 0049EE09 push _PspCidTable PAGE: 0049EE0F call _ExCreateHandle@ 8 ; ExCreateHandle(x,x) PAGE: 0049EE14 mov [esi + _ETHREAD.Cid.UniqueThread], eax ; 将得到的句柄值也就是线程 ID 赋给ETHRAED - >Cid.UniqueThread PAGE: 0049EE1A test eax, eax PAGE: 0049EE1C jz loc_52DAD0 |
继续为线程ETHREAD对象赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | PAGE: 0049EE22 mov eax, _MmReadClusterSize PAGE: 0049EE27 mov [esi + _ETHREAD.ReadClusterSize], eax ; 为ReadClusterSize赋值 PAGE: 0049EE2D push 1 ; Limit PAGE: 0049EE2F push 0 ; Count PAGE: 0049EE31 lea eax, [esi + 1F4h ] ; 取出LpcReplySemaphore地址 PAGE: 0049EE37 push eax ; Semaphore PAGE: 0049EE38 call _KeInitializeSemaphore@ 12 ; KeInitializeSemaphore(x,x,x) PAGE: 0049EE3D lea eax, [esi + 1C8h ] ; 取出LpcReplyChain地址 PAGE: 0049EE43 mov [eax + LIST_ENTRY.Blink], eax PAGE: 0049EE46 mov [eax + LIST_ENTRY.Flink], eax PAGE: 0049EE48 lea eax, [esi + _ETHREAD.IrpList] ; 取出IrpList地址 PAGE: 0049EE4E mov [eax + LIST_ENTRY.Blink], eax PAGE: 0049EE51 mov [eax + LIST_ENTRY.Flink], eax PAGE: 0049EE53 lea eax, [esi + _ETHREAD.PostBlockList] ; 取出PostBlockList地址 PAGE: 0049EE59 mov [eax + LIST_ENTRY.Blink], eax PAGE: 0049EE5C mov [eax + LIST_ENTRY.Flink], eax PAGE: 0049EE5E and dword ptr [esi + _ETHREAD.ThreadLock.___u0], 0 ; 将ThreadLock清 0 PAGE: 0049EE65 lea eax, [esi + _ETHREAD.ActiveTimerListLock] ; 取出ActiveTimerListLock地址 PAGE: 0049EE6B push eax ; SpinLock PAGE: 0049EE6C call _KeInitializeSpinLock@ 4 ; KeInitializeSpinLock(x) PAGE: 0049EE71 lea eax, [esi + _ETHREAD.ActiveTimerListHead] ; 取出ActiveTimerListHead地址 PAGE: 0049EE77 mov [eax + LIST_ENTRY.Blink], eax PAGE: 0049EE7A mov [eax + LIST_ENTRY.Flink], eax4 and [esi + 234h ], eax ; 将RundownProtect赋值为 0 |
获得进程的RundownProtect锁,以免再创建过程中进程被停掉
1 2 3 4 | PAGE: 0049EE82 mov [ebp + var_RundownProtectAddress], ecx PAGE: 0049EE85 call @ExAcquireRundownProtection@ 4 ; ExAcquireRundownProtection(x) PAGE: 0049EE8A test al, al PAGE: 0049EE8C jz loc_52DADA |
判断传入的ThreadContext是否为NULL
1 2 | PAGE: 0049EE92 cmp [ebp + ThraedContext], 0 PAGE: 0049EE96 jnz loc_4ADFA9 |
如果不为NULL,说明此次创建的是用户模式的进程,接下来就会调用MmCreateTeb来创建一个TEB并用InitialTeb来对其进行初始化
1 2 3 4 5 6 7 8 9 10 | PAGE: 004ADFA9 loc_4ADFA9: ; CODE XREF: PspCreateThread(x,x,x,x,x,x,x,x,x,x,x) + 14A ↑j PAGE: 004ADFA9 lea eax, [ebp + var_Teb] PAGE: 004ADFAC push eax ; int PAGE: 004ADFAD push edi ; int PAGE: 004ADFAE push [ebp + InitialTeb] ; int PAGE: 004ADFB1 push ebx ; BugCheckParameter1 PAGE: 004ADFB2 call _MmCreateTeb@ 16 ; MmCreateTeb(x,x,x,x) PAGE: 004ADFB7 mov edi, eax PAGE: 004ADFB9 test edi, edi PAGE: 004ADFBB jl loc_52DAE4 |
用ThreadContext中的Eip和eax为线程对象赋值
1 2 3 4 5 | PAGE: 004ADFC5 mov eax, [ebp + ThreadContext] PAGE: 004ADFC8 mov ecx, [eax + CONTEXT._Eip] PAGE: 004ADFCE mov [esi + _ETHREAD.StartAddress], ecx PAGE: 004ADFD4 mov eax, [eax + CONTEXT._Eax] PAGE: 004ADFDA mov [esi + _ETHREAD.___u17.Win32StartAddress], eax |
调用KeInitThread来初始化线程对象,此时第三个参数是PspUserThreadStartup函数的地址,第六个参数是ThreadContext
1 2 3 4 5 6 7 8 9 | PAGE: 004ADFEC push ebx PAGE: 004ADFED push [ebp + var_Teb] PAGE: 004ADFF0 push [ebp + ThreadContext] PAGE: 004ADFF3 push [esi + _ETHREAD.StartAddress] PAGE: 004ADFF9 push 0 PAGE: 004ADFFB push offset _PspUserThreadStartup@ 8 ; PspUserThreadStartup(x,x) PAGE: 004AE000 push 0 PAGE: 0049EEC3 push esi PAGE: 0049EEC4 call _KeInitThread@ 32 |
如果ThreadContext为NULL,则说明此次创建的是内核模式的线程,函数首先先对局部变量var_Teb清0以后,对线程对象的CrossThreadFlags位进行或操作,然后再用传入的StartRoutine来赋值线程对象中的StartAddress
1 2 3 4 5 6 7 | PAGE: 0049EE9E mov [ebp + var_Teb], ecx PAGE: 0049EEA1 push 10h PAGE: 0049EEA3 pop eax PAGE: 0049EEA4 lea edx, [esi + 248h ] ; 取出CrossThreadFlags地址赋给edx PAGE: 0049EEAA lock or [edx], eax PAGE: 0049EEAD mov eax, [ebp + StartRoutine] PAGE: 0049EEB0 mov [esi + _ETHREAD.StartAddress], eax |
接下来也是调用KeInitThread来初始化线程对象,但是此时第三个参数已经变成了PspSystemThreadStartup函数地址,第5个参数是StartContext
1 2 3 4 5 6 7 8 9 10 11 | PAGE: 0049EEB6 push ebx PAGE: 0049EEB7 push ecx PAGE: 0049EEB8 push ecx PAGE: 0049EEB9 push [ebp + StartContext] PAGE: 0049EEBC push eax PAGE: 0049EEBD push offset _PspSystemThreadStartup@ 8 ; PspSystemThreadStartup(x,x) PAGE: 0049EEC2 push ecx PAGE: 0049EEC3 PAGE: 0049EEC3 loc_49EEC3: ; CODE XREF: PspCreateThread(x,x,x,x,x,x,x,x,x,x,x) + F2B6↓j PAGE: 0049EEC3 push esi PAGE: 0049EEC4 call _KeInitThread@ 32 |
当KeInitThread函数返回的时候,就说明新创建的线程的状态是"已初始化"状态,接下来保存进程的活动线程到局部变量,随后将进程的活动线程数量加1
1 2 3 4 5 | PAGE: 0049EEFD lea eax, [ebx + _EPROCESS.ActiveThreads] ; 将ActiveThreads地址赋给edi PAGE: 0049EF03 mov ecx, [eax] PAGE: 0049EF05 mov [ebp + var_ActiveThreads], ecx ; 保存进程原来的ActiveThreads PAGE: 0049EF08 inc ecx PAGE: 0049EF09 mov [eax], ecx ; 进程的ActiveThreads加 1 |
并且将新建的线程加入到进程的线程链表中
1 2 3 4 5 6 7 | PAGE: 0049EF0B lea eax, [esi + _ETHREAD.ThreadListEntry] PAGE: 0049EF11 lea ecx, [ebx + _EPROCESS.ThreadListHead] PAGE: 0049EF17 mov edx, [ecx + LIST_ENTRY.Blink] PAGE: 0049EF1A mov [eax + LIST_ENTRY.Flink], ecx PAGE: 0049EF1C mov [eax + LIST_ENTRY.Blink], edx PAGE: 0049EF1F mov [edx + LIST_ENTRY.Flink], eax PAGE: 0049EF21 mov [ecx + LIST_ENTRY.Blink], eax |
调用KeStartThread初始化剩余的域,尤其是跟调度相关的域,比如优先级,时限设置,处理器亲和性等等
1 2 | PAGE: 0049EF24 push esi PAGE: 0049EF25 call _KeStartThread@ 4 |
判断传入的标志中是否有挂起标志
1 2 | PAGE: 0049EFAB cmp [ebp + CreateSuspended], 0 PAGE: 0049EFAF jnz loc_4AE482 |
如果有就调用KeSuspendThread挂起线程
1 2 3 | PAGE: 004AE482 loc_4AE482: ; CODE XREF: PspCreateThread(x,x,x,x,x,x,x,x,x,x,x) + 263 ↑j PAGE: 004AE489 push esi PAGE: 004AE48A call _KeSuspendThread@ 4 |
调用ObInsertObject将创建的线程插入到句柄表中
1 2 3 4 5 6 7 8 9 | PAGE: 0049EFED lea eax, [ebp + var_ThreadHandle] PAGE: 0049EFF0 push eax ; Handle PAGE: 0049EFF1 push edi ; NewObject PAGE: 0049EFF2 push edi ; ObjectPointerBias PAGE: 0049EFF3 push [ebp + AccessMask] ; DesiredAccess PAGE: 0049EFF6 lea eax, [ebp + var_AccessState] PAGE: 0049EFFC push eax ; PACCESS_STATE PAGE: 0049EFFD push esi ; Object PAGE: 0049EFFE call _ObInsertObject@ 24 |
最后调用KeReadyThread让线程进入"就绪"状态
1 2 | PAGE: 0049F0F8 push esi PAGE: 0049F0F9 call _KeReadyThread@ 4 |
3.线程的退出
在执行体层上,线程终止函数是NtTerminateThread,而系统线程终止函数则是PsTerminateSystemThread,这两个函数最终都是通过调用PspTerminateThreadByPointer来完成操作的。
A.NtTerminateThread
在该函数中,首先将当前线程结构体取出赋给esi,判断传入的线程句柄是否为NULL
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 | PAGE: 004AD986 ; NTSTATUS __stdcall NtTerminateThread(HANDLE ThreadHandle, NTSTATUS ExitStatus) PAGE: 004AD986 _NtTerminateThread@ 8 proc near ; DATA XREF: .text: 0040DC28 ↑o PAGE: 004AD986 PAGE: 004AD986 var_68 = dword ptr - 68h PAGE: 004AD986 var_64 = dword ptr - 64h PAGE: 004AD986 var_60 = dword ptr - 60h PAGE: 004AD986 var_5C = dword ptr - 5Ch PAGE: 004AD986 var_58 = dword ptr - 58h PAGE: 004AD986 BaseAddress = dword ptr - 44h PAGE: 004AD986 RegionSize = dword ptr - 40h PAGE: 004AD986 Object = dword ptr - 3Ch PAGE: 004AD986 var_30 = dword ptr - 30h PAGE: 004AD986 AccessMode = byte ptr - 4 PAGE: 004AD986 ThreadHandle = dword ptr 8 PAGE: 004AD986 ExitStatus = dword ptr 0Ch PAGE: 004AD986 arg_10 = dword ptr 18h PAGE: 004AD986 PAGE: 004AD986 mov edi, edi PAGE: 004AD988 push ebp PAGE: 004AD989 mov ebp, esp PAGE: 004AD98B push ecx PAGE: 004AD98C push ebx PAGE: 004AD98D push esi PAGE: 004AD98E push edi PAGE: 004AD98F xor edi, edi ; edi清 0 PAGE: 004AD991 mov eax, large fs: 124h ; 将线程对象赋给eax PAGE: 004AD997 cmp [ebp + ThreadHandle], edi ; 判断线程句柄是否为NULL PAGE: 004AD99A mov esi, eax ; 将eax赋给esi PAGE: 004AD99C jz loc_4ADB15 |
如果为空,接下来取出当前线程对应的进程对象,判断进程对象是否有活跃线程
1 2 3 4 5 | PAGE: 004ADB15 loc_4ADB15: ; CODE XREF: NtTerminateThread(x,x) + 16 ↑j PAGE: 004ADB15 mov eax, [esi + _KTHREAD.ApcState.Process] PAGE: 004ADB18 cmp [eax + _EPROCESS.ActiveThreads], 1 PAGE: 004ADB1F jnz short loc_4ADB07 PAGE: 004ADB21 jmp loc_52E6B0 |
如果有活跃线程则跳转到loc_4ADB07调用PspTerminateThreadPointer来结束线程
1 2 3 4 5 6 | PAGE: 004ADB07 loc_4ADB07: ; CODE XREF: NtTerminateThread(x,x) + 20 ↑j PAGE: 004ADB07 ; NtTerminateThread(x,x) + 199 ↓j ... PAGE: 004ADB07 push [ebp + ExitStatus] PAGE: 004ADB0A push esi ; Response PAGE: 004ADB0B call _PspTerminateThreadByPointer@ 8 ; PspTerminateThreadByPointer(x,x) PAGE: 004ADB10 jmp loc_4AD9F1 |
如果传入的线程句柄不为NULL,则接下来判断线程句柄是否是当前线程句柄(-2)
1 2 | PAGE: 004AD9A2 cmp [ebp + ThreadHandle], 0FFFFFFFEh ; 判断是否是当前线程 PAGE: 004AD9A6 jz loc_4ADB07 |
如果是当前线程句柄就会跳转到上面说的loc_4ADB07地址结束线程
如果不是当前线程的句柄,接下来就会通过ObReferenceObjectByHandle来通过线程句柄获取线程对象
1 2 3 4 5 6 7 8 9 10 11 12 13 | PAGE: 004AD9AC mov al, [esi + _KTHREAD.PreviousMode] PAGE: 004AD9B2 push 0 ; HandleInformation PAGE: 004AD9B4 mov [ebp + AccessMode], al PAGE: 004AD9B7 lea eax, [ebp + Object ] PAGE: 004AD9BA push eax ; Object PAGE: 004AD9BB push dword ptr [ebp + AccessMode] ; AccessMode PAGE: 004AD9BE push _PsThreadType ; ObjectType PAGE: 004AD9C4 push 1 ; DesiredAccess PAGE: 004AD9C6 push [ebp + ThreadHandle] ; Handle PAGE: 004AD9C9 call _ObReferenceObjectByHandle@ 24 ; ObReferenceObjectByHandle(x,x,x,x,x,x) PAGE: 004AD9CE mov edi, eax PAGE: 004AD9D0 test edi, edi PAGE: 004AD9D2 jl short loc_4AD9F1 |
接着在调用PspTerminateThreadPointer来结束线程
1 2 3 4 5 6 7 8 9 | PAGE: 004AD9D4 mov ebx, [ebp + Object ] PAGE: 004AD9D7 cmp ebx, esi PAGE: 004AD9D9 jz loc_52E6BA PAGE: 004AD9DF push [ebp + ExitStatus] PAGE: 004AD9E2 push ebx ; Response PAGE: 004AD9E3 call _PspTerminateThreadByPointer@ 8 ; PspTerminateThreadByPointer(x,x) PAGE: 004AD9E8 mov ecx, ebx ; Object PAGE: 004AD9EA mov edi, eax PAGE: 004AD9EC call @ObfDereferenceObject@ 4 |
B.PsTerminateSystemThread
PsTerminateSystemThread函数则非常简单,它就直接取出线程对象以后调用函数结束线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | PAGE: 004AD44D ; NTSTATUS __stdcall PsTerminateSystemThread(NTSTATUS ExitStatus) PAGE: 004AD44D public _PsTerminateSystemThread@ 4 PAGE: 004AD44D _PsTerminateSystemThread@ 4 proc near ; CODE XREF: InbvRotateGuiBootDisplay(x) + 4A ↑p PAGE: 004AD44D ; ExpWorkerThread(x) + 4A96C ↑p ... PAGE: 004AD44D PAGE: 004AD44D ExitStatus = dword ptr 8 PAGE: 004AD44D PAGE: 004AD44D ; FUNCTION CHUNK AT PAGE: 0052E6CB SIZE 0000000A BYTES PAGE: 004AD44D PAGE: 004AD44D mov edi, edi PAGE: 004AD44F push ebp PAGE: 004AD450 mov ebp, esp PAGE: 004AD452 mov eax, large fs: 124h ; 将当前线程赋给eax PAGE: 004AD458 test byte ptr [eax + _ETHREAD.___u24.CrossThreadFlags], 10h PAGE: 004AD45F jz loc_52E6CB PAGE: 004AD465 push [ebp + ExitStatus] PAGE: 004AD468 push eax ; Response PAGE: 004AD469 call _PspTerminateThreadByPointer@ 8 ; PspTerminateThreadByPointer(x,x) PAGE: 004AD46E PAGE: 004AD46E loc_4AD46E: ; CODE XREF: PsTerminateSystemThread(x) + 81283 ↓j PAGE: 004AD46E pop ebp PAGE: 004AD46F retn 4 PAGE: 004AD46F _PsTerminateSystemThread@ 4 |
C.PspTermiateThreadByPointer
该函数首先判断结束的线程的CrossTheadFlags是否等于0x40,以及要结束的线程是否是当前线程对象,如果都是的话就会调用PspExitThread来结束线程
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 | PAGE: 0049EB6F ; int __stdcall PspTerminateThreadByPointer(PETHREAD Thread, NTSTATUS ExitStatus) PAGE: 0049EB6F _PspTerminateThreadByPointer@ 8 proc near PAGE: 0049EB6F ; CODE XREF: PspSystemThreadStartup(x,x) + 3B ↓p PAGE: 0049EB6F ; PsTerminateSystemThread(x) + 1C ↓p ... PAGE: 0049EB6F PAGE: 0049EB6F Interval = LARGE_INTEGER ptr - 0Ch PAGE: 0049EB6F var_4 = dword ptr - 4 PAGE: 0049EB6F Thread = byte ptr 8 PAGE: 0049EB6F ExitStatus = dword ptr 0Ch PAGE: 0049EB6F mov edi, edi PAGE: 0049EB71 push ebp PAGE: 0049EB72 mov ebp, esp PAGE: 0049EB74 sub esp, 0Ch PAGE: 0049EB77 or dword ptr [ebp - 8 ], 0FFFFFFFFh PAGE: 0049EB7B push esi PAGE: 0049EB7C push edi PAGE: 0049EB7D mov edi, dword ptr [ebp + Thread] ; 将线程对象赋给edi PAGE: 0049EB80 lea esi, [edi + 248h ] ; 将CrossThreadFlags赋给edi PAGE: 0049EB86 test byte ptr [esi], 40h PAGE: 0049EB89 mov dword ptr [ebp + Interval], 0FFF0BDC0h PAGE: 0049EB90 jnz loc_52E5AF PAGE: 0049EB96 PAGE: 0049EB96 loc_49EB96: ; CODE XREF: PspTerminateThreadByPointer(x,x) + 8FA57 ↓j PAGE: 0049EB96 mov eax, large fs: 124h ; 将当前线程赋给eax PAGE: 0049EB9C cmp edi, eax ; 判断要结束的线程是否是当前线程 PAGE: 0049EB9E jnz loc_4B7CE7 PAGE: 0049EBA4 xor eax, eax PAGE: 0049EBA6 inc eax PAGE: 0049EBA7 lock or [esi], eax PAGE: 0049EBAA push [ebp + ExitStatus] PAGE: 0049EBAD call _PspExitThread@ 4 ; PspExitThread(x) PAGE: 0049EBB2 nop PAGE: 0049EBB3 nop PAGE: 0049EBB4 nop PAGE: 0049EBB5 nop PAGE: 0049EBB6 nop PAGE: 0049EBB6 _PspTerminateThreadByPointer@ 8 |
【看雪培训】目录重大更新!《安卓高级研修班》2022年春季班开始招生!