关于Windows创建进程的过程

时间:2022-01-23 07:50:58

之前有听到别人的面试题是问系统创建进程的具体过程是什么,首先想到的是CreateProcess,但是对于具体过程却不是很清楚,今天整理一下。

从操作系统的角度来说

创建进程步骤:
        1.申请进程块      
        2.为进程分配内存资源
        3.初始化进程块
        4.将进程块链入就绪队列

课本上的知识。。。

从CreateProcess的具体流程来说:

CreateProcess它首先创建一个执行体进程对象,即EPROCESS 对象,然后创建一个初始线程,为初始线程建立一个栈,并设置好它的初始执行环境。完成这些工作以后,该线程就可以参与系统的线程调度了。然而,通过Windows API 函数创建的进程也要接受Windows 子系统的管理,在这种情况下,仅仅内核部分的工作还不够,系统在创建进程过程中,,还需要跟子系统打交道。另外,还需建立起独立的内存地址空间。

CreateProcess通过内核创建进程的步骤,大致分为六个阶段:

NtCreateProcess,它只是简单地对参数稍作处理,然后把创建进程的任务交给NtCreateProcessEx 函数,所以我们来看NtCreateProcessEx 的原型及其流程。

NTSTATUS NtCreateProcessEx( __out PHANDLE ProcessHandle, __in ACCESS_MASK DesiredAccess, __in_opt POBJECT_ATTRIBUTES ObjectAttributes, __in HANDLE ParentProcess, __in ULONG Flags, __in_opt HANDLE SectionHandle, __in_opt HANDLE DebugPort, __in_opt HANDLE ExceptionPort, __in ULONG JobMemberLevel );

NtCreateProcessEx 函数的代码只是简单地检查ProcessHandle 参数代表的句柄是否可写,然后把真正的创建工作交给PspCreateProcess 函数,所以,PspCreateProcess 才是真正创建进程的函数。

PsCreateSystemProcess 可用于创建系统进程对象,它创建的进程都是PsInitialSystemProcess 的子进程。所以,PspCreateProcess函数负责创建系统中的所有进程,包括System 进程。下面介绍此函数的基本流程。

第一阶段:打开目标映像文件

第二阶段:创建内核中的进程对象

第三阶段:创建初始线程

第四阶段:通知windows子系统进程csrss.exe进程来对新进程进行管理

第五阶段:启动初始线程

第六阶段:用户空间的初始化和Dll连接

具体内容:

在Windows中,CreateProcess要先通过系统调用NtCreateProcess创建进程,成功以后就立即通过系统调用NtCreateThread创建其第一个线程。
 
第一阶段:打开目标映像文件
 
首先用CreateProcess(实际上是CreateProcessW)打开指定的可执行映像文件,并创建一个内存区对象。注意,内存区对象并没有被映射到内存中(由于目标进程尚未建立起来,不可能完成内存映射),但它确实是打开了。
 
 
第二阶段:创建内核中的进程对象
 
实际上就是创建以EPROCESS为核心的相关数据结构,主要包括:
 
调用内核中的NtCreateProcessEx 系统服务,实际的调用过程是这样的:kernel32.dll 中的CreateProcessW调用ntdll.dll 中的存根函数NtCreateProcessEx,而ntdll.dll的NtCreateProcessEx 利用处理器的陷阱机制切换到内核模式下;在内核模式下,系统服务分发函数KiSystemService 获得控制,它利用当前线程指定的系统服务表,调用到执行体层的NtCreateProcessEx 函数。然后,执行体层的NtCreateProcessEx 函数执行前面介绍的进程创建逻辑,包括创建EPROCESS 对象、初始化其中的域、创建初始的进程地址空间、创建和初始化句柄表,并设置好EPROCESS 和KPROCESS 中的各种属性,如进程优先级、安全属性、创建时间等。到这里,执行体层的进程对象已经建立起来,进程的地址空间已经初始化,并且EPROCESS 中的PEB 也已初始化。
 
第三阶段:创建初始线程
 
这个阶段是通过调用NtCreateThread()完成的,主要包括:
 现在,虽然进程对象已经建立起来,但是它没有线程,所以,它自己还不能做任何事情。接下来需要创建一个初始线程,在此之前,首先要构造一个栈以及一个可供运行的环境。初始线程的栈的大小可以通过映像文件获得,而创建线程则可以通过调用ntdll.dll 中的NtCreateThread 函数来完成。
创建和设置目标线程的ETHREAD数据结构,并处理好与EPROCESS的关系(例如进程块中的线程计数等等)。
在目标进程的用户空间创建并设置目标线程的TEB。
将目标线程在用户空间的起始地址设置成指向Kernel32.dll中的BaseProcessStart()或BaseThreadStart(),前者用于进程中的第一个线程,后者用于随后的线程。
     用户程序在调用NtCreateThread()时也要提供一个用户级的起始函数(地址), BaseProcessStart()和BaseThreadStart()在完成初始化时会调用这个起始函数。
     ETHREAD数据结构中有两个成份,分别用来存放这两个地址。
调用KeInitThread设置目标线程的KTHREAD数据结构并为其分配堆栈和建立执行环境。
   特别地,将其上下文中的断点(返回点)设置成指向内核中的一段程序KiThreadStartup,使得该线程一旦被调度运行时就从这里开始执行。
系统中可能登记了一些每当创建线程时就应加以调用的“通知”函数,调用这些函数。
 
 
第四阶段:通知windows子系统
 
每个进程在创建/退出的时候都要向windows子系统进程csrss.exe进程发出通知,因为它担负着对windows所有进程的管理的责任,
注意,这里发出通知的是CreateProcess的调用者,不是新建出来的进程,因为它还没有开始运行。
 
至此,CreateProcess的操作已经完成,但子进程中的线程却尚未开始运行,它的运行还要经历下面的第五和第六阶段。
 
 
第五阶段:启动初始线程