Windows进程/线程创建过程 - yifi

时间:2024-03-11 15:44:21

Windows进程/线程创建过程

 

参考链接:http://www.cnblogs.com/LittleHann/p/3458736.html

创建一个Windows进程,是由操作系统进行3各部分执行组成

1.客户的Windows库的Kernel32.dll

2.Windows执行

3.Windows子系统进程(Csrss.exe)

由于windows是多环境子系统的体系结构,因此,创建一个windows执行体进程对象(其他的子系统也可以使用),与创建一个windows进程的工作是分离的。

也就是说windows在创建进程的过程中有两大类的工作要做:

1.Windows系统加入的语义

2.执行体/内核层对象等的创建

Windows的CreateProcess创建一个进程时所涉及的主要阶段:

1.打开将要在该进程中执行的映像文件(*.exe)

2.创建Windows执行体的进程对象

3.创建初始化线程(栈、执行环境、Windows执行体线程对象)

4.通知Windows子系统新线程创建了,所以他可以为新进程和线程做好准备

5.开始执行初始线程

6.在新进程和线程中 完成地址空间的初始化(加载必要的Dll),并开始执行程序

 

2. 详细分析(CreateProcess)

阶段一:打开将要被执行的映像

如果指定的可执行文件是一个windows.exe类型的文件,那么,它可被直接使用。如果它不是一个windows.exe(例如MS-DOS、Win16、POSIX应用程序),那么CreateProcess通过一系列的步骤来找到一个windows支持映像(support image),以便运行它。这个过程是必须的,因为"非windows"应用程序不能直接被运行,相反,windows使用少数几个特定的"支持映像"中的某一个,由它负责实际运行这一个"非windows"程序。

1. 如果你试图运行一个POSIX应用程序,那么CreateProcess把它识别出来,并改变该映像,改成可以在windows可执行文件Posix.exe上运行的新映像。
2. 如果你试图运行一个MS-DOS或者Win16可执行文件,那么,被运行的映像变成了windows的可执行文件Ntvdm.exe。

 


 

 

 

在CreateProcessA函数中调用了CreateProcessInternalA,调用该函数时,增加了两个参数(一头一尾加了个零参数),在该函数里面也没看到对这两个参数的引用,而是直接传递给CreateProcessInternalW函数。

 

 1 BOOL __stdcall CreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, 
 2       LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, 
 3       LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)
 4 {
 5   return CreateProcessInternalA(
 6            0,
 7            lpApplicationName,
 8            lpCommandLine,
 9            lpProcessAttributes,
10            lpThreadAttributes,
11            bInheritHandles,
12            dwCreationFlags,
13            lpEnvironment,
14            lpCurrentDirectory,
15            lpStartupInfo,
16            lpProcessInformation,
17            0);
18 }
IDA->Kernel32.dll

 

XDBG->动态调试

 

 

  1 int __stdcall CreateProcessInternalA(int var_0, int lpApplicationName, int lpCommandLine, int lpProcessAttributes, int lpThreadAttributes, 
  2     int bInheritHandles, int dwCreationFlags, int lpEnvironment, int lpCurrentDirectory, int lpStartupInfo, int lpProcessInformation, 
  3     int var_0_)
  4 {
  5   int result; // eax@2
  6   int v13; // eax@7
  7   int v14; // eax@10
  8   int v15; // eax@17
  9   int v16; // eax@19
 10   int v17; // eax@21
 11   int v18; // eax@24
 12   int v19; // eax@26
 13   int v20; // eax@30
 14   int v21; // eax@32
 15   signed int v22; // [sp-4h] [bp-B8h]@20
 16   char v23; // [sp+14h] [bp-A0h]@15
 17   int v24; // [sp+18h] [bp-9Ch]@15
 18   unsigned __int32 v25; // [sp+1Ch] [bp-98h]@32
 19   char Dst; // [sp+20h] [bp-94h]@3
 20   int v27; // [sp+24h] [bp-90h]@3
 21   int v28; // [sp+28h] [bp-8Ch]@3
 22   int v29; // [sp+2Ch] [bp-88h]@3
 23   char *v30; // [sp+68h] [bp-4Ch]@15
 24   int v31; // [sp+6Ch] [bp-48h]@21
 25   char v32; // [sp+70h] [bp-44h]@4
 26   int v33; // [sp+74h] [bp-40h]@3
 27   char v34; // [sp+78h] [bp-3Ch]@6
 28   int v35; // [sp+7Ch] [bp-38h]@3
 29   char v36; // [sp+80h] [bp-34h]@2
 30   int v37; // [sp+84h] [bp-30h]@10
 31   int v38; // [sp+88h] [bp-2Ch]@11
 32   char v39; // [sp+8Ch] [bp-28h]@21
 33   __int16 v40; // [sp+8Eh] [bp-26h]@19
 34   int v41; // [sp+90h] [bp-24h]@21
 35   unsigned __int16 v42; // [sp+94h] [bp-20h]@16
 36   CPPEH_RECORD ms_exc; // [sp+9Ch] [bp-18h]@3
 37 
 38   if ( !lpCommandLine )
 39   {
 40     v37 = 0;
 41     v30 = &v23;
 42     v24 = 0;
 43 LABEL_3:
 44     v33 = 0;
 45     v35 = 0;
 46     _memmove(&Dst, (const void *)lpStartupInfo, 0x44u);
 47     v27 = 0;
 48     v28 = 0;
 49     v29 = 0;
 50     ms_exc.disabled = 1;
 51     if ( lpApplicationName && !Basep8BitStringToDynamicUnicodeString(&v32, lpApplicationName)
 52       || lpCurrentDirectory && !Basep8BitStringToDynamicUnicodeString(&v34, lpCurrentDirectory) )
 53       goto LABEL_14;
 54     v13 = *(_DWORD *)(lpStartupInfo + 4);
 55     if ( v13 )
 56     {
 57       ms_exc.disabled = 2;
 58       RtlInitAnsiString(&v42, v13);
 59       ms_exc.disabled = 1;
 60       if ( (_BYTE)NlsMbCodePageTag )
 61         LOWORD(v15) = RtlxAnsiStringToUnicodeSize(&v42);
 62       else
 63         v15 = 2 * v42 + 2;
 64       v40 = v15;
 65       v16 = RtlAllocateHeap(*(_DWORD *)(*(_DWORD *)(__readfsdword(24) + 48) + 24), BaseDllTag, (unsigned __int16)v15);
 66       v27 = v16;
 67       if ( !v16 )
 68         goto LABEL_20;
 69       v41 = v16;
 70       v17 = RtlAnsiStringToUnicodeString(&v39, &v42, 0);
 71       v31 = v17;
 72       if ( v17 < 0 )
 73         goto LABEL_34;
 74     }
 75     if ( *(_DWORD *)(lpStartupInfo + 8) )
 76     {
 77       RtlInitAnsiString(&v42, *(_DWORD *)(lpStartupInfo + 8));
 78       if ( (_BYTE)NlsMbCodePageTag )
 79         LOWORD(v18) = RtlxAnsiStringToUnicodeSize(&v42);
 80       else
 81         v18 = 2 * v42 + 2;
 82       v40 = v18;
 83       v19 = RtlAllocateHeap(*(_DWORD *)(*(_DWORD *)(__readfsdword(24) + 48) + 24), BaseDllTag, (unsigned __int16)v18);
 84       v28 = v19;
 85       if ( !v19 )
 86         goto LABEL_20;
 87       v41 = v19;
 88       v17 = RtlAnsiStringToUnicodeString(&v39, &v42, 0);
 89       v31 = v17;
 90       if ( v17 < 0 )
 91       {
 92 LABEL_34:
 93         v22 = v17;
 94         goto LABEL_35;
 95       }
 96     }
 97     if ( !*(_DWORD *)(lpStartupInfo + 12) )
 98     {
 99 LABEL_10:
100       ms_exc.disabled = 0;
101       v14 = v37;
102       if ( !v37 )
103         v14 = *((_DWORD *)v30 + 1);
104       v38 = CreateProcessInternalW(
105               var_0,
106               v33,
107               v14,
108               lpProcessAttributes,
109               lpThreadAttributes,
110               bInheritHandles,
111               dwCreationFlags,
112               lpEnvironment,
113               v35,
114               &Dst,
115               lpProcessInformation,
116               var_0_);
117       goto LABEL_12;
118     }
119     RtlInitAnsiString(&v42, *(_DWORD *)(lpStartupInfo + 12));
120     if ( (_BYTE)NlsMbCodePageTag )
121       LOWORD(v20) = RtlxAnsiStringToUnicodeSize(&v42);
122     else
123       v20 = 2 * v42 + 2;
124     v40 = v20;
125     v25 = __readfsdword(24);
126     v21 = RtlAllocateHeap(*(_DWORD *)(*(_DWORD *)(v25 + 48) + 24), BaseDllTag, (unsigned __int16)v20);
127     v29 = v21;
128     if ( v21 )
129     {
130       v41 = v21;
131       v17 = RtlAnsiStringToUnicodeString(&v39, &v42, 0);
132       v31 = v17;
133       if ( v17 >= 0 )
134         goto LABEL_10;
135       goto LABEL_34;
136     }
137 LABEL_20:
138     v22 = -1073741801;
139 LABEL_35:
140     BaseSetLastNTError(v22);
141 LABEL_14:
142     v38 = 0;
143     ms_exc.disabled = 0;
144 LABEL_12:
145     ms_exc.disabled = -1;
146     RtlFreeUnicodeString(&v36);
147     RtlFreeUnicodeString(&v32);
148     RtlFreeUnicodeString(&v34);
149     RtlFreeHeap(*(_DWORD *)(*(_DWORD *)(__readfsdword(24) + 48) + 24), 0, v27);
150     RtlFreeHeap(*(_DWORD *)(*(_DWORD *)(__readfsdword(24) + 48) + 24), 0, v28);
151     return RtlFreeHeap(*(_DWORD *)(*(_DWORD *)(__readfsdword(24) + 48) + 24), 0, v29);
152   }
153   result = Basep8BitStringToDynamicUnicodeString(&v36, lpCommandLine);
154   if ( result )
155     goto LABEL_3;
156   return result;
157 }
CreateProcessInternalA源码

1. 函数的开始首先是一大段的临时栈区变量的申请。由于是IDA反编译出来的,

名字都是这个递增名字,其中有一个结构体变量CPPEH_RECORD注意一下:

1 CPPEH_RECORD    struc ; (sizeof=0x18, standard type)
2    old_esp         dd ?
3    exc_ptr         dd ?                    ; offset
4    prev_er         dd ?                    ; offset
5    handler         dd ?                    ; offset
6    msEH_ptr        dd ?                    ; offset
7    disabled        dd ?
8 CPPEH_RECORD    ends
View Code

2. 判断lpCommandLine(就是我们输入的notepad.exe这个命令)是否为空。即判断是否为0,不为0则将lpCommandLine初始化为UNICODE字符串

if ( !lpCommandLine )
{
    ...
}
result = Basep8BitStringToDynamicUnicodeString(&lpCommandLine_UNICODE, lpCommandLine);
if ( result )
    goto LABEL_3;
...
View Code

接着继续检查参数(lpApplicationName / lpCurrentDirectory),我们跟随代码来到LABEL_3:

1. 先将lpStartupInfor的内容拷贝到一个局部变量lpStartupInfo_buf
2. 然后判断参数lpApplicationName是否为0,不为0则参数转换为UNICODE字符串
3. 为0则跳过转换继续判断参数lpCurrentDirectory是否为0(思考"与运算符""或运算符"在汇编代码中的表现形式,即"与运算符"的提前退出性,"或运算符"的并行检查性),
不为0则参数转换为UNICODE字符串
4. lpApplicationName和lpCurrentDirectory中只有至少有一个不为0,则这个if判断成立,继续检查参数
View Code

3. 继续检查参数(lpStartupInfo.lpReserved / lpStartupInfo.lpDesktop / lpStartupInfo.lpTitle),接着判断STARTUPINFOA.lpReserved域是否为0,如果不为0,则申请了一个堆空间并将STARTUPINFOA.lpReserved的ASCII字符串转换成了UNICODE,接下来判断lpStartupInfo.lpDesktop、lpStartupInfo.lpTitle。代码模式都是一样的:

4.参数判断完毕 开始调用CreateProcessInternalW(),这次传入的参数全部都是UNICODE字符串了,也就是符合NT式的标准,我们知道,windows在NT系统以后把所有的API(例如CreateProcessInternalW)实现都以UNICODE实现了,而保留原本的ASCII版本的API(例如CreateProcessInternalA)只是在中间做了一个"转阶层",最终都一定要调用UNICODE版本的API,

 

阶段三: CreateProcessInternalW(),ring3阶段进程创建流程

int __stdcall CreateProcessInternalW(int var_0, size_t lpApplicationName, wchar_t *lpCommandLine, int lpProcessAttributes, int lpThreadAttributes, 
    int bInheritHandles, int dwCreationFlags, int lpEnvironment, const WCHAR *lpCurrentDirectory, const void *lpStartupInfo, 
    int lpProcessInformation, int var_0_)
{
  unsigned __int16 v12; // ax@22
  int v13; // eax@22
  int v14; // esi@23
  size_t v15; // edi@33
  int v16; // eax@34
  int v17; // edi@39
  int v18; // eax@40
  signed int v19; // esi@42
  int v20; // eax@47
  int v21; // eax@50
  signed int v22; // edi@55
  int v23; // eax@55
  HMODULE v24; // eax@65
  HMODULE v25; // esi@65
  signed int v26; // esi@76
  int v27; // eax@76
  int v28; // ecx@81
  int v29; // eax@2
  int v30; // eax@17
  int v31; // edi@89
  DWORD v32; // eax@91
  int v33; // ecx@97
  signed int v34; // eax@101
  int v35; // eax@121
  int v36; // esi@123
  void (__stdcall *v37)(_DWORD, _DWORD, _DWORD); // esi@127
  int *v38; // esi@131
  int v39; // edi@131
  int v40; // eax@132
  int v41; // eax@135
  int v42; // ecx@136
  int v43; // esi@138
  int result; // eax@154
  int *v45; // esi@161
  int v46; // edi@161
  int v47; // ecx@161
  int v48; // edx@162
  int v49; // ecx@163
  int v50; // eax@165
  int v51; // esi@167
  HMODULE v52; // eax@182
  int v53; // eax@182
  signed int v54; // esi@198
  unsigned int v55; // eax@201
  const wchar_t *v56; // edi@201
  int v57; // edi@206
  size_t v58; // eax@209
  int v59; // eax@209
  wchar_t *v60; // esi@209
  wchar_t *v61; // edi@226
  DWORD v62; // eax@259
  int v63; // esi@259
  DWORD v64; // eax@261
  size_t v65; // eax@270
  wchar_t v66; // ax@272
  HANDLE v67; // eax@297
  int *v68; // eax@329
  int v69; // ecx@329
  int v70; // ecx@330
  size_t v71; // eax@358
  int v72; // esi@358
  unsigned __int32 v73; // eax@377
  unsigned __int32 v74; // esi@377
  size_t v75; // eax@377
  wchar_t *v76; // eax@377
  const wchar_t *v77; // edi@378
  int v78; // eax@393
  int v79; // eax@398
  int v80; // esi@398
  signed int v81; // [sp-4h] [bp-A28h]@173
  signed int v82; // [sp-4h] [bp-A28h]@179
  DWORD v83; // [sp-4h] [bp-A28h]@277
  int v84; // [sp-4h] [bp-A28h]@289
  signed int v85; // [sp-4h] [bp-A28h]@235
  int v86; // [sp+18h] [bp-A0Ch]@22
  unsigned __int32 v87; // [sp+28h] [bp-9FCh]@394
  char v88; // [sp+2Ch] [bp-9F8h]@14
  int v89; // [sp+30h] [bp-9F4h]@182
  int v90; // [sp+34h] [bp-9F0h]@47
  int v91; // [sp+38h] [bp-9ECh]@103
  unsigned __int32 v92; // [sp+3Ch] [bp-9E8h]@140
  unsigned __int32 v93; // [sp+40h] [bp-9E4h]@192
  unsigned __int32 v94; // [sp+44h] [bp-9E0h]@171
  unsigned __int32 v95; // [sp+48h] [bp-9DCh]@191
  unsigned __int32 v96; // [sp+4Ch] [bp-9D8h]@409
  unsigned __int32 v97; // [sp+50h] [bp-9D4h]@241
  unsigned int v98; // [sp+54h] [bp-9D0h]@201
  unsigned __int32 v99; // [sp+58h] [bp-9CCh]@190
  unsigned __int32 v100; // [sp+5Ch] [bp-9C8h]@309
  unsigned __int32 v101; // [sp+60h] [bp-9C4h]@247
  HANDLE v102; // [sp+64h] [bp-9C0h]@297
  unsigned __int32 v103; // [sp+68h] [bp-9BCh]@189
  unsigned __int32 v104; // [sp+6Ch] [bp-9B8h]@220
  unsigned __int32 v105; // [sp+70h] [bp-9B4h]@418
  unsigned __int32 v106; // [sp+74h] [bp-9B0h]@144
  unsigned __int32 v107; // [sp+78h] [bp-9ACh]@144
  unsigned __int32 v108; // [sp+7Ch] [bp-9A8h]@377
  unsigned int v109; // [sp+80h] [bp-9A4h]@209
  DWORD v110; // [sp+84h] [bp-9A0h]@91
  int v111; // [sp+88h] [bp-99Ch]@50
  DWORD v112; // [sp+8Ch] [bp-998h]@90
  unsigned __int32 v113; // [sp+90h] [bp-994h]@310
  unsigned __int32 v114; // [sp+94h] [bp-990h]@89
  unsigned __int32 v115; // [sp+98h] [bp-98Ch]@84
  unsigned __int32 v116; // [sp+9Ch] [bp-988h]@158
  int v117; // [sp+A0h] [bp-984h]@34
  HMODULE v118; // [sp+A4h] [bp-980h]@65
  int v119; // [sp+A8h] [bp-97Ch]@248
  unsigned __int32 v120; // [sp+ACh] [bp-978h]@358
  unsigned __int32 v121; // [sp+B0h] [bp-974h]@269
  unsigned __int32 v122; // [sp+B4h] [bp-970h]@358
  unsigned __int32 v123; // [sp+B8h] [bp-96Ch]@231
  unsigned __int32 v124; // [sp+BCh] [bp-968h]@358
  unsigned __int32 v125; // [sp+C0h] [bp-964h]@20
  unsigned __int32 v126; // [sp+C4h] [bp-960h]@352
  unsigned __int32 v127; // [sp+C8h] [bp-95Ch]@144
  int v128; // [sp+CCh] [bp-958h]@209
  unsigned __int32 v129; // [sp+D0h] [bp-954h]@144
  unsigned __int32 v130; // [sp+D4h] [bp-950h]@209
  unsigned __int32 v131; // [sp+D8h] [bp-94Ch]@181
  char v132; // [sp+DCh] [bp-948h]@411
  int v133; // [sp+E0h] [bp-944h]@411
  DWORD v134; // [sp+E4h] [bp-940h]@261
  char *v135; // [sp+E8h] [bp-93Ch]@224
  char *v136; // [sp+ECh] [bp-938h]@224
  char *v137; // [sp+F0h] [bp-934h]@224
  char *v138; // [sp+F4h] [bp-930h]@1
  char *v139; // [sp+F8h] [bp-92Ch]@1
  char *v140; // [sp+FCh] [bp-928h]@1
  char *v141; // [sp+100h] [bp-924h]@1
  char *v142; // [sp+104h] [bp-920h]@1
  int *v143; // [sp+108h] [bp-91Ch]@409
  const void *v144; // [sp+110h] [bp-914h]@1
  int v145; // [sp+114h] [bp-910h]@193
  int v146; // [sp+118h] [bp-90Ch]@1
  size_t v147; // [sp+11Ch] [bp-908h]@209
  int v148; // [sp+120h] [bp-904h]@1
  int v149; // [sp+124h] [bp-900h]@76
  int v150; // [sp+128h] [bp-8FCh]@163
  int *v151; // [sp+12Ch] [bp-8F8h]@86
  int v152; // [sp+130h] [bp-8F4h]@198
  int v153; // [sp+134h] [bp-8F0h]@81
  int v154; // [sp+138h] [bp-8ECh]@22
  int v155; // [sp+13Ch] [bp-8E8h]@17
  int v156; // [sp+140h] [bp-8E4h]@1
  char v157; // [sp+144h] [bp-8E0h]@23
  __int16 v158; // [sp+146h] [bp-8DEh]@23
  int v159; // [sp+148h] [bp-8DCh]@22
  int v160; // [sp+14Ch] [bp-8D8h]@1
  int v161; // [sp+150h] [bp-8D4h]@208
  int v162; // [sp+154h] [bp-8D0h]@1
  char v163[4]; // [sp+158h] [bp-8CCh]@1
  int v164; // [sp+15Ch] [bp-8C8h]@1
  char v165; // [sp+163h] [bp-8C1h]@33
  int v166; // [sp+164h] [bp-8C0h]@1
  int v167; // [sp+168h] [bp-8BCh]@232
  int v168; // [sp+16Ch] [bp-8B8h]@409
  int v169; // [sp+170h] [bp-8B4h]@1
  int v170; // [sp+174h] [bp-8B0h]@25
  DWORD dwErrCode; // [sp+178h] [bp-8ACh]@30
  int v172; // [sp+17Ch] [bp-8A8h]@259
  int v173; // [sp+180h] [bp-8A4h]@161
  int v174; // [sp+184h] [bp-8A0h]@25
  int v175; // [sp+18Ch] [bp-898h]@38
  int v176; // [sp+1B0h] [bp-874h]@25
  LPCWSTR lpFileName; // [sp+1C8h] [bp-85Ch]@1
  size_t v178; // [sp+1CCh] [bp-858h]@356
  wchar_t v179; // [sp+1D0h] [bp-854h]@252
  wchar_t *v180; // [sp+1D4h] [bp-850h]@226
  int v181; // [sp+1D8h] [bp-84Ch]@1
  int v182; // [sp+1DCh] [bp-848h]@88
  int v183; // [sp+1E0h] [bp-844h]@1
  int v184; // [sp+1E4h] [bp-840h]@1
  int v185; // [sp+1E8h] [bp-83Ch]@1
  int v186; // [sp+1ECh] [bp-838h]@70
  int v187; // [sp+1F0h] [bp-834h]@55
  unsigned int v188; // [sp+1F8h] [bp-82Ch]@101
  int v189; // [sp+1FCh] [bp-828h]@103
  int v190; // [sp+200h] [bp-824h]@61
  unsigned __int16 v191; // [sp+204h] [bp-820h]@63
  unsigned __int16 v192; // [sp+206h] [bp-81Eh]@63
  char v193; // [sp+20Dh] [bp-817h]@56
  unsigned __int16 v194; // [sp+210h] [bp-814h]@59
  int v195; // [sp+220h] [bp-804h]@72
  wchar_t *Dest; // [sp+224h] [bp-800h]@25
  int v197; // [sp+228h] [bp-7FCh]@1
  int v198; // [sp+22Ch] [bp-7F8h]@25
  char v199; // [sp+230h] [bp-7F4h]@87
  int v200; // [sp+234h] [bp-7F0h]@88
  wchar_t *v201; // [sp+248h] [bp-7DCh]@1
  LPWSTR Str1; // [sp+24Ch] [bp-7D8h]@25
  char v203; // [sp+253h] [bp-7D1h]@30
  int v204; // [sp+254h] [bp-7D0h]@1
  int v205; // [sp+258h] [bp-7CCh]@37
  int v206; // [sp+25Ch] [bp-7C8h]@37
  int *v207; // [sp+260h] [bp-7C4h]@37
  int v208; // [sp+264h] [bp-7C0h]@37
  int v209; // [sp+268h] [bp-7BCh]@37
  int v210; // [sp+26Ch] [bp-7B8h]@37
  char v211; // [sp+270h] [bp-7B4h]@103
  int v212; // [sp+278h] [bp-7ACh]@104
  int i; // [sp+284h] [bp-7A0h]@81
  char v214; // [sp+28Ah] [bp-79Ah]@122
  char v215; // [sp+28Bh] [bp-799h]@1
  int v216; // [sp+28Ch] [bp-798h]@188
  int v217; // [sp+290h] [bp-794h]@1
  int v218; // [sp+294h] [bp-790h]@1
  int v219; // [sp+298h] [bp-78Ch]@1
  int v220; // [sp+29Ch] [bp-788h]@1
  int v221; // [sp+2A0h] [bp-784h]@1
  int v222; // [sp+2A4h] [bp-780h]@1
  int v223; // [sp+2A8h] [bp-77Ch]@1
  int v224; // [sp+2ACh] [bp-778h]@1
  int v225; // [sp+2B0h] [bp-774h]@86
  int v226; // [sp+2B4h] [bp-770h]@375
  int v227; // [sp+2B8h] [bp-76Ch]@375
  int v228; // [sp+2BCh] [bp-768h]@375
  int v229; // [sp+2C0h] [bp-764h]@1
  int v230; // [sp+2C4h] [bp-760h]@1
  int v231; // [sp+2C8h] [bp-75Ch]@1
  int v232; // [sp+2CCh] [bp-758h]@1
  int v233; // [sp+2D0h] [bp-754h]@1
  int v234; // [sp+2D4h] [bp-750h]@1
  char v235; // [sp+2DBh] [bp-749h]@1
  int v236; // [sp+2DCh] [bp-748h]@37
  int v237; // [sp+2E0h] [bp-744h]@33
  int v238; // [sp+2E4h] [bp-740h]@176
  int v239; // [sp+2E8h] [bp-73Ch]@36
  int v240; // [sp+2ECh] [bp-738h]@1
  int v241; // [sp+2F0h] [bp-734h]@1
  int v242; // [sp+2F4h] [bp-730h]@1
  int v243; // [sp+2F8h] [bp-72Ch]@1
  int v244; // [sp+2FCh] [bp-728h]@1
  int v245; // [sp+300h] [bp-724h]@1
  wchar_t *Source; // [sp+304h] [bp-720h]@1
  size_t Size; // [sp+308h] [bp-71Ch]@1
  char v248; // [sp+30Eh] [bp-716h]@25
  char v249; // [sp+30Fh] [bp-715h]@1
  char v250; // [sp+310h] [bp-714h]@37
  char v251; // [sp+31Bh] [bp-709h]@30
  char *v252; // [sp+31Ch] [bp-708h]@1
  char *v253; // [sp+320h] [bp-704h]@1
  char *v254; // [sp+324h] [bp-700h]@1
  char *v255; // [sp+328h] [bp-6FCh]@1
  int *v256; // [sp+32Ch] [bp-6F8h]@1
  int *v257; // [sp+330h] [bp-6F4h]@1
  int v258; // [sp+334h] [bp-6F0h]@1
  int v259; // [sp+338h] [bp-6ECh]@35
  int v260; // [sp+33Ch] [bp-6E8h]@106
  int v261; // [sp+340h] [bp-6E4h]@107
  char v262; // [sp+344h] [bp-6E0h]@324
  int v263; // [sp+348h] [bp-6DCh]@3
  char v264; // [sp+34Ch] [bp-6D8h]@324
  int v265; // [sp+350h] [bp-6D4h]@3
  char v266; // [sp+354h] [bp-6D0h]@99
  wchar_t *v267; // [sp+358h] [bp-6CCh]@25
  int v268; // [sp+35Ch] [bp-6C8h]@1
  int v269; // [sp+360h] [bp-6C4h]@181
  __int16 v270; // [sp+364h] [bp-6C0h]@154
  __int16 v271; // [sp+366h] [bp-6BEh]@358
  wchar_t *v272; // [sp+368h] [bp-6BCh]@25
  int v273; // [sp+36Ch] [bp-6B8h]@33
  int v274; // [sp+370h] [bp-6B4h]@34
  char v275; // [sp+377h] [bp-6ADh]@30
  char v276; // [sp+378h] [bp-6ACh]@120
  char v277; // [sp+37Ch] [bp-6A8h]@67
  int v278; // [sp+380h] [bp-6A4h]@370
  LPWSTR FilePart; // [sp+384h] [bp-6A0h]@25
  int v280; // [sp+388h] [bp-69Ch]@25
  int v281; // [sp+38Ch] [bp-698h]@1
  int v282; // [sp+390h] [bp-694h]@1
  int v283; // [sp+394h] [bp-690h]@1
  int v284; // [sp+398h] [bp-68Ch]@1
  int v285; // [sp+39Ch] [bp-688h]@1
  int v286; // [sp+3A0h] [bp-684h]@25
  int v287; // [sp+3A4h] [bp-680h]@1
  int v288; // [sp+3A8h] [bp-67Ch]@25
  int v289; // [sp+3ACh] [bp-678h]@25
  int v290; // [sp+3B0h] [bp-674h]@1
  int v291; // [sp+3B4h] [bp-670h]@25
  int v292; // [sp+3B8h] [bp-66Ch]@25
  char v293; // [sp+3BCh] [bp-668h]@10
  char v294; // [sp+3BDh] [bp-667h]@9
  char v295; // [sp+3C0h] [bp-664h]@104
  char v296; // [sp+68Ch] [bp-398h]@115
  int v297; // [sp+6ACh] [bp-378h]@116
  int v298; // [sp+6B4h] [bp-370h]@107
  int v299; // [sp+6B8h] [bp-36Ch]@107
  int v300; // [sp+6BCh] [bp-368h]@107
  int v301; // [sp+6C0h] [bp-364h]@107
  int v302; // [sp+6C4h] [bp-360h]@109
  int v303; // [sp+6C8h] [bp-35Ch]@109
  int v304; // [sp+6CCh] [bp-358h]@109
  int v305; // [sp+6D0h] [bp-354h]@113
  int v306; // [sp+6D4h] [bp-350h]@395
  int v307; // [sp+6D8h] [bp-34Ch]@395
  int v308; // [sp+6DCh] [bp-348h]@1
  char v309; // [sp+6E8h] [bp-33Ch]@224
  char v310; // [sp+710h] [bp-314h]@224
  char v311; // [sp+716h] [bp-30Eh]@329
  char v312; // [sp+734h] [bp-2F0h]@224
  __int64 v313; // [sp+73Ch] [bp-2E8h]@107
  signed __int16 v314; // [sp+744h] [bp-2E0h]@108
  char v315; // [sp+74Ch] [bp-2D8h]@1
  int v316; // [sp+760h] [bp-2C4h]@81
  char v317; // [sp+770h] [bp-2B4h]@1
  int v318; // [sp+784h] [bp-2A0h]@81
  char v319; // [sp+794h] [bp-290h]@1
  int v320; // [sp+7A8h] [bp-27Ch]@81
  char v321; // [sp+7B8h] [bp-26Ch]@1
  int v322; // [sp+7CCh] [bp-258h]@81
  char v323; // [sp+7DCh] [bp-248h]@1
  int v324; // [sp+7F0h] [bp-234h]@81
  __int16 v325; // [sp+800h] [bp-224h]@57
  CPPEH_RECORD ms_exc; // [sp+A0Ch] [bp-18h]@25
  int v327; // [sp+A44h] [bp+20h]@2

  v185 = var_0;
  Size = lpApplicationName;
  Source = lpCommandLine;
  v169 = lpProcessAttributes;
  v164 = lpThreadAttributes;
  v234 = lpEnvironment;
  lpFileName = lpCurrentDirectory;
  v144 = lpStartupInfo;
  v166 = lpProcessInformation;
  v148 = var_0_;
  v290 = 0;
  v201 = 0;
  v287 = 0;
  v233 = 0;
  v204 = 0;
  v160 = 0;
  v146 = dwCreationFlags & 0x8000000;
  v235 = 0;
  v162 = 0;
  v284 = 0;
  v282 = 0;
  v249 = 0;
  v285 = 0;
  v281 = 0;
  v283 = 0;
  v181 = 0;
  v138 = &v317;
  v139 = &v321;
  v140 = &v323;
  v141 = &v315;
  v142 = &v319;
  v229 = 0;
  v230 = 0;
  v231 = 0;
  v232 = 0;
  v221 = 0;
  v222 = 0;
  v223 = 0;
  v224 = 0;
  v256 = &v268;
  v257 = &v258;
  v252 = &v317;
  v253 = &v315;
  v254 = &v321;
  v255 = &v319;
  v217 = 0;
  v218 = 0;
  v219 = 0;
  v220 = 0;
  v197 = 0;
  v156 = 0;
  v184 = 0;
  v183 = 0;
  *(_DWORD *)v163 = 0;
  v243 = 0;
  v244 = 0;
  v245 = 0;
  v240 = 0;
  v241 = 0;
  v242 = 0;
  v215 = 0;
  memset(&v308, 0, 0x60u);
  *(_DWORD *)lpProcessInformation = 0;
  *(_DWORD *)(lpProcessInformation + 4) = 0;
  *(_DWORD *)(lpProcessInformation + 8) = 0;
  *(_DWORD *)(lpProcessInformation + 12) = 0;
  if ( var_0_ )
    *(_DWORD *)var_0_ = 0;
  v29 = dwCreationFlags & 0xF7FFFFFF;
  v327 = v29;
  if ( (v29 & 0x18) == 24 )
    goto LABEL_279;
  v265 = 0;
  v263 = 0;
  if ( v29 & 0x40 )
  {
    v294 = 1;
  }
  else
  {
    if ( BYTE1(v29) & 0x40 )
    {
      v294 = 5;
    }
    else
    {
      if ( v29 & 0x20 )
      {
        v294 = 2;
      }
      else
      {
        if ( SBYTE1(v29) < 0 )
        {
          v294 = 6;
        }
        else
        {
          if ( (char)v29 < 0 )
          {
            v294 = 3;
          }
          else
          {
            if ( BYTE1(v29) & 1 )
              v294 = (BasepIsRealtimeAllowed(0) != 0) + 3;
            else
              v294 = 0;
          }
        }
      }
    }
  }
  v293 = 0;
  LOWORD(v327) = v327 & 0x3E1F;
  if ( v327 & 0x800 )
  {
    if ( !(v327 & 0x1000) )
      goto LABEL_13;
LABEL_279:
    SetLastError(0x57u);
    return 0;
  }
  if ( !(v327 & 0x1000) && *(_BYTE *)(BaseStaticServerData + 6644) )
    v327 |= 0x800u;
LABEL_13:
  if ( !(v327 & 0x800) && NtQueryInformationJobObject(0, 4, &v88, 4, 0) != -1073741790 )
    v327 = v327 & 0xFFFFEFFF | 0x800;
  if ( !v234 || BYTE1(v327) & 4 )
    goto LABEL_25;
  v30 = v234;
  v155 = v234;
  while ( *(_BYTE *)v30 || *(_BYTE *)(v30 + 1) )
    ++v30;
  LOWORD(v154) = v30 - (_WORD)v234 + 1;
  v12 = v30 - (_WORD)v234 + 2;
  HIWORD(v154) = v12;
  v86 = 2 * v12;
  v159 = 0;
  v13 = NtAllocateVirtualMemory(-1, &v159, 0, &v86, 4096, 4);
  if ( v13 < 0 )
  {
    v84 = v13;
LABEL_290:
    BaseSetLastNTError(v84);
    return 0;
  }
  v158 = v86;
  v14 = RtlAnsiStringToUnicodeString(&v157, &v154, 0);
  if ( v14 < 0 )
  {
    NtFreeVirtualMemory(-1, &v159, &v86, 32768);
    v84 = v14;
    goto LABEL_290;
  }
  v234 = v159;
LABEL_25:
  v289 = 0;
  v291 = 0;
  v292 = 0;
  v288 = 0;
  v198 = 0;
  Str1 = 0;
  v267 = 0;
  v280 = 1;
  v286 = 0;
  v170 = 0;
  FilePart = 0;
  v272 = 0;
  v248 = 0;
  Dest = 0;
  ms_exc.disabled = 0;
  memcpy(&v174, v144, 0x44u);
  if ( BYTE1(v176) & 1 && BYTE1(v176) & 6 )
    BYTE1(v176) &= 0xFEu;
  while ( 1 )
  {
    if ( Str1 )
    {
      v123 = __readfsdword(24);
      RtlFreeHeap(*(_DWORD *)(*(_DWORD *)(v123 + 48) + 24), 0, Str1);
      Str1 = 0;
    }
    if ( v198 )
    {
      v104 = __readfsdword(24);
      RtlFreeHeap(*(_DWORD *)(*(_DWORD *)(v104 + 48) + 24), 0, v198);
      v198 = 0;
    }
    if ( v289 )
    {
      NtClose(v289);
      v289 = 0;
    }
    dwErrCode = 0;
    v203 = 1;
    v251 = 0;
    v275 = 0;
    if ( Size )
    {
      if ( !Source || !*Source )
      {
        v275 = 1;
        Source = (wchar_t *)Size;
      }
      goto LABEL_33;
    }
    v121 = __readfsdword(24);
    Str1 = (LPWSTR)RtlAllocateHeap(*(_DWORD *)(*(_DWORD *)(v121 + 48) + 24), BaseDllTag, 520);
    if ( !Str1 )
      goto LABEL_293;
    v65 = (size_t)Source;
    Size = (size_t)Source;
    v201 = Source;
    v61 = Source;
    v180 = Source;
    if ( *Source != 34 )
      goto LABEL_271;
    v203 = 0;
    v61 = Source + 1;
    v180 = Source + 1;
    Size = (size_t)(Source + 1);
    while ( *v61 )
    {
      if ( *v61 == 34 )
      {
        v201 = v61;
        v248 = 1;
        break;
      }
      ++v61;
      v180 = v61;
      v201 = v61;
    }
LABEL_259:
    v179 = *v201;
    *v201 = 0;
    v62 = SearchPathW(0, (LPCWSTR)Size, L".exe", 0x104u, Str1, 0);
    v63 = 2 * v62;
    v172 = 2 * v62;
    if ( 2 * v62 && (unsigned int)v63 < 0x208 )
    {
      v64 = GetFileAttributesW(Str1);
      v134 = v64;
      if ( v64 != -1 && v64 & 0x10 )
      {
        v63 = 0;
      }
      else
      {
        v172 = v63 + 1;
        v63 += 2;
      }
      v172 = v63;
    }
    if ( !v63 || (unsigned int)v63 >= 0x208 )
    {
      v119 = RtlDetermineDosPathNameType_U(Size);
      if ( v119 == 5 )
        goto LABEL_249;
      v67 = CreateFileW((LPCWSTR)Size, 0x80000000u, 3u, 0, 3u, 0x80u, 0);
      v102 = v67;
      if ( v67 != (HANDLE)-1 )
      {
        CloseHandle(v67);
LABEL_249:
        BaseSetLastNTError(-1073741772);
      }
      if ( dwErrCode )
        SetLastError(dwErrCode);
      else
        dwErrCode = GetLastError();
      *v201 = v179;
      Size = (size_t)Str1;
      if ( !*v61 || !v203 )
        goto LABEL_173;
      ++v61;
      v180 = v61;
      v201 = v61;
      v251 = 1;
      v248 = 1;
      v65 = (size_t)Source;
LABEL_271:
      Size = v65;
      while ( 1 )
      {
        v66 = *v61;
        if ( !*v61 )
          goto LABEL_259;
        if ( v66 == 32 || v66 == 9 )
        {
          v201 = v61;
          goto LABEL_259;
        }
        ++v61;
        v180 = v61;
        v201 = v61;
      }
    }
    *v201 = v179;
    Size = (size_t)Str1;
    if ( BasepIsSetupInvokedByWinLogon(Str1) && !(BYTE3(v327) & 0x80) )
      BYTE3(v327) |= 0x80u;
LABEL_33:
    v15 = Size;
    v165 = RtlDosPathNameToNtPathName_U(Size, &v273, 0, &v237);
    if ( !v165 )
    {
      v83 = 3;
      goto LABEL_278;
    }
    v198 = v274;
    RtlInitUnicodeString(&v268, v15);
    v16 = RtlDetermineDosPathNameType_U(v15);
    v117 = v16;
    if ( v16 != 2 && v16 != 1 )
    {
      if ( !v197 )
      {
        v94 = __readfsdword(24);
        v197 = RtlAllocateHeap(*(_DWORD *)(*(_DWORD *)(v94 + 48) + 24), 0, 522);
        if ( !v197 )
        {
          v83 = 8;
          goto LABEL_278;
        }
      }
      RtlGetFullPathName_U(v15, 522, v197, 0);
      RtlInitUnicodeString(&v268, v197);
    }
    v258 = v273;
    v259 = v274;
    if ( (_WORD)v237 )
    {
      v273 = v237;
      v274 = v238;
    }
    else
    {
      v239 = 0;
    }
    v205 = 24;
    v206 = v239;
    v208 = 64;
    v207 = &v273;
    v209 = 0;
    v210 = 0;
    v236 = NtOpenFile(&v289, 1048737, &v205, &v250, 5, 96);
    if ( v236 < 0 )
    {
      v236 = NtOpenFile(&v289, 1048608, &v205, &v250, 5, 96);
      if ( v236 < 0 )
        break;
    }
    if ( !v175 )
    {
      v115 = __readfsdword(24);
      v175 = *(_DWORD *)(*(_DWORD *)(*(_DWORD *)(v115 + 48) + 16) + 124);
    }
    v17 = NtCreateSection(&v291, 983071, 0, 0, 16, 16777216, v289);
    v236 = v17;
    if ( v17 < 0 )
      goto LABEL_425;
    v18 = BasepIsProcessAllowed(Size);
    v17 = v18;
    v236 = v18;
    if ( v18 < 0 )
    {
      BaseSetLastNTError(v18);
      NtClose(v291);
      goto LABEL_173;
    }
    if ( BYTE1(v327) & 0x20 && *(_BYTE *)(BaseStaticServerData + 6645) )
    {
      BYTE1(v327) &= 0xCFu;
      v19 = 2048;
      v327 |= 0x800u;
      v17 = -1073741519;
      v236 = -1073741519;
      v160 = 1;
      NtClose(v291);
      v291 = 0;
    }
    else
    {
LABEL_425:
      v19 = 2048;
    }
    if ( !v249 )
    {
      if ( v17 >= 0 || v17 == -1073741521 && !BaseIsDosApplication(&v273, -1073741521) )
      {
        if ( v284 )
        {
          v100 = __readfsdword(24);
          RtlFreeHeap(*(_DWORD *)(*(_DWORD *)(v100 + 48) + 24), 0, v284);
          v284 = 0;
        }
        if ( v285 )
        {
          v113 = __readfsdword(24);
          RtlFreeHeap(*(_DWORD *)(*(_DWORD *)(v113 + 48) + 24), 0, v285);
          v285 = 0;
        }
        v20 = BasepCheckBadapp(v289, v274, v234, &v284, &v282, &v285, &v281);
        v90 = v20;
        if ( v20 < 0 )
        {
          if ( v20 == -1073741790 )
            SetLastError(0x4C7u);
          else
            BaseSetLastNTError(v20);
          if ( v291 )
          {
            NtClose(v291);
            v291 = 0;
          }
          goto LABEL_173;
        }
      }
      if ( !v249 && !(BYTE3(v327) & 2) )
      {
        v21 = BasepCheckWinSaferRestrictions(v185, Size, v289, &v156, &v183, &v184);
        v111 = v21;
        if ( v21 == -1 )
        {
          SetLastError(0x4ECu);
        }
        else
        {
          if ( v21 >= 0 )
            goto LABEL_52;
          BaseSetLastNTError(v21);
        }
        v214 = 0;
        goto LABEL_126;
      }
    }
LABEL_52:
    if ( v17 >= 0 )
      goto LABEL_53;
    if ( v17 == -1073741541 )
      goto LABEL_198;
    if ( v17 <= -1073741522 )
      goto LABEL_277;
    if ( v17 <= -1073741520 )
    {
LABEL_198:
      v54 = 1;
      v152 = 1;
      if ( v17 != -1073741520 )
      {
        if ( v17 != -1073741541 )
        {
          v54 = BaseIsDosApplication(&v273, v17);
          v152 = v54;
          if ( !v54 )
          {
            v55 = (unsigned int)(unsigned __int16)v273 >> 1;
            v56 = (const wchar_t *)(v274 + 2 * v55 - 8);
            v98 = v274 + 2 * v55 - 8;
            if ( (unsigned __int16)v273 < 8u
              || __wcsnicmp((const wchar_t *)(v274 + 2 * v55 - 8), L".bat", 4u) && __wcsnicmp(v56, L".cmd", 4u) )
              goto LABEL_277;
            v57 = v275 || v248;
            if ( v275 || (v161 = 0, v248) )
              v161 = 1;
            v147 = _wcslen(Source);
            v58 = _wcslen(Str);
            v109 = v161 + v147 + v58 + v57 + 1;
            v130 = __readfsdword(24);
            v59 = RtlAllocateHeap(*(_DWORD *)(*(_DWORD *)(v130 + 48) + 24), BaseDllTag, 2 * v109);
            v60 = (wchar_t *)v59;
            v128 = v59;
            if ( !v59 )
              goto LABEL_293;
            _wcscpy((wchar_t *)v59, Str);
            if ( v275 || v248 )
              _wcscat(v60, L"\"");
            _wcscat(v60, Source);
            if ( v275 || v248 )
              _wcscat(v60, L"\"");
            RtlInitUnicodeString(&v270, v60);
LABEL_215:
            Source = v272;
            Size = 0;
            goto LABEL_216;
          }
        }
      }
      v204 = 16;
      if ( !BaseCreateVDMEnvironment(v234, &v264, &v262)
        || !BaseCheckVDM(v54 | 0x10, Size, Source, lpFileName, &v264, &v296, &v287, v327, &v174) )
        goto LABEL_173;
      v68 = &v298;
      v69 = (v311 & 7) - 1;
      if ( (v311 & 7) == 1 )
      {
        v233 = 1;
        if ( v327 & 8 )
        {
          v83 = 5;
          goto LABEL_278;
        }
        if ( !BaseGetVdmConfigInfo(Source, v287, 16, &v266, &v286) )
          goto LABEL_344;
        Source = v267;
        Size = 0;
LABEL_346:
        v35 = v290;
        goto LABEL_347;
      }
      goto LABEL_330;
    }
    if ( v17 == -1073741519 )
    {
      if ( BYTE1(v327) & 0x20 )
        goto LABEL_198;
      v235 = 1;
      if ( !BaseCreateVDMEnvironment(v234, &v264, &v262) )
        goto LABEL_173;
      while ( 1 )
      {
        v204 = (v19 & v327) != 0 ? 64 : 32;
        if ( BaseCheckVDM((v19 & v327) != 0 ? 64 : 32, Size, Source, lpFileName, &v264, &v296, &v287, v327, &v174) )
          break;
        if ( v204 != 32 || GetLastError() != 5 )
          goto LABEL_173;
        v327 |= v19;
      }
      v68 = &v298;
      v69 = (v311 & 7) - 1;
      if ( (v311 & 7) == 1 )
      {
        v233 = 1;
        if ( v160 )
          v286 = 1;
        if ( !BaseGetVdmConfigInfo(Source, v287, v204, &v266, &v286) )
        {
LABEL_344:
          v82 = v17;
          goto LABEL_180;
        }
        Source = v267;
        Size = 0;
        BYTE3(v327) |= 8u;
        v327 &= 0xFFFFFFE7u;
        v176 |= 0x40u;
        goto LABEL_346;
      }
LABEL_330:
      v70 = v69 - 1;
      if ( !v70 )
      {
        v83 = 21;
        goto LABEL_278;
      }
      if ( v70 != 2 )
        goto LABEL_346;
      v233 = 4;
      v35 = v68[3];
      v290 = v35;
LABEL_347:
      --v286;
      if ( v35 )
        goto LABEL_122;
      bInheritHandles = 0;
      if ( v234 && !(BYTE1(v327) & 4) )
        RtlDestroyEnvironment(v234);
      v234 = v263;
LABEL_216:
      v249 = 1;
    }
    else
    {
      if ( v17 != -1073741209 )
        goto LABEL_277;
      SetLastError(0x10FEu);
LABEL_53:
      if ( !v235 && v19 & v327 )
        BYTE1(v327) &= 0xF7u;
      v22 = 1;
      v23 = NtQuerySection(v291, 1, &v187, 48, 0);
      v236 = v23;
      if ( v23 < 0 )
        goto LABEL_242;
      if ( v193 & 0x20 )
        goto LABEL_277;
      v325 = 0;
      if ( !(v327 & 3) || (v126 = __readfsdword(24), *(_BYTE *)(*(_DWORD *)(v126 + 48) + 1)) )
        LdrQueryImageFileExecutionOptions(&v273, L"Debugger", 1, &v325, 520, 0);
      if ( v194 < v7FFE002C || v194 > v7FFE002E )
      {
        v168 = 6;
        v143 = &v273;
        NtRaiseHardError(1073741859, 1, 1, &v143, 1, &v168);
        v96 = __readfsdword(24);
        if ( *(_DWORD *)(*(_DWORD *)(v96 + 48) + 184) <= 3u )
LABEL_277:
          v83 = 193;
        else
          v83 = 216;
        goto LABEL_278;
      }
      if ( v190 != 2 && v190 != 3 )
      {
        NtClose(v291);
        v291 = 0;
        if ( v190 != 7 )
        {
          v83 = 129;
          goto LABEL_278;
        }
        if ( !BuildSubSysCommandLine(L"POSIX /P ", Size, Source, &v270) )
          goto LABEL_173;
        goto LABEL_215;
      }
      if ( !BasepIsImageVersionOk(v192, v191) )
        goto LABEL_277;
      if ( !v325 )
      {
        v24 = LoadLibraryA("advapi32.dll");
        v25 = v24;
        v118 = v24;
        if ( v24 )
        {
          if ( GetProcAddress(v24, "CreateProcessAsUserSecure") )
          {
            v236 = NtQuerySystemInformation(71, &v277, 4, 0);
            if ( !v236 )
              v215 = 1;
          }
          FreeLibrary(v25);
        }
        v186 = BaseFormatObjectAttributes(&v205, v169, 0);
        if ( v215 && v185 && v169 )
        {
          v243 = *(_DWORD *)v169;
          v244 = *(_DWORD *)(v169 + 4);
          v245 = *(_DWORD *)(v169 + 8);
          v244 = 0;
          v186 = BaseFormatObjectAttributes(&v205, &v243, 0);
          v22 = 1;
        }
        v195 = 0;
        if ( BYTE3(v327) & 1 )
          v195 = v22;
        if ( v327 & 3 )
        {
          v23 = DbgUiConnectToDbg();
          v236 = v23;
          if ( v23 < 0 )
            goto LABEL_242;
          v162 = DbgUiGetThreadDebugObject();
          if ( v327 & 2 )
            v195 |= 2u;
        }
        if ( bInheritHandles )
          v195 |= 4u;
        v149 = v194 == 332 ? v284 : 0;
        v26 = -1;
        v27 = NtCreateProcessEx(&v292, 2035711, v186, -1, v195, v291, v162, 0, v156);
        v236 = v27;
        if ( v27 < 0 )
          goto LABEL_426;
        if ( v294 )
        {
          v167 = 0;
          if ( v294 == 4 )
            v167 = BasepIsRealtimeAllowed(v22);
          v236 = NtSetInformationProcess(v292, 18, &v293, 2);
          if ( v167 )
            BasepReleasePrivilege(v167);
          if ( v236 < 0 )
          {
            v85 = v236;
LABEL_363:
            BaseSetLastNTError(v85);
LABEL_364:
            v81 = v26;
            goto LABEL_174;
          }
        }
        if ( BYTE3(v327) & 4 )
        {
          v145 = v22;
          NtSetInformationProcess(v292, 12, &v145, 4);
        }
        if ( v204 )
        {
          v290 = v292;
          if ( !BaseUpdateVDMEntry(v22, &v290, v287, v204) )
          {
            v290 = 0;
            goto LABEL_364;
          }
          v233 |= 2u;
        }
        if ( v286 )
        {
          v278 = v286;
          v27 = NtAllocateVirtualMemory(v292, &v280, 0, &v278, 8192, 64);
          v236 = v27;
          if ( v27 < 0 )
            goto LABEL_426;
        }
        v318 = (unsigned __int16)v268 + 20;
        v322 = (unsigned __int16)v268 + 16;
        v324 = (unsigned __int16)v268 + 2;
        v316 = (unsigned __int16)v258 + 20;
        v320 = (unsigned __int16)v258 + 16;
        v28 = 0;
        v153 = 0;
        for ( i = 0; i != 5; ++i )
        {
          v28 += *((_DWORD *)(&v138)[4 * i] + 5);
          v153 = v28;
        }
        v116 = __readfsdword(24);
        v181 = RtlAllocateHeap(*(_DWORD *)(*(_DWORD *)(v116 + 48) + 24), 0, v28);
        if ( !v181 )
        {
          v85 = -1073741801;
          goto LABEL_363;
        }
        i = 0;
        while ( i != 5 )
        {
          v45 = (int *)&(&v138)[4 * i];
          v46 = *v45;
          v47 = *(_DWORD *)(*v45 + 20);
          v173 = *(_DWORD *)(*v45 + 20);
          if ( i )
            v48 = *(_DWORD *)(*(v45 - 1) + 8) + *(_DWORD *)(*(v45 - 1) + 20);
          else
            v48 = v181;
          v150 = v48;
          v49 = v47 & 0xFFFFFFFE;
          v173 = v49;
          if ( (unsigned int)v49 > 0xFFFE )
          {
            v49 = 65534;
            v173 = 65534;
          }
          if ( (unsigned int)v49 < 2 )
          {
            v48 = v46 + 32;
            v150 = v46 + 32;
            v49 = 2;
            v173 = 2;
          }
          v50 = *v45;
          *(_DWORD *)(*v45 + 8) = v48;
          *(_DWORD *)(v50 + 16) = v49;
          *(_DWORD *)(v50 + 12) = v48;
          *(_DWORD *)(v50 + 20) = v49;
          *(_DWORD *)(v50 + 4) = v48;
          if ( v48 )
            **(_WORD **)(v46 + 4) = 0;
          v51 = *v45;
          *(_WORD *)v51 = 0;
          *(_WORD *)(v51 + 2) = v49;
          ++i;
          v26 = -1;
        }
        v230 = v292;
        v229 = v289;
        v231 = v291;
        if ( v285 )
        {
          v225 = v268;
          v226 = v269;
          v227 = v285;
          v228 = v281;
        }
        v151 = &v308;
        v27 = BasepSxsCreateProcessCsrMessage(
                v285 != 0 ? (int)&v225 : 0,
                0,
                &v252,
                &v221,
                &v256,
                &v229,
                &v254,
                &v217,
                &v323,
                &v308);
        v236 = v27;
        if ( v27 < 0 || (v27 = NtQueryInformationProcess(v292, 0, &v199, 24, 0), v236 = v27, v27 < 0) )
        {
LABEL_426:
          v85 = v27;
          goto LABEL_363;
        }
        v182 = v200;
        if ( lpFileName )
        {
          v114 = __readfsdword(24);
          v31 = RtlAllocateHeap(*(_DWORD *)(*(_DWORD *)(v114 + 48) + 24), BaseDllTag, 522);
          v170 = v31;
          if ( !v31 )
          {
LABEL_293:
            v82 = -1073741801;
            goto LABEL_180;
          }
          v112 = GetFullPathNameW(lpFileName, 0x104u, (LPWSTR)v31, &FilePart);
          if ( v112 > 0x104 || (v32 = GetFileAttributesW((LPCWSTR)v31), v110 = v32, v32 == -1) || !(v32 & 0x10) )
          {
            v83 = 267;
            goto LABEL_278;
          }
        }
        if ( v251 || v275 )
        {
          v73 = __readfsdword(24);
          v74 = v73;
          v108 = v73;
          v75 = _wcslen(Source);
          v76 = (wchar_t *)RtlAllocateHeap(*(_DWORD *)(*(_DWORD *)(v74 + 48) + 24), 0, 2 * v75 + 6);
          Dest = v76;
          if ( v76 )
          {
            _wcscpy(v76, L"\"");
            v77 = v201;
            if ( v251 )
            {
              v179 = *v201;
              *v201 = 0;
            }
            _wcscat(Dest, Source);
            _wcscat(Dest, L"\"");
            if ( v251 )
            {
              *v77 = v179;
              _wcscat(Dest, v77);
            }
          }
          else
          {
            if ( v251 )
              v251 = 0;
            if ( v275 )
              v275 = 0;
          }
        }
        if ( *(_BYTE *)v151 & 1 )
          *(_DWORD *)v163 |= 1u;
        if ( v251 || (v33 = (int)Source, v275) )
          v33 = (int)Dest;
        if ( !BasePushProcessParameters(
                v163[0],
                v292,
                v182,
                (LPCWSTR)Size,
                v170,
                v33,
                v234,
                (int)&v174,
                v327 | v146,
                bInheritHandles,
                v235 != 0 ? 2 : 0,
                v149,
                v282) )
          goto LABEL_173;
        RtlFreeUnicodeString(&v266);
        v267 = 0;
        if ( !v204 )
        {
          if ( !bInheritHandles )
          {
            if ( !(BYTE1(v176) & 1) )
            {
              if ( !(v327 & 0x8000018) )
              {
                if ( v190 == 3 )
                {
                  v236 = NtReadVirtualMemory(v292, v182 + 16, &v216, 4, 0);
                  if ( v236 >= 0 )
                  {
                    v103 = __readfsdword(24);
                    if ( (*(_DWORD *)(*(_DWORD *)(*(_DWORD *)(v103 + 48) + 16) + 24) & 0x10000003) != 3 )
                    {
                      v101 = __readfsdword(24);
                      StuffStdHandle(v292, *(_DWORD *)(*(_DWORD *)(*(_DWORD *)(v101 + 48) + 16) + 24), v216 + 24);
                    }
                    v99 = __readfsdword(24);
                    if ( (*(_DWORD *)(*(_DWORD *)(*(_DWORD *)(v99 + 48) + 16) + 28) & 0x10000003) != 3 )
                    {
                      v97 = __readfsdword(24);
                      StuffStdHandle(v292, *(_DWORD *)(*(_DWORD *)(*(_DWORD *)(v97 + 48) + 16) + 28), v216 + 28);
                    }
                    v95 = __readfsdword(24);
                    if ( (*(_DWORD *)(*(_DWORD *)(*(_DWORD *)(v95 + 48) + 16) + 32) & 0x10000003) != 3 )
                    {
                      v93 = __readfsdword(24);
                      StuffStdHandle(v292, *(_DWORD *)(*(_DWORD *)(*(_DWORD *)(v93 + 48) + 16) + 32), v216 + 32);
                    }
                  }
                }
              }
            }
          }
        }
        v34 = 262144;
        if ( v188 >= 0x40000 )
          v34 = v188;
        v23 = BaseCreateStack(v292, v189, v34, &v211);
        v91 = v23;
        if ( v23 < 0 )
          goto LABEL_242;
        BaseInitializeContext(&v295, v182, v187, v212, 0);
        v186 = BaseFormatObjectAttributes(&v205, v164, 0);
        if ( v215 && v185 && v164 )
        {
          v240 = *(_DWORD *)v164;
          v241 = *(_DWORD *)(v164 + 4);
          v242 = *(_DWORD *)(v164 + 8);
          v241 = 0;
          v186 = BaseFormatObjectAttributes(&v205, &v240, 0);
        }
        v23 = NtCreateThread(&v288, 2032639, v186, v292, &v260, &v295, &v211, 1);
        v236 = v23;
        if ( v23 < 0 )
          goto LABEL_242;
        v313 = v182;
        v298 = v292;
        v299 = v288;
        v300 = v260;
        v301 = v261;
        switch ( v194 )
        {
          case 0x14Cu:
            v314 = 0;
            break;
          case 0x200u:
            v314 = 6;
            break;
          case 0x8664u:
            v314 = 9;
            break;
          default:
            DbgPrint("kernel32: No mapping for ImageInformation.Machine == %04x\n", v194);
            v314 = -1;
            break;
        }
        v304 = v327 & 0xFFFFFFFC;
        v302 = 0;
        v303 = 0;
        if ( v190 == 2 || v235 )
        {
          v298 |= 2u;
          v52 = GetModuleHandleA(0);
          v53 = RtlImageNtHeader(v52);
          v89 = v53;
          if ( v53 )
          {
            if ( *(_WORD *)(v53 + 92) == 2 )
              v298 |= 1u;
          }
        }
        if ( v176 & 0x40 )
          v298 |= 1u;
        if ( v176 & 0x80 )
          v298 &= 0xFFFFFFFEu;
        v305 = v204;
        if ( v204 )
        {
          if ( v287 )
          {
            v78 = 0;
          }
          else
          {
            v87 = __readfsdword(24);
            v78 = *(_DWORD *)(*(_DWORD *)(*(_DWORD *)(v87 + 48) + 16) + 16);
          }
          v307 = v78;
          v306 = v287;
        }
        memcpy(&v298, &v298, 0x98u);
        if ( v308 )
        {
          v135 = &v309;
          v136 = &v310;
          v137 = &v312;
          v23 = CsrCaptureMessageMultiUnicodeStringsInPlace(&v283, 3, &v135);
          v236 = v23;
          if ( v23 < 0 )
          {
LABEL_242:
            v82 = v23;
LABEL_180:
            BaseSetLastNTError(v82);
            goto LABEL_173;
          }
        }
        CsrClientCallServer(&v296, v283, 65536, 152);
        if ( v283 )
        {
          CsrFreeCaptureBuffer(v283);
          v283 = 0;
        }
        if ( v297 < 0 )
        {
          BaseSetLastNTError(v297);
          NtTerminateProcess(v292, v297);
          goto LABEL_173;
        }
        if ( v183 )
        {
          if ( !v185 )
          {
            v79 = BasepReplaceProcessThreadTokens(v183, v292, v288);
            v80 = v79;
            v236 = v79;
            if ( v79 < 0 )
            {
              NtTerminateProcess(v292, v79);
              v82 = v80;
              goto LABEL_180;
            }
          }
        }
        if ( v184 )
        {
          v236 = NtAssignProcessToJobObject(v184, v292);
          if ( v236 < 0 )
          {
            NtTerminateProcess(v292, -1073741790);
LABEL_179:
            v82 = v236;
            goto LABEL_180;
          }
        }
        if ( !(v327 & 4) )
          NtResumeThread(v288, &v276);
        v35 = v290;
LABEL_122:
        v214 = 1;
        if ( v233 )
          v233 |= 8u;
        ms_exc.disabled = 1;
        v36 = v166;
        if ( v35 )
        {
          if ( v204 == 32 )
          {
            *(_DWORD *)v166 = v35 | 2;
            if ( v233 & 4 )
            {
              v260 = 0;
              v261 = 0;
            }
          }
          else
          {
            *(_DWORD *)v166 = v35 | 1;
          }
          if ( v292 )
            NtClose(v292);
        }
        else
        {
          *(_DWORD *)v166 = v292;
        }
        *(_DWORD *)(v36 + 4) = v288;
        *(_DWORD *)(v36 + 8) = v260;
        *(_DWORD *)(v36 + 12) = v261;
        v292 = 0;
        v288 = 0;
        ms_exc.disabled = 0;
LABEL_126:
        ms_exc.disabled = -1;
        if ( v197 )
        {
          v269 = 0;
          v268 = 0;
          v131 = __readfsdword(24);
          v37 = RtlFreeHeap;
          RtlFreeHeap(*(_DWORD *)(*(_DWORD *)(v131 + 48) + 24), 0, v197);
          v197 = 0;
        }
        else
        {
          v37 = RtlFreeHeap;
        }
        if ( !v204 )
        {
          BasepSxsCloseHandles(&v221);
          BasepSxsCloseHandles(&v217);
          if ( v181 )
          {
            i = 0;
            do
            {
              v38 = (int *)&(&v138)[4 * i];
              v39 = *v38;
              if ( *v38 )
              {
                v40 = v39 + 8;
                if ( v39 != -8 && *(_DWORD *)v40 )
                {
                  if ( *(_DWORD *)(v39 + 8) != *(_DWORD *)(v39 + 12) )
                  {
                    v133 = *(_DWORD *)v40;
                    RtlFreeUnicodeString(&v132);
                  }
                  v41 = *v38;
                  *(_DWORD *)(*v38 + 8) = *(_DWORD *)(*v38 + 12);
                  *(_DWORD *)(v41 + 16) = *(_DWORD *)(v41 + 20);
                }
                v42 = *(_DWORD *)(*v38 + 12);
                *(_DWORD *)(*v38 + 4) = v42;
                if ( v42 )
                  **(_WORD **)(v39 + 4) = 0;
                v43 = *v38;
                *(_WORD *)v43 = 0;
                *(_WORD *)(v43 + 2) = *(_WORD *)(v43 + 20);
              }
              ++i;
            }
            while ( i != 5 );
            v92 = __readfsdword(24);
            RtlFreeHeap(*(_DWORD *)(*(_DWORD *)(v92 + 48) + 24), 0, v181);
            v37 = RtlFreeHeap;
          }
        }
        if ( v234 && !(BYTE1(v327) & 4) )
        {
          RtlDestroyEnvironment(v234);
          v234 = 0;
        }
        v129 = __readfsdword(24);
        v37(*(_DWORD *)(*(_DWORD *)(v129 + 48) + 24), 0, Dest);
        v107 = __readfsdword(24);
        v37(*(_DWORD *)(*(_DWORD *)(v107 + 48) + 24), 0, Str1);
        v127 = __readfsdword(24);
        v37(*(_DWORD *)(*(_DWORD *)(v127 + 48) + 24), 0, v170);
        v106 = __readfsdword(24);
        v37(*(_DWORD *)(*(_DWORD *)(v106 + 48) + 24), 0, v198);
        if ( v289 )
          NtClose(v289);
        if ( v291 )
          NtClose(v291);
        if ( v288 )
        {
          NtTerminateProcess(v292, 0);
          NtClose(v288);
        }
        if ( v292 )
          NtClose(v292);
        if ( v184 )
          NtClose(v184);
        if ( v183 )
        {
          if ( v185 )
            *(_DWORD *)v148 = v183;
          else
            NtClose(v183);
        }
        if ( v284 )
        {
          v125 = __readfsdword(24);
          v37(*(_DWORD *)(*(_DWORD *)(v125 + 48) + 24), 0, v284);
        }
        if ( v285 )
        {
          v105 = __readfsdword(24);
          v37(*(_DWORD *)(*(_DWORD *)(v105 + 48) + 24), 0, v285);
        }
        RtlFreeUnicodeString(&v266);
        result = RtlFreeUnicodeString(&v270);
        if ( v265 || v263 )
          result = BaseDestroyVDMEnvironment(&v264, &v262);
        if ( v233 )
        {
          if ( !(v233 & 8) )
          {
            result = BaseUpdateVDMEntry(0, &v287, v233, v204);
            if ( v290 )
              result = NtClose(v290);
          }
        }
        return result;
      }
      v178 = _wcslen(Source);
      if ( !v178 )
      {
        Source = (wchar_t *)Size;
        v178 = _wcslen((const wchar_t *)Size);
      }
      v71 = _wcslen((const wchar_t *)&v325);
      v72 = 2 * (v178 + v71 + 3);
      v178 = v72;
      v124 = __readfsdword(24);
      v272 = (wchar_t *)RtlAllocateHeap(*(_DWORD *)(*(_DWORD *)(v124 + 48) + 24), BaseDllTag, v72);
      v270 = 0;
      v271 = v72;
      RtlAppendUnicodeToString(&v270, &v325);
      RtlAppendUnicodeToString(&v270, L" ");
      RtlAppendUnicodeToString(&v270, Source);
      Source = v272;
      Size = 0;
      NtClose(v291);
      v291 = 0;
      v122 = __readfsdword(24);
      RtlFreeHeap(*(_DWORD *)(*(_DWORD *)(v122 + 48) + 24), 0, Str1);
      Str1 = 0;
      v120 = __readfsdword(24);
      RtlFreeHeap(*(_DWORD *)(*(_DWORD *)(v120 + 48) + 24), 0, v198);
      v198 = 0;
    }
  }
  if ( !RtlIsDosDeviceName_U(v15) )
    goto LABEL_179;
  v83 = 1200;
LABEL_278:
  SetLastError(v83);
LABEL_173:
  v81 = -1;
LABEL_174:
  _local_unwind2(&ms_exc.prev_er, v81);
  return 0;
}
View Code

1. 函数的一开始是一大段临时变量声明,然后将从CreateProcessInternalA传过来的参数保存到局部变量中。

复制代码
     ...
  v185 = var_0;
  Size = lpApplicationName;
  Source = lpCommandLine;
  v169 = lpProcessAttributes;
  v164 = lpThreadAttributes;
  v234 = lpEnvironment;
  lpFileName = lpCurrentDirectory;
  v144 = lpStartupInfo;
  v166 = lpProcessInformation;
  v148 = var_0_;
  v290 = 0;
  v201 = 0;
  v287 = 0;
  v233 = 0;
  v204 = 0;
  v160 = 0;
  v146 = dwCreationFlags & 0x8000000;
      ...
复制代码
Ps: 插个题外话: v146 = dwCreationFlags & 0x8000000;这句话的意思是判断dwCreationFlags的最高位是不是1,这里使用了"与运算符"来达到目的

 

 

2. 对lpProcessInformation字段进行初始化赋值,lpProcessInformation字段是我们传入的一个数据结构的引用,操作系统在创建完进程之后,会在这个数据结构中填入关于"这次"创建的进程的一些相关信息

*(_DWORD *) lpProcessInformation = 0;
*(_DWORD *)(lpProcessInformation + 4) = 0;
*(_DWORD *)(lpProcessInformation + 8) = 0;
*(_DWORD *)(lpProcessInformation + 12) = 0;
A pointer to a PROCESS_INFORMATION structure that receives identification information about the new process.

关于这个数据结构的定义如下:

复制代码
typedef struct _PROCESS_INFORMATION 
{
  HANDLE hProcess;
  HANDLE hThread;
  DWORD  dwProcessId;
  DWORD  dwThreadId;
} PROCESS_INFORMATION, *LPPROCESS_INFORMATION;
复制代码

 

3. dwCreationFlags字段的检测和判断

dwCreationFlags 的值至少由一个标志组合成(一些创建标志和优先级类型)。

3.1 首先屏蔽 CREATE_NO_WINDOW 标志,代码如下:

v29 = dwCreationFlags & 0xF7FFFFFF;
v327 = v29;

因为: CREATE_NO_WINDOW = 0x08000000,所以,为了将这个宏代表的0x08000000给置0,所以采用了0xF7FFFFFF,这也是一种"位操作"的思想。

 

3.2 判断dwCreationFlags中的非法位组合

经过屏蔽标志位后,判断dwCreationFlags中是否包含CREATE_NEW_CONSOLE | DETACHED_PROCESS的组合, 如果包含它们的组合(参考MSDN上的说明), 存在这种组合是不合法的, 因此跳转到错误处理中:

if ( (v29 & 0x18) == 24 ) //24 = DETACHED_PROCESS | CREATE_NEW_CONSOLE
    goto LABEL_279;

我们继续跟踪错误代码 LABEL_279:

LABEL_279:
    SetLastError(0x57u);
    return 0;

继续跟进SetLastError()函数.

复制代码
void __stdcall SetLastError(DWORD dwErrCode)
{
  unsigned __int32 v1; // edi@1

  v1 = __readfsdword(24);
  if ( g_dwLastErrorToBreakOn && dwErrCode == g_dwLastErrorToBreakOn )
    DbgBreakPoint();
  if ( *(_DWORD *)(v1 + 52) != dwErrCode )
    *(_DWORD *)(v1 + 52) = dwErrCode;
}
复制代码

将57h与Teb->LastErrorValue想比较,如果不相等,就更新LastErrorValue的值为57h, 实际上GetLastError() 函数的返回的错误码就是从Teb->LastErrorValue获取的。

 

3.3 判断dwCreationFlags中的优先级类型的组合

在一开始提到判断dwCreationFlags中包含了"创建标志"和"优先级",接下来代码先判断优先级,我们接着分析:

复制代码
.text:7C8199A6     mov     [ebp+var_6D4], ebx
.text:7C8199AC     mov     [ebp+var_6DC], ebx
.text:7C8199B2     test    al, 40h
.text:7C8199B4     jnz     IsIdlePriority  //优先级为IDLE_PRIORITY_CLASS
.text:7C8199B4
.text:7C8199BA     test    ah, 40h
.text:7C8199BD     jnz     loc_7C84278E
.text:7C8199BD
.text:7C8199C3     test    al, 20h
.text:7C8199C5     jnz     IsNormalPriority  //优先级为NORMAL_PRIORITY_CLASS
.text:7C8199C5
.text:7C8199CB     test    ah, ah
.text:7C8199CD     js      loc_7C84279A
.text:7C8199CD
.text:7C8199D3     test    al, al
.text:7C8199D5     js      IsHighPriotity   //优先级为HIGH_PRIORITY_CLASS
.text:7C8199D5
.text:7C8199DB     test    ah, 1
.text:7C8199DE     jnz     IsRealTimePriority  //优先级为REALTIME_PRIORITY_CLASS
复制代码

判断的顺序依次是

IDLE_PRIORITY_CLASS -> NORMAL_PRIORITY_CLASS -> HIGH_PRIORITY_CLASS -> REALTIME_PRIORITY_CLASS 

只要满足其中一个优先级,就跳过其他优先级的判断,如果都不满足, 将权限级置为0.

(当满足IDLE_PRIORITY_CLASS时),置优先级标志为1.
(当满足NORMAL_PRIORITY_CLASS时),置优先级标志为2.
(当满足HIGH_PRIORITY_CLASS时),置优先级标志3.
(当满足REALTIME_PRIORITY_CLASS时),由于该优先级别很高,比操作系统优先级还高(参考MSDN),作了如下操作, 申请堆空间 -->打开一个令牌对象与线程关联,并返回一个句柄可用于访问
该令牌。-->调整优先级令牌。 置优先级标志4。

这里回想一点我们之前说的关于进程创建时如果指定了过高的优先级时,操作系统是怎么处理的:

3. 如果为新进程指定了Real-Time优先级类别,但是该进程的调用者没有"Increase Scheduling Priority(增加调度优先级)"特权,则使用High优先级类别。换句话说,CreateProcess
不会仅仅因为调用者没有足够的特权来创建Real-Time优先级类别的进程而失败,而是自动"降低"一点,新进程只是没有Real-Time那么高的优先级而已

 

3.4 判断dwCreationFlags中的创建标志的组合

继续判断dwCreationFlag的情况,首先在dwCreationFlag过滤掉表示优先级的标志位,然后在判断是什么创建标志。用与运算符把所有不属于创建标志的位都置0

LOWORD(dwCreationFlagsa) = dwCreationFlagsa & 0x3E1F;

判断CREATE_SEPARATE_WOW_VDM / CREATE_SHARED_WOW_VDM(这两个位是用来表示16bit的windows程序)

复制代码
.text:7C8199F6      mov     edi, 800h
.text:7C8199FB      mov     esi, 1000h
.text:7C819A00      test    [ebp+dwCreationFlags], edi
.text:7C819A03      jnz     loc_7C8427CA   //dwCreationFlag = CREATE_SEPARATE_WOW_VDM 
.text:7C819A03
.text:7C819A09      test    [ebp+dwCreationFlags], esi
.text:7C819A0C      jnz     short loc_7C819A1F   //dwCreationFlag = CREATE_SHARED_WOW_VDM
.text:7C819A0C
.text:7C819A0E      mov     eax, _BaseStaticServerData
.text:7C819A13      cmp     [eax+19F4h], bl
.text:7C819A19      jnz     loc_7C8427D4
复制代码

 

 

4.  判断lpEnvironment,和可执行程序的路径相关的处理

判断lpEnvironment是否为空, 如果不为空,将lpEnvironment对应的ANSI字符串转换为UNICODE_STRING, 为空的话跳过这一步,接着调用RtlDosPathNameToNtPathName_U函数将DOS路径转换为NT路径,由于用户给定的路径一般都是DOS路径,而内核需要的是NT路径,因此需要转换一下。

复制代码
if ( !lpEnvironment_ || BYTE1(dwCreationFlagsa) & 4 )  //判断lpEnvironment是否为空
{
    ...
    v13 = NtAllocateVirtualMemory(-1, &v161, 0, &v88, 4096, 4);  //申请存储UNICODE的空间
        ...
    v14 = RtlAnsiStringToUnicodeString(&v159, &v156, 0); //将ANSI转换为UNICODE
    if ( v14 < 0 )
    {
        NtFreeVirtualMemory(-1, &v161, &v88, 32768);  //释放临时的存储空间
        ..
    }
    ..
    goto LABEL_33;
    ...
LABEL_33:
    v15 = Size;
    v167 = RtlDosPathNameToNtPathName_U(Size, &v275, 0, &v239);  //将DOS路径转换为NT路径
复制代码

这个关键函数的声明如下:

复制代码
NTSTATUS  NtAllocateVirtualMemory(
    __in HANDLE  ProcessHandle,
    __inout PVOID  *BaseAddress,
    __in ULONG_PTR  ZeroBits,
    __inout PSIZE_T  RegionSize,
    __in ULONG  AllocationType,
    __in ULONG  Protect
    ); 
复制代码
NTSTATUS RtlAnsiStringToUnicodeString(
    IN OUT PUNICODE_STRING  DestinationString,
    IN PANSI_STRING  SourceString,
    IN BOOLEAN  AllocateDestinationString
    );
RtlDosPathNameToNtPathName_U是一个windows未公开的函数,如果我们自己需要使用,需要自己在头文件中定义
复制代码
typedef  NTSTATUS (*DOSPATH_TO_NTPATH)(
           IN PCWSTR DosPathName,
           OUT PUNICODE_STRING NtPathName,
           OUT PWSTR* FilePathInNtPathName OPTIONAL,
           OUT PRELATIVE_NAME* RelativeName OPTIONAL
           );
DOSPATH_TO_NTPATH RtlDosPathNameToNtPathName_U;
复制代码

 

 

5. 打开文件映像
5.1 接着调用NtOpenFile()得到文件句柄,我们先来看一下这个函数的声明:

复制代码
NTSTATUS NtOpenFile(
    OUT PHANDLE  FileHandle,
    IN ACCESS_MASK  DesiredAccess, //期望的访问属性
    IN POBJECT_ATTRIBUTES  ObjectAttributes, //对象属性
    OUT PIO_STATUS_BLOCK  IoStatusBlock, //I/0状态块
    IN ULONG  ShareAccess,  //共享模式
    IN ULONG  OpenOptions  //打开选项
    );
复制代码
复制代码
v238 = NtOpenFile(&v291, 1048737, &v207, &v252, 5, 96);
if ( v238 < 0 )
{
      v238 = NtOpenFile(&v291, 1048608, &v207, &v252, 5, 96);
      if ( v238 < 0 )
        break;
}
复制代码

 

5.2 创建程序的内存区
接着通过NtOpenFile得到的handle接着调用了NtCreateSectiond函数得到内存区对象句柄,即我们常说的进程用户空间的虚拟地址空间,在这一步完成创建(事实上,这里用创建这个词不是非常恰当,因为进程的内存空间一直4GB,这里做的实际是获得这个内存区对象的句柄,以方便我们之后使用)。

复制代码
NTSTATUS NtCreateSection(
    OUT PHANDLE  SectionHandle,  //指向内存区对象的指针(传出参数)
    IN ACCESS_MASK  DesiredAccess,  //访问掩码
    IN POBJECT_ATTRIBUTES  ObjectAttributes  OPTIONAL, //对象属性
    IN PLARGE_INTEGER  MaximumSize  OPTIONAL,  //SETION大小
    IN ULONG  SectionPageProtection,  //内存区页面保护属性
    IN ULONG  AllocationAttributes,
    IN HANDLE  FileHandle  OPTIONAL  //文件句柄
    ); 
复制代码
v17 = NtCreateSection(&v293, 983071, 0, 0, 16, 16777216, v291);
v238 = v17;
if ( v17 < 0 )
      goto LABEL_425;

 

 

6. 检查windows程序运行授权策略

接着调用BasepIsProcessAllowed函数该函数用来判断应用程序名是否在授权文件列表中,函数实现调用了NtOpenKey函数打开了注册表中的HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options键

复制代码
v18 = BasepIsProcessAllowed(Size);
v17 = v18;
v238 = v18;
if ( v18 < 0 )
{
      BaseSetLastNTError(v18);
      NtClose(v293);
      goto LABEL_173;
}
复制代码

7. 获取进程内存区对象的基本信息

在得到进程对应的内存区对象句柄后调用了NtQuerySection函数,返回后得到节的基本信息(节基地址,大小,属性),中间的那一大段代码咱们可以忽略,我通读之后,发现只是一些无关紧要的内存区的申请,释放操作,还有和前面说的win16的VDM相关的代码(在win32下不会执行),所以可以掠过。

我们直接来到NtQuerySection函数这个代码块,先来看看这个函数的声明:

复制代码
//将内存区对象作为"镜像执行"文件来查询信息
typedef DWORD ( WINAPI* NTQUERYSECTION)(
            HANDLE, //内存区句柄
            ULONG,  //edi = 1 = SectionImageInformation
            PVOID,  //接受内存区信息Buffer
            ULONG,    //内存区信息的长度
            PULONG  //接受返回的大小
        );  
NTQUERYSECTION NtQuerySection;                                              
复制代码
v23 = NtQuerySection(v293, 1, &v189, 48, 0);
v238 = v23;
if ( v23 < 0 )
        goto LABEL_242;

 

 

8. 判断映像文件(刚才加载到进程内存对象区域中的映像文件)信息的有效性

8.1 然后判断创建标志中是否包含DEBUG_PROCESS或者DEBUG_ONLY_THIS_PROCESS

如果包含该标志,判断PEB->ReadImageFileExecOptions域是否为0,如果不为0, 调用LdrQueryImageFileExecutionOptions函数查询该信息,如果不包含该标志,也用调用LdrQueryImageFileExecutionOptions函数。

if ( !(dwCreationFlagsa & 3) || (v128 = __readfsdword(24), *(_BYTE *)(*(_DWORD *)(v128 + 48) + 1)) )
//DEBUG_PROCESS OR DEBUG_ONLY_THIS_PROCESS = 3
        LdrQueryImageFileExecutionOptions(&v275, L"Debugger", 1, &v327, 520, 0);

 

8.2 下面检查镜像文件的部分信息的有效性

复制代码
if ( MachineType < v7FFE002C || MachineType > v7FFE002E )
{
    //MachineType为机器类型,
    ..
}
if ( subsystem_machineType != 2 && subsystem_machineType != 3 )
{
    //子系统版本号 2: 控制台  3: GUI
    ..  
    if ( subsystem_machineType != 7 )
    {
    ..
    }
        if ( !BuildSubSysCommandLine(L"POSIX /P ", Size, Source, &v272) )
          goto LABEL_173;
        goto LABEL_215;
}
复制代码

接着调用函数BasepIsImageVersionOk判断镜像文件版本是否合法

if ( !BasepIsImageVersionOk(subsystem_major_type, subsystem_minor_type) )
//subsystem_major_type: 子系统的主版本号
//subsystem_minor_type: 子系统的次版本号
        goto LABEL_277;

 

 

9. 加载advapi32.dll
如果创建标志中是否包含DEBUG_PROCESS或者DEBUG_ONLY_THIS_PROCESS(即当前处在调试模式中),就载advapi32.dll并获得CreateProcessAsUserSecure函数的地址:

复制代码
if ( !v327 )
{
        v24 = LoadLibraryA("advapi32.dll");
    ..
        if ( v24 )
        {
          if ( GetProcAddress(v24, "CreateProcessAsUserSecure") )
          {
            v238 = NtQuerySystemInformation(71, &v279, 4, 0);
        ..
          }
          FreeLibrary(v25);
        }
复制代码

 

(Ps: 进程分析的过程非常之繁杂的琐碎,很容易让你陷入代码的细节而不能自拔,小瀚建议朋友们每分析一段时间后就翻到前面去仔细看看"进程创建流程"的概览,不断地提醒自己同时从宏观
和微观的角度去看待这个过程,这样,不至于迷失在windows的代码的海洋中,自己也可以准备一张进程创建的纵览图,补充着看)

 

10. NT对象属性的创建

然后调用BaseFormatObjectAttributes将安全属性结构(关于进程安全、权限方面的信息,例如普通应用程序如果想要删除systrem32\calc.exe就必须利用这个字段来提升权限到TrustedInstaller(win7下))格式为NT对象属性结构(得到了对象属性)。

typedef struct _SECURITY_ATTRIBUTES {
  DWORD  nLength;
  LPVOID lpSecurityDescriptor;
  BOOL   bInheritHandle;
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
..
v188 = BaseFormatObjectAttributes(&v207, v171, 0);
..

 

接着调用了_DbgUiConnectToDbg在实现通过调用NtCreateDebugObject函数来创建调试对象,调用DbgUiGetThreadDebugObject来获得调试对象(作为参数传递到0环)。

 

11. 创建调试对象

接着调用了_DbgUiConnectToDbg在实现通过调用NtCreateDebugObject函数来创建调试对象,然后调用DbgUiGetThreadDebugObject来获得调试对象(作为参数传递到0环)。

复制代码
if ( dwCreationFlagsa & 3 )
{
          v23 = DbgUiConnectToDbg();
          v238 = v23;
          if ( v23 < 0 )
            goto LABEL_242;
          v164 = DbgUiGetThreadDebugObject();
    ...
}
复制代码

 

 

12. 最后一步,准备进行模式穿越

最后调用NtCreateProcessEx函数完成3环的创建过程,我们先来看看NtCreateProcessEx的声明:

复制代码
NTSYSAPI NTSTATUS NTAPI NtCreateProcessEx(
                OUT PHANDLE ProcessHandle,  //保留进程句柄值(传出参数)
                IN ACCESS_MASK DesiredAccess,  //(访问掩码)
                IN POBJECT_ATTRIBUTES ObjectAttributes,  //对象属性
                IN HANDLE InheritFromProcessHandle,  //父进程句柄(FFFFFFFF)
                IN BOOLEAN InheritHandles,   //创建标志
                IN HANDLE SectionHandle OPTIONAL,  //内存区对象句柄
                IN HANDLE DebugPort OPTIONAL,  //调试对象句柄
                IN HANDLE ExceptionPort OPTIONAL,  //异常端口对象句柄
                IN HANDLE Unknown  //作业级别
    );
复制代码
v27 = NtCreateProcessEx(&v294, 2035711, v188, -1, v197, v293, v164, 0, v158);

这里要注意一点,这个函数NtCreateProcessEx是一个ntdll.dll导出的存根函数,它是我们从用户模式穿越进内核层的一条"通道"

 

 

 

总结Ring3层

1.在Ring3层  CreateProcess已经打开一个有效的Windows可执行文件 并且创建了一个内存区对象,以便稍后将他映射到进程地址空间中

2.接下来Ring3代码会调用NtCreateProcessEx,来创建一个"Windows执行体进程对象",以运行该"进程映像"。

 

创建执行体进程EPROCESS(过程自然也包含了创建内核层进程对象KPROCESS)的大致过程

1. 建立起EPROCESS块
2. 创建初始的进程地址空间
3. 初始化内核进程块(KPROCESS)
4. 结束进程地址空间的创建过程(包括初始化工作集列表和虚拟地址空间描述符,以及将映像映射到地址空间中)
5. 建立PEB
6. 完成执行体进程对象的创建过程
 1 NTSTATUS
 2 NtCreateProcessEx(
 3     __out PHANDLE ProcessHandle,    //输出参数
 4     __in ACCESS_MASK DesiredAccess,    //新进程的访问权限
 5     __in_opt POBJECT_ATTRIBUTES ObjectAttributes,//进程对象属性
 6     __in HANDLE ParentProcess,//父进程
 7     __in ULONG Flags,//创建标志
 8     __in_opt HANDLE SectionHandle,//该进程的内存区对象
 9     __in_opt HANDLE DebugPort,//调试端口
10     __in_opt HANDLE ExceptionPort,//异常端口
11     __in ULONG JobMemberLevel    //级别
12     )
13 
14 /*++
15 
16 Routine Description:
17 
18     This routine creates a process object.
19 
20 Arguments:
21 
22     ProcessHandle - Returns the handle for the new process.
23 
24     DesiredAccess - Supplies the desired access modes to the new process.
25 
26     ObjectAttributes - Supplies the object attributes of the new process.
27     .
28     .
29     .
30 
31 --*/
32 
33 {
34     NTSTATUS Status;
35 
36     PAGED_CODE();
37 
38     if (KeGetPreviousMode() != KernelMode) {
39 
40         //
41         // Probe all arguments    判断了线程的前一个模式是不是"内核态(KernelMode)"
42         //
43 
44         try {
45             ProbeForWriteHandle (ProcessHandle);
46         } except (EXCEPTION_EXECUTE_HANDLER) {
47             return GetExceptionCode ();
48         }
49     }
50 
51     if (ARGUMENT_PRESENT (ParentProcess)) {
52         //这是真正的创建进程的函数
53         Status = PspCreateProcess (ProcessHandle,
54                                    DesiredAccess,
55                                    ObjectAttributes,
56                                    ParentProcess,
57                                    Flags,
58                                    SectionHandle,
59                                    DebugPort,
60                                    ExceptionPort,
61                                    JobMemberLevel);
62     } else {
63         Status = STATUS_INVALID_PARAMETER;
64     }
65 
66     return Status;
67 }
base\ntos\ps\create.c
  1 /*
  2 PspCreateProcess 会被3个函数调用 NtCreateProcessEx  PsCreateSystemProcess  PsInitPhase
  3 
  4 1. PsInitPhase()是在系统初始化的早期被调用的,
  5 它创建的进程(即System进程)的句柄保存在全局变量PspInitialSystemProcessHandle中,
  6 进程对象存放于全局变量 PsInitialSystemProcess中
  7 2. PsCreateSystemProcess()可用于创建系统进程对象,它创建的进程都是PsInitialSystemProcess的子进程
  8 3. NtCreateProcessEx()  这个NtCreateProcessEx也有可能会从ring3或ring0发出,
  9 */
 10 NTSTATUS
 11 PspCreateProcess(
 12     OUT PHANDLE ProcessHandle,
 13     IN ACCESS_MASK DesiredAccess,
 14     IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
 15     IN HANDLE ParentProcess OPTIONAL,
 16     IN ULONG Flags,
 17     IN HANDLE SectionHandle OPTIONAL,
 18     IN HANDLE DebugPort OPTIONAL,
 19     IN HANDLE ExceptionPort OPTIONAL,
 20     IN ULONG JobMemberLevel
 21     )
 22 
 23 /*++
 24 
 25 Routine Description:
 26 
 27     This routine creates and initializes a process object.  It implements the
 28     foundation for NtCreateProcess and for system initialization process
 29     creation.
 30 
 31 Arguments:
 32 
 33     ProcessHandle - Returns the handle for the new process.
 34 
 35     DesiredAccess - Supplies the desired access modes to the new process.
 36 
 37     ObjectAttributes - Supplies the object attributes of the new process.
 38 
 39     ParentProcess - Supplies a handle to the process\' parent process.  If this
 40                     parameter is not specified, then the process has no parent
 41                     and is created using the system address space.
 42 
 43     Flags         - Process creation flags
 44 
 45     SectionHandle - Supplies a handle to a section object to be used to create
 46                     the process\' address space.  If this parameter is not
 47                     specified, then the address space is simply a clone of the
 48                     parent process\' address space.
 49 
 50     DebugPort - Supplies a handle to a port object that will be used as the
 51                 process\' debug port.
 52 
 53     ExceptionPort - Supplies a handle to a port object that will be used as the
 54                     process\' exception port.
 55 
 56     JobMemberLevel - Level for a create process in a jobset
 57 
 58 --*/
 59 
 60 {
 61 
 62     NTSTATUS Status;
 63     PEPROCESS Process;
 64     PEPROCESS CurrentProcess;
 65     PEPROCESS Parent;
 66     PETHREAD CurrentThread;
 67     KAFFINITY Affinity;
 68     KPRIORITY BasePriority;
 69     PVOID SectionObject;
 70     PVOID ExceptionPortObject;
 71     PVOID DebugPortObject;
 72     ULONG WorkingSetMinimum, WorkingSetMaximum;
 73     HANDLE LocalProcessHandle;
 74     KPROCESSOR_MODE PreviousMode;
 75     INITIAL_PEB InitialPeb;
 76     BOOLEAN CreatePeb;
 77     ULONG_PTR DirectoryTableBase[2];
 78     BOOLEAN AccessCheck;
 79     BOOLEAN MemoryAllocated;
 80     PSECURITY_DESCRIPTOR SecurityDescriptor;
 81     SECURITY_SUBJECT_CONTEXT SubjectContext;
 82     NTSTATUS accesst;
 83     NTSTATUS SavedStatus;
 84     ULONG ImageFileNameSize;
 85     HANDLE_TABLE_ENTRY CidEntry;
 86     PEJOB Job;
 87     PPEB Peb;
 88     AUX_ACCESS_DATA AuxData;
 89     PACCESS_STATE AccessState;
 90     ACCESS_STATE LocalAccessState;
 91     BOOLEAN UseLargePages;
 92     SCHAR QuantumReset;
 93 #if defined(_WIN64)
 94     INITIAL_PEB32 InitialPeb32;
 95 #endif
 96 
 97     PAGED_CODE();
 98 
 99     CurrentThread = PsGetCurrentThread ();
100     PreviousMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
101     CurrentProcess = PsGetCurrentProcessByThread (CurrentThread);
102 
103     CreatePeb = FALSE;
104     UseLargePages = FALSE;
105     DirectoryTableBase[0] = 0;
106     DirectoryTableBase[1] = 0;
107     Peb = NULL;
108     
109     //
110     // Reject bogus create parameters for future expansion
111     //
112     if (Flags&~PROCESS_CREATE_FLAGS_LEGAL_MASK) {
113         return STATUS_INVALID_PARAMETER;
114     }
115 
116     //
117     // Parent    
118     //    如果父进程句柄不为NULL,则通过ObReferenceObjectByHandle获得父进程对象的EPROCESS指针,
119     //    放在Parent局部变量中
120     //
121 
122     if (ARGUMENT_PRESENT (ParentProcess)) {
123         Status = ObReferenceObjectByHandle (ParentProcess,
124                                             PROCESS_CREATE_PROCESS,
125                                             PsProcessType,
126                                             PreviousMode,
127                                             &Parent,
128                                             NULL);
129         if (!NT_SUCCESS (Status)) {
130             return Status;
131         }
132 
133         if (JobMemberLevel != 0 && Parent->Job == NULL) {
134             ObDereferenceObject (Parent);
135             return STATUS_INVALID_PARAMETER;
136         }
137         /*
138         获得父进程的Affinity(CPU亲和性)设置,新进程工作集最大/最小值被初始化为
139         全局变量PsMaximumWorkingSet和PsMinimumWorkingSet
140         */
141         Affinity = Parent->Pcb.Affinity;
142         WorkingSetMinimum = PsMinimumWorkingSet;
143         WorkingSetMaximum = PsMaximumWorkingSet;
144 
145 
146     } else {
147         /*
148         如果父进程句柄为NULL,则Affinity设置为全局变量KeActiveProcessors,
149         即系统中当前的可用处理器。此时因为新进程对象尚未被创建,所以这些设置都保存在局部变量中
150         */
151 
152         Parent = NULL;
153         Affinity = KeActiveProcessors;
154         WorkingSetMinimum = PsMinimumWorkingSet;
155         WorkingSetMaximum = PsMaximumWorkingSet;
156     }
157 
158     //
159     // Create the process object
160     //
161 
162     /*
163     调用ObCreateObject函数,创建一个类型为PsProcessType的"内核对象",
164     并置于局部变量Process中,这里所谓的PsProcessType类型的内核对象指的就是"EPROCESS",
165     即这一步开始创建了一个执行体进程对象
166       */
167     Status = ObCreateObject (PreviousMode,
168                              PsProcessType,
169                              ObjectAttributes,
170                              PreviousMode,
171                              NULL,
172                              sizeof (EPROCESS),
173                              0,
174                              0,
175                              &Process);
176 
177     if (!NT_SUCCESS (Status)) {
178         goto exit_and_deref_parent;
179     }
180 
181     //
182     // The process object is created set to NULL. Errors
183     // That occur after this step cause the process delete
184     // routine to be entered.
185     //
186     // Teardown actions that occur in the process delete routine
187     // do not need to be performed inline.
188     //
189 
190 
191     /*
192     把Process(EPROCESS)对象中的所有域置为0,然后初始化(或者直接继承父进程的对应成员域)
193     其中部分成员(RundownProtect、ProcessLock、ThreadListHead、QuotaUsage、QuotaPeak、QuotaBlock、DeviceMap)
194       */
195     RtlZeroMemory (Process, sizeof(EPROCESS));
196     ExInitializeRundownProtection (&Process->RundownProtect);
197     PspInitializeProcessLock (Process);
198     InitializeListHead (&Process->ThreadListHead);
199 
200 #if defined(_WIN64)
201 
202     if (Flags & PROCESS_CREATE_FLAGS_OVERRIDE_ADDRESS_SPACE) {
203         PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_OVERRIDE_ADDRESS_SPACE);
204     }
205 
206 #endif
207 
208     PspInheritQuota (Process, Parent);
209     ObInheritDeviceMap (Process, Parent);
210 
211     /*
212     如果父进程句柄不为NULL,则将新进程的Process(EPROCESS)继承父进程的DefaultHardErrorProcessing
213     (默认的硬件错误处理)、InheritedFromUniqueProcessId(父进程的PID)
214       */
215     if (Parent != NULL) {
216         Process->DefaultHardErrorProcessing = Parent->DefaultHardErrorProcessing;
217         Process->InheritedFromUniqueProcessId = Parent->UniqueProcessId;
218 
219     } else {
220         Process->DefaultHardErrorProcessing = PROCESS_HARDERROR_DEFAULT;
221         Process->InheritedFromUniqueProcessId = NULL;
222     }
223 
224     //
225     // Section
226     //
227 
228     /*
229     检查内存区句柄参数SectionHandle,对于系统进程,此参数为NULL,
230     此时,除非父进程为PsInitialSystemProcess(System进程),否则内存区对象继承自父进程,并且不得为NULL。
231     如果此参数不为NULL,则利用此句柄参数调用ObReferenceObjectByHandle获得内存区对象的指针。所以,新进程对象的内存区对象已经完成初始化。
232     通过总体路线,我们知道下一步基本是要创建"进程内核对象KPROCESS"了,基本的代码模式我们也能猜测出来了
233      */
234 
235     if (ARGUMENT_PRESENT (SectionHandle)) {
236         Status = ObReferenceObjectByHandle (SectionHandle,
237                                             SECTION_MAP_EXECUTE,
238                                             MmSectionObjectType,
239                                             PreviousMode,
240                                             &SectionObject,
241                                             NULL);
242         if (!NT_SUCCESS (Status)) {
243             goto exit_and_deref;
244         }
245 
246     } else {
247         SectionObject = NULL;
248         if (Parent != PsInitialSystemProcess) {
249 
250             //
251             // Fetch the section pointer from the parent process
252             // as we will be cloning. Since the section pointer
253             // is removed at last thread exit we need to protect against
254             // process exit here to be safe.
255             //
256 
257             if (ExAcquireRundownProtection (&Parent->RundownProtect)) {
258                 SectionObject = Parent->SectionObject;
259                 if (SectionObject != NULL) {
260                     ObReferenceObject (SectionObject);
261                 }
262 
263                 ExReleaseRundownProtection (&Parent->RundownProtect);
264             }
265 
266             if (SectionObject == NULL) {
267                 Status = STATUS_PROCESS_IS_TERMINATING;
268                 goto exit_and_deref;
269             }
270         }
271     }
272     /*
273     完成新进程对象的内存区对象初始化为新进程的EPROCESS成员域赋值
274     */
275     Process->SectionObject = SectionObject;
276 
277     //
278     // DebugPort
279     //
280     /*
281     根据DebugPort参数来初始化新进程对象的DebugPort(调试端口)成员域
282     */
283 
284     if (ARGUMENT_PRESENT (DebugPort)) {
285         Status = ObReferenceObjectByHandle (DebugPort,
286                                             DEBUG_PROCESS_ASSIGN,
287                                             DbgkDebugObjectType,
288                                             PreviousMode,
289                                             &DebugPortObject,
290                                             NULL);
291 
292         if (!NT_SUCCESS (Status)) {
293             goto exit_and_deref;
294         }
295 
296         Process->DebugPort = DebugPortObject;
297         if (Flags&PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT) {
298             PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_NO_DEBUG_INHERIT);
299         }
300 
301     } else {
302         if (Parent != NULL) {
303             DbgkCopyProcessDebugPort (Process, Parent);
304         }
305     }
306 
307     //
308     // ExceptionPort
309     //
310     /*
311     根据ExceptionPort参数来初始化新进程对象的ExceptionPort(异常端口)成员域
312     */
313     if (ARGUMENT_PRESENT (ExceptionPort)) {
314         Status = ObReferenceObjectByHandle (ExceptionPort,
315                                             0,
316                                             LpcPortObjectType,
317                                             PreviousMode,
318                                             &ExceptionPortObject,
319                                             NULL);
320 
321         if (!NT_SUCCESS (Status)) {
322             goto exit_and_deref;
323         }
324 
325         Process->ExceptionPort = ExceptionPortObject;
326     }
327     /*
328     设置新进程的退出状态,这里设置为STATUS_PENDING表示尚未完成,正在处理ing
329     */
330     Process->ExitStatus = STATUS_PENDING;
331 
332     //
333     // Clone parent\'s object table.
334     // If no parent (booting) then use the current object table created in
335     // ObInitSystem.
336     //
337 
338     /*
339     如果指定的父进程为NULL(即系统进程,系统进程是没有父进程的说法的),
340     让新进程的句柄表指向当前进程(CurrentProcess)的句柄表,并且利用空闲线程的地址空间来初始化新进程的地址空间
341       */
342 
343     if (Parent != NULL) {
344 
345         //
346         // Calculate address space
347         //
348         //      If Parent == PspInitialSystem
349         //
350 
351         if (!MmCreateProcessAddressSpace (WorkingSetMinimum,
352                                           Process,
353                                           &DirectoryTableBase[0])) {
354 
355             Status = STATUS_INSUFFICIENT_RESOURCES;
356             goto exit_and_deref;
357         }
358 
359     } else {
360         Process->ObjectTable = CurrentProcess->ObjectTable;
361 
362         //
363         // Initialize the Working Set Mutex and address creation mutex
364         // for this "hand built" process.
365         // Normally, the call to MmInitializeAddressSpace initializes the
366         // working set mutex, however, in this case, we have already initialized
367         // the address space and we are now creating a second process using
368         // the address space of the idle thread.
369         //
370 
371         Status = MmInitializeHandBuiltProcess (Process, &DirectoryTableBase[0]);
372         if (!NT_SUCCESS (Status)) {
373             goto exit_and_deref;
374         }
375     }
376 
377     PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_HAS_ADDRESS_SPACE);
378     Process->Vm.MaximumWorkingSetSize = WorkingSetMaximum;
379 
380     /*
381     调用KeInitializeProcess函数来初始化新进程内核对象(KPROCESS)的"基本优先级(BasePriority)"、
382     Affinity(CPU亲和性)、进程页面目录(DirectoryTableBase)和超空间的页帧号
383        
384       */
385     KeInitializeProcess (&Process->Pcb,
386                          NORMAL_BASE_PRIORITY,
387                          Affinity,
388                          &DirectoryTableBase[0],
389                          (BOOLEAN)(Process->DefaultHardErrorProcessing & PROCESS_HARDERROR_ALIGNMENT_BIT));
390 
391     //
392     //  Initialize the security fields of the process
393     //  The parent may be null exactly once (during system init).
394     //  Thereafter, a parent is always required so that we have a
395     //  security context to duplicate for the new process.
396     //
397     /*
398     通过PspInitializeProcessSecurity函数初始化新进程的安全属性,主要是从父进程复制一个令牌
399     */
400     Status = PspInitializeProcessSecurity (Parent, Process);
401     if (!NT_SUCCESS (Status)) {
402         goto exit_and_deref;
403     }
404     /*
405     设置新进程的优先级类别: PROCESS_PRIORITY_CLASS_NORMAL(普通级别)
406     */
407     Process->PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
408     if (Parent != NULL) {
409         if (Parent->PriorityClass == PROCESS_PRIORITY_CLASS_IDLE ||
410             Parent->PriorityClass == PROCESS_PRIORITY_CLASS_BELOW_NORMAL) {
411             Process->PriorityClass = Parent->PriorityClass;
412         }
413 
414         //
415         // if address space creation worked, then when going through
416         // delete, we will attach. Of course, attaching means that the kprocess
417         // must be initialized, so we delay the object stuff till here.
418         //
419         /*
420         初始化新进程的句柄表,若Flags参数中包含了句柄继承标志,
421         则把父进程句柄表中凡是有继承属性的对象拷贝到新进程的句柄表中
422         (回想句柄表的知识: 即使是复制,同一个对象的句柄在不同的进程空间中是不同的)
423         */
424         Status = ObInitProcess ((Flags&PROCESS_CREATE_FLAGS_INHERIT_HANDLES) ? Parent : NULL,
425                                 Process);
426 
427         if (!NT_SUCCESS (Status)) {
428             goto exit_and_deref;
429         }
430 
431     } else {
432         Status = MmInitializeHandBuiltProcess2 (Process);
433         if (!NT_SUCCESS (Status)) {
434             goto exit_and_deref;
435         }
436     }
437 
438     Status = STATUS_SUCCESS;
439     SavedStatus = STATUS_SUCCESS;
440 
441     //
442     // Initialize the process address space
443     // The address space has four possibilities
444     //
445     //      1 - Boot Process. Address space is initialized during
446     //          MmInit. Parent is not specified.
447     //
448     //      2 - System Process. Address space is a virgin address
449     //          space that only maps system space. Process is same
450     //          as PspInitialSystemProcess.
451     //
452     //      3 - User Process (Cloned Address Space). Address space
453     //          is cloned from the specified process.
454     //
455     //      4 - User Process (New Image Address Space). Address space
456     //          is initialized so that it maps the specified section.
457     //
458     /*
459     接下来开始初始化新进程的进程地址空间
460     我们现在已经到了"结束进程地址空间的创建过程(包括初始化工作集列表和虚拟地址空间描述符,以及将映像映射到地址空间中)"这一步
461     */
462     /*
463     接下来初始化新进程的进程地址空间,有三种可能性
464     */
465     if (SectionHandle != NULL) {
466 
467         //
468         // User Process (New Image Address Space). Don\'t specify Process to
469         // clone, just SectionObject.
470         //
471         // Passing in the 4th parameter as below lets the EPROCESS struct contain its image file name, provided that
472         // appropriate audit settings are enabled.  Memory is allocated inside of MmInitializeProcessAddressSpace
473         // and pointed to by ImageFileName, so that must be freed in the process deletion routine (PspDeleteProcess())
474         //
475         /*
476         1. 新进程有新的可执行映像内存区对象SectionObject,调用MmInitializeProcessAddressSpace函数,
477         根据指定的内存区对象来初始化进程地址空间
478         */
479         Status = MmInitializeProcessAddressSpace (Process,
480                                                   NULL,
481                                                   SectionObject,
482                                                   &Flags,
483                                                   &(Process->SeAuditProcessCreationInfo.ImageFileName));
484 
485         if (!NT_SUCCESS (Status)) {
486             goto exit_and_deref;
487         }
488 
489         //
490         // In order to support relocating executables, the proper status
491         // (STATUS_IMAGE_NOT_AT_BASE) must be returned, so save it here.
492         //
493 
494         SavedStatus = Status;
495         CreatePeb = TRUE;
496         UseLargePages = ((Flags & PROCESS_CREATE_FLAGS_LARGE_PAGES) != 0 ? TRUE : FALSE);
497 
498     } else if (Parent != NULL) {
499         if (Parent != PsInitialSystemProcess) {
500             Process->SectionBaseAddress = Parent->SectionBaseAddress;
501 
502             //
503             // User Process ( Cloned Address Space ).  Don\'t specify section to
504             // map, just Process to clone.
505             //
506 
507             /*
508             2. 没有指定映像内存区对象SectionObject,但父进程也并非PsInitialSystemProcess(非NULL)。
509             也调用MmInitializeProcessAddressSpace函数,但根据父进程来初始化进程地址空间。
510                 */
511 
512             Status = MmInitializeProcessAddressSpace (Process,
513                                                       Parent,
514                                                       NULL,
515                                                       &Flags,
516                                                       NULL);
517 
518             if (!NT_SUCCESS (Status)) {
519                 goto exit_and_deref;
520             }
521 
522             CreatePeb = TRUE;
523             UseLargePages = ((Flags & PROCESS_CREATE_FLAGS_LARGE_PAGES) != 0 ? TRUE : FALSE);
524             
525             //
526             // A cloned process isn\'t started from an image file, so we give it the name
527             // of the process of which it is a clone, provided the original has a name.
528             //
529 
530             if (Parent->SeAuditProcessCreationInfo.ImageFileName != NULL) {
531                 ImageFileNameSize = sizeof(OBJECT_NAME_INFORMATION) +
532                                     Parent->SeAuditProcessCreationInfo.ImageFileName->Name.MaximumLength;
533 
534                 Process->SeAuditProcessCreationInfo.ImageFileName =
535                     ExAllocatePoolWithTag (PagedPool,
536                                            ImageFileNameSize,
537                                            \'aPeS\');
538 
539                 if (Process->SeAuditProcessCreationInfo.ImageFileName != NULL) {
540                     RtlCopyMemory (Process->SeAuditProcessCreationInfo.ImageFileName,
541                                    Parent->SeAuditProcessCreationInfo.ImageFileName,
542                                    ImageFileNameSize);
543 
544                     //
545                     // The UNICODE_STRING in the process is self contained, so calculate the
546                     // offset for the buffer.
547                     //
548 
549                     Process->SeAuditProcessCreationInfo.ImageFileName->Name.Buffer =
550                         (PUSHORT)(((PUCHAR) Process->SeAuditProcessCreationInfo.ImageFileName) +
551                         sizeof(UNICODE_STRING));
552 
553                 } else {
554                     Status = STATUS_INSUFFICIENT_RESOURCES;
555                     goto exit_and_deref;
556                 }
557             }
558 
559         } else {
560             /*
561             3. 没有指定映像内存区对象SectionObject,但指定了PsInitialSystemProcess(System进程)作为父进程
562             这对应于系统进程的情形。调用MmInitializeProcessAddressSpace,不指定内存区和父进程,直接初始化。
563             */
564             //
565             // System Process.  Don\'t specify Process to clone or section to map
566             //
567 
568             Flags &= ~PROCESS_CREATE_FLAGS_ALL_LARGE_PAGE_FLAGS;
569             Status = MmInitializeProcessAddressSpace (Process,
570                                                       NULL,
571                                                       NULL,
572                                                       &Flags,
573                                                       NULL);
574 
575             if (!NT_SUCCESS (Status)) {
576                 goto exit_and_deref;
577             }
578 
579             //
580             // In case the image file name of this system process is ever queried, we give
581             // a zero length UNICODE_STRING.
582             //
583 
584             Process->SeAuditProcessCreationInfo.ImageFileName =
585                 ExAllocatePoolWithTag (PagedPool,
586                                        sizeof(OBJECT_NAME_INFORMATION),
587                                        \'aPeS\');
588 
589             if (Process->SeAuditProcessCreationInfo.ImageFileName != NULL) {
590                 RtlZeroMemory (Process->SeAuditProcessCreationInfo.ImageFileName,
591                                sizeof(OBJECT_NAME_INFORMATION));
592             } else {
593                 Status = STATUS_INSUFFICIENT_RESOURCES;
594                 goto exit_and_deref;
595             }
596         }
597     }
598 
599     //
600     // Create the process ID
601     //
602     /*
603     创建进程ID。利用ExCreateHandle函数在CID句柄表中创建一个进程ID项
604     */
605 
606     CidEntry.Object = Process;
607     CidEntry.GrantedAccess = 0;
608     Process->UniqueProcessId = ExCreateHandle (PspCidTable, &CidEntry);
609     if (Process->UniqueProcessId == NULL) {
610         Status = STATUS_INSUFFICIENT_RESOURCES;
611         goto exit_and_deref;
612     }
613 
614     ExSetHandleTableOwner (Process->ObjectTable, Process->UniqueProcessId);
615 
616     //
617     // Audit the process creation.
618     //
619     /*
620     对这次进程创建行为进行审计
621     */
622 
623     if (SeDetailedAuditingWithToken (NULL)) {
624         SeAuditProcessCreation (Process);
625     }
626 
627     //
628     // See if the parent has a job. If so reference the job
629     // and add the process in.
630     //
631 
632     if (Parent) {
633         Job = Parent->Job;
634         /*
635         如果父进程属于一个作业对象,则也加入到父进程所在的作业中,
636         调用PspAddProcessToJob完成操作,注意这里对作业的最大容纳进程数有要求
637         */
638         if (Job != NULL && !(Job->LimitFlags & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)) {
639             if (Flags&PROCESS_CREATE_FLAGS_BREAKAWAY) {
640                 if (!(Job->LimitFlags & JOB_OBJECT_LIMIT_BREAKAWAY_OK)) {
641                     Status = STATUS_ACCESS_DENIED;
642 
643                 } else {
644                     Status = STATUS_SUCCESS;
645                 }
646 
647             } else {
648                 Status = PspGetJobFromSet (Job, JobMemberLevel, &Process->Job);
649                 if (NT_SUCCESS (Status)) {
650                     PACCESS_TOKEN Token, NewToken;
651                     Job = Process->Job;
652                     Status = PspAddProcessToJob (Job, Process);
653 
654                     //
655                     // Duplicate a new process token if one is specified for the job
656                     //
657 
658                     Token = Job->Token;
659                     if (Token != NULL) {
660                         Status = SeSubProcessToken (Token,
661                                                     &NewToken,
662                                                     FALSE,
663                                                     Job->SessionId);
664 
665                         if (!NT_SUCCESS (Status)) {
666                             goto exit_and_deref;
667                         }
668 
669                         SeAssignPrimaryToken (Process, NewToken);    
670                         ObDereferenceObject (NewToken);                    
671                     }
672                 }
673             }
674 
675             if (!NT_SUCCESS (Status)) {
676                 goto exit_and_deref;
677             }
678         }
679     }
680     /*
681     开始创建Peb,创建Peb分为两种情况,创建进程时建立Peb、复制进程时建立Peb
682     我们现在来到了"建立PEB"这一步
683     */
684     if (Parent && CreatePeb) {
685 
686         //
687         // For processes created w/ a section,
688         // a new "virgin" PEB is created. Otherwise,
689         // for forked processes, uses inherited PEB
690         // with an updated mutant.
691         //
692 
693         RtlZeroMemory (&InitialPeb, FIELD_OFFSET(INITIAL_PEB, Mutant));
694 
695         InitialPeb.Mutant = (HANDLE)(-1);
696         InitialPeb.ImageUsesLargePages = (BOOLEAN) UseLargePages;
697             
698         if (SectionHandle != NULL) {
699 
700             /*
701             1. 对于通过映像内存区对象SectionObject来创建进程的情形,调用MmCreatePeb来创建一个Peb,
702             创建数据结构的基本模式都是类似的:申请空间->初始化刚才申请的空间->为数据结构中的指定成员域赋值
703                     */
704             Status = MmCreatePeb (Process, &InitialPeb, &Process->Peb);
705             if (!NT_SUCCESS (Status)) {
706                 Process->Peb = NULL;
707                 goto exit_and_deref;
708             }
709 
710             Peb =  Process->Peb;
711 
712         } else {
713             /*
714             2. 对于进程拷贝(fork)的情形,则使用从父进程继承而来的Peb
715             (从这里也可以明白为什么说子进程继承了大部分的父进程的代码空间,从源代码上可以找到理论依据)
716             */
717             SIZE_T BytesCopied;
718 
719             InitialPeb.InheritedAddressSpace = TRUE;
720             Process->Peb = Parent->Peb;
721             MmCopyVirtualMemory (CurrentProcess,
722                                  &InitialPeb,
723                                  Process,
724                                  Process->Peb,
725                                  sizeof (INITIAL_PEB),
726                                  KernelMode,
727                                  &BytesCopied);
728 
729 #if defined(_WIN64)
730             if (Process->Wow64Process != NULL) {
731                 
732                 RtlZeroMemory (&InitialPeb32, FIELD_OFFSET(INITIAL_PEB32, Mutant));
733                 InitialPeb32.Mutant = -1;
734                 InitialPeb32.InheritedAddressSpace = TRUE;
735                 InitialPeb32.ImageUsesLargePages = (BOOLEAN) UseLargePages;
736 
737                 MmCopyVirtualMemory (CurrentProcess,
738                                      &InitialPeb32,
739                                      Process,
740                                      Process->Wow64Process->Wow64,
741                                      sizeof (INITIAL_PEB32),
742                                      KernelMode,
743                                      &BytesCopied);
744             }
745 #endif
746 
747         }
748     }
749 
750     Peb = Process->Peb;
751 
752     //
753     // Add the process to the global list of processes.
754     //
755 
756     PspLockProcessList (CurrentThread);
757     InsertTailList (&PsActiveProcessHead, &Process->ActiveProcessLinks);
758     /*
759     把新进程加入到全局的进程链表PsActiveProcessHead中,
760     从数据结构的角度理解就是把Process->ActiveProcessLinks这个链表项插入一个双链表中(头尾断链再重新结合)
761     Ps: 回想利用PsActiveProcessHead进行内核中进程枚举的思路
762     */
763     PspUnlockProcessList (CurrentThread);
764     AccessState = NULL;
765     if (!PsUseImpersonationToken) {
766         AccessState = &LocalAccessState;
767         Status = SeCreateAccessStateEx (NULL,
768                                         (Parent == NULL || Parent != PsInitialSystemProcess)?
769                                            PsGetCurrentProcessByThread (CurrentThread) :
770                                            PsInitialSystemProcess,
771                                         AccessState,
772                                         &AuxData,
773                                         DesiredAccess,
774                                         &PsProcessType->TypeInfo.GenericMapping);
775         if (!NT_SUCCESS (Status)) {
776             goto exit_and_deref;
777         }
778     }
779 
780     //
781     // Insert the object. Once we do this is reachable from the outside world via
782     // open by name. Open by ID is still disabled. Since its reachable
783     // somebody might create a thread in the process and cause
784     // rundown.
785     //
786 
787     Status = ObInsertObject (Process,
788                              AccessState,
789                              DesiredAccess,
790                              1,     // bias the refcnt by one for future process manipulations
791                              NULL,
792                              &LocalProcessHandle);
793 
794     if (AccessState != NULL) {
795         SeDeleteAccessState (AccessState);
796     }
797 
798     if (!NT_SUCCESS (Status)) {
799         goto exit_and_deref_parent;
800     }
801 
802     //
803     // Compute the base priority and quantum reset values for the process and
804     // set the memory priority.
805     //
806 
807     ASSERT(IsListEmpty(&Process->ThreadListHead) == TRUE);
808 
809     BasePriority = PspComputeQuantumAndPriority(Process,
810                                                 PsProcessPriorityBackground,
811                                                 &QuantumReset);
812 
813     Process->Pcb.BasePriority = (SCHAR)BasePriority;
814     Process->Pcb.QuantumReset = QuantumReset;
815 
816     //
817     // As soon as a handle to the process is accessible, allow the process to
818     // be deleted.
819     //
820 
821     Process->GrantedAccess = PROCESS_TERMINATE;
822     if (Parent && Parent != PsInitialSystemProcess) {
823         Status = ObGetObjectSecurity (Process,
824                                       &SecurityDescriptor,
825                                       &MemoryAllocated);
826 
827         if (!NT_SUCCESS (Status)) {
828             ObCloseHandle (LocalProcessHandle, PreviousMode);
829             goto exit_and_deref;
830         }
831 
832         //
833         // Compute the subject security context
834         //
835 
836         SubjectContext.ProcessAuditId = Process;
837         SubjectContext.PrimaryToken = PsReferencePrimaryToken(Process);
838         SubjectContext.ClientToken = NULL;
839         AccessCheck = SeAccessCheck (SecurityDescriptor,
840                                      &SubjectContext,
841                                      FALSE,
842                                      MAXIMUM_ALLOWED,
843                                      0,
844                                      NULL,
845                                      &PsProcessType->TypeInfo.GenericMapping,
846                                      PreviousMode,
847                                      &Process->GrantedAccess,
848                                      &accesst);
849 
850         PsDereferencePrimaryTokenEx (Process, SubjectContext.PrimaryToken);
851         ObReleaseObjectSecurity (SecurityDescriptor,
852                                  MemoryAllocated);
853 
854         if (!AccessCheck) {
855             Process->GrantedAccess = 0;
856         }
857 
858         //
859         // It does not make any sense to create a process that can not
860         // do anything to itself.
861         // Note: Changes to this set of bits should be reflected in psquery.c
862         // code, in PspSetPrimaryToken.
863         //
864 
865         Process->GrantedAccess |= (PROCESS_VM_OPERATION |
866                                    PROCESS_VM_READ |
867                                    PROCESS_VM_WRITE |
868                                    PROCESS_QUERY_INFORMATION |
869                                    PROCESS_TERMINATE |
870                                    PROCESS_CREATE_THREAD |
871                                    PROCESS_DUP_HANDLE |
872                                    PROCESS_CREATE_PROCESS |
873                                    PROCESS_SET_INFORMATION |
874                                    STANDARD_RIGHTS_ALL |
875                                    PROCESS_SET_QUOTA);
876 
877     } else {
878         Process->GrantedAccess = PROCESS_ALL_ACCESS;
879     }
880 
881     KeQuerySystemTime (&Process->CreateTime);
882     try {
883         if (Peb != NULL && CurrentThread->Tcb.Teb != NULL) {
884             ((PTEB)(CurrentThread->Tcb.Teb))->NtTib.ArbitraryUserPointer = Peb;
885         }
886 
887         *ProcessHandle = LocalProcessHandle;
888 
889     } except (EXCEPTION_EXECUTE_HANDLER) {
890         NOTHING;
891     }
892 
893     if (SavedStatus != STATUS_SUCCESS) {
894         Status = SavedStatus;
895     }
896 
897 exit_and_deref:
898     ObDereferenceObject (Process);
899 
900 exit_and_deref_parent:
901     if (Parent != NULL) {
902         ObDereferenceObject (Parent);
903     }
904 
905     return Status;
906 }
base\ntos\ps\create.c

 

1. 分配并初始化windows EPROCESS执行体进程块
2. 从父进程处继承得到进程的亲和性掩码
3. 新进程的最小和最大工作集尺寸被分别设置为PsMinimumWorkingSet和PsMaximumWorkingSet
4. 将新进程的配额块设置为其父进程配额块的地址,并且递增父进程配额块的引用计数
5. 继承windows的设备名字空间(包括驱动器字母的定义、COM端口等等)
6. 将父进程的进程ID保存在新进程对象的InheritedFromUniqueProcessId域中
7. 创建新进程的主访问令牌(父进程主令牌的副本)。新进程继承了其父进程的安全轮廓(安全描述符),如果通过CreateProcessAsUser来为新进程指定一个不同的访问令牌,
  则该令牌将在后面被正确地改过来
8. 初始化新进程句柄表,如果父进程已经被设置了继承句柄标志,那么,从父进程的句柄表中将任何可继承的句柄拷贝到新进程中
9. 将新进程的退出状态设置为STATUS_PENDING
10. 创建和初始新进程的地址空间
11. 创建内核进程块(KPROCESS)
12. 结束进程地址空间的创建过程
13. 建立Peb
14. 完成执行体进程对象的创建过程(设置返回值,开启审计等)

 

 至此,这就是一个"进程对象(EPROCESS/KPROCESS)"的初始化过程了。然而,经过PspCreateProcess函数之后,新建的进程中并没有任何线程对象,所以,它现在还是一个死的进程空间,也就是说,其中的代码并没被"真正"运行起来。所以,我们接下来讨论线程对象的的创建和初始化。

 

回到之前留下的一个问题,我们在分析Kernel32.dll中的NtCreateProcessEx()的时候,我们提到,那个时候是"暂时"离开ring3,穿越进ring0去创建进程对象(EPROCESS/KPROCESS)。
到了这里,可以解释这句话的意思了,我们的内核代码在创建完进程相关的结构后,就会退出内核模式,穿越回ring3用户模式。继续执行kernel32.dll中的代码逻辑。




阶段五: 创建线程的内核对象

创建完进程对象后,代码从内核模式穿越会用户模式,我们在kernel32.dll中的NtCreateProcessEx()代码处继续往下翻:

 

1. 处理下创建进程后的残余工作
在创建进程返回后,此时EPROCESS,PEB结构已经创建,进程地址空间也已经创建并初始化了。接下处理下创建进程后的残余工作,调用NtSetInformationProcess函数设置进程的优先级和默认处理模式.

复制代码
NTSYSAPI NTSTATUS NTAPI NtSetInformationProcess(
    IN HANDLE ProcessHandle,  //进程句柄
    IN PROCESS_INFORMATION_CLASS ProcessInformationClass, //进程信息类型索引(18 = ProcessPriorityClass)
    IN PVOID ProcessInformation,  //进程信息
    IN ULONG ProcessInformationLength //进程信息的的大小
);
复制代码
..
v238 = NtSetInformationProcess(v294, 18, &v295, 2);
..

 

2. 构建线程的环境

接下来要做的就是创建线程, 不过在在此之前还要构建线程的环境,调用BaseCreateStack函数创建栈:

NTSTATUS WINAPI BaseCreateStack    (    
        HANDLE hProcess,  //进程句柄
        SIZE_T StackReserve,  //栈大小
        SIZE_T StackCommit,  //栈的最大值
        PINITIAL_TEB InitialTeb  //初始TEB
);    
...
v23 = BaseCreateStack(v294, v191, v36, &v213);
...

接着调用BaseInitializeContext初始化线程上下文, 然后调用BaseFormatObjectAttributes函数格式化对象(以便传递给NtCreateThread)

....
BaseInitializeContext(&v297, v184, v189, v214, 0);
v188 = BaseFormatObjectAttributes(&v207, v166, 0);
...

 

3. 最后调用NtCreateThread创建线程

NTSYSAPI NTSTATUS NTAPI NtCreateThread( 
    OUT PHANDLE ThreadHandle,  //线程句柄指针
    IN ACCESS_MASK DesiredAccess,  //访问掩码
    IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,  //对象属性
    IN HANDLE ProcessHandle,   //进程句柄
    OUT PCLIENT_ID ClientId,   //客户端ID结构指针
    IN PCONTEXT ThreadContext,   //线程上下文结构指针
    IN PINITIAL_TEB InitialTeb,  //初始化TEB
    IN BOOLEAN CreateSuspended   //是否创建后挂起(默认是挂起的,题外话: 这就是APC进程注入的原理所在了)
);
复制代码
//kernel32.dll
..
v23 = NtCreateThread(&v290, 2032639, v188, v294, &v262, &v297, &v213, 1);
...

执行了一小段之后,又再次调用NtCreateThread穿越进内核模式,这次的目的是创建线程对象。

类似于进程的创建过程,线程的创建过程是从NtCreateThread()开始的, 它的源代码位于 base\ntos\ps\create.c 中,我还是决定直接在代码中插入注释,逐行的分析源代码

NTSTATUS
PspCreateThread(
    OUT PHANDLE ThreadHandle,    //线程句柄
    IN ACCESS_MASK DesiredAccess,    //新线程的访问权限
    IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,    //新线程对象的属性
    IN HANDLE ProcessHandle,        //线程所属进程的句柄
    IN PEPROCESS ProcessPointer,    //所属进程的EPROCESS对象,此参数仅当创建系统线程时才会指向全局
    OUT PCLIENT_ID ClientId OPTIONAL,//指向新线程的CLIENT_ID结构
    IN PCONTEXT ThreadContext OPTIONAL,//提供新线程的执行环境(之前创建好的), 它代表了用户模式线程的初始执行环境
    IN PINITIAL_TEB InitialTeb OPTIONAL,//为新线程Teb提供初始值
    IN BOOLEAN CreateSuspended,//指明新线程被创建起来后是否"被挂起"。如果为TRUE则意味着新线程创建完以后并不立即执行      
    IN PKSTART_ROUTINE StartRoutine OPTIONAL,//系统线程启动函数的地址
    IN PVOID StartContext//系统线程启动函数的执行环境
)

/*++

Routine Description:

    This routine creates and initializes a thread object. It implements the
    foundation for NtCreateThread and for PsCreateSystemThread.

Arguments:

    ThreadHandle - Returns the handle for the new thread.

    DesiredAccess - Supplies the desired access modes to the new thread.

    ObjectAttributes - Supplies the object attributes of the new thread.

    ProcessHandle - Supplies a handle to the process that the thread is being
                    created within.

    ClientId - Returns the CLIENT_ID of the new thread.

    ThreadContext - Supplies a pointer to a context frame that represents the
                initial user-mode context for a user-mode thread. The absence
                of this parameter indicates that a system thread is being
                created.

    InitialTeb - Supplies the contents of certain fields for the new threads
                 TEB. This parameter is only examined if both a trap and
                 exception frame were specified.

    CreateSuspended - Supplies a value that controls whether or not a user-mode
                      thread is created in a suspended state.

    StartRoutine - Supplies the address of the system thread start routine.

    StartContext - Supplies context for a system thread start routine.

--*/

{

    /*    1. PspCteateThread函数仅仅被NtCreateThread和PsCreateSystemThread这两个函数调用,
    分别用于创建用户线程和系统线程对象。
        2. 在PspCteateThread函数的参数中,ThreadContext和InitialTeb参数针对用户线程的创建操作,
    而StartRoutine和StartContext参数则针对系统线程的创建操作
    */

    HANDLE_TABLE_ENTRY CidEntry;
    NTSTATUS Status;
    PETHREAD Thread;
    PETHREAD CurrentThread;
    PEPROCESS Process;
    PTEB Teb;
    KPROCESSOR_MODE PreviousMode;
    HANDLE LocalThreadHandle;
    BOOLEAN AccessCheck;
    BOOLEAN MemoryAllocated;
    PSECURITY_DESCRIPTOR SecurityDescriptor;
    SECURITY_SUBJECT_CONTEXT SubjectContext;
    NTSTATUS accesst;
    LARGE_INTEGER CreateTime;
    ULONG OldActiveThreads;
    PEJOB Job;
    AUX_ACCESS_DATA AuxData;
    PACCESS_STATE AccessState;
    ACCESS_STATE LocalAccessState;

    PAGED_CODE();


    CurrentThread = PsGetCurrentThread ();
    /*
    首先获得当前线程对象,以及获取此次创建操作来自于内核模式还是用户模式(PreviousMode)
    */
    if (StartRoutine != NULL) {
        PreviousMode = KernelMode;
    } else {
        PreviousMode = KeGetPreviousModeByThread (&CurrentThread->Tcb);
    }

    Teb = NULL;

    Thread = NULL;
    Process = NULL;

    if (ProcessHandle != NULL) {
        //
        // Process object reference count is biased by one for each thread.
        // This accounts for the pointer given to the kernel that remains
        // in effect until the thread terminates (and becomes signaled)
        //
        /*
        根据进程句柄参数ProcessHandle获得相应的进程对象,放到局部变量Process中
         
        */
        Status = ObReferenceObjectByHandle (ProcessHandle,
                                            PROCESS_CREATE_THREAD,
                                            PsProcessType,
                                            PreviousMode,
                                            &Process,
                                            NULL);
    } else {
        if (StartRoutine != NULL) {
            ObReferenceObject (ProcessPointer);
            Process = ProcessPointer;
            Status = STATUS_SUCCESS;
        } else {
            Status = STATUS_INVALID_HANDLE;
        }
    }

    if (!NT_SUCCESS (Status)) {
        return Status;
    }

    //
    // If the previous mode is user and the target process is the system
    // process, then the operation cannot be performed.
    //

    if ((PreviousMode != KernelMode) && (Process == PsInitialSystemProcess)) {
        ObDereferenceObject (Process);
        return STATUS_INVALID_HANDLE;
    }
    /*
    调用ObCreateObject函数创建一个线程对象ETHREAD
    */
    Status = ObCreateObject (PreviousMode,
                             PsThreadType,
                             ObjectAttributes,
                             PreviousMode,
                             NULL,
                             sizeof(ETHREAD),
                             0,
                             0,
                             &Thread);

    if (!NT_SUCCESS (Status)) {
        ObDereferenceObject (Process);
        return Status;
    }
    /*
    把整个ETHREAD结构清零
    */
    RtlZeroMemory (Thread, sizeof (ETHREAD));

    //
    // Initialize rundown protection for cross thread TEB refs etc.
    //
    ExInitializeRundownProtection (&Thread->RundownProtect);

    //
    // Assign this thread to the process so that from now on
    // we don\'t have to dereference in error paths.
    //
    /*
    对ETHREAD的一些基本的域进行初始化(RundownProtect、ThreadsProcess(指向由进程句柄参数解析出来的EPROCESS对象)、Cid(包括UniqueProcess和UniqueThread成员))
    这里Cid.UniqueProcess是从Process对象中来的,而Cid.UniqueThread则是通过调用ExCreateHandle()函数在CID句柄表中创建一个句柄表项而获得的
    */
    Thread->ThreadsProcess = Process;

    Thread->Cid.UniqueProcess = Process->UniqueProcessId;

    CidEntry.Object = Thread;
    CidEntry.GrantedAccess = 0;
    Thread->Cid.UniqueThread = ExCreateHandle (PspCidTable, &CidEntry);

    if (Thread->Cid.UniqueThread == NULL) {
        ObDereferenceObject (Thread);
        return (STATUS_INSUFFICIENT_RESOURCES);
    }

    //
    // Initialize Mm
    //

    Thread->ReadClusterSize = MmReadClusterSize;

    //
    // Initialize LPC
    //
    /*
    继续初始化新线程对象ETHREAD结构中的一些域(ReadClusterSize、LpcReplySemaphore、LpcReplyChain、IrpList、PostBlockList、ThreadLock(线程锁成员)、
      ActiveTimerListLock、ActiveTimerListHead)
     */
    KeInitializeSemaphore (&Thread->LpcReplySemaphore, 0L, 1L);
    InitializeListHead (&Thread->LpcReplyChain);

    //
    // Initialize Io
    //

    InitializeListHead (&Thread->IrpList);

    //
    // Initialize Registry
    //

    InitializeListHead (&Thread->PostBlockList);

    //
    // Initialize the thread lock
    //

    PspInitializeThreadLock (Thread);

    KeInitializeSpinLock (&Thread->ActiveTimerListLock);
    InitializeListHead (&Thread->ActiveTimerListHead);
    /*
    获得"进程"的RundownProtect锁,以避免在创建过程中该进程被停掉(rundown)。
    直到该线程被插入到进程的线程链表中(通过KeStartThread函数)之后,
    PspCreateThread此释放该锁
    */

    if (!ExAcquireRundownProtection (&Process->RundownProtect)) {
        ObDereferenceObject (Thread);
        return STATUS_PROCESS_IS_TERMINATING;
    }

    if (ARGUMENT_PRESENT (ThreadContext)) {

        //
        // User-mode thread. Create TEB etc
        //
        /*
        如果ThreadContext非NULL,则此次创建的是用户模式线程,于是调用MmCreateTeb创建一个Teb,并用InitialTeb进行初始化
        */
        Status = MmCreateTeb (Process, InitialTeb, &Thread->Cid, &Teb);
        if (!NT_SUCCESS (Status)) {
            ExReleaseRundownProtection (&Process->RundownProtect);
            ObDereferenceObject (Thread);
            return Status;
        }


        try {
            //
            // Initialize kernel thread object for user mode thread.
            //
            /*
            利用ThreadContext中的程序指针(EIP寄存器)来设置线程的启动地址StartAddress,并且将ThreadContext中的EAX寄存器设置到线程的Win32StartAddress域
            */

            Thread->StartAddress = (PVOID)CONTEXT_TO_PROGRAM_COUNTER(ThreadContext);

#if defined(_AMD64_)

            Thread->Win32StartAddress = (PVOID)ThreadContext->Rdx;

#elif defined(_X86_)

            Thread->Win32StartAddress = (PVOID)ThreadContext->Eax;

#else

#error "no target architecture"

#endif

        } except (EXCEPTION_EXECUTE_HANDLER) {

            Status = GetExceptionCode();
        }

        if (NT_SUCCESS (Status)) {

            /*
            调用KeInitThread函数,根据进程对象中的信息来初始化新线程内核对象(KTHREAD)的一些属性(包括跟同步相关的各个域: 同步头(Header)、WaitBlock初始化、
             线程的系统服务表(SSDT ServiceTable域)、与APC、定时器相关的域、线程的内核栈的初始化等等)。最后,KeInitThread函数根据所提供的参数信息调用
                KiInitializeContextThread以便完成与特定处理器相关的执行环境的初始化。因此,当这个函数执行完后,新线程的状态是"已初始化(Initialized)"
            
            */
            Status = KeInitThread (&Thread->Tcb,
                                   NULL,
                                   PspUserThreadStartup,
                                   (PKSTART_ROUTINE)NULL,
                                   Thread->StartAddress,
                                   ThreadContext,
                                   Teb,
                                   &Process->Pcb);
       }


    } else {
        /*
        否则,则是系统线程。首先在CrossThreadFlags标志中设置系统线程位。然后将线程的启动地址设置为StartRoutine参数。最后同样地调用KeInitThread函数,
             所以,KeInitThread函数既可以被用来初始化用户模式线程,也可以被用来初始化系线程
        */
        Teb = NULL;
        //
        // Set the system thread bit thats kept for all time
        //
        PS_SET_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_SYSTEM);

        //
        // Initialize kernel thread object for kernel mode thread.
        //

        Thread->StartAddress = (PKSTART_ROUTINE) StartRoutine;
        Status = KeInitThread (&Thread->Tcb,
                               NULL,
                               PspSystemThreadStartup,
                               StartRoutine,
                               StartContext,
                               NULL,
                               NULL,
                               &Process->Pcb);
    }


    if (!NT_SUCCESS (Status)) {
        if (Teb != NULL) {
            MmDeleteTeb(Process, Teb);
        }
        ExReleaseRundownProtection (&Process->RundownProtect);
        ObDereferenceObject (Thread);
        return Status;
    }
    /*
    锁住进程,并确保此进程并不是在退出或终止过程中
    */
    PspLockProcessExclusive (Process, CurrentThread);
    //
    // Process is exiting or has had delete process called
    // We check the calling threads termination status so we
    // abort any thread creates while ExitProcess is being called --
    // but the call is blocked only if the new thread would be created
    // in the terminating thread\'s process.
    //
    if ((Process->Flags&PS_PROCESS_FLAGS_PROCESS_DELETE) != 0 ||
        (((CurrentThread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_TERMINATED) != 0) &&
        (ThreadContext != NULL) &&
        (THREAD_TO_PROCESS(CurrentThread) == Process))) {

        PspUnlockProcessExclusive (Process, CurrentThread);

        KeUninitThread (&Thread->Tcb);

        if (Teb != NULL) {
            MmDeleteTeb(Process, Teb);
        }
        ExReleaseRundownProtection (&Process->RundownProtect);
        ObDereferenceObject(Thread);

        return STATUS_PROCESS_IS_TERMINATING;
    }

    /*
    然后进程的活动线程数加1,并且将新线程加入到进程的线程链表中。
    */
    OldActiveThreads = Process->ActiveThreads++;
    InsertTailList (&Process->ThreadListHead, &Thread->ThreadListEntry);
    /*
    调用KeStartThread,初始化KTHREAD剩余的域(和调度相关的域: 优先级(BasePriority)、实现设置(Quantum)、处理器亲和性(Affinity)等等)。经过这一步的处理,新线程就可以
      开始运行了
      Ps: 在PspCreateThread和KeStartThread这两个函数中,我们都可以看到InsertTailList(&Process->ThreadListHead, &Thread->ThreadListEntry);这样的调用,这是分别
        在执行体层和内核层维护线程和进程的关系(线程从属进程)即EPROCESS->ETHREAD、KPROCESS->KTHREAD。
        */
    KeStartThread (&Thread->Tcb);

    PspUnlockProcessExclusive (Process, CurrentThread);

    ExReleaseRundownProtection (&Process->RundownProtect);

    //
    // Failures that occur after this point cause the thread to
    // go through PspExitThread
    //

    /*
    若这是进程中的第一个线程,则触发该进程的创建通知
    */
    if (OldActiveThreads == 0) {
        PERFINFO_PROCESS_CREATE (Process);

        if (PspCreateProcessNotifyRoutineCount != 0) {
            ULONG i;
            PEX_CALLBACK_ROUTINE_BLOCK CallBack;
            PCREATE_PROCESS_NOTIFY_ROUTINE Rtn;

            for (i=0; i<PSP_MAX_CREATE_PROCESS_NOTIFY; i++) {
                CallBack = ExReferenceCallBackBlock (&PspCreateProcessNotifyRoutine[i]);
                if (CallBack != NULL) {
                    Rtn = (PCREATE_PROCESS_NOTIFY_ROUTINE) ExGetCallBackBlockRoutine (CallBack);
                    Rtn (Process->InheritedFromUniqueProcessId,
                         Process->UniqueProcessId,
                         TRUE);
                    ExDereferenceCallBackBlock (&PspCreateProcessNotifyRoutine[i],
                                                CallBack);
                }
            }
        }
    }

    //
    // If the process has a job with a completion port,
    // AND if the process is really considered to be in the Job, AND
    // the process has not reported, report in
    //
    // This should really be done in add process to job, but can\'t
    // in this path because the process\'s ID isn\'t assigned until this point
    // in time
    //
    /*
    如果新线程所属的进程在一个作业中,则需要做特定的处理
    */
    Job = Process->Job;
    if (Job != NULL && Job->CompletionPort &&
        !(Process->JobStatus & (PS_JOB_STATUS_NOT_REALLY_ACTIVE|PS_JOB_STATUS_NEW_PROCESS_REPORTED))) {

        PS_SET_BITS (&Process->JobStatus, PS_JOB_STATUS_NEW_PROCESS_REPORTED);

        KeEnterCriticalRegionThread (&CurrentThread->Tcb);
        ExAcquireResourceSharedLite (&Job->JobLock, TRUE);
        if (Job->CompletionPort != NULL) {
            IoSetIoCompletion (Job->CompletionPort,
                               Job->CompletionKey,
                               (PVOID)Process->UniqueProcessId,
                               STATUS_SUCCESS,
                               JOB_OBJECT_MSG_NEW_PROCESS,
                               FALSE);
        }
        ExReleaseResourceLite (&Job->JobLock);
        KeLeaveCriticalRegionThread (&CurrentThread->Tcb);
    }

    PERFINFO_THREAD_CREATE(Thread, InitialTeb);

    //
    // Notify registered callout routines of thread creation.
    //
    /*
    通知哪些接收线程创建事件的出调例程(callout routine),即之前绑定在这个线程创建事件的回调函数的例程
    */
    if (PspCreateThreadNotifyRoutineCount != 0) {
        ULONG i;
        PEX_CALLBACK_ROUTINE_BLOCK CallBack;
        PCREATE_THREAD_NOTIFY_ROUTINE Rtn;

        for (i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i++) {
            CallBack = ExReferenceCallBackBlock (&PspCreateThreadNotifyRoutine[i]);
            if (CallBack != NULL) {
                Rtn = (PCREATE_THREAD_NOTIFY_ROUTINE) ExGetCallBackBlockRoutine (CallBack);
                Rtn (Thread->Cid.UniqueProcess,
                     Thread->Cid.UniqueThread,
                     TRUE);
                ExDereferenceCallBackBlock (&PspCreateThreadNotifyRoutine[i],
                                            CallBack);
            }
        }
    }


    //
    // Reference count of thread is biased once for itself and once for the handle if we create it.
    //
    /*
    线程对象的引用计数加2: 一个针对当前的创建操作,另一个针对要返回的线程句柄
    */
    ObReferenceObjectEx (Thread, 2);
    /*
    如果CreateSuspended参数指示新线程立即被挂起,则调用KeSuspendThread挂起新线程
    */
    if (CreateSuspended) {
        try {
            KeSuspendThread (&Thread->Tcb);
        } except ((GetExceptionCode () == STATUS_SUSPEND_COUNT_EXCEEDED)?
                     EXCEPTION_EXECUTE_HANDLER :
                     EXCEPTION_CONTINUE_SEARCH) {
        }
        //
        // If deletion was started after we suspended then wake up the thread
        //
        if (Thread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_TERMINATED) {
            KeForceResumeThread (&Thread->Tcb);
        }
    }
    /*
    根据指定的期望访问权限,调用SeCreateAccessStateEx()函数创建一个访问状态结构(ACCESS_STATE)
    */
    AccessState = NULL;
    if (!PsUseImpersonationToken) {
        AccessState = &LocalAccessState;
        Status = SeCreateAccessStateEx (NULL,
                                        ARGUMENT_PRESENT (ThreadContext)?PsGetCurrentProcessByThread (CurrentThread) : Process,
                                        AccessState,
                                        &AuxData,
                                        DesiredAccess,
                                        &PsThreadType->TypeInfo.GenericMapping);

        if (!NT_SUCCESS (Status)) {
            PS_SET_BITS (&Thread->CrossThreadFlags,
                         PS_CROSS_THREAD_FLAGS_DEADTHREAD);

            if (CreateSuspended) {
                (VOID) KeResumeThread (&Thread->Tcb);
            }
            KeReadyThread (&Thread->Tcb);
            ObDereferenceObjectEx (Thread, 2);

            return Status;
        }
    }
    /*
    调用ObInsertObject函数把新线程对象插入到当前进程的句柄表中
    */
    Status = ObInsertObject (Thread,
                             AccessState,
                             DesiredAccess,
                             0,
                             NULL,
                             &LocalThreadHandle);

    if (AccessState != NULL) {
        SeDeleteAccessState (AccessState);
    }

    if (!NT_SUCCESS (Status)) {

        //
        // The insert failed. Terminate the thread.
        //

        //
        // This trick is used so that Dbgk doesn\'t report
        // events for dead threads
        //

        PS_SET_BITS (&Thread->CrossThreadFlags,
                     PS_CROSS_THREAD_FLAGS_DEADTHREAD);

        if (CreateSuspended) {
            KeResumeThread (&Thread->Tcb);
        }

    } else {

        try {

            *ThreadHandle = LocalThreadHandle;
            if (ARGUMENT_PRESENT (ClientId)) {
                *ClientId = Thread->Cid;
            }
        } except(EXCEPTION_EXECUTE_HANDLER) {

            PS_SET_BITS (&Thread->CrossThreadFlags,
                         PS_CROSS_THREAD_FLAGS_DEADTHREAD);

            if (CreateSuspended) {
                (VOID) KeResumeThread (&Thread->Tcb);
            }
            KeReadyThread (&Thread->Tcb);
            ObDereferenceObject (Thread);
            ObCloseHandle (LocalThreadHandle, PreviousMode);
            return GetExceptionCode();
        }
    }
    /*
    设置新线程的创建时间
    */
    KeQuerySystemTime(&CreateTime);
    ASSERT ((CreateTime.HighPart & 0xf0000000) == 0);
    PS_SET_THREAD_CREATE_TIME(Thread, CreateTime);


    if ((Thread->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_DEADTHREAD) == 0) {
        Status = ObGetObjectSecurity (Thread,
                                      &SecurityDescriptor,
                                      &MemoryAllocated);
        if (!NT_SUCCESS (Status)) {
            //
            // This trick us used so that Dbgk doesn\'t report
            // events for dead threads
            //
            PS_SET_BITS (&Thread->CrossThreadFlags,
                         PS_CROSS_THREAD_FLAGS_DEADTHREAD);

            if (CreateSuspended) {
                KeResumeThread(&Thread->Tcb);
            }
            KeReadyThread (&Thread->Tcb);
            ObDereferenceObject (Thread);
            ObCloseHandle (LocalThreadHandle, PreviousMode);
            return Status;
        }

        //
        // Compute the subject security context
        //

        SubjectContext.ProcessAuditId = Process;
        SubjectContext.PrimaryToken = PsReferencePrimaryToken(Process);
        SubjectContext.ClientToken = NULL;

        AccessCheck = SeAccessCheck (SecurityDescriptor,
                                     &SubjectContext,
                                     FALSE,
                                     MAXIMUM_ALLOWED,
                                     0,
                                     NULL,
                                     &PsThreadType->TypeInfo.GenericMapping,
                                     PreviousMode,
                                     &Thread->GrantedAccess,
                                     &accesst);

        PsDereferencePrimaryTokenEx (Process, SubjectContext.PrimaryToken);

        ObReleaseObjectSecurity (SecurityDescriptor,
                                 MemoryAllocated);

        if (!AccessCheck) {
            Thread->GrantedAccess = 0;
        }

        Thread->GrantedAccess |= (THREAD_TERMINATE | THREAD_SET_INFORMATION | THREAD_QUERY_INFORMATION);

    } else {
        Thread->GrantedAccess = THREAD_ALL_ACCESS;
    }
    /*
    最后,调用KeReadyThread函数,调用KeReadyThread函数将线程加入到进程对象中的线程就绪队列中(kprocess->ReadyListHead)
      他使新线程进入"就绪(ready)状态",准备马上执行(就绪态只是表明它有机会获得CPU的调度,并不是指立刻就能执行)。
      或者,若此时进程被内存调度换出了内存,则新线程的状态为"转移(transition)",以等待换入内存后再执行
      */
    KeReadyThread (&Thread->Tcb);
    ObDereferenceObject (Thread);

    return Status;
}
base\ntos\ps\create.c

 

1. 递增进程对象中的线程计数值
2. 创建并初始化一个执行体线程块(ETHREAD)
3. 为新线程生成一个线程ID
4. 在进程的用户模式地址空间中建立Teb
5. 用户模式线程的起始地址被保存在ETHREAD中。对于windows线程,这是系统提供的线程启动函数,位于Kernel32.dll中(对于进程中的第一个线程是BaseThreadStart,对于其他的线程则
  是BaseThreadStart)。用户指定的windows启动地址被保存在ETHREAD块中的另一个位置上,因而,系统提供的线程启动函数可以调用用户指定的启动函数 6. 调用KeInitThread来建立起KTHREAD块。该线程初始的基本优先级和当前优先级均被设置为所属进程的基本优先级,它的亲和性和时限被设置为进程的亲和性和时限。该函数也会设置初始线程
  的理想处理器。KeInitThread接下来为该线程分配一个内核栈(注意和用户线程栈区分),并且为它初始化与机器相关的硬件环境,包括执行环境、陷阱和异常帧。该线程的执行环境也被建立
  起来,因而在内核模式下此线程可在KiThreadStartup中启动起来。最后,KeInitThread将该线程的状态设置为"已初始化",并返回到PspCreateThread 7. 调用任何已等级的系统全局范围的线程来创建通知例程 8. 该线程的访问令牌被设置为指向进程的访问令牌,然后做一次访问检查,以确定调用者是否有权创建该线程。如果你是在本地进程中创建一个线程,那么这一检查将总是成功,但是,如果你是在
  另一个进程中创建一个"远程线程",并且创建线程的进程没有""调试特权,那么这一访问检查可能会失败 9. 最后,该线程做好执行准备




线程创建完成后调用CsrClientCallServer通知CRSS 线程创建成功,所以这个通知过程,我们详细说明一下:

复制代码
到这个点上,所有必要的执行体进程对象和执行体线程对象都已经被创建出来了。接下来kernel32.dll给windows"子系统CSRSS"发送一个消息,从而它可以进一步建立起新进程和线程,
该消息包含以下信息: 1. 进程和线程的句柄 2. 创建标志中的各个项目 3. 该进程的创建者ID 4. 指示该进程是否属于一个windows应用程序的标志(Csrss可以确定是否要显示启动光标) 当windows子系统Csrss接收到此消息时,它执行以下12个步骤 1. CreateProcess复制一份该进程和线程的句柄。在这一步,进程和线程的使用计数从1(在创建时设置的)增加到2 2. 如果进程的优先级和类别没有指定的话,则CreateProcess自动进行优先级类别设置 3. 分配Csrss进程块 4. 新进程的异常端口被设置为windows子系统的通用功能端口,这样,当该进程中发生异常时,windows子系统将会收到一个消息 5. 如果该进程正在被调试(即它被附载到一个调试器进程)的话,则该进程的调试端口被设置为windows子系统的通用功能端口。这一设置可以保证,windows将把在新进程中发生的调试事件
  (比如线程创建和删除、异常、等等)作为消息发送给windows子系统in个,从而它可以把这些事件分发给新进程的调试器进程 6. 分配并初始化Csrss线程块 7. CreateProcess把该线程插入到进程的线程列表中 8. 递增该会话中的进程计数值 9. 进程的停机级别被设置为0x280,这是进程默认停机级别 10. 将新进程块插入到windows子系统范围内的进程的列表中 11. windows子系统的内核模式部分所用到的,针对每个进程的数据结构(W32PROCESS结构)被分配并初始化 12. 显示应用程序启动光标。即沙漏箭头。这是windows提醒用户的方式: "我正在启动某些东西"
复制代码

代码调用NtRequestWaitReplyPort()来等待回应,最后调用NtResumeThread()函数恢复线程的执行。

在以上调用完后,会调用内核中线程的启动函数KiThreadStartup, 该函数的实现中调用了PspUserThreadStartup,该函数初始化用户APC,将LdrInitializeThunk函数作为APC函数挂入
APC队列中,最后调用KiDeliverApc发生APC, 通过中断返回3环(注意,这里又再次回到ring3)。
//这里插个题外话,在一个进程注入的技术中谈到的APC注入指的就是这一步了,我们通过注册"进程创建"回调函数往"远程进程"的APC队列中插入了DLL文件,即插入了远程进程的APC队列中,
那么操作系统在这一步就会逐个执行APC队列中的任务,同样也把我们注入的DLL给执行了

当PspUserThreadStartup返回到KiThreadStartup中后,它从内核模式中返回,切换到3环KiUserApcDispatcher,实现中调用LdrInitializeThunk来加载需要的DLL,并且用DLL_PROCESS_ATTACH功能代码来调用DLL的入口点

复制代码
KiUserApcDispatcher
lea     edi, [esp+arg_C]
pop     eax
call    eax            //LdrInitializeThunk
push    1
push    edi
call    _ZwContinue@8         ;加载完DLL后,调用该函数继续返回到内核
复制代码

切换到内核后作部分操作后,再次回到3环(注意,这里又再次回到ring3,这一阶段来回次数较多,放慢速度理解一下),

调用用户空间的线程入口函数BaseProcessStart(Kernel32.dll中的BaseProcessStart函数), 该函数在3环中BaseInitializeContext中有指定。 这个时候,线程就从之前指定的入口点代码处开始执行起来了。当然,它要根据优先级遵循CPU的调度,分配到了时间才能执行。

 

复制代码
//上面说了这一大段,整理一下流程:

1. KiThreadStartup降低IRQL到APC_LEVEL
2. 然后调用系统初始的线程函数PspUserThreadStartup
3. PspUserThreadStartup把一个用户模式的APC插入到线程的用户APC队列中,此APC例程是在全局变量PspSystemDll中指定的,指向ntdll.dll的LdrInitializeThunk函数
4. PspUserThreadStartup函数返回之后,KiThreadStartup函数返回到用户模式,此时,PspUserThreadStartup插入的APC被交付,于是LdrInitializeThunk函数被调用,
  这是映像加载器(image loader)的初始化函数。LdrInitializeThunk函数完成加载器、堆管理器等初始化工作,然后加载必要的DLL,并且调用这些DLL的入口函数。最后,
  当LdrInitializeThunk返回到用户模式APC分发器时,该线程开始在用户模式下执行,调用应用程序指定的线程启动函数,此启动函数的地址已经在APC交付时备被压入到用户栈中 5. 至此,进程已经完成建立起来了,开始执行用户空间的代码
复制代码

附上线程创建的流程图

我们在内核层创建进程调用的NtCreateProcess(),这也是一个"转接层"函数

复制代码
NTSTATUS NtCreateProcess(
    __out PHANDLE ProcessHandle,
    __in ACCESS_MASK DesiredAccess,
    __in_opt POBJECT_ATTRIBUTES ObjectAttributes,
    __in HANDLE ParentProcess,
    __in BOOLEAN InheritObjectTable,
    __in_opt HANDLE SectionHandle,
    __in_opt HANDLE DebugPort,
    __in_opt HANDLE ExceptionPort
    )
{
    ULONG Flags = 0;

    if ((ULONG_PTR)SectionHandle & 1) 
    {
        Flags |= PROCESS_CREATE_FLAGS_BREAKAWAY;
    }
    if ((ULONG_PTR) DebugPort & 1) 
    {
        Flags |= PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT;
    }
    if (InheritObjectTable) 
    {
        Flags |= PROCESS_CREATE_FLAGS_INHERIT_HANDLES;
    }

    return NtCreateProcessEx (ProcessHandle,
                              DesiredAccess,
                              ObjectAttributes OPTIONAL,
                              ParentProcess,
                              Flags,
                              SectionHandle,
                              DebugPort,
                              ExceptionPort,
                              0);
}
复制代码

函数的源代码位于 base\ntos\ps\create.c  中,它知识简单地对参数稍作处理,然后把创建进程得分任务交给NtCreateProcessEx函数,之后的流程就和我们之前分析的一模一样了。

ring3创建进程和ring0创建进程的大致流程是:

1.1. ring3: CreateProcessA->CreateProcessInternalA->CreateProcessInternalW->NtCreateProcessEx->PspCreateProcess->NtCreateThread->PspCreateThread->
KiThreadStartup->PspUserThreadStartup->LdrInitializeThunk->BaseProcessStart
////CreateProcessA()只是起来转接层的作用
1.1 ring3: CreateProcess->CreateProcessInternalW->NtCreateProcessEx->PspCreateProcess->->NtCreateThread->PspCreateThread->KiThreadStartup->
PspUserThreadStartup->LdrInitializeThunk->BaseProcessStart

2. ring0: NtCreateProcess->NtCreateProcessEx->PspCreateProcss->->NtCreateThread->PspCreateThread->KiThreadStartup->PspUserThreadStartup->
LdrInitializeThunk->BaseProcessStart