0%
毅种循环

模块 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 来演示如何使用该函数创建一个最基础的进程。

实现 PPID 欺骗 + Block DLL#

本模块最精彩的部分在于,既然我们直接控制了 NtCreateUserProcess,我们就可以随意组合各种属性。

通过将 PS_ATTRIBUTE_LIST 的大小增加到包含 3 个属性,我们可以同时实现:

  1. PS_ATTRIBUTE_IMAGE_NAME: 设置目标为 RuntimeBroker.exe
  2. PS_ATTRIBUTE_PARENT_PROCESS: 设置父进程为 explorer.exe (PPID Spoofing)。
  3. 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),你可以构建出极难被检测的加载器。