Windows 进程创建过程
一、进程创建函数
Windows API 提供了多个创建进程的函数,最简单的是CreateProess()
,该函数创建一个和当前进程具有相同用户Token的进程。
如果想要创建和当前进程具有不同用户Token的进程,则需要使用CreateProcessAsUser()
,该函数可以接受一个Token句柄作为参数,并创建一个具有该Token的进程。
其他创建进程的函数还包括CreateProcessWithTokenW()
以及CreateProcessWithLogonW()
(都是advapi.dll
的导出函数)。CreateProcessWithTokenW()
和函数CreateProcessAsUser()
很相似,但是两者对当前进程Token的权限要求不同。CreateProcsssWithTokenW()
需要当前进程中的用户Token具有 SE_IMPERSONATE_NAME 权限,CreateProcessWithLogonW()
则需要当前进程中具有 SE_INCREASE_NOT_HELD 权限。
CreateProcessWithLogonW()
是通过用户凭证登录并创建具有对应用户Token的进程的便捷方式。
这两个函数都要通过 RPC 来调用位于 Svchost.exe 中的 SecLogon.dll 来实现实际的进程创建。SecLogon 调用位于内部的SLrCreatePrcoessWithLogonW()
函数,如果该过程顺利进行,最终会调用CreateProcssAsUser()
函数。
SecLogon 服务被默认配置为手动启动,所以当第一次调用 CreateProcessWithTokenW()
以及 CreateProcessWithLogonW()
时,该服务会被启动。如果该服务启动失败,则这两个函数也无法执行。常用的命令runas
,就调用了这两个函数。
用户态创建进程函数调用流程图:
二、进程创建过程
上面所描述的4个函数都只能对一个具有的PE文件结构的文件(不需要有.exe拓展)、批处理文件或者16位COM应用进行处理。
对于除了这三种文件外的其他类型的文件,上述的这些函数不知道如何根据文件拓展名来定位到可以执行的应用程序 ( 比如:不能根据.txt扩展定位到,可以启动 Notepad 来打开该txt文件。
但是Windows Shell 提供了这种定位功能,就是对于函数ShellExecute()
以及ShellExecuteEx()
来说,其可以接受一个非可执行文件作为输入,并且可以根据其拓展名以及位于HKEY_CLASSES_ROOT
中的设置来定位合适的可执行程序来打开这些文件。最后,这两个函数会通过具有合适的命令行参数的CreateProcess()
函数实现用户需要的意图。
CreateProcess()函数会首先找到我们指定的可执行文件映像,并创建一个内存区域,稍后会调用其他函数将该文件映像映射到进程的地址空间中。此外,当然,根据不同的文件映像名和参数,该函数还会启动其他程序来支持指定文件类型的运行,并重新执行CreateProcess()函数。
之后,如上图所述,CreateProcess()
函数会调用 CreateProcessInternel()
。该函数完成创建一个用户模式进程的实际工作。
-->CreateProcessInternel()首先会对传入的参数进行一些判断,并将参数或者结构体中的ANSI格式的字符串转化为Unicode字符串.
-->再做一些初始化和判断后,会调用NtOpenFile()得到映像文件句柄,并调用NtCreateSection()来得到内存区句柄,即我们所说的进程用户空间的虚拟地址空间会在这一步完成创建。
-->接着会调用BaseplsProcessAllowed()函数来判断应用程序是否在授权文件列表中,该函数通过调用 NtOpenKey()函数打开注册表的 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Option键
-->得到进程对应的内存区对象句柄后,调用NtQuerySection()函数,返回得到的节的基本信息(基址、大小、属性)
-->然后对映像文件信息的有效性进行判断,判断是否包含DEBUG_PROCESS或者DEBUG_ONLY_THIS_PROCESS,检查机器类型、子系统版本号、控制台、GUI等,并通过BaseIsImageVersionOk()判断镜像文件版本是否合法。
-->如果创建标志中包含DEBUG_PROCESS或者DEBUG_ONLY_THIS_PROCESS(即当前处于调试模式中),就加载advapi32.dl并获取CreateProcessAsSecure()函数的地址。
-->然后调用BaseFormatObjectAttrubutes()将安全属性结构(关于进程安全、权限方面的信息)格式为NT对象属性结构(得到了对象属性)。
-->接着调用_DbgUiConnectToDbg()实现通过调用NtCreateDebugObject()函数来创建调试对象,调用DbgUiGetThreadObject()来获得调试对象。
而最终,CreateProcessInternel()
会调用位于NTdll.dll中的NtCreateUserProcess()
来转换到位于内核模式的执行体模块中的同名函数中,完成进程创建过程内核部分的行为。
此外,内核中的 执行体模块还支持必须绕过Windows API的进程 的创建。例如:native processes,minimal processes,以及 Pico processes 。
像 Smss(the Session Manager) 就是一个 native process,其直接被内核创建,显然不会使用CreateProcess()
接口,而是直接调用进入执行体模块的NtCreateUserProcess()
函数中。同样的,当 Smss 创建 Autochk(检测磁盘组件) 或者 Csrss (Windows subsystem process) 时,Windows API 也是不可用的。此外,native processes 也不能作为 Windows应用程序 被创建,因为CreatePrcoessInternel()
会拒绝具有 native subsysem 映像类型的内存映像。而为了减轻上诉的复杂性Ntdll.dll中存在一个封装于NtCreateUserProcess()
的函数RtlCreateUserProcess()
。
对于一些内核模式的进程,比如 System process 以及 Memory Compression processes(minimal processes) 以及被 Windows Subsystem for Linux 管理的 Pico procsses 。这些进程的创建通过调用NtCreateProcessEx()
函数(仅仅能被内核态进程调用)实现。
对于被 Pico providers 管理的进程,在创建时,Pico providers会调用一个帮助函数 PspCreatePicoProcess()
,该函数不仅可以创建 minimal process,也会初始化 Pico provider上下文环境。该函数并未导出,仅仅能够被 Pico providers 通过其特殊接口调用。
最后,虽然NtCreateProcessEx()
以及函数NtCreateUserProcess()
是不同的系统调用,但这两个函数最后会调用相同的内部子例程[PspAllocateProcess()以及PspInsertProcess()
] 来实现创建进程的工作。并且,所有的你能举出来的创建进程的方式,从WMI Powershell cmdlet到 内核驱动,都会在这两个函数处终止。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。