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,回到内核状态
参考
博客: