Windows API 提供了多个建立进程的函数,最简单的是CreateProess()
,该函数建立一个和当前进程具备相同用户Token的进程。css
若是想要建立和当前进程具备不一样用户Token的进程,则须要使用CreateProcessAsUser()
,该函数能够接受一个Token句柄做为参数,并建立一个具备该Token的进程。html
其余建立进程的函数还包括CreateProcessWithTokenW()
以及CreateProcessWithLogonW()
(都是advapi.dll
的导出函数)。CreateProcessWithTokenW()
和函数CreateProcessAsUser()
很类似,可是二者对当前进程Token的权限要求不一样。CreateProcsssWithTokenW()
须要当前进程中的用户Token具备 SE_IMPERSONATE_NAME 权限,CreateProcessWithLogonW()
则须要当前进程中具备 SE_INCREASE_NOT_HELD 权限。shell
CreateProcessWithLogonW()
是经过用户凭证登陆并建立具备对应用户Token的进程的便捷方式。api
这两个函数都要经过 RPC 来调用位于 Svchost.exe 中的 SecLogon.dll 来实现实际的进程建立。SecLogon 调用位于内部的SLrCreatePrcoessWithLogonW()
函数,若是该过程顺利进行,最终会调用CreateProcssAsUser()
函数。安全
SecLogon 服务被默认配置为手动启动,因此当第一次调用 CreateProcessWithTokenW()
以及 CreateProcessWithLogonW()
时,该服务会被启动。若是该服务启动失败,则这两个函数也没法执行。经常使用的命令runas
,就调用了这两个函数。ide
用户态建立进程函数调用流程图:函数
上面所描述的4个函数都只能对一个具备的PE文件结构的文件(不须要有.exe拓展)、批处理文件或者16位COM应用进行处理。命令行
对于除了这三种文件外的其余类型的文件,上述的这些函数不知道如何根据文件拓展名来定位到能够执行的应用程序 ( 好比:不能根据.txt扩展定位到,能够启动 Notepad 来打开该txt文件。调试
可是Windows Shell 提供了这种定位功能,就是对于函数ShellExecute()
以及ShellExecuteEx()
来讲,其能够接受一个非可执行文件做为输入,而且能够根据其拓展名以及位于HKEY_CLASSES_ROOT
中的设置来定位合适的可执行程序来打开这些文件。最后,这两个函数会经过具备合适的命令行参数的CreateProcess()
函数实现用户须要的意图。code
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到 内核驱动,都会在这两个函数处终止。