CreateProcess函数源码分析
CreateProcess函数源码分析
源码版本:Windows 2003 源码
源码阅读工具:Source Insight
函数功能分析
函数原型
BOOL CreateProcessA(
[in, optional] LPCSTR lpApplicationName,
[in, out, optional] LPSTR lpCommandLine,
[in, optional] LPSECURITY_ATTRIBUTES lpProcessAttributes,
[in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,
[in] BOOL bInheritHandles,
[in] DWORD dwCreationFlags,
[in, optional] LPVOID lpEnvironment,
[in, optional] LPCSTR lpCurrentDirectory,
[in] LPSTARTUPINFOA lpStartupInfo,
[out] LPPROCESS_INFORMATION lpProcessInformation
);
函数详细文档:CreateProcessA function (processthreadsapi.h) - Win32 apps |微软文档 (microsoft.com)
创建进程流程
总结:创建进程共六个阶段
一、第一阶段:打开目标映像文件,创建文件映射区(NtOpenFile,NtCreateSection)
二、第二阶段:创建进程内核对象和进程地址空间,此时创建的进程只是一个容器
1.ObCreateObject创建内核对象
2.MmCreateProcessAddressSpace创建地址空间
3.KeInitializeProcess初始化新进程内核对象
4.ObInitProcess初始化进程句柄表
5.MmInitializeProcessAddressSpace->MmMapViewOfSection将进程模块映射到地址空间
6.PspMapSystemDll->MmMapViewOfSection映射系统模块ntdll.dll
7.创建进程ID,调用ExCreateHandle把该内核对象加入到进程线程对象表(PspCidTable)中
8.完成EPROCESS创建,将其挂入进程队列并插入创建者的句柄表
9.调用MmCreatePeb创建一个PEB
10.把新进程加入全局的进程链表中:PsActiveProcessHead
11.调用ObInsertObject把新进程对象插入到当前进程的句柄表中,获得进程句柄
过渡阶段:
设置线程优先级,设置PEB的参数块,调用BaseCreateStack创建线程栈,初始化线程上下文
Context->Eip = (ULONG)BaseProcessStartThunk; //主线程的用户空间总入口,内部调用BaseProcessStart
三、第三阶段:创建初始线程
1.调用ObCreateObject创建一个线程对象ETHREAD
2.创建线程ID,调用ExCreateHandle把该内核对象加入到进程线程对象表(PspCidTable)中
3.调用MmCreateTeb创建TEB
4.初始化线程启动地址为BaseProcessStartThunk,Windows子系统启动地址为ImageInformation.TransferAddress
5.调用KeInitThread初始化线程的一些属性;设置线程的启动函数为KiThreadStartup
6.进程的活动线程计数加一,新线程加入进程的线程链表
7.调用KeStartThread初始化线程对象,把线程插入到进程对象(如果线程有挂靠进程,则插入挂靠进程)的线程列表中,此时线程可以被调度执行
8.调用ObReferenceObjectEx线程对象的引用计数加2,一个针对当前的创建操作,一个针对要返回的线程句柄
9.调用ObInsertObject把新线程对象插入到当前进程的句柄表
10.调用KeReadyThread,使线程进入“就绪”状态,此时引用计数-1
四、第四阶段:通知windows子系统有新进程创建
填充向csrss进程发出的消息,调用CsrClientCallServer向csrss通知有新进程创建
五、第五阶段:启动初始线程
根据是否传入CREATE_SUSPENDED决定是否调用NtResumeThread启动初始线程
////下述操作在上述操作完成后自动启动//////////
调用内核中线程的启动函数KiThreadStartup
1.调用PspUserThreadStartup,该函数初始化用户APC,将LdrInitializeThunk函数作为APC函数挂入APC队列中
2.发出中断,隐式调用KiUserApcDispatcher回到用户模式,此时执行PspUserThreadStartup插入的APC函数LdrInitializeThunk
六、用户空间的初始化和Dll连接(加载任何必要的DLL,并且调用这些DLL的入口函数)
LdrInitializeThunk内部实现
1.遍历主模块输入表并加载相关模块(LdrpWalkImportDescriptor)
2.修正主模块重定位(LdrRelocateImageWithBias)
3.初始化TLS(LdrpInitializeTls) 计算TLS占用的空间大小
4.调用模块入口点LdrpRunInitializeRoutines(包括TLS CALLBACK)(DLL_PROCESS_ATTACH)
5.调用NtTestAlert检查当前线程的APC队列,如果APC队列不为空的话,其将会直接调用函数KiUserApcDispatcher处理用户APC,回到内核状态
内核做完部分操作后,再次回到用户态调用BaseProcessStart
1.将主线程的入口函数设置为mainCRTStartup
2.异常处理
调用OEP(_mainCRTStartup)
调用main函数

函数源码分析
函数调用栈
CreateProcess->CreateProcessInternalW
CreateProcessInternalW函数分析
函数原型
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
)
函数详细分析
打开目标映像文件,为其创建一个Section即文件映射区,将文件内容映射进来
1)打开目标文件Status = NtOpenFile(&FileHandle,
SYNCHRONIZE | FILE_EXECUTE,
&LocalObjectAttributes,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_DELETE,
FILE_SYNCHRONOUS_IO_NONALERT |
FILE_NON_DIRECTORY_FILE);
2)为其创建一个文件映射区
Status = NtCreateSection(&SectionHandle,
SECTION_ALL_ACCESS,
NULL,
NULL,
PAGE_EXECUTE,
SEC_IMAGE,
FileHandle);
调用NtCreateProcessEx创建进程内核对象
Status = NtCreateProcessEx(
&ProcessHandle,
PROCESS_ALL_ACCESS,
pObja,
NtCurrentProcess(),
Flags,
SectionHandle,
DebugPortHandle,
NULL,
dwJobMemberLevel // Job member level
);
设置进程优先级
Status = NtSetInformationProcess(ProcessHandle,
ProcessPriorityClass,
&PriorityClass,
sizeof(PROCESS_PRIORITY_CLASS));
if (RealTimePrivilegeState) RtlReleasePrivilege(RealTimePrivilegeState);
设置进程参数(BasePushProcessParameters):设置进程参数、当前目录、环境变量等信息
//内部调用了RtlCreateProcessParameters来创建进程参数, 该函数对RTL_USER_PROCESS_PARAMETERS结构中的字符串域的地址改为相对的偏移量
Result = BasePushProcessParameters(ParameterFlags,
ProcessHandle,
RemotePeb,
lpApplicationName,
CurrentDirectory,
lpCommandLine,
lpEnvironment,
&StartupInfo,
dwCreationFlags | NoWindow,
bInheritHandles,
IsWowApp ? IMAGE_SUBSYSTEM_WINDOWS_GUI: 0,
AppCompatData,
AppCompatDataSize);
调用BaseCreateStack创建线程栈
Status = BaseCreateStack(ProcessHandle,
ImageInformation.CommittedStackSize,
StackSize,
&InitialTeb);
初始化线程上下文
BaseInitializeContext(&Context,
Peb,
ImageInformation.TransferAddress,
InitialTeb.StackBase,
0);
BaseInitializeContext内部实现:
1)设置寄存器值
Context->Eax = (ULONG)StartAddress; //入口点 ImageInformation.TransferAddress
Context->Ebx = (ULONG)Parameter;//参数 Peb
Context->Esp = (ULONG)StackAddress;//栈底指针 InitialTeb.StackBase
2)设置段寄存器的值
3)判断上下文类型是否为1 (创建进程中传入0) 注意:eip = BaseProcessStartThunkif (ContextType == 1)
{
/* For Threads */
Context->Eip = (ULONG)BaseThreadStartupThunk; //普通线程的用户空间总入口
}
else if (ContextType == 2)
{
Context->Esp -= sizeof(PVOID);
*((PVOID*)Context->Esp) = BaseFiberStartup;//纤程
}
else
{
Context->Eip = (ULONG)BaseProcessStartThunk; //主线程的用户空间总入口
//内部调用BaseProcessStart,这个函数就是调用OEP所在函数的上层函数
}
调用NtCreateThread创建初始线程
Status = NtCreateThread(
&ThreadHandle,
THREAD_ALL_ACCESS,
pObja,
ProcessHandle,
&ClientId,
&ThreadContext,
&InitialTeb,
TRUE
);
通知windows子系统有新进程创建
每个进程在创建/退出的时候都要向windows子系统进程csrss.exe进程发出通知,因为它担负着对windows所有进程的管理的责任
注意,这里发出通知的是CreateProcess的调用者,不是新建出来的进程,因为它还没有开始运行
1)填充向csrss进程发出的信息CreateProcessMsg->ProcessHandle = ProcessHandle;
CreateProcessMsg->ThreadHandle = ThreadHandle;
CreateProcessMsg->ClientId = ClientId;
CreateProcessMsg->PebAddressNative = RemotePeb;
CreateProcessMsg->PebAddressWow64 = (ULONG)RemotePeb;
RemotePeb = NULL;
switch (ImageInformation.Machine)
{
/* IA32, IA64 and AMD64 are supported in Server 2003 */
case IMAGE_FILE_MACHINE_I386:
CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
break;
case IMAGE_FILE_MACHINE_IA64:
CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_IA64;
break;
case IMAGE_FILE_MACHINE_AMD64:
CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64;
break;
/* Anything else results in image unknown -- but no failure */
default:
DbgPrint("kernel32: No mapping for ImageInformation.Machine == %04x\n", ImageInformation.Machine);
CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_UNKNOWN;
break;
}
2)通知csrss进程
CsrClientCallServer((PCSR_API_MESSAGE)&CsrMsg[0],
CaptureBuffer,
CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepCreateProcess),
sizeof(*CreateProcessMsg));
启动初始线程
根据是否传入CREATE_SUSPENDED决定新创建的线程是否被立即调度运行
if (!(dwCreationFlags & CREATE_SUSPENDED))
{
NtResumeThread(ThreadHandle, &ResumeCount);
}
上述工作做完以后自动调用以下函数
调用内核中线程的启动函数KiThreadStartup
1)调用PspUserThreadStartup,该函数初始化用户APC,将LdrInitializeThunk函数作为APC函数挂入APC队列中
①初始化用户APCKiInitializeUserApc(KeGetExceptionFrame(&Thread->Tcb),
KeGetTrapFrame(&Thread->Tcb),
PspSystemDllEntryPoint,
NULL,
PspSystemDllBase,
NULL);
2)中断,隐式调用KiUserApcDispatcher回到用户模式,此时执行PspUserThreadStartup插入的APC函数LdrInitializeThunk
KiServiceExit2(TrapFrame);
调用LdrInitializeThunk完成用户空间的初始化和Dll连接(加载任何必要的DLL,并且调用这些DLL的入口函数)
LdrInitializeThunk()是 ntdll.dll 中不经连接就可进入的函数,实质上就是 ntdll.dll 的入口内核做完部分操作后,再次回到用户态调用BaseProcessStart
内部实现:
1)将主线程的入口函数设置为mainCRTStartup
NtSetInformationThread(GetCurrentThread(),SET_THREAD_ENTRY_ROUTINE,&lpfnStartRoutine,sizeof(lpfnStartRoutine));
2)异常处理调用OEP(_mainCRTStartup)
1)初始化缓冲区溢出全局变量->__security_init_cookie
2)初始化C语法中的全局数据->_initterm_e
3)初始化C++语法中的全局数据->_initterm
4)线程局部存储变量->__scrt_get_dyn_tls_init_callback
5)注册线程局部存储析构函数->__scrt_get_dyn_tls_dtor_callback
6)初始化完成,调用main()函数->invoke_main
7)main()函数返回执行析构函数或atexit注册的函数指针,并结束程序->exit(main_result)
NtCreateProcessEx函数分析
函数调用栈
NtCreateProcessEx->PspCreateProcess
PspCreateProcess函数详细分析
如果父进程句柄ParentProcess不为空,则通过ObReferenceObjectByHandle获得父进程对象的EPROCESS指针,放在局部变量Parent
Status = ObReferenceObjectByHandle(ParentProcess,
PROCESS_CREATE_PROCESS,
PsProcessType,
PreviousMode,
(PVOID*)&Parent,
NULL);
调用ObCreateObject创建一个类型为PsProcessType的内核对象,置于局部变量Process中,对象体为EPROCESS,即创建一个EPROCESS
Status = ObCreateObject(PreviousMode,
PsProcessType,
ObjectAttributes,
PreviousMode,
NULL,
sizeof(EPROCESS),
0,
0,
(PVOID*)&Process);
把Process置0,并初始化部分成员
//初始化进程的引用计数
ExInitializeRundownProtection(&Process->RundownProtect);
//初始化进程的线程链表
InitializeListHead(&Process->ThreadListHead);
//继承资源配额
PspInheritQuota(Process, Parent);
//继承父进程的设备位图
ObInheritDeviceMap(Parent, Process);
//继承父进程
Process->DefaultHardErrorProcessing = Parent->DefaultHardErrorProcessing;
//保存父进程PID
Process->InheritedFromUniqueProcessId = Parent->UniqueProcessId;
获取文件映射内存区对象的指针
Status = ObReferenceObjectByHandle(SectionHandle,
SECTION_MAP_EXECUTE,
MmSectionObjectType,
PreviousMode,
(PVOID*)&SectionObject,
NULL);
调用MmCreateProcessAddressSpace创建新的进程地址空间
MmCreateProcessAddressSpace(MinWs,Process,DirectoryTableBase)
初始化新进程内核对象的基本优先级、Affinity、进程页表目录和超空间的页帧号
KeInitializeProcess(&Process->Pcb,
PROCESS_PRIORITY_NORMAL,
Affinity,
DirectoryTableBase,
(BOOLEAN)(Process->DefaultHardErrorProcessing & 4));
初始化新进程的安全属性(赋值父进程的Token)
Status = PspInitializeProcessSecurity(Process, Parent);
设置新进程优先级
Process->PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
初始化进程的句柄表 一种就是通过复制父进程的句柄表,一种就是创建一个新的句柄表
Status = ObInitProcess ((Flags&PROCESS_CREATE_FLAGS_INHERIT_HANDLES) ? Parent : NULL, Process);
把进程模块映射进内存,初始化新进程的地址空间,内部调用MmMapViewOfSection将进程模块映射到地址空间
//1.User Process(New Image Address Space):根据指定文件区域对象映射地址空间
Status = MmInitializeProcessAddressSpace(Process,
NULL,
SectionObject,
&Flags,
&Process->
SeAuditProcessCreationInfo.
ImageFileName);
//2.User Process(Cloned Address Space):根据父进程克隆地址空间,并把父进程的映像名称拷贝到新进程对象的数据结构中
NeedsPeb = TRUE;
//3.System Process:系统进程的初始化,无父进程,无文件区域对象,如果是系统进程,同样拷贝父进程的映像名称到新进程对象的数据结构中
Status = MmInitializeProcessAddressSpace(Process,
NULL,
NULL,
&Flags,
NULL);
//4.Boot Process:系统引导时调用,无父进程。地址空间在MmInitSystem执行过程中初始化,由MiInitMachineDependent调用MmInitializeProcessAddressSpace来完成
MmInitializeProcessAddressSpace调用MmMapViewOfSection(即将进程映射到默认地址处)
BaseAddress = NULL;
ViewSize = 0;
ZERO_LARGE (SectionOffset); Status = MmMapViewOfSection ((PSECTION)SectionToMap,
ProcessToInitialize,
&BaseAddress,
0,
0,
&SectionOffset,
&ViewSize,
ViewShare,
0,
PAGE_READWRITE); ProcessToInitialize->SectionBaseAddress = BaseAddress;
映射系统模块ntdll.dll(PspMapSystemDll-->MmMapViewOfSection)
if (SectionObject) PspMapSystemDll(Process, NULL, FALSE);
把该内核对象加入到进程线程对象表(PspCidTable)中,得到进程ID,创建进程ID。调用ExCreateHandle在System进程句柄表中存一个句柄,句柄值就是PID
CidEntry.Object = Process;
CidEntry.GrantedAccess = 0;
Process->UniqueProcessId = ExCreateHandle(PspCidTable, &CidEntry);
设置对象表的进程ID
Process->ObjectTable->UniqueProcessId = Process->UniqueProcessId;
如果父进程属于一个作业对象,则也加入到父进程所在的作业中
对于通过映像内存区来创建进程的情形,创建一个PEB,对于进程拷贝的情况,则使用继承的PEB
Status = MmCreatePeb(Process, &InitialPeb, &Process->Peb);
把新进程加入全局的进程链表中。PsActiveProcessHead
InsertTailList(&PsActiveProcessHead, &Process->ActiveProcessLinks);
创建一个access state
Status = SeCreateAccessStateEx(CurrentThread,
((Parent) &&
(Parent == PsInitialSystemProcess)) ?
Parent : CurrentProcess,
&LocalAccessState,
&AuxData,
DesiredAccess,
&PsProcessType->TypeInfo.GenericMapping);
把新进程对象插入到当前进程的句柄表中,获得进程句柄
Status = ObInsertObject(Process,
AccessState,
DesiredAccess,
1,
NULL,
&hProcess);
计算新进程的优先级和时限重置值,设置内存优先级
Process->Pcb.BasePriority =(SCHAR)PspComputeQuantumAndPriority(Process,PsProcessPriorityBackground,&Quantum);
Process->Pcb.QuantumReset = Quantum;
设置进程访问权限,当前进程句柄可访问,允许进程终止
//有父进程,但不是PsInitialialSystemProcess,首先执行访问检查,然后计算进程的访问权限
如果是PsInitialialSystemProcess的子进程,则授予所有的访问权限
Process->GrantedAccess = PROCESS_TERMINATE;
设置进程的创建时间,把新进程的句柄赋到ProcessHandle中
KeQuerySystemTime(&Process->CreateTime);
执行(监控进程创建)回调函数
PspRunCreateProcessNotifyRoutines(Process, TRUE);
NtCreateThread函数分析
函数调用栈
NtCreateThread->PspCreateThread
函数详细分析
获取进程对象,存储到局部变量 Process
Status = ObReferenceObjectByHandle(ProcessHandle,
PROCESS_CREATE_THREAD,
PsProcessType,
PreviousMode,
(PVOID*)&Process,
NULL);
PSREFTRACE(Process);
调用ObCreateObject创建一个线程对象ETHREAD,并初始化为零
Status = ObCreateObject(PreviousMode,
PsThreadType,
ObjectAttributes,
PreviousMode,
NULL,
sizeof(ETHREAD),
0,
0,
(PVOID*)&Thread);
RtlZeroMemory(Thread, sizeof(ETHREAD));
初始化RundownProtect
ExInitializeRundownProtection(&Thread->RundownProtect);
设置新线程的父进程
Thread->ThreadsProcess = Process;
Thread->Cid.UniqueProcess = Process->UniqueProcessId;
创建线程ID,调用ExCreateHandle在PspCidTable内核句柄表中存一个句柄,句柄值就是TID
PspCidTable:存放进程和线程的内核对象(EPROCESS 和 ETHREAD),并通过 PID 和 TID 进行索引,ID 号以 4 递增
CidEntry.Object = Thread;
CidEntry.GrantedAccess = 0;
Thread->Cid.UniqueThread = ExCreateHandle(PspCidTable, &CidEntry);
调用MmCreateTeb函数创建TEB,用ThreadContext的Eip初始化StartAddress,用ThreadContext的Eax初始化Win32StartAddress
Status = MmCreateTeb(Process, &Thread->Cid, InitialTeb, &TebBase);
初始化线程启动地址和Windows子系统的启动地址
Thread->StartAddress = (PVOID)KeGetContextPc(ThreadContext);//ThreadContext->Eip 初始化线程的启动地址
Thread->Win32StartAddress = (PVOID)KeGetContextReturnRegister(ThreadContext);//ThreadContext->Eax 初始化WINDOWS子系统的启动地址
初始化线程的一些属性,包括同步头(Header域), WaitBlock, ServiceTable, APC,定时器,线程的内核栈;设置线程的启动函数为KiThreadStartup
·当创建用户线程赋值PspUserThreadStartup 当线程创建完毕后,在3环下启动线程时,即通过此函数入口点执行。
·当创建系统线程赋值PspSystemThreadStartupStatus = KeInitThread(&Thread->Tcb,
NULL,
PspUserThreadStartup,
NULL,
Thread->StartAddress,
ThreadContext,
TebBase,
&Process->Pcb);
KeInitThread内部实现:
1)根据进程对象中的信息来初始化新线程的一些属性
2)根据所提供的参数信息调用KiInitializeContextThreadKiInitializeContextThread(Thread,
SystemRoutine,//PspUserThreadStartup
StartRoutine,
StartContext,//Thread->StartAddress
Context);//ThreadContext
KiInitializeContextThread内部实现:
1)在堆栈上构建KTRAP_FRAME(陷井框架, 用来系统调用时保存用户空间堆栈的信息,或者在为了返回到用户空间时构建的上下文环境)
KeContextToTrapFrame(Context,
NULL,
TrapFrame,
CONTEXT_AMD64 | ContextFlags,
UserMode);
2)指定了线程的启动函数KiThreadStartup
CtxSwitchFrame->Return = (ULONG64)KiThreadStartup;
启动函数内部调用PspUserThreadStartup:
StartFrame->SystemRoutine(StartFrame->StartRoutine, StartFrame->StartContext);锁住进程,确保不是在退出或终止过程中
KeEnterCriticalRegion();
ExAcquirePushLockExclusive(&Process->ProcessLock);
进程的活动线程计数加一,新线程加入进程的线程链表
InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry);
Process->ActiveThreads++;
初始化剩余的域,尤其是和调度相关的,比如优先级、时限设置、CPU亲和性等,把线程插入到进程对象(如果线程有挂靠进程,则插入挂靠进程)的线程列表中,此时线程可以被调度执行了
KeStartThread(&Thread->Tcb);
释放进程锁
ExReleasePushLockExclusive(&Process->ProcessLock);
KeLeaveCriticalRegion();
如果被创建的线程是该进程的第一个线程,触发该进程的创建通知
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);
}
}
}
}
作业处理,暂时略过
线程对象的引用计数加2,一个针对当前的创建操作,一个针对要返回的线程句柄
ObReferenceObjectEx(Thread, 2);
如果传入挂起标识,挂起该线程
if (CreateSuspended) KeSuspendThread(&Thread->Tcb);
根据指定的期望访问权限,调用 SeCreateAccessStateEx 创建一个访问状态结构
Status = SeCreateAccessStateEx(NULL,
ThreadContext ?
PsGetCurrentProcess() : Process,
&LocalAccessState,
&AuxData,
DesiredAccess,
&PsThreadType->TypeInfo.GenericMapping);
把新线程对象插入到当前进程的句柄表
Status = ObInsertObject(Thread,
AccessState,
DesiredAccess,
0,
NULL,
&hThread);
设置输出参数ThreadHandle,设置输出参数 ClientId
if (ClientId) *ClientId = Thread->Cid;
*ThreadHandle = hThread;
设置线程的创建时间
KeQuerySystemTime(&Thread->CreateTime);
设置线程访问权限
调用KeReadyThread,使线程进入“就绪”状态,准备马上执行。或者此时进程未在内存中,则新线程的状态为“转移”,以等待换入内存后再执行。引用计数减1
KeReadyThread(&Thread->Tcb);
ObDereferenceObject(Thread);
LdrInitializeThunk函数分析
函数调用栈
LdrInitializeThunk->LdrpInitialize
LdrpInitialize函数详细分析
如果是进程中第一个线程,调用LdrpInitializeProcess
InitStatus = LdrpInitializeProcess (Context, SystemArgument1);
LdrpInitializeProcess函数实现:
1)获得TEB和PEB的相关信息
Teb = NtCurrentTeb();
Peb = Teb->ProcessEnvironmentBlock;
ProcessParameters = Peb->ProcessParameters;
pw = ProcessParameters->ImagePathName.Buffer;
if (!(ProcessParameters->Flags & RTL_USER_PROC_PARAMS_NORMALIZED)) {
pw = (PWSTR)((PCHAR)pw + (ULONG_PTR)(ProcessParameters));
}
UnicodeImageName.Buffer = pw;
UnicodeImageName.Length = ProcessParameters->ImagePathName.Length;
UnicodeImageName.MaximumLength = UnicodeImageName.Length + sizeof(WCHAR);
StaticCurDir = TRUE;
UseCOR = FALSE;
ImagePathNameBuffer = NULL;
DebugProcessHeapOnly = 0;
2)获得NtHeader
NtHeader = RtlImageNtHeader (Peb->ImageBaseAddress);
3)初始化NLS
RtlInitNlsTables (Peb->AnsiCodePageData,
Peb->OemCodePageData,
Peb->UnicodeCaseTableData,
&xInitTableInfo);
4)设置PEB的标志及相关内容
5)创建进程堆(RtlCreateHeap)
ProcessHeap = RtlCreateHeap (ProcessHeapFlags,
NULL,
NtHeader->OptionalHeader.SizeOfHeapReserve,
NtHeader->OptionalHeader.SizeOfHeapCommit,
NULL, // Lock to use for serialization
&HeapParameters);
6)为Loader创建堆
LdrpHeap = RtlCreateHeap (
HEAP_GROWABLE | HEAP_CLASS_1,
NULL,
64 * 1024, // 0 is ok here, 64k is a chosen tuned number
24 * 1024, // 0 is ok here, 24k is a chosen tuned number
NULL,
&LdrpHeapParameters);
NtdllBaseTag = RtlCreateTagHeap (ProcessHeap,
0,
L"NTDLL!",
L"!Process\0"
L"CSRSS Client\0"
L"LDR Database\0"
L"Current Directory\0"
L"TLS Storage\0"
L"DBGSS Client\0"
L"SE Temporary\0"
L"Temporary\0"
L"LocalAtom\0");
7)设置系统Dll路径为\system32\mscoree.dll
SystemDllPath.Buffer = SystemDllPathBuffer;
SystemDllPath.Length = 0;
SystemDllPath.MaximumLength = sizeof (SystemDllPathBuffer);
RtlInitUnicodeString (&SystemRoot, USER_SHARED_DATA->NtSystemRoot);
RtlAppendUnicodeStringToString (&SystemDllPath, &SystemRoot);
RtlAppendUnicodeStringToString (&SystemDllPath, &SlashSystem32SlashString);
8)获得Knowndll目录所在的路径,打开如\KnownDlls\KnownDllPath形式的符号链接,获得KnownDllPath
InitializeObjectAttributes (&Obja,
(PUNICODE_STRING)&SlashKnownDllsString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
st = NtOpenDirectoryObject (&LdrpKnownDllObjectDirectory,
DIRECTORY_QUERY | DIRECTORY_TRAVERSE,
&Obja);

9)为第一个LdrDataTableEntry分配内存并初始化(第一个模块即为当前进程exe),将其插入模块链表中
10)为系统DLL(ntdll)分配内存并初始化,将其插入模块链表中
11)遍历主模块输入表并加载相关模块(LdrpWalkImportDescriptor)
st = LdrpWalkImportDescriptor (LdrpDefaultPath.Buffer, LdrpImageEntry);
12)修正主模块重定位(LdrRelocateImageWithBias)
if ((PVOID)NtHeader->OptionalHeader.ImageBase != Peb->ImageBaseAddress) {
PVOID ViewBase;
ViewBase = Peb->ImageBaseAddress;
st = LdrpSetProtection (ViewBase, FALSE);
st = LdrRelocateImage (ViewBase,
"LDR",
STATUS_SUCCESS,
STATUS_CONFLICTING_ADDRESSES,
STATUS_INVALID_IMAGE_FORMAT);
}
Peb->ImageBaseAddress的赋值如下:
1.在MmCreateProcessAddressSpace中调用MmCreateSection->MiCreateImageFileMap:
NewSegment->BasedAddress = (PVOID) NextVa;//NextVa即为0x400000 2.在MmInitializeProcessAddressSpace调用MmMapViewOfSection(即将进程映射到默认地址处):
BaseAddress = NULL;
ViewSize = 0;
ZERO_LARGE (SectionOffset);
Status = MmMapViewOfSection ((PSECTION)SectionToMap,
ProcessToInitialize,
&BaseAddress,
0,
0,
&SectionOffset,
&ViewSize,
ViewShare,
0,
PAGE_READWRITE);
3.在MmMapViewOfSection中调用MiMapViewOfImageSection:
1)若传入BaseAddress不为空
StartingAddress = MI_64K_ALIGN(*CapturedBase);
EndingAddress = (PVOID)(((ULONG_PTR)StartingAddress + *CapturedViewSize - 1) | (PAGE_SIZE - 1));
2)若传入BaseAddress为空
StartingAddress = (PVOID)((ULONG_PTR)BasedAddress + (ULONG_PTR)MI_64K_ALIGN(SectionOffset->LowPart));
EndingAddress = (PVOID)(((ULONG_PTR)StartingAddress + *CapturedViewSize - 1) | (PAGE_SIZE - 1)); 4.回到MmInitializeProcessAddressSpace函数中,对EPROCESS.SectionBaseAddress进行赋值:
ProcessToInitialize->SectionBaseAddress = BaseAddress; 5.在MmCreatePeb函数中:
PebBase->ImageBaseAddress = TargetProcess->SectionBaseAddress;
13)初始化TLS(LdrpInitializeTls) 计算TLS占用的空间大小
14)如果有调试器,通知调试器
15)调用模块入口点LdrpRunInitializeRoutines(包括TLS CALLBACK)(DLL_PROCESS_ATTACH)
st = LdrpRunInitializeRoutines (Context);
非第一个线程,调用LdrpInitializeThread函数
LdrpInitializeThread函数实现:循环调用各个模块的TLS callback,入口点函数 (DLL_THREAD_ATTACH)
while (Next != &PebLdr.InMemoryOrderModuleList) {
LdrDataTableEntry = (PLDR_DATA_TABLE_ENTRY)(CONTAINING_RECORD(Next, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks));
if ((Peb->ImageBaseAddress != LdrDataTableEntry->DllBase) &&
(!(LdrDataTableEntry->Flags & LDRP_DONT_CALL_FOR_THREADS))) { InitRoutine = (PDLL_INIT_ROUTINE)(ULONG_PTR)LdrDataTableEntry->EntryPoint;
if ((InitRoutine) &&
(LdrDataTableEntry->Flags & LDRP_PROCESS_ATTACH_CALLED) &&
(LdrDataTableEntry->Flags & LDRP_IMAGE_DLL)) { LDRP_ACTIVATE_ACTIVATION_CONTEXT (LdrDataTableEntry); if (LdrDataTableEntry->TlsIndex) {
if (!LdrpShutdownInProgress) {
LdrpCallTlsInitializers (LdrDataTableEntry->DllBase, DLL_THREAD_ATTACH);
}
} if (!LdrpShutdownInProgress) { LdrpCallInitRoutine (InitRoutine,
LdrDataTableEntry->DllBase,
DLL_THREAD_ATTACH,
NULL);
}
LDRP_DEACTIVATE_ACTIVATION_CONTEXT ();
}
}
Next = Next->Flink;
}
测试Alart(NtTestAlert):不用alertable状态运行APC作业的函数
检查当前线程的APC队列,如果APC队列不为空的话,其将会直接调用函数KiUserApcDispatcher处理用户APC,回到内核状态
参考
博客:
内核原理与实现 011进程线程创建过程 | 码农家园 (codenong.com)
(74条消息) CreateProcess 内部实现_zhou191954的博客-CSDN博客
CreateProcess函数源码分析的更多相关文章
- Vue中之nextTick函数源码分析
Vue中之nextTick函数源码分析 1. 什么是Vue.nextTick()?官方文档解释如下:在下次DOM更新循环结束之后执行的延迟回调.在修改数据之后立即使用这个方法,获取更新后的DOM. 2 ...
- PHP 源码 — intval 函数源码分析
PHP 源码 - intval 函数源码分析 文章来源: https://github.com/suhanyujie/learn-computer/ 作者:suhanyujie 基于PHP 7.3.3 ...
- PHP 源码 —— is_array 函数源码分析
is_array 函数源码分析 本文首发于 https://github.com/suhanyujie/learn-computer/blob/master/src/function/array/is ...
- 序列化器中钩子函数源码分析、many关键字源码分析
局部钩子和全局钩子源码分析(2星) # 入口是 ser.is_valid(),是BaseSerializer的方法 # 最核心的代码 self._validated_data = self.run_v ...
- Spark GraphX的函数源码分析及应用实例
1. outerJoinVertices函数 首先给出源代码 override def outerJoinVertices[U: ClassTag, VD2: ClassTag] (other: RD ...
- 【C++】【源码解读】std::is_same函数源码解读
std::is_same使用很简单 重点在于对源码的解读 参考下面一句静态断言: static_assert(!std::is_same<bool, T>::value, "ve ...
- lodash框架中的chunk与drop函数源码逐行分析
lodash是一个工具库,跟underscore差不多 chunk函数的作用: 把一维数组,按照固定的长度分段成二维数组 如: chunk( [ 10, 20, 30, 40 ], 2 ) 结 ...
- Go语言fmt库的print函数源码解析
// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a B ...
- 巡风视图函数源码学习--view.py
记录一下巡风扫描器view.py这个脚本里的视图函数的学习,直接在代码里面做的注释,里面有一些print 代码是为了把数据打印出来小白我自己加的,勿怪勿怪.可能存在一些理解错误和不到位的地方,希望大佬 ...
- mongodb操作:利用javaScript封装db.collection.find()后可调用函数源码解读
{ "_mongo" : connection to YOURIP:27017{ SSL: { sslSupport: false, sslPEMKeyFile: "&q ...
随机推荐
- 看火山引擎DataLeap如何做好电商治理(一):挑战与痛点
现在人们的日常生活中,网购已经成为人们生活中不可或缺的购物形式.根据中国电子商会发布的<中国社交电商行业发展白皮书(2022)>的数据显示,2022年社交电商市场交易规模达到28542.8 ...
- 【flask】flask-sqlalchemy使用 flask-migrate使用 flask项目演示
目录 昨日回顾 今日内容 1 flask-sqlalchemy使用 2 flask-migrate使用 3 flask项目演示 昨日回顾 类装饰器可能有两种含义: 使用类作为装饰器 装饰类的装饰器 基 ...
- 记录一次Java内存泄露分析过程
版权说明: 本文章版权归本人及博客园共同所有,转载请在文章前标明原文出处( https://www.cnblogs.com/mikevictor07/p/13032635.html ),以下内容为个人 ...
- awk 文本编辑器
1.简介 文本编辑器 非交互式的编辑器 编程语言 功能:对文本数据进行汇总和处理 是一个报告生成器 能够对数据进行排版 工作模式:行工作模式 读入一行 将整行内容存在$0里,一行等于一个记录 记录分隔 ...
- 在Windows上D盘上安装Docker
Reference https://www.willh.cn/articles/2022/07/13/1657676401964.html Docker默认安装在C盘: "C:\Progra ...
- OS | 银行家算法C语言实现
算法简介 银行家算法(Banker's Algorithm)是一个避免死锁( Deadlock)的著名算法,是由艾兹格·迪杰斯特拉在1965年为T.H.E系统设计的一种避免死锁产生的算法.它以银行借贷 ...
- 基于 HTML5 WebGL + WebVR 的 3D 虚拟现实可视化培训系统
前言 2019 年 VR, AR, XR, 5G, 工业互联网等名词频繁出现在我们的视野中,信息的分享与虚实的结合已经成为大势所趋,5G 是新一代信息通信技术升级的重要方向,工业互联网是制造业转型升级 ...
- 通义千问预体验,如何让 AI 模型应用“奔跑”在函数计算上?
立即体验基于函数计算部署通义千问预体验: https://developer.aliyun.com/topic/aigc_fc AIGC 浪潮已来,从文字生成到图片生成,AIGC 的创造力让人惊叹,更 ...
- hdu 5547
***题意:4*4数独,要求在同一行同一列不能有相同的数字,另外在2*2的小单元里也不能有相同的数字 思路:DFS暴力搜索, 每个位置填1-4,递归回溯,判断是否符合条件,递归到最后一个位置+1则输出 ...
- loadrunner12的安装教程
一.LR12安装包: 链接:https://pan.baidu.com/s/1UU304e-nP7qAL-fV8T39YQ 密码:jpln 二.LR12安装: 1.下载完成后点击解压