首页
论坛
课程
招聘
[原创]Windows内核学习笔记之线程(上)
2021-12-20 17:18 10694

[原创]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

下篇:Windows内核学习笔记之线程(中)


【看雪培训】目录重大更新!《安卓高级研修班》2022年春季班开始招生!

最后于 2021-12-22 19:12 被1900编辑 ,原因:
收藏
点赞1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回