64位CreateProcess逆向:(二)0环下参数的整合即创建进程的整体流程
转载:https://bbs.pediy.com/thread-207683.htm
点击下面进入总目录:
64位Windows创建64位进程逆向分析(总目录)
在上一篇文章中,我们介绍了CreateProcess在3环的整个流程。在其中特别提到,当操作系统由3环切换到0环,是由NtCreateUserProcess完成所有关键工作的。
在这篇文章中,我们将会介绍0环函数NtCreateUserProcess的整体流程。
准备工作
我们分析64位的Windows 7发现,其3环切换0环所用的特权指令是syscall(而不是sysenter),不过,他们两者的区别主要只在兼容模式是否有效上,与我们分析CreateProcess关系不大。不过,0环3环的切换,是既重要又基础的概念,对于还没概念的朋友,可以先查阅下Intel手册或者相关书籍。
NtCreateUserProcess是一个复杂的函数,如果想在成千上万行的汇编代码中不迷失自己的目标,就要把握好它的核心,在此,我们提前强调三个结构体:
_PPROCESS_CREATE_INFO是NtCreateUserProcess的带出参数,进程创建完成(或失败)后,它其中的内容就会填充相关信息,因此关注它,可以得到回溯到进程创建后的关键信息。
//CREATEPROCESSCONTEXT注释版
//CREATEPROCESSCONTEXT无注释版
简单说,这个结构体代表了整个新进程,因此与进程有关的几乎所有信息,都在其中,如文件映像信息、线程信息等。夸张一点讲,这个结构体贯穿了整个创建进程的过程,弄明白它,就弄明白了操作系统如何创建进程。
接下来,我们正式来看NtCreateUserProcess所做的工作。
NtCreateUserProcess
NtCreateUserProcess的函数原型如下:
从文末附上的整体流程图可以看出,NtCreateUserProcess内部的模块化是做的挺好的,由多个函数完成各自的任务。因此,我们除了梳理NtCreateUserProcess的流程外,就是逐个解剖NtCreateUserProcess所调用的函数。在这篇文章中,我们将详细介绍PspBuildCreateProcessContext、PspCaptureCreateInfo、PspCaptureProcessParameters三个功能函数。
NtCreateUserProcess流程
初始化
在一开始,NtCreateUserProcess会做一些初始化的工作,具体是:
将参数ThreadDesiredAccess、ProcessDesiredAccess、ThreadHandle、ProcessHandle、ProcessObjectAttributes、ThreadObjectAttributes、ProcessParameters保存到局部变量中。
将句柄变量var_Context(新线程环境)初始化为0.
检查参数ThreadHandle、ProcessHandle地址是否小于MmUserProbeAddress(是否小于系统地址),不是则将其设置为MmUserProbeAddress。
检查参数ProcessObjectAttributes地址是否对齐,不对齐则调用ExRaiseDatatypeMisalignment触发STATUS_DATATYPE_MISALIGNMENT异常。
检查参数ProcessObjectAttributes地址是否小于MmUserProbeAddress(是否小于系统地址),不是则将其设置为MmUserProbeAddress。
用参数ProcessObjectAttributes.Attributes初始化var_AccessState1.HandleAttributes,var_AccessState1后面将作为创建新进程句柄的参数。
以及:
初始化var_CreateProcessContext(见上文的结构体介绍)为0。
调用PspBuildCreateProcessContext解析参数AttributeList到var_CreateProcessContext中。(具体看之后的函数分析)
000000014031D8FB lea r9, [rsp+0B28h+var_CreateProcessContext] ; 参数pCreateProcessContext
调用PspCaptureCreateInfo解析参数CreateInfo到var_CreateProcessContext中。(具体看之后的函数分析)
接着:会根据CreateProcessContext.Flags的标志状态来选择父进程。在后续函数PspAllocateProcess中我们会看到,新进程在一定条件下,是可以获取父进程的section的,因此此处的判断很必要。
如果CreateProcessContext.Flags&1的结果为假,则调用ObReferenceObjectByHandle获取CreateProcessContext.hProcess进程对象。这样就保证了无论如何新进程是有父进程的。
创建文件映射对象
我们知道,代码最开始保存在PE文件中的,而最终执行前CPU是从内存中读取的,那么操作系统一定有一个步骤,实现了(磁盘上的)文件到内存中的加载。这一步就是在NtCreateUserProcess初始化完成后开始做的。
具体而言:
调用ZwOpenFile打开新进程可执行文件,并根据打开的文件句柄获取文件对象:
调用ZwCreateSection通过文件句柄创建文件映像:
此处使用的ZwOpenFile、与ZwCreateSection都是WDK文档中公开的函数,ZwOpenFile顾名思义就不用多说了,ZwCreateSection则类似于3环的CreateFileMapping,ZwCreateSection创建的Section Objects,既可用于进程间共享信息,又可用于文件映射,更具体的可以参考WDK文档。
不过,在ZwCreateSection存在以下调用关系:
而其中的MiVerifyImageHeader就是PE检查,对64位PE格式感兴趣的朋友,一定不能放过。我们将会在下一篇文章中,详细介绍MiVerifyImageHeader,并献上攻防实例。
将参数整合到CREATEPROCESSCONTEXT结构体中
如果文件映像对象Section不为空,则调用PspCaptureProcessParameters将参数
关于PspCaptureProcessParameters内部的具体分析见本贴最后部分。
接着,调用PspAllocateProcess创建进程:
然后调用PspCreateUserContext创建新进程主线程运行环境:
000000014031DDB6 mov dword ptr [rsp+0B28h+var_B08], ebx
000000014031DDBA mov r8, [rsp+0B28h+var_CreateProcessContext.SectionImageInfo.TransferAddress] ; 参数TransferAddress
000000014031DDC2 mov rdx, cs:PspUserThreadStart ; 参数StartAddress
000000014031DDC9 lea rcx, [rsp+0B28h+var_Context] ; 参数Context
000000014031DDD1 call PspCreateUserContext ;
000000014031DDD1 ; PspCreateUserContext(
000000014031DDD1 ; Context,
000000014031DDD1 ; StartAddress,
000000014031DDD1 ; TransferAddress,
000000014031DDD1 ; Peb);
另外,从我们的流程图很容易看出,若Section Object为空的话,也并不意味着创建进程失败,系统会PspGetContextThreadInternal获取当前线程环境(此系列的后续文章会剖析它),之后的流程与Section不为空类似,不再详述。
总之,当得到了线程Context之后,系统会调用PspAllocateThread创建、初始化线程:
新的进程及它的主线程就已经创建,只等ResumeThread(这个被放在3环),程序开始执行代码。
进程链表、线程链表的更新
在3环下编程,我们通常不大关心对方进程的信息,因为进程的内存是隔离的(想关心也关心不了)。但是,少数情况下,我们还是可以利用进程间通讯或者注入的手段,获取到对方进程中的信息。
比如简单的SendMessage就可以在进程间通讯。
那么深入一点点,就自然可以提出一个问题:既然进程间是隔离的,为什么SendMessage这样的API可以跨进程通讯呢?
深入一点点,可以自然得到一个答案的方向:说明Windows操作系统本身,记录了所有进程的信息,以及各个进程之间的关系,使得各进程在操作系统那个层次,被组织到了一起。
而这些进程被组织的具体细节,就藏在了NtCreateUserPorcess接下来调用的函数中:
操作系统用链表的结构保存所有进程的EPROCESS结构体。
NtCreateUserPorcess通过调用PspInsertProcess将新进程加入到那个全局链表中。
关于PspInsertProcess的具体剖析,此系列的后续文章会介绍。
在调用PspInsertProcess失败后会调用PspDoHandleSweepSingle。
又因为进程与线程是一对多关系,每一个进程也对应着一个链表,该链表中保存着这个进程的所有线程信息。
所以,NtCreateUserPorcess会调用PspInsertThread将新进程的主线程加入进程的线程链表中。
在调用PspInsertThread失败后会调用SeDeleteAccessState并接着调用PsTerminateProcess结束新进程。
交付APC
异步过程调用(APC)是Windows提出的一种调用机制。对于有些函数调用,可能耗时比较多,而我们又希望调用完后函数能够立即返回,那么就适合用APC。
APC的原理:对于需要使用APC的地方,一般,用户(程序员)会多传入一个函数指针,专有名词成为ApcRoutine,对于这样的调用,就是异步的了(即用户调用后立即返回),而当任务真正执行完毕,用户传入的ApcRoutine函数指针会被调用。类似我们传入了一个回调函数,响应任务完成的时机。
它在Windows中应用很多,比如写文件时系统调用NtWriteFile().
NtWriteFile声明如下
可以看到NtWriteFile有使用APC。
不过在此处,我们只要知道NtCreateUserPorcess会有APC检查和交付步骤就可以了,以后遇到会继续深入介绍:
KiCheckForKernelApcDelivery交付当前线程APC:
接着,就是一些检查、释放资源类的扫尾工作了
总之,若一切顺利,会调用PspUpdateCreateInfo将进程创建信息保存到传出参数CreateInfo中:
若不顺利,会调用PspDoHandleSweepSingle或者PsTerminateProcess结束新进程:
最后,会调用PspDeleteCreateProcessContext清理CreateProcessContext,释放资源。
PspBuildCreateProcessContext剖析
函数原型:
函数功能:将参数AttributeList中信息保存到CreateProcessContext中。参数AttributeList为变长数组_NT_PROC_THREAD_ATTRIBUTE_LIST类型,定义如下:
函数流程概要:
1、 循环从AttributeList中取出PROC_THREAD_ATTRIBUTE_ENTRY对象。
2、 根据PROC_THREAD_ATTRIBUTE_ENTRY对象的属性Attribute,判断大小Size是否正确,若正确则将值Value保存到CreateProcessContext相应的成员中。
函数流程图:
函数细节:
1、检查参数,主要检查AttributeList的长度和地址:
2、循环取出数组元素:
3、循环中间判断Entry的属性Attribute:
4、根据属性Attribute检查值Value大小是否正确:
5、将Value保存到CreateProcessContext相应的成员中, ProcessCreateContext中的各成员的内容,由AttributeList中的Attribute的值决定,已经分析出的对应关系如下:
PspCaptureCreateInfo剖析
函数原型:
函数功能:将CreateInfo的0x11(Flags2)偏移处进行运算后赋值给pCreateProcessContext的成员Flag2,将CreateInfo的0x13偏移处(ImageCharacteristics)赋值给pCreateProcessContext的成员ImageCharacteristics,将第二个参数CreateInfo的地址给pCreateProcessContext的成员pCreateInfo。
函数流程图:
关键代码实现:
PspCaptureProcessParameters剖析
函数原型:
函数功能:
检查ProcessParameters参数中的unicode字符串是否为有效的,并且申请新的内存将字符串复制到申请的空间中,把新申请的内存地址保存在pCreateProcessContext->pProcessParamers中后返回。
主要流程:
判断accessMode是userMode还是kernelMode
若为kernelMode则设置Flags2为0FBh,且pCreateProcessContext->pProcessParamers = ProcessParameters 赋值完成后直接返回,函数结束。
若为userMode则先检查ProcessParameters参数中的unicode字符串是否有效,之后计算空间大小,申请新内存,将参数的unicode保存到新内存中,最后将内存地址赋值给pCreateProcessContext->pProcessParamers,函数结束。
流程图:
细节:
1、 判断accessMode是userMode还是kernelMode。
2、 若是kernelMode跳走,并且直接给pCreateProcessContext->Flags2和pCreateProcessContext->pProcessParameters赋值,完成后函数退出
3、 若是UserMode 则先用PspCaptureAndValidateUnicodeString检测字符串有效性并获取字符串的unicode_string结构,保存在局部变量中
4、 之后计算空间,调用ExAllocatePoolWithQuotaTag申请内存
5、 通过PspCopyUnicodeString拷贝参数里的字符串到申请的空间中
6、最后设置Flages2和pProcessParameters并退出
64位Windows创建64位进程逆向分析(总目录)
在上一篇文章中,我们介绍了CreateProcess在3环的整个流程。在其中特别提到,当操作系统由3环切换到0环,是由NtCreateUserProcess完成所有关键工作的。
在这篇文章中,我们将会介绍0环函数NtCreateUserProcess的整体流程。
准备工作
我们分析64位的Windows 7发现,其3环切换0环所用的特权指令是syscall(而不是sysenter),不过,他们两者的区别主要只在兼容模式是否有效上,与我们分析CreateProcess关系不大。不过,0环3环的切换,是既重要又基础的概念,对于还没概念的朋友,可以先查阅下Intel手册或者相关书籍。
NtCreateUserProcess是一个复杂的函数,如果想在成千上万行的汇编代码中不迷失自己的目标,就要把握好它的核心,在此,我们提前强调三个结构体:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
struct _PPROCESS_CREATE_INFO { QWORD cb; // 结构体大小 QWORD UnKown; DWORD Flags2; BYTE UnKown; WORD ImageCharacteristics; DWORD DesiredAccess; //3 环下会赋值,0环下赋值给CREATEPROCESSCONTEXT的成员DesiredAccess QWORD UnKown; QWORD UnKown; QWORD UnKown; DWORD UnKown; BYTE UnKown; DWORD Flags; PVOID* CurrentPeb; PVOID* ParentProcessPeb; QWORD UnKown; DWORD UnKown; BYTE UnKown; }; |
_PPROCESS_CREATE_INFO是NtCreateUserProcess的带出参数,进程创建完成(或失败)后,它其中的内容就会填充相关信息,因此关注它,可以得到回溯到进程创建后的关键信息。
//CREATEPROCESSCONTEXT注释版
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
struct CREATEPROCESSCONTEXT { /* 标志位 主要在PspAllocateProcess中使用 */ DWORD Flags; /* 标志位: */ BYTE Flags2; BYTE UnKown; /* 镜像特征,由SECTION_IMAGE_INFORMATION结构ImageCharacteristics成员而来 */ WORD ImageCharacteristics; /* 指向CLIENT_ID结构体的指针,CLIENT_ID定义如下: typedef struct _CLIENT_ID { HANDLE PID; HANDLE TID; } CLIENT_ID, *PCLIENT_ID; */ CLIENT_ID* pClient_ID; /* 指向TEB结构体的指针 */ TEB* pTeb; /* 指向_SECTION_IMAGE_INFORMATION结构体的指针,该成员由最后一个参数传入,其属性值为6. */ SECTION_IMAGE_INFORMATION *pSectionImageInfo; /* 指向CREATEINFO结构体的指针,该结构体由PspCaptureCreateInfo函数负责初始化. */ CREATEINFO *pCreateInfo; /* SECTION_IMAGE_INFORMATION结构体,这是即将创建进程的可执行文件映射到内存后的内存对象. */ SECTION_IMAGE_INFORMATION SectionImageInfo; /* 父进程句柄,由最后一个参数传入,其属性值为0x60000. */ HANDLE hParentProcess; /* 新进程的EPROCESS指针. */ EPROCESS* pEprocess; /* 调试对象的句柄,如果调用CreateProcess时创建选项中带有调试标志,则由最后一个参数会传入调试对象的句柄,将会保持到这个成员里面来. */ HANDLE DebugObjectHandle; /* 令牌对象句柄,由最后一个参数会传入,属性值0x60002. */ HANDLE hSeTokenObject; DWORD DesiredAccess; /* 文件句柄,新进程可执行文件的文件句柄. */ HANDLE FileHandle; /* 文件对象,新进程可执行文件的文件对象 */ FILE_OBJECT* pFileObject; /* 新进程加载到内存后的内存对象. */ HANDLE SectionHandle; HANDLE KeyHandle; SECTION_OBJECT* pSectionObject; /* _RTL_USER_PROCESS_PARAMETERS结构体指针. */ PVOID pRtlUserProcessParameter; PVOID pBackupRtlUserProcessParameter; DWORD UnKown; /* PspWow64SetupUserProcessAddressSpace函数内赋值,具体含义不太清楚 */ DWORD UnKown; /* 新进程可执行文件全路径 */ UNICODE_STRING FileName; /* 优先级 */ BYTE PriorityClass; /* 该成员会负责给新进程EPROCESS.Pcb.Flags. */ BYTE EprocessFlags; /* 作为查询全局表KeNodeBlock的下标 */ WORD KnodeIndex; /* 全局表KiProcessorNumberToIndexMappingTable的表项 */ DWORD Mapping; QWORD UnKown; QWORD UnKown; QWORD UnKown; QWORD UnKown; DWORD UnKown; DWORD UnKown; QWORD UnKown; QWORD UnKown; DWORD UnKown; /* 由最后一个参数传入,其属性值为0x20009. */ DWORD DefaultHardErrorProcessing; QWORD UnKown; /* 和全局变量KiActiveGroups作比较 */ WORD ActiveGroupCount; WORD UnKown; WORD UnKown; WORD UnKown; QWORD UnKown; QWORD UnKown; QWORD UnKown; }; |
//CREATEPROCESSCONTEXT无注释版
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
struct CREATEPROCESSCONTEXT { DWORD Flags; BYTE Flags2; BYTE UnKown; WORD ImageCharacteristics; CLIENT_ID* pClient_ID; TEB* pTeb; SECTION_IMAGE_INFORMATION *pSectionImageInfo; CREATEINFO *pCreateInfo; SECTION_IMAGE_INFORMATION SectionImageInfo; HANDLE hParentProcess; EPROCESS* pEprocess; HANDLE DebugObjectHandle; HANDLE hSeTokenObject; DWORD DesiredAccess; HANDLE FileHandle; FILE_OBJECT* pFileObject; HANDLE SectionHandle; HANDLE KeyHandle; SECTION_OBJECT* pSectionObject; PVOID pRtlUserProcessParameter; PVOID pBackupRtlUserProcessParameter; DWORD UnKown; DWORD UnKown; UNICODE_STRING FileName; BYTE PriorityClass; BYTE EprocessFlags; WORD KnodeIndex; DWORD Mapping; QWORD UnKown; QWORD UnKown; QWORD UnKown; QWORD UnKown; DWORD UnKown; DWORD UnKown; QWORD UnKown; QWORD UnKown; DWORD UnKown; DWORD DefaultHardErrorProcessing; QWORD UnKown; WORD ActiveGroupCount; WORD UnKown; WORD UnKown; WORD UnKown; QWORD UnKown; QWORD UnKown; QWORD UnKown; }; |
简单说,这个结构体代表了整个新进程,因此与进程有关的几乎所有信息,都在其中,如文件映像信息、线程信息等。夸张一点讲,这个结构体贯穿了整个创建进程的过程,弄明白它,就弄明白了操作系统如何创建进程。
1
2
3
4
5
6
7
8
|
struct ALL_ACCESS_STATE // 为结构体ACCESS_STATE的扩展版本 { ACCESS_STATE AssedAccessState; // 具体可查看MSDN BYTE AuxData[216]; DWORD HandleAttributes; DWORD AcessMode; QWORD hProcess; }; |
接下来,我们正式来看NtCreateUserProcess所做的工作。
NtCreateUserProcess
NtCreateUserProcess的函数原型如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
void NtCreateUserProcess( // 传出新进程句柄 OUT PHANDLE ProcessHandle, // 传出新进程主线程句柄 OUT PHANDLE ThreadHandle, // 当前进程对新进程操作权限描述, 一般是MAXIMUM_ALLOWED, 无限制 IN ACCESS_MASK ProcessDesiredAccess, // 当前进程对新线程的操作权限描述, 一般是 MAXIMUM_ALLOWED IN ACCESS_MASK ThreadDesiredAccess, // // 进程对象属性, 可空 IN POBJECT_ATTRIBUTES ProcessObjectAttributes OPTIONAL, // 线程对象属性, 可为空 IN POBJECT_ATTRIBUTES ThreadObjectAttributes OPTIONAL, // 新进程创建标志 IN ULONG CreateProcessFlags, // 新线程创建标志 IN ULONG CreateThreadFlags, // 进程创建的相关参数信息,包括待启动进程的映像路径,命令参数,环境 // 行变量串等信息 IN PRTL_USER_PROCESS_PARAMETERS ProcessParameters, // 传出一些基本信息, 比如新进程的PEB OUT PPROCESS_CREATE_INFO CreateInfo, // 传入参数, 保存了一些信息. 比如程序路径 父进程PID等 IN PNT_PROC_THREAD_ATTRIBUTE_LIST AttributeList); |
从文末附上的整体流程图可以看出,NtCreateUserProcess内部的模块化是做的挺好的,由多个函数完成各自的任务。因此,我们除了梳理NtCreateUserProcess的流程外,就是逐个解剖NtCreateUserProcess所调用的函数。在这篇文章中,我们将详细介绍PspBuildCreateProcessContext、PspCaptureCreateInfo、PspCaptureProcessParameters三个功能函数。
NtCreateUserProcess流程
初始化
在一开始,NtCreateUserProcess会做一些初始化的工作,具体是:
将参数ThreadDesiredAccess、ProcessDesiredAccess、ThreadHandle、ProcessHandle、ProcessObjectAttributes、ThreadObjectAttributes、ProcessParameters保存到局部变量中。
将句柄变量var_Context(新线程环境)初始化为0.
检查参数ThreadHandle、ProcessHandle地址是否小于MmUserProbeAddress(是否小于系统地址),不是则将其设置为MmUserProbeAddress。
检查参数ProcessObjectAttributes地址是否对齐,不对齐则调用ExRaiseDatatypeMisalignment触发STATUS_DATATYPE_MISALIGNMENT异常。
检查参数ProcessObjectAttributes地址是否小于MmUserProbeAddress(是否小于系统地址),不是则将其设置为MmUserProbeAddress。
用参数ProcessObjectAttributes.Attributes初始化var_AccessState1.HandleAttributes,var_AccessState1后面将作为创建新进程句柄的参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
000000014031D745 mov [rsp+0B28h+var_ThreadDesiredAccess], r9d ; ThreadDesiredAccess 000000014031D74A mov [rsp+0B28h+var_ProcessDesiredAccess], r8d ; ProcessDesiredAccess 000000014031D74F mov [rsp+0B28h+var_ThreadHandle], rdx ; ThreadHandle 000000014031D757 mov [rsp+0B28h+var_ProcessHandle], rcx ; ProcessHandle 000000014031D75F mov rsi, [rsp+0B28h+ProcessObjectAttributes] ; ProcessObjectAttributes 000000014031D767 mov [rsp+0B28h+var_ProcessObjectAttributes], rsi 000000014031D76F mov rax, [rsp+0B28h+ThreadObjectAttributes] ; ThreadObjectAttributes 000000014031D777 mov [rsp+0B28h+var_ThreadObjectAttributes], rax ; ThreadFlags 000000014031D77F mov rax, [rsp+0B28h+ProcessParameters] ; ProcessParameters 000000014031D787 mov [rsp+0B28h+var_ProcessParameters], rax 000000014031D78F mov r12, [rsp+0B28h+CreateInfo] ; CreateInfo 000000014031D797 mov rdi, [rsp+0B28h+AttributeList] ; AttributeList 000000014031D79F xor ebx, ebx 000000014031D7A1 mov [rsp+130h], rbx 000000014031D7A9 xor edx, edx ; Val 000000014031D7AB lea r8d, [rbx+38h] ; Size 000000014031D7AF lea rcx, [rsp+0B28h+Dst] ; Dst 000000014031D7B7 call memset 000000014031D7BC mov [rsp+0B28h+var_Context.P1Home], rbx 000000014031D7C4 xor edx, edx ; Val 000000014031D7C6 mov r8d, 4C8h ; Size 000000014031D7CC lea rcx, [rsp+0B28h+var_Context.P2Home] ; Dst 000000014031D7D4 call memset ; memset(&Context.P2Home,0,sizeof(Context) - 4); 000000014031D7D9 mov r14, gs:188h 000000014031D7E2 mov [rsp+0B28h+var_Ethread], r14 000000014031D7EA mov r15, [r14+_ETHREAD.Tcb.ApcState.ApcState.Process] 000000014031D7EE mov [rsp+0B28h+var_TempProcess], r15 000000014031D7F3 mov r13b, [r14+_ETHREAD.Tcb.PreviousMode] 000000014031D7FA mov [rsp+0B28h+var_PreviousMode], r13b 000000014031D7FF mov edx, dword ptr [rsp+0B28h+CreateProcessFlags] ; ProcessFlags |
以及:
初始化var_CreateProcessContext(见上文的结构体介绍)为0。
1
2
3
4
|
000000014031D8E1 xor edx, edx ; Val 000000014031D8E3 mov r8d, 150h ; Size 000000014031D8E9 lea rcx, [rsp+0B28h+var_CreateProcessContext] ; Dst 000000014031D8F1 call memset ; memset(&CreateProcessContext,0,sizeof(CreateProcessContext)); |
调用PspBuildCreateProcessContext解析参数AttributeList到var_CreateProcessContext中。(具体看之后的函数分析)
000000014031D8FB lea r9, [rsp+0B28h+var_CreateProcessContext] ; 参数pCreateProcessContext
1
2
3
4
5
6
7
8
9
|
000000014031D903 xor r8d, r8d ; 参数Unkown=0 000000014031D906 mov dl, r13b ; 参数PreviousMode 000000014031D909 mov rcx, rdi ; 参数AttributeList 000000014031D90C call PspBuildCreateProcessContext ; 000000014031D90C ; PspBuildCreateProcessContext( 000000014031D90C ; AttributeList, 000000014031D90C ; PreviousMode, 000000014031D90C ; Unkown=0, 000000014031D90C ; pCreateProcessContext); |
调用PspCaptureCreateInfo解析参数CreateInfo到var_CreateProcessContext中。(具体看之后的函数分析)
1
2
3
4
5
6
7
8
|
000000014031D938 lea r8, [rsp+0B28h+var_CreateProcessContext] ; 参数pCreateProcessContext 000000014031D940 mov rdx, r12 ; 参数pCreateInfo 000000014031D943 mov cl, r13b ; 参数AccessMode 000000014031D946 call PspCaptureCreateInfo ; 000000014031D946 ; PspCaptureCreateInfo( 000000014031D946 ; AccessMode, 000000014031D946 ; pCreateInfo, 000000014031D946 ; CreateProcessContext); |
接着:会根据CreateProcessContext.Flags的标志状态来选择父进程。在后续函数PspAllocateProcess中我们会看到,新进程在一定条件下,是可以获取父进程的section的,因此此处的判断很必要。
如果CreateProcessContext.Flags&1的结果为假,则调用ObReferenceObjectByHandle获取CreateProcessContext.hProcess进程对象。这样就保证了无论如何新进程是有父进程的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
000000014031D955 mov ecx, [rsp+0B28h+var_CreateProcessContext.Flags] 000000014031D95C mov r12d, 1 000000014031D962 test r12b, cl 000000014031D965 jz short loc_14031D9C8 ; if (CreateProcessContext.Flags&1) 000000014031D965 ; ParentEProcess=CurrentProcess 000000014031D965 ; 如果CreateProcessContext.Flags&1为真,则父进程为当前进程 000000014031D965 ; 否则父进程为参数AttributeList中指定的进程 000000014031D967 mov [rsp+28h], rbx ; 参数HandleInformation 000000014031D96C lea rax, [rsp+0B28h+var_Eprocess] 000000014031D974 mov [rsp+20h], rax ; 参数pEprocess 000000014031D979 mov r9b, r13b ; 参数AccessMode 000000014031D97C mov r8, cs:PsProcessType ; 参数ObjectType 000000014031D983 lea edx, [r12+7Fh] ; 参数DesiredAccess 000000014031D988 mov rcx, [rsp+0B28h+var_CreateProcessContext.hParentProcess] ; Handle 000000014031D990 call ObReferenceObjectByHandle ; 000000014031D990 ; ObReferenceObjectByHandle( 000000014031D990 ; CreateProcessContext.hParentProcess, 000000014031D990 ; DesiredAccess, 000000014031D990 ; PsProcessType, 000000014031D990 ; AccessMode, 000000014031D990 ; pEprocess, 000000014031D990 ; HandleInformation); |
创建文件映射对象
我们知道,代码最开始保存在PE文件中的,而最终执行前CPU是从内存中读取的,那么操作系统一定有一个步骤,实现了(磁盘上的)文件到内存中的加载。这一步就是在NtCreateUserProcess初始化完成后开始做的。
具体而言:
调用ZwOpenFile打开新进程可执行文件,并根据打开的文件句柄获取文件对象:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
000000014031DA18 mov [rsp+0B28h+var_ObjectAttributes.Length], 30h 000000014031DA23 mov [rsp+0B28h+var_ObjectAttributes.RootDirectory], rbx 000000014031DA2B or eax, 240h 000000014031DA30 mov [rsp+0B28h+var_ObjectAttributes.Attributes], eax 000000014031DA37 lea rax, [rsp+0B28h+var_CreateProcessContext.FileName] 000000014031DA3F mov [rsp+0B28h+var_ObjectAttributes.ObjectName], rax 000000014031DA47 mov [rsp+0B28h+var_ObjectAttributes.SecurityDescriptor], rbx 000000014031DA4F mov [rsp+0B28h+var_ObjectAttributes.SecurityQualityOfService], rbx 000000014031DA57 mov edx, [rsp+0B28h+var_CreateProcessContext.DesiredAccess] 000000014031DA5E or edx, 100020h ; 参数DesiredAccess 000000014031DA64 mov dword ptr [rsp+28h], 60h ; 参数OpenOptions 000000014031DA6C mov dword ptr [rsp+20h], 5 ; 参数ShareAccess 000000014031DA74 lea r9, [rsp+0B28h+var_IoStatusBlock] ; 参数IoStatusBlock 000000014031DA7C lea r8, [rsp+0B28h+var_ObjectAttributes] ; 参数ObjectAttributes 000000014031DA84 lea rcx, [rsp+0B28h+var_CreateProcessContext.FileHandle] ; 参数FileHandle 000000014031DA8C call ZwOpenFile ; ZwOpenFile(CreateProcessContext.FileHandle, 000000014031DA8C ; DesiredAccess, 000000014031DA8C ; ObjectAttributes, 000000014031DA8C ; IoStatusBlock, 000000014031DA8C ; ShareAccess, 000000014031DA8C ; OpenOptions); 000000014031DA91 mov edi, eax 000000014031DA93 cmp eax, ebx 000000014031DA95 jge short loc_14031DAD4 000000014031DA97 cmp [rsp+0B28h+var_CreateProcessContext.DesiredAccess], ebx 000000014031DA9E jz short loc_14031DAD4 000000014031DAA0 mov dword ptr [rsp+28h], 60h ; 参数OpenOptions 000000014031DAA8 mov dword ptr [rsp+20h], 5 ; 参数ShareAccess 000000014031DAB0 lea r9, [rsp+0B28h+var_IoStatusBlock] ; 参数IoStatusBlock 000000014031DAB8 lea r8, [rsp+0B28h+var_ObjectAttributes] ; 参数ObjectAttributes 000000014031DAC0 mov edx, 100020h ; 参数DesiredAccess 000000014031DAC5 lea rcx, [rsp+0B28h+var_CreateProcessContext.FileHandle] ; 参数FileHandle 000000014031DACD call ZwOpenFile ; ZwOpenFile(CreateProcessContext.FileHandle, 000000014031DACD ; DesiredAccess, 000000014031DACD ; ObjectAttributes, 000000014031DACD ; IoStatusBlock, 000000014031DACD ; ShareAccess, 000000014031DACD ; OpenOptions); 000000014031DAD2 mov edi, eax 000000014031DAD4 000000014031DAD4 loc_14031DAD4: ; CODE XREF: NtCreateUserProcess+375j 000000014031DAD4 ; NtCreateUserProcess+37Ej 000000014031DAD4 cmp edi, ebx 000000014031DAD6 jge short loc_14031DAF8 000000014031DAD8 mov [rsp+0B28h+var_CreateProcessContext.FileHandle], rbx 000000014031DAE0 xor r8d, r8d 000000014031DAE3 lea rdx, [rsp+0B28h+var_CreateProcessContext] 000000014031DAEB mov ecx, r12d 000000014031DAEE call PspUpdateCreateInfo 000000014031DAF3 jmp loc_14031E1D8 ; 打开文件失败 000000014031DAF8 ; --------------------------------------------------------------------------- 000000014031DAF8 000000014031DAF8 loc_14031DAF8: ; CODE XREF: NtCreateUserProcess+3B6j 000000014031DAF8 mov [rsp+0B28h+var_B00], rbx ; 参数HandleInformation 000000014031DAFD lea rax, [rsp+0B28h+var_File] 000000014031DB05 mov [rsp+20h], rax ; 参数pFile 000000014031DB0A xor r9d, r9d ; 参数AccessMode 000000014031DB0D mov r8, cs:IoFileObjectType ; 参数ObjectType 000000014031DB14 mov edx, 100020h ; 参数DesiredAccess 000000014031DB19 mov rcx, [rsp+0B28h+var_CreateProcessContext.FileHandle] ; 参数Handle 000000014031DB21 call ObReferenceObjectByHandle ; 000000014031DB21 ; ObReferenceObjectByHandle( 000000014031DB21 ; CreateProcessContext.FileHandle, 000000014031DB21 ; DesiredAccess, 000000014031DB21 ; IoFileObjectType, 000000014031DB21 ; AccessMode, 000000014031DB21 ; pFile, 000000014031DB21 ; HandleInformation); 000000014031DB26 mov edi, eax 000000014031DB28 mov rax, [rsp+0B28h+var_File] 000000014031DB30 mov [rsp+0B28h+var_CreateProcessContext.FileObject], rax 000000014031DB38 cmp edi, ebx 000000014031DB3A jge short loc_14031DB49 000000014031DB3C mov [rsp+0B28h+var_CreateProcessContext.FileObject], rbx 000000014031DB44 jmp loc_14031E1D8 ; 获取文件对象失败 |
调用ZwCreateSection通过文件句柄创建文件映像:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
000000014031DB60 mov rax, [rsp+0B28h+var_CreateProcessContext.FileHandle] 000000014031DB68 mov [rsp+30h], rax ; 参数FileHandle 000000014031DB6D mov [rsp+28h], ecx ; 参数AllocationAttributes 000000014031DB71 mov dword ptr [rsp+20h], 10h ; 参数SectionPageProtection 000000014031DB79 xor r9d, r9d ; 参数MaximumSize 000000014031DB7C lea r8, [rsp+0B28h+var_ObjectAttributes] ; 参数ObjectAttributes 000000014031DB84 mov edx, 0F001Fh ; 参数DesiredAccess 000000014031DB89 lea rcx, [rsp+0B28h+var_CreateProcessContext.SectionHandle] ; 参数SectionHandle 000000014031DB91 call ZwCreateSection ; ZwCreateSection(SectionHandle, 000000014031DB91 ; DesiredAccess, 000000014031DB91 ; ObjectAttributes, 000000014031DB91 ; MaximumSize, 000000014031DB91 ; SectionPageProtection, 000000014031DB91 ; AllocationAttributes, 000000014031DB91 ; CreateProcessContext.FileHandle); 000000014031DBBD mov [rsp+28h], rbx ; 参数HandleInformation 000000014031DBC2 lea rax, [rsp+0B28h+var_SectionObject] 000000014031DBCA mov [rsp+20h], rax ; 参数SectionObject 000000014031DBCF xor r9d, r9d ; 参数AccessMode 000000014031DBD2 mov r8, cs:MmSectionObjectType ; 参数ObjectType 000000014031DBD9 lea edx, [r9+8] ; 参数DesiredAccess 000000014031DBDD mov rcx, [rsp+0B28h+var_CreateProcessContext.SectionHandle] ; 参数Handle 000000014031DBE5 call ObReferenceObjectByHandle ; 000000014031DBE5 ; ObReferenceObjectByHandle( 000000014031DBE5 ; CreateProcessContext.SectionHandle, 000000014031DBE5 ; DesiredAccess, 000000014031DBE5 ; MmSectionObjectType, 000000014031DBE5 ; AccessMode, 000000014031DBE5 ; pSectionObject, 000000014031DBE5 ; HandleInformation); 000000014031DBEA mov edi, eax 000000014031DBEC mov rax, [rsp+0B28h+var_SectionObject] 000000014031DBF4 mov [rsp+0B28h+var_CreateProcessContext.SectionObject], rax 000000014031DBFC cmp edi, ebx 000000014031DBFE jge short loc_14031DC0D 000000014031DC00 mov [rsp+0B28h+var_CreateProcessContext.SectionObject], rbx 000000014031DC08 jmp loc_14031E1D8 ; 获取进程文件映像对象失败 |
此处使用的ZwOpenFile、与ZwCreateSection都是WDK文档中公开的函数,ZwOpenFile顾名思义就不用多说了,ZwCreateSection则类似于3环的CreateFileMapping,ZwCreateSection创建的Section Objects,既可用于进程间共享信息,又可用于文件映射,更具体的可以参考WDK文档。
不过,在ZwCreateSection存在以下调用关系:
而其中的MiVerifyImageHeader就是PE检查,对64位PE格式感兴趣的朋友,一定不能放过。我们将会在下一篇文章中,详细介绍MiVerifyImageHeader,并献上攻防实例。
将参数整合到CREATEPROCESSCONTEXT结构体中
如果文件映像对象Section不为空,则调用PspCaptureProcessParameters将参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
ProcessParameters中的信息保存到CreateProcessContext中: 000000014031DC25 cmp rax, rbx 000000014031DC28 jz short loc_14031DC7E ; if (CreateProcessContext.SectionObject==NULL) 000000014031DC2A bt [r15+_EPROCESS.Flags2], 0Bh 000000014031DC33 jb short loc_14031DC46 ; 参数pCreateProcessContext 000000014031DC35 cmp esi, ebx 000000014031DC37 jz short loc_14031DC46 ; 参数pCreateProcessContext 000000014031DC39 cmp r13b, bl 000000014031DC3C jz short loc_14031DC46 ; 参数pCreateProcessContext 000000014031DC3E or [rsp+0B28h+var_CreateProcessContext.Flags2], 10h 000000014031DC46 000000014031DC46 loc_14031DC46: ; CODE XREF: NtCreateUserProcess+513j 000000014031DC46 ; NtCreateUserProcess+517j 000000014031DC46 ; NtCreateUserProcess+51Cj 000000014031DC46 lea r8, [rsp+0B28h+var_CreateProcessContext] ; 参数pCreateProcessContext 000000014031DC4E mov rdx, [rsp+0B28h+var_ProcessParameters] ; 参数ProcessParameters 000000014031DC56 mov cl, r13b ; 参数PreviousMode 000000014031DC59 call PspCaptureProcessParameters ; // 初始化pRtlUserProcessParameter 000000014031DC59 ; PspCaptureProcessParameters( 000000014031DC59 ; PreviousMode, 000000014031DC59 ; ProcessParameters, 000000014031DC59 ; pCreateProcessContext); 000000014031DC5E mov edi, eax 000000014031DC60 cmp eax, ebx 000000014031DC62 jge short loc_14031DC71 000000014031DC64 and [rsp+0B28h+var_CreateProcessContext.Flags2], 0FBh 000000014031DC6C jmp loc_14031E1D8 ; PspCaptureProcessParameters执行失败 |
关于PspCaptureProcessParameters内部的具体分析见本贴最后部分。
接着,调用PspAllocateProcess创建进程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
000000014031DD27 lea rax, [rsp+0B28h+var_pTempNewEprocess] ; 参数pNewProcess 000000014031DD2C mov [rsp+40h], rax 000000014031DD31 lea rax, [rsp+0B28h+var_AA0] ; 参数Unkown 000000014031DD39 mov [rsp+38h], rax 000000014031DD3E lea rax, [rsp+0B28h+var_CreateProcessContext] ; 参数CreateProcessContext 000000014031DD46 mov [rsp+30h], rax 000000014031DD4B mov eax, dword ptr [rsp+0B28h+CreateProcessFlags] ; 参数ProcessFlags 000000014031DD52 mov [rsp+28h], eax 000000014031DD56 mov rax, [rsp+0B28h+var_CreateProcessContext.hSeTokenObject] ; 参数hSeTokenObject 000000014031DD5E mov [rsp+20h], rax 000000014031DD63 mov r9, [rsp+0B28h+var_CreateProcessContext.SectionObject] ; 参数SectionObject 000000014031DD6B mov r8, [rsp+0B28h+var_ProcessObjectAttributes] ; 参数ProcessObjectAttributes 000000014031DD73 mov dl, r13b ; 参数PreviousMode 000000014031DD76 mov rcx, qword ptr [rsp+0B28h+var_pProcess] ; 参数ParentEProcess 000000014031DD7E call PspAllocateProcess ; 000000014031DD7E ; PspAllocateProcess( 000000014031DD7E ; ParentEProcess, 000000014031DD7E ; PreviousMode, 000000014031DD7E ; ProcessObjectAttributes, 000000014031DD7E ; SectionObject, 000000014031DD7E ; hSeTokenObject, 000000014031DD7E ; ProcessFlags, 000000014031DD7E ; CreateProcessContext, 000000014031DD7E ; Unkown, 000000014031DD7E ; pNewProcess); |
然后调用PspCreateUserContext创建新进程主线程运行环境:
000000014031DDB6 mov dword ptr [rsp+0B28h+var_B08], ebx
000000014031DDBA mov r8, [rsp+0B28h+var_CreateProcessContext.SectionImageInfo.TransferAddress] ; 参数TransferAddress
000000014031DDC2 mov rdx, cs:PspUserThreadStart ; 参数StartAddress
000000014031DDC9 lea rcx, [rsp+0B28h+var_Context] ; 参数Context
000000014031DDD1 call PspCreateUserContext ;
000000014031DDD1 ; PspCreateUserContext(
000000014031DDD1 ; Context,
000000014031DDD1 ; StartAddress,
000000014031DDD1 ; TransferAddress,
000000014031DDD1 ; Peb);
另外,从我们的流程图很容易看出,若Section Object为空的话,也并不意味着创建进程失败,系统会PspGetContextThreadInternal获取当前线程环境(此系列的后续文章会剖析它),之后的流程与Section不为空类似,不再详述。
总之,当得到了线程Context之后,系统会调用PspAllocateThread创建、初始化线程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
000000014031DCE8 mov [rsp+0B28h+var_Context.ContextFlags], 10001Bh 000000014031DCF3 mov [rsp+20h], r12b ; 参数dwOne=1 000000014031DCF8 mov r9b, r12b ; 参数isSystemThread=1 000000014031DCFB xor r8d, r8d ; 参数AccessMode=0 000000014031DCFE lea rdx, [rsp+0B28h+var_Context] ; 参数pContext 000000014031DD06 mov rcx, r14 ; 参数Ethread 000000014031DD09 call PspGetContextThreadInternal ; 000000014031DD09 ; PspGetContextThreadInternal( 000000014031DD09 ; Ethread, 000000014031DD09 ; pContext, 000000014031DD09 ; AccessMode, 000000014031DD09 ; isSystemThread, 000000014031DD09 ; dwOne); 000000014031DE77 mov [rsp+0B28h+var_AccessStateExpand], eax 000000014031DE7B lea rax, [rsp+0B28h+var_AccessState2] 000000014031DE83 mov [rsp+58h], rax ; 参数pNewAccessState 000000014031DE88 mov [rsp+50h], r14 ; 参数unknow 000000014031DE8D lea rax, [rsp+0B28h+var_pThread] 000000014031DE95 mov [rsp+48h], rax ; 参数pptrEthread 000000014031DE9A lea rax, [rsp+6Ch] ; 参数pProcessFlag 000000014031DE9F mov [rsp+40h], rax ; __int64 000000014031DEA4 mov [rsp+38h], rbx ; 参数StartContext 000000014031DEA9 mov [rsp+30h], rbx ; 参数StartRoutine 000000014031DEAE lea rax, [rsp+0B28h+var_Inital_teb] ; 参数pInitTeb 000000014031DEB6 mov [rsp+28h], rax ; __int64 000000014031DEBB lea rax, [rsp+0B28h+var_Context] 000000014031DEC3 mov [rsp+20h], rax ; 参数Context 000000014031DEC8 lea r9, [rsp+0B28h+var_CreateProcessContext] ; __int64 000000014031DED0 mov r8b, r13b ; 参数AccessMode 000000014031DED3 mov rdx, [rsp+0B28h+var_ThreadObjectAttributes] ; __int64 000000014031DEDB mov rcx, rsi ; 参数newProcess 000000014031DEDE call PspAllocateThread ; 000000014031DEDE ; PspAllocateThread( 000000014031DEDE ; newProcess, 000000014031DEDE ; ObjectAttributes, 000000014031DEDE ; AccessMode, 000000014031DEDE ; CreateProcessContext, 000000014031DEDE ; context, 000000014031DEDE ; pInitTeb, 000000014031DEDE ; StartRoutine, 000000014031DEDE ; StartContext, 000000014031DEDE ; ptrProcessFlag, 000000014031DEDE ; pptrEthread, 000000014031DEDE ; mydiy, 000000014031DEDE ; pNewAccessState); |
新的进程及它的主线程就已经创建,只等ResumeThread(这个被放在3环),程序开始执行代码。
进程链表、线程链表的更新
在3环下编程,我们通常不大关心对方进程的信息,因为进程的内存是隔离的(想关心也关心不了)。但是,少数情况下,我们还是可以利用进程间通讯或者注入的手段,获取到对方进程中的信息。
比如简单的SendMessage就可以在进程间通讯。
那么深入一点点,就自然可以提出一个问题:既然进程间是隔离的,为什么SendMessage这样的API可以跨进程通讯呢?
深入一点点,可以自然得到一个答案的方向:说明Windows操作系统本身,记录了所有进程的信息,以及各个进程之间的关系,使得各进程在操作系统那个层次,被组织到了一起。
而这些进程被组织的具体细节,就藏在了NtCreateUserPorcess接下来调用的函数中:
操作系统用链表的结构保存所有进程的EPROCESS结构体。
NtCreateUserPorcess通过调用PspInsertProcess将新进程加入到那个全局链表中。
关于PspInsertProcess的具体剖析,此系列的后续文章会介绍。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
000000014031DFC8 lea rdx, [rsp+0B28h+var_AccessState1] 000000014031DFD0 mov [rsp+40h], rdx ; 参数AccessState 000000014031DFD5 mov [rsp+38h], rax ; 参数enumType 000000014031DFDA mov [rsp+30h], r15d ; 参数unKnownFlag 000000014031DFDF mov rax, [rsp+0B28h+var_CreateProcessContext.DebugObjectHandle] 000000014031DFE7 mov [rsp+28h], rax ; 参数DebugObjectHandle 000000014031DFEC mov [rsp+20h], ebx ; 参数JobMemberLevel 000000014031DFF0 mov r9d, dword ptr [rsp+0B28h+CreateProcessFlags] ; 参数ProcessFlags 000000014031DFF8 mov r8d, ecx ; 参数ProcessDesiredAccess 000000014031DFFB mov rdx, qword ptr [rsp+0B28h+var_pProcess] ; 参数ParentEProcess 000000014031E003 mov rcx, rsi ; 参数Eprocess 000000014031E006 call PspInsertProcess ; 000000014031E006 ; PspInsertProcess( 000000014031E006 ; Eprocess, 000000014031E006 ; ParentEProcess, 000000014031E006 ; AccessMode, 000000014031E006 ; ProcessFlags, 000000014031E006 ; JobMemberLevel, 000000014031E006 ; DebugObjectHandle, 000000014031E006 ; unKnownFlag, 000000014031E006 ; enumType, 000000014031E006 ; AccessState); |
在调用PspInsertProcess失败后会调用PspDoHandleSweepSingle。
1
2
3
4
5
|
000000014031E09E jge short loc_14031E0B0 ; 如果PspInsertProcess执行失败 000000014031E0A0 mov rcx, rsi ; 参数Eprocess 000000014031E0A3 call PspDoHandleSweepSingle ; PspDoHandleSweepSingle(Eprocess); 000000014031E0A8 mov edi, r13d 000000014031E0AB jmp loc_14031E1C1 |
又因为进程与线程是一对多关系,每一个进程也对应着一个链表,该链表中保存着这个进程的所有线程信息。
所以,NtCreateUserPorcess会调用PspInsertThread将新进程的主线程加入进程的线程链表中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
000000014031E00E mov rcx, [rsp+0B28h+var_CreateProcessContext.pClient_ID] 000000014031E016 mov [rsp+50h], rcx ; 参数pClient_ID 000000014031E01B mov rax, [rsp+0B28h+var_ThreadHandle] 000000014031E023 mov [rsp+48h], rax ; 参数pThreadHandle 000000014031E028 mov [rsp+40h], rbx ; 参数 000000014031E02D lea rax, [rsp+0B28h+var_AccessState2] 000000014031E035 mov [rsp+38h], rax ; 参数NewAccessState 000000014031E03A lea rax, [rsp+0B28h+var_CreateProcessContext] 000000014031E042 mov [rsp+30h], rax ; 参数pCreateProcessContext 000000014031E047 mov [rsp+28h], r14 ; 参数 000000014031E04C mov dword ptr [rsp+0B28h+var_B08], edi 000000014031E050 lea r9, [rsp+0B28h+var_AccessStateExpand] ; 参数pProcessFlag 000000014031E055 lea r8, [rsp+0B28h+var_Inital_teb] ; 参数pInital_teb 000000014031E05D mov rdx, rsi ; 参数pEprocess 000000014031E060 mov r14, [rsp+0B28h+var_pThread] ; 参数pThread 000000014031E068 mov rcx, r14 000000014031E06B call PspInsertThread ; 000000014031E06B ; PspInsertProcess( 000000014031E06B ; pThread, 000000014031E06B ; pEprocess, 000000014031E06B ; pInital_teb, 000000014031E06B ; ProcessFlags, 000000014031E06B ; pClient_ID, 000000014031E06B ; pThreadHandle, 000000014031E06B ; unknow, 000000014031E06B ; NewAccessState, 000000014031E06B ; CreateProcessContext, 000000014031E06B ; ); |
在调用PspInsertThread失败后会调用SeDeleteAccessState并接着调用PsTerminateProcess结束新进程。
1
2
3
4
5
6
7
8
9
|
000000014031E1A6 lea rcx, [rsp+0B28h+var_AccessState1] ; 参数AccessState 000000014031E1AE call SeDeleteAccessState ; SeDeleteAccessState(pAccessState); 000000014031E1B3 cmp edi, ebx 000000014031E1B5 jge short loc_14031E1C1 000000014031E1B7 mov edx, edi ; 参数ExitStatus 000000014031E1B9 mov rcx, rsi ; 参数NewProcess 000000014031E1BC call PsTerminateProcess ; PsTerminateProcess( 000000014031E1BC ; NewProcess, 000000014031E1BC ; ExitStatus); |
交付APC
异步过程调用(APC)是Windows提出的一种调用机制。对于有些函数调用,可能耗时比较多,而我们又希望调用完后函数能够立即返回,那么就适合用APC。
APC的原理:对于需要使用APC的地方,一般,用户(程序员)会多传入一个函数指针,专有名词成为ApcRoutine,对于这样的调用,就是异步的了(即用户调用后立即返回),而当任务真正执行完毕,用户传入的ApcRoutine函数指针会被调用。类似我们传入了一个回调函数,响应任务完成的时机。
它在Windows中应用很多,比如写文件时系统调用NtWriteFile().
NtWriteFile声明如下
1
2
3
4
5
6
7
8
9
10
11
|
NTSTATUS NtWriteFile ( __in HANDLE FileHandle, __in_opt HANDLE Event, __in_opt PIO_APC_ROUTINE ApcRoutine, __in_opt PVOID ApcContext, __out PIO_STATUS_BLOCK IoStatusBlock, __in_bcount(Length) PVOID Buffer, __in ULONG Length, __in_opt PLARGE_INTEGER ByteOffset, __in_opt PULONG Key )。 |
可以看到NtWriteFile有使用APC。
不过在此处,我们只要知道NtCreateUserPorcess会有APC检查和交付步骤就可以了,以后遇到会继续深入介绍:
KiCheckForKernelApcDelivery交付当前线程APC:
1
2
3
4
5
6
7
8
9
|
000000014031E072 mov rcx, [rsp+0B28h+var_Ethread] 000000014031E07A add [rcx+_ETHREAD.Tcb.___u22.__s5.KernelApcDisable], r12w 000000014031E082 jnz short loc_14031E09B 000000014031E084 lea rax, [rcx+_ETHREAD.Tcb.ApcState] 000000014031E088 cmp [rax], rax 000000014031E08B jz short loc_14031E09B 000000014031E08D cmp [rcx+_ETHREAD.Tcb.___u22.__s5.SpecialApcDisable], bx 000000014031E094 jnz short loc_14031E09B 000000014031E096 call KiCheckForKernelApcDelivery |
接着,就是一些检查、释放资源类的扫尾工作了
总之,若一切顺利,会调用PspUpdateCreateInfo将进程创建信息保存到传出参数CreateInfo中:
1
2
3
4
5
6
7
|
000000014031E131 mov r8, rsi ; 参数Eprocess 000000014031E134 lea rdx, [rsp+0B28h+var_CreateProcessContext] ; 参数pCreateProcessContext 000000014031E13C mov ecx, 6 ; 参数 000000014031E141 call PspUpdateCreateInfo ; PspUpdateCreateInfo( 000000014031E141 ; dwEmCode, 000000014031E141 ; pCreateProcessContext, 000000014031E141 ; Eprocess); |
若不顺利,会调用PspDoHandleSweepSingle或者PsTerminateProcess结束新进程:
1
2
3
4
5
|
000000014031E1B7 mov edx, edi ; 参数ExitStatus 000000014031E1B9 mov rcx, rsi ; 参数NewProcess 000000014031E1BC call PsTerminateProcess ; PsTerminateProcess( 000000014031E1BC ; NewProcess, 000000014031E1BC ; ExitStatus); |
最后,会调用PspDeleteCreateProcessContext清理CreateProcessContext,释放资源。
PspBuildCreateProcessContext剖析
函数原型:
1
2
3
4
5
6
7
8
9
|
PspBuildCreateProcessContext( // 属性列表 IN PNT_PROC_THREAD_ATTRIBUTE_LIST AtributeList, // 访问模式,表明调用来自用户态,还是内核态 IN BYTE AccessMode, // 未知 IN DWORD Unknown, // 创建进程上下文 OUT CreateProcessContext pCreateProcessContext) |
函数功能:将参数AttributeList中信息保存到CreateProcessContext中。参数AttributeList为变长数组_NT_PROC_THREAD_ATTRIBUTE_LIST类型,定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
typedef struct _NT_PROC_THREAD_ATTRIBUTE_ENTRY { //PROC_THREAD_ATTRIBUTE_XXX ,参见MSDN中UpdateProcThreadAttribute // 的说明 ULONG_PTR Attribute; //Value 的大小 SIZE_T Size; // 保存4字节数据(比如一个Handle)或数据指针 ULONG_PTR Value; // 总是0,可能是用来返回数据给调用者 ULONG Unknown; } PROC_THREAD_ATTRIBUTE_ENTRY, *PPROC_THREAD_ATTRIBUTE_ENTRY; typedef struct _NT_PROC_THREAD_ATTRIBUTE_LIST { // 结构总大小 SIZE_T Length; PROC_THREAD_ATTRIBUTE_ENTRY Entry[1]; } NT_PROC_THREAD_ATTRIBUTE_LIST,*PNT_PROC_THREAD_ATTRIBUTE_LIST; |
函数流程概要:
1、 循环从AttributeList中取出PROC_THREAD_ATTRIBUTE_ENTRY对象。
2、 根据PROC_THREAD_ATTRIBUTE_ENTRY对象的属性Attribute,判断大小Size是否正确,若正确则将值Value保存到CreateProcessContext相应的成员中。
函数流程图:
函数细节:
1、检查参数,主要检查AttributeList的长度和地址:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
00000001403661CC mov rax, [rbx+_NT_PROC_THREAD_ATTRIBUTE_LIST.Length] 00000001403661CF mov [rsp+128h+Length], rax 00000001403661D4 cmp rax, 28h 00000001403661D8 jb loc_140366763 ; if (AttributeList.Length<28h) 00000001403661DE cmp r8b, sil ; if (PreviousMode==0) 00000001403661E1 jz short loc_14036621A ; 取出AttributeList长度 00000001403661E3 add rax, 0FFFFFFFFFFFFFFD8h 00000001403661E7 cmp rax, rsi 00000001403661EA jz short loc_14036621A ; if (AttributeList.Length==28h) 00000001403661EC test r15b, bl 00000001403661EF jnz loc_14036676D ; 检查地址是否对齐 00000001403661F5 mov rdx, [rsp+128h+Length] 00000001403661FA add rdx, rbx 00000001403661FD mov rcx, cs:MmUserProbeAddress 0000000140366204 cmp rdx, rcx 0000000140366207 ja loc_140366773 000000014036620D lea rax, [rbx+28h] 0000000140366211 cmp rdx, rax 0000000140366214 jb loc_140366773 |
2、循环取出数组元素:
1
2
3
4
5
6
7
8
9
|
0000000140366230 shr [rsp+128h+Length], 5 0000000140366236 add rbx, 8 ; 第一个元素 000000014036623A mov [rsp+128h+Entry], rbx 000000014036632F add rbx, 20h ; +=sizeof(_NT_PROC_THREAD_ATTRIBUTE_ENTRY) 0000000140366333 mov [rsp+128h+Entry], rbx 0000000140366338 dec [rsp+128h+Length] ; AttributeList.Length--; 000000014036633D mov r8b, [rsp+128h+PreviousMode] 0000000140366345 jmp loc_14036624C |
3、循环中间判断Entry的属性Attribute:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
0000000140366298 cmp rax, 60010h ; if (Attribute>60010h) 000000014036629E ja loc_140366EE0 00000001403662A4 cmp eax, 2000Bh ; if (Attribute>2000Bh) 00000001403662A9 ja loc_14036669B 00000001403662AF cmp eax, 2000Bh ; if (Attribute==2000Bh) 00000001403662B4 jz loc_1403669C5 00000001403662BA sub eax, 6 ; if (Attribute==6) 00000001403662BD jz loc_140366454 00000001403662C3 sub eax, 0FFFDh ; if (Attribute!=10003) 00000001403662C8 jnz loc_14036636A 000000014036669B sub eax, 2000Dh 00000001403666A0 jz loc_140366E53 ; if (Attribute==2000Dh) 00000001403666A6 sub eax, 0FFFFh 00000001403666AB jz loc_140366D6B ; if (Attribute==3000Ch) 00000001403666B1 sub eax, 2 00000001403666B4 jz loc_140366C8A ; if (Attribute==3000Eh) 00000001403666BA sub eax, 1 00000001403666BD jz loc_140366B44 ; if (Attribute==3000Fh) 00000001403666C3 sub eax, 2FFF1h 00000001403666C8 jz loc_140366B1F ; if (Attribute==60000h) 00000001403666CE sub eax, 1 00000001403666D1 jz loc_140366AFA ; if (Attribute==60001h) 00000001403666D7 sub eax, 1 00000001403666DA jnz loc_140366A99 ; if (Attribute!=60002h) 0000000140366593 sub eax, 2 0000000140366596 jz loc_140366861 ; if (Attribute==20007h) 000000014036659C sub eax, 1 000000014036659F jz loc_14036670A ; if (Attribute==20008h) 00000001403665A5 sub eax, 1 00000001403665A8 jz loc_1403667F8 ; if (Attribute==20009h) 00000001403665AE sub eax, 1 00000001403665B1 jnz loc_140366EE0 ; if (Attribute!=2000Ah) 000000014036636A sub eax, 1 000000014036636D jz loc_1403664F1 ; if (Attribute==10004) 0000000140366373 sub eax, 10001h 0000000140366378 jnz loc_140366593 ; if (Attribute!=20005) |
4、根据属性Attribute检查值Value大小是否正确:
1
2
3
4
5
6
7
8
|
000000014036637E mov rdi, [rbx+_NT_PROC_THREAD_ATTRIBUTE_ENTRY.Size] 0000000140366382 mov [rsp+128h+var_D0], rdi 0000000140366387 cmp rdi, rsi 000000014036638A jz loc_140366961 0000000140366390 test dil, 1 0000000140366394 jnz loc_140366961 000000014036639A cmp rdi, 0FFFFh 00000001403663A1 ja loc_140366961 |
5、将Value保存到CreateProcessContext相应的成员中, ProcessCreateContext中的各成员的内容,由AttributeList中的Attribute的值决定,已经分析出的对应关系如下:
PspCaptureCreateInfo剖析
函数原型:
1
2
3
4
5
6
7
8
|
NTSTATUS NTAPI PspCaptureCreateInfo( // 访问模式 IN BYTE AccessMode, // 进程创建信息结构体 IN PPROCESS_CREATE_INFO CreateInfo, // 创建进程上下文 IN CreateProcessContext* pCreateProcessContext ) |
函数功能:将CreateInfo的0x11(Flags2)偏移处进行运算后赋值给pCreateProcessContext的成员Flag2,将CreateInfo的0x13偏移处(ImageCharacteristics)赋值给pCreateProcessContext的成员ImageCharacteristics,将第二个参数CreateInfo的地址给pCreateProcessContext的成员pCreateInfo。
函数流程图:
关键代码实现:
1
2
3
4
5
6
7
|
pCreateProcessContext->UnKown_1 &= 0xFCu; pCreateProcessContext->UnKown_1 |= (pCreateInfo->UnKown0 & 3) & 3; pCreateProcessContext->DesiredAccess = pCreateInfo->DesiredAccess; pCreateProcessContext->Flags2 ^= (pCreateProcessContext->Flags2 ^ 2 * pCreateInfo->Flags2) & 2; pCreateProcessContext->Flags2 ^= (pCreateProcessContext->Flags2 ^ 16 * pCreateInfo->Flags2) & 0x20; pCreateProcessContext->ImageCharacteristics = pCreateInfo->ImageCharacteristics; pCreateProcessContext->pCreateInfo = pCreateInfo; |
PspCaptureProcessParameters剖析
函数原型:
1
2
3
4
5
6
7
8
|
NTSTATUS PspCaptureProcessParameters( // 访问模式,表明调用来自用户态,还是内核态 IN _MODE AccessMode, // 此结构体包含了创建进程指定的STARTINFO结构中的信息 IN PRTL_USER_PROCESS_PARAMETERS ProcessParameters, // 传入传出结构体指针, 存放进程创建过程中一些句柄 内核对象 IN OUT PCREATEPROCESSCONTEXT pCreateProcessContext ); |
函数功能:
检查ProcessParameters参数中的unicode字符串是否为有效的,并且申请新的内存将字符串复制到申请的空间中,把新申请的内存地址保存在pCreateProcessContext->pProcessParamers中后返回。
主要流程:
判断accessMode是userMode还是kernelMode
若为kernelMode则设置Flags2为0FBh,且pCreateProcessContext->pProcessParamers = ProcessParameters 赋值完成后直接返回,函数结束。
若为userMode则先检查ProcessParameters参数中的unicode字符串是否有效,之后计算空间大小,申请新内存,将参数的unicode保存到新内存中,最后将内存地址赋值给pCreateProcessContext->pProcessParamers,函数结束。
流程图:
细节:
1、 判断accessMode是userMode还是kernelMode。
1
2
|
000000014031F83C cmp cl, bl ; if (accessMode != KernelMode) 000000014031F83E jz loc_1403C9EF2 |
2、 若是kernelMode跳走,并且直接给pCreateProcessContext->Flags2和pCreateProcessContext->pProcessParameters赋值,完成后函数退出
1
2
|
00000001403C9EF2 and [r8+CreateProcessContext.Flags2], 0FBh 00000001403C9EF7 mov [r8+CreateProcessContext.PRTL_USER_PROCESS_PARAMETERS], rdx // 参数2直接给CreateProcessContext->PRTL_USER_PROCESS_PARAMETERS |
3、 若是UserMode 则先用PspCaptureAndValidateUnicodeString检测字符串有效性并获取字符串的unicode_string结构,保存在局部变量中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
000000014031F8F1 lea rcx, [r12+_RTL_USER_PROCESS_PARAMETERS.CurrentDirectory] ; src 000000014031F8F6 lea rdx, [rsp+118h+CurrentDirectory] ; dst 000000014031F8FB call PspCaptureAndValidateUnicodeString 000000014031F900 cmp eax, ebx 000000014031F902 jl loc_14031FC9F 000000014031F908 mov eax, 208h 000000014031F90D cmp [rsp+118h+CurrentDirectory.Length], ax 000000014031F912 jnb loc_1403C9F3E 000000014031F918 mov [rsp+118h+CurrentDirectory.MaximumLength], ax 000000014031F91D lea rcx, [r12+_RTL_USER_PROCESS_PARAMETERS.DllPath] 000000014031F922 lea rdx, [rsp+118h+dllPath] 000000014031F927 call PspCaptureAndValidateUnicodeString 000000014031F92C cmp eax, ebx 000000014031F92E jl loc_14031FC9F 000000014031F934 000000014031F934 loc_14031F934: ; CODE XREF: PspCaptureProcessParameters+AA721j 000000014031F934 lea rcx, [r12+_RTL_USER_PROCESS_PARAMETERS.ImagePathName] 000000014031F939 lea rdx, [rsp+118h+ImagePathName] 000000014031F941 call PspCaptureAndValidateUnicodeString 000000014031F946 cmp eax, ebx 000000014031F948 jl loc_14031FC9F 000000014031F94E lea rcx, [r12+_RTL_USER_PROCESS_PARAMETERS.CommandLine] 000000014031F953 lea rdx, [rsp+118h+CommandLine] 000000014031F95B call PspCaptureAndValidateUnicodeString 000000014031F960 cmp eax, ebx 000000014031F962 jl loc_14031FC9F 000000014031F968 lea rcx, [r12+_RTL_USER_PROCESS_PARAMETERS.WindowTitle] 000000014031F970 lea rdx, [rsp+118h+WindowTitle] 000000014031F978 call PspCaptureAndValidateUnicodeString 000000014031F97D cmp eax, ebx 000000014031F97F jl loc_14031FC9F 000000014031F985 lea rcx, [r12+_RTL_USER_PROCESS_PARAMETERS.DesktopInfo] 000000014031F98D lea rdx, [rsp+118h+DesktopInfo] 000000014031F995 call PspCaptureAndValidateUnicodeString 000000014031F99A cmp eax, ebx 000000014031F99C jl loc_14031FC9F 000000014031F9A2 lea rcx, [r12+_RTL_USER_PROCESS_PARAMETERS.ShellInfo] 000000014031F9AA lea rdx, [rsp+118h+ShellInfo] 000000014031F9B2 call PspCaptureAndValidateUnicodeString |
4、 之后计算空间,调用ExAllocatePoolWithQuotaTag申请内存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
000000014031F9E4 mov r15, [rsp+118h+RuntimeData.Buffer] 000000014031F9E9 cmp r15, rbx 000000014031F9EC jnz loc_1403C9F70 000000014031F9F2 movzx r14d, [rsp+118h+RuntimeData.Length] 000000014031F9F8 cmp r14w, bx 000000014031F9FC jnz loc_1403C9F66 000000014031FA02 mov [rsp+118h+RuntimeData.MaximumLength], bx 000000014031FA07 000000014031FA07 loc_14031FA07: ; CODE XREF: PspCaptureProcessParameters+AA749j 000000014031FA07 ; PspCaptureProcessParameters+AA7ABj 000000014031FA07 movzx ecx, [rsp+118h+var_4E] 000000014031FA0F movzx eax, [rsp+118h+var_6E] 000000014031FA17 add rcx, rax 000000014031FA1A movzx eax, [rsp+118h+var_7E] 000000014031FA22 add rcx, rax 000000014031FA25 movzx eax, [rsp+118h+var_5E] 000000014031FA2D add rcx, rax 000000014031FA30 movzx eax, [rsp+118h+var_8E] 000000014031FA38 add rcx, rax 000000014031FA3B movzx eax, [rsp+118h+dllPath.MaximumLength] 000000014031FA40 add rcx, rax 000000014031FA43 movzx eax, [rsp+118h+RuntimeData.MaximumLength] 000000014031FA48 add rcx, rax 000000014031FA4B movzx eax, [rsp+118h+CurrentDirectory.MaximumLength] 000000014031FA50 lea rcx, [rcx+rax+401h] 000000014031FA58 and rcx, 0FFFFFFFFFFFFFFFEh 000000014031FA5C mov [rsp+118h+var_C8], rcx 000000014031FA61 mov [rsp+118h+var_B0], rcx 000000014031FA66 mov rax, [rsp+118h+EnvironmentSize] 000000014031FA6B lea rdx, [rcx+rax] ; NumberOfBytes 000000014031FA6F cmp rdx, rcx 000000014031FA72 jb loc_1403C9FD1 000000014031FA78 mov [rsp+118h+var_A8], rdx 000000014031FA7D mov esi, ebx |
5、 通过PspCopyUnicodeString拷贝参数里的字符串到申请的空间中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
000000014031FB09 lea rdx, [rdi+_RTL_USER_PROCESS_PARAMETERS.CurrentDirectory] 000000014031FB0D lea r8, [rsp+118h+Dst] 000000014031FB15 lea rcx, [rsp+118h+CurrentDirectory] ; src 000000014031FB1A call PspCopyUnicodeString 000000014031FB1F mov esi, eax 000000014031FB21 cmp eax, ebx 000000014031FB23 jl loc_1403CA0AC 000000014031FB29 lea rdx, [rdi+_RTL_USER_PROCESS_PARAMETERS.DllPath] 000000014031FB2D lea r8, [rsp+118h+Dst] 000000014031FB35 lea rcx, [rsp+118h+dllPath] 000000014031FB3A call PspCopyUnicodeString 000000014031FB3F mov esi, eax 000000014031FB41 cmp eax, ebx 000000014031FB43 jl loc_1403CA0AC 000000014031FB49 lea rdx, [rdi+_RTL_USER_PROCESS_PARAMETERS.ImagePathName] 000000014031FB4D lea r8, [rsp+118h+Dst] 000000014031FB55 lea rcx, [rsp+118h+ImagePathName] 000000014031FB5D call PspCopyUnicodeString 000000014031FB62 mov esi, eax 000000014031FB64 cmp eax, ebx 000000014031FB66 jl loc_1403CA0AC 000000014031FB6C lea rdx, [rdi+_RTL_USER_PROCESS_PARAMETERS.CommandLine] 000000014031FB70 lea r8, [rsp+118h+Dst] 000000014031FB78 lea rcx, [rsp+118h+CommandLine] 000000014031FB80 call PspCopyUnicodeString 000000014031FB85 mov esi, eax 000000014031FB87 cmp eax, ebx 000000014031FB89 jl loc_1403CA0AC 000000014031FB8F lea rdx, [rdi+_RTL_USER_PROCESS_PARAMETERS.WindowTitle] 000000014031FB96 lea r8, [rsp+118h+Dst] 000000014031FB9E lea rcx, [rsp+118h+WindowTitle] 000000014031FBA6 call PspCopyUnicodeString 000000014031FBAB mov esi, eax 000000014031FBAD cmp eax, ebx 000000014031FBAF jl loc_1403CA0AC 000000014031FBB5 lea rdx, [rdi+_RTL_USER_PROCESS_PARAMETERS.DesktopInfo] 000000014031FBBC lea r8, [rsp+118h+Dst] 000000014031FBC4 lea rcx, [rsp+118h+DesktopInfo] 000000014031FBCC call PspCopyUnicodeString 000000014031FBD1 mov esi, eax 000000014031FBD3 cmp eax, ebx 000000014031FBD5 jl loc_1403CA0AC 000000014031FBDB lea rdx, [rdi+_RTL_USER_PROCESS_PARAMETERS.ShellInfo] ; Src 000000014031FBE2 lea r8, [rsp+118h+Dst] ; Dst 000000014031FBEA lea rcx, [rsp+118h+ShellInfo] ; Size 000000014031FBF2 call PspCopyUnicodeString |
6、最后设置Flages2和pProcessParameters并退出
1
2
3
4
5
6
7
|
000000014031FC91 or [r13+CreateProcessContext.Flags2], 4 000000014031FC96 mov [r13+CreateProcessContext.PRTL_USER_PROCESS_PARAMETERS], rdi 000000014031FC9D xor eax, eax 000000014031FC9F add rsp, 0E0h 000000014031FCA6 pop r15 000000014031FCA8 pop r14 000000014031FCAA pop r13 |