转载: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手册或者相关书籍。
![](https://bbs.pediy.com/upload/attach/201602/406681_zhffua4l40hot5d.png)
![](https://bbs.pediy.com/upload/attach/201602/406681_4l4gt0snmjfk8wa.png)
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流程
初始化
![](upload/attach/201602/406681_8ugr21m5oqyyxv9.png)
在一开始,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初始化完成后开始做的。
![](upload/attach/201602/406681_x7xyp0s7gvl9idp.png)
具体而言:
调用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存在以下调用关系:
![](upload/attach/201602/406681_qt49dafg2lyk7jf.png)
而其中的MiVerifyImageHeader就是PE检查,对64位PE格式感兴趣的朋友,一定不能放过。我们将会在下一篇文章中,详细介绍MiVerifyImageHeader,并献上攻防实例。
将参数整合到CREATEPROCESSCONTEXT结构体中
![](upload/attach/201602/406681_c03pz0lkd5mu88f.png)
如果文件映像对象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结构体。
![](upload/attach/201602/406681_465dofyom1znsyv.png)
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
|
接着,就是一些检查、释放资源类的扫尾工作了
![](upload/attach/201602/406681_gzfx5b1ekj21mue.png)
总之,若一切顺利,会调用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,释放资源。
![](upload/attach/201602/406681_a3nvjyz3icjcwtx.png)
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相应的成员中。
函数流程图:
![](upload/attach/201602/406681_vyludzjuffj9s83.png)
函数细节:
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的值决定,已经分析出的对应关系如下:
![](upload/attach/201602/406681_s8pjhdkdf0hgo79.png)
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。
函数流程图:
![](upload/attach/201602/406681_ypqwm55clh0jlxl.png)
关键代码实现:
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,函数结束。
流程图:
![](upload/attach/201602/406681_75j5iuggyebosqn.png)
细节:
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
|