进程创建过程详解 CreateProcess
转载请您注明出处:http://www.cnblogs.com/lsh123/p/7405796.html
0x01 CreateProcessW
CreateProcess的使用有ANSI版本的CreateProcessA和UNICODE版本的CreateProcessW:
不过查看源码就可以发现其实CreateProcessA内部调用的还是CreateProcessW:
BOOL WINAPI CreateProcessA( LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ) /*++ ANSI thunk to CreateProcessW --*/ { NTSTATUS Status; PUNICODE_STRING CommandLine; UNICODE_STRING ApplicationName; UNICODE_STRING CurrentDirectory; STARTUPINFOW StartupInfo; ANSI_STRING AnsiString; UNICODE_STRING Unicode; UNICODE_STRING DynamicCommandLine; UNICODE_STRING NullUnicodeString; BOOL ReturnStatus; if (ARGUMENT_PRESENT (lpCommandLine)) { if ( (strlen( lpCommandLine ) + 1) * sizeof( WCHAR ) < NtCurrentTeb()->StaticUnicodeString.MaximumLength ) { DynamicCommandLine.Buffer = NULL; CommandLine = Basep8BitStringToStaticUnicodeString( lpCommandLine ); if (CommandLine == NULL) { return FALSE; } } else { if (!Basep8BitStringToDynamicUnicodeString( &DynamicCommandLine, lpCommandLine )) { return FALSE; } } } else { DynamicCommandLine.Buffer = NULL; CommandLine = &NullUnicodeString; CommandLine->Buffer = NULL; } ApplicationName.Buffer = NULL; ApplicationName.Buffer = NULL; CurrentDirectory.Buffer = NULL; RtlMoveMemory(&StartupInfo,lpStartupInfo,sizeof(*lpStartupInfo)); ASSERT(sizeof(StartupInfo) == sizeof(*lpStartupInfo)); StartupInfo.lpReserved = NULL; StartupInfo.lpDesktop = NULL; StartupInfo.lpTitle = NULL; try { try { if (ARGUMENT_PRESENT(lpApplicationName)) { if (!Basep8BitStringToDynamicUnicodeString( &ApplicationName, lpApplicationName )) { ReturnStatus = FALSE; goto tryexit; } } if (ARGUMENT_PRESENT(lpCurrentDirectory)) { if (!Basep8BitStringToDynamicUnicodeString( &CurrentDirectory, lpCurrentDirectory )) { ReturnStatus = FALSE; goto tryexit; } } if (ARGUMENT_PRESENT(lpStartupInfo->lpReserved)) { // // Win95 does not touch reserved, and Intergraph Voxtel passes // garbage for this. Handle this by probing lpReserved, and if // the pointer is bad, ignore it // try { RtlInitAnsiString(&AnsiString,lpStartupInfo->lpReserved); } except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { goto bail_on_reserved; } Unicode.MaximumLength = (USHORT)RtlAnsiStringToUnicodeSize(&AnsiString) ; StartupInfo.lpReserved = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( TMP_TAG ), Unicode.MaximumLength); if ( !StartupInfo.lpReserved ) { BaseSetLastNTError(STATUS_NO_MEMORY); ReturnStatus = FALSE; goto tryexit; } Unicode.Buffer = StartupInfo.lpReserved; Status = RtlAnsiStringToUnicodeString(&Unicode,&AnsiString,FALSE); if ( !NT_SUCCESS(Status) ) { BaseSetLastNTError(Status); ReturnStatus = FALSE; goto tryexit; } } bail_on_reserved: if (ARGUMENT_PRESENT(lpStartupInfo->lpDesktop)) { RtlInitAnsiString(&AnsiString,lpStartupInfo->lpDesktop); Unicode.MaximumLength = (USHORT)RtlAnsiStringToUnicodeSize(&AnsiString) ; StartupInfo.lpDesktop = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( TMP_TAG ), Unicode.MaximumLength); if ( !StartupInfo.lpDesktop ) { BaseSetLastNTError(STATUS_NO_MEMORY); ReturnStatus = FALSE; goto tryexit; } Unicode.Buffer = StartupInfo.lpDesktop; Status = RtlAnsiStringToUnicodeString(&Unicode,&AnsiString,FALSE); if ( !NT_SUCCESS(Status) ) { BaseSetLastNTError(Status); ReturnStatus = FALSE; goto tryexit; } } if (ARGUMENT_PRESENT(lpStartupInfo->lpTitle)) { RtlInitAnsiString(&AnsiString,lpStartupInfo->lpTitle); Unicode.MaximumLength = (USHORT)RtlAnsiStringToUnicodeSize(&AnsiString) ; StartupInfo.lpTitle = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( TMP_TAG ), Unicode.MaximumLength); if ( !StartupInfo.lpTitle ) { BaseSetLastNTError(STATUS_NO_MEMORY); ReturnStatus = FALSE; goto tryexit; } Unicode.Buffer = StartupInfo.lpTitle; Status = RtlAnsiStringToUnicodeString(&Unicode,&AnsiString,FALSE); if ( !NT_SUCCESS(Status) ) { BaseSetLastNTError(Status); ReturnStatus = FALSE; goto tryexit; } } } except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { BaseSetLastNTError(GetExceptionCode()); ReturnStatus = FALSE; goto tryexit; } ReturnStatus = CreateProcessW( ApplicationName.Buffer, DynamicCommandLine.Buffer ? DynamicCommandLine.Buffer : CommandLine->Buffer, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, CurrentDirectory.Buffer, &StartupInfo, lpProcessInformation ); tryexit:; } finally { RtlFreeUnicodeString(&DynamicCommandLine); RtlFreeUnicodeString(&ApplicationName); RtlFreeUnicodeString(&CurrentDirectory); RtlFreeHeap(RtlProcessHeap(), 0,StartupInfo.lpReserved); RtlFreeHeap(RtlProcessHeap(), 0,StartupInfo.lpDesktop); RtlFreeHeap(RtlProcessHeap(), 0,StartupInfo.lpTitle); } return ReturnStatus; }
CreateProcessW的十个参数:
WINBASEAPI BOOL WINAPI CreateProcessW( LPCWSTR lpApplicationName, //指向一个NULL结尾的,新进程的可执行文件的名称 LPWSTR lpCommandLine, //指向一个NULL结尾的,传给新进程的命令行字符串 LPSECURITY_ATTRIBUTES lpProcessAttributes, //指向一个SECURITY_ATTRIBUTES结构体,分配给新的进程对象 //SECURITY_ATTRIBUTES结构可以决定是否返回的句柄可以被子进程继承(bInheritHandle )。如果lpProcessAttributes参数为空(NULL),那么句柄不能被继承。 //SECURITY_ATTRIBUTES结构的lpSecurityDescriptor成员可以指定新进程的安全描述符,如果参数为空,新进程使用默认的安全描述符。 LPSECURITY_ATTRIBUTES lpThreadAttributes, //指向一个SECURITY_ATTRIBUTES结构体,分配给新的线程对象 BOOL bInheritHandles, //标识新进程是否可以从调用进程处继承所有可继承的句柄。被继承的句柄与原进程拥有完全相同的值和访问权限。 DWORD dwCreationFlags, //标识了影响新进程创建方式的标志,多个标志按位或进行组合 LPVOID lpEnvironment, //指向一块内存,其中包含新进程要使用的环境字符串。如果此参数为空,新进程继承父进程的一组环境字符串。 LPCWSTR lpCurrentDirectory, //指向一个以NULL结尾的字符串,用来设置新进程的当前驱动器和目录,这个字符串必须是一个包含驱动器名的绝对路径。如果这个参数为空,新进程将使用与父进程相同的驱动器和目录。 LPSTARTUPINFOW lpStartupInfo, //指向一个用于决定新进程的主窗体如何显示的STARTUPINFO结构体。 LPPROCESS_INFORMATION lpProcessInformation //指向一个用来接收新进程的识别信息的PROCESS_INFORMATION结构体。 );
(1)STARTUPINFO 和 PROCESS_INFORMATION的使用前初始化为空:
STARTUPINFO StartupInfo = { 0 };
StartupInfo.cb = sizeof(STARTUPINFO);
PROCESS_INFORMATION ProcessInfo = { 0 };
(2)第六参数:dwCreationFlags 的部分标志:
1 BOOL 2 WINAPI 3 CreateProcessW( 4 LPCWSTR lpApplicationName, 5 LPWSTR lpCommandLine, 6 LPSECURITY_ATTRIBUTES lpProcessAttributes, 7 LPSECURITY_ATTRIBUTES lpThreadAttributes, 8 BOOL bInheritHandles, 9 DWORD dwCreationFlags, 10 LPVOID lpEnvironment, 11 LPCWSTR lpCurrentDirectory, 12 LPSTARTUPINFOW lpStartupInfo, 13 LPPROCESS_INFORMATION lpProcessInformation 14 ) 15 16 { 17 NTSTATUS Status; 18 OBJECT_ATTRIBUTES Obja; 19 POBJECT_ATTRIBUTES pObja; 20 HANDLE ProcessHandle, ThreadHandle, VdmWaitHandle = NULL; 21 HANDLE FileHandle, SectionHandle; 22 CLIENT_ID ClientId; 23 UNICODE_STRING PathName; 24 IO_STATUS_BLOCK IoStatusBlock; 25 BOOLEAN TranslationStatus; 26 RTL_RELATIVE_NAME RelativeName; 27 PVOID FreeBuffer; 28 LPWSTR NameBuffer; 29 LPWSTR WhiteScan; 30 ULONG Length,i; 31 PROCESS_BASIC_INFORMATION ProcessInfo; 32 SECTION_IMAGE_INFORMATION ImageInformation; 33 NTSTATUS StackStatus; 34 BOOLEAN bStatus; 35 INITIAL_TEB InitialTeb; 36 CONTEXT ThreadContext; 37 PPEB Peb; 38 BASE_API_MSG m; 39 PBASE_CREATEPROCESS_MSG a= (PBASE_CREATEPROCESS_MSG)&m.u.CreateProcess; 40 PBASE_CHECKVDM_MSG b= (PBASE_CHECKVDM_MSG)&m.u.CheckVDM; 41 PWCH TempNull = NULL; 42 WCHAR TempChar; 43 UNICODE_STRING VdmNameString; 44 PVOID BaseAddress; 45 ULONG VdmReserve; 46 SIZE_T BigVdmReserve; 47 ULONG iTask=0; 48 LPWSTR CurdirBuffer, CurdirFilePart; 49 DWORD CurdirLength,CurdirLength2; 50 ULONG VDMCreationState=0; 51 ULONG VdmBinaryType = 0; 52 UNICODE_STRING SubSysCommandLine; 53 PIMAGE_NT_HEADERS NtHeaders; 54 DWORD dwNoWindow = (dwCreationFlags & CREATE_NO_WINDOW); 55 ANSI_STRING AnsiStringVDMEnv; 56 UNICODE_STRING UnicodeStringVDMEnv; 57 WCHAR ImageFileDebuggerCommand[ 64 ]; 58 LPWSTR QuotedBuffer; 59 BOOLEAN QuoteInsert; 60 BOOLEAN QuoteCmdLine = FALSE; 61 BOOLEAN QuoteFound; 62 BOOLEAN SearchRetry; 63 BOOLEAN IsWowBinary = FALSE; 64 STARTUPINFOW StartupInfo; 65 DWORD LastError; 66 DWORD fileattr; 67 PROCESS_PRIORITY_CLASS PriClass; 68 PVOID State; 69 #if defined(BUILD_WOW6432) || defined(_WIN64) 70 LPCWSTR lpOriginalApplicationName = lpApplicationName; 71 LPWSTR lpOriginalCommandLine = lpCommandLine; 72 #endif 73 74 #if defined(WX86) || defined(_AXP64_) 75 HANDLE Wx86Info = NULL; 76 #endif 77 78 #if defined WX86 79 BOOLEAN UseKnownWx86Dll; 80 UseKnownWx86Dll = NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll; 81 NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = FALSE; 82 #endif 83 84 85 RtlZeroMemory(lpProcessInformation,sizeof(*lpProcessInformation)); 86 87 // Private VDM flag should be ignored; Its meant for internal use only. 88 dwCreationFlags &= (ULONG)~CREATE_NO_WINDOW; 89 90 // 91 // CREATE_WITH_USERPROFILE is the new Create Flag that is used 92 // only by CreateProcessWithLogonW. If this flags ends up getting 93 // passed to CreateProcess, we must reject it. 94 // 95 if (dwCreationFlags & CREATE_WITH_USERPROFILE ) { 96 SetLastError(ERROR_INVALID_PARAMETER); 97 return FALSE; 98 } 99 100 if ((dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) == 101 (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) { 102 103 SetLastError(ERROR_INVALID_PARAMETER); 104 return FALSE; 105 } 106 107 AnsiStringVDMEnv.Buffer = NULL; 108 UnicodeStringVDMEnv.Buffer = NULL; 109 110 // 111 // the lowest specified priority class is used. 112 // 113 114 if (dwCreationFlags & IDLE_PRIORITY_CLASS ) { 115 PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE; 116 } 117 else if (dwCreationFlags & BELOW_NORMAL_PRIORITY_CLASS ) { 118 PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL; 119 } 120 else if (dwCreationFlags & NORMAL_PRIORITY_CLASS ) { 121 PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL; 122 } 123 else if (dwCreationFlags & ABOVE_NORMAL_PRIORITY_CLASS ) { 124 PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL; 125 } 126 else if (dwCreationFlags & HIGH_PRIORITY_CLASS ) { 127 PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH; 128 } 129 else if (dwCreationFlags & REALTIME_PRIORITY_CLASS ) { 130 if ( BasepIsRealtimeAllowed(FALSE) ) { 131 PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_REALTIME; 132 } 133 else { 134 PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH; 135 } 136 } 137 else { 138 PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_UNKNOWN; 139 } 140 PriClass.Foreground = FALSE; 141 142 dwCreationFlags = (dwCreationFlags & ~PRIORITY_CLASS_MASK ); 143 144 // 145 // Default separate/shared VDM option if not explicitly specified. 146 // 147 148 if (dwCreationFlags & CREATE_SEPARATE_WOW_VDM) { 149 if (dwCreationFlags & CREATE_SHARED_WOW_VDM) { 150 SetLastError(ERROR_INVALID_PARAMETER); 151 152 return FALSE; 153 } 154 } 155 else 156 if ((dwCreationFlags & CREATE_SHARED_WOW_VDM) == 0) { 157 if (BaseStaticServerData->DefaultSeparateVDM) { 158 dwCreationFlags |= CREATE_SEPARATE_WOW_VDM; 159 } 160 } 161 162 if ((dwCreationFlags & CREATE_SEPARATE_WOW_VDM) == 0) { 163 // 164 // If the creator is running inside a job object, always 165 // set SEPERATE_WOW_VDM so the VDM is part of the job. 166 // 167 JOBOBJECT_BASIC_UI_RESTRICTIONS UiRestrictions; 168 169 Status = NtQueryInformationJobObject(NULL, 170 JobObjectBasicUIRestrictions, 171 &UiRestrictions, 172 sizeof(UiRestrictions), 173 NULL); 174 if (Status != STATUS_ACCESS_DENIED) { 175 // 176 // Anything other than STATUS_ACCESS_DENIED indicates the 177 // current process is inside a job. 178 // 179 dwCreationFlags = (dwCreationFlags & (~CREATE_SHARED_WOW_VDM)) | 180 CREATE_SEPARATE_WOW_VDM; 181 } 182 } 183 184 185 // 186 // If ANSI environment, convert to Unicode 187 // 188 189 if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT) ) { 190 PUCHAR s; 191 STRING Ansi; 192 UNICODE_STRING Unicode; 193 MEMORY_BASIC_INFORMATION MemoryInformation; 194 195 Ansi.Buffer = s = lpEnvironment; 196 while (*s || *(s+1)) // find end of block 197 s++; 198 199 Ansi.Length = (USHORT)(s - Ansi.Buffer) + 1; 200 Ansi.MaximumLength = Ansi.Length + 1; 201 MemoryInformation.RegionSize = Ansi.MaximumLength * sizeof(WCHAR); 202 Unicode.Buffer = NULL; 203 Status = NtAllocateVirtualMemory( NtCurrentProcess(), 204 &Unicode.Buffer, 205 0, 206 &MemoryInformation.RegionSize, 207 MEM_COMMIT, 208 PAGE_READWRITE 209 ); 210 if (!NT_SUCCESS(Status) ) { 211 BaseSetLastNTError(Status); 212 213 return FALSE; 214 } 215 216 Unicode.MaximumLength = (USHORT)MemoryInformation.RegionSize; 217 Status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, FALSE); 218 if (!NT_SUCCESS(Status) ) { 219 NtFreeVirtualMemory( NtCurrentProcess(), 220 &Unicode.Buffer, 221 &MemoryInformation.RegionSize, 222 MEM_RELEASE 223 ); 224 BaseSetLastNTError(Status); 225 226 return FALSE; 227 } 228 lpEnvironment = Unicode.Buffer; 229 } 230 231 FileHandle = NULL; 232 SectionHandle = NULL; 233 ProcessHandle = NULL; 234 ThreadHandle = NULL; 235 FreeBuffer = NULL; 236 NameBuffer = NULL; 237 VdmNameString.Buffer = NULL; 238 BaseAddress = (PVOID)1; 239 VdmReserve = 0; 240 CurdirBuffer = NULL; 241 CurdirFilePart = NULL; 242 SubSysCommandLine.Buffer = NULL; 243 QuoteFound = FALSE; 244 QuoteInsert = FALSE; 245 QuotedBuffer = NULL; 246 247 try { 248 249 // 250 // Make a copy of the startup info so we can change it. 251 // 252 253 StartupInfo = *lpStartupInfo; 254 255 // 256 // STARTF_USEHOTKEY means hStdInput is really the hotkey value. 257 // STARTF_HASSHELLDATA means std handles are used for shell-private 258 // data. This flag is used if an icon is passed to ShellExecuteEx. 259 // As a result they cannot be specified with STARTF_USESTDHANDLES. 260 // Consistent with Win95, USESTDHANDLES is ignored. 261 // 262 263 if (StartupInfo.dwFlags & STARTF_USESTDHANDLES && 264 StartupInfo.dwFlags & (STARTF_USEHOTKEY | STARTF_HASSHELLDATA)) { 265 266 StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES; 267 } 268 269 VdmRetry: 270 LastError = 0; 271 SearchRetry = TRUE; 272 QuoteInsert = FALSE; 273 QuoteCmdLine = FALSE; 274 if (!ARGUMENT_PRESENT( lpApplicationName )) { 275 276 // 277 // Locate the image 278 // 279 280 // forgot to free NameBuffer before goto VdmRetry??? 281 ASSERT(NameBuffer == NULL); 282 283 NameBuffer = RtlAllocateHeap( RtlProcessHeap(), 284 MAKE_TAG( TMP_TAG ), 285 MAX_PATH * sizeof( WCHAR )); 286 if ( !NameBuffer ) { 287 BaseSetLastNTError(STATUS_NO_MEMORY); 288 return FALSE; 289 } 290 lpApplicationName = lpCommandLine; 291 TempNull = (PWCH)lpApplicationName; 292 WhiteScan = (LPWSTR)lpApplicationName; 293 294 // 295 // check for lead quote 296 // 297 if ( *WhiteScan == L'\"' ) { 298 SearchRetry = FALSE; 299 WhiteScan++; 300 lpApplicationName = WhiteScan; 301 while(*WhiteScan) { 302 if ( *WhiteScan == (WCHAR)'\"' ) { 303 TempNull = (PWCH)WhiteScan; 304 QuoteFound = TRUE; 305 break; 306 } 307 WhiteScan++; 308 TempNull = (PWCH)WhiteScan; 309 } 310 } 311 else { 312 retrywsscan: 313 lpApplicationName = lpCommandLine; 314 while(*WhiteScan) { 315 if ( *WhiteScan == (WCHAR)' ' || 316 *WhiteScan == (WCHAR)'\t' ) { 317 TempNull = (PWCH)WhiteScan; 318 break; 319 } 320 WhiteScan++; 321 TempNull = (PWCH)WhiteScan; 322 } 323 } 324 TempChar = *TempNull; 325 *TempNull = UNICODE_NULL; 326 327 #ifdef WX86 328 329 // 330 // Wx86 applications must use x86 version of known exes 331 // for compatibility. 332 // 333 334 if (UseKnownWx86Dll) { 335 LPWSTR KnownName; 336 337 NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = FALSE; 338 339 KnownName = BasepWx86KnownExe(lpApplicationName); 340 if (KnownName) { 341 lpApplicationName = KnownName; 342 } 343 } 344 #endif 345 346 347 Length = SearchPathW( 348 NULL, 349 lpApplicationName, 350 (PWSTR)L".exe", 351 MAX_PATH, 352 NameBuffer, 353 NULL 354 )*2; 355 356 if (Length != 0 && Length < MAX_PATH * sizeof( WCHAR )) { 357 // 358 // SearchPathW worked, but file might be a directory 359 // if this happens, we need to keep trying 360 // 361 fileattr = GetFileAttributesW(NameBuffer); 362 if ( fileattr != 0xffffffff && 363 (fileattr & FILE_ATTRIBUTE_DIRECTORY) ) { 364 Length = 0; 365 } else { 366 Length++; 367 Length++; 368 } 369 } 370 371 if ( !Length || Length >= MAX_PATH<<1 ) { 372 373 // 374 // If we search pathed, then return file not found. 375 // otherwise, try to be more specific. 376 // 377 RTL_PATH_TYPE PathType; 378 HANDLE hFile; 379 380 PathType = RtlDetermineDosPathNameType_U(lpApplicationName); 381 if ( PathType != RtlPathTypeRelative ) { 382 383 // 384 // The failed open should set get last error properly. 385 // 386 387 hFile = CreateFileW( 388 lpApplicationName, 389 GENERIC_READ, 390 FILE_SHARE_READ | FILE_SHARE_WRITE, 391 NULL, 392 OPEN_EXISTING, 393 FILE_ATTRIBUTE_NORMAL, 394 NULL 395 ); 396 if ( hFile != INVALID_HANDLE_VALUE ) { 397 CloseHandle(hFile); 398 BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND); 399 } 400 } 401 else { 402 BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND); 403 } 404 405 // 406 // remember initial last error value for the retry scan path 407 // 408 409 if ( LastError ) { 410 SetLastError(LastError); 411 } 412 else { 413 LastError = GetLastError(); 414 } 415 416 // 417 // restore the command line 418 // 419 420 *TempNull = TempChar; 421 lpApplicationName = NameBuffer; 422 423 // 424 // If we still have command line left, then keep going 425 // the point is to march through the command line looking 426 // for whitespace so we can try to find an image name 427 // launches of things like: 428 // c:\word 95\winword.exe /embedding -automation 429 // require this. Our first iteration will stop at c:\word, our next 430 // will stop at c:\word 95\winword.exe 431 // 432 if (*WhiteScan && SearchRetry) { 433 WhiteScan++; 434 TempNull = WhiteScan; 435 QuoteInsert = TRUE; 436 QuoteFound = TRUE; 437 goto retrywsscan; 438 } 439 440 return FALSE; 441 } 442 // 443 // restore the command line 444 // 445 446 *TempNull = TempChar; 447 lpApplicationName = NameBuffer; 448 } 449 else 450 if (!ARGUMENT_PRESENT( lpCommandLine ) || *lpCommandLine == UNICODE_NULL ) { 451 QuoteCmdLine = TRUE; 452 lpCommandLine = (LPWSTR)lpApplicationName; 453 } 454 455 456 #ifdef WX86 457 458 // 459 // Wx86 applications must use x86 version of known exes 460 // for compatibility. 461 // 462 463 if (UseKnownWx86Dll) { 464 LPWSTR KnownName; 465 466 NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = FALSE; 467 468 KnownName = BasepWx86KnownExe(lpApplicationName); 469 if (KnownName) { 470 471 RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer); 472 NameBuffer = KnownName; 473 lpApplicationName = KnownName; 474 } 475 } 476 477 #endif 478 479 480 // 481 // Translate to an NT name. 482 // 483 484 TranslationStatus = RtlDosPathNameToNtPathName_U( 485 lpApplicationName, 486 &PathName, 487 NULL, 488 &RelativeName 489 ); 490 491 if ( !TranslationStatus ) { 492 SetLastError(ERROR_PATH_NOT_FOUND); 493 494 return FALSE; 495 } 496 497 // forgot to free FreeBuffer before goto VdmRetry???? 498 ASSERT(FreeBuffer == NULL); 499 FreeBuffer = PathName.Buffer; 500 501 if ( RelativeName.RelativeName.Length ) { 502 PathName = *(PUNICODE_STRING)&RelativeName.RelativeName; 503 } 504 else { 505 RelativeName.ContainingDirectory = NULL; 506 } 507 508 InitializeObjectAttributes( 509 &Obja, 510 &PathName, 511 OBJ_CASE_INSENSITIVE, 512 RelativeName.ContainingDirectory, 513 NULL 514 ); 515 516 // 517 // Open the file for execute access 518 // 519 520 Status = NtOpenFile( 521 &FileHandle, 522 SYNCHRONIZE | FILE_EXECUTE, 523 &Obja, 524 &IoStatusBlock, 525 FILE_SHARE_READ | FILE_SHARE_DELETE, 526 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE 527 ); 528 if (!NT_SUCCESS(Status) ) { 529 530 // 531 // if we failed, see if this is a device. If it is a device, 532 // then just return invalid image format 533 // 534 535 if ( RtlIsDosDeviceName_U((PWSTR)lpApplicationName) ) { 536 SetLastError(ERROR_BAD_DEVICE); 537 } 538 else { 539 BaseSetLastNTError(Status); 540 } 541 542 return FALSE; 543 } 544 545 // 546 // If no desktop has been specified, use the caller's 547 // desktop. 548 // 549 550 if (StartupInfo.lpDesktop == NULL) { 551 StartupInfo.lpDesktop = 552 (LPWSTR)((PRTL_USER_PROCESS_PARAMETERS)NtCurrentPeb()-> 553 ProcessParameters)->DesktopInfo.Buffer; 554 } 555 556 // 557 // Create a section object backed by the file 558 // 559 560 Status = NtCreateSection( 561 &SectionHandle, 562 SECTION_ALL_ACCESS, 563 NULL, 564 NULL, 565 PAGE_EXECUTE, 566 SEC_IMAGE, 567 FileHandle 568 ); 569 570 571 NtClose(FileHandle); 572 FileHandle = NULL; 573 574 575 576 // 577 // App Certification DLL 578 // 579 580 if (NT_SUCCESS(Status)) { 581 582 Status = BasepIsProcessAllowed(lpApplicationName); 583 584 if (!NT_SUCCESS(Status)) { 585 BaseSetLastNTError(Status); 586 return FALSE; 587 } 588 589 } 590 591 592 593 if (!NT_SUCCESS(Status)) { 594 595 switch (Status) { 596 // 16 bit OS/2 exe 597 case STATUS_INVALID_IMAGE_NE_FORMAT: 598 #ifdef i386 599 // 600 // Use OS/2 if x86 (OS/2 not supported on risc), 601 // and CreationFlags don't have forcedos bit 602 // and Registry didn't specify ForceDos 603 // 604 // else execute as a DOS bound app. 605 // 606 // 607 608 if (!(dwCreationFlags & CREATE_FORCEDOS) && 609 !BaseStaticServerData->ForceDos) 610 { 611 612 if ( !BuildSubSysCommandLine( L"OS2 /P ", 613 lpApplicationName, 614 lpCommandLine, 615 &SubSysCommandLine 616 ) ) { 617 return FALSE; 618 } 619 620 lpCommandLine = SubSysCommandLine.Buffer; 621 622 lpApplicationName = NULL; 623 624 RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer); 625 FreeBuffer = NULL; 626 RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer); 627 NameBuffer = NULL; 628 goto VdmRetry; 629 } 630 #endif 631 // Falls into Dos case, so that stub message will be 632 // printed, and bound apps will run w/o OS/2 subsytem 633 634 // Dos .exe or .com 635 636 case STATUS_INVALID_IMAGE_PROTECT: 637 case STATUS_INVALID_IMAGE_NOT_MZ: 638 ForceDos: 639 { 640 ULONG BinarySubType; 641 642 BinarySubType = BINARY_TYPE_DOS_EXE; 643 if (Status == STATUS_INVALID_IMAGE_PROTECT || 644 Status == STATUS_INVALID_IMAGE_NE_FORMAT || 645 (BinarySubType = BaseIsDosApplication(&PathName,Status)) ) 646 { 647 VdmBinaryType = BINARY_TYPE_DOS; 648 649 // create the environment before going to the 650 // server. This was done becuase we want NTVDM 651 // to have the new environment when it gets 652 // created. 653 if (!BaseCreateVDMEnvironment( 654 lpEnvironment, 655 &AnsiStringVDMEnv, 656 &UnicodeStringVDMEnv 657 )) 658 return FALSE; 659 660 if(!BaseCheckVDM(VdmBinaryType | BinarySubType, 661 lpApplicationName, 662 lpCommandLine, 663 lpCurrentDirectory, 664 &AnsiStringVDMEnv, 665 &m, 666 &iTask, 667 dwCreationFlags, 668 &StartupInfo 669 )) 670 return FALSE; 671 672 673 // Check the return value from the server 674 switch (b->VDMState & VDM_STATE_MASK){ 675 case VDM_NOT_PRESENT: 676 // mark this so the server can undo 677 // creation if something goes wrong. 678 // We marked it "partitially created" because 679 // the NTVDM has yet not been fully created. 680 // a call to UpdateVdmEntry to update 681 // process handle will signal the NTVDM 682 // process completed creation 683 VDMCreationState = VDM_PARTIALLY_CREATED; 684 // fail the call if NTVDM process is being 685 // created DETACHED. 686 // note that, we let it go if NTVDM process 687 // is already running. 688 if (dwCreationFlags & DETACHED_PROCESS) { 689 SetLastError(ERROR_ACCESS_DENIED); 690 return FALSE; 691 } 692 if (!BaseGetVdmConfigInfo(lpCommandLine, 693 iTask, 694 VdmBinaryType, 695 &VdmNameString, 696 &VdmReserve 697 )) 698 { 699 BaseSetLastNTError(Status); 700 return FALSE; 701 } 702 703 lpCommandLine = VdmNameString.Buffer; 704 lpApplicationName = NULL; 705 706 break; 707 708 case VDM_PRESENT_NOT_READY: 709 SetLastError (ERROR_NOT_READY); 710 return FALSE; 711 712 case VDM_PRESENT_AND_READY: 713 VDMCreationState = VDM_BEING_REUSED; 714 VdmWaitHandle = b->WaitObjectForParent; 715 break; 716 } 717 RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer); 718 FreeBuffer = NULL; 719 RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer); 720 NameBuffer = NULL; 721 VdmReserve--; // we reserve from addr 1 722 if(VdmWaitHandle) 723 goto VdmExists; 724 else{ 725 bInheritHandles = FALSE; 726 if (lpEnvironment && 727 !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT)){ 728 RtlDestroyEnvironment(lpEnvironment); 729 } 730 lpEnvironment = UnicodeStringVDMEnv.Buffer; 731 goto VdmRetry; 732 } 733 } 734 else { 735 736 // 737 // must be a .bat or .cmd file 738 // 739 740 static PWCHAR CmdPrefix = L"cmd /c "; 741 PWCHAR NewCommandLine; 742 ULONG Length; 743 PWCHAR Last4 = &PathName.Buffer[PathName.Length / sizeof( WCHAR )-4]; 744 745 if ( PathName.Length < 8 ) { 746 SetLastError(ERROR_BAD_EXE_FORMAT); 747 return FALSE; 748 } 749 750 if (_wcsnicmp( Last4, L".bat", 4 ) && _wcsnicmp( Last4, L".cmd", 4 )) { 751 SetLastError(ERROR_BAD_EXE_FORMAT); 752 return FALSE; 753 } 754 755 Length = wcslen( CmdPrefix ) 756 + (QuoteCmdLine || QuoteFound ) 757 + wcslen( lpCommandLine ) 758 + (QuoteCmdLine || QuoteFound) 759 + 1; 760 761 NewCommandLine = RtlAllocateHeap( RtlProcessHeap( ), 762 MAKE_TAG( TMP_TAG ), 763 Length * sizeof( WCHAR ) ); 764 765 if (NewCommandLine == NULL) { 766 BaseSetLastNTError(STATUS_NO_MEMORY); 767 return FALSE; 768 } 769 770 wcscpy( NewCommandLine, CmdPrefix ); 771 if (QuoteCmdLine || QuoteFound) { 772 wcscat( NewCommandLine, L"\"" ); 773 } 774 wcscat( NewCommandLine, lpCommandLine ); 775 if (QuoteCmdLine || QuoteFound) { 776 wcscat( NewCommandLine, L"\"" ); 777 } 778 779 RtlInitUnicodeString( &SubSysCommandLine, NewCommandLine ); 780 781 lpCommandLine = SubSysCommandLine.Buffer; 782 783 lpApplicationName = NULL; 784 785 RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer); 786 FreeBuffer = NULL; 787 RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer); 788 NameBuffer = NULL; 789 goto VdmRetry; 790 791 } 792 793 } 794 795 // 16 bit windows exe 796 case STATUS_INVALID_IMAGE_WIN_16: 797 #if defined(BUILD_WOW6432) || defined(_WIN64) 798 if (lpOriginalApplicationName == NULL) { 799 // pass in the part of the command line after the exe name 800 // including whitespace 801 lpCommandLine = ((*TempNull == '\"') ? TempNull + 1 : TempNull); 802 } else { 803 lpCommandLine = lpOriginalCommandLine; 804 } 805 return NtVdm64CreateProcess(lpOriginalApplicationName == NULL, 806 lpApplicationName, // this is now the real file name we've loaded 807 lpCommandLine, 808 lpProcessAttributes, 809 lpThreadAttributes, 810 bInheritHandles, 811 (dwCreationFlags & ~CREATE_UNICODE_ENVIRONMENT), // the environment has already been converted to unicode 812 lpEnvironment, 813 lpCurrentDirectory, 814 lpStartupInfo, 815 lpProcessInformation 816 ); 817 #endif 818 if (dwCreationFlags & CREATE_FORCEDOS) { 819 goto ForceDos; 820 } 821 822 IsWowBinary = TRUE; 823 if (!BaseCreateVDMEnvironment(lpEnvironment, 824 &AnsiStringVDMEnv, 825 &UnicodeStringVDMEnv 826 )) 827 { 828 return FALSE; 829 } 830 831 832 833 RetrySepWow: 834 VdmBinaryType = dwCreationFlags & CREATE_SEPARATE_WOW_VDM 835 ? BINARY_TYPE_SEPWOW : BINARY_TYPE_WIN16; 836 837 if (!BaseCheckVDM(VdmBinaryType, 838 lpApplicationName, 839 lpCommandLine, 840 lpCurrentDirectory, 841 &AnsiStringVDMEnv, 842 &m, 843 &iTask, 844 dwCreationFlags, 845 &StartupInfo 846 )) 847 { 848 // 849 // If we failed with access denied, caller may not 850 // be allowed allowed to access the shared wow's 851 // desktop, so retry as a separate wow 852 // 853 if (VdmBinaryType == BINARY_TYPE_WIN16 && 854 GetLastError() == ERROR_ACCESS_DENIED) 855 { 856 dwCreationFlags |= CREATE_SEPARATE_WOW_VDM; 857 } 858 else { 859 return FALSE; 860 } 861 goto RetrySepWow; 862 } 863 864 865 // Check the return value from the server 866 switch (b->VDMState & VDM_STATE_MASK){ 867 case VDM_NOT_PRESENT: 868 // mark this so the server can undo 869 // creation if something goes wrong. 870 // We marked it "partitially created" because 871 // the NTVDM has yet not been fully created. 872 // a call to UpdateVdmEntry to update 873 // process handle will signal the NTVDM 874 // process completed creation 875 876 VDMCreationState = VDM_PARTIALLY_CREATED; 877 878 if (!BaseGetVdmConfigInfo( 879 lpCommandLine, 880 iTask, 881 VdmBinaryType, 882 &VdmNameString, 883 &VdmReserve 884 )) 885 { 886 BaseSetLastNTError(Status); 887 return FALSE; 888 } 889 890 lpCommandLine = VdmNameString.Buffer; 891 lpApplicationName = NULL; 892 893 894 // 895 // Wow must have a hidden console 896 // Throw away DETACHED_PROCESS flag which isn't 897 // meaningful for Win16 apps. 898 // 899 900 dwCreationFlags |= CREATE_NO_WINDOW; 901 dwCreationFlags &= ~(CREATE_NEW_CONSOLE | DETACHED_PROCESS); 902 903 904 // 905 // We're starting a WOW VDM, turn on feedback unless 906 // the creator passed STARTF_FORCEOFFFEEDBACK. 907 // 908 909 StartupInfo.dwFlags |= STARTF_FORCEONFEEDBACK; 910 911 break; 912 913 case VDM_PRESENT_NOT_READY: 914 SetLastError (ERROR_NOT_READY); 915 return FALSE; 916 917 case VDM_PRESENT_AND_READY: 918 VDMCreationState = VDM_BEING_REUSED; 919 VdmWaitHandle = b->WaitObjectForParent; 920 break; 921 } 922 923 RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer); 924 FreeBuffer = NULL; 925 RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer); 926 NameBuffer = NULL; 927 VdmReserve--; // we reserve from addr 1 928 if(VdmWaitHandle) 929 goto VdmExists; 930 else { 931 bInheritHandles = FALSE; 932 // replace the environment with ours 933 if (lpEnvironment && 934 !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT)) { 935 RtlDestroyEnvironment(lpEnvironment); 936 } 937 lpEnvironment = UnicodeStringVDMEnv.Buffer; 938 goto VdmRetry; 939 } 940 941 942 default : 943 SetLastError(ERROR_BAD_EXE_FORMAT); 944 return FALSE; 945 } 946 } 947 948 // 949 // Make sure only WOW apps can have the CREATE_SEPARATE_WOW_VDM flag. 950 // 951 952 if (!IsWowBinary && (dwCreationFlags & CREATE_SEPARATE_WOW_VDM)) { 953 dwCreationFlags &= ~CREATE_SEPARATE_WOW_VDM; 954 } 955 956 // 957 // Query the section to determine the stack parameters and 958 // image entrypoint. 959 // 960 961 Status = NtQuerySection( 962 SectionHandle, 963 SectionImageInformation, 964 &ImageInformation, 965 sizeof( ImageInformation ), 966 NULL 967 ); 968 969 if (!NT_SUCCESS( Status )) { 970 BaseSetLastNTError(Status); 971 RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer); 972 FreeBuffer = NULL; 973 return FALSE; 974 } 975 976 if (ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL) { 977 SetLastError(ERROR_BAD_EXE_FORMAT); 978 RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer); 979 FreeBuffer = NULL; 980 return FALSE; 981 } 982 983 ImageFileDebuggerCommand[ 0 ] = UNICODE_NULL; 984 if (!(dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) || 985 NtCurrentPeb()->ReadImageFileExecOptions 986 ) { 987 LdrQueryImageFileExecutionOptions( &PathName, 988 L"Debugger", 989 REG_SZ, 990 ImageFileDebuggerCommand, 991 sizeof( ImageFileDebuggerCommand ), 992 NULL 993 ); 994 } 995 996 997 998 if ((ImageInformation.Machine < USER_SHARED_DATA->ImageNumberLow) || 999 (ImageInformation.Machine > USER_SHARED_DATA->ImageNumberHigh)) { 1000 #if defined(WX86) || defined(_AXP64_) 1001 if (ImageInformation.Machine == IMAGE_FILE_MACHINE_I386) 1002 { 1003 Wx86Info = (HANDLE)UIntToPtr(sizeof(WX86TIB)); 1004 } 1005 else 1006 #endif // WX86 1007 #if defined(_AXP64_) 1008 if (ImageInformation.Machine == IMAGE_FILE_MACHINE_ALPHA) { 1009 // Fall through since this is a valid machine type. 1010 } 1011 else 1012 #elif defined(_IA64_) 1013 if (ImageInformation.Machine == IMAGE_FILE_MACHINE_I386) { 1014 // Fall through since this is a valid machine type. 1015 } 1016 else 1017 #endif // _AXP64_ 1018 #if defined(BUILD_WOW6432) 1019 // 32-bit kernel32.dll on NT64 can run 64-bit binaries 1020 #if defined(_ALPHA_) 1021 if (ImageInformation.Machine == IMAGE_FILE_MACHINE_ALPHA) { 1022 // Fall through since this is a valid machine type. 1023 } 1024 else 1025 #elif defined(_X86_) 1026 if (ImageInformation.Machine == IMAGE_FILE_MACHINE_I386) { 1027 // Fall through since this is a valid machine type. 1028 } 1029 else 1030 #endif // ALPHA or IA64 1031 #endif // BUILD_WOW6432 1032 { 1033 ULONG_PTR ErrorParameters[2]; 1034 ULONG ErrorResponse; 1035 1036 ErrorResponse = ResponseOk; 1037 ErrorParameters[0] = (ULONG_PTR)&PathName; 1038 1039 NtRaiseHardError( STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE, 1040 1, 1041 1, 1042 ErrorParameters, 1043 OptionOk, 1044 &ErrorResponse 1045 ); 1046 if ( NtCurrentPeb()->ImageSubsystemMajorVersion <= 3 ) { 1047 SetLastError(ERROR_BAD_EXE_FORMAT); 1048 } 1049 else { 1050 SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH); 1051 } 1052 RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer); 1053 FreeBuffer = NULL; 1054 return FALSE; 1055 } 1056 } 1057 1058 RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer); 1059 FreeBuffer = NULL; 1060 if ( ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_GUI && 1061 ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_CUI ) { 1062 1063 // POSIX exe 1064 1065 NtClose(SectionHandle); 1066 SectionHandle = NULL; 1067 1068 if ( ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_POSIX_CUI ) { 1069 1070 if ( !BuildSubSysCommandLine( L"POSIX /P ", 1071 lpApplicationName, 1072 lpCommandLine, 1073 &SubSysCommandLine 1074 ) ) { 1075 return FALSE; 1076 } 1077 1078 lpCommandLine = SubSysCommandLine.Buffer; 1079 1080 lpApplicationName = NULL; 1081 RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer); 1082 NameBuffer = NULL; 1083 goto VdmRetry; 1084 } 1085 else { 1086 SetLastError(ERROR_CHILD_NOT_COMPLETE); 1087 return FALSE; 1088 } 1089 } 1090 else { 1091 if (!BasepIsImageVersionOk( ImageInformation.SubSystemMajorVersion, 1092 ImageInformation.SubSystemMinorVersion) ) { 1093 SetLastError(ERROR_BAD_EXE_FORMAT); 1094 return FALSE; 1095 } 1096 } 1097 1098 if (ImageFileDebuggerCommand[ 0 ] != UNICODE_NULL) { 1099 USHORT n; 1100 1101 n = (USHORT)wcslen( lpCommandLine ); 1102 if (n == 0) { 1103 lpCommandLine = (LPWSTR)lpApplicationName; 1104 n = (USHORT)wcslen( lpCommandLine ); 1105 } 1106 1107 n += wcslen( ImageFileDebuggerCommand ) + 1 + 2; 1108 n *= sizeof( WCHAR ); 1109 1110 SubSysCommandLine.Buffer = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( TMP_TAG ), n ); 1111 SubSysCommandLine.Length = 0; 1112 SubSysCommandLine.MaximumLength = n; 1113 RtlAppendUnicodeToString( &SubSysCommandLine, ImageFileDebuggerCommand ); 1114 RtlAppendUnicodeToString( &SubSysCommandLine, L" " ); 1115 RtlAppendUnicodeToString( &SubSysCommandLine, (PWSTR)lpCommandLine ); 1116 #if DBG 1117 DbgPrint( "BASE: Calling debugger with '%wZ'\n", &SubSysCommandLine ); 1118 #endif 1119 lpCommandLine = SubSysCommandLine.Buffer; 1120 lpApplicationName = NULL; 1121 1122 NtClose(SectionHandle); 1123 SectionHandle = NULL; 1124 RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer); 1125 NameBuffer = NULL; 1126 goto VdmRetry; 1127 } 1128 1129 // 1130 // Create the process object 1131 // 1132 1133 pObja = BaseFormatObjectAttributes(&Obja,lpProcessAttributes,NULL); 1134 1135 if (dwCreationFlags & CREATE_BREAKAWAY_FROM_JOB ) { 1136 SectionHandle = (HANDLE)( (UINT_PTR)SectionHandle | 1); 1137 } 1138 1139 Status = NtCreateProcess( 1140 &ProcessHandle, 1141 PROCESS_ALL_ACCESS, 1142 pObja, 1143 NtCurrentProcess(), 1144 (BOOLEAN)bInheritHandles, 1145 SectionHandle, 1146 NULL, 1147 NULL 1148 ); 1149 if ( !NT_SUCCESS(Status) ) { 1150 BaseSetLastNTError(Status); 1151 return FALSE; 1152 } 1153 1154 // 1155 // NtCreateProcess will set to normal OR inherit if parent is IDLE or Below 1156 // only override if a mask is given during the create. 1157 // 1158 1159 if ( PriClass.PriorityClass != PROCESS_PRIORITY_CLASS_UNKNOWN ) { 1160 State = NULL; 1161 if ( PriClass.PriorityClass == PROCESS_PRIORITY_CLASS_REALTIME ) { 1162 State = BasepIsRealtimeAllowed(TRUE); 1163 } 1164 Status = NtSetInformationProcess( 1165 ProcessHandle, 1166 ProcessPriorityClass, 1167 (PVOID)&PriClass, 1168 sizeof(PriClass) 1169 ); 1170 if ( State ) { 1171 BasepReleasePrivilege( State ); 1172 } 1173 1174 if ( !NT_SUCCESS(Status) ) { 1175 BaseSetLastNTError(Status); 1176 return FALSE; 1177 } 1178 } 1179 1180 NtClose(SectionHandle); 1181 SectionHandle = NULL; 1182 1183 if (dwCreationFlags & CREATE_DEFAULT_ERROR_MODE) { 1184 UINT NewMode; 1185 NewMode = SEM_FAILCRITICALERRORS; 1186 NtSetInformationProcess( 1187 ProcessHandle, 1188 ProcessDefaultHardErrorMode, 1189 (PVOID) &NewMode, 1190 sizeof(NewMode) 1191 ); 1192 } 1193 1194 // 1195 // If the process is being created for a VDM call the server with 1196 // process handle. 1197 // 1198 1199 if (VdmBinaryType) { 1200 VdmWaitHandle = ProcessHandle; 1201 if (!BaseUpdateVDMEntry(UPDATE_VDM_PROCESS_HANDLE, 1202 &VdmWaitHandle, 1203 iTask, 1204 VdmBinaryType 1205 )) 1206 { 1207 //make sure we don't close the handle twice -- 1208 //(VdmWaitHandle == ProcessHandle) if we don't do this. 1209 VdmWaitHandle = NULL; 1210 return FALSE; 1211 } 1212 1213 // 1214 // For Sep wow the VdmWaitHandle = NULL (there is none!) 1215 // 1216 1217 VDMCreationState |= VDM_FULLY_CREATED; 1218 } 1219 1220 // 1221 // if we're a detached priority, we don't have the focus, so 1222 // don't create with boosted priority. 1223 // 1224 1225 if (dwCreationFlags & DETACHED_PROCESS) { 1226 KPRIORITY SetBasePriority; 1227 1228 SetBasePriority = (KPRIORITY)NORMAL_BASE_PRIORITY; 1229 Status = NtSetInformationProcess(ProcessHandle, 1230 ProcessBasePriority, 1231 (PVOID) &SetBasePriority, 1232 sizeof(SetBasePriority) 1233 ); 1234 ASSERT(NT_SUCCESS(Status)); 1235 } 1236 1237 #if defined(i386) || defined(_IA64_) 1238 // 1239 // Reserve memory in the new process' address space if necessary 1240 // (for vdms). This is required only for x86 system. 1241 // 1242 1243 if ( VdmReserve ) { 1244 BigVdmReserve = VdmReserve; 1245 Status = NtAllocateVirtualMemory( 1246 ProcessHandle, 1247 &BaseAddress, 1248 0L, 1249 &BigVdmReserve, 1250 MEM_RESERVE, 1251 PAGE_EXECUTE_READWRITE 1252 ); 1253 if ( !NT_SUCCESS(Status) ){ 1254 BaseSetLastNTError(Status); 1255 return FALSE; 1256 } 1257 } 1258 #endif 1259 1260 // 1261 // Determine the location of the 1262 // processes PEB. 1263 // 1264 1265 Status = NtQueryInformationProcess( 1266 ProcessHandle, 1267 ProcessBasicInformation, 1268 &ProcessInfo, 1269 sizeof( ProcessInfo ), 1270 NULL 1271 ); 1272 if ( !NT_SUCCESS( Status ) ) { 1273 BaseSetLastNTError(Status); 1274 return FALSE; 1275 } 1276 1277 Peb = ProcessInfo.PebBaseAddress; 1278 1279 // 1280 // Push the parameters into the address space of the new process 1281 // 1282 1283 if ( ARGUMENT_PRESENT(lpCurrentDirectory) ) { 1284 CurdirBuffer = RtlAllocateHeap( RtlProcessHeap(), 1285 MAKE_TAG( TMP_TAG ), 1286 (MAX_PATH + 1) * sizeof( WCHAR ) ); 1287 if ( !CurdirBuffer ) { 1288 BaseSetLastNTError(STATUS_NO_MEMORY); 1289 return FALSE; 1290 } 1291 CurdirLength2 = GetFullPathNameW( 1292 lpCurrentDirectory, 1293 MAX_PATH, 1294 CurdirBuffer, 1295 &CurdirFilePart 1296 ); 1297 if ( CurdirLength2 > MAX_PATH ) { 1298 SetLastError(ERROR_DIRECTORY); 1299 return FALSE; 1300 } 1301 1302 // 1303 // now make sure the directory exists 1304 // 1305 1306 CurdirLength = GetFileAttributesW(CurdirBuffer); 1307 if ( (CurdirLength == 0xffffffff) || 1308 !(CurdirLength & FILE_ATTRIBUTE_DIRECTORY) ) { 1309 SetLastError(ERROR_DIRECTORY); 1310 return FALSE; 1311 } 1312 } 1313 1314 1315 if ( QuoteInsert || QuoteCmdLine) { 1316 QuotedBuffer = RtlAllocateHeap(RtlProcessHeap(),0,wcslen(lpCommandLine)*2+6); 1317 1318 if ( QuotedBuffer ) { 1319 wcscpy(QuotedBuffer,L"\""); 1320 1321 if ( QuoteInsert ) { 1322 TempChar = *TempNull; 1323 *TempNull = UNICODE_NULL; 1324 } 1325 1326 wcscat(QuotedBuffer,lpCommandLine); 1327 wcscat(QuotedBuffer,L"\""); 1328 1329 if ( QuoteInsert ) { 1330 *TempNull = TempChar; 1331 wcscat(QuotedBuffer,TempNull); 1332 } 1333 1334 } 1335 else { 1336 if ( QuoteInsert ) { 1337 QuoteInsert = FALSE; 1338 } 1339 if ( QuoteCmdLine ) { 1340 QuoteCmdLine = FALSE; 1341 } 1342 } 1343 } 1344 1345 1346 if (!BasePushProcessParameters( 1347 ProcessHandle, 1348 Peb, 1349 lpApplicationName, 1350 CurdirBuffer, 1351 QuoteInsert || QuoteCmdLine ? QuotedBuffer : lpCommandLine, 1352 lpEnvironment, 1353 &StartupInfo, 1354 dwCreationFlags | dwNoWindow, 1355 bInheritHandles, 1356 IsWowBinary ? IMAGE_SUBSYSTEM_WINDOWS_GUI : 0 1357 ) ) { 1358 return FALSE; 1359 } 1360 1361 1362 RtlFreeUnicodeString(&VdmNameString); 1363 VdmNameString.Buffer = NULL; 1364 1365 // 1366 // Stuff in the standard handles if needed 1367 // 1368 if (!VdmBinaryType && 1369 !bInheritHandles && 1370 !(StartupInfo.dwFlags & STARTF_USESTDHANDLES) && 1371 !(dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE | CREATE_NO_WINDOW)) && 1372 ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_CUI 1373 ) { 1374 PRTL_USER_PROCESS_PARAMETERS ParametersInNewProcess; 1375 1376 Status = NtReadVirtualMemory( ProcessHandle, 1377 &Peb->ProcessParameters, 1378 &ParametersInNewProcess, 1379 sizeof( ParametersInNewProcess ), 1380 NULL 1381 ); 1382 if (NT_SUCCESS( Status )) { 1383 if (!CONSOLE_HANDLE( NtCurrentPeb()->ProcessParameters->StandardInput )) { 1384 StuffStdHandle( ProcessHandle, 1385 NtCurrentPeb()->ProcessParameters->StandardInput, 1386 &ParametersInNewProcess->StandardInput 1387 ); 1388 } 1389 if (!CONSOLE_HANDLE( NtCurrentPeb()->ProcessParameters->StandardOutput )) { 1390 StuffStdHandle( ProcessHandle, 1391 NtCurrentPeb()->ProcessParameters->StandardOutput, 1392 &ParametersInNewProcess->StandardOutput 1393 ); 1394 } 1395 if (!CONSOLE_HANDLE( NtCurrentPeb()->ProcessParameters->StandardError )) { 1396 StuffStdHandle( ProcessHandle, 1397 NtCurrentPeb()->ProcessParameters->StandardError, 1398 &ParametersInNewProcess->StandardError 1399 ); 1400 } 1401 } 1402 } 1403 1404 // 1405 // Create the thread... 1406 // 1407 1408 // 1409 // Allocate a stack for this thread in the address space of the target 1410 // process. 1411 // 1412 1413 StackStatus = BaseCreateStack( 1414 ProcessHandle, 1415 ImageInformation.CommittedStackSize, 1416 (ImageInformation.MaximumStackSize < 256*1024) ? 256*1024 : ImageInformation.MaximumStackSize, 1417 &InitialTeb 1418 ); 1419 1420 if ( !NT_SUCCESS(StackStatus) ) { 1421 BaseSetLastNTError(StackStatus); 1422 return FALSE; 1423 } 1424 1425 1426 // 1427 // Create an initial context for the new thread. 1428 // 1429 1430 BaseInitializeContext( 1431 &ThreadContext, 1432 Peb, 1433 ImageInformation.TransferAddress, 1434 InitialTeb.StackBase, 1435 BaseContextTypeProcess 1436 ); 1437 1438 1439 // 1440 // Create the actual thread object 1441 // 1442 1443 pObja = BaseFormatObjectAttributes(&Obja,lpThreadAttributes,NULL); 1444 1445 Status = NtCreateThread( 1446 &ThreadHandle, 1447 THREAD_ALL_ACCESS, 1448 pObja, 1449 ProcessHandle, 1450 &ClientId, 1451 &ThreadContext, 1452 &InitialTeb, 1453 TRUE 1454 ); 1455 1456 if (!NT_SUCCESS(Status) ) { 1457 BaseSetLastNTError(Status); 1458 return FALSE; 1459 } 1460 1461 // 1462 // From here on out, do not modify the address space of the 1463 // new process. WOW64's implementation of NtCreateThread() 1464 // reshuffles the new process' address space if the current 1465 // process is 32-bit and the new process is 64-bit. 1466 // 1467 #if DBG 1468 Peb = NULL; 1469 #endif 1470 1471 #if defined(WX86) || defined(_AXP64_) 1472 1473 // 1474 // if this is a Wx86 Process, setup for a Wx86 emulated Thread 1475 // 1476 1477 if (Wx86Info) { 1478 1479 // 1480 // create a WX86Tib and initialize it's Teb->Vdm. 1481 // 1482 Status = BaseCreateWx86Tib(ProcessHandle, 1483 ThreadHandle, 1484 (ULONG)((ULONG_PTR)ImageInformation.TransferAddress), 1485 (ULONG)ImageInformation.CommittedStackSize, 1486 (ULONG)ImageInformation.MaximumStackSize, 1487 TRUE 1488 ); 1489 1490 if (!NT_SUCCESS(Status)) { 1491 BaseSetLastNTError(Status); 1492 return( FALSE ); 1493 } 1494 1495 1496 // 1497 // Mark Process as WX86 1498 // 1499 Status = NtSetInformationProcess (ProcessHandle, 1500 ProcessWx86Information, 1501 &Wx86Info, 1502 sizeof(Wx86Info) 1503 ); 1504 1505 if (!NT_SUCCESS(Status)) { 1506 BaseSetLastNTError(Status); 1507 return( FALSE ); 1508 } 1509 } 1510 #endif 1511 1512 1513 // 1514 // Call the Windows server to let it know about the 1515 // process. 1516 // 1517 1518 a->ProcessHandle = ProcessHandle; 1519 a->ThreadHandle = ThreadHandle; 1520 a->ClientId = ClientId; 1521 a->CreationFlags = dwCreationFlags; 1522 1523 if ( dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS) ) { 1524 Status = DbgUiConnectToDbg(); 1525 if ( !NT_SUCCESS(Status) ) { 1526 NtTerminateProcess(ProcessHandle, Status); 1527 BaseSetLastNTError(Status); 1528 return FALSE; 1529 } 1530 a->DebuggerClientId = NtCurrentTeb()->ClientId; 1531 } 1532 else { 1533 a->DebuggerClientId.UniqueProcess = NULL; 1534 a->DebuggerClientId.UniqueThread = NULL; 1535 } 1536 1537 // 1538 // Set the 2 bit if a gui app is starting. The window manager needs to 1539 // know this so it can synchronize the startup of this app 1540 // (WaitForInputIdle api). This info is passed using the process 1541 // handle tag bits. The 1 bit asks the window manager to turn on 1542 // or turn off the application start cursor (hourglass/pointer). 1543 // 1544 // When starting a WOW process, lie and tell UserSrv NTVDM.EXE is a GUI 1545 // process. We also turn on bit 0x8 so that UserSrv can ignore the 1546 // UserNotifyConsoleApplication call made by the console during startup. 1547 // 1548 1549 if ( ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_GUI || 1550 IsWowBinary ) { 1551 1552 a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle | 2); 1553 1554 // 1555 // If the creating process is a GUI app, turn on the app. start cursor 1556 // by default. This can be overridden by STARTF_FORCEOFFFEEDBACK. 1557 // 1558 1559 NtHeaders = RtlImageNtHeader((PVOID)GetModuleHandle(NULL)); 1560 if ( NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI ) 1561 a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle | 1); 1562 1563 } 1564 1565 1566 // 1567 // If feedback is forced on, turn it on. If forced off, turn it off. 1568 // Off overrides on. 1569 // 1570 1571 if (StartupInfo.dwFlags & STARTF_FORCEONFEEDBACK) 1572 a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle | 1); 1573 if (StartupInfo.dwFlags & STARTF_FORCEOFFFEEDBACK) 1574 a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle & ~1); 1575 1576 a->VdmBinaryType = VdmBinaryType; // just tell server the truth 1577 1578 if (VdmBinaryType){ 1579 a->hVDM = iTask ? 0 : NtCurrentPeb()->ProcessParameters->ConsoleHandle; 1580 a->VdmTask = iTask; 1581 } 1582 1583 #if defined(BUILD_WOW6432) 1584 m.ReturnValue = CsrBasepCreateProcess(a); 1585 #else 1586 CsrClientCallServer( (PCSR_API_MSG)&m, 1587 NULL, 1588 CSR_MAKE_API_NUMBER( BASESRV_SERVERDLL_INDEX, 1589 BasepCreateProcess 1590 ), 1591 sizeof( *a ) 1592 ); 1593 #endif 1594 1595 if (!NT_SUCCESS((NTSTATUS)m.ReturnValue)) { 1596 BaseSetLastNTError((NTSTATUS)m.ReturnValue); 1597 NtTerminateProcess(ProcessHandle, (NTSTATUS)m.ReturnValue); 1598 return FALSE; 1599 } 1600 1601 1602 if (!( dwCreationFlags & CREATE_SUSPENDED) ) { 1603 NtResumeThread(ThreadHandle,&i); 1604 } 1605 1606 VdmExists: 1607 bStatus = TRUE; 1608 if (VDMCreationState) 1609 VDMCreationState |= VDM_CREATION_SUCCESSFUL; 1610 1611 try { 1612 if (VdmWaitHandle) { 1613 1614 // 1615 // tag Shared WOW VDM handles so that wait for input idle has a 1616 // chance to work. Shared WOW VDM "process" handles are actually 1617 // event handles, Separate WOW VDM handles are real process 1618 // handles. Also mark DOS handles with 0x1 so WaitForInputIdle 1619 // has a way to distinguish DOS apps and not block forever. 1620 // 1621 1622 if (VdmBinaryType == BINARY_TYPE_WIN16) { 1623 lpProcessInformation->hProcess = 1624 (HANDLE)((ULONG_PTR)VdmWaitHandle | 0x2); 1625 1626 // 1627 // Shared WOW doesn't always start a process, so 1628 // we don't have a process ID or thread ID to 1629 // return if the VDM already existed. 1630 // 1631 // Separate WOW doesn't hit this codepath 1632 // (no VdmWaitHandle). 1633 // 1634 1635 if (VDMCreationState & VDM_BEING_REUSED) { 1636 ClientId.UniqueProcess = 0; 1637 ClientId.UniqueThread = 0; 1638 } 1639 1640 } 1641 else { 1642 lpProcessInformation->hProcess = 1643 (HANDLE)((ULONG_PTR)VdmWaitHandle | 0x1); 1644 } 1645 1646 1647 // 1648 // Close the ProcessHandle, since we are returning the 1649 // VdmProcessHandle instead. 1650 // 1651 1652 if (ProcessHandle != NULL) 1653 NtClose(ProcessHandle); 1654 } 1655 else{ 1656 lpProcessInformation->hProcess = ProcessHandle; 1657 } 1658 1659 lpProcessInformation->hThread = ThreadHandle; 1660 lpProcessInformation->dwProcessId = HandleToUlong(ClientId.UniqueProcess); 1661 lpProcessInformation->dwThreadId = HandleToUlong(ClientId.UniqueThread); 1662 ProcessHandle = NULL; 1663 ThreadHandle = NULL; 1664 } 1665 except ( EXCEPTION_EXECUTE_HANDLER ) { 1666 NtClose( ProcessHandle ); 1667 NtClose( ThreadHandle ); 1668 ProcessHandle = NULL; 1669 ThreadHandle = NULL; 1670 if (VDMCreationState) 1671 VDMCreationState &= ~VDM_CREATION_SUCCESSFUL; 1672 } 1673 } 1674 finally { 1675 if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT) ) { 1676 RtlDestroyEnvironment(lpEnvironment); 1677 lpEnvironment = NULL; 1678 } 1679 RtlFreeHeap(RtlProcessHeap(), 0,QuotedBuffer); 1680 RtlFreeHeap(RtlProcessHeap(), 0,NameBuffer); 1681 RtlFreeHeap(RtlProcessHeap(), 0,CurdirBuffer); 1682 RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer); 1683 if ( FileHandle ) { 1684 NtClose(FileHandle); 1685 } 1686 if ( SectionHandle ) { 1687 NtClose(SectionHandle); 1688 } 1689 if ( ThreadHandle ) { 1690 NtTerminateProcess(ProcessHandle,STATUS_SUCCESS); 1691 NtClose(ThreadHandle); 1692 } 1693 if ( ProcessHandle ) { 1694 NtClose(ProcessHandle); 1695 } 1696 RtlFreeUnicodeString(&VdmNameString); 1697 RtlFreeUnicodeString(&SubSysCommandLine); 1698 if (AnsiStringVDMEnv.Buffer || UnicodeStringVDMEnv.Buffer) 1699 BaseDestroyVDMEnvironment(&AnsiStringVDMEnv, &UnicodeStringVDMEnv); 1700 1701 if (VDMCreationState && !(VDMCreationState & VDM_CREATION_SUCCESSFUL)){ 1702 BaseUpdateVDMEntry ( 1703 UPDATE_VDM_UNDO_CREATION, 1704 (HANDLE *)&iTask, 1705 VDMCreationState, 1706 VdmBinaryType 1707 ); 1708 if(VdmWaitHandle) { 1709 NtClose(VdmWaitHandle); 1710 } 1711 } 1712 } 1713 1714 if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT) ) { 1715 RtlDestroyEnvironment(lpEnvironment); 1716 } 1717 return bStatus; 1718 }
// // Open the file for execute access // Status = NtOpenFile( &FileHandle, SYNCHRONIZE | FILE_EXECUTE, &Obja, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE );
// // Create a section object backed by the file // Status = NtCreateSection( &SectionHandle, SECTION_ALL_ACCESS, NULL, NULL, PAGE_EXECUTE, SEC_IMAGE, FileHandle );
继续向下看关键函数:NtCreateProcess出现啦!
Status = NtCreateProcess( &ProcessHandle, PROCESS_ALL_ACCESS, pObja, NtCurrentProcess(), (BOOLEAN)bInheritHandles, SectionHandle, NULL, NULL );
在源码中继续延伸,发现调用过程是NtCreateProcess——>NtCreateProcessEx——>PspCreateProcess(Windows2000源码中是没有NtCreateProcessEx函数这个中间过程的,我是在另外一套源码中找到的NtCreateProcessEx函数,但无奈另一套较新的源码中又没有CreateProcessW的定义,故而开始参考的是Windows2000 源码)
NtCreateProcess函数和NtCreateProcessEx函数:
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); } NTSTATUS NtCreateProcessEx( __out PHANDLE ProcessHandle, __in ACCESS_MASK DesiredAccess, __in_opt POBJECT_ATTRIBUTES ObjectAttributes, __in HANDLE ParentProcess, __in ULONG Flags, __in_opt HANDLE SectionHandle, __in_opt HANDLE DebugPort, __in_opt HANDLE ExceptionPort, __in ULONG JobMemberLevel ) { NTSTATUS Status; PAGED_CODE(); if (KeGetPreviousMode() != KernelMode) { // // Probe all arguments // try { ProbeForWriteHandle (ProcessHandle); } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode (); } } if (ARGUMENT_PRESENT (ParentProcess)) { Status = PspCreateProcess (ProcessHandle, DesiredAccess, ObjectAttributes, ParentProcess, Flags, SectionHandle, DebugPort, ExceptionPort, JobMemberLevel); } else { Status = STATUS_INVALID_PARAMETER; } return Status; }
看以看到NtCreateProcess函数简单地对处理一下参数,然后把创建进程的任务交给NtCreateProcessEx函数。
NtCreateProcessEx函数的流程。它也只是简单地检查ProcessHandle参数代表的句柄是否可写,ParentProcess是否不为空。真正的创建工作交给PspCreateProcess函数。
NTSTATUS PspCreateProcess( OUT PHANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN HANDLE ParentProcess OPTIONAL, IN ULONG Flags, IN HANDLE SectionHandle OPTIONAL, IN HANDLE DebugPort OPTIONAL, IN HANDLE ExceptionPort OPTIONAL, IN ULONG JobMemberLevel ) /*++ Routine Description: This routine creates and initializes a process object. It implements the foundation for NtCreateProcess and for system initialization process creation. --*/ { NTSTATUS Status; PEPROCESS Process; PEPROCESS CurrentProcess; PEPROCESS Parent; PETHREAD CurrentThread; KAFFINITY Affinity; KPRIORITY BasePriority; PVOID SectionObject; PVOID ExceptionPortObject; PVOID DebugPortObject; ULONG WorkingSetMinimum, WorkingSetMaximum; HANDLE LocalProcessHandle; KPROCESSOR_MODE PreviousMode; INITIAL_PEB InitialPeb; BOOLEAN CreatePeb; ULONG_PTR DirectoryTableBase[2]; BOOLEAN AccessCheck; BOOLEAN MemoryAllocated; PSECURITY_DESCRIPTOR SecurityDescriptor; SECURITY_SUBJECT_CONTEXT SubjectContext; NTSTATUS accesst; NTSTATUS SavedStatus; ULONG ImageFileNameSize; HANDLE_TABLE_ENTRY CidEntry; PEJOB Job; PPEB Peb; AUX_ACCESS_DATA AuxData; PACCESS_STATE AccessState; ACCESS_STATE LocalAccessState; BOOLEAN UseLargePages; SCHAR QuantumReset; #if defined(_WIN64) INITIAL_PEB32 InitialPeb32; #endif PAGED_CODE(); CurrentThread = PsGetCurrentThread (); PreviousMode = KeGetPreviousModeByThread(&CurrentThread->Tcb); CurrentProcess = PsGetCurrentProcessByThread (CurrentThread); CreatePeb = FALSE; UseLargePages = FALSE; DirectoryTableBase[0] = 0; DirectoryTableBase[1] = 0; Peb = NULL; // // Reject bogus create parameters for future expansion // if (Flags&~PROCESS_CREATE_FLAGS_LEGAL_MASK) { return STATUS_INVALID_PARAMETER; } // // Parent // if (ARGUMENT_PRESENT (ParentProcess)) { Status = ObReferenceObjectByHandle (ParentProcess, PROCESS_CREATE_PROCESS, PsProcessType, PreviousMode, &Parent, NULL); if (!NT_SUCCESS (Status)) { return Status; } if (JobMemberLevel != 0 && Parent->Job == NULL) { ObDereferenceObject (Parent); return STATUS_INVALID_PARAMETER; } Affinity = Parent->Pcb.Affinity; WorkingSetMinimum = PsMinimumWorkingSet; WorkingSetMaximum = PsMaximumWorkingSet; } else { Parent = NULL; Affinity = KeActiveProcessors; WorkingSetMinimum = PsMinimumWorkingSet; WorkingSetMaximum = PsMaximumWorkingSet; } // // Create the process object // Status = ObCreateObject (PreviousMode, PsProcessType, ObjectAttributes, PreviousMode, NULL, sizeof (EPROCESS), 0, 0, &Process); if (!NT_SUCCESS (Status)) { goto exit_and_deref_parent; } // // The process object is created set to NULL. Errors // That occur after this step cause the process delete // routine to be entered. // // Teardown actions that occur in the process delete routine // do not need to be performed inline. // RtlZeroMemory (Process, sizeof(EPROCESS)); ExInitializeRundownProtection (&Process->RundownProtect); PspInitializeProcessLock (Process); InitializeListHead (&Process->ThreadListHead); #if defined(_WIN64) if (Flags & PROCESS_CREATE_FLAGS_OVERRIDE_ADDRESS_SPACE) { PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_OVERRIDE_ADDRESS_SPACE); } #endif PspInheritQuota (Process, Parent); ObInheritDeviceMap (Process, Parent); if (Parent != NULL) { Process->DefaultHardErrorProcessing = Parent->DefaultHardErrorProcessing; Process->InheritedFromUniqueProcessId = Parent->UniqueProcessId; } else { Process->DefaultHardErrorProcessing = PROCESS_HARDERROR_DEFAULT; Process->InheritedFromUniqueProcessId = NULL; } // // Section // if (ARGUMENT_PRESENT (SectionHandle)) { Status = ObReferenceObjectByHandle (SectionHandle, SECTION_MAP_EXECUTE, MmSectionObjectType, PreviousMode, &SectionObject, NULL); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } } else { SectionObject = NULL; if (Parent != PsInitialSystemProcess) { // // Fetch the section pointer from the parent process // as we will be cloning. Since the section pointer // is removed at last thread exit we need to protect against // process exit here to be safe. // if (ExAcquireRundownProtection (&Parent->RundownProtect)) { SectionObject = Parent->SectionObject; if (SectionObject != NULL) { ObReferenceObject (SectionObject); } ExReleaseRundownProtection (&Parent->RundownProtect); } if (SectionObject == NULL) { Status = STATUS_PROCESS_IS_TERMINATING; goto exit_and_deref; } } } Process->SectionObject = SectionObject; // // DebugPort // if (ARGUMENT_PRESENT (DebugPort)) { Status = ObReferenceObjectByHandle (DebugPort, DEBUG_PROCESS_ASSIGN, DbgkDebugObjectType, PreviousMode, &DebugPortObject, NULL); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } Process->DebugPort = DebugPortObject; if (Flags&PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT) { PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_NO_DEBUG_INHERIT); } } else { if (Parent != NULL) { DbgkCopyProcessDebugPort (Process, Parent); } } // // ExceptionPort // if (ARGUMENT_PRESENT (ExceptionPort)) { Status = ObReferenceObjectByHandle (ExceptionPort, 0, LpcPortObjectType, PreviousMode, &ExceptionPortObject, NULL); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } Process->ExceptionPort = ExceptionPortObject; } Process->ExitStatus = STATUS_PENDING; // // Clone parent's object table. // If no parent (booting) then use the current object table created in // ObInitSystem. // if (Parent != NULL) { // // Calculate address space // // If Parent == PspInitialSystem // if (!MmCreateProcessAddressSpace (WorkingSetMinimum, Process, &DirectoryTableBase[0])) { Status = STATUS_INSUFFICIENT_RESOURCES; goto exit_and_deref; } } else { Process->ObjectTable = CurrentProcess->ObjectTable; // // Initialize the Working Set Mutex and address creation mutex // for this "hand built" process. // Normally, the call to MmInitializeAddressSpace initializes the // working set mutex, however, in this case, we have already initialized // the address space and we are now creating a second process using // the address space of the idle thread. // Status = MmInitializeHandBuiltProcess (Process, &DirectoryTableBase[0]); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } } PS_SET_BITS (&Process->Flags, PS_PROCESS_FLAGS_HAS_ADDRESS_SPACE); Process->Vm.MaximumWorkingSetSize = WorkingSetMaximum; KeInitializeProcess (&Process->Pcb, NORMAL_BASE_PRIORITY, Affinity, &DirectoryTableBase[0], (BOOLEAN)(Process->DefaultHardErrorProcessing & PROCESS_HARDERROR_ALIGNMENT_BIT)); // // Initialize the security fields of the process // The parent may be null exactly once (during system init). // Thereafter, a parent is always required so that we have a // security context to duplicate for the new process. // Status = PspInitializeProcessSecurity (Parent, Process); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } Process->PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL; if (Parent != NULL) { if (Parent->PriorityClass == PROCESS_PRIORITY_CLASS_IDLE || Parent->PriorityClass == PROCESS_PRIORITY_CLASS_BELOW_NORMAL) { Process->PriorityClass = Parent->PriorityClass; } // // if address space creation worked, then when going through // delete, we will attach. Of course, attaching means that the kprocess // must be initialized, so we delay the object stuff till here. // Status = ObInitProcess ((Flags&PROCESS_CREATE_FLAGS_INHERIT_HANDLES) ? Parent : NULL, Process); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } } else { Status = MmInitializeHandBuiltProcess2 (Process); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } } Status = STATUS_SUCCESS; SavedStatus = STATUS_SUCCESS; // // Initialize the process address space // The address space has four possibilities // // 1 - Boot Process. Address space is initialized during // MmInit. Parent is not specified. // // 2 - System Process. Address space is a virgin address // space that only maps system space. Process is same // as PspInitialSystemProcess. // // 3 - User Process (Cloned Address Space). Address space // is cloned from the specified process. // // 4 - User Process (New Image Address Space). Address space // is initialized so that it maps the specified section. // if (SectionHandle != NULL) { // // User Process (New Image Address Space). Don't specify Process to // clone, just SectionObject. // // Passing in the 4th parameter as below lets the EPROCESS struct contain its image file name, provided that // appropriate audit settings are enabled. Memory is allocated inside of MmInitializeProcessAddressSpace // and pointed to by ImageFileName, so that must be freed in the process deletion routine (PspDeleteProcess()) // Status = MmInitializeProcessAddressSpace (Process, NULL, SectionObject, &Flags, &(Process->SeAuditProcessCreationInfo.ImageFileName)); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } // // In order to support relocating executables, the proper status // (STATUS_IMAGE_NOT_AT_BASE) must be returned, so save it here. // SavedStatus = Status; CreatePeb = TRUE; UseLargePages = ((Flags & PROCESS_CREATE_FLAGS_LARGE_PAGES) != 0 ? TRUE : FALSE); } else if (Parent != NULL) { if (Parent != PsInitialSystemProcess) { Process->SectionBaseAddress = Parent->SectionBaseAddress; // // User Process ( Cloned Address Space ). Don't specify section to // map, just Process to clone. // Status = MmInitializeProcessAddressSpace (Process, Parent, NULL, &Flags, NULL); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } CreatePeb = TRUE; UseLargePages = ((Flags & PROCESS_CREATE_FLAGS_LARGE_PAGES) != 0 ? TRUE : FALSE); // // A cloned process isn't started from an image file, so we give it the name // of the process of which it is a clone, provided the original has a name. // if (Parent->SeAuditProcessCreationInfo.ImageFileName != NULL) { ImageFileNameSize = sizeof(OBJECT_NAME_INFORMATION) + Parent->SeAuditProcessCreationInfo.ImageFileName->Name.MaximumLength; Process->SeAuditProcessCreationInfo.ImageFileName = ExAllocatePoolWithTag (PagedPool, ImageFileNameSize, 'aPeS'); if (Process->SeAuditProcessCreationInfo.ImageFileName != NULL) { RtlCopyMemory (Process->SeAuditProcessCreationInfo.ImageFileName, Parent->SeAuditProcessCreationInfo.ImageFileName, ImageFileNameSize); // // The UNICODE_STRING in the process is self contained, so calculate the // offset for the buffer. // Process->SeAuditProcessCreationInfo.ImageFileName->Name.Buffer = (PUSHORT)(((PUCHAR) Process->SeAuditProcessCreationInfo.ImageFileName) + sizeof(UNICODE_STRING)); } else { Status = STATUS_INSUFFICIENT_RESOURCES; goto exit_and_deref; } } } else { // // System Process. Don't specify Process to clone or section to map // Flags &= ~PROCESS_CREATE_FLAGS_ALL_LARGE_PAGE_FLAGS; Status = MmInitializeProcessAddressSpace (Process, NULL, NULL, &Flags, NULL); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } // // In case the image file name of this system process is ever queried, we give // a zero length UNICODE_STRING. // Process->SeAuditProcessCreationInfo.ImageFileName = ExAllocatePoolWithTag (PagedPool, sizeof(OBJECT_NAME_INFORMATION), 'aPeS'); if (Process->SeAuditProcessCreationInfo.ImageFileName != NULL) { RtlZeroMemory (Process->SeAuditProcessCreationInfo.ImageFileName, sizeof(OBJECT_NAME_INFORMATION)); } else { Status = STATUS_INSUFFICIENT_RESOURCES; goto exit_and_deref; } } } // // Create the process ID // CidEntry.Object = Process; CidEntry.GrantedAccess = 0; Process->UniqueProcessId = ExCreateHandle (PspCidTable, &CidEntry); if (Process->UniqueProcessId == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto exit_and_deref; } ExSetHandleTableOwner (Process->ObjectTable, Process->UniqueProcessId); // // Audit the process creation. // if (SeDetailedAuditingWithToken (NULL)) { SeAuditProcessCreation (Process); } // // See if the parent has a job. If so reference the job // and add the process in. // if (Parent) { Job = Parent->Job; if (Job != NULL && !(Job->LimitFlags & JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)) { if (Flags&PROCESS_CREATE_FLAGS_BREAKAWAY) { if (!(Job->LimitFlags & JOB_OBJECT_LIMIT_BREAKAWAY_OK)) { Status = STATUS_ACCESS_DENIED; } else { Status = STATUS_SUCCESS; } } else { Status = PspGetJobFromSet (Job, JobMemberLevel, &Process->Job); if (NT_SUCCESS (Status)) { PACCESS_TOKEN Token, NewToken; Job = Process->Job; Status = PspAddProcessToJob (Job, Process); // // Duplicate a new process token if one is specified for the job // Token = Job->Token; if (Token != NULL) { Status = SeSubProcessToken (Token, &NewToken, FALSE, Job->SessionId); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } SeAssignPrimaryToken (Process, NewToken); ObDereferenceObject (NewToken); } } } if (!NT_SUCCESS (Status)) { goto exit_and_deref; } } } if (Parent && CreatePeb) { // // For processes created w/ a section, // a new "virgin" PEB is created. Otherwise, // for forked processes, uses inherited PEB // with an updated mutant. // RtlZeroMemory (&InitialPeb, FIELD_OFFSET(INITIAL_PEB, Mutant)); InitialPeb.Mutant = (HANDLE)(-1); InitialPeb.ImageUsesLargePages = (BOOLEAN) UseLargePages; if (SectionHandle != NULL) { Status = MmCreatePeb (Process, &InitialPeb, &Process->Peb); if (!NT_SUCCESS (Status)) { Process->Peb = NULL; goto exit_and_deref; } Peb = Process->Peb; } else { SIZE_T BytesCopied; InitialPeb.InheritedAddressSpace = TRUE; Process->Peb = Parent->Peb; MmCopyVirtualMemory (CurrentProcess, &InitialPeb, Process, Process->Peb, sizeof (INITIAL_PEB), KernelMode, &BytesCopied); #if defined(_WIN64) if (Process->Wow64Process != NULL) { RtlZeroMemory (&InitialPeb32, FIELD_OFFSET(INITIAL_PEB32, Mutant)); InitialPeb32.Mutant = -1; InitialPeb32.InheritedAddressSpace = TRUE; InitialPeb32.ImageUsesLargePages = (BOOLEAN) UseLargePages; MmCopyVirtualMemory (CurrentProcess, &InitialPeb32, Process, Process->Wow64Process->Wow64, sizeof (INITIAL_PEB32), KernelMode, &BytesCopied); } #endif } } Peb = Process->Peb; // // Add the process to the global list of processes. // PspLockProcessList (CurrentThread); InsertTailList (&PsActiveProcessHead, &Process->ActiveProcessLinks); PspUnlockProcessList (CurrentThread); AccessState = NULL; if (!PsUseImpersonationToken) { AccessState = &LocalAccessState; Status = SeCreateAccessStateEx (NULL, (Parent == NULL || Parent != PsInitialSystemProcess)? PsGetCurrentProcessByThread (CurrentThread) : PsInitialSystemProcess, AccessState, &AuxData, DesiredAccess, &PsProcessType->TypeInfo.GenericMapping); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } } // // Insert the object. Once we do this is reachable from the outside world via // open by name. Open by ID is still disabled. Since its reachable // somebody might create a thread in the process and cause // rundown. // Status = ObInsertObject (Process, AccessState, DesiredAccess, 1, // bias the refcnt by one for future process manipulations NULL, &LocalProcessHandle); if (AccessState != NULL) { SeDeleteAccessState (AccessState); } if (!NT_SUCCESS (Status)) { goto exit_and_deref_parent; } // // Compute the base priority and quantum reset values for the process and // set the memory priority. // ASSERT(IsListEmpty(&Process->ThreadListHead) == TRUE); BasePriority = PspComputeQuantumAndPriority(Process, PsProcessPriorityBackground, &QuantumReset); Process->Pcb.BasePriority = (SCHAR)BasePriority; Process->Pcb.QuantumReset = QuantumReset; // // As soon as a handle to the process is accessible, allow the process to // be deleted. // Process->GrantedAccess = PROCESS_TERMINATE; if (Parent && Parent != PsInitialSystemProcess) { Status = ObGetObjectSecurity (Process, &SecurityDescriptor, &MemoryAllocated); if (!NT_SUCCESS (Status)) { ObCloseHandle (LocalProcessHandle, PreviousMode); goto exit_and_deref; } // // Compute the subject security context // SubjectContext.ProcessAuditId = Process; SubjectContext.PrimaryToken = PsReferencePrimaryToken(Process); SubjectContext.ClientToken = NULL; AccessCheck = SeAccessCheck (SecurityDescriptor, &SubjectContext, FALSE, MAXIMUM_ALLOWED, 0, NULL, &PsProcessType->TypeInfo.GenericMapping, PreviousMode, &Process->GrantedAccess, &accesst); PsDereferencePrimaryTokenEx (Process, SubjectContext.PrimaryToken); ObReleaseObjectSecurity (SecurityDescriptor, MemoryAllocated); if (!AccessCheck) { Process->GrantedAccess = 0; } // // It does not make any sense to create a process that can not // do anything to itself. // Note: Changes to this set of bits should be reflected in psquery.c // code, in PspSetPrimaryToken. // Process->GrantedAccess |= (PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_QUERY_INFORMATION | PROCESS_TERMINATE | PROCESS_CREATE_THREAD | PROCESS_DUP_HANDLE | PROCESS_CREATE_PROCESS | PROCESS_SET_INFORMATION | STANDARD_RIGHTS_ALL | PROCESS_SET_QUOTA); } else { Process->GrantedAccess = PROCESS_ALL_ACCESS; } KeQuerySystemTime (&Process->CreateTime); try { if (Peb != NULL && CurrentThread->Tcb.Teb != NULL) { ((PTEB)(CurrentThread->Tcb.Teb))->NtTib.ArbitraryUserPointer = Peb; } *ProcessHandle = LocalProcessHandle; } except (EXCEPTION_EXECUTE_HANDLER) { NOTHING; } if (SavedStatus != STATUS_SUCCESS) { Status = SavedStatus; } exit_and_deref: ObDereferenceObject (Process); exit_and_deref_parent: if (Parent != NULL) { ObDereferenceObject (Parent); } return Status; }
来具体分析一下PspCreateProcess所做的关键工作:
(1)调用ObCreateObject 创建一个类型为PsProcessType的内核对象,置于局部变量Process中,对象体为EPROCESS,即创建EPROCESS
Status = ObCreateObject (PreviousMode, PsProcessType, ObjectAttributes, PreviousMode, NULL, sizeof (EPROCESS), 0, 0, &Process);
(2)MmCreateProcessAddressSpace创建新的地址空间
if (Parent != NULL) { //创建新的地址空间 if (!MmCreateProcessAddressSpace (WorkingSetMinimum, Process, &DirectoryTableBase[0])) { Status = STATUS_INSUFFICIENT_RESOURCES; goto exit_and_deref; }
(3)MmInitializeProcessAddressSpace 初始化新进程的地址空间
if (Parent != PsInitialSystemProcess) {
//根据父进程来初始化进程地址空间,并把父进程的映像名称拷贝到新进程对象的数据结构中。 Process->SectionBaseAddress = Parent->SectionBaseAddress; // // User Process ( Cloned Address Space ). Don't specify section to // map, just Process to clone. // Status = MmInitializeProcessAddressSpace (Process, Parent, NULL, &Flags, NULL); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } CreatePeb = TRUE; UseLargePages = ((Flags & PROCESS_CREATE_FLAGS_LARGE_PAGES) != 0 ? TRUE : FALSE); // // A cloned process isn't started from an image file, so we give it the name // of the process of which it is a clone, provided the original has a name. // if (Parent->SeAuditProcessCreationInfo.ImageFileName != NULL) { ImageFileNameSize = sizeof(OBJECT_NAME_INFORMATION) + Parent->SeAuditProcessCreationInfo.ImageFileName->Name.MaximumLength; Process->SeAuditProcessCreationInfo.ImageFileName = ExAllocatePoolWithTag (PagedPool, ImageFileNameSize, 'aPeS'); if (Process->SeAuditProcessCreationInfo.ImageFileName != NULL) { RtlCopyMemory (Process->SeAuditProcessCreationInfo.ImageFileName, Parent->SeAuditProcessCreationInfo.ImageFileName, ImageFileNameSize); // // The UNICODE_STRING in the process is self contained, so calculate the // offset for the buffer. // Process->SeAuditProcessCreationInfo.ImageFileName->Name.Buffer = (PUSHORT)(((PUCHAR) Process->SeAuditProcessCreationInfo.ImageFileName) + sizeof(UNICODE_STRING)); } else { Status = STATUS_INSUFFICIENT_RESOURCES; goto exit_and_deref; } } } else { // // System Process. Don't specify Process to clone or section to map // Flags &= ~PROCESS_CREATE_FLAGS_ALL_LARGE_PAGE_FLAGS; Status = MmInitializeProcessAddressSpace (Process, NULL, NULL, &Flags, NULL); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } // // In case the image file name of this system process is ever queried, we give // a zero length UNICODE_STRING. // Process->SeAuditProcessCreationInfo.ImageFileName = ExAllocatePoolWithTag (PagedPool, sizeof(OBJECT_NAME_INFORMATION), 'aPeS'); if (Process->SeAuditProcessCreationInfo.ImageFileName != NULL) { RtlZeroMemory (Process->SeAuditProcessCreationInfo.ImageFileName, sizeof(OBJECT_NAME_INFORMATION)); } else { Status = STATUS_INSUFFICIENT_RESOURCES; goto exit_and_deref; } } }
(4)ExCreateHandle函数在CID句柄表中创建一个进程ID项。
CidEntry.Object = Process; CidEntry.GrantedAccess = 0; Process->UniqueProcessId = ExCreateHandle (PspCidTable, &CidEntry); if (Process->UniqueProcessId == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto exit_and_deref; } ExSetHandleTableOwner (Process->ObjectTable, Process->UniqueProcessId);
(5)MmCreatePeb 创建一个PEB,如果进程通过映像内存区来创建,创建一个PEB,如果是进程拷贝,则使用继承的PEB。
RtlZeroMemory (&InitialPeb, FIELD_OFFSET(INITIAL_PEB, Mutant)); InitialPeb.Mutant = (HANDLE)(-1); InitialPeb.ImageUsesLargePages = (BOOLEAN) UseLargePages; if (SectionHandle != NULL) { Status = MmCreatePeb (Process, &InitialPeb, &Process->Peb); if (!NT_SUCCESS (Status)) { Process->Peb = NULL; goto exit_and_deref; } Peb = Process->Peb; } else { SIZE_T BytesCopied; InitialPeb.InheritedAddressSpace = TRUE; Process->Peb = Parent->Peb; MmCopyVirtualMemory (CurrentProcess, &InitialPeb, Process, Process->Peb, sizeof (INITIAL_PEB), KernelMode, &BytesCopied);
(6)InsertTailList 函数把新进程加入全局的LIST_ENTRY进程链表中(位置:PsActiveProcessHead)
PspLockProcessList (CurrentThread); InsertTailList (&PsActiveProcessHead, &Process->ActiveProcessLinks); PspUnlockProcessList (CurrentThread); AccessState = NULL; if (!PsUseImpersonationToken) { AccessState = &LocalAccessState; Status = SeCreateAccessStateEx (NULL, (Parent == NULL || Parent != PsInitialSystemProcess)? PsGetCurrentProcessByThread (CurrentThread) : PsInitialSystemProcess, AccessState, &AuxData, DesiredAccess, &PsProcessType->TypeInfo.GenericMapping); if (!NT_SUCCESS (Status)) { goto exit_and_deref; } }
(7)ObInsertObject 将新进程对象记录到当前进程的句柄表中。
Status = ObInsertObject (Process, AccessState, DesiredAccess, 1, // bias the refcnt by one for future process manipulations NULL, &LocalProcessHandle);
NtCreateProcess结束后,就创建好了进程,然而进程只不过是一个容器,接下来的代码就可以看到NtCreateThread函数的线程创建了:
NTSTATUS NtCreateThread( __out PHANDLE ThreadHandle, __in ACCESS_MASK DesiredAccess, __in_opt POBJECT_ATTRIBUTES ObjectAttributes, __in HANDLE ProcessHandle, __out PCLIENT_ID ClientId, __in PCONTEXT ThreadContext, __in PINITIAL_TEB InitialTeb, __in BOOLEAN CreateSuspended ) /*++ Routine Description: This system service API creates and initializes a thread object. --*/ { NTSTATUS Status; INITIAL_TEB CapturedInitialTeb; PAGED_CODE(); // // Probe all arguments // try { if (KeGetPreviousMode () != KernelMode) { ProbeForWriteHandle (ThreadHandle); if (ARGUMENT_PRESENT (ClientId)) { ProbeForWriteSmallStructure (ClientId, sizeof (CLIENT_ID), sizeof (ULONG)); } if (ARGUMENT_PRESENT (ThreadContext) ) { ProbeForReadSmallStructure (ThreadContext, sizeof (CONTEXT), CONTEXT_ALIGN); } else { return STATUS_INVALID_PARAMETER; } ProbeForReadSmallStructure (InitialTeb, sizeof (InitialTeb->OldInitialTeb), sizeof (ULONG)); } CapturedInitialTeb.OldInitialTeb = InitialTeb->OldInitialTeb; if (CapturedInitialTeb.OldInitialTeb.OldStackBase == NULL && CapturedInitialTeb.OldInitialTeb.OldStackLimit == NULL) { // // Since the structure size here is less than 64k we don't need to reprobe // CapturedInitialTeb = *InitialTeb; } } except (ExSystemExceptionFilter ()) { return GetExceptionCode (); } Status = PspCreateThread (ThreadHandle, DesiredAccess, ObjectAttributes, ProcessHandle, NULL, ClientId, ThreadContext, &CapturedInitialTeb, CreateSuspended, NULL, NULL); return Status; }
看到这里NtCreateThread函数还是调用了PspCreateThread函数来完成实际的工作。NtCreateThread——>PspCreateThread
NTSTATUS PspCreateThread( OUT PHANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN HANDLE ProcessHandle, IN PEPROCESS ProcessPointer, OUT PCLIENT_ID ClientId OPTIONAL, IN PCONTEXT ThreadContext OPTIONAL, IN PINITIAL_TEB InitialTeb OPTIONAL, IN BOOLEAN CreateSuspended, 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. --*/ { 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 (); 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) // 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; } Status = ObCreateObject (PreviousMode, PsThreadType, ObjectAttributes, PreviousMode, NULL, sizeof(ETHREAD), 0, 0, &Thread); if (!NT_SUCCESS (Status)) { ObDereferenceObject (Process); return Status; } 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. // 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 // 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); if (!ExAcquireRundownProtection (&Process->RundownProtect)) { ObDereferenceObject (Thread); return STATUS_PROCESS_IS_TERMINATING; } if (ARGUMENT_PRESENT (ThreadContext)) { // // User-mode thread. Create TEB etc // 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. // 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)) { Status = KeInitThread (&Thread->Tcb, NULL, PspUserThreadStartup, (PKSTART_ROUTINE)NULL, Thread->StartAddress, ThreadContext, Teb, &Process->Pcb); } } else { 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; } OldActiveThreads = Process->ActiveThreads++; InsertTailList (&Process->ThreadListHead, &Thread->ThreadListEntry); 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. // 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. // ObReferenceObjectEx (Thread, 2); 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); } } 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; } } 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 (&Thread->Tcb); ObDereferenceObject (Thread); return Status; }
继续分析PspCreateThread函数实现的关键步骤:
(1)ObCreateObject创建一个线程对象ETHREAD,并初始化为零。
Status = ObCreateObject (PreviousMode, PsThreadType, ObjectAttributes, PreviousMode, NULL, sizeof(ETHREAD), 0, 0, &Thread); if (!NT_SUCCESS (Status)) { ObDereferenceObject (Process); return Status; } RtlZeroMemory (Thread, sizeof (ETHREAD));
(2)ExCreateHandle函数创建线程ID
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); }
(3)MmCreateTeb函数创建TEB
Status = MmCreateTeb (Process, InitialTeb, &Thread->Cid, &Teb); if (!NT_SUCCESS (Status)) { ExReleaseRundownProtection (&Process->RundownProtect); ObDereferenceObject (Thread); return Status; }
(4)利用ThreadContext中的程序指针(Eip寄存器)来设置线程的启动地址
Thread->StartAddress = (PKSTART_ROUTINE) StartRoutine;
(5)InsertTailList 函数将线程插入线程LIST_ENTRY链表
OldActiveThreads = Process->ActiveThreads++; InsertTailList (&Process->ThreadListHead, &Thread->ThreadListEntry);
(6)判断如果这个线程是进程中的第一个进程,回调函数则触发进程的创建通知
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); } } } }
(7)ObInsertObject函数将线程对象插入到当前进程的句柄表中
Status = ObInsertObject (Thread, AccessState, DesiredAccess, 0, NULL, &LocalThreadHandle);
(8)KeReadyThread函数使线程进入“就绪”状态,准备马上执行
KeReadyThread (&Thread->Tcb); ObDereferenceObject (Thread);
到目前为止进程对象和线程对象的创建工作就完成了。
0x03 创建进程详细步骤2: 通知windows子系统有新进程创建,启动初始线程,用户空间的初始化和Dll连接
四:通知windows子系统
每个进程在创建/退出的时候都要向windows子系统进程csrss.exe进程发出通知,因为它担负着对windows所有进程的管理的责任,
注意,这里发出通知的是CreateProcess的调用者,不是新建出来的进程,因为它还没有开始运行。
至此,CreateProcess的操作已经完成,但子进程中的线程却尚未开始运行,它的运行还要经历下面的第五和第六阶段。
五:启动初始线程
在内核中,新线程的启动例程是KiThreadStartup函数,这是当PspCreateThread 调用KeInitThread 函数时,KeInitThread 函数调用KiInitializeContextThread(参见base\ntos\ke\i386\thredini.c 文件)来设置的。
KiThreadStartup 函数首先将IRQL 降低到APC_LEVEL,然后调用系统初始的线程函数PspUserThreadStartup。这里的PspUserThreadStartup 函数是PspCreateThread 函数在调用KeInitThread 时指定的,。注意,PspCreateThread函数在创建系统线程时指定的初始线程函数为PspSystemThreadStartup 。线程启动函数被作为一个参数传递给PspUserThreadStartup,在这里,它应该是kernel32.dll 中的BaseProcessStart。
PspUserThreadStartup 函数被调用。逻辑并不复杂,但是涉及异步函数调用(APC)机制。
新创建的线程未必是可以被立即调度运行的,因为用户可能在创建时把标志位CREATE_ SUSPENDED设成了1;
如果那样的话,就需要等待别的进程通过系统调用恢复其运行资格以后才可以被调度运行。否则现在已经可以被调度运行了。至于什么时候才会被调度运行,则就要看优先级等等条件了。
六:用户空间的初始化和Dll连接
PspUserThreadStartup 函数返回以后,KiThreadStartup 函数返回到用户模式,此时,PspUserThreadStartup 插入的APC 被交付,于是LdrInitializeThunk 函数被调用,这是映像加载器(image loader)的初始化函数。LdrInitializeThunk 函数完成加载器、堆管理器等初始化工作,然后加载任何必要的DLL,并且调用这些DLL 的入口函数。最后,当LdrInitializeThunk 返回到用户模式APC 分发器时,该线程开始在用户模式下执行,调用应用程序指定的线程启动函数,此启动函数的地址已经在APC 交付时被压到用户栈中。
DLL连接由ntdll.dll中的LdrInitializeThunk()在用户空间完成。在此之前ntdll.dll与应用软件尚未连接,但是已经被映射到了用户空间
函数LdrInitializeThunk()在映像中的位置是系统初始化时就预先确定并记录在案的,所以在进入这个函数之前也不需要连接。
0x04 最后总结一下整个流程
1.打开目标映像文件(NtOpenFile()获取句柄, NtCreateSectiont通过文件句柄创建一个Section文件映射区,将文件内容映射进来)
2.创建进程对象
NtCreateProcess——>NtCreateProcessEx——>PspCreateProcess——>
ObCreateObject函数创建EPROCESS
MmCreateProcessAddressSpace创建新的地址空间,MmInitializeProcessAddressSpace 初始化新进程的地址空间
ExCreateHandle函数在CID句柄表中创建一个进程ID项。
MmCreatePeb 创建一个PEB,如果进程通过映像内存区来创建,创建一个PEB,如果是进程拷贝,则使用继承的PEB。
InsertTailList 函数把新进程加入全局的LIST_ENTRY进程链表中(位置:PsActiveProcessHead)
ObInsertObject 将新进程对象记录到当前进程的句柄表中。
3.创建线程对象
NtCreateThread——>PspCreateThread——>
ObCreateObject创建一个线程对象ETHREAD,并初始化为零。
ExCreateHandle函数创建线程ID
MmCreateTeb函数创建TEB
利用ThreadContext中的程序指针(Eip寄存器)来设置线程的启动地址Thread->StartAddress = (PKSTART_ROUTINE) StartRoutine;
InsertTailList 函数将线程插入线程LIST_ENTRY链表
判断如果这个线程是进程中的第一个进程,回调函数则触发进程的创建通知PCREATE_PROCESS_NOTIFY_ROUTINE Rtn;
ObInsertObject函数将线程对象插入到当前进程的句柄表中
KeReadyThread函数使线程进入“就绪”状态,准备马上执行
4.通知windows子系统
CreateProcess的调用者向windows子系统进程csrss.exe进程发出进程创建通知
5.启动初始线程
在内核中,新线程的启动例程是KiThreadStartup函数,这是当PspCreateThread 调用KeInitThread 函数时,KeInitThread 函数调用KiInitializeContextThread(参见base\ntos\ke\i386\thredini.c 文件)来设置的。
6.用户空间的初始化和Dll连接
DLL连接由ntdll.dll中的LdrInitializeThunk()在用户空间完成