【内核原理与实现 011】进程线程创建过程
一、进程创建过程
所有进程都通过 PspCreateProcess 函数创建,包括 System 进程。它被三个函数调用,分别是NtCreateProcessEx、PsCreateSystemProcess 和 PspInitPhase0 。
NtCreateProcessEx 是 CreateProcess 的内核服务;
PspInitPhase0 函数是系统初始化早期调用的,它创建了 System 进程,System 进程的句柄保存在全局变量 PspInitialSystemProcessHandle 中,EPROCESS 保存在 PsInitialSystemProcess 中;
PsCreateSystemProcess 是用来创建系统进程的,它创建的进程都是 PsInitialSystemProcess 的子进程。
PspCreateProcess 的大致流程在136-140页分析了。这里简单介绍一下,完整代码放在文末。
- 创建进程对象
- 初始化进程对象
- 初始化内存区对象 SectionObject
- 初始化调试端口
- 初始化异常端口
- 创建进程地址空间
- 初始化进程基本优先级,CPU亲和性,页目录基址,超空间页帧号
- 初始化进程安全属性(从父进程复制令牌)
- 设置优先级类别
- 初始化句柄表
- 初始化进程地址空间
- 创建进程ID
- 审计此次进程创建行为
- 作业相关操作
- 创建或复制PEB
- 新进程对象插入 PsActiveProcessHead 链表
- 新进程对象插入当前进程句柄表
- 计算新进程的基本优先级和时限重置值
- 设置进程访问权限,当前进程句柄可访问,允许进程终止
- 设置进程创建时间
PspCreateProcess 创建了进程,此时进程中的代码还没运行起来,进程还是死的,因为此时还没有线程,接下来介绍线程的创建过程。
二、线程创建过程
NtCreateThread 和 PsCreateSystemThread 函数会调用 PspCreateThread ,分别用于创建用户线程和系统线程。
下面简单概括 PspCreateThread 创建线程的工作,可能有遗漏或错误。
- 获取当前CPU模式
- 获取进程对象
- 创建线程对象并初始化为0
- 设置父进程
- 创建CID
- 初始化线程结构的部分属性
- 初始化定时器链表
- 如果是用户线程,创建并初始化TEB,用 ThreadConatext 初始化R0 R3的入口点
- 如果是系统线程,用 StartRoutine 初始化R0入口点
- 不管是用户线程还是内核线程,都调用 KeInitThread 初始化 Header, WaitBlock, ServiceTable, APC,定时器,内核栈等属性
- 进程的活动线程计数加1
- 新线程加入进程的线程链表
- 调用 KeStartThread 初始化剩余的域,主要是和调度相关的优先级、时限、CPU亲和性等
- 如果是该进程的第一个线程,则触发该进程的创建通知
- 工作集相关的操作
- 线程对象的引用计数加2,一个针对当前的创建操作,一个针对要返回的线程句柄
- 如果 CreateSuspended 为真,指示新线程立即被挂起
- 根据指定的期望访问权限,调用 SeCreateAccessStateEx 创建一个访问状态结构
- 把新线程对象插入到当前进程的句柄表
- 设置输出参数 ThreadHandle
- 设置输出参数 ClientId
- 设置线程创建时间
- 设置线程访问权限
- 新线程加入就绪链表,等待调度;或者此时进程不在内存中,设置新线程状态为转移
- 引用计数减1
PspCreateThread 函数返回后,新线程随时可以被调度执行。
三、创建进程的全貌
进程创建后才创建线程,但是 PspCreateProcess 函数中根本没有创建线程的动作,也没有打开进程可执行映像文件的代码。在WRK中,我们看不到完整的进程创建过程,这是不行的!下面从 CreateProcess 函数调用开始,分析进程创建的全过程。
实际上,0环的东西已经分析过了,我们只需要分析3环 CreateProcessW 进0环之前干了啥就行。
CreateProcessW 本身没干啥,而是调用了 CreateProcessInternalW 函数,源码见文末。
CreateProcessInternalW 函数在 base\win32\client\process.c,总共 2582 行C代码,我看不懂,主要是不知道怎么把文件名转化成 SectionHandle 的,代码实在太多了,而且有很多看不懂的操作。
BOOL
WINAPI
CreateProcessInternalW(
HANDLE hUserToken,
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation,
PHANDLE hRestrictedUserToken
)
CreateProcessInternalW 打开指定的可执行文件,并创建一个内存区对象,注意,内存区对象并没有被映射到内存中(由于目标进程尚未创建,不可能完成内存映射),但它确实是打开了。
接下来就是调用 ntdll 的 NtCreateProcessEx,通过系统调用,CPU模式变成内核模式,进入0环 KiSystemService / KiFastCallEntry 分发函数,然后调用执行体的 NtCreateProcessEx 函数。
接下来的工作我们先前已经分析过了,这里再次过一遍。NtCreateProcessEx 函数执行前面介绍的进程创建逻辑,包括创建并初始化 EPROCESS 对象,创建初始的进程地址空间,创建和初始化句柄表,设置 EPROCESS KPROCESS 的各种属性,如进程优先级,安全属性,创建时间等。到这里,执行体层的进程对象已经建立,进程地址空间已经初始化,PEB也已初始化。
接下来是创建线程,首先需要构造一个栈,和一个上下文环境。栈的大小通过映像文件获得,创建线程通过 ntdll 的 NtCreateThread 调用执行体的 NtCreateThread 完成,具体工作就是先前介绍过的,包括创建并初始化 ETHREAD,生成线程ID,建立TEB和设置线程安全性。
进程的第一个线程启动函数是 kernel32 的 BaseProcessStart ,这里创建的线程并不会立即执行,而是要等进程完全初始化后才执行。
到此为止,从内核角度来看,进程对象和第一个线程对象已经建立起来了,但是对子系统而言,进程创建才刚刚开始。kernel32 给windows子系统发送一个消息,消息中包括进程和线程的句柄、进程创建者的ID等必要信息。windows子系统 csrss.exe 接收到此消息,执行以下操作:
- 保留一份句柄
- 设定新进程的优先级类别
- 在子系统中分配一个内部进程块
- 设置新进程的异常端口,从而子系统可以接收到该进程中发生的异常
- 对于被调试进程,设置它的调试端口,从而子系统可以接收到该进程的调试事件
- 分配并初始化一个内部线程块,并插入到进程的线程列表中
- 窗口会话中的进程计数加1
- 设置进程的停机级别位默认级别
- 将新进程插入到子系统的进程列表中
- 分配并初始化一块内存供子系统的内核模式部分使用(W32PROCESS结构)
- 显示应用程序启动光标
到此为止,进程环境已经建好,其线程将要使用的资源也分配好了,windows子系统已经知道并登记了此进程和线程。所以,初始线程被恢复执行,余下部分的初始化工作是在初始线程在新进程环境中完成的。在内核中,新线程启动的例程是 KiThreadStartup 函数,这是在 PspCreateThread 函数中调用 KeInitThread 时,KeInitThread 函数又调用 KiInitializeContextThread 函数来设置的。
cPublicProc _KiThreadStartup ,1
xor ebx,ebx ; clear registers
xor esi,esi ;
xor edi,edi ;
xor ebp,ebp ;
LowerIrql APC_LEVEL ; KeLowerIrql(APC_LEVEL)
pop eax ; (eax)->SystemRoutine
call eax ; SystemRoutine(StartRoutine, StartContext)
pop ecx ; (ecx) = UserContextFlag
or ecx, ecx
jz short kits10 ; No user context, go bugcheck
mov ebp,esp ; (bp) -> TrapFrame holding UserContext
jmp _KiServiceExit2
kits10: stdCall _KeBugCheck, <NO_USER_MODE_CONTEXT>
stdENDP _KiThreadStartup
KiThreadStartup 函数首先将 IRQL 降低到 APC_LEVEL,然后调用系统初始的线程函数 PspUserThreadStartup (PspCreateThread 函数在调用KeInitThread 时指定的,如果是创建系统线程,这里就是 PspSystemThreadStartup 函数)。线程启动函数被作为一个参数传递给 PspUserThreadStartup ,此处应是 kernel32 的 BaseProcessStart。
PspUserThreadStartup 函数设置异步函数调用APC机制,基本流程如下:
-
获得当前线程和进程对象。
-
是否由于创建过程中出错而需要终止本线程。
-
如果需要,通知调试器。
-
如果这是进程中的第一个线程,则判断系统是否支持应用程序预取的特性,如果
是,则通知缓存管理器预取可执行映像文件中的页面(见2 106 行的CcPfBeginAppLaunch
调用)。所谓应用程序预取,是指将该进程上一次启动的前10 s 内引用到的页面直接读
入到内存中。 -
然后,PspUserThreadStartup 把一个用户模式APC 插入到线程的用户APC 队列中,
此APC 例程是在全局变量PspSystemDll 中指定的,指向ntdll.dll 的LdrInitializeThunk 函数。 -
接下来填充系统范围的一个Cookie 值。
PspUserThreadStartup 返回后,KiThreadStartup 函数返回到用户模式,此时,PspUserThreadStartup 插入的APC 被交付,于是 LdrInitializeThunk 函数被调用,这是映像加载器(image loader)的初始化函数,完成加载器,堆管理器等初始化工作,然后加载必要的dll,并调用它们的入口函数。最后,当 LdrInitializeThunk 返回到用户模式 APC 分发器时,该线程开始在用户模式下执行,调用应用程序指定的线程启动函数,此启动函数的地址已经在APC交付时被压到用户栈中。
至此,进程创建完毕,开始执行用户空间中的代码。
本文涉及的函数源码
PspCreateProcess
NTSTATUS
PspCreateProcess(
OUT PHANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN HANDLE ParentProcess OPTIONAL,
IN ULONG Flags,
IN HANDLE SectionHandle OPTIONAL,
IN HANDLE DebugPort OPTIONAL,
IN HANDLE ExceptionPort OPTIONAL,
IN ULONG JobMemberLevel
)
/*++
Routine Description:
This routine creates and initializes a process object. It implements the
foundation for NtCreateProcess and for system initialization process
creation.
这个函数创建并初始化一个进程对象。NtCreateProcess 会调用它;系统进程初始化也会调用它。
Arguments:
ProcessHandle - Returns the handle for the new process.
输出参数,返回新进程句柄
DesiredAccess - Supplies the desired access modes to the new process.
期望对新进程的访问权限
ObjectAttributes - Supplies the object attributes of the new process.
新进程的对象属性
ParentProcess - Supplies a handle to the process' parent process. If this
parameter is not specified, then the process has no parent
and is created using the system address space.
指定父进程句柄。如果未指定,说明没有父进程,那就是用系统地址空间创建进程。
Flags - Process creation flags
进程创建标志
SectionHandle - Supplies a handle to a section object to be used to create
the process' address space. If this parameter is not
specified, then the address space is simply a clone of the
parent process' address space.
提供一个内存区对象句柄用来创建进程地址空间。如果此参数未指定,则简单地复制父进程
的地址空间。
DebugPort - Supplies a handle to a port object that will be used as the
process' debug port.
调试端口句柄
ExceptionPort - Supplies a handle to a port object that will be used as the
process' exception port.
异常端口句柄
JobMemberLevel - Level for a create process in a jobset
工作集等级
--*/
{
NTSTATUS Status;
PEPROCESS Process;
PEPROCESS CurrentProcess;
PEPROCESS Parent;
PETHREAD CurrentThread;
KAFFINITY Affinity;
KPRIORITY BasePriority;
PVOID SectionObject;
PVOID ExceptionPortObject;
PVOID DebugPortObject;
ULONG WorkingSetMinimum, WorkingSetMaximum;
HANDLE LocalProcessHandle;
KPROCESSOR_MODE PreviousMode;
INITIAL_PEB InitialPeb;
BOOLEAN CreatePeb;
ULONG_PTR DirectoryTableBase[2];
BOOLEAN AccessCheck;
BOOLEAN MemoryAllocated;
PSECURITY_DESCRIPTOR SecurityDescriptor;
SECURITY_SUBJECT_CONTEXT SubjectContext;
NTSTATUS accesst;
NTSTATUS SavedStatus;
ULONG ImageFileNameSize;
HANDLE_TABLE_ENTRY CidEntry;
PEJOB Job;
PPEB Peb;
AUX_ACCESS_DATA AuxData;
PACCESS_STATE AccessState;
ACCESS_STATE LocalAccessState;
BOOLEAN UseLargePages;
SCHAR QuantumReset;
#if defined(_WIN64)
INITIAL_PEB32 InitialPeb32;
#endif
PAGED_CODE();
// 获取当前线程、CPU模式、当前进程
CurrentThread = PsGetCurrentThread ();
PreviousMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
CurrentProcess = PsGetCurrentProcessByThread (CurrentThread);
CreatePeb = FALSE;
UseLargePages = FALSE;
DirectoryTableBase[0] = 0;
DirectoryTableBase[1] = 0;
Peb = NULL;
//
// Reject bogus create parameters for future expansion
// 如果 Flags 里的保留位被置1,就是非法参数
if (Flags&~PROCESS_CREATE_FLAGS_LEGAL_MASK) {
return STATUS_INVALID_PARAMETER;
}
//
// Parent
// 检查有无指定父进程
//
if (ARGUMENT_PRESENT (ParentProcess)) {
// 如果指定了父进程句柄,就获取它的EPROCESS
Status = ObReferenceObjectByHandle (ParentProcess,
PROCESS_CREATE_PROCESS,
PsProcessType,
PreviousMode,
&Parent,
NULL);
if (!NT_SUCCESS (Status)) {
return Status;
}
// 工作集相关
if (JobMemberLevel != 0 && Parent->Job == NULL) {
ObDereferenceObject (Parent);
return STATUS_INVALID_PARAMETER;
}
// 继承父进程的CPU亲和性
Affinity = Parent->Pcb.Affinity;
// 用全局变量初始化工作集最大最小值
WorkingSetMinimum = PsMinimumWorkingSet;
WorkingSetMaximum = PsMaximumWorkingSet;
} else {
// 没有父进程
Parent = NULL;
Affinity = KeActiveProcessors;
WorkingSetMinimum = PsMinimumWorkingSet;
WorkingSetMaximum = PsMaximumWorkingSet;
}
//
// Create the process object
// 创建进程对象 EPROCESS
//
Status = ObCreateObject (PreviousMode,
PsProcessType,
ObjectAttributes,
PreviousMode,
NULL,
sizeof (EPROCESS),
0,
0,
&Process);
if (!NT_SUCCESS (Status)) {
goto exit_and_deref_parent;
}
//
// The process object is created set to NULL. Errors
// That occur after this step cause the process delete
// routine to be entered.
// EPROCESS 数据清零。此后发生的错误会导致调用进程删除函数
//
// Teardown actions that occur in the process delete routine
// do not need to be performed inline.
//
RtlZeroMemory (Process, sizeof(EPROCESS));
ExInitializeRundownProtection (&Process->RundownProtect);
PspInitializeProcessLock (Process);
InitializeListHead (&Process->ThreadListHead);
#if defined(_WIN64)
if (Flags & PROCESS_CREATE_FLAGS_OVERRIDE_ADDRESS_SPACE) {
PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_OVERRIDE_ADDRESS_SPACE);
}
#endif
PspInheritQuota (Process, Parent);
ObInheritDeviceMap (Process, Parent);
if (Parent != NULL) {
Process->DefaultHardErrorProcessing = Parent->DefaultHardErrorProcessing;
Process->InheritedFromUniqueProcessId = Parent->UniqueProcessId;
} else {
Process->DefaultHardErrorProcessing = PROCESS_HARDERROR_DEFAULT;
Process->InheritedFromUniqueProcessId = NULL;
}
//
// Section
//
if (ARGUMENT_PRESENT (SectionHandle)) {
// 如果指定了内存区对象句柄参数 SectionHandle,就获取内存区对象
Status = ObReferenceObjectByHandle (SectionHandle,
SECTION_MAP_EXECUTE,
MmSectionObjectType,
PreviousMode,
&SectionObject,
NULL);
if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
}
} else {
// SectionHandle 参数为NULL,要看父进程是不是 System 进程
SectionObject = NULL;
if (Parent != PsInitialSystemProcess) {
// 如果父进程不是 System 进程 ,那么内存区对象继承自父进程
//
// Fetch the section pointer from the parent process
// as we will be cloning. Since the section pointer
// is removed at last thread exit we need to protect against
// process exit here to be safe.
//
if (ExAcquireRundownProtection (&Parent->RundownProtect)) {
SectionObject = Parent->SectionObject;
if (SectionObject != NULL) {
ObReferenceObject (SectionObject);
}
ExReleaseRundownProtection (&Parent->RundownProtect);
}
if (SectionObject == NULL) {
Status = STATUS_PROCESS_IS_TERMINATING;
goto exit_and_deref;
}
}
// 如果父进程是 System 进程,那么 SectionObject 就是 NULL
}
// 内存区对象 SectionObject 初始化完成(如果是NULL则表示父进程是System进程)
Process->SectionObject = SectionObject;
//
// DebugPort
// 调试端口初始化
if (ARGUMENT_PRESENT (DebugPort)) {
Status = ObReferenceObjectByHandle (DebugPort,
DEBUG_PROCESS_ASSIGN,
DbgkDebugObjectType,
PreviousMode,
&DebugPortObject,
NULL);
if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
}
Process->DebugPort = DebugPortObject;
if (Flags&PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT) {
PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_NO_DEBUG_INHERIT);
}
} else {
if (Parent != NULL) {
DbgkCopyProcessDebugPort (Process, Parent);
}
}
//
// ExceptionPort
// 异常端口初始化
if (ARGUMENT_PRESENT (ExceptionPort)) {
Status = ObReferenceObjectByHandle (ExceptionPort,
0,
LpcPortObjectType,
PreviousMode,
&ExceptionPortObject,
NULL);
if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
}
Process->ExceptionPort = ExceptionPortObject;
}
Process->ExitStatus = STATUS_PENDING;
//
// Clone parent's object table.
// If no parent (booting) then use the current object table created in
// ObInitSystem.
//
// 创建进程地址空间
if (Parent != NULL) {
// 如果有父进程
//
// Calculate address space
//
// If Parent == PspInitialSystem
//
// 创建一个全新的地址空间,这个函数有三个版本,wrk选 procx86.c 的
if (!MmCreateProcessAddressSpace (WorkingSetMinimum,
Process,
&DirectoryTableBase[0])) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit_and_deref;
}
} else {
// 如果没有父进程
// 复制当前进程的句柄表
Process->ObjectTable = CurrentProcess->ObjectTable;
//
// Initialize the Working Set Mutex and address creation mutex
// for this "hand built" process.
// Normally, the call to MmInitializeAddressSpace initializes the
// working set mutex, however, in this case, we have already initialized
// the address space and we are now creating a second process using
// the address space of the idle thread.
//
// 使用空闲线程的地址空间
Status = MmInitializeHandBuiltProcess (Process, &DirectoryTableBase[0]);
if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
}
}
// 进程地址空间初始化完成
PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_HAS_ADDRESS_SPACE);
Process->Vm.MaximumWorkingSetSize = WorkingSetMaximum;
// 初始化进程基础优先级,CPU亲和性,页目录基址,超空间的页帧号
KeInitializeProcess (&Process->Pcb,
NORMAL_BASE_PRIORITY,
Affinity,
&DirectoryTableBase[0],
(BOOLEAN)(Process->DefaultHardErrorProcessing & PROCESS_HARDERROR_ALIGNMENT_BIT));
//
// Initialize the security fields of the process
// The parent may be null exactly once (during system init).
// Thereafter, a parent is always required so that we have a
// security context to duplicate for the new process.
//
// 函数初始化新进程的安全属性,主要是从父进程复制一个令牌
Status = PspInitializeProcessSecurity (Parent, Process);
if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
}
// 设置新进程优先级类别
Process->PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
if (Parent != NULL) {
// 拷贝父进程的优先级类别
if (Parent->PriorityClass == PROCESS_PRIORITY_CLASS_IDLE ||
Parent->PriorityClass == PROCESS_PRIORITY_CLASS_BELOW_NORMAL) {
Process->PriorityClass = Parent->PriorityClass;
}
//
// if address space creation worked, then when going through
// delete, we will attach. Of course, attaching means that the kprocess
// must be initialized, so we delay the object stuff till here.
// 如果地址空间已创建,
// 初始化进程句柄表。如果指定父进程,则拷贝所有设置了继承属性的句柄,句柄计数加一
Status = ObInitProcess ((Flags&PROCESS_CREATE_FLAGS_INHERIT_HANDLES) ? Parent : NULL,
Process);
if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
}
} else {
Status = MmInitializeHandBuiltProcess2 (Process);
if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
}
}
Status = STATUS_SUCCESS;
SavedStatus = STATUS_SUCCESS;
//
// Initialize the process address space
// The address space has four possibilities
// 初始化进程地址空间,有4种可能性
//
// 1 - Boot Process. Address space is initialized during
// MmInit. Parent is not specified.
// 引导进程,这种情况不在这里初始化地址空间,而是在 MmInitSystem 函数初始化
//
// 2 - System Process. Address space is a virgin address
// space that only maps system space. Process is same
// as PspInitialSystemProcess.
// System 进程。地址空间未使用,映射到系统空间。
//
// 3 - User Process (Cloned Address Space). Address space
// is cloned from the specified process.
// 用户进程(克隆父进程地址空间)
//
// 4 - User Process (New Image Address Space). Address space
// is initialized so that it maps the specified section.
// 用户进程(新地址空间镜像)
//
if (SectionHandle != NULL) {
//
// User Process (New Image Address Space). Don't specify Process to
// clone, just SectionObject.
//
// Passing in the 4th parameter as below lets the EPROCESS struct contain its image file name, provided that
// appropriate audit settings are enabled. Memory is allocated inside of MmInitializeProcessAddressSpace
// and pointed to by ImageFileName, so that must be freed in the process deletion routine (PspDeleteProcess())
//
// 新进程指定了内存区对象,调用 MmInitializeProcessAddressSpace 函数初始化进程地址空间
//
Status = MmInitializeProcessAddressSpace (Process,
NULL,
SectionObject,
&Flags,
&(Process->SeAuditProcessCreationInfo.ImageFileName));
if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
}
//
// In order to support relocating executables, the proper status
// (STATUS_IMAGE_NOT_AT_BASE) must be returned, so save it here.
//
SavedStatus = Status;
CreatePeb = TRUE;
UseLargePages = ((Flags & PROCESS_CREATE_FLAGS_LARGE_PAGES) != 0 ? TRUE : FALSE);
} else if (Parent != NULL) {
if (Parent != PsInitialSystemProcess) {
Process->SectionBaseAddress = Parent->SectionBaseAddress;
//
// User Process ( Cloned Address Space ). Don't specify section to
// map, just Process to clone.
//
// 指定了父进程,父进程不是 System 进程,调用 MmInitializeProcessAddressSpace
// 根据父进程初始化地址空间
//
Status = MmInitializeProcessAddressSpace (Process,
Parent,
NULL,
&Flags,
NULL);
if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
}
CreatePeb = TRUE;
UseLargePages = ((Flags & PROCESS_CREATE_FLAGS_LARGE_PAGES) != 0 ? TRUE : FALSE);
//
// A cloned process isn't started from an image file, so we give it the name
// of the process of which it is a clone, provided the original has a name.
//
if (Parent->SeAuditProcessCreationInfo.ImageFileName != NULL) {
ImageFileNameSize = sizeof(OBJECT_NAME_INFORMATION) +
Parent->SeAuditProcessCreationInfo.ImageFileName->Name.MaximumLength;
Process->SeAuditProcessCreationInfo.ImageFileName =
ExAllocatePoolWithTag (PagedPool,
ImageFileNameSize,
'aPeS');
if (Process->SeAuditProcessCreationInfo.ImageFileName != NULL) {
RtlCopyMemory (Process->SeAuditProcessCreationInfo.ImageFileName,
Parent->SeAuditProcessCreationInfo.ImageFileName,
ImageFileNameSize);
//
// The UNICODE_STRING in the process is self contained, so calculate the
// offset for the buffer.
//
Process->SeAuditProcessCreationInfo.ImageFileName->Name.Buffer =
(PUSHORT)(((PUCHAR) Process->SeAuditProcessCreationInfo.ImageFileName) +
sizeof(UNICODE_STRING));
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit_and_deref;
}
}
} else {
//
// System Process. Don't specify Process to clone or section to map
//
// 没有指定内存区对象,但父进程是System进程
//
Flags &= ~PROCESS_CREATE_FLAGS_ALL_LARGE_PAGE_FLAGS;
Status = MmInitializeProcessAddressSpace (Process,
NULL,
NULL,
&Flags,
NULL);
if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
}
//
// In case the image file name of this system process is ever queried, we give
// a zero length UNICODE_STRING.
//
Process->SeAuditProcessCreationInfo.ImageFileName =
ExAllocatePoolWithTag (PagedPool,
sizeof(OBJECT_NAME_INFORMATION),
'aPeS');
if (Process->SeAuditProcessCreationInfo.ImageFileName != NULL) {
RtlZeroMemory (Process->SeAuditProcessCreationInfo.ImageFileName,
sizeof(OBJECT_NAME_INFORMATION));
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit_and_deref;
}
}
}
//
// Create the process ID
//
// 创建进程ID,方法是调用 ExCreateHandle 在System 进程句柄表中存一个句柄
// 句柄值就是PID
//
CidEntry.Object = Process;
CidEntry.GrantedAccess = 0;
Process->UniqueProcessId = ExCreateHandle (PspCidTable, &CidEntry);
if (Process->UniqueProcessId == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto exit_and_deref;
}
ExSetHandleTableOwner (Process->ObjectTable, Process->UniqueProcessId);
//
// Audit the process creation.
// 审计此次进程创建行为
if (SeDetailedAuditingWithToken (NULL)) {
SeAuditProcessCreation (Process);
}
//
// See if the parent has a job. If so reference the job
// and add the process in.
//
// 作业相关,本书不分析
if (Parent) {
Job = Parent->Job;
if (Job != NULL && !(Job->LimitFlags & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)) {
if (Flags&PROCESS_CREATE_FLAGS_BREAKAWAY) {
if (!(Job->LimitFlags & JOB_OBJECT_LIMIT_BREAKAWAY_OK)) {
Status = STATUS_ACCESS_DENIED;
} else {
Status = STATUS_SUCCESS;
}
} else {
Status = PspGetJobFromSet (Job, JobMemberLevel, &Process->Job);
if (NT_SUCCESS (Status)) {
PACCESS_TOKEN Token, NewToken;
Job = Process->Job;
Status = PspAddProcessToJob (Job, Process);
//
// Duplicate a new process token if one is specified for the job
//
Token = Job->Token;
if (Token != NULL) {
Status = SeSubProcessToken (Token,
&NewToken,
FALSE,
Job->SessionId);
if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
}
SeAssignPrimaryToken (Process, NewToken);
ObDereferenceObject (NewToken);
}
}
}
if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
}
}
}
// 对于通过映像内存区对象创建的进程,创建一个PEB
// 对于进程拷贝(fork)的情景,则使用继承的PEB
if (Parent && CreatePeb) {
//
// For processes created w/ a section,
// a new "virgin" PEB is created. Otherwise,
// for forked processes, uses inherited PEB
// with an updated mutant.
//
RtlZeroMemory (&InitialPeb, FIELD_OFFSET(INITIAL_PEB, Mutant));
InitialPeb.Mutant = (HANDLE)(-1);
InitialPeb.ImageUsesLargePages = (BOOLEAN) UseLargePages;
if (SectionHandle != NULL) {
Status = MmCreatePeb (Process, &InitialPeb, &Process->Peb);
if (!NT_SUCCESS (Status)) {
Process->Peb = NULL;
goto exit_and_deref;
}
Peb = Process->Peb;
} else {
SIZE_T BytesCopied;
InitialPeb.InheritedAddressSpace = TRUE;
Process->Peb = Parent->Peb;
MmCopyVirtualMemory (CurrentProcess,
&InitialPeb,
Process,
Process->Peb,
sizeof (INITIAL_PEB),
KernelMode,
&BytesCopied);
#if defined(_WIN64)
if (Process->Wow64Process != NULL) {
RtlZeroMemory (&InitialPeb32, FIELD_OFFSET(INITIAL_PEB32, Mutant));
InitialPeb32.Mutant = -1;
InitialPeb32.InheritedAddressSpace = TRUE;
InitialPeb32.ImageUsesLargePages = (BOOLEAN) UseLargePages;
MmCopyVirtualMemory (CurrentProcess,
&InitialPeb32,
Process,
Process->Wow64Process->Wow64,
sizeof (INITIAL_PEB32),
KernelMode,
&BytesCopied);
}
#endif
}
}
Peb = Process->Peb;
//
// Add the process to the global list of processes.
// 新进程对象插入到全局活动进程链表 PsActiveProcessHead
PspLockProcessList (CurrentThread);
InsertTailList (&PsActiveProcessHead, &Process->ActiveProcessLinks);
PspUnlockProcessList (CurrentThread);
AccessState = NULL;
if (!PsUseImpersonationToken) {
AccessState = &LocalAccessState;
Status = SeCreateAccessStateEx (NULL,
(Parent == NULL || Parent != PsInitialSystemProcess)?
PsGetCurrentProcessByThread (CurrentThread) :
PsInitialSystemProcess,
AccessState,
&AuxData,
DesiredAccess,
&PsProcessType->TypeInfo.GenericMapping);
if (!NT_SUCCESS (Status)) {
goto exit_and_deref;
}
}
//
// Insert the object. Once we do this is reachable from the outside world via
// open by name. Open by ID is still disabled. Since its reachable
// somebody might create a thread in the process and cause
// rundown.
//
// 新进程对象插入到当前进程句柄表中
Status = ObInsertObject (Process,
AccessState,
DesiredAccess,
1, // bias the refcnt by one for future process manipulations
NULL,
&LocalProcessHandle);
if (AccessState != NULL) {
SeDeleteAccessState (AccessState);
}
if (!NT_SUCCESS (Status)) {
goto exit_and_deref_parent;
}
//
// Compute the base priority and quantum reset values for the process and
// set the memory priority.
//
ASSERT(IsListEmpty(&Process->ThreadListHead) == TRUE);
// 计算新进程的基本优先级和时限重置值
BasePriority = PspComputeQuantumAndPriority(Process,
PsProcessPriorityBackground,
&QuantumReset);
Process->Pcb.BasePriority = (SCHAR)BasePriority;
Process->Pcb.QuantumReset = QuantumReset;
//
// As soon as a handle to the process is accessible, allow the process to
// be deleted.
//
// 设置进程访问权限,当前进程句柄可访问,允许进程终止
Process->GrantedAccess = PROCESS_TERMINATE;
if (Parent && Parent != PsInitialSystemProcess) {
Status = ObGetObjectSecurity (Process,
&SecurityDescriptor,
&MemoryAllocated);
if (!NT_SUCCESS (Status)) {
ObCloseHandle (LocalProcessHandle, PreviousMode);
goto exit_and_deref;
}
//
// Compute the subject security context
//
SubjectContext.ProcessAuditId = Process;
SubjectContext.PrimaryToken = PsReferencePrimaryToken(Process);
SubjectContext.ClientToken = NULL;
AccessCheck = SeAccessCheck (SecurityDescriptor,
&SubjectContext,
FALSE,
MAXIMUM_ALLOWED,
0,
NULL,
&PsProcessType->TypeInfo.GenericMapping,
PreviousMode,
&Process->GrantedAccess,
&accesst);
PsDereferencePrimaryTokenEx (Process, SubjectContext.PrimaryToken);
ObReleaseObjectSecurity (SecurityDescriptor,
MemoryAllocated);
if (!AccessCheck) {
Process->GrantedAccess = 0;
}
//
// It does not make any sense to create a process that can not
// do anything to itself.
// Note: Changes to this set of bits should be reflected in psquery.c
// code, in PspSetPrimaryToken.
//
Process->GrantedAccess |= (PROCESS_VM_OPERATION |
PROCESS_VM_READ |
PROCESS_VM_WRITE |
PROCESS_QUERY_INFORMATION |
PROCESS_TERMINATE |
PROCESS_CREATE_THREAD |
PROCESS_DUP_HANDLE |
PROCESS_CREATE_PROCESS |
PROCESS_SET_INFORMATION |
STANDARD_RIGHTS_ALL |
PROCESS_SET_QUOTA);
} else {
Process->GrantedAccess = PROCESS_ALL_ACCESS;
}
// 设置进程创建时间
KeQuerySystemTime (&Process->CreateTime);
try {
if (Peb != NULL && CurrentThread->Tcb.Teb != NULL) {
((PTEB)(CurrentThread->Tcb.Teb))->NtTib.ArbitraryUserPointer = Peb;
}
*ProcessHandle = LocalProcessHandle;
} except (EXCEPTION_EXECUTE_HANDLER) {
NOTHING;
}
if (SavedStatus != STATUS_SUCCESS) {
Status = SavedStatus;
}
exit_and_deref:
ObDereferenceObject (Process);
exit_and_deref_parent:
if (Parent != NULL) {
ObDereferenceObject (Parent);
}
return Status;
}
PspCreateThread
NTSTATUS
PspCreateThread(
OUT PHANDLE ThreadHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN HANDLE ProcessHandle,
IN PEPROCESS ProcessPointer,
OUT PCLIENT_ID ClientId OPTIONAL,
IN PCONTEXT ThreadContext OPTIONAL,
IN PINITIAL_TEB InitialTeb OPTIONAL,
IN BOOLEAN CreateSuspended,
IN PKSTART_ROUTINE StartRoutine OPTIONAL,
IN PVOID StartContext
)
/*++
Routine Description:
This routine creates and initializes a thread object. It implements the
foundation for NtCreateThread and for PsCreateSystemThread.
Arguments:
ThreadHandle - Returns the handle for the new thread.
输出参数,返回新线程句柄
DesiredAccess - Supplies the desired access modes to the new thread.
期望的新线程访问权限
ObjectAttributes - Supplies the object attributes of the new thread.
指定新线程对象的属性
ProcessHandle - Supplies a handle to the process that the thread is being
created within.
新线程将运行在此进程的环境中
ProcessPointer
仅当创建系统线程时,此参数指向 System进程 PsInitialSystemProcess
其他情况下都是NULL
ClientId - Returns the CLIENT_ID of the new thread.
返回新线程的CID
ThreadContext - Supplies a pointer to a context frame that represents the
initial user-mode context for a user-mode thread. The absence
of this parameter indicates that a system thread is being
created.
用户线程执行环境;如果是NULL,则表示创建系统线程
InitialTeb - Supplies the contents of certain fields for the new threads
TEB. This parameter is only examined if both a trap and
exception frame were specified.
用户线程初始化TEB结构
CreateSuspended - Supplies a value that controls whether or not a user-mode
thread is created in a suspended state.
是否挂起这个用户线程
StartRoutine - Supplies the address of the system thread start routine.
系统线程启动函数
StartContext - Supplies context for a system thread start routine.
系统线程启动函数执行环境
--*/
{
HANDLE_TABLE_ENTRY CidEntry;
NTSTATUS Status;
PETHREAD Thread;
PETHREAD CurrentThread;
PEPROCESS Process;
PTEB Teb;
KPROCESSOR_MODE PreviousMode;
HANDLE LocalThreadHandle;
BOOLEAN AccessCheck;
BOOLEAN MemoryAllocated;
PSECURITY_DESCRIPTOR SecurityDescriptor;
SECURITY_SUBJECT_CONTEXT SubjectContext;
NTSTATUS accesst;
LARGE_INTEGER CreateTime;
ULONG OldActiveThreads;
PEJOB Job;
AUX_ACCESS_DATA AuxData;
PACCESS_STATE AccessState;
ACCESS_STATE LocalAccessState;
PAGED_CODE();
// 当前线程
CurrentThread = PsGetCurrentThread ();
// 当前CPU模式
if (StartRoutine != NULL) {
// 如果指定了 StartRoutine ,就是创建系统线程
PreviousMode = KernelMode;
} else {
// 如果未指定 StartRoutine,就通过当前线程判断
PreviousMode = KeGetPreviousModeByThread (&CurrentThread->Tcb);
}
Teb = NULL;
Thread = NULL;
Process = NULL;
// 获取进程对象,存储到局部变量 Process
if (ProcessHandle != NULL) {
// 如果指定了 ProcessHandle,就通过 ProcessHandle 获取进程对象
//
// Process object reference count is biased by one for each thread.
// This accounts for the pointer given to the kernel that remains
// in effect until the thread terminates (and becomes signaled)
//
Status = ObReferenceObjectByHandle (ProcessHandle,
PROCESS_CREATE_THREAD,
PsProcessType,
PreviousMode,
&Process,
NULL);
} else {
if (StartRoutine != NULL) {
// 如果 ProcessHandle 是 NULL,且指定了 StartRoutine ,说明是System进程要创建系统线程
ObReferenceObject (ProcessPointer);
Process = ProcessPointer;
Status = STATUS_SUCCESS;
} else {
// 否则,返回错误
Status = STATUS_INVALID_HANDLE;
}
}
if (!NT_SUCCESS (Status)) {
return Status;
}
//
// If the previous mode is user and the target process is the system
// process, then the operation cannot be performed.
//
// 如果是用户模式,父进程却是System进程,就返回错误
if ((PreviousMode != KernelMode) && (Process == PsInitialSystemProcess)) {
ObDereferenceObject (Process);
return STATUS_INVALID_HANDLE;
}
// 创建 ETHREAD 并清零
Status = ObCreateObject (PreviousMode,
PsThreadType,
ObjectAttributes,
PreviousMode,
NULL,
sizeof(ETHREAD),
0,
0,
&Thread);
if (!NT_SUCCESS (Status)) {
ObDereferenceObject (Process);
return Status;
}
RtlZeroMemory (Thread, sizeof (ETHREAD));
//
// Initialize rundown protection for cross thread TEB refs etc.
//
// 初始化 RundownProtect
ExInitializeRundownProtection (&Thread->RundownProtect);
//
// Assign this thread to the process so that from now on
// we don't have to dereference in error paths.
//
// 设置新线程的父进程
Thread->ThreadsProcess = Process;
// 创建CID
Thread->Cid.UniqueProcess = Process->UniqueProcessId;
CidEntry.Object = Thread;
CidEntry.GrantedAccess = 0;
Thread->Cid.UniqueThread = ExCreateHandle (PspCidTable, &CidEntry);
if (Thread->Cid.UniqueThread == NULL) {
ObDereferenceObject (Thread);
return (STATUS_INSUFFICIENT_RESOURCES);
}
//
// Initialize Mm
//
Thread->ReadClusterSize = MmReadClusterSize;
//
// Initialize LPC
//
KeInitializeSemaphore (&Thread->LpcReplySemaphore, 0L, 1L);
InitializeListHead (&Thread->LpcReplyChain);
//
// Initialize Io
//
InitializeListHead (&Thread->IrpList);
//
// Initialize Registry
//
InitializeListHead (&Thread->PostBlockList);
//
// Initialize the thread lock
//
PspInitializeThreadLock (Thread);
// 初始化定时器链表
KeInitializeSpinLock (&Thread->ActiveTimerListLock);
InitializeListHead (&Thread->ActiveTimerListHead);
// 获得进程的 RundownProtect 锁,防止创建过程中进程被停掉(rundown)
// 直到线程被插入到进程的线程链表中,才能解锁
if (!ExAcquireRundownProtection (&Process->RundownProtect)) {
ObDereferenceObject (Thread);
return STATUS_PROCESS_IS_TERMINATING;
}
if (ARGUMENT_PRESENT (ThreadContext)) {
//
// User-mode thread. Create TEB etc
// 如果 ThreadContext 不空,说明是创建用户线程
// 于是创建TEB,并用 InitialTeb 初始化
Status = MmCreateTeb (Process, InitialTeb, &Thread->Cid, &Teb);
if (!NT_SUCCESS (Status)) {
ExReleaseRundownProtection (&Process->RundownProtect);
ObDereferenceObject (Thread);
return Status;
}
try {
//
// Initialize kernel thread object for user mode thread.
//
// 用 ThreadContext 的 Eip 初始化 StartAddress
Thread->StartAddress = (PVOID)CONTEXT_TO_PROGRAM_COUNTER(ThreadContext);
#if defined(_AMD64_)
Thread->Win32StartAddress = (PVOID)ThreadContext->Rdx;
#elif defined(_X86_)
// 用 ThreadContext 的 Eax 初始化 Win32StartAddress
Thread->Win32StartAddress = (PVOID)ThreadContext->Eax;
#else
#error "no target architecture"
#endif
} except (EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
}
// 根据进程对象的信息初始化线程的一些属性
// 包括 Header, WaitBlock, ServiceTable, APC,定时器,内核栈等
if (NT_SUCCESS (Status)) {
Status = KeInitThread (&Thread->Tcb,
NULL,
PspUserThreadStartup,
(PKSTART_ROUTINE)NULL,
Thread->StartAddress,
ThreadContext,
Teb,
&Process->Pcb);
}
} else {
Teb = NULL;
//
// Set the system thread bit thats kept for all time
// 设置系统线程标志位
//
PS_SET_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_SYSTEM);
//
// Initialize kernel thread object for kernel mode thread.
//
// 设置系统线程启动地址 StartRoutine
Thread->StartAddress = (PKSTART_ROUTINE) StartRoutine;
// 初始化线程
Status = KeInitThread (&Thread->Tcb,
NULL,
PspSystemThreadStartup,
StartRoutine,
StartContext,
NULL,
NULL,
&Process->Pcb);
}
if (!NT_SUCCESS (Status)) {
if (Teb != NULL) {
MmDeleteTeb(Process, Teb);
}
ExReleaseRundownProtection (&Process->RundownProtect);
ObDereferenceObject (Thread);
return Status;
}
// 锁住进程,确保不是在退出或终止过程中
PspLockProcessExclusive (Process, CurrentThread);
//
// Process is exiting or has had delete process called
// We check the calling threads termination status so we
// abort any thread creates while ExitProcess is being called --
// but the call is blocked only if the new thread would be created
// in the terminating thread's process.
//
if ((Process->Flags&PS_PROCESS_FLAGS_PROCESS_DELETE) != 0 ||
(((CurrentThread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_TERMINATED) != 0) &&
(ThreadContext != NULL) &&
(THREAD_TO_PROCESS(CurrentThread) == Process))) {
PspUnlockProcessExclusive (Process, CurrentThread);
KeUninitThread (&Thread->Tcb);
if (Teb != NULL) {
MmDeleteTeb(Process, Teb);
}
ExReleaseRundownProtection (&Process->RundownProtect);
ObDereferenceObject(Thread);
return STATUS_PROCESS_IS_TERMINATING;
}
// 进程的活动线程计数加一
OldActiveThreads = Process->ActiveThreads++;
// 新线程加入进程的线程链表
InsertTailList (&Process->ThreadListHead, &Thread->ThreadListEntry);
// 初始化剩余的域,尤其是和调度相关的,比如优先级、时限设置、CPU亲和性等
KeStartThread (&Thread->Tcb);
// 此时线程可以被调度执行了
PspUnlockProcessExclusive (Process, CurrentThread);
ExReleaseRundownProtection (&Process->RundownProtect);
//
// Failures that occur after this point cause the thread to
// go through PspExitThread
//
// 如果是该进程的第一个线程,触发该进程的创建通知
if (OldActiveThreads == 0) {
PERFINFO_PROCESS_CREATE (Process);
if (PspCreateProcessNotifyRoutineCount != 0) {
ULONG i;
PEX_CALLBACK_ROUTINE_BLOCK CallBack;
PCREATE_PROCESS_NOTIFY_ROUTINE Rtn;
for (i=0; i<PSP_MAX_CREATE_PROCESS_NOTIFY; i++) {
CallBack = ExReferenceCallBackBlock (&PspCreateProcessNotifyRoutine[i]);
if (CallBack != NULL) {
Rtn = (PCREATE_PROCESS_NOTIFY_ROUTINE) ExGetCallBackBlockRoutine (CallBack);
Rtn (Process->InheritedFromUniqueProcessId,
Process->UniqueProcessId,
TRUE);
ExDereferenceCallBackBlock (&PspCreateProcessNotifyRoutine[i],
CallBack);
}
}
}
}
//
// If the process has a job with a completion port,
// AND if the process is really considered to be in the Job, AND
// the process has not reported, report in
//
// This should really be done in add process to job, but can't
// in this path because the process's ID isn't assigned until this point
// in time
//
Job = Process->Job;
if (Job != NULL && Job->CompletionPort &&
!(Process->JobStatus & (PS_JOB_STATUS_NOT_REALLY_ACTIVE|PS_JOB_STATUS_NEW_PROCESS_REPORTED))) {
PS_SET_BITS (&Process->JobStatus, PS_JOB_STATUS_NEW_PROCESS_REPORTED);
KeEnterCriticalRegionThread (&CurrentThread->Tcb);
ExAcquireResourceSharedLite (&Job->JobLock, TRUE);
if (Job->CompletionPort != NULL) {
IoSetIoCompletion (Job->CompletionPort,
Job->CompletionKey,
(PVOID)Process->UniqueProcessId,
STATUS_SUCCESS,
JOB_OBJECT_MSG_NEW_PROCESS,
FALSE);
}
ExReleaseResourceLite (&Job->JobLock);
KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
}
PERFINFO_THREAD_CREATE(Thread, InitialTeb);
//
// Notify registered callout routines of thread creation.
//
if (PspCreateThreadNotifyRoutineCount != 0) {
ULONG i;
PEX_CALLBACK_ROUTINE_BLOCK CallBack;
PCREATE_THREAD_NOTIFY_ROUTINE Rtn;
for (i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++) {
CallBack = ExReferenceCallBackBlock (&PspCreateThreadNotifyRoutine[i]);
if (CallBack != NULL) {
Rtn = (PCREATE_THREAD_NOTIFY_ROUTINE) ExGetCallBackBlockRoutine (CallBack);
Rtn (Thread->Cid.UniqueProcess,
Thread->Cid.UniqueThread,
TRUE);
ExDereferenceCallBackBlock (&PspCreateThreadNotifyRoutine[i],
CallBack);
}
}
}
//
// Reference count of thread is biased once for itself and once for the handle if we create it.
//
// 线程对象的引用计数加2,一个针对当前的创建操作,一个针对要返回的线程句柄
ObReferenceObjectEx (Thread, 2);
// 如果 CreateSuspended ,指示新线程立即被挂起
if (CreateSuspended) {
try {
KeSuspendThread (&Thread->Tcb);
} except ((GetExceptionCode () == STATUS_SUSPEND_COUNT_EXCEEDED)?
EXCEPTION_EXECUTE_HANDLER :
EXCEPTION_CONTINUE_SEARCH) {
}
//
// If deletion was started after we suspended then wake up the thread
//
if (Thread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_TERMINATED) {
KeForceResumeThread (&Thread->Tcb);
}
}
// 根据指定的期望访问权限,调用 SeCreateAccessStateEx 创建一个访问状态结构
AccessState = NULL;
if (!PsUseImpersonationToken) {
AccessState = &LocalAccessState;
Status = SeCreateAccessStateEx (NULL,
ARGUMENT_PRESENT (ThreadContext)?PsGetCurrentProcessByThread (CurrentThread) : Process,
AccessState,
&AuxData,
DesiredAccess,
&PsThreadType->TypeInfo.GenericMapping);
if (!NT_SUCCESS (Status)) {
PS_SET_BITS (&Thread->CrossThreadFlags,
PS_CROSS_THREAD_FLAGS_DEADTHREAD);
if (CreateSuspended) {
(VOID) KeResumeThread (&Thread->Tcb);
}
KeReadyThread (&Thread->Tcb);
ObDereferenceObjectEx (Thread, 2);
return Status;
}
}
// 把新线程对象插入到当前进程的句柄表
Status = ObInsertObject (Thread,
AccessState,
DesiredAccess,
0,
NULL,
&LocalThreadHandle);
if (AccessState != NULL) {
SeDeleteAccessState (AccessState);
}
if (!NT_SUCCESS (Status)) {
//
// The insert failed. Terminate the thread.
//
//
// This trick is used so that Dbgk doesn't report
// events for dead threads
//
PS_SET_BITS (&Thread->CrossThreadFlags,
PS_CROSS_THREAD_FLAGS_DEADTHREAD);
if (CreateSuspended) {
KeResumeThread (&Thread->Tcb);
}
} else {
try {
// 设置输出参数 ThreadHandle
*ThreadHandle = LocalThreadHandle;
// 设置输出参数 ClientId
if (ARGUMENT_PRESENT (ClientId)) {
*ClientId = Thread->Cid;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
PS_SET_BITS (&Thread->CrossThreadFlags,
PS_CROSS_THREAD_FLAGS_DEADTHREAD);
if (CreateSuspended) {
(VOID) KeResumeThread (&Thread->Tcb);
}
KeReadyThread (&Thread->Tcb);
ObDereferenceObject (Thread);
ObCloseHandle (LocalThreadHandle, PreviousMode);
return GetExceptionCode();
}
}
// 设置线程创建时间
KeQuerySystemTime(&CreateTime);
ASSERT ((CreateTime.HighPart & 0xf0000000) == 0);
PS_SET_THREAD_CREATE_TIME(Thread, CreateTime);
// 设置线程访问权限
if ((Thread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_DEADTHREAD) == 0) {
Status = ObGetObjectSecurity (Thread,
&SecurityDescriptor,
&MemoryAllocated);
if (!NT_SUCCESS (Status)) {
//
// This trick us used so that Dbgk doesn't report
// events for dead threads
//
PS_SET_BITS (&Thread->CrossThreadFlags,
PS_CROSS_THREAD_FLAGS_DEADTHREAD);
if (CreateSuspended) {
KeResumeThread(&Thread->Tcb);
}
KeReadyThread (&Thread->Tcb);
ObDereferenceObject (Thread);
ObCloseHandle (LocalThreadHandle, PreviousMode);
return Status;
}
//
// Compute the subject security context
//
SubjectContext.ProcessAuditId = Process;
SubjectContext.PrimaryToken = PsReferencePrimaryToken(Process);
SubjectContext.ClientToken = NULL;
AccessCheck = SeAccessCheck (SecurityDescriptor,
&SubjectContext,
FALSE,
MAXIMUM_ALLOWED,
0,
NULL,
&PsThreadType->TypeInfo.GenericMapping,
PreviousMode,
&Thread->GrantedAccess,
&accesst);
PsDereferencePrimaryTokenEx (Process, SubjectContext.PrimaryToken);
ObReleaseObjectSecurity (SecurityDescriptor,
MemoryAllocated);
if (!AccessCheck) {
Thread->GrantedAccess = 0;
}
Thread->GrantedAccess |= (THREAD_TERMINATE | THREAD_SET_INFORMATION | THREAD_QUERY_INFORMATION);
} else {
Thread->GrantedAccess = THREAD_ALL_ACCESS;
}
// 新线程加入就绪链表,等待调度;或者此时进程不在内存中,设置新线程状态为转移
KeReadyThread (&Thread->Tcb);
// 引用计数减一
ObDereferenceObject (Thread);
return Status;
}
CreateProcessW
BOOL
WINAPI
CreateProcessW(
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
)
{
return CreateProcessInternalW(
NULL, // Create new process with the token on the creator process
lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation,
NULL // Do not return the restricted token
);
}