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 }
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 }
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
2. 判断lpCommandLine(就是我们输入的notepad.exe这个命令)是否为空。即判断是否为0,不为0则将lpCommandLine初始化为UNICODE字符串
if ( !lpCommandLine ) { ... } result = Basep8BitStringToDynamicUnicodeString(&lpCommandLine_UNICODE, lpCommandLine); if ( result ) goto LABEL_3; ...
接着继续检查参数(lpApplicationName / lpCurrentDirectory),我们跟随代码来到LABEL_3:
1. 先将lpStartupInfor的内容拷贝到一个局部变量lpStartupInfo_buf 2. 然后判断参数lpApplicationName是否为0,不为0则参数转换为UNICODE字符串 3. 为0则跳过转换继续判断参数lpCurrentDirectory是否为0(思考"与运算符"和"或运算符"在汇编代码中的表现形式,即"与运算符"的提前退出性,"或运算符"的并行检查性), 不为0则参数转换为UNICODE字符串 4. lpApplicationName和lpCurrentDirectory中只有至少有一个不为0,则这个if判断成立,继续检查参数
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; }
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 }
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 }
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; }
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