1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * FILE: dll/win32/kernel32/client/proc.c 5 * PURPOSE: Process functions 6 * PROGRAMMERS: Ariadne (ariadne@xs4all.nl) 7 * UPDATE HISTORY: 8 * Created 01/11/98 9 */ 10 11 /* INCLUDES ****************************************************************/ 12 13 #include <k32.h> 14 15 #define NDEBUG 16 #include <debug.h> 17 18 /* GLOBALS *******************************************************************/ 19 20 WaitForInputIdleType UserWaitForInputIdleRoutine; 21 UNICODE_STRING BaseUnicodeCommandLine; 22 ANSI_STRING BaseAnsiCommandLine; 23 UNICODE_STRING BasePathVariableName = RTL_CONSTANT_STRING(L"PATH"); 24 LPSTARTUPINFOA BaseAnsiStartupInfo = NULL; 25 PLDR_DATA_TABLE_ENTRY BasepExeLdrEntry; 26 BOOLEAN g_AppCertInitialized; 27 BOOLEAN g_HaveAppCerts; 28 LIST_ENTRY BasepAppCertDllsList; 29 RTL_CRITICAL_SECTION gcsAppCert; 30 PBASEP_APPCERT_EMBEDDED_FUNC fEmbeddedCertFunc; 31 NTSTATUS g_AppCertStatus; 32 RTL_QUERY_REGISTRY_TABLE BasepAppCertTable[2] = 33 { 34 { 35 BasepConfigureAppCertDlls, 36 1, 37 L"AppCertDlls", 38 &BasepAppCertDllsList, 39 0, 40 NULL, 41 0 42 } 43 }; 44 45 PSAFER_REPLACE_PROCESS_THREAD_TOKENS g_SaferReplaceProcessThreadTokens; 46 HMODULE gSaferHandle = (HMODULE)-1; 47 48 VOID WINAPI 49 RegisterWaitForInputIdle(WaitForInputIdleType lpfnRegisterWaitForInputIdle); 50 51 #define CMD_STRING L"cmd /c " 52 53 /* FUNCTIONS ****************************************************************/ 54 55 VOID 56 WINAPI 57 StuffStdHandle(IN HANDLE ProcessHandle, 58 IN HANDLE StandardHandle, 59 IN PHANDLE Address) 60 { 61 NTSTATUS Status; 62 HANDLE DuplicatedHandle; 63 SIZE_T NumberOfBytesWritten; 64 65 /* If there is no handle to duplicate, return immediately */ 66 if (!StandardHandle) return; 67 68 /* Duplicate the handle */ 69 Status = NtDuplicateObject(NtCurrentProcess(), 70 StandardHandle, 71 ProcessHandle, 72 &DuplicatedHandle, 73 0, 74 0, 75 DUPLICATE_SAME_ACCESS | 76 DUPLICATE_SAME_ATTRIBUTES); 77 if (!NT_SUCCESS(Status)) return; 78 79 /* Write it */ 80 NtWriteVirtualMemory(ProcessHandle, 81 Address, 82 &DuplicatedHandle, 83 sizeof(HANDLE), 84 &NumberOfBytesWritten); 85 } 86 87 BOOLEAN 88 WINAPI 89 BuildSubSysCommandLine(IN LPCWSTR SubsystemName, 90 IN LPCWSTR ApplicationName, 91 IN LPCWSTR CommandLine, 92 OUT PUNICODE_STRING SubsysCommandLine) 93 { 94 UNICODE_STRING CommandLineString, ApplicationNameString; 95 PWCHAR Buffer; 96 ULONG Length; 97 98 /* Convert to unicode strings */ 99 RtlInitUnicodeString(&CommandLineString, ApplicationName); 100 RtlInitUnicodeString(&ApplicationNameString, CommandLine); 101 102 /* Allocate buffer for the output string */ 103 Length = CommandLineString.MaximumLength + ApplicationNameString.MaximumLength + 32; 104 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length); 105 RtlInitEmptyUnicodeString(SubsysCommandLine, Buffer, (USHORT)Length); 106 if (!Buffer) 107 { 108 /* Fail, no memory */ 109 BaseSetLastNTError(STATUS_NO_MEMORY); 110 return FALSE; 111 } 112 113 /* Build the final subsystem command line */ 114 RtlAppendUnicodeToString(SubsysCommandLine, SubsystemName); 115 RtlAppendUnicodeStringToString(SubsysCommandLine, &CommandLineString); 116 RtlAppendUnicodeToString(SubsysCommandLine, L" /C "); 117 RtlAppendUnicodeStringToString(SubsysCommandLine, &ApplicationNameString); 118 return TRUE; 119 } 120 121 BOOLEAN 122 WINAPI 123 BasepIsImageVersionOk(IN ULONG ImageMajorVersion, 124 IN ULONG ImageMinorVersion) 125 { 126 /* Accept images for NT 3.1 or higher */ 127 if (ImageMajorVersion > 3 || 128 (ImageMajorVersion == 3 && ImageMinorVersion >= 10)) 129 { 130 /* ReactOS-specific: Accept images even if they are newer than our internal NT version. */ 131 if (ImageMajorVersion > SharedUserData->NtMajorVersion || 132 (ImageMajorVersion == SharedUserData->NtMajorVersion && ImageMinorVersion > SharedUserData->NtMinorVersion)) 133 { 134 DPRINT1("Accepting image version %lu.%lu, although ReactOS is an NT %hu.%hu OS!\n", 135 ImageMajorVersion, 136 ImageMinorVersion, 137 SharedUserData->NtMajorVersion, 138 SharedUserData->NtMinorVersion); 139 } 140 141 return TRUE; 142 } 143 144 return FALSE; 145 } 146 147 NTSTATUS 148 WINAPI 149 BasepCheckWebBladeHashes(IN HANDLE FileHandle) 150 { 151 NTSTATUS Status; 152 CHAR Hash[16]; 153 154 /* Get all the MD5 hashes */ 155 Status = RtlComputeImportTableHash(FileHandle, Hash, 1); 156 if (!NT_SUCCESS(Status)) return Status; 157 158 /* Depending on which suite this is, run a bsearch and block the appropriate ones */ 159 if (SharedUserData->SuiteMask & VER_SUITE_COMPUTE_SERVER) 160 { 161 DPRINT1("Egad! This is a ReactOS Compute Server and we should prevent you from using certain APIs...but we won't."); 162 } 163 else if (SharedUserData->SuiteMask & VER_SUITE_STORAGE_SERVER) 164 { 165 DPRINT1("Gasp! This is a ReactOS Storage Server and we should prevent you from using certain APIs...but we won't."); 166 } 167 else if (SharedUserData->SuiteMask & VER_SUITE_BLADE) 168 { 169 DPRINT1("Golly! This is a ReactOS Web Blade Server and we should prevent you from using certain APIs...but we won't."); 170 } 171 172 /* Actually, fuck it, don't block anything, we're open source */ 173 return STATUS_SUCCESS; 174 } 175 176 NTSTATUS 177 NTAPI 178 BasepSaveAppCertRegistryValue(IN PLIST_ENTRY List, 179 IN PWCHAR ComponentName, 180 IN PWCHAR DllName) 181 { 182 /* Pretty much the only thing this key is used for, is malware */ 183 UNIMPLEMENTED; 184 return STATUS_NOT_IMPLEMENTED; 185 } 186 187 NTSTATUS 188 NTAPI 189 BasepConfigureAppCertDlls(IN PWSTR ValueName, 190 IN ULONG ValueType, 191 IN PVOID ValueData, 192 IN ULONG ValueLength, 193 IN PVOID Context, 194 IN PVOID EntryContext) 195 { 196 /* Add this to the certification list */ 197 return BasepSaveAppCertRegistryValue(Context, ValueName, ValueData); 198 } 199 200 NTSTATUS 201 WINAPI 202 BasepIsProcessAllowed(IN LPWSTR ApplicationName) 203 { 204 NTSTATUS Status, Status1; 205 PWCHAR Buffer; 206 UINT Length; 207 HMODULE TrustLibrary; 208 PBASEP_APPCERT_ENTRY Entry; 209 ULONG CertFlag; 210 PLIST_ENTRY NextEntry; 211 HANDLE KeyHandle; 212 UNICODE_STRING CertKey = RTL_CONSTANT_STRING(L"\\Registry\\MACHINE\\System\\CurrentControlSet\\Control\\Session Manager\\AppCertDlls"); 213 OBJECT_ATTRIBUTES KeyAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(&CertKey, OBJ_CASE_INSENSITIVE); 214 215 /* Try to initialize the certification subsystem */ 216 while (!g_AppCertInitialized) 217 { 218 /* Defaults */ 219 Status = STATUS_SUCCESS; 220 Buffer = NULL; 221 222 /* Acquire the lock while initializing and see if we lost a race */ 223 RtlEnterCriticalSection(&gcsAppCert); 224 if (g_AppCertInitialized) break; 225 226 /* On embedded, there is a special DLL */ 227 if (SharedUserData->SuiteMask & VER_SUITE_EMBEDDEDNT) 228 { 229 /* Allocate a buffer for the name */ 230 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 231 0, 232 MAX_PATH * sizeof(WCHAR) + 233 sizeof(UNICODE_NULL)); 234 if (!Buffer) 235 { 236 /* Fail if no memory */ 237 Status = STATUS_NO_MEMORY; 238 } 239 else 240 { 241 /* Now get the system32 directory in our buffer, make sure it fits */ 242 Length = GetSystemDirectoryW(Buffer, MAX_PATH - sizeof("EmbdTrst.DLL")); 243 if ((Length) && (Length <= MAX_PATH - sizeof("EmbdTrst.DLL"))) 244 { 245 /* Add a slash if needed, and add the embedded cert DLL name */ 246 if (Buffer[Length - 1] != '\\') Buffer[Length++] = '\\'; 247 RtlCopyMemory(&Buffer[Length], 248 L"EmbdTrst.DLL", 249 sizeof(L"EmbdTrst.DLL")); 250 251 /* Try to load it */ 252 TrustLibrary = LoadLibraryW(Buffer); 253 if (TrustLibrary) 254 { 255 /* And extract the special function out of it */ 256 fEmbeddedCertFunc = (PVOID)GetProcAddress(TrustLibrary, 257 "ImageOkToRunOnEmbeddedNT"); 258 } 259 } 260 261 /* If we didn't get this far, set a failure code */ 262 if (!fEmbeddedCertFunc) Status = STATUS_UNSUCCESSFUL; 263 } 264 } 265 else 266 { 267 /* Other systems have a registry entry for this */ 268 Status1 = NtOpenKey(&KeyHandle, KEY_READ, &KeyAttributes); 269 if (NT_SUCCESS(Status1)) 270 { 271 /* Close it, we'll query it through Rtl */ 272 NtClose(KeyHandle); 273 274 /* Do the query, which will call a special callback */ 275 Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL, 276 L"Session Manager", 277 BasepAppCertTable, 278 NULL, 279 NULL); 280 if (Status == STATUS_OBJECT_NAME_NOT_FOUND) 281 { 282 Status = STATUS_SUCCESS; 283 } 284 } 285 } 286 287 /* Free any buffer if we had one */ 288 if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); 289 290 /* Check for errors, or a missing embedded/custom certification DLL */ 291 if (!NT_SUCCESS(Status) || 292 (!(fEmbeddedCertFunc) && (IsListEmpty(&BasepAppCertDllsList)))) 293 { 294 /* The subsystem is not active on this machine, so give up */ 295 g_HaveAppCerts = FALSE; 296 g_AppCertStatus = Status; 297 } 298 else 299 { 300 /* We have certification DLLs active, remember this */ 301 g_HaveAppCerts = TRUE; 302 } 303 304 /* We are done the initialization phase, release the lock */ 305 g_AppCertInitialized = TRUE; 306 RtlLeaveCriticalSection(&gcsAppCert); 307 } 308 309 /* If there's no certification DLLs present, return the failure code */ 310 if (!g_HaveAppCerts) return g_AppCertStatus; 311 312 /* Otherwise, assume success and make sure we have *something* */ 313 ASSERT(fEmbeddedCertFunc || !IsListEmpty(&BasepAppCertDllsList)); 314 Status = STATUS_SUCCESS; 315 316 /* If the something is an embedded certification DLL, call it and return */ 317 if (fEmbeddedCertFunc) return fEmbeddedCertFunc(ApplicationName); 318 319 /* Otherwise we have custom certification DLLs, parse them */ 320 NextEntry = BasepAppCertDllsList.Flink; 321 CertFlag = 2; 322 while (NextEntry != &BasepAppCertDllsList) 323 { 324 /* Make sure the entry has a callback */ 325 Entry = CONTAINING_RECORD(NextEntry, BASEP_APPCERT_ENTRY, Entry); 326 ASSERT(Entry->fPluginCertFunc != NULL); 327 328 /* Call it and check if it failed */ 329 Status = Entry->fPluginCertFunc(ApplicationName, 1); 330 if (!NT_SUCCESS(Status)) CertFlag = 3; 331 332 /* Move on */ 333 NextEntry = NextEntry->Flink; 334 } 335 336 /* Now loop them again */ 337 NextEntry = BasepAppCertDllsList.Flink; 338 while (NextEntry != &BasepAppCertDllsList) 339 { 340 /* Make sure the entry has a callback */ 341 Entry = CONTAINING_RECORD(NextEntry, BASEP_APPCERT_ENTRY, Entry); 342 ASSERT(Entry->fPluginCertFunc != NULL); 343 344 /* Call it, this time with the flag from the loop above */ 345 Status = Entry->fPluginCertFunc(ApplicationName, CertFlag); 346 } 347 348 /* All done, return the status */ 349 return Status; 350 } 351 352 NTSTATUS 353 WINAPI 354 BasepReplaceProcessThreadTokens(IN HANDLE TokenHandle, 355 IN HANDLE ProcessHandle, 356 IN HANDLE ThreadHandle) 357 { 358 NTSTATUS Status; 359 ANSI_STRING SaferiReplaceProcessThreadTokens = RTL_CONSTANT_STRING("SaferiReplaceProcessThreadTokens"); 360 361 /* Enter the application certification lock */ 362 RtlEnterCriticalSection(&gcsAppCert); 363 364 /* Check if we already know the function */ 365 if (g_SaferReplaceProcessThreadTokens) 366 { 367 /* Call it */ 368 Status = g_SaferReplaceProcessThreadTokens(TokenHandle, 369 ProcessHandle, 370 ThreadHandle) ? 371 STATUS_SUCCESS : 372 STATUS_UNSUCCESSFUL; 373 } 374 else 375 { 376 /* Check if the app certification DLL isn't loaded */ 377 if (!(gSaferHandle) || 378 (gSaferHandle == (HMODULE)-1) || 379 (gSaferHandle == (HMODULE)-2)) 380 { 381 /* Then we can't call the function */ 382 Status = STATUS_ENTRYPOINT_NOT_FOUND; 383 } 384 else 385 { 386 /* We have the DLL, find the address of the Safer function */ 387 Status = LdrGetProcedureAddress(gSaferHandle, 388 &SaferiReplaceProcessThreadTokens, 389 0, 390 (PVOID*)&g_SaferReplaceProcessThreadTokens); 391 if (NT_SUCCESS(Status)) 392 { 393 /* Found it, now call it */ 394 Status = g_SaferReplaceProcessThreadTokens(TokenHandle, 395 ProcessHandle, 396 ThreadHandle) ? 397 STATUS_SUCCESS : 398 STATUS_UNSUCCESSFUL; 399 } 400 else 401 { 402 /* We couldn't find it, so this must be an unsupported DLL */ 403 LdrUnloadDll(gSaferHandle); 404 gSaferHandle = NULL; 405 Status = STATUS_ENTRYPOINT_NOT_FOUND; 406 } 407 } 408 } 409 410 /* Release the lock and return the result */ 411 RtlLeaveCriticalSection(&gcsAppCert); 412 return Status; 413 } 414 415 VOID 416 WINAPI 417 BasepSxsCloseHandles(IN PBASE_MSG_SXS_HANDLES Handles) 418 { 419 NTSTATUS Status; 420 421 /* Sanity checks */ 422 ASSERT(Handles != NULL); 423 ASSERT(Handles->Process == NULL || Handles->Process == NtCurrentProcess()); 424 425 /* Close the file handle */ 426 if (Handles->File) 427 { 428 Status = NtClose(Handles->File); 429 ASSERT(NT_SUCCESS(Status)); 430 } 431 432 /* Close the section handle */ 433 if (Handles->Section) 434 { 435 Status = NtClose(Handles->Section); 436 ASSERT(NT_SUCCESS(Status)); 437 } 438 439 /* Unmap the section view */ 440 if (Handles->ViewBase.QuadPart) 441 { 442 Status = NtUnmapViewOfSection(NtCurrentProcess(), 443 (PVOID)(ULONG_PTR)Handles->ViewBase.QuadPart); 444 ASSERT(NT_SUCCESS(Status)); 445 } 446 } 447 448 static 449 LONG BaseExceptionFilter(EXCEPTION_POINTERS *ExceptionInfo) 450 { 451 LONG ExceptionDisposition = EXCEPTION_EXECUTE_HANDLER; 452 LPTOP_LEVEL_EXCEPTION_FILTER RealFilter; 453 RealFilter = RtlDecodePointer(GlobalTopLevelExceptionFilter); 454 455 if (RealFilter != NULL) 456 { 457 _SEH2_TRY 458 { 459 ExceptionDisposition = RealFilter(ExceptionInfo); 460 } 461 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 462 { 463 } 464 _SEH2_END; 465 } 466 if ((ExceptionDisposition == EXCEPTION_CONTINUE_SEARCH || ExceptionDisposition == EXCEPTION_EXECUTE_HANDLER) && 467 RealFilter != UnhandledExceptionFilter) 468 { 469 ExceptionDisposition = UnhandledExceptionFilter(ExceptionInfo); 470 } 471 472 return ExceptionDisposition; 473 } 474 475 VOID 476 WINAPI 477 BaseProcessStartup(PPROCESS_START_ROUTINE lpStartAddress) 478 { 479 DPRINT("BaseProcessStartup(..) - setting up exception frame.\n"); 480 481 _SEH2_TRY 482 { 483 /* Set our Start Address */ 484 NtSetInformationThread(NtCurrentThread(), 485 ThreadQuerySetWin32StartAddress, 486 &lpStartAddress, 487 sizeof(PPROCESS_START_ROUTINE)); 488 489 /* Call the Start Routine */ 490 ExitThread(lpStartAddress()); 491 } 492 _SEH2_EXCEPT(BaseExceptionFilter(_SEH2_GetExceptionInformation())) 493 { 494 /* Get the Exit code from the SEH Handler */ 495 if (!BaseRunningInServerProcess) 496 { 497 /* Kill the whole process, usually */ 498 ExitProcess(_SEH2_GetExceptionCode()); 499 } 500 else 501 { 502 /* If running inside CSRSS, kill just this thread */ 503 ExitThread(_SEH2_GetExceptionCode()); 504 } 505 } 506 _SEH2_END; 507 } 508 509 NTSTATUS 510 WINAPI 511 BasepNotifyCsrOfThread(IN HANDLE ThreadHandle, 512 IN PCLIENT_ID ClientId) 513 { 514 BASE_API_MESSAGE ApiMessage; 515 PBASE_CREATE_THREAD CreateThreadRequest = &ApiMessage.Data.CreateThreadRequest; 516 517 DPRINT("BasepNotifyCsrOfThread: Thread: %p, Handle %p\n", 518 ClientId->UniqueThread, ThreadHandle); 519 520 /* Fill out the request */ 521 CreateThreadRequest->ClientId = *ClientId; 522 CreateThreadRequest->ThreadHandle = ThreadHandle; 523 524 /* Call CSR */ 525 CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage, 526 NULL, 527 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepCreateThread), 528 sizeof(*CreateThreadRequest)); 529 if (!NT_SUCCESS(ApiMessage.Status)) 530 { 531 DPRINT1("Failed to tell CSRSS about new thread: %lx\n", ApiMessage.Status); 532 return ApiMessage.Status; 533 } 534 535 /* Return Success */ 536 return STATUS_SUCCESS; 537 } 538 539 BOOLEAN 540 WINAPI 541 BasePushProcessParameters(IN ULONG ParameterFlags, 542 IN HANDLE ProcessHandle, 543 IN PPEB RemotePeb, 544 IN LPCWSTR ApplicationPathName, 545 IN LPWSTR lpCurrentDirectory, 546 IN LPWSTR lpCommandLine, 547 IN LPVOID lpEnvironment, 548 IN LPSTARTUPINFOW StartupInfo, 549 IN DWORD CreationFlags, 550 IN BOOL InheritHandles, 551 IN ULONG ImageSubsystem, 552 IN PVOID AppCompatData, 553 IN ULONG AppCompatDataSize) 554 { 555 WCHAR FullPath[MAX_PATH + 5]; 556 PWCHAR Remaining, DllPathString, ScanChar; 557 PRTL_USER_PROCESS_PARAMETERS ProcessParameters, RemoteParameters; 558 PVOID RemoteAppCompatData; 559 UNICODE_STRING DllPath, ImageName, CommandLine, CurrentDirectory; 560 UNICODE_STRING Desktop, Shell, Runtime, Title; 561 NTSTATUS Status; 562 ULONG EnviroSize; 563 SIZE_T Size; 564 BOOLEAN HavePebLock = FALSE, Result; 565 PPEB Peb = NtCurrentPeb(); 566 567 /* Get the full path name */ 568 Size = GetFullPathNameW(ApplicationPathName, 569 MAX_PATH + 4, 570 FullPath, 571 &Remaining); 572 if ((Size) && (Size <= (MAX_PATH + 4))) 573 { 574 /* Get the DLL Path */ 575 DllPathString = BaseComputeProcessDllPath(FullPath, lpEnvironment); 576 if (!DllPathString) 577 { 578 /* Fail */ 579 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 580 return FALSE; 581 } 582 583 /* Initialize Strings */ 584 RtlInitUnicodeString(&DllPath, DllPathString); 585 RtlInitUnicodeString(&ImageName, FullPath); 586 } 587 else 588 { 589 /* Couldn't get the path name. Just take the original path */ 590 DllPathString = BaseComputeProcessDllPath((LPWSTR)ApplicationPathName, 591 lpEnvironment); 592 if (!DllPathString) 593 { 594 /* Fail */ 595 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 596 return FALSE; 597 } 598 599 /* Initialize Strings */ 600 RtlInitUnicodeString(&DllPath, DllPathString); 601 RtlInitUnicodeString(&ImageName, ApplicationPathName); 602 } 603 604 /* Initialize Strings */ 605 RtlInitUnicodeString(&CommandLine, lpCommandLine); 606 RtlInitUnicodeString(&CurrentDirectory, lpCurrentDirectory); 607 608 /* Initialize more Strings from the Startup Info */ 609 if (StartupInfo->lpDesktop) 610 { 611 RtlInitUnicodeString(&Desktop, StartupInfo->lpDesktop); 612 } 613 else 614 { 615 RtlInitUnicodeString(&Desktop, L""); 616 } 617 if (StartupInfo->lpReserved) 618 { 619 RtlInitUnicodeString(&Shell, StartupInfo->lpReserved); 620 } 621 else 622 { 623 RtlInitUnicodeString(&Shell, L""); 624 } 625 if (StartupInfo->lpTitle) 626 { 627 RtlInitUnicodeString(&Title, StartupInfo->lpTitle); 628 } 629 else 630 { 631 RtlInitUnicodeString(&Title, ApplicationPathName); 632 } 633 634 /* This one is special because the length can differ */ 635 Runtime.Buffer = (LPWSTR)StartupInfo->lpReserved2; 636 Runtime.MaximumLength = Runtime.Length = StartupInfo->cbReserved2; 637 638 /* Enforce no app compat data if the pointer was NULL */ 639 if (!AppCompatData) AppCompatDataSize = 0; 640 641 /* Create the Parameter Block */ 642 ProcessParameters = NULL; 643 DPRINT("ImageName: '%wZ'\n", &ImageName); 644 DPRINT("DllPath : '%wZ'\n", &DllPath); 645 DPRINT("CurDir : '%wZ'\n", &CurrentDirectory); 646 DPRINT("CmdLine : '%wZ'\n", &CommandLine); 647 DPRINT("Title : '%wZ'\n", &Title); 648 DPRINT("Desktop : '%wZ'\n", &Desktop); 649 DPRINT("Shell : '%wZ'\n", &Shell); 650 DPRINT("Runtime : '%wZ'\n", &Runtime); 651 Status = RtlCreateProcessParameters(&ProcessParameters, 652 &ImageName, 653 &DllPath, 654 lpCurrentDirectory ? 655 &CurrentDirectory : NULL, 656 &CommandLine, 657 lpEnvironment, 658 &Title, 659 &Desktop, 660 &Shell, 661 &Runtime); 662 if (!NT_SUCCESS(Status)) goto FailPath; 663 664 /* Clear the current directory handle if not inheriting */ 665 if (!InheritHandles) ProcessParameters->CurrentDirectory.Handle = NULL; 666 667 /* Check if the user passed in an environment */ 668 if (lpEnvironment) 669 { 670 /* We should've made it part of the parameters block, enforce this */ 671 ASSERT(ProcessParameters->Environment == lpEnvironment); 672 lpEnvironment = ProcessParameters->Environment; 673 } 674 else 675 { 676 /* The user did not, so use the one from the current PEB */ 677 HavePebLock = TRUE; 678 RtlAcquirePebLock(); 679 lpEnvironment = Peb->ProcessParameters->Environment; 680 } 681 682 /* Save pointer and start lookup */ 683 ScanChar = lpEnvironment; 684 if (lpEnvironment) 685 { 686 /* Find the environment size */ 687 while (*ScanChar++) while (*ScanChar++); 688 EnviroSize = (ULONG)((ULONG_PTR)ScanChar - (ULONG_PTR)lpEnvironment); 689 690 /* Allocate and Initialize new Environment Block */ 691 Size = EnviroSize; 692 ProcessParameters->Environment = NULL; 693 Status = NtAllocateVirtualMemory(ProcessHandle, 694 (PVOID*)&ProcessParameters->Environment, 695 0, 696 &Size, 697 MEM_COMMIT, 698 PAGE_READWRITE); 699 if (!NT_SUCCESS(Status)) goto FailPath; 700 701 /* Write the Environment Block */ 702 Status = NtWriteVirtualMemory(ProcessHandle, 703 ProcessParameters->Environment, 704 lpEnvironment, 705 EnviroSize, 706 NULL); 707 708 /* No longer need the PEB lock anymore */ 709 if (HavePebLock) 710 { 711 /* Release it */ 712 RtlReleasePebLock(); 713 HavePebLock = FALSE; 714 } 715 716 /* Check if the write failed */ 717 if (!NT_SUCCESS(Status)) goto FailPath; 718 } 719 720 /* Write new parameters */ 721 ProcessParameters->StartingX = StartupInfo->dwX; 722 ProcessParameters->StartingY = StartupInfo->dwY; 723 ProcessParameters->CountX = StartupInfo->dwXSize; 724 ProcessParameters->CountY = StartupInfo->dwYSize; 725 ProcessParameters->CountCharsX = StartupInfo->dwXCountChars; 726 ProcessParameters->CountCharsY = StartupInfo->dwYCountChars; 727 ProcessParameters->FillAttribute = StartupInfo->dwFillAttribute; 728 ProcessParameters->WindowFlags = StartupInfo->dwFlags; 729 ProcessParameters->ShowWindowFlags = StartupInfo->wShowWindow; 730 731 /* Write the handles only if we have to */ 732 if (StartupInfo->dwFlags & 733 (STARTF_USESTDHANDLES | STARTF_USEHOTKEY | STARTF_SHELLPRIVATE)) 734 { 735 ProcessParameters->StandardInput = StartupInfo->hStdInput; 736 ProcessParameters->StandardOutput = StartupInfo->hStdOutput; 737 ProcessParameters->StandardError = StartupInfo->hStdError; 738 } 739 740 /* Use Special Flags for ConDllInitialize in Kernel32 */ 741 if (CreationFlags & DETACHED_PROCESS) 742 { 743 ProcessParameters->ConsoleHandle = HANDLE_DETACHED_PROCESS; 744 } 745 else if (CreationFlags & CREATE_NEW_CONSOLE) 746 { 747 ProcessParameters->ConsoleHandle = HANDLE_CREATE_NEW_CONSOLE; 748 } 749 else if (CreationFlags & CREATE_NO_WINDOW) 750 { 751 ProcessParameters->ConsoleHandle = HANDLE_CREATE_NO_WINDOW; 752 } 753 else 754 { 755 /* Inherit our Console Handle */ 756 ProcessParameters->ConsoleHandle = Peb->ProcessParameters->ConsoleHandle; 757 758 /* Make sure that the shell isn't trampling on our handles first */ 759 if (!(StartupInfo->dwFlags & 760 (STARTF_USESTDHANDLES | STARTF_USEHOTKEY | STARTF_SHELLPRIVATE))) 761 { 762 /* Copy the handle if we are inheriting or if it's a console handle */ 763 if ((InheritHandles) || 764 (IsConsoleHandle(Peb->ProcessParameters->StandardInput))) 765 { 766 ProcessParameters->StandardInput = Peb->ProcessParameters->StandardInput; 767 } 768 if ((InheritHandles) || 769 (IsConsoleHandle(Peb->ProcessParameters->StandardOutput))) 770 { 771 ProcessParameters->StandardOutput = Peb->ProcessParameters->StandardOutput; 772 } 773 if ((InheritHandles) || 774 (IsConsoleHandle(Peb->ProcessParameters->StandardError))) 775 { 776 ProcessParameters->StandardError = Peb->ProcessParameters->StandardError; 777 } 778 } 779 } 780 781 /* Also set the Console Flag */ 782 if ((CreationFlags & CREATE_NEW_PROCESS_GROUP) && 783 (!(CreationFlags & CREATE_NEW_CONSOLE))) 784 { 785 ProcessParameters->ConsoleFlags = 1; 786 } 787 788 /* Check if there's a .local file present */ 789 if (ParameterFlags & 1) 790 { 791 ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_LOCAL_DLL_PATH; 792 } 793 794 /* Check if we failed to open the IFEO key */ 795 if (ParameterFlags & 2) 796 { 797 ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_IMAGE_KEY_MISSING; 798 } 799 800 /* Allocate memory for the parameter block */ 801 Size = ProcessParameters->Length; 802 RemoteParameters = NULL; 803 Status = NtAllocateVirtualMemory(ProcessHandle, 804 (PVOID*)&RemoteParameters, 805 0, 806 &Size, 807 MEM_COMMIT, 808 PAGE_READWRITE); 809 if (!NT_SUCCESS(Status)) goto FailPath; 810 811 /* Set the allocated size */ 812 ProcessParameters->MaximumLength = Size; 813 814 /* Handle some Parameter Flags */ 815 ProcessParameters->Flags |= (CreationFlags & PROFILE_USER) ? 816 RTL_USER_PROCESS_PARAMETERS_PROFILE_USER : 0; 817 ProcessParameters->Flags |= (CreationFlags & PROFILE_KERNEL) ? 818 RTL_USER_PROCESS_PARAMETERS_PROFILE_KERNEL : 0; 819 ProcessParameters->Flags |= (CreationFlags & PROFILE_SERVER) ? 820 RTL_USER_PROCESS_PARAMETERS_PROFILE_SERVER : 0; 821 ProcessParameters->Flags |= (Peb->ProcessParameters->Flags & 822 RTL_USER_PROCESS_PARAMETERS_DISABLE_HEAP_CHECKS); 823 824 /* Write the Parameter Block */ 825 Status = NtWriteVirtualMemory(ProcessHandle, 826 RemoteParameters, 827 ProcessParameters, 828 ProcessParameters->Length, 829 NULL); 830 if (!NT_SUCCESS(Status)) goto FailPath; 831 832 /* Write the PEB Pointer */ 833 Status = NtWriteVirtualMemory(ProcessHandle, 834 &RemotePeb->ProcessParameters, 835 &RemoteParameters, 836 sizeof(PVOID), 837 NULL); 838 if (!NT_SUCCESS(Status)) goto FailPath; 839 840 /* Check if there's any app compat data to write */ 841 RemoteAppCompatData = NULL; 842 if (AppCompatData) 843 { 844 /* Allocate some space for the application compatibility data */ 845 Size = AppCompatDataSize; 846 Status = NtAllocateVirtualMemory(ProcessHandle, 847 &RemoteAppCompatData, 848 0, 849 &Size, 850 MEM_COMMIT, 851 PAGE_READWRITE); 852 if (!NT_SUCCESS(Status)) goto FailPath; 853 854 /* Write the application compatibility data */ 855 Status = NtWriteVirtualMemory(ProcessHandle, 856 RemoteAppCompatData, 857 AppCompatData, 858 AppCompatDataSize, 859 NULL); 860 if (!NT_SUCCESS(Status)) goto FailPath; 861 } 862 863 /* Write the PEB Pointer to the app compat data (might be NULL) */ 864 Status = NtWriteVirtualMemory(ProcessHandle, 865 &RemotePeb->pShimData, 866 &RemoteAppCompatData, 867 sizeof(PVOID), 868 NULL); 869 if (!NT_SUCCESS(Status)) goto FailPath; 870 871 /* Now write Peb->ImageSubSystem */ 872 if (ImageSubsystem) 873 { 874 NtWriteVirtualMemory(ProcessHandle, 875 &RemotePeb->ImageSubsystem, 876 &ImageSubsystem, 877 sizeof(ImageSubsystem), 878 NULL); 879 } 880 881 /* Success path */ 882 Result = TRUE; 883 884 Quickie: 885 /* Cleanup */ 886 if (HavePebLock) RtlReleasePebLock(); 887 RtlFreeHeap(RtlGetProcessHeap(), 0, DllPath.Buffer); 888 if (ProcessParameters) RtlDestroyProcessParameters(ProcessParameters); 889 return Result; 890 FailPath: 891 DPRINT1("Failure to create process parameters: %lx\n", Status); 892 BaseSetLastNTError(Status); 893 Result = FALSE; 894 goto Quickie; 895 } 896 897 VOID 898 WINAPI 899 InitCommandLines(VOID) 900 { 901 NTSTATUS Status; 902 903 /* Read the UNICODE_STRING from the PEB */ 904 BaseUnicodeCommandLine = NtCurrentPeb()->ProcessParameters->CommandLine; 905 906 /* Convert to ANSI_STRING for the *A callers */ 907 Status = RtlUnicodeStringToAnsiString(&BaseAnsiCommandLine, 908 &BaseUnicodeCommandLine, 909 TRUE); 910 if (!NT_SUCCESS(Status)) RtlInitEmptyAnsiString(&BaseAnsiCommandLine, 0, 0); 911 } 912 913 /* PUBLIC FUNCTIONS ***********************************************************/ 914 915 /* 916 * @implemented 917 */ 918 BOOL 919 WINAPI 920 GetProcessAffinityMask(IN HANDLE hProcess, 921 OUT PDWORD_PTR lpProcessAffinityMask, 922 OUT PDWORD_PTR lpSystemAffinityMask) 923 { 924 PROCESS_BASIC_INFORMATION ProcessInfo; 925 NTSTATUS Status; 926 927 /* Query information on the process from the kernel */ 928 Status = NtQueryInformationProcess(hProcess, 929 ProcessBasicInformation, 930 &ProcessInfo, 931 sizeof(ProcessInfo), 932 NULL); 933 if (!NT_SUCCESS(Status)) 934 { 935 /* Fail */ 936 BaseSetLastNTError(Status); 937 return FALSE; 938 } 939 940 /* Copy the affinity mask, and get the system one from our shared data */ 941 *lpProcessAffinityMask = (DWORD)ProcessInfo.AffinityMask; 942 *lpSystemAffinityMask = (DWORD)BaseStaticServerData->SysInfo.ActiveProcessorsAffinityMask; 943 return TRUE; 944 } 945 946 /* 947 * @implemented 948 */ 949 BOOL 950 WINAPI 951 SetProcessAffinityMask(IN HANDLE hProcess, 952 IN DWORD_PTR dwProcessAffinityMask) 953 { 954 NTSTATUS Status; 955 956 /* Directly set the affinity mask */ 957 Status = NtSetInformationProcess(hProcess, 958 ProcessAffinityMask, 959 (PVOID)&dwProcessAffinityMask, 960 sizeof(DWORD)); 961 if (!NT_SUCCESS(Status)) 962 { 963 /* Handle failure */ 964 BaseSetLastNTError(Status); 965 return FALSE; 966 } 967 968 /* Everything was ok */ 969 return TRUE; 970 } 971 972 /* 973 * @implemented 974 */ 975 BOOL 976 WINAPI 977 GetProcessShutdownParameters(OUT LPDWORD lpdwLevel, 978 OUT LPDWORD lpdwFlags) 979 { 980 BASE_API_MESSAGE ApiMessage; 981 PBASE_GETSET_PROCESS_SHUTDOWN_PARAMS ShutdownParametersRequest = &ApiMessage.Data.ShutdownParametersRequest; 982 983 /* Ask CSRSS for shutdown information */ 984 CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage, 985 NULL, 986 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepGetProcessShutdownParam), 987 sizeof(*ShutdownParametersRequest)); 988 if (!NT_SUCCESS(ApiMessage.Status)) 989 { 990 /* Return the failure from CSRSS */ 991 BaseSetLastNTError(ApiMessage.Status); 992 return FALSE; 993 } 994 995 /* Get the data back */ 996 *lpdwLevel = ShutdownParametersRequest->ShutdownLevel; 997 *lpdwFlags = ShutdownParametersRequest->ShutdownFlags; 998 return TRUE; 999 } 1000 1001 /* 1002 * @implemented 1003 */ 1004 BOOL 1005 WINAPI 1006 SetProcessShutdownParameters(IN DWORD dwLevel, 1007 IN DWORD dwFlags) 1008 { 1009 BASE_API_MESSAGE ApiMessage; 1010 PBASE_GETSET_PROCESS_SHUTDOWN_PARAMS ShutdownParametersRequest = &ApiMessage.Data.ShutdownParametersRequest; 1011 1012 /* Write the data into the CSRSS request and send it */ 1013 ShutdownParametersRequest->ShutdownLevel = dwLevel; 1014 ShutdownParametersRequest->ShutdownFlags = dwFlags; 1015 CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage, 1016 NULL, 1017 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepSetProcessShutdownParam), 1018 sizeof(*ShutdownParametersRequest)); 1019 if (!NT_SUCCESS(ApiMessage.Status)) 1020 { 1021 /* Return the failure from CSRSS */ 1022 BaseSetLastNTError(ApiMessage.Status); 1023 return FALSE; 1024 } 1025 1026 /* All went well */ 1027 return TRUE; 1028 } 1029 1030 /* 1031 * @implemented 1032 */ 1033 BOOL 1034 WINAPI 1035 GetProcessWorkingSetSizeEx(IN HANDLE hProcess, 1036 OUT PSIZE_T lpMinimumWorkingSetSize, 1037 OUT PSIZE_T lpMaximumWorkingSetSize, 1038 OUT PDWORD Flags) 1039 { 1040 QUOTA_LIMITS_EX QuotaLimits; 1041 NTSTATUS Status; 1042 1043 /* Query the kernel about this */ 1044 Status = NtQueryInformationProcess(hProcess, 1045 ProcessQuotaLimits, 1046 &QuotaLimits, 1047 sizeof(QuotaLimits), 1048 NULL); 1049 if (!NT_SUCCESS(Status)) 1050 { 1051 /* Return error */ 1052 BaseSetLastNTError(Status); 1053 return FALSE; 1054 } 1055 1056 /* Copy the quota information out */ 1057 *lpMinimumWorkingSetSize = QuotaLimits.MinimumWorkingSetSize; 1058 *lpMaximumWorkingSetSize = QuotaLimits.MaximumWorkingSetSize; 1059 *Flags = QuotaLimits.Flags; 1060 return TRUE; 1061 } 1062 1063 /* 1064 * @implemented 1065 */ 1066 BOOL 1067 WINAPI 1068 GetProcessWorkingSetSize(IN HANDLE hProcess, 1069 OUT PSIZE_T lpMinimumWorkingSetSize, 1070 OUT PSIZE_T lpMaximumWorkingSetSize) 1071 { 1072 DWORD Dummy; 1073 return GetProcessWorkingSetSizeEx(hProcess, 1074 lpMinimumWorkingSetSize, 1075 lpMaximumWorkingSetSize, 1076 &Dummy); 1077 } 1078 1079 /* 1080 * @implemented 1081 */ 1082 BOOL 1083 WINAPI 1084 SetProcessWorkingSetSizeEx(IN HANDLE hProcess, 1085 IN SIZE_T dwMinimumWorkingSetSize, 1086 IN SIZE_T dwMaximumWorkingSetSize, 1087 IN DWORD Flags) 1088 { 1089 QUOTA_LIMITS_EX QuotaLimits; 1090 NTSTATUS Status, ReturnStatus; 1091 BOOL Result; 1092 PVOID State; 1093 ULONG Privilege = SE_INC_BASE_PRIORITY_PRIVILEGE; 1094 1095 /* Zero out the input structure */ 1096 RtlZeroMemory(&QuotaLimits, sizeof(QuotaLimits)); 1097 1098 /* Check if the caller sent any limits */ 1099 if ((dwMinimumWorkingSetSize) && (dwMaximumWorkingSetSize)) 1100 { 1101 /* Write the quota information */ 1102 QuotaLimits.MinimumWorkingSetSize = dwMinimumWorkingSetSize; 1103 QuotaLimits.MaximumWorkingSetSize = dwMaximumWorkingSetSize; 1104 QuotaLimits.Flags = Flags; 1105 1106 /* Acquire the required privilege */ 1107 Status = RtlAcquirePrivilege(&Privilege, 1, 0, &State); 1108 1109 /* Request the new quotas */ 1110 ReturnStatus = NtSetInformationProcess(hProcess, 1111 ProcessQuotaLimits, 1112 &QuotaLimits, 1113 sizeof(QuotaLimits)); 1114 Result = NT_SUCCESS(ReturnStatus); 1115 if (NT_SUCCESS(Status)) 1116 { 1117 /* Release the privilege and set succes code */ 1118 ASSERT(State != NULL); 1119 RtlReleasePrivilege(State); 1120 State = NULL; 1121 } 1122 } 1123 else 1124 { 1125 /* No limits, fail the call */ 1126 ReturnStatus = STATUS_INVALID_PARAMETER; 1127 Result = FALSE; 1128 } 1129 1130 /* Return result code, set error code if this was a failure */ 1131 if (!Result) BaseSetLastNTError(ReturnStatus); 1132 return Result; 1133 } 1134 1135 /* 1136 * @implemented 1137 */ 1138 BOOL 1139 WINAPI 1140 SetProcessWorkingSetSize(IN HANDLE hProcess, 1141 IN SIZE_T dwMinimumWorkingSetSize, 1142 IN SIZE_T dwMaximumWorkingSetSize) 1143 { 1144 /* Call the newer API */ 1145 return SetProcessWorkingSetSizeEx(hProcess, 1146 dwMinimumWorkingSetSize, 1147 dwMaximumWorkingSetSize, 1148 0); 1149 } 1150 1151 /* 1152 * @implemented 1153 */ 1154 BOOL 1155 WINAPI 1156 GetProcessTimes(IN HANDLE hProcess, 1157 IN LPFILETIME lpCreationTime, 1158 IN LPFILETIME lpExitTime, 1159 IN LPFILETIME lpKernelTime, 1160 IN LPFILETIME lpUserTime) 1161 { 1162 KERNEL_USER_TIMES Kut; 1163 NTSTATUS Status; 1164 1165 /* Query the times */ 1166 Status = NtQueryInformationProcess(hProcess, 1167 ProcessTimes, 1168 &Kut, 1169 sizeof(Kut), 1170 NULL); 1171 if (!NT_SUCCESS(Status)) 1172 { 1173 /* Handle failure */ 1174 BaseSetLastNTError(Status); 1175 return FALSE; 1176 } 1177 1178 /* Copy all the times and return success */ 1179 lpCreationTime->dwLowDateTime = Kut.CreateTime.u.LowPart; 1180 lpCreationTime->dwHighDateTime = Kut.CreateTime.u.HighPart; 1181 lpExitTime->dwLowDateTime = Kut.ExitTime.u.LowPart; 1182 lpExitTime->dwHighDateTime = Kut.ExitTime.u.HighPart; 1183 lpKernelTime->dwLowDateTime = Kut.KernelTime.u.LowPart; 1184 lpKernelTime->dwHighDateTime = Kut.KernelTime.u.HighPart; 1185 lpUserTime->dwLowDateTime = Kut.UserTime.u.LowPart; 1186 lpUserTime->dwHighDateTime = Kut.UserTime.u.HighPart; 1187 return TRUE; 1188 } 1189 1190 /* 1191 * @implemented 1192 */ 1193 HANDLE 1194 WINAPI 1195 GetCurrentProcess(VOID) 1196 { 1197 return (HANDLE)NtCurrentProcess(); 1198 } 1199 1200 /* 1201 * @implemented 1202 */ 1203 HANDLE 1204 WINAPI 1205 GetCurrentThread(VOID) 1206 { 1207 return (HANDLE)NtCurrentThread(); 1208 } 1209 1210 /* 1211 * @implemented 1212 */ 1213 DWORD 1214 WINAPI 1215 GetCurrentProcessId(VOID) 1216 { 1217 return HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess); 1218 } 1219 1220 /* 1221 * @implemented 1222 */ 1223 BOOL 1224 WINAPI 1225 GetExitCodeProcess(IN HANDLE hProcess, 1226 IN LPDWORD lpExitCode) 1227 { 1228 PROCESS_BASIC_INFORMATION ProcessBasic; 1229 NTSTATUS Status; 1230 1231 /* Ask the kernel */ 1232 Status = NtQueryInformationProcess(hProcess, 1233 ProcessBasicInformation, 1234 &ProcessBasic, 1235 sizeof(ProcessBasic), 1236 NULL); 1237 if (!NT_SUCCESS(Status)) 1238 { 1239 /* We failed, was this because this is a VDM process? */ 1240 if (BaseCheckForVDM(hProcess, lpExitCode) != FALSE) return TRUE; 1241 1242 /* Not a VDM process, fail the call */ 1243 BaseSetLastNTError(Status); 1244 return FALSE; 1245 } 1246 1247 /* Succes case, return the exit code */ 1248 *lpExitCode = (DWORD)ProcessBasic.ExitStatus; 1249 return TRUE; 1250 } 1251 1252 /* 1253 * @implemented 1254 */ 1255 DWORD 1256 WINAPI 1257 GetProcessId(IN HANDLE Process) 1258 { 1259 PROCESS_BASIC_INFORMATION ProcessBasic; 1260 NTSTATUS Status; 1261 1262 /* Query the kernel */ 1263 Status = NtQueryInformationProcess(Process, 1264 ProcessBasicInformation, 1265 &ProcessBasic, 1266 sizeof(ProcessBasic), 1267 NULL); 1268 if (!NT_SUCCESS(Status)) 1269 { 1270 /* Handle failure */ 1271 BaseSetLastNTError(Status); 1272 return 0; 1273 } 1274 1275 /* Return the PID */ 1276 return (DWORD)ProcessBasic.UniqueProcessId; 1277 } 1278 1279 /* 1280 * @implemented 1281 */ 1282 HANDLE 1283 WINAPI 1284 OpenProcess(IN DWORD dwDesiredAccess, 1285 IN BOOL bInheritHandle, 1286 IN DWORD dwProcessId) 1287 { 1288 NTSTATUS Status; 1289 HANDLE ProcessHandle; 1290 OBJECT_ATTRIBUTES ObjectAttributes; 1291 CLIENT_ID ClientId; 1292 1293 /* Setup the input client ID structure */ 1294 ClientId.UniqueProcess = UlongToHandle(dwProcessId); 1295 ClientId.UniqueThread = 0; 1296 1297 /* This is needed just to define the inheritance flags */ 1298 InitializeObjectAttributes(&ObjectAttributes, 1299 NULL, 1300 (bInheritHandle ? OBJ_INHERIT : 0), 1301 NULL, 1302 NULL); 1303 1304 /* Now try to open the process */ 1305 Status = NtOpenProcess(&ProcessHandle, 1306 dwDesiredAccess, 1307 &ObjectAttributes, 1308 &ClientId); 1309 if (!NT_SUCCESS(Status)) 1310 { 1311 /* Handle failure */ 1312 BaseSetLastNTError(Status); 1313 return NULL; 1314 } 1315 1316 /* Otherwise return a handle to the process */ 1317 return ProcessHandle; 1318 } 1319 1320 /* 1321 * @implemented 1322 */ 1323 VOID 1324 WINAPI 1325 RegisterWaitForInputIdle(IN WaitForInputIdleType lpfnRegisterWaitForInputIdle) 1326 { 1327 /* Write the global function pointer */ 1328 UserWaitForInputIdleRoutine = lpfnRegisterWaitForInputIdle; 1329 } 1330 1331 /* 1332 * @implemented 1333 */ 1334 VOID 1335 WINAPI 1336 GetStartupInfoW(IN LPSTARTUPINFOW lpStartupInfo) 1337 { 1338 PRTL_USER_PROCESS_PARAMETERS Params; 1339 1340 /* Get the process parameters */ 1341 Params = NtCurrentPeb()->ProcessParameters; 1342 1343 /* Copy the data out of there */ 1344 lpStartupInfo->cb = sizeof(STARTUPINFOW); 1345 lpStartupInfo->lpReserved = Params->ShellInfo.Buffer; 1346 lpStartupInfo->lpDesktop = Params->DesktopInfo.Buffer; 1347 lpStartupInfo->lpTitle = Params->WindowTitle.Buffer; 1348 lpStartupInfo->dwX = Params->StartingX; 1349 lpStartupInfo->dwY = Params->StartingY; 1350 lpStartupInfo->dwXSize = Params->CountX; 1351 lpStartupInfo->dwYSize = Params->CountY; 1352 lpStartupInfo->dwXCountChars = Params->CountCharsX; 1353 lpStartupInfo->dwYCountChars = Params->CountCharsY; 1354 lpStartupInfo->dwFillAttribute = Params->FillAttribute; 1355 lpStartupInfo->dwFlags = Params->WindowFlags; 1356 lpStartupInfo->wShowWindow = (WORD)Params->ShowWindowFlags; 1357 lpStartupInfo->cbReserved2 = Params->RuntimeData.Length; 1358 lpStartupInfo->lpReserved2 = (LPBYTE)Params->RuntimeData.Buffer; 1359 1360 /* Check if the standard handles are being used for other features */ 1361 if (lpStartupInfo->dwFlags & (STARTF_USESTDHANDLES | 1362 STARTF_USEHOTKEY | 1363 STARTF_SHELLPRIVATE)) 1364 { 1365 /* These are, so copy the standard handles too */ 1366 lpStartupInfo->hStdInput = Params->StandardInput; 1367 lpStartupInfo->hStdOutput = Params->StandardOutput; 1368 lpStartupInfo->hStdError = Params->StandardError; 1369 } 1370 } 1371 1372 /* 1373 * @implemented 1374 */ 1375 VOID 1376 WINAPI 1377 GetStartupInfoA(IN LPSTARTUPINFOA lpStartupInfo) 1378 { 1379 PRTL_USER_PROCESS_PARAMETERS Params; 1380 ANSI_STRING TitleString, ShellString, DesktopString; 1381 LPSTARTUPINFOA StartupInfo; 1382 NTSTATUS Status; 1383 1384 /* Get the cached information as well as the PEB parameters */ 1385 StartupInfo = BaseAnsiStartupInfo; 1386 Params = NtCurrentPeb()->ProcessParameters; 1387 1388 /* Check if this is the first time we have to get the cached version */ 1389 while (!StartupInfo) 1390 { 1391 /* Create new ANSI startup info */ 1392 StartupInfo = RtlAllocateHeap(RtlGetProcessHeap(), 1393 0, 1394 sizeof(*StartupInfo)); 1395 if (StartupInfo) 1396 { 1397 /* Zero out string pointers in case we fail to create them */ 1398 StartupInfo->lpReserved = NULL; 1399 StartupInfo->lpDesktop = NULL; 1400 StartupInfo->lpTitle = NULL; 1401 1402 /* Set the size */ 1403 StartupInfo->cb = sizeof(*StartupInfo); 1404 1405 /* Copy what's already stored in the PEB */ 1406 StartupInfo->dwX = Params->StartingX; 1407 StartupInfo->dwY = Params->StartingY; 1408 StartupInfo->dwXSize = Params->CountX; 1409 StartupInfo->dwYSize = Params->CountY; 1410 StartupInfo->dwXCountChars = Params->CountCharsX; 1411 StartupInfo->dwYCountChars = Params->CountCharsY; 1412 StartupInfo->dwFillAttribute = Params->FillAttribute; 1413 StartupInfo->dwFlags = Params->WindowFlags; 1414 StartupInfo->wShowWindow = (WORD)Params->ShowWindowFlags; 1415 StartupInfo->cbReserved2 = Params->RuntimeData.Length; 1416 StartupInfo->lpReserved2 = (LPBYTE)Params->RuntimeData.Buffer; 1417 StartupInfo->hStdInput = Params->StandardInput; 1418 StartupInfo->hStdOutput = Params->StandardOutput; 1419 StartupInfo->hStdError = Params->StandardError; 1420 1421 /* Copy shell info string */ 1422 Status = RtlUnicodeStringToAnsiString(&ShellString, 1423 &Params->ShellInfo, 1424 TRUE); 1425 if (NT_SUCCESS(Status)) 1426 { 1427 /* Save it */ 1428 StartupInfo->lpReserved = ShellString.Buffer; 1429 1430 /* Copy desktop info string */ 1431 Status = RtlUnicodeStringToAnsiString(&DesktopString, 1432 &Params->DesktopInfo, 1433 TRUE); 1434 if (NT_SUCCESS(Status)) 1435 { 1436 /* Save it */ 1437 StartupInfo->lpDesktop = DesktopString.Buffer; 1438 1439 /* Copy window title string */ 1440 Status = RtlUnicodeStringToAnsiString(&TitleString, 1441 &Params->WindowTitle, 1442 TRUE); 1443 if (NT_SUCCESS(Status)) 1444 { 1445 /* Save it */ 1446 StartupInfo->lpTitle = TitleString.Buffer; 1447 1448 /* We finished with the ANSI version, try to cache it */ 1449 if (!InterlockedCompareExchangePointer((PVOID*)&BaseAnsiStartupInfo, 1450 StartupInfo, 1451 NULL)) 1452 { 1453 /* We were the first thread through, use the data */ 1454 break; 1455 } 1456 1457 /* Someone beat us to it, use their data instead */ 1458 StartupInfo = BaseAnsiStartupInfo; 1459 Status = STATUS_SUCCESS; 1460 1461 /* We're going to free our own stuff, but not raise */ 1462 RtlFreeAnsiString(&TitleString); 1463 } 1464 RtlFreeAnsiString(&DesktopString); 1465 } 1466 RtlFreeAnsiString(&ShellString); 1467 } 1468 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo); 1469 } 1470 else 1471 { 1472 /* No memory, fail */ 1473 Status = STATUS_NO_MEMORY; 1474 } 1475 1476 /* Raise an error unless we got here due to the race condition */ 1477 if (!NT_SUCCESS(Status)) RtlRaiseStatus(Status); 1478 } 1479 1480 /* Now copy from the cached ANSI version */ 1481 lpStartupInfo->cb = StartupInfo->cb; 1482 lpStartupInfo->lpReserved = StartupInfo->lpReserved; 1483 lpStartupInfo->lpDesktop = StartupInfo->lpDesktop; 1484 lpStartupInfo->lpTitle = StartupInfo->lpTitle; 1485 lpStartupInfo->dwX = StartupInfo->dwX; 1486 lpStartupInfo->dwY = StartupInfo->dwY; 1487 lpStartupInfo->dwXSize = StartupInfo->dwXSize; 1488 lpStartupInfo->dwYSize = StartupInfo->dwYSize; 1489 lpStartupInfo->dwXCountChars = StartupInfo->dwXCountChars; 1490 lpStartupInfo->dwYCountChars = StartupInfo->dwYCountChars; 1491 lpStartupInfo->dwFillAttribute = StartupInfo->dwFillAttribute; 1492 lpStartupInfo->dwFlags = StartupInfo->dwFlags; 1493 lpStartupInfo->wShowWindow = StartupInfo->wShowWindow; 1494 lpStartupInfo->cbReserved2 = StartupInfo->cbReserved2; 1495 lpStartupInfo->lpReserved2 = StartupInfo->lpReserved2; 1496 1497 /* Check if the shell is hijacking the handles for other features */ 1498 if (lpStartupInfo->dwFlags & 1499 (STARTF_USESTDHANDLES | STARTF_USEHOTKEY | STARTF_SHELLPRIVATE)) 1500 { 1501 /* It isn't, so we can return the raw values */ 1502 lpStartupInfo->hStdInput = StartupInfo->hStdInput; 1503 lpStartupInfo->hStdOutput = StartupInfo->hStdOutput; 1504 lpStartupInfo->hStdError = StartupInfo->hStdError; 1505 } 1506 else 1507 { 1508 /* It is, so make sure nobody uses these as console handles */ 1509 lpStartupInfo->hStdInput = INVALID_HANDLE_VALUE; 1510 lpStartupInfo->hStdOutput = INVALID_HANDLE_VALUE; 1511 lpStartupInfo->hStdError = INVALID_HANDLE_VALUE; 1512 } 1513 } 1514 1515 /* 1516 * @implemented 1517 */ 1518 BOOL 1519 WINAPI 1520 FlushInstructionCache(IN HANDLE hProcess, 1521 IN LPCVOID lpBaseAddress, 1522 IN SIZE_T nSize) 1523 { 1524 NTSTATUS Status; 1525 1526 /* Call the native function */ 1527 Status = NtFlushInstructionCache(hProcess, (PVOID)lpBaseAddress, nSize); 1528 if (!NT_SUCCESS(Status)) 1529 { 1530 /* Handle failure case */ 1531 BaseSetLastNTError(Status); 1532 return FALSE; 1533 } 1534 1535 /* All good */ 1536 return TRUE; 1537 } 1538 1539 /* 1540 * @implemented 1541 */ 1542 VOID 1543 WINAPI 1544 ExitProcess(IN UINT uExitCode) 1545 { 1546 BASE_API_MESSAGE ApiMessage; 1547 PBASE_EXIT_PROCESS ExitProcessRequest = &ApiMessage.Data.ExitProcessRequest; 1548 1549 ASSERT(!BaseRunningInServerProcess); 1550 1551 _SEH2_TRY 1552 { 1553 /* Acquire the PEB lock */ 1554 RtlAcquirePebLock(); 1555 1556 /* Kill all the threads */ 1557 NtTerminateProcess(NULL, uExitCode); 1558 1559 /* Unload all DLLs */ 1560 LdrShutdownProcess(); 1561 1562 /* Notify Base Server of process termination */ 1563 ExitProcessRequest->uExitCode = uExitCode; 1564 CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage, 1565 NULL, 1566 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepExitProcess), 1567 sizeof(*ExitProcessRequest)); 1568 1569 /* Now do it again */ 1570 NtTerminateProcess(NtCurrentProcess(), uExitCode); 1571 } 1572 _SEH2_FINALLY 1573 { 1574 /* Release the PEB lock */ 1575 RtlReleasePebLock(); 1576 } 1577 _SEH2_END; 1578 1579 /* should never get here */ 1580 ASSERT(0); 1581 while(1); 1582 } 1583 1584 /* 1585 * @implemented 1586 */ 1587 BOOL 1588 WINAPI 1589 TerminateProcess(IN HANDLE hProcess, 1590 IN UINT uExitCode) 1591 { 1592 NTSTATUS Status; 1593 1594 /* Check if no handle was passed in */ 1595 if (!hProcess) 1596 { 1597 /* Set error code */ 1598 SetLastError(ERROR_INVALID_HANDLE); 1599 } 1600 else 1601 { 1602 /* Otherwise, try to terminate the process */ 1603 Status = NtTerminateProcess(hProcess, uExitCode); 1604 if (NT_SUCCESS(Status)) return TRUE; 1605 1606 /* It failed, convert error code */ 1607 BaseSetLastNTError(Status); 1608 } 1609 1610 /* This is the failure path */ 1611 return FALSE; 1612 } 1613 1614 /* 1615 * @implemented 1616 */ 1617 VOID 1618 WINAPI 1619 FatalAppExitA(UINT uAction, 1620 LPCSTR lpMessageText) 1621 { 1622 PUNICODE_STRING MessageTextU; 1623 ANSI_STRING MessageText; 1624 NTSTATUS Status; 1625 1626 /* Initialize the string using the static TEB pointer */ 1627 MessageTextU = &NtCurrentTeb()->StaticUnicodeString; 1628 RtlInitAnsiString(&MessageText, (LPSTR)lpMessageText); 1629 1630 /* Convert to unicode, or just exit normally if this failed */ 1631 Status = RtlAnsiStringToUnicodeString(MessageTextU, &MessageText, FALSE); 1632 if (!NT_SUCCESS(Status)) ExitProcess(0); 1633 1634 /* Call the Wide function */ 1635 FatalAppExitW(uAction, MessageTextU->Buffer); 1636 } 1637 1638 /* 1639 * @implemented 1640 */ 1641 VOID 1642 WINAPI 1643 FatalAppExitW(IN UINT uAction, 1644 IN LPCWSTR lpMessageText) 1645 { 1646 UNICODE_STRING UnicodeString; 1647 ULONG Response; 1648 NTSTATUS Status; 1649 1650 /* Setup the string to print out */ 1651 RtlInitUnicodeString(&UnicodeString, lpMessageText); 1652 1653 /* Display the hard error no matter what */ 1654 Status = NtRaiseHardError(STATUS_FATAL_APP_EXIT | HARDERROR_OVERRIDE_ERRORMODE, 1655 1, 1656 1, 1657 (PULONG_PTR)&UnicodeString, 1658 #if DBG 1659 /* On Checked builds, Windows allows the user to cancel the operation */ 1660 OptionOkCancel, 1661 #else 1662 OptionOk, 1663 #endif 1664 &Response); 1665 1666 #if DBG 1667 /* Give the user a chance to abort */ 1668 if ((NT_SUCCESS(Status)) && (Response == ResponseCancel)) return; 1669 #endif 1670 1671 /* Otherwise kill the process */ 1672 ExitProcess(0); 1673 } 1674 1675 /* 1676 * @implemented 1677 */ 1678 VOID 1679 WINAPI 1680 FatalExit(IN int ExitCode) 1681 { 1682 #if DBG 1683 /* On Checked builds, Windows gives the user a nice little debugger UI */ 1684 CHAR ch[2]; 1685 DbgPrint("FatalExit...\n"); 1686 DbgPrint("\n"); 1687 1688 while (TRUE) 1689 { 1690 DbgPrompt( "A (Abort), B (Break), I (Ignore)? ", ch, sizeof(ch)); 1691 switch (ch[0]) 1692 { 1693 case 'B': case 'b': 1694 DbgBreakPoint(); 1695 break; 1696 1697 case 'A': case 'a': 1698 ExitProcess(ExitCode); 1699 1700 case 'I': case 'i': 1701 return; 1702 } 1703 } 1704 #endif 1705 /* On other builds, just kill the process */ 1706 ExitProcess(ExitCode); 1707 } 1708 1709 /* 1710 * @implemented 1711 */ 1712 DWORD 1713 WINAPI 1714 GetPriorityClass(IN HANDLE hProcess) 1715 { 1716 NTSTATUS Status; 1717 PROCESS_PRIORITY_CLASS PriorityClass; 1718 1719 /* Query the kernel */ 1720 Status = NtQueryInformationProcess(hProcess, 1721 ProcessPriorityClass, 1722 &PriorityClass, 1723 sizeof(PriorityClass), 1724 NULL); 1725 if (NT_SUCCESS(Status)) 1726 { 1727 /* Handle the conversion from NT to Win32 classes */ 1728 switch (PriorityClass.PriorityClass) 1729 { 1730 case PROCESS_PRIORITY_CLASS_IDLE: return IDLE_PRIORITY_CLASS; 1731 case PROCESS_PRIORITY_CLASS_BELOW_NORMAL: return BELOW_NORMAL_PRIORITY_CLASS; 1732 case PROCESS_PRIORITY_CLASS_ABOVE_NORMAL: return ABOVE_NORMAL_PRIORITY_CLASS; 1733 case PROCESS_PRIORITY_CLASS_HIGH: return HIGH_PRIORITY_CLASS; 1734 case PROCESS_PRIORITY_CLASS_REALTIME: return REALTIME_PRIORITY_CLASS; 1735 case PROCESS_PRIORITY_CLASS_NORMAL: default: return NORMAL_PRIORITY_CLASS; 1736 } 1737 } 1738 1739 /* Failure path */ 1740 BaseSetLastNTError(Status); 1741 return FALSE; 1742 } 1743 1744 /* 1745 * @implemented 1746 */ 1747 BOOL 1748 WINAPI 1749 SetPriorityClass(IN HANDLE hProcess, 1750 IN DWORD dwPriorityClass) 1751 { 1752 NTSTATUS Status; 1753 PVOID State = NULL; 1754 PROCESS_PRIORITY_CLASS PriorityClass; 1755 1756 /* Handle conversion from Win32 to NT priority classes */ 1757 switch (dwPriorityClass) 1758 { 1759 case IDLE_PRIORITY_CLASS: 1760 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE; 1761 break; 1762 1763 case BELOW_NORMAL_PRIORITY_CLASS: 1764 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL; 1765 break; 1766 1767 case NORMAL_PRIORITY_CLASS: 1768 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL; 1769 break; 1770 1771 case ABOVE_NORMAL_PRIORITY_CLASS: 1772 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL; 1773 break; 1774 1775 case HIGH_PRIORITY_CLASS: 1776 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH; 1777 break; 1778 1779 case REALTIME_PRIORITY_CLASS: 1780 /* Try to acquire the privilege. If it fails, just use HIGH */ 1781 State = BasepIsRealtimeAllowed(TRUE); 1782 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH; 1783 PriorityClass.PriorityClass += (State != NULL); 1784 break; 1785 1786 default: 1787 /* Unrecognized priority classes don't make it to the kernel */ 1788 SetLastError(ERROR_INVALID_PARAMETER); 1789 return FALSE; 1790 } 1791 1792 /* Send the request to the kernel, and don't touch the foreground flag */ 1793 PriorityClass.Foreground = FALSE; 1794 Status = NtSetInformationProcess(hProcess, 1795 ProcessPriorityClass, 1796 &PriorityClass, 1797 sizeof(PROCESS_PRIORITY_CLASS)); 1798 1799 /* Release the privilege if we had it */ 1800 if (State) RtlReleasePrivilege(State); 1801 if (!NT_SUCCESS(Status)) 1802 { 1803 /* Handle error path */ 1804 BaseSetLastNTError(Status); 1805 return FALSE; 1806 } 1807 1808 /* All done */ 1809 return TRUE; 1810 } 1811 1812 /* 1813 * @implemented 1814 */ 1815 DWORD 1816 WINAPI 1817 GetProcessVersion(IN DWORD ProcessId) 1818 { 1819 DWORD Version = 0; 1820 PIMAGE_NT_HEADERS NtHeader; 1821 PIMAGE_DOS_HEADER DosHeader; 1822 PPEB Peb; 1823 PROCESS_BASIC_INFORMATION ProcessBasicInfo; 1824 PVOID BaseAddress; 1825 ULONG e_lfanew; 1826 HANDLE ProcessHandle = NULL; 1827 NTSTATUS Status; 1828 USHORT VersionData[2]; 1829 BOOLEAN Result; 1830 1831 /* We'll be accessing stuff that can fault, so protect everything with SEH */ 1832 _SEH2_TRY 1833 { 1834 /* It this an in-process or out-of-process request? */ 1835 if (!(ProcessId) || (GetCurrentProcessId() == ProcessId)) 1836 { 1837 /* It's in-process, so just read our own header */ 1838 NtHeader = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress); 1839 if (!NtHeader) 1840 { 1841 /* Unable to read the NT header, something is wrong here... */ 1842 Status = STATUS_INVALID_IMAGE_FORMAT; 1843 goto Error; 1844 } 1845 1846 /* Get the version straight out of the NT header */ 1847 Version = MAKELONG(NtHeader->OptionalHeader.MinorSubsystemVersion, 1848 NtHeader->OptionalHeader.MajorSubsystemVersion); 1849 } 1850 else 1851 { 1852 /* Out-of-process, so open it */ 1853 ProcessHandle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, 1854 FALSE, 1855 ProcessId); 1856 if (!ProcessHandle) _SEH2_YIELD(return 0); 1857 1858 /* Try to find out where its PEB lives */ 1859 Status = NtQueryInformationProcess(ProcessHandle, 1860 ProcessBasicInformation, 1861 &ProcessBasicInfo, 1862 sizeof(ProcessBasicInfo), 1863 NULL); 1864 1865 if (!NT_SUCCESS(Status)) goto Error; 1866 Peb = ProcessBasicInfo.PebBaseAddress; 1867 1868 /* Now that we have the PEB, read the image base address out of it */ 1869 Result = ReadProcessMemory(ProcessHandle, 1870 &Peb->ImageBaseAddress, 1871 &BaseAddress, 1872 sizeof(BaseAddress), 1873 NULL); 1874 if (!Result) goto Error; 1875 1876 /* Now read the e_lfanew (offset to NT header) from the base */ 1877 DosHeader = BaseAddress; 1878 Result = ReadProcessMemory(ProcessHandle, 1879 &DosHeader->e_lfanew, 1880 &e_lfanew, 1881 sizeof(e_lfanew), 1882 NULL); 1883 if (!Result) goto Error; 1884 1885 /* And finally, read the NT header itself by adding the offset */ 1886 NtHeader = (PVOID)((ULONG_PTR)BaseAddress + e_lfanew); 1887 Result = ReadProcessMemory(ProcessHandle, 1888 &NtHeader->OptionalHeader.MajorSubsystemVersion, 1889 &VersionData, 1890 sizeof(VersionData), 1891 NULL); 1892 if (!Result) goto Error; 1893 1894 /* Get the version straight out of the NT header */ 1895 Version = MAKELONG(VersionData[0], VersionData[1]); 1896 1897 Error: 1898 /* If there was an error anywhere, set the last error */ 1899 if (!NT_SUCCESS(Status)) BaseSetLastNTError(Status); 1900 } 1901 } 1902 _SEH2_FINALLY 1903 { 1904 /* Close the process handle */ 1905 if (ProcessHandle) CloseHandle(ProcessHandle); 1906 } 1907 _SEH2_END; 1908 1909 /* And return the version data */ 1910 return Version; 1911 } 1912 1913 /* 1914 * @implemented 1915 */ 1916 BOOL 1917 WINAPI 1918 GetProcessIoCounters(IN HANDLE hProcess, 1919 OUT PIO_COUNTERS lpIoCounters) 1920 { 1921 NTSTATUS Status; 1922 1923 /* Query the kernel. Structures are identical, so let it do the copy too. */ 1924 Status = NtQueryInformationProcess(hProcess, 1925 ProcessIoCounters, 1926 lpIoCounters, 1927 sizeof(IO_COUNTERS), 1928 NULL); 1929 if (!NT_SUCCESS(Status)) 1930 { 1931 /* Handle error path */ 1932 BaseSetLastNTError(Status); 1933 return FALSE; 1934 } 1935 1936 /* All done */ 1937 return TRUE; 1938 } 1939 1940 /* 1941 * @implemented 1942 */ 1943 BOOL 1944 WINAPI 1945 GetProcessPriorityBoost(IN HANDLE hProcess, 1946 OUT PBOOL pDisablePriorityBoost) 1947 { 1948 NTSTATUS Status; 1949 ULONG PriorityBoost; 1950 1951 /* Query the kernel */ 1952 Status = NtQueryInformationProcess(hProcess, 1953 ProcessPriorityBoost, 1954 &PriorityBoost, 1955 sizeof(PriorityBoost), 1956 NULL); 1957 if (NT_SUCCESS(Status)) 1958 { 1959 /* Convert from ULONG to a BOOL */ 1960 *pDisablePriorityBoost = PriorityBoost ? TRUE : FALSE; 1961 return TRUE; 1962 } 1963 1964 /* Handle error path */ 1965 BaseSetLastNTError(Status); 1966 return FALSE; 1967 } 1968 1969 /* 1970 * @implemented 1971 */ 1972 BOOL 1973 WINAPI 1974 SetProcessPriorityBoost(IN HANDLE hProcess, 1975 IN BOOL bDisablePriorityBoost) 1976 { 1977 NTSTATUS Status; 1978 ULONG PriorityBoost; 1979 1980 /* Enforce that this is a BOOL, and send it to the kernel as a ULONG */ 1981 PriorityBoost = (bDisablePriorityBoost ? TRUE : FALSE); 1982 Status = NtSetInformationProcess(hProcess, 1983 ProcessPriorityBoost, 1984 &PriorityBoost, 1985 sizeof(ULONG)); 1986 if (!NT_SUCCESS(Status)) 1987 { 1988 /* Handle error path */ 1989 BaseSetLastNTError(Status); 1990 return FALSE; 1991 } 1992 1993 /* All done */ 1994 return TRUE; 1995 } 1996 1997 /* 1998 * @implemented 1999 */ 2000 BOOL 2001 WINAPI 2002 GetProcessHandleCount(IN HANDLE hProcess, 2003 OUT PDWORD pdwHandleCount) 2004 { 2005 ULONG phc; 2006 NTSTATUS Status; 2007 2008 /* Query the kernel */ 2009 Status = NtQueryInformationProcess(hProcess, 2010 ProcessHandleCount, 2011 &phc, 2012 sizeof(phc), 2013 NULL); 2014 if (NT_SUCCESS(Status)) 2015 { 2016 /* Copy the count and return success */ 2017 *pdwHandleCount = phc; 2018 return TRUE; 2019 } 2020 2021 /* Handle error path */ 2022 BaseSetLastNTError(Status); 2023 return FALSE; 2024 } 2025 2026 /* 2027 * @implemented 2028 */ 2029 BOOL 2030 WINAPI 2031 IsWow64Process(IN HANDLE hProcess, 2032 OUT PBOOL Wow64Process) 2033 { 2034 ULONG_PTR pbi; 2035 NTSTATUS Status; 2036 2037 /* Query the kernel */ 2038 Status = NtQueryInformationProcess(hProcess, 2039 ProcessWow64Information, 2040 &pbi, 2041 sizeof(pbi), 2042 NULL); 2043 if (!NT_SUCCESS(Status)) 2044 { 2045 /* Handle error path */ 2046 BaseSetLastNTError(Status); 2047 return FALSE; 2048 } 2049 2050 /* Enforce this is a BOOL, and return success */ 2051 *Wow64Process = (pbi != 0); 2052 return TRUE; 2053 } 2054 2055 /* 2056 * @implemented 2057 */ 2058 LPSTR 2059 WINAPI 2060 GetCommandLineA(VOID) 2061 { 2062 return BaseAnsiCommandLine.Buffer; 2063 } 2064 2065 /* 2066 * @implemented 2067 */ 2068 LPWSTR 2069 WINAPI 2070 GetCommandLineW(VOID) 2071 { 2072 return BaseUnicodeCommandLine.Buffer; 2073 } 2074 2075 /* 2076 * @implemented 2077 */ 2078 BOOL 2079 NTAPI 2080 ReadProcessMemory(IN HANDLE hProcess, 2081 IN LPCVOID lpBaseAddress, 2082 IN LPVOID lpBuffer, 2083 IN SIZE_T nSize, 2084 OUT SIZE_T* lpNumberOfBytesRead) 2085 { 2086 NTSTATUS Status; 2087 2088 /* Do the read */ 2089 Status = NtReadVirtualMemory(hProcess, 2090 (PVOID)lpBaseAddress, 2091 lpBuffer, 2092 nSize, 2093 &nSize); 2094 2095 /* In user-mode, this parameter is optional */ 2096 if (lpNumberOfBytesRead) *lpNumberOfBytesRead = nSize; 2097 if (!NT_SUCCESS(Status)) 2098 { 2099 /* We failed */ 2100 BaseSetLastNTError(Status); 2101 return FALSE; 2102 } 2103 2104 /* Return success */ 2105 return TRUE; 2106 } 2107 2108 /* 2109 * @implemented 2110 */ 2111 BOOL 2112 NTAPI 2113 WriteProcessMemory(IN HANDLE hProcess, 2114 IN LPVOID lpBaseAddress, 2115 IN LPCVOID lpBuffer, 2116 IN SIZE_T nSize, 2117 OUT SIZE_T *lpNumberOfBytesWritten) 2118 { 2119 NTSTATUS Status; 2120 ULONG OldValue; 2121 SIZE_T RegionSize; 2122 PVOID Base; 2123 BOOLEAN UnProtect; 2124 2125 /* Set parameters for protect call */ 2126 RegionSize = nSize; 2127 Base = lpBaseAddress; 2128 2129 /* Check the current status */ 2130 Status = NtProtectVirtualMemory(hProcess, 2131 &Base, 2132 &RegionSize, 2133 PAGE_EXECUTE_READWRITE, 2134 &OldValue); 2135 if (NT_SUCCESS(Status)) 2136 { 2137 /* Check if we are unprotecting */ 2138 UnProtect = OldValue & (PAGE_READWRITE | 2139 PAGE_WRITECOPY | 2140 PAGE_EXECUTE_READWRITE | 2141 PAGE_EXECUTE_WRITECOPY) ? FALSE : TRUE; 2142 if (!UnProtect) 2143 { 2144 /* Set the new protection */ 2145 Status = NtProtectVirtualMemory(hProcess, 2146 &Base, 2147 &RegionSize, 2148 OldValue, 2149 &OldValue); 2150 2151 /* Write the memory */ 2152 Status = NtWriteVirtualMemory(hProcess, 2153 lpBaseAddress, 2154 (LPVOID)lpBuffer, 2155 nSize, 2156 &nSize); 2157 2158 /* In Win32, the parameter is optional, so handle this case */ 2159 if (lpNumberOfBytesWritten) *lpNumberOfBytesWritten = nSize; 2160 2161 if (!NT_SUCCESS(Status)) 2162 { 2163 /* We failed */ 2164 BaseSetLastNTError(Status); 2165 return FALSE; 2166 } 2167 2168 /* Flush the ITLB */ 2169 NtFlushInstructionCache(hProcess, lpBaseAddress, nSize); 2170 return TRUE; 2171 } 2172 else 2173 { 2174 /* Check if we were read only */ 2175 if (OldValue & (PAGE_NOACCESS | PAGE_READONLY)) 2176 { 2177 /* Restore protection and fail */ 2178 NtProtectVirtualMemory(hProcess, 2179 &Base, 2180 &RegionSize, 2181 OldValue, 2182 &OldValue); 2183 BaseSetLastNTError(STATUS_ACCESS_VIOLATION); 2184 2185 /* Note: This is what Windows returns and code depends on it */ 2186 return STATUS_ACCESS_VIOLATION; 2187 } 2188 2189 /* Otherwise, do the write */ 2190 Status = NtWriteVirtualMemory(hProcess, 2191 lpBaseAddress, 2192 (LPVOID)lpBuffer, 2193 nSize, 2194 &nSize); 2195 2196 /* In Win32, the parameter is optional, so handle this case */ 2197 if (lpNumberOfBytesWritten) *lpNumberOfBytesWritten = nSize; 2198 2199 /* And restore the protection */ 2200 NtProtectVirtualMemory(hProcess, 2201 &Base, 2202 &RegionSize, 2203 OldValue, 2204 &OldValue); 2205 if (!NT_SUCCESS(Status)) 2206 { 2207 /* We failed */ 2208 BaseSetLastNTError(STATUS_ACCESS_VIOLATION); 2209 2210 /* Note: This is what Windows returns and code depends on it */ 2211 return STATUS_ACCESS_VIOLATION; 2212 } 2213 2214 /* Flush the ITLB */ 2215 NtFlushInstructionCache(hProcess, lpBaseAddress, nSize); 2216 return TRUE; 2217 } 2218 } 2219 else 2220 { 2221 /* We failed */ 2222 BaseSetLastNTError(Status); 2223 return FALSE; 2224 } 2225 } 2226 2227 /* 2228 * @implemented 2229 */ 2230 BOOL 2231 WINAPI 2232 ProcessIdToSessionId(IN DWORD dwProcessId, 2233 OUT PDWORD pSessionId) 2234 { 2235 PROCESS_SESSION_INFORMATION SessionInformation; 2236 OBJECT_ATTRIBUTES ObjectAttributes; 2237 CLIENT_ID ClientId; 2238 HANDLE ProcessHandle; 2239 NTSTATUS Status; 2240 2241 /* Do a quick check if the pointer is not writable */ 2242 if (IsBadWritePtr(pSessionId, sizeof(DWORD))) 2243 { 2244 /* Fail fast */ 2245 SetLastError(ERROR_INVALID_PARAMETER); 2246 return FALSE; 2247 } 2248 2249 /* Open the process passed in by ID */ 2250 ClientId.UniqueProcess = UlongToHandle(dwProcessId); 2251 ClientId.UniqueThread = 0; 2252 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); 2253 Status = NtOpenProcess(&ProcessHandle, 2254 PROCESS_QUERY_INFORMATION, 2255 &ObjectAttributes, 2256 &ClientId); 2257 if (NT_SUCCESS(Status)) 2258 { 2259 /* Query the session ID from the kernel */ 2260 Status = NtQueryInformationProcess(ProcessHandle, 2261 ProcessSessionInformation, 2262 &SessionInformation, 2263 sizeof(SessionInformation), 2264 NULL); 2265 2266 /* Close the handle and check if we succeeded */ 2267 NtClose(ProcessHandle); 2268 if (NT_SUCCESS(Status)) 2269 { 2270 /* Return the session ID */ 2271 *pSessionId = SessionInformation.SessionId; 2272 return TRUE; 2273 } 2274 } 2275 2276 /* Set error code and fail */ 2277 BaseSetLastNTError(Status); 2278 return FALSE; 2279 } 2280 2281 2282 #define AddToHandle(x,y) (x) = (HANDLE)((ULONG_PTR)(x) | (y)); 2283 #define RemoveFromHandle(x,y) (x) = (HANDLE)((ULONG_PTR)(x) & ~(y)); 2284 C_ASSERT(PROCESS_PRIORITY_CLASS_REALTIME == (PROCESS_PRIORITY_CLASS_HIGH + 1)); 2285 2286 /* 2287 * @implemented 2288 */ 2289 BOOL 2290 WINAPI 2291 CreateProcessInternalW(IN HANDLE hUserToken, 2292 IN LPCWSTR lpApplicationName, 2293 IN LPWSTR lpCommandLine, 2294 IN LPSECURITY_ATTRIBUTES lpProcessAttributes, 2295 IN LPSECURITY_ATTRIBUTES lpThreadAttributes, 2296 IN BOOL bInheritHandles, 2297 IN DWORD dwCreationFlags, 2298 IN LPVOID lpEnvironment, 2299 IN LPCWSTR lpCurrentDirectory, 2300 IN LPSTARTUPINFOW lpStartupInfo, 2301 IN LPPROCESS_INFORMATION lpProcessInformation, 2302 OUT PHANDLE hNewToken) 2303 { 2304 // 2305 // Core variables used for creating the initial process and thread 2306 // 2307 SECURITY_ATTRIBUTES LocalThreadAttributes, LocalProcessAttributes; 2308 OBJECT_ATTRIBUTES LocalObjectAttributes; 2309 POBJECT_ATTRIBUTES ObjectAttributes; 2310 SECTION_IMAGE_INFORMATION ImageInformation; 2311 IO_STATUS_BLOCK IoStatusBlock; 2312 CLIENT_ID ClientId; 2313 ULONG NoWindow, StackSize, ErrorCode, Flags; 2314 SIZE_T RegionSize; 2315 USHORT ImageMachine; 2316 ULONG ParameterFlags, PrivilegeValue, HardErrorMode, ErrorResponse; 2317 ULONG_PTR ErrorParameters[2]; 2318 BOOLEAN InJob, SaferNeeded, UseLargePages, HavePrivilege; 2319 BOOLEAN QuerySection, SkipSaferAndAppCompat; 2320 CONTEXT Context; 2321 BASE_API_MESSAGE CsrMsg[2]; 2322 PBASE_CREATE_PROCESS CreateProcessMsg; 2323 PCSR_CAPTURE_BUFFER CaptureBuffer; 2324 PVOID BaseAddress, PrivilegeState, RealTimePrivilegeState; 2325 HANDLE DebugHandle, TokenHandle, JobHandle, KeyHandle, ThreadHandle; 2326 HANDLE FileHandle, SectionHandle, ProcessHandle; 2327 ULONG ResumeCount; 2328 PROCESS_PRIORITY_CLASS PriorityClass; 2329 NTSTATUS Status, AppCompatStatus, SaferStatus, IFEOStatus, ImageDbgStatus; 2330 PPEB Peb, RemotePeb; 2331 PTEB Teb; 2332 INITIAL_TEB InitialTeb; 2333 PVOID TibValue; 2334 PIMAGE_NT_HEADERS NtHeaders; 2335 STARTUPINFOW StartupInfo; 2336 PRTL_USER_PROCESS_PARAMETERS ProcessParameters; 2337 UNICODE_STRING DebuggerString; 2338 BOOL Result; 2339 // 2340 // Variables used for command-line and argument parsing 2341 // 2342 PCHAR pcScan; 2343 SIZE_T n; 2344 WCHAR SaveChar; 2345 ULONG Length, FileAttribs, CmdQuoteLength; 2346 ULONG ResultSize; 2347 SIZE_T EnvironmentLength, CmdLineLength; 2348 PWCHAR QuotedCmdLine, AnsiCmdCommand, ExtBuffer, CurrentDirectory; 2349 PWCHAR NullBuffer, ScanString, NameBuffer, SearchPath, DebuggerCmdLine; 2350 ANSI_STRING AnsiEnv; 2351 UNICODE_STRING UnicodeEnv, PathName; 2352 BOOLEAN SearchRetry, QuotesNeeded, CmdLineIsAppName, HasQuotes; 2353 2354 // 2355 // Variables used for Fusion/SxS (Side-by-Side Assemblies) 2356 // 2357 RTL_PATH_TYPE SxsPathType, PathType; 2358 #if _SXS_SUPPORT_ENABLED_ 2359 PRTL_BUFFER ByteBuffer; 2360 PRTL_UNICODE_STRING_BUFFER ThisBuffer, Buffer, SxsStaticBuffers[5]; 2361 PRTL_UNICODE_STRING_BUFFER* BufferHead, SxsStringBuffer; 2362 RTL_UNICODE_STRING_BUFFER SxsWin32ManifestPath, SxsNtManifestPath; 2363 RTL_UNICODE_STRING_BUFFER SxsWin32PolicyPath, SxsNtPolicyPath; 2364 RTL_UNICODE_STRING_BUFFER SxsWin32AssemblyDirectory; 2365 BASE_MSG_SXS_HANDLES MappedHandles, Handles, FileHandles; 2366 PVOID CapturedStrings[3]; 2367 SXS_WIN32_NT_PATH_PAIR ExePathPair, ManifestPathPair, PolicyPathPair; 2368 SXS_OVERRIDE_MANIFEST OverrideManifest; 2369 UNICODE_STRING FreeString, SxsNtExePath; 2370 PWCHAR SxsConglomeratedBuffer, StaticBuffer; 2371 ULONG ConglomeratedBufferSizeBytes, StaticBufferSize, i; 2372 #endif 2373 ULONG FusionFlags; 2374 2375 // 2376 // Variables used for path conversion (and partially Fusion/SxS) 2377 // 2378 PWCHAR FilePart, PathBuffer, FreeBuffer; 2379 BOOLEAN TranslationStatus; 2380 RTL_RELATIVE_NAME_U SxsWin32RelativePath; 2381 UNICODE_STRING PathBufferString, SxsWin32ExePath; 2382 2383 // 2384 // Variables used by Application Compatibility (and partially Fusion/SxS) 2385 // 2386 PVOID AppCompatSxsData, AppCompatData; 2387 ULONG AppCompatSxsDataSize, AppCompatDataSize; 2388 // 2389 // Variables used by VDM (Virtual Dos Machine) and WOW32 (16-bit Support) 2390 // 2391 ULONG BinarySubType, VdmBinaryType, VdmTask, VdmReserve; 2392 ULONG VdmUndoLevel; 2393 BOOLEAN UseVdmReserve; 2394 HANDLE VdmWaitObject; 2395 ANSI_STRING VdmAnsiEnv; 2396 UNICODE_STRING VdmString, VdmUnicodeEnv; 2397 BOOLEAN IsWowApp; 2398 PBASE_CHECK_VDM CheckVdmMsg; 2399 2400 /* Zero out the initial core variables and handles */ 2401 QuerySection = FALSE; 2402 InJob = FALSE; 2403 SkipSaferAndAppCompat = FALSE; 2404 ParameterFlags = 0; 2405 Flags = 0; 2406 DebugHandle = NULL; 2407 JobHandle = NULL; 2408 TokenHandle = NULL; 2409 FileHandle = NULL; 2410 SectionHandle = NULL; 2411 ProcessHandle = NULL; 2412 ThreadHandle = NULL; 2413 BaseAddress = (PVOID)1; 2414 2415 /* Zero out initial SxS and Application Compatibility state */ 2416 AppCompatData = NULL; 2417 AppCompatDataSize = 0; 2418 AppCompatSxsData = NULL; 2419 AppCompatSxsDataSize = 0; 2420 CaptureBuffer = NULL; 2421 #if _SXS_SUPPORT_ENABLED_ 2422 SxsConglomeratedBuffer = NULL; 2423 #endif 2424 FusionFlags = 0; 2425 2426 /* Zero out initial parsing variables -- others are initialized later */ 2427 DebuggerCmdLine = NULL; 2428 PathBuffer = NULL; 2429 SearchPath = NULL; 2430 NullBuffer = NULL; 2431 FreeBuffer = NULL; 2432 NameBuffer = NULL; 2433 CurrentDirectory = NULL; 2434 FilePart = NULL; 2435 DebuggerString.Buffer = NULL; 2436 HasQuotes = FALSE; 2437 QuotedCmdLine = NULL; 2438 2439 /* Zero out initial VDM state */ 2440 VdmAnsiEnv.Buffer = NULL; 2441 VdmUnicodeEnv.Buffer = NULL; 2442 VdmString.Buffer = NULL; 2443 VdmTask = 0; 2444 VdmUndoLevel = 0; 2445 VdmBinaryType = 0; 2446 VdmReserve = 0; 2447 VdmWaitObject = NULL; 2448 UseVdmReserve = FALSE; 2449 IsWowApp = FALSE; 2450 2451 /* Set message structures */ 2452 CreateProcessMsg = &CsrMsg[0].Data.CreateProcessRequest; 2453 CheckVdmMsg = &CsrMsg[1].Data.CheckVDMRequest; 2454 2455 /* Clear the more complex structures by zeroing out their entire memory */ 2456 RtlZeroMemory(&Context, sizeof(Context)); 2457 #if _SXS_SUPPORT_ENABLED_ 2458 RtlZeroMemory(&FileHandles, sizeof(FileHandles)); 2459 RtlZeroMemory(&MappedHandles, sizeof(MappedHandles)); 2460 RtlZeroMemory(&Handles, sizeof(Handles)); 2461 #endif 2462 RtlZeroMemory(&CreateProcessMsg->Sxs, sizeof(CreateProcessMsg->Sxs)); 2463 RtlZeroMemory(&LocalProcessAttributes, sizeof(LocalProcessAttributes)); 2464 RtlZeroMemory(&LocalThreadAttributes, sizeof(LocalThreadAttributes)); 2465 2466 /* Zero out output arguments as well */ 2467 RtlZeroMemory(lpProcessInformation, sizeof(*lpProcessInformation)); 2468 if (hNewToken) *hNewToken = NULL; 2469 2470 /* Capture the special window flag */ 2471 NoWindow = dwCreationFlags & CREATE_NO_WINDOW; 2472 dwCreationFlags &= ~CREATE_NO_WINDOW; 2473 2474 #if _SXS_SUPPORT_ENABLED_ 2475 /* Setup the SxS static string arrays and buffers */ 2476 SxsStaticBuffers[0] = &SxsWin32ManifestPath; 2477 SxsStaticBuffers[1] = &SxsWin32PolicyPath; 2478 SxsStaticBuffers[2] = &SxsWin32AssemblyDirectory; 2479 SxsStaticBuffers[3] = &SxsNtManifestPath; 2480 SxsStaticBuffers[4] = &SxsNtPolicyPath; 2481 ExePathPair.Win32 = &SxsWin32ExePath; 2482 ExePathPair.Nt = &SxsNtExePath; 2483 ManifestPathPair.Win32 = &SxsWin32ManifestPath.String; 2484 ManifestPathPair.Nt = &SxsNtManifestPath.String; 2485 PolicyPathPair.Win32 = &SxsWin32PolicyPath.String; 2486 PolicyPathPair.Nt = &SxsNtPolicyPath.String; 2487 #endif 2488 2489 DPRINT("CreateProcessInternalW: '%S' '%S' %lx\n", lpApplicationName, lpCommandLine, dwCreationFlags); 2490 2491 /* Finally, set our TEB and PEB */ 2492 Teb = NtCurrentTeb(); 2493 Peb = NtCurrentPeb(); 2494 2495 /* This combination is illegal (see MSDN) */ 2496 if ((dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) == 2497 (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) 2498 { 2499 DPRINT1("Invalid flag combo used\n"); 2500 SetLastError(ERROR_INVALID_PARAMETER); 2501 return FALSE; 2502 } 2503 2504 /* Convert the priority class */ 2505 if (dwCreationFlags & IDLE_PRIORITY_CLASS) 2506 { 2507 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE; 2508 } 2509 else if (dwCreationFlags & BELOW_NORMAL_PRIORITY_CLASS) 2510 { 2511 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL; 2512 } 2513 else if (dwCreationFlags & NORMAL_PRIORITY_CLASS) 2514 { 2515 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL; 2516 } 2517 else if (dwCreationFlags & ABOVE_NORMAL_PRIORITY_CLASS) 2518 { 2519 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL; 2520 } 2521 else if (dwCreationFlags & HIGH_PRIORITY_CLASS) 2522 { 2523 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH; 2524 } 2525 else if (dwCreationFlags & REALTIME_PRIORITY_CLASS) 2526 { 2527 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH; 2528 PriorityClass.PriorityClass += (BasepIsRealtimeAllowed(FALSE) != NULL); 2529 } 2530 else 2531 { 2532 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_INVALID; 2533 } 2534 2535 /* Done with the priority masks, so get rid of them */ 2536 PriorityClass.Foreground = FALSE; 2537 dwCreationFlags &= ~(NORMAL_PRIORITY_CLASS | 2538 IDLE_PRIORITY_CLASS | 2539 HIGH_PRIORITY_CLASS | 2540 REALTIME_PRIORITY_CLASS | 2541 BELOW_NORMAL_PRIORITY_CLASS | 2542 ABOVE_NORMAL_PRIORITY_CLASS); 2543 2544 /* You cannot request both a shared and a separate WoW VDM */ 2545 if ((dwCreationFlags & CREATE_SEPARATE_WOW_VDM) && 2546 (dwCreationFlags & CREATE_SHARED_WOW_VDM)) 2547 { 2548 /* Fail such nonsensical attempts */ 2549 DPRINT1("Invalid WOW flags\n"); 2550 SetLastError(ERROR_INVALID_PARAMETER); 2551 return FALSE; 2552 } 2553 else if (!(dwCreationFlags & CREATE_SHARED_WOW_VDM) && 2554 (BaseStaticServerData->DefaultSeparateVDM)) 2555 { 2556 /* A shared WoW VDM was not requested but system enforces separation */ 2557 dwCreationFlags |= CREATE_SEPARATE_WOW_VDM; 2558 } 2559 2560 /* If a shared WoW VDM is used, make sure the process isn't in a job */ 2561 if (!(dwCreationFlags & CREATE_SEPARATE_WOW_VDM) && 2562 (NtIsProcessInJob(NtCurrentProcess(), NULL))) 2563 { 2564 /* Remove the shared flag and add the separate flag */ 2565 dwCreationFlags = (dwCreationFlags &~ CREATE_SHARED_WOW_VDM) | 2566 CREATE_SEPARATE_WOW_VDM; 2567 } 2568 2569 /* Convert the environment */ 2570 if ((lpEnvironment) && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT)) 2571 { 2572 /* Scan the environment to calculate its Unicode size */ 2573 AnsiEnv.Buffer = pcScan = (PCHAR)lpEnvironment; 2574 while ((*pcScan) || (*(pcScan + 1))) ++pcScan; 2575 2576 /* Make sure the environment is not too large */ 2577 EnvironmentLength = (pcScan + sizeof(ANSI_NULL) - (PCHAR)lpEnvironment); 2578 if (EnvironmentLength > MAXUSHORT) 2579 { 2580 /* Fail */ 2581 SetLastError(ERROR_INVALID_PARAMETER); 2582 return FALSE; 2583 } 2584 2585 /* Create our ANSI String */ 2586 AnsiEnv.Length = (USHORT)EnvironmentLength; 2587 AnsiEnv.MaximumLength = AnsiEnv.Length + sizeof(ANSI_NULL); 2588 2589 /* Allocate memory for the Unicode Environment */ 2590 UnicodeEnv.Buffer = NULL; 2591 RegionSize = AnsiEnv.MaximumLength * sizeof(WCHAR); 2592 Status = NtAllocateVirtualMemory(NtCurrentProcess(), 2593 (PVOID)&UnicodeEnv.Buffer, 2594 0, 2595 &RegionSize, 2596 MEM_COMMIT, 2597 PAGE_READWRITE); 2598 if (!NT_SUCCESS(Status)) 2599 { 2600 /* Fail */ 2601 BaseSetLastNTError(Status); 2602 return FALSE; 2603 } 2604 2605 /* Use the allocated size and convert */ 2606 UnicodeEnv.MaximumLength = (USHORT)RegionSize; 2607 Status = RtlAnsiStringToUnicodeString(&UnicodeEnv, &AnsiEnv, FALSE); 2608 if (!NT_SUCCESS(Status)) 2609 { 2610 /* Fail */ 2611 NtFreeVirtualMemory(NtCurrentProcess(), 2612 (PVOID)&UnicodeEnv.Buffer, 2613 &RegionSize, 2614 MEM_RELEASE); 2615 BaseSetLastNTError(Status); 2616 return FALSE; 2617 } 2618 2619 /* Now set the Unicode environment as the environment string pointer */ 2620 lpEnvironment = UnicodeEnv.Buffer; 2621 } 2622 2623 /* Make a copy of the caller's startup info since we'll modify it */ 2624 StartupInfo = *lpStartupInfo; 2625 2626 /* Check if private data is being sent on the same channel as std handles */ 2627 if ((StartupInfo.dwFlags & STARTF_USESTDHANDLES) && 2628 (StartupInfo.dwFlags & (STARTF_USEHOTKEY | STARTF_SHELLPRIVATE))) 2629 { 2630 /* Cannot use the std handles since we have monitor/hotkey values */ 2631 StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES; 2632 } 2633 2634 /* If there's a debugger, or we have to launch cmd.exe, we go back here */ 2635 AppNameRetry: 2636 /* New iteration -- free any existing name buffer */ 2637 if (NameBuffer) 2638 { 2639 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer); 2640 NameBuffer = NULL; 2641 } 2642 2643 /* New iteration -- free any existing free buffer */ 2644 if (FreeBuffer) 2645 { 2646 RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer); 2647 FreeBuffer = NULL; 2648 } 2649 2650 /* New iteration -- close any existing file handle */ 2651 if (FileHandle) 2652 { 2653 NtClose(FileHandle); 2654 FileHandle = NULL; 2655 } 2656 2657 /* Set the initial parsing state. This code can loop -- don't move this! */ 2658 ErrorCode = 0; 2659 SearchRetry = TRUE; 2660 QuotesNeeded = FALSE; 2661 CmdLineIsAppName = FALSE; 2662 2663 /* First check if we don't have an application name */ 2664 if (!lpApplicationName) 2665 { 2666 /* This should be the first time we attempt creating one */ 2667 ASSERT(NameBuffer == NULL); 2668 2669 /* Allocate a buffer to hold it */ 2670 NameBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 2671 0, 2672 MAX_PATH * sizeof(WCHAR)); 2673 if (!NameBuffer) 2674 { 2675 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 2676 Result = FALSE; 2677 goto Quickie; 2678 } 2679 2680 /* Initialize the application name and our parsing parameters */ 2681 lpApplicationName = NullBuffer = ScanString = lpCommandLine; 2682 2683 /* Check for an initial quote*/ 2684 if (*lpCommandLine == L'\"') 2685 { 2686 /* We found a quote, keep searching for another one */ 2687 SearchRetry = FALSE; 2688 ScanString++; 2689 lpApplicationName = ScanString; 2690 while (*ScanString) 2691 { 2692 /* Have we found the terminating quote? */ 2693 if (*ScanString == L'\"') 2694 { 2695 /* We're done, get out of here */ 2696 NullBuffer = ScanString; 2697 HasQuotes = TRUE; 2698 break; 2699 } 2700 2701 /* Keep searching for the quote */ 2702 ScanString++; 2703 NullBuffer = ScanString; 2704 } 2705 } 2706 else 2707 { 2708 StartScan: 2709 /* We simply make the application name be the command line*/ 2710 lpApplicationName = lpCommandLine; 2711 while (*ScanString) 2712 { 2713 /* Check if it starts with a space or tab */ 2714 if ((*ScanString == L' ') || (*ScanString == L'\t')) 2715 { 2716 /* Break out of the search loop */ 2717 NullBuffer = ScanString; 2718 break; 2719 } 2720 2721 /* Keep searching for a space or tab */ 2722 ScanString++; 2723 NullBuffer = ScanString; 2724 } 2725 } 2726 2727 /* We have found the end of the application name, terminate it */ 2728 SaveChar = *NullBuffer; 2729 *NullBuffer = UNICODE_NULL; 2730 2731 /* New iteration -- free any existing saved path */ 2732 if (SearchPath) 2733 { 2734 RtlFreeHeap(RtlGetProcessHeap(), 0, SearchPath); 2735 SearchPath = NULL; 2736 } 2737 2738 /* Now compute the final EXE path based on the name */ 2739 SearchPath = BaseComputeProcessExePath((LPWSTR)lpApplicationName); 2740 DPRINT("Search Path: %S\n", SearchPath); 2741 if (!SearchPath) 2742 { 2743 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 2744 Result = FALSE; 2745 goto Quickie; 2746 } 2747 2748 /* And search for the executable in the search path */ 2749 Length = SearchPathW(SearchPath, 2750 lpApplicationName, 2751 L".exe", 2752 MAX_PATH, 2753 NameBuffer, 2754 NULL); 2755 2756 /* Did we find it? */ 2757 if ((Length) && (Length < MAX_PATH)) 2758 { 2759 /* Get file attributes */ 2760 FileAttribs = GetFileAttributesW(NameBuffer); 2761 if ((FileAttribs != INVALID_FILE_ATTRIBUTES) && 2762 (FileAttribs & FILE_ATTRIBUTE_DIRECTORY)) 2763 { 2764 /* This was a directory, fail later on */ 2765 Length = 0; 2766 } 2767 else 2768 { 2769 /* It's a file! */ 2770 Length++; 2771 } 2772 } 2773 2774 DPRINT("Length: %lu Buffer: %S\n", Length, NameBuffer); 2775 2776 /* Check if there was a failure in SearchPathW */ 2777 if ((Length) && (Length < MAX_PATH)) 2778 { 2779 /* Everything looks good, restore the name */ 2780 *NullBuffer = SaveChar; 2781 lpApplicationName = NameBuffer; 2782 } 2783 else 2784 { 2785 /* Check if this was a relative path, which would explain it */ 2786 PathType = RtlDetermineDosPathNameType_U(lpApplicationName); 2787 if (PathType != RtlPathTypeRelative) 2788 { 2789 /* This should fail, and give us a detailed LastError */ 2790 FileHandle = CreateFileW(lpApplicationName, 2791 GENERIC_READ, 2792 FILE_SHARE_READ | 2793 FILE_SHARE_WRITE, 2794 NULL, 2795 OPEN_EXISTING, 2796 FILE_ATTRIBUTE_NORMAL, 2797 NULL); 2798 if (FileHandle != INVALID_HANDLE_VALUE) 2799 { 2800 /* It worked? Return a generic error */ 2801 CloseHandle(FileHandle); 2802 FileHandle = NULL; 2803 BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND); 2804 } 2805 } 2806 else 2807 { 2808 /* Path was absolute, which means it doesn't exist */ 2809 BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND); 2810 } 2811 2812 /* Did we already fail once? */ 2813 if (ErrorCode) 2814 { 2815 /* Set the error code */ 2816 SetLastError(ErrorCode); 2817 } 2818 else 2819 { 2820 /* Not yet, cache it */ 2821 ErrorCode = GetLastError(); 2822 } 2823 2824 /* Put back the command line */ 2825 *NullBuffer = SaveChar; 2826 lpApplicationName = NameBuffer; 2827 2828 /* It's possible there's whitespace in the directory name */ 2829 if (!(*ScanString) || !(SearchRetry)) 2830 { 2831 /* Not the case, give up completely */ 2832 Result = FALSE; 2833 goto Quickie; 2834 } 2835 2836 /* There are spaces, so keep trying the next possibility */ 2837 ScanString++; 2838 NullBuffer = ScanString; 2839 2840 /* We will have to add a quote, since there is a space */ 2841 QuotesNeeded = TRUE; 2842 HasQuotes = TRUE; 2843 goto StartScan; 2844 } 2845 } 2846 else if (!(lpCommandLine) || !(*lpCommandLine)) 2847 { 2848 /* We don't have a command line, so just use the application name */ 2849 CmdLineIsAppName = TRUE; 2850 lpCommandLine = (LPWSTR)lpApplicationName; 2851 } 2852 2853 /* Convert the application name to its NT path */ 2854 TranslationStatus = RtlDosPathNameToRelativeNtPathName_U(lpApplicationName, 2855 &PathName, 2856 NULL, 2857 &SxsWin32RelativePath); 2858 if (!TranslationStatus) 2859 { 2860 /* Path must be invalid somehow, bail out */ 2861 DPRINT1("Path translation for SxS failed\n"); 2862 SetLastError(ERROR_PATH_NOT_FOUND); 2863 Result = FALSE; 2864 goto Quickie; 2865 } 2866 2867 /* Setup the buffer that needs to be freed at the end */ 2868 ASSERT(FreeBuffer == NULL); 2869 FreeBuffer = PathName.Buffer; 2870 2871 /* Check what kind of path the application is, for SxS (Fusion) purposes */ 2872 RtlInitUnicodeString(&SxsWin32ExePath, lpApplicationName); 2873 SxsPathType = RtlDetermineDosPathNameType_U(lpApplicationName); 2874 if ((SxsPathType != RtlPathTypeDriveAbsolute) && 2875 (SxsPathType != RtlPathTypeLocalDevice) && 2876 (SxsPathType != RtlPathTypeRootLocalDevice) && 2877 (SxsPathType != RtlPathTypeUncAbsolute)) 2878 { 2879 /* Relative-type path, get the full path */ 2880 RtlInitEmptyUnicodeString(&PathBufferString, NULL, 0); 2881 Status = RtlGetFullPathName_UstrEx(&SxsWin32ExePath, 2882 NULL, 2883 &PathBufferString, 2884 NULL, 2885 NULL, 2886 NULL, 2887 &SxsPathType, 2888 NULL); 2889 if (!NT_SUCCESS(Status)) 2890 { 2891 /* Fail the rest of the create */ 2892 RtlReleaseRelativeName(&SxsWin32RelativePath); 2893 BaseSetLastNTError(Status); 2894 Result = FALSE; 2895 goto Quickie; 2896 } 2897 2898 /* Use this full path as the SxS path */ 2899 SxsWin32ExePath = PathBufferString; 2900 PathBuffer = PathBufferString.Buffer; 2901 PathBufferString.Buffer = NULL; 2902 DPRINT("SxS Path: %S\n", PathBuffer); 2903 } 2904 2905 /* Also set the .EXE path based on the path name */ 2906 #if _SXS_SUPPORT_ENABLED_ 2907 SxsNtExePath = PathName; 2908 #endif 2909 if (SxsWin32RelativePath.RelativeName.Length) 2910 { 2911 /* If it's relative, capture the relative name */ 2912 PathName = SxsWin32RelativePath.RelativeName; 2913 } 2914 else 2915 { 2916 /* Otherwise, it's absolute, make sure no relative dir is used */ 2917 SxsWin32RelativePath.ContainingDirectory = NULL; 2918 } 2919 2920 /* Now use the path name, and the root path, to try opening the app */ 2921 DPRINT("Path: %wZ. Dir: %p\n", &PathName, SxsWin32RelativePath.ContainingDirectory); 2922 InitializeObjectAttributes(&LocalObjectAttributes, 2923 &PathName, 2924 OBJ_CASE_INSENSITIVE, 2925 SxsWin32RelativePath.ContainingDirectory, 2926 NULL); 2927 Status = NtOpenFile(&FileHandle, 2928 SYNCHRONIZE | 2929 FILE_READ_ATTRIBUTES | 2930 FILE_READ_DATA | 2931 FILE_EXECUTE, 2932 &LocalObjectAttributes, 2933 &IoStatusBlock, 2934 FILE_SHARE_READ | FILE_SHARE_DELETE, 2935 FILE_SYNCHRONOUS_IO_NONALERT | 2936 FILE_NON_DIRECTORY_FILE); 2937 if (!NT_SUCCESS(Status)) 2938 { 2939 /* Try to open the app just for execute purposes instead */ 2940 Status = NtOpenFile(&FileHandle, 2941 SYNCHRONIZE | FILE_EXECUTE, 2942 &LocalObjectAttributes, 2943 &IoStatusBlock, 2944 FILE_SHARE_READ | FILE_SHARE_DELETE, 2945 FILE_SYNCHRONOUS_IO_NONALERT | 2946 FILE_NON_DIRECTORY_FILE); 2947 } 2948 2949 /* Failure path, display which file failed to open */ 2950 if (!NT_SUCCESS(Status)) 2951 DPRINT1("Open file failed: %lx (%wZ)\n", Status, &PathName); 2952 2953 /* Cleanup in preparation for failure or success */ 2954 RtlReleaseRelativeName(&SxsWin32RelativePath); 2955 2956 if (!NT_SUCCESS(Status)) 2957 { 2958 /* Failure path, try to understand why */ 2959 if (RtlIsDosDeviceName_U(lpApplicationName)) 2960 { 2961 /* If a device is being executed, return this special error code */ 2962 SetLastError(ERROR_BAD_DEVICE); 2963 Result = FALSE; 2964 goto Quickie; 2965 } 2966 else 2967 { 2968 /* Otherwise return the converted NT error code */ 2969 BaseSetLastNTError(Status); 2970 Result = FALSE; 2971 goto Quickie; 2972 } 2973 } 2974 2975 /* Did the caller specify a desktop? */ 2976 if (!StartupInfo.lpDesktop) 2977 { 2978 /* Use the one from the current process */ 2979 StartupInfo.lpDesktop = Peb->ProcessParameters->DesktopInfo.Buffer; 2980 } 2981 2982 /* Create a section for this file */ 2983 Status = NtCreateSection(&SectionHandle, 2984 SECTION_ALL_ACCESS, 2985 NULL, 2986 NULL, 2987 PAGE_EXECUTE, 2988 SEC_IMAGE, 2989 FileHandle); 2990 DPRINT("Section status: %lx\n", Status); 2991 if (NT_SUCCESS(Status)) 2992 { 2993 /* Are we running on Windows Embedded, Datacenter, Blade or Starter? */ 2994 if (SharedUserData->SuiteMask & (VER_SUITE_EMBEDDEDNT | 2995 VER_SUITE_DATACENTER | 2996 VER_SUITE_PERSONAL | 2997 VER_SUITE_BLADE)) 2998 { 2999 /* These SKUs do not allow running certain applications */ 3000 Status = BasepCheckWebBladeHashes(FileHandle); 3001 if (Status == STATUS_ACCESS_DENIED) 3002 { 3003 /* And this is one of them! */ 3004 DPRINT1("Invalid Blade hashes!\n"); 3005 SetLastError(ERROR_ACCESS_DISABLED_WEBBLADE); 3006 Result = FALSE; 3007 goto Quickie; 3008 } 3009 3010 /* Did we get some other failure? */ 3011 if (!NT_SUCCESS(Status)) 3012 { 3013 /* If we couldn't check the hashes, assume nefariousness */ 3014 DPRINT1("Tampered Blade hashes!\n"); 3015 SetLastError(ERROR_ACCESS_DISABLED_WEBBLADE_TAMPER); 3016 Result = FALSE; 3017 goto Quickie; 3018 } 3019 } 3020 3021 /* Now do Winsafer, etc, checks */ 3022 Status = BasepIsProcessAllowed((LPWSTR)lpApplicationName); 3023 if (!NT_SUCCESS(Status)) 3024 { 3025 /* Fail if we're not allowed to launch the process */ 3026 DPRINT1("Process not allowed to launch: %lx\n", Status); 3027 BaseSetLastNTError(Status); 3028 if (SectionHandle) 3029 { 3030 NtClose(SectionHandle); 3031 SectionHandle = NULL; 3032 } 3033 Result = FALSE; 3034 goto Quickie; 3035 } 3036 3037 /* Is a DOS VDM being forced, but we already have a WOW32 instance ready? */ 3038 if ((dwCreationFlags & CREATE_FORCEDOS) && 3039 (BaseStaticServerData->IsWowTaskReady)) 3040 { 3041 /* This request can't be satisfied, instead, a separate VDM is needed */ 3042 dwCreationFlags &= ~(CREATE_FORCEDOS | CREATE_SHARED_WOW_VDM); 3043 dwCreationFlags |= CREATE_SEPARATE_WOW_VDM; 3044 3045 /* Set a failure code, ask for VDM reservation */ 3046 Status = STATUS_INVALID_IMAGE_WIN_16; 3047 UseVdmReserve = TRUE; 3048 3049 /* Close the current handle */ 3050 NtClose(SectionHandle); 3051 SectionHandle = NULL; 3052 3053 /* Don't query the section later */ 3054 QuerySection = FALSE; 3055 } 3056 } 3057 3058 /* Did we already do these checks? */ 3059 if (!SkipSaferAndAppCompat) 3060 { 3061 /* Is everything OK so far, OR do we have an non-MZ, non-DOS app? */ 3062 if ((NT_SUCCESS(Status)) || 3063 ((Status == STATUS_INVALID_IMAGE_NOT_MZ) && 3064 !(BaseIsDosApplication(&PathName, Status)))) 3065 { 3066 /* Clear the machine type in case of failure */ 3067 ImageMachine = 0; 3068 3069 /* Clean any app compat data that may have accumulated */ 3070 BasepFreeAppCompatData(AppCompatData, AppCompatSxsData); 3071 AppCompatData = NULL; 3072 AppCompatSxsData = NULL; 3073 3074 /* Do we have a section? */ 3075 if (SectionHandle) 3076 { 3077 /* Have we already queried it? */ 3078 if (QuerySection) 3079 { 3080 /* Nothing to do */ 3081 AppCompatStatus = STATUS_SUCCESS; 3082 } 3083 else 3084 { 3085 /* Get some information about the executable */ 3086 AppCompatStatus = NtQuerySection(SectionHandle, 3087 SectionImageInformation, 3088 &ImageInformation, 3089 sizeof(ImageInformation), 3090 NULL); 3091 } 3092 3093 /* Do we have section information now? */ 3094 if (NT_SUCCESS(AppCompatStatus)) 3095 { 3096 /* Don't ask for it again, save the machine type */ 3097 QuerySection = TRUE; 3098 ImageMachine = ImageInformation.Machine; 3099 } 3100 } 3101 3102 /* Is there a reason/Shim we shouldn't run this application? */ 3103 AppCompatStatus = BasepCheckBadapp(FileHandle, 3104 FreeBuffer, 3105 lpEnvironment, 3106 ImageMachine, 3107 &AppCompatData, 3108 &AppCompatDataSize, 3109 &AppCompatSxsData, 3110 &AppCompatSxsDataSize, 3111 &FusionFlags); 3112 if (!NT_SUCCESS(AppCompatStatus)) 3113 { 3114 /* This is usually the status we get back */ 3115 DPRINT1("App compat launch failure: %lx\n", AppCompatStatus); 3116 if (AppCompatStatus == STATUS_ACCESS_DENIED) 3117 { 3118 /* Convert it to something more Win32-specific */ 3119 SetLastError(ERROR_CANCELLED); 3120 } 3121 else 3122 { 3123 /* Some other error */ 3124 BaseSetLastNTError(AppCompatStatus); 3125 } 3126 3127 /* Did we have a section? */ 3128 if (SectionHandle) 3129 { 3130 /* Clean it up */ 3131 NtClose(SectionHandle); 3132 SectionHandle = NULL; 3133 } 3134 3135 /* Fail the call */ 3136 Result = FALSE; 3137 goto Quickie; 3138 } 3139 } 3140 } 3141 3142 //ASSERT((dwFusionFlags & ~SXS_APPCOMPACT_FLAG_APP_RUNNING_SAFEMODE) == 0); 3143 3144 /* Have we already done, and do we need to do, SRP (WinSafer) checks? */ 3145 if (!(SkipSaferAndAppCompat) && 3146 ~(dwCreationFlags & CREATE_PRESERVE_CODE_AUTHZ_LEVEL)) 3147 { 3148 /* Assume yes */ 3149 SaferNeeded = TRUE; 3150 switch (Status) 3151 { 3152 case STATUS_INVALID_IMAGE_NE_FORMAT: 3153 case STATUS_INVALID_IMAGE_PROTECT: 3154 case STATUS_INVALID_IMAGE_WIN_16: 3155 case STATUS_FILE_IS_OFFLINE: 3156 /* For all DOS, 16-bit, OS/2 images, we do*/ 3157 break; 3158 3159 case STATUS_INVALID_IMAGE_NOT_MZ: 3160 /* For invalid files, we don't, unless it's a .BAT file */ 3161 if (BaseIsDosApplication(&PathName, Status)) break; 3162 3163 default: 3164 /* Any other error codes we also don't */ 3165 if (!NT_SUCCESS(Status)) 3166 { 3167 SaferNeeded = FALSE; 3168 } 3169 3170 /* But for success, we do */ 3171 break; 3172 } 3173 3174 /* Okay, so what did the checks above result in? */ 3175 if (SaferNeeded) 3176 { 3177 /* We have to call into the WinSafer library and actually check */ 3178 SaferStatus = BasepCheckWinSaferRestrictions(hUserToken, 3179 (LPWSTR)lpApplicationName, 3180 FileHandle, 3181 &InJob, 3182 &TokenHandle, 3183 &JobHandle); 3184 if (SaferStatus == 0xFFFFFFFF) 3185 { 3186 /* Back in 2003, they didn't have an NTSTATUS for this... */ 3187 DPRINT1("WinSafer blocking process launch\n"); 3188 SetLastError(ERROR_ACCESS_DISABLED_BY_POLICY); 3189 Result = FALSE; 3190 goto Quickie; 3191 } 3192 3193 /* Other status codes are not-Safer related, just convert them */ 3194 if (!NT_SUCCESS(SaferStatus)) 3195 { 3196 DPRINT1("Error checking WinSafer: %lx\n", SaferStatus); 3197 BaseSetLastNTError(SaferStatus); 3198 Result = FALSE; 3199 goto Quickie; 3200 } 3201 } 3202 } 3203 3204 /* The last step is to figure out why the section object was not created */ 3205 switch (Status) 3206 { 3207 case STATUS_INVALID_IMAGE_WIN_16: 3208 { 3209 /* 16-bit binary. Should we use WOW or does the caller force VDM? */ 3210 if (!(dwCreationFlags & CREATE_FORCEDOS)) 3211 { 3212 /* Remember that we're launching WOW */ 3213 IsWowApp = TRUE; 3214 3215 /* Create the VDM environment, it's valid for WOW too */ 3216 Result = BaseCreateVDMEnvironment(lpEnvironment, 3217 &VdmAnsiEnv, 3218 &VdmUnicodeEnv); 3219 if (!Result) 3220 { 3221 DPRINT1("VDM environment for WOW app failed\n"); 3222 goto Quickie; 3223 } 3224 3225 /* We're going to try this twice, so do a loop */ 3226 while (TRUE) 3227 { 3228 /* Pick which kind of WOW mode we want to run in */ 3229 VdmBinaryType = (dwCreationFlags & 3230 CREATE_SEPARATE_WOW_VDM) ? 3231 BINARY_TYPE_SEPARATE_WOW : BINARY_TYPE_WOW; 3232 3233 /* Get all the VDM settings and current status */ 3234 Status = BaseCheckVDM(VdmBinaryType, 3235 lpApplicationName, 3236 lpCommandLine, 3237 lpCurrentDirectory, 3238 &VdmAnsiEnv, 3239 &CsrMsg[1], 3240 &VdmTask, 3241 dwCreationFlags, 3242 &StartupInfo, 3243 hUserToken); 3244 3245 /* If it worked, no need to try again */ 3246 if (NT_SUCCESS(Status)) break; 3247 3248 /* Check if it's disallowed or if it's our second time */ 3249 BaseSetLastNTError(Status); 3250 if ((Status == STATUS_VDM_DISALLOWED) || 3251 (VdmBinaryType == BINARY_TYPE_SEPARATE_WOW) || 3252 (GetLastError() == ERROR_ACCESS_DENIED)) 3253 { 3254 /* Fail the call -- we won't try again */ 3255 DPRINT1("VDM message failure for WOW: %lx\n", Status); 3256 Result = FALSE; 3257 goto Quickie; 3258 } 3259 3260 /* Try one more time, but with a separate WOW instance */ 3261 dwCreationFlags |= CREATE_SEPARATE_WOW_VDM; 3262 } 3263 3264 /* Check which VDM state we're currently in */ 3265 switch (CheckVdmMsg->VDMState & (VDM_NOT_LOADED | 3266 VDM_NOT_READY | 3267 VDM_READY)) 3268 { 3269 case VDM_NOT_LOADED: 3270 /* VDM is not fully loaded, so not that much to undo */ 3271 VdmUndoLevel = VDM_UNDO_PARTIAL; 3272 3273 /* Reset VDM reserve if needed */ 3274 if (UseVdmReserve) VdmReserve = 1; 3275 3276 /* Get the required parameters and names for launch */ 3277 Result = BaseGetVdmConfigInfo(lpCommandLine, 3278 VdmTask, 3279 VdmBinaryType, 3280 &VdmString, 3281 &VdmReserve); 3282 if (!Result) 3283 { 3284 DPRINT1("VDM Configuration failed for WOW\n"); 3285 BaseSetLastNTError(Status); 3286 goto Quickie; 3287 } 3288 3289 /* Update the command-line with the VDM one instead */ 3290 lpCommandLine = VdmString.Buffer; 3291 lpApplicationName = NULL; 3292 3293 /* We don't want a console, detachment, nor a window */ 3294 dwCreationFlags |= CREATE_NO_WINDOW; 3295 dwCreationFlags &= ~(CREATE_NEW_CONSOLE | DETACHED_PROCESS); 3296 3297 /* Force feedback on */ 3298 StartupInfo.dwFlags |= STARTF_FORCEONFEEDBACK; 3299 break; 3300 3301 3302 case VDM_READY: 3303 /* VDM is ready, so we have to undo everything */ 3304 VdmUndoLevel = VDM_UNDO_REUSE; 3305 3306 /* Check if CSRSS wants us to wait on VDM */ 3307 VdmWaitObject = CheckVdmMsg->WaitObjectForParent; 3308 break; 3309 3310 case VDM_NOT_READY: 3311 /* Something is wrong with VDM, we'll fail the call */ 3312 DPRINT1("VDM is not ready for WOW\n"); 3313 SetLastError(ERROR_NOT_READY); 3314 Result = FALSE; 3315 goto Quickie; 3316 3317 default: 3318 break; 3319 } 3320 3321 /* Since to get NULL, we allocate from 0x1, account for this */ 3322 VdmReserve--; 3323 3324 /* This implies VDM is ready, so skip everything else */ 3325 if (VdmWaitObject) goto VdmShortCircuit; 3326 3327 /* Don't inherit handles since we're doing VDM now */ 3328 bInheritHandles = FALSE; 3329 3330 /* Had the user passed in environment? If so, destroy it */ 3331 if ((lpEnvironment) && 3332 !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT)) 3333 { 3334 RtlDestroyEnvironment(lpEnvironment); 3335 } 3336 3337 /* We've already done all these checks, don't do them again */ 3338 SkipSaferAndAppCompat = TRUE; 3339 goto AppNameRetry; 3340 } 3341 3342 // There is no break here on purpose, so FORCEDOS drops down! 3343 } 3344 3345 case STATUS_INVALID_IMAGE_PROTECT: 3346 case STATUS_INVALID_IMAGE_NOT_MZ: 3347 case STATUS_INVALID_IMAGE_NE_FORMAT: 3348 { 3349 /* We're launching an executable application */ 3350 BinarySubType = BINARY_TYPE_EXE; 3351 3352 /* We can drop here from other "cases" above too, so check */ 3353 if ((Status == STATUS_INVALID_IMAGE_PROTECT) || 3354 (Status == STATUS_INVALID_IMAGE_NE_FORMAT) || 3355 (BinarySubType = BaseIsDosApplication(&PathName, Status))) 3356 { 3357 /* We're launching a DOS application */ 3358 VdmBinaryType = BINARY_TYPE_DOS; 3359 3360 /* Based on the caller environment, create a VDM one */ 3361 Result = BaseCreateVDMEnvironment(lpEnvironment, 3362 &VdmAnsiEnv, 3363 &VdmUnicodeEnv); 3364 if (!Result) 3365 { 3366 DPRINT1("VDM environment for DOS failed\n"); 3367 goto Quickie; 3368 } 3369 3370 /* Check the current state of the VDM subsystem */ 3371 Status = BaseCheckVDM(VdmBinaryType | BinarySubType, 3372 lpApplicationName, 3373 lpCommandLine, 3374 lpCurrentDirectory, 3375 &VdmAnsiEnv, 3376 &CsrMsg[1], 3377 &VdmTask, 3378 dwCreationFlags, 3379 &StartupInfo, 3380 NULL); 3381 if (!NT_SUCCESS(Status)) 3382 { 3383 /* Failed to inquire about VDM, fail the call */ 3384 DPRINT1("VDM message failure for DOS: %lx\n", Status); 3385 BaseSetLastNTError(Status); 3386 Result = FALSE; 3387 goto Quickie; 3388 }; 3389 3390 /* Handle possible VDM states */ 3391 switch (CheckVdmMsg->VDMState & (VDM_NOT_LOADED | 3392 VDM_NOT_READY | 3393 VDM_READY)) 3394 { 3395 case VDM_NOT_LOADED: 3396 /* If VDM is not loaded, we'll do a partial undo */ 3397 VdmUndoLevel = VDM_UNDO_PARTIAL; 3398 3399 /* A VDM process can't also be detached, so fail */ 3400 if (dwCreationFlags & DETACHED_PROCESS) 3401 { 3402 DPRINT1("Detached process but no VDM, not allowed\n"); 3403 SetLastError(ERROR_ACCESS_DENIED); 3404 return FALSE; 3405 } 3406 3407 /* Get the required parameters and names for launch */ 3408 Result = BaseGetVdmConfigInfo(lpCommandLine, 3409 VdmTask, 3410 VdmBinaryType, 3411 &VdmString, 3412 &VdmReserve); 3413 if (!Result) 3414 { 3415 DPRINT1("VDM Configuration failed for DOS\n"); 3416 BaseSetLastNTError(Status); 3417 goto Quickie; 3418 } 3419 3420 /* Update the command-line to launch VDM instead */ 3421 lpCommandLine = VdmString.Buffer; 3422 lpApplicationName = NULL; 3423 break; 3424 3425 case VDM_READY: 3426 /* VDM is ready, so we have to undo everything */ 3427 VdmUndoLevel = VDM_UNDO_REUSE; 3428 3429 /* Check if CSRSS wants us to wait on VDM */ 3430 VdmWaitObject = CheckVdmMsg->WaitObjectForParent; 3431 break; 3432 3433 case VDM_NOT_READY: 3434 /* Something is wrong with VDM, we'll fail the call */ 3435 DPRINT1("VDM is not ready for DOS\n"); 3436 SetLastError(ERROR_NOT_READY); 3437 Result = FALSE; 3438 goto Quickie; 3439 3440 default: 3441 break; 3442 } 3443 3444 /* Since to get NULL, we allocate from 0x1, account for this */ 3445 VdmReserve--; 3446 3447 /* This implies VDM is ready, so skip everything else */ 3448 if (VdmWaitObject) goto VdmShortCircuit; 3449 3450 /* Don't inherit handles since we're doing VDM now */ 3451 bInheritHandles = FALSE; 3452 3453 /* Had the user passed in environment? If so, destroy it */ 3454 if ((lpEnvironment) && 3455 !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT)) 3456 { 3457 RtlDestroyEnvironment(lpEnvironment); 3458 } 3459 3460 /* Use our VDM Unicode environment instead */ 3461 lpEnvironment = VdmUnicodeEnv.Buffer; 3462 } 3463 else 3464 { 3465 /* It's a batch file, get the extension */ 3466 ExtBuffer = &PathName.Buffer[PathName.Length / sizeof(WCHAR) - 4]; 3467 3468 /* Make sure the extensions are correct */ 3469 if ((PathName.Length < (4 * sizeof(WCHAR))) || 3470 ((_wcsnicmp(ExtBuffer, L".bat", 4)) && 3471 (_wcsnicmp(ExtBuffer, L".cmd", 4)))) 3472 { 3473 DPRINT1("'%wZ': Invalid EXE, and not a batch or script file\n", &PathName); 3474 SetLastError(ERROR_BAD_EXE_FORMAT); 3475 Result = FALSE; 3476 goto Quickie; 3477 } 3478 3479 /* Check if we need to account for quotes around the path */ 3480 CmdQuoteLength = CmdLineIsAppName || HasQuotes; 3481 if (!CmdLineIsAppName) 3482 { 3483 if (HasQuotes) CmdQuoteLength++; 3484 } 3485 else 3486 { 3487 CmdQuoteLength++; 3488 } 3489 3490 /* Calculate the length of the command line */ 3491 CmdLineLength = wcslen(lpCommandLine); 3492 CmdLineLength += wcslen(CMD_STRING); 3493 CmdLineLength += CmdQuoteLength + sizeof(ANSI_NULL); 3494 CmdLineLength *= sizeof(WCHAR); 3495 3496 /* Allocate space for the new command line */ 3497 AnsiCmdCommand = RtlAllocateHeap(RtlGetProcessHeap(), 3498 0, 3499 CmdLineLength); 3500 if (!AnsiCmdCommand) 3501 { 3502 BaseSetLastNTError(STATUS_NO_MEMORY); 3503 Result = FALSE; 3504 goto Quickie; 3505 } 3506 3507 /* Build it */ 3508 wcscpy(AnsiCmdCommand, CMD_STRING); 3509 if ((CmdLineIsAppName) || (HasQuotes)) 3510 { 3511 wcscat(AnsiCmdCommand, L"\""); 3512 } 3513 wcscat(AnsiCmdCommand, lpCommandLine); 3514 if ((CmdLineIsAppName) || (HasQuotes)) 3515 { 3516 wcscat(AnsiCmdCommand, L"\""); 3517 } 3518 3519 /* Create it as a Unicode String */ 3520 RtlInitUnicodeString(&DebuggerString, AnsiCmdCommand); 3521 3522 /* Set the command line to this */ 3523 lpCommandLine = DebuggerString.Buffer; 3524 lpApplicationName = NULL; 3525 DPRINT1("Retrying with: %S\n", lpCommandLine); 3526 } 3527 3528 /* We've already done all these checks, don't do them again */ 3529 SkipSaferAndAppCompat = TRUE; 3530 goto AppNameRetry; 3531 } 3532 3533 case STATUS_INVALID_IMAGE_WIN_64: 3534 { 3535 /* 64-bit binaries are not allowed to run on 32-bit ReactOS */ 3536 DPRINT1("64-bit binary, failing\n"); 3537 SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH); 3538 Result = FALSE; 3539 goto Quickie; 3540 } 3541 3542 case STATUS_FILE_IS_OFFLINE: 3543 { 3544 /* Set the correct last error for this */ 3545 DPRINT1("File is offline, failing\n"); 3546 SetLastError(ERROR_FILE_OFFLINE); 3547 break; 3548 } 3549 3550 default: 3551 { 3552 /* Any other error, convert it to a generic Win32 error */ 3553 if (!NT_SUCCESS(Status)) 3554 { 3555 DPRINT1("Failed to create section: %lx\n", Status); 3556 SetLastError(ERROR_BAD_EXE_FORMAT); 3557 Result = FALSE; 3558 goto Quickie; 3559 } 3560 3561 /* Otherwise, this must be success */ 3562 ASSERT(Status == STATUS_SUCCESS); 3563 break; 3564 } 3565 } 3566 3567 /* Is this not a WOW application, but a WOW32 VDM was requested for it? */ 3568 if (!(IsWowApp) && (dwCreationFlags & CREATE_SEPARATE_WOW_VDM)) 3569 { 3570 /* Ignore the nonsensical request */ 3571 dwCreationFlags &= ~CREATE_SEPARATE_WOW_VDM; 3572 } 3573 3574 /* Did we already check information for the section? */ 3575 if (!QuerySection) 3576 { 3577 /* Get some information about the executable */ 3578 Status = NtQuerySection(SectionHandle, 3579 SectionImageInformation, 3580 &ImageInformation, 3581 sizeof(ImageInformation), 3582 NULL); 3583 if (!NT_SUCCESS(Status)) 3584 { 3585 /* We failed, bail out */ 3586 DPRINT1("Section query failed\n"); 3587 BaseSetLastNTError(Status); 3588 Result = FALSE; 3589 goto Quickie; 3590 } 3591 3592 /* Don't check this later */ 3593 QuerySection = TRUE; 3594 } 3595 3596 /* Check if this was linked as a DLL */ 3597 if (ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL) 3598 { 3599 /* These aren't valid images to try to execute! */ 3600 DPRINT1("Trying to launch a DLL, failing\n"); 3601 SetLastError(ERROR_BAD_EXE_FORMAT); 3602 Result = FALSE; 3603 goto Quickie; 3604 } 3605 3606 /* Don't let callers pass in this flag -- we'll only get it from IFEO */ 3607 Flags &= ~PROCESS_CREATE_FLAGS_LARGE_PAGES; 3608 3609 /* Clear the IFEO-missing flag, before we know for sure... */ 3610 ParameterFlags &= ~2; 3611 3612 /* If the process is being debugged, only read IFEO if the PEB says so */ 3613 if (!(dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) || 3614 (NtCurrentPeb()->ReadImageFileExecOptions)) 3615 { 3616 /* Let's do this! Attempt to open IFEO */ 3617 IFEOStatus = LdrOpenImageFileOptionsKey(&PathName, 0, &KeyHandle); 3618 if (!NT_SUCCESS(IFEOStatus)) 3619 { 3620 /* We failed, set the flag so we store this in the parameters */ 3621 if (IFEOStatus == STATUS_OBJECT_NAME_NOT_FOUND) ParameterFlags |= 2; 3622 } 3623 else 3624 { 3625 /* Was this our first time going through this path? */ 3626 if (!DebuggerCmdLine) 3627 { 3628 /* Allocate a buffer for the debugger path */ 3629 DebuggerCmdLine = RtlAllocateHeap(RtlGetProcessHeap(), 3630 0, 3631 MAX_PATH * sizeof(WCHAR)); 3632 if (!DebuggerCmdLine) 3633 { 3634 /* Close IFEO on failure */ 3635 IFEOStatus = NtClose(KeyHandle); 3636 ASSERT(NT_SUCCESS(IFEOStatus)); 3637 3638 /* Fail the call */ 3639 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 3640 Result = FALSE; 3641 goto Quickie; 3642 } 3643 } 3644 3645 /* Now query for the debugger */ 3646 IFEOStatus = LdrQueryImageFileKeyOption(KeyHandle, 3647 L"Debugger", 3648 REG_SZ, 3649 DebuggerCmdLine, 3650 MAX_PATH * sizeof(WCHAR), 3651 &ResultSize); 3652 if (!(NT_SUCCESS(IFEOStatus)) || 3653 (ResultSize < sizeof(WCHAR)) || 3654 (DebuggerCmdLine[0] == UNICODE_NULL)) 3655 { 3656 /* If it's not there, or too small, or invalid, ignore it */ 3657 RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine); 3658 DebuggerCmdLine = NULL; 3659 } 3660 3661 /* Also query if we should map with large pages */ 3662 IFEOStatus = LdrQueryImageFileKeyOption(KeyHandle, 3663 L"UseLargePages", 3664 REG_DWORD, 3665 &UseLargePages, 3666 sizeof(UseLargePages), 3667 NULL); 3668 if ((NT_SUCCESS(IFEOStatus)) && (UseLargePages)) 3669 { 3670 /* Do it! This is the only way this flag can be set */ 3671 Flags |= PROCESS_CREATE_FLAGS_LARGE_PAGES; 3672 } 3673 3674 /* We're done with IFEO, can close it now */ 3675 IFEOStatus = NtClose(KeyHandle); 3676 ASSERT(NT_SUCCESS(IFEOStatus)); 3677 } 3678 } 3679 3680 /* Make sure the image was compiled for this processor */ 3681 if ((ImageInformation.Machine < SharedUserData->ImageNumberLow) || 3682 (ImageInformation.Machine > SharedUserData->ImageNumberHigh)) 3683 { 3684 /* It was not -- raise a hard error */ 3685 ErrorResponse = ResponseOk; 3686 ErrorParameters[0] = (ULONG_PTR)&PathName; 3687 NtRaiseHardError(STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE, 3688 1, 3689 1, 3690 ErrorParameters, 3691 OptionOk, 3692 &ErrorResponse); 3693 if (Peb->ImageSubsystemMajorVersion <= 3) 3694 { 3695 /* If it's really old, return this error */ 3696 SetLastError(ERROR_BAD_EXE_FORMAT); 3697 } 3698 else 3699 { 3700 /* Otherwise, return a more modern error */ 3701 SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH); 3702 } 3703 3704 /* Go to the failure path */ 3705 DPRINT1("Invalid image architecture: %lx\n", ImageInformation.Machine); 3706 Result = FALSE; 3707 goto Quickie; 3708 } 3709 3710 /* Check if this isn't a Windows image */ 3711 if ((ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_GUI) && 3712 (ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_CUI)) 3713 { 3714 /* Get rid of section-related information since we'll retry */ 3715 NtClose(SectionHandle); 3716 SectionHandle = NULL; 3717 QuerySection = FALSE; 3718 3719 /* The only other non-Windows image type we support here is POSIX */ 3720 if (ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_POSIX_CUI) 3721 { 3722 /* Bail out if it's something else */ 3723 SetLastError(ERROR_CHILD_NOT_COMPLETE); 3724 Result = FALSE; 3725 goto Quickie; 3726 } 3727 3728 /* Now build the command-line to have posix launch this image */ 3729 Result = BuildSubSysCommandLine(L"POSIX /P ", 3730 lpApplicationName, 3731 lpCommandLine, 3732 &DebuggerString); 3733 if (!Result) 3734 { 3735 /* Bail out if that failed */ 3736 DPRINT1("Subsystem command line failed\n"); 3737 goto Quickie; 3738 } 3739 3740 /* And re-try launching the process, with the new command-line now */ 3741 lpCommandLine = DebuggerString.Buffer; 3742 lpApplicationName = NULL; 3743 3744 /* We've already done all these checks, don't do them again */ 3745 SkipSaferAndAppCompat = TRUE; 3746 DPRINT1("Retrying with: %S\n", lpCommandLine); 3747 goto AppNameRetry; 3748 } 3749 3750 /* Was this image built for a version of Windows whose images we can run? */ 3751 Result = BasepIsImageVersionOk(ImageInformation.SubSystemMajorVersion, 3752 ImageInformation.SubSystemMinorVersion); 3753 if (!Result) 3754 { 3755 /* It was not, bail out */ 3756 DPRINT1("Invalid subsystem version: %hu.%hu\n", 3757 ImageInformation.SubSystemMajorVersion, 3758 ImageInformation.SubSystemMinorVersion); 3759 SetLastError(ERROR_BAD_EXE_FORMAT); 3760 goto Quickie; 3761 } 3762 3763 /* Check if there is a debugger associated with the application */ 3764 if (DebuggerCmdLine) 3765 { 3766 /* Get the length of the command line */ 3767 n = wcslen(lpCommandLine); 3768 if (!n) 3769 { 3770 /* There's no command line, use the application name instead */ 3771 lpCommandLine = (LPWSTR)lpApplicationName; 3772 n = wcslen(lpCommandLine); 3773 } 3774 3775 /* Protect against overflow */ 3776 if (n > UNICODE_STRING_MAX_CHARS) 3777 { 3778 BaseSetLastNTError(STATUS_NAME_TOO_LONG); 3779 Result = FALSE; 3780 goto Quickie; 3781 } 3782 3783 /* Now add the length of the debugger command-line */ 3784 n += wcslen(DebuggerCmdLine); 3785 3786 /* Again make sure we don't overflow */ 3787 if (n > UNICODE_STRING_MAX_CHARS) 3788 { 3789 BaseSetLastNTError(STATUS_NAME_TOO_LONG); 3790 Result = FALSE; 3791 goto Quickie; 3792 } 3793 3794 /* Account for the quotes and space between the two */ 3795 n += sizeof("\" \"") - sizeof(ANSI_NULL); 3796 3797 /* Convert to bytes, and make sure we don't overflow */ 3798 n *= sizeof(WCHAR); 3799 if (n > UNICODE_STRING_MAX_BYTES) 3800 { 3801 BaseSetLastNTError(STATUS_NAME_TOO_LONG); 3802 Result = FALSE; 3803 goto Quickie; 3804 } 3805 3806 /* Allocate space for the string */ 3807 DebuggerString.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, n); 3808 if (!DebuggerString.Buffer) 3809 { 3810 SetLastError(ERROR_NOT_ENOUGH_MEMORY); 3811 Result = FALSE; 3812 goto Quickie; 3813 } 3814 3815 /* Set the length */ 3816 RtlInitEmptyUnicodeString(&DebuggerString, 3817 DebuggerString.Buffer, 3818 (USHORT)n); 3819 3820 /* Now perform the command line creation */ 3821 ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString, 3822 DebuggerCmdLine); 3823 ASSERT(NT_SUCCESS(ImageDbgStatus)); 3824 ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString, L" "); 3825 ASSERT(NT_SUCCESS(ImageDbgStatus)); 3826 ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString, lpCommandLine); 3827 ASSERT(NT_SUCCESS(ImageDbgStatus)); 3828 3829 /* Make sure it all looks nice */ 3830 DbgPrint("BASE: Calling debugger with '%wZ'\n", &DebuggerString); 3831 3832 /* Update the command line and application name */ 3833 lpCommandLine = DebuggerString.Buffer; 3834 lpApplicationName = NULL; 3835 3836 /* Close all temporary state */ 3837 NtClose(SectionHandle); 3838 SectionHandle = NULL; 3839 QuerySection = FALSE; 3840 3841 /* Free all temporary memory */ 3842 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer); 3843 NameBuffer = NULL; 3844 RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer); 3845 FreeBuffer = NULL; 3846 RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine); 3847 DebuggerCmdLine = NULL; 3848 DPRINT1("Retrying with: %S\n", lpCommandLine); 3849 goto AppNameRetry; 3850 } 3851 3852 /* Initialize the process object attributes */ 3853 ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes, 3854 lpProcessAttributes, 3855 NULL); 3856 if ((hUserToken) && (lpProcessAttributes)) 3857 { 3858 /* Augment them with information from the user */ 3859 3860 LocalProcessAttributes = *lpProcessAttributes; 3861 LocalProcessAttributes.lpSecurityDescriptor = NULL; 3862 ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes, 3863 &LocalProcessAttributes, 3864 NULL); 3865 } 3866 3867 /* Check if we're going to be debugged */ 3868 if (dwCreationFlags & DEBUG_PROCESS) 3869 { 3870 /* Set process flag */ 3871 Flags |= PROCESS_CREATE_FLAGS_BREAKAWAY; 3872 } 3873 3874 /* Check if we're going to be debugged */ 3875 if (dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) 3876 { 3877 /* Connect to DbgUi */ 3878 Status = DbgUiConnectToDbg(); 3879 if (!NT_SUCCESS(Status)) 3880 { 3881 DPRINT1("Failed to connect to DbgUI!\n"); 3882 BaseSetLastNTError(Status); 3883 Result = FALSE; 3884 goto Quickie; 3885 } 3886 3887 /* Get the debug object */ 3888 DebugHandle = DbgUiGetThreadDebugObject(); 3889 3890 /* Check if only this process will be debugged */ 3891 if (dwCreationFlags & DEBUG_ONLY_THIS_PROCESS) 3892 { 3893 /* Set process flag */ 3894 Flags |= PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT; 3895 } 3896 } 3897 3898 /* Set inherit flag */ 3899 if (bInheritHandles) Flags |= PROCESS_CREATE_FLAGS_INHERIT_HANDLES; 3900 3901 /* Check if the process should be created with large pages */ 3902 HavePrivilege = FALSE; 3903 PrivilegeState = NULL; 3904 if (Flags & PROCESS_CREATE_FLAGS_LARGE_PAGES) 3905 { 3906 /* Acquire the required privilege so that the kernel won't fail the call */ 3907 PrivilegeValue = SE_LOCK_MEMORY_PRIVILEGE; 3908 Status = RtlAcquirePrivilege(&PrivilegeValue, 1, 0, &PrivilegeState); 3909 if (NT_SUCCESS(Status)) 3910 { 3911 /* Remember to release it later */ 3912 HavePrivilege = TRUE; 3913 } 3914 } 3915 3916 /* Save the current TIB value since kernel overwrites it to store PEB */ 3917 TibValue = Teb->NtTib.ArbitraryUserPointer; 3918 3919 /* Tell the kernel to create the process */ 3920 Status = NtCreateProcessEx(&ProcessHandle, 3921 PROCESS_ALL_ACCESS, 3922 ObjectAttributes, 3923 NtCurrentProcess(), 3924 Flags, 3925 SectionHandle, 3926 DebugHandle, 3927 NULL, 3928 InJob); 3929 3930 /* Load the PEB address from the hacky location where the kernel stores it */ 3931 RemotePeb = Teb->NtTib.ArbitraryUserPointer; 3932 3933 /* And restore the old TIB value */ 3934 Teb->NtTib.ArbitraryUserPointer = TibValue; 3935 3936 /* Release the large page privilege if we had acquired it */ 3937 if (HavePrivilege) RtlReleasePrivilege(PrivilegeState); 3938 3939 /* And now check if the kernel failed to create the process */ 3940 if (!NT_SUCCESS(Status)) 3941 { 3942 /* Go to failure path */ 3943 DPRINT1("Failed to create process: %lx\n", Status); 3944 BaseSetLastNTError(Status); 3945 Result = FALSE; 3946 goto Quickie; 3947 } 3948 3949 /* Check if there is a priority class to set */ 3950 if (PriorityClass.PriorityClass) 3951 { 3952 /* Reset current privilege state */ 3953 RealTimePrivilegeState = NULL; 3954 3955 /* Is realtime priority being requested? */ 3956 if (PriorityClass.PriorityClass == PROCESS_PRIORITY_CLASS_REALTIME) 3957 { 3958 /* Check if the caller has real-time access, and enable it if so */ 3959 RealTimePrivilegeState = BasepIsRealtimeAllowed(TRUE); 3960 } 3961 3962 /* Set the new priority class and release the privilege */ 3963 Status = NtSetInformationProcess(ProcessHandle, 3964 ProcessPriorityClass, 3965 &PriorityClass, 3966 sizeof(PROCESS_PRIORITY_CLASS)); 3967 if (RealTimePrivilegeState) RtlReleasePrivilege(RealTimePrivilegeState); 3968 3969 /* Check if we failed to set the priority class */ 3970 if (!NT_SUCCESS(Status)) 3971 { 3972 /* Bail out on failure */ 3973 DPRINT1("Failed to set priority class: %lx\n", Status); 3974 BaseSetLastNTError(Status); 3975 Result = FALSE; 3976 goto Quickie; 3977 } 3978 } 3979 3980 /* Check if the caller wants the default error mode */ 3981 if (dwCreationFlags & CREATE_DEFAULT_ERROR_MODE) 3982 { 3983 /* Set Error Mode to only fail on critical errors */ 3984 HardErrorMode = SEM_FAILCRITICALERRORS; 3985 NtSetInformationProcess(ProcessHandle, 3986 ProcessDefaultHardErrorMode, 3987 &HardErrorMode, 3988 sizeof(ULONG)); 3989 } 3990 3991 /* Check if this was a VDM binary */ 3992 if (VdmBinaryType) 3993 { 3994 /* Update VDM by telling it the process has now been created */ 3995 VdmWaitObject = ProcessHandle; 3996 Result = BaseUpdateVDMEntry(VdmEntryUpdateProcess, 3997 &VdmWaitObject, 3998 VdmTask, 3999 VdmBinaryType); 4000 4001 if (!Result) 4002 { 4003 /* Bail out on failure */ 4004 DPRINT1("Failed to update VDM with wait object\n"); 4005 VdmWaitObject = NULL; 4006 goto Quickie; 4007 } 4008 4009 /* At this point, a failure means VDM has to undo all the state */ 4010 VdmUndoLevel |= VDM_UNDO_FULL; 4011 } 4012 4013 /* Check if VDM needed reserved low-memory */ 4014 if (VdmReserve) 4015 { 4016 /* Reserve the requested allocation */ 4017 RegionSize = VdmReserve; 4018 Status = NtAllocateVirtualMemory(ProcessHandle, 4019 &BaseAddress, 4020 0, 4021 &RegionSize, 4022 MEM_RESERVE, 4023 PAGE_EXECUTE_READWRITE); 4024 if (!NT_SUCCESS(Status)) 4025 { 4026 /* Bail out on failure */ 4027 DPRINT1("Failed to reserve memory for VDM: %lx\n", Status); 4028 BaseSetLastNTError(Status); 4029 Result = FALSE; 4030 goto Quickie; 4031 } 4032 4033 VdmReserve = (ULONG)RegionSize; 4034 } 4035 4036 /* Check if we've already queried information on the section */ 4037 if (!QuerySection) 4038 { 4039 /* We haven't, so get some information about the executable */ 4040 Status = NtQuerySection(SectionHandle, 4041 SectionImageInformation, 4042 &ImageInformation, 4043 sizeof(ImageInformation), 4044 NULL); 4045 if (!NT_SUCCESS(Status)) 4046 { 4047 /* Bail out on failure */ 4048 DPRINT1("Failed to query section: %lx\n", Status); 4049 BaseSetLastNTError(Status); 4050 Result = FALSE; 4051 goto Quickie; 4052 } 4053 4054 /* If we encounter a restart, don't re-query this information again */ 4055 QuerySection = TRUE; 4056 } 4057 4058 /* Do we need to apply SxS to this image? */ 4059 if (!(ImageInformation.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NO_ISOLATION)) 4060 { 4061 /* Too bad, we don't support this yet */ 4062 DPRINT1("Image should receive SxS Fusion Isolation\n"); 4063 } 4064 4065 /* There's some SxS flag that we need to set if fusion flags have 1 set */ 4066 if (FusionFlags & 1) CreateProcessMsg->Sxs.Flags |= 0x10; 4067 4068 /* Check if we have a current directory */ 4069 if (lpCurrentDirectory) 4070 { 4071 /* Allocate a buffer so we can keep a Unicode copy */ 4072 DPRINT("Current directory: %S\n", lpCurrentDirectory); 4073 CurrentDirectory = RtlAllocateHeap(RtlGetProcessHeap(), 4074 0, 4075 (MAX_PATH * sizeof(WCHAR)) + 4076 sizeof(UNICODE_NULL)); 4077 if (!CurrentDirectory) 4078 { 4079 /* Bail out if this failed */ 4080 BaseSetLastNTError(STATUS_NO_MEMORY); 4081 Result = FALSE; 4082 goto Quickie; 4083 } 4084 4085 /* Get the length in Unicode */ 4086 Length = GetFullPathNameW(lpCurrentDirectory, 4087 MAX_PATH, 4088 CurrentDirectory, 4089 &FilePart); 4090 if (Length > MAX_PATH) 4091 { 4092 /* The directory is too long, so bail out */ 4093 SetLastError(ERROR_DIRECTORY); 4094 Result = FALSE; 4095 goto Quickie; 4096 } 4097 4098 /* Make sure the directory is actually valid */ 4099 FileAttribs = GetFileAttributesW(CurrentDirectory); 4100 if ((FileAttribs == INVALID_FILE_ATTRIBUTES) || 4101 !(FileAttribs & FILE_ATTRIBUTE_DIRECTORY)) 4102 { 4103 /* It isn't, so bail out */ 4104 DPRINT1("Current directory is invalid\n"); 4105 SetLastError(ERROR_DIRECTORY); 4106 Result = FALSE; 4107 goto Quickie; 4108 } 4109 } 4110 4111 /* Insert quotes if needed */ 4112 if ((QuotesNeeded) || (CmdLineIsAppName)) 4113 { 4114 /* Allocate our buffer, plus enough space for quotes and a NULL */ 4115 QuotedCmdLine = RtlAllocateHeap(RtlGetProcessHeap(), 4116 0, 4117 (wcslen(lpCommandLine) * sizeof(WCHAR)) + 4118 (2 * sizeof(L'\"') + sizeof(UNICODE_NULL))); 4119 if (QuotedCmdLine) 4120 { 4121 /* Copy the first quote */ 4122 wcscpy(QuotedCmdLine, L"\""); 4123 4124 /* Save the current null-character */ 4125 if (QuotesNeeded) 4126 { 4127 SaveChar = *NullBuffer; 4128 *NullBuffer = UNICODE_NULL; 4129 } 4130 4131 /* Copy the command line and the final quote */ 4132 wcscat(QuotedCmdLine, lpCommandLine); 4133 wcscat(QuotedCmdLine, L"\""); 4134 4135 /* Copy the null-char back */ 4136 if (QuotesNeeded) 4137 { 4138 *NullBuffer = SaveChar; 4139 wcscat(QuotedCmdLine, NullBuffer); 4140 } 4141 } 4142 else 4143 { 4144 /* We can't put quotes around the thing, so try it anyway */ 4145 if (QuotesNeeded) QuotesNeeded = FALSE; 4146 if (CmdLineIsAppName) CmdLineIsAppName = FALSE; 4147 } 4148 } 4149 4150 /* Use isolation if needed */ 4151 if (CreateProcessMsg->Sxs.Flags & 1) ParameterFlags |= 1; 4152 4153 /* Set the new command-line if needed */ 4154 if ((QuotesNeeded) || (CmdLineIsAppName)) lpCommandLine = QuotedCmdLine; 4155 4156 /* Call the helper function in charge of RTL_USER_PROCESS_PARAMETERS */ 4157 Result = BasePushProcessParameters(ParameterFlags, 4158 ProcessHandle, 4159 RemotePeb, 4160 lpApplicationName, 4161 CurrentDirectory, 4162 lpCommandLine, 4163 lpEnvironment, 4164 &StartupInfo, 4165 dwCreationFlags | NoWindow, 4166 bInheritHandles, 4167 IsWowApp ? IMAGE_SUBSYSTEM_WINDOWS_GUI: 0, 4168 AppCompatData, 4169 AppCompatDataSize); 4170 if (!Result) 4171 { 4172 /* The remote process would have an undefined state, so fail the call */ 4173 DPRINT1("BasePushProcessParameters failed\n"); 4174 goto Quickie; 4175 } 4176 4177 /* Free the VDM command line string as it's no longer needed */ 4178 RtlFreeUnicodeString(&VdmString); 4179 VdmString.Buffer = NULL; 4180 4181 /* Non-VDM console applications usually inherit handles unless specified */ 4182 if (!(VdmBinaryType) && 4183 !(bInheritHandles) && 4184 !(StartupInfo.dwFlags & STARTF_USESTDHANDLES) && 4185 !(dwCreationFlags & (CREATE_NO_WINDOW | 4186 CREATE_NEW_CONSOLE | 4187 DETACHED_PROCESS)) && 4188 (ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_CUI)) 4189 { 4190 /* Get the remote parameters */ 4191 Status = NtReadVirtualMemory(ProcessHandle, 4192 &RemotePeb->ProcessParameters, 4193 &ProcessParameters, 4194 sizeof(PRTL_USER_PROCESS_PARAMETERS), 4195 NULL); 4196 if (NT_SUCCESS(Status)) 4197 { 4198 /* Duplicate standard input unless it's a console handle */ 4199 if (!IsConsoleHandle(Peb->ProcessParameters->StandardInput)) 4200 { 4201 StuffStdHandle(ProcessHandle, 4202 Peb->ProcessParameters->StandardInput, 4203 &ProcessParameters->StandardInput); 4204 } 4205 4206 /* Duplicate standard output unless it's a console handle */ 4207 if (!IsConsoleHandle(Peb->ProcessParameters->StandardOutput)) 4208 { 4209 StuffStdHandle(ProcessHandle, 4210 Peb->ProcessParameters->StandardOutput, 4211 &ProcessParameters->StandardOutput); 4212 } 4213 4214 /* Duplicate standard error unless it's a console handle */ 4215 if (!IsConsoleHandle(Peb->ProcessParameters->StandardError)) 4216 { 4217 StuffStdHandle(ProcessHandle, 4218 Peb->ProcessParameters->StandardError, 4219 &ProcessParameters->StandardError); 4220 } 4221 } 4222 } 4223 4224 /* Create the Thread's Stack */ 4225 StackSize = max(256 * 1024, ImageInformation.MaximumStackSize); 4226 Status = BaseCreateStack(ProcessHandle, 4227 ImageInformation.CommittedStackSize, 4228 StackSize, 4229 &InitialTeb); 4230 if (!NT_SUCCESS(Status)) 4231 { 4232 DPRINT1("Creating the thread stack failed: %lx\n", Status); 4233 BaseSetLastNTError(Status); 4234 Result = FALSE; 4235 goto Quickie; 4236 } 4237 4238 /* Create the Thread's Context */ 4239 BaseInitializeContext(&Context, 4240 Peb, 4241 ImageInformation.TransferAddress, 4242 InitialTeb.StackBase, 4243 0); 4244 4245 /* Convert the thread attributes */ 4246 ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes, 4247 lpThreadAttributes, 4248 NULL); 4249 if ((hUserToken) && (lpThreadAttributes)) 4250 { 4251 /* If the caller specified a user token, zero the security descriptor */ 4252 LocalThreadAttributes = *lpThreadAttributes; 4253 LocalThreadAttributes.lpSecurityDescriptor = NULL; 4254 ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes, 4255 &LocalThreadAttributes, 4256 NULL); 4257 } 4258 4259 /* Create the Kernel Thread Object */ 4260 Status = NtCreateThread(&ThreadHandle, 4261 THREAD_ALL_ACCESS, 4262 ObjectAttributes, 4263 ProcessHandle, 4264 &ClientId, 4265 &Context, 4266 &InitialTeb, 4267 TRUE); 4268 if (!NT_SUCCESS(Status)) 4269 { 4270 /* A process is not allowed to exist without a main thread, so fail */ 4271 DPRINT1("Creating the main thread failed: %lx\n", Status); 4272 BaseSetLastNTError(Status); 4273 Result = FALSE; 4274 goto Quickie; 4275 } 4276 4277 /* Begin filling out the CSRSS message, first with our IDs and handles */ 4278 CreateProcessMsg->ProcessHandle = ProcessHandle; 4279 CreateProcessMsg->ThreadHandle = ThreadHandle; 4280 CreateProcessMsg->ClientId = ClientId; 4281 4282 /* Write the remote PEB address and clear it locally, we no longer use it */ 4283 CreateProcessMsg->PebAddressNative = RemotePeb; 4284 #ifdef _WIN64 4285 DPRINT1("TODO: WOW64 is not supported yet\n"); 4286 CreateProcessMsg->PebAddressWow64 = 0; 4287 #else 4288 CreateProcessMsg->PebAddressWow64 = (ULONG)RemotePeb; 4289 #endif 4290 RemotePeb = NULL; 4291 4292 /* Now check what kind of architecture this image was made for */ 4293 switch (ImageInformation.Machine) 4294 { 4295 /* IA32, IA64 and AMD64 are supported in Server 2003 */ 4296 case IMAGE_FILE_MACHINE_I386: 4297 CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL; 4298 break; 4299 case IMAGE_FILE_MACHINE_IA64: 4300 CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_IA64; 4301 break; 4302 case IMAGE_FILE_MACHINE_AMD64: 4303 CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64; 4304 break; 4305 4306 /* Anything else results in image unknown -- but no failure */ 4307 default: 4308 DbgPrint("kernel32: No mapping for ImageInformation.Machine == %04x\n", 4309 ImageInformation.Machine); 4310 CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_UNKNOWN; 4311 break; 4312 } 4313 4314 /* Write the input creation flags except any debugger-related flags */ 4315 CreateProcessMsg->CreationFlags = dwCreationFlags & 4316 ~(DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS); 4317 4318 /* CSRSS needs to know if this is a GUI app or not */ 4319 if ((ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_GUI) || 4320 (IsWowApp)) 4321 { 4322 /* 4323 * For GUI apps we turn on the 2nd bit. This allow CSRSS server dlls 4324 * (basesrv in particular) to know whether or not this is a GUI or a 4325 * TUI application. 4326 */ 4327 AddToHandle(CreateProcessMsg->ProcessHandle, 2); 4328 4329 /* Also check if the parent is also a GUI process */ 4330 NtHeaders = RtlImageNtHeader(GetModuleHandle(NULL)); 4331 if ((NtHeaders) && 4332 (NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI)) 4333 { 4334 /* Let it know that it should display the hourglass mouse cursor */ 4335 AddToHandle(CreateProcessMsg->ProcessHandle, 1); 4336 } 4337 } 4338 4339 /* For all apps, if this flag is on, the hourglass mouse cursor is shown */ 4340 if (StartupInfo.dwFlags & STARTF_FORCEONFEEDBACK) 4341 { 4342 AddToHandle(CreateProcessMsg->ProcessHandle, 1); 4343 } 4344 4345 /* Likewise, the opposite holds as well */ 4346 if (StartupInfo.dwFlags & STARTF_FORCEOFFFEEDBACK) 4347 { 4348 RemoveFromHandle(CreateProcessMsg->ProcessHandle, 1); 4349 } 4350 4351 /* Also store which kind of VDM app (if any) this is */ 4352 CreateProcessMsg->VdmBinaryType = VdmBinaryType; 4353 4354 /* And if it really is a VDM app... */ 4355 if (VdmBinaryType) 4356 { 4357 /* Store the task ID and VDM console handle */ 4358 CreateProcessMsg->hVDM = VdmTask ? 0 : Peb->ProcessParameters->ConsoleHandle; 4359 CreateProcessMsg->VdmTask = VdmTask; 4360 } 4361 else if (VdmReserve) 4362 { 4363 /* Extended VDM, set a flag */ 4364 CreateProcessMsg->VdmBinaryType |= BINARY_TYPE_WOW_EX; 4365 } 4366 4367 /* Check if there's side-by-side assembly data associated with the process */ 4368 if (CreateProcessMsg->Sxs.Flags) 4369 { 4370 /* This should not happen in ReactOS yet */ 4371 DPRINT1("This is an SxS Message -- should not happen yet\n"); 4372 BaseSetLastNTError(STATUS_NOT_IMPLEMENTED); 4373 NtTerminateProcess(ProcessHandle, STATUS_NOT_IMPLEMENTED); 4374 Result = FALSE; 4375 goto Quickie; 4376 } 4377 4378 /* We are finally ready to call CSRSS to tell it about our new process! */ 4379 CsrClientCallServer((PCSR_API_MESSAGE)&CsrMsg[0], 4380 CaptureBuffer, 4381 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, 4382 BasepCreateProcess), 4383 sizeof(*CreateProcessMsg)); 4384 4385 /* CSRSS has returned, free the capture buffer now if we had one */ 4386 if (CaptureBuffer) 4387 { 4388 CsrFreeCaptureBuffer(CaptureBuffer); 4389 CaptureBuffer = NULL; 4390 } 4391 4392 /* Check if CSRSS failed to accept ownership of the new Windows process */ 4393 if (!NT_SUCCESS(CsrMsg[0].Status)) 4394 { 4395 /* Terminate the process and enter failure path with the CSRSS status */ 4396 DPRINT1("Failed to tell csrss about new process\n"); 4397 BaseSetLastNTError(CsrMsg[0].Status); 4398 NtTerminateProcess(ProcessHandle, CsrMsg[0].Status); 4399 Result = FALSE; 4400 goto Quickie; 4401 } 4402 4403 /* Check if we have a token due to Authz/Safer, not passed by the user */ 4404 if ((TokenHandle) && !(hUserToken)) 4405 { 4406 /* Replace the process and/or thread token with the one from Safer */ 4407 Status = BasepReplaceProcessThreadTokens(TokenHandle, 4408 ProcessHandle, 4409 ThreadHandle); 4410 if (!NT_SUCCESS(Status)) 4411 { 4412 /* If this failed, kill the process and enter the failure path */ 4413 DPRINT1("Failed to update process token: %lx\n", Status); 4414 NtTerminateProcess(ProcessHandle, Status); 4415 BaseSetLastNTError(Status); 4416 Result = FALSE; 4417 goto Quickie; 4418 } 4419 } 4420 4421 /* Check if a job was associated with this process */ 4422 if (JobHandle) 4423 { 4424 /* Bind the process and job together now */ 4425 Status = NtAssignProcessToJobObject(JobHandle, ProcessHandle); 4426 if (!NT_SUCCESS(Status)) 4427 { 4428 /* Kill the process and enter the failure path if binding failed */ 4429 DPRINT1("Failed to assign process to job: %lx\n", Status); 4430 NtTerminateProcess(ProcessHandle, STATUS_ACCESS_DENIED); 4431 BaseSetLastNTError(Status); 4432 Result = FALSE; 4433 goto Quickie; 4434 } 4435 } 4436 4437 /* Finally, resume the thread to actually get the process started */ 4438 if (!(dwCreationFlags & CREATE_SUSPENDED)) 4439 { 4440 NtResumeThread(ThreadHandle, &ResumeCount); 4441 } 4442 4443 VdmShortCircuit: 4444 /* We made it this far, meaning we have a fully created process and thread */ 4445 Result = TRUE; 4446 4447 /* Anyone doing a VDM undo should now undo everything, since we are done */ 4448 if (VdmUndoLevel) VdmUndoLevel |= VDM_UNDO_COMPLETED; 4449 4450 /* Having a VDM wait object implies this must be a VDM process */ 4451 if (VdmWaitObject) 4452 { 4453 /* Check if it's a 16-bit separate WOW process */ 4454 if (VdmBinaryType == BINARY_TYPE_SEPARATE_WOW) 4455 { 4456 /* OR-in the special flag to indicate this, and return to caller */ 4457 AddToHandle(VdmWaitObject, 2); 4458 lpProcessInformation->hProcess = VdmWaitObject; 4459 4460 /* Check if this was a re-used VDM */ 4461 if (VdmUndoLevel & VDM_UNDO_REUSE) 4462 { 4463 /* No Client ID should be returned in this case */ 4464 ClientId.UniqueProcess = 0; 4465 ClientId.UniqueThread = 0; 4466 } 4467 } 4468 else 4469 { 4470 /* OR-in the special flag to indicate this is not a separate VDM */ 4471 AddToHandle(VdmWaitObject, 1); 4472 4473 /* Return handle to the caller */ 4474 lpProcessInformation->hProcess = VdmWaitObject; 4475 } 4476 4477 /* Close the original process handle, since it's not needed for VDM */ 4478 if (ProcessHandle) NtClose(ProcessHandle); 4479 } 4480 else 4481 { 4482 /* This is a regular process, so return the real process handle */ 4483 lpProcessInformation->hProcess = ProcessHandle; 4484 } 4485 4486 /* Return the rest of the process information based on what we have so far */ 4487 lpProcessInformation->hThread = ThreadHandle; 4488 lpProcessInformation->dwProcessId = HandleToUlong(ClientId.UniqueProcess); 4489 lpProcessInformation->dwThreadId = HandleToUlong(ClientId.UniqueThread); 4490 4491 /* NULL these out here so we know to treat this as a success scenario */ 4492 ProcessHandle = NULL; 4493 ThreadHandle = NULL; 4494 4495 Quickie: 4496 /* Free the debugger command line if one was allocated */ 4497 if (DebuggerCmdLine) RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine); 4498 4499 /* Check if an SxS full path as queried */ 4500 if (PathBuffer) 4501 { 4502 /* Reinitialize the executable path */ 4503 RtlInitEmptyUnicodeString(&SxsWin32ExePath, NULL, 0); 4504 SxsWin32ExePath.Length = 0; 4505 4506 /* Free the path buffer */ 4507 RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer); 4508 } 4509 4510 #if _SXS_SUPPORT_ENABLED_ 4511 /* Check if this was a non-VDM process */ 4512 if (!VdmBinaryType) 4513 { 4514 /* Then it must've had SxS data, so close the handles used for it */ 4515 BasepSxsCloseHandles(&Handles); 4516 BasepSxsCloseHandles(&FileHandles); 4517 4518 /* Check if we built SxS byte buffers for this create process request */ 4519 if (SxsConglomeratedBuffer) 4520 { 4521 /* Loop all of them */ 4522 for (i = 0; i < 5; i++) 4523 { 4524 /* Check if this one was allocated */ 4525 ThisBuffer = SxsStaticBuffers[i]; 4526 if (ThisBuffer) 4527 { 4528 /* Get the underlying RTL_BUFFER structure */ 4529 ByteBuffer = &ThisBuffer->ByteBuffer; 4530 if ((ThisBuffer != (PVOID)-8) && (ByteBuffer->Buffer)) 4531 { 4532 /* Check if it was dynamic */ 4533 if (ByteBuffer->Buffer != ByteBuffer->StaticBuffer) 4534 { 4535 /* Free it from the heap */ 4536 FreeString.Buffer = (PWCHAR)ByteBuffer->Buffer; 4537 RtlFreeUnicodeString(&FreeString); 4538 } 4539 4540 /* Reset the buffer to its static data */ 4541 ByteBuffer->Buffer = ByteBuffer->StaticBuffer; 4542 ByteBuffer->Size = ByteBuffer->StaticSize; 4543 } 4544 4545 /* Reset the string to the static buffer */ 4546 RtlInitEmptyUnicodeString(&ThisBuffer->String, 4547 (PWCHAR)ByteBuffer->StaticBuffer, 4548 ByteBuffer->StaticSize); 4549 if (ThisBuffer->String.Buffer) 4550 { 4551 /* Also NULL-terminate it */ 4552 *ThisBuffer->String.Buffer = UNICODE_NULL; 4553 } 4554 } 4555 } 4556 } 4557 } 4558 #endif 4559 /* Check if an environment was passed in */ 4560 if ((lpEnvironment) && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT)) 4561 { 4562 /* Destroy it */ 4563 RtlDestroyEnvironment(lpEnvironment); 4564 4565 /* If this was the VDM environment too, clear that as well */ 4566 if (VdmUnicodeEnv.Buffer == lpEnvironment) VdmUnicodeEnv.Buffer = NULL; 4567 lpEnvironment = NULL; 4568 } 4569 4570 /* Unconditionally free all the name parsing buffers we always allocate */ 4571 RtlFreeHeap(RtlGetProcessHeap(), 0, QuotedCmdLine); 4572 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer); 4573 RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentDirectory); 4574 RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer); 4575 4576 /* Close open file/section handles */ 4577 if (FileHandle) NtClose(FileHandle); 4578 if (SectionHandle) NtClose(SectionHandle); 4579 4580 /* If we have a thread handle, this was a failure path */ 4581 if (ThreadHandle) 4582 { 4583 /* So kill the process and close the thread handle */ 4584 NtTerminateProcess(ProcessHandle, 0); 4585 NtClose(ThreadHandle); 4586 } 4587 4588 /* If we have a process handle, this was a failure path, so close it */ 4589 if (ProcessHandle) NtClose(ProcessHandle); 4590 4591 /* Thread/process handles, if any, are now processed. Now close this one. */ 4592 if (JobHandle) NtClose(JobHandle); 4593 4594 /* Check if we had created a token */ 4595 if (TokenHandle) 4596 { 4597 /* And if the user asked for one */ 4598 if (hUserToken) 4599 { 4600 /* Then return it */ 4601 *hNewToken = TokenHandle; 4602 } 4603 else 4604 { 4605 /* User didn't want it, so we used it temporarily -- close it */ 4606 NtClose(TokenHandle); 4607 } 4608 } 4609 4610 /* Free any temporary app compatibility data, it's no longer needed */ 4611 BasepFreeAppCompatData(AppCompatData, AppCompatSxsData); 4612 4613 /* Free a few strings. The API takes care of these possibly being NULL */ 4614 RtlFreeUnicodeString(&VdmString); 4615 RtlFreeUnicodeString(&DebuggerString); 4616 4617 /* Check if we had built any sort of VDM environment */ 4618 if ((VdmAnsiEnv.Buffer) || (VdmUnicodeEnv.Buffer)) 4619 { 4620 /* Free it */ 4621 BaseDestroyVDMEnvironment(&VdmAnsiEnv, &VdmUnicodeEnv); 4622 } 4623 4624 /* Check if this was any kind of VDM application that we ended up creating */ 4625 if ((VdmUndoLevel) && (!(VdmUndoLevel & VDM_UNDO_COMPLETED))) 4626 { 4627 /* Send an undo */ 4628 BaseUpdateVDMEntry(VdmEntryUndo, 4629 (PHANDLE)&VdmTask, 4630 VdmUndoLevel, 4631 VdmBinaryType); 4632 4633 /* And close whatever VDM handle we were using for notifications */ 4634 if (VdmWaitObject) NtClose(VdmWaitObject); 4635 } 4636 4637 /* Check if we ended up here with an allocated search path, and free it */ 4638 if (SearchPath) RtlFreeHeap(RtlGetProcessHeap(), 0, SearchPath); 4639 4640 /* Finally, return the API's result */ 4641 return Result; 4642 } 4643 4644 /* 4645 * @implemented 4646 */ 4647 BOOL 4648 WINAPI 4649 DECLSPEC_HOTPATCH 4650 CreateProcessW(LPCWSTR lpApplicationName, 4651 LPWSTR lpCommandLine, 4652 LPSECURITY_ATTRIBUTES lpProcessAttributes, 4653 LPSECURITY_ATTRIBUTES lpThreadAttributes, 4654 BOOL bInheritHandles, 4655 DWORD dwCreationFlags, 4656 LPVOID lpEnvironment, 4657 LPCWSTR lpCurrentDirectory, 4658 LPSTARTUPINFOW lpStartupInfo, 4659 LPPROCESS_INFORMATION lpProcessInformation) 4660 { 4661 /* Call the internal (but exported) version */ 4662 return CreateProcessInternalW(NULL, 4663 lpApplicationName, 4664 lpCommandLine, 4665 lpProcessAttributes, 4666 lpThreadAttributes, 4667 bInheritHandles, 4668 dwCreationFlags, 4669 lpEnvironment, 4670 lpCurrentDirectory, 4671 lpStartupInfo, 4672 lpProcessInformation, 4673 NULL); 4674 } 4675 4676 /* 4677 * @implemented 4678 */ 4679 BOOL 4680 WINAPI 4681 CreateProcessInternalA(HANDLE hToken, 4682 LPCSTR lpApplicationName, 4683 LPSTR lpCommandLine, 4684 LPSECURITY_ATTRIBUTES lpProcessAttributes, 4685 LPSECURITY_ATTRIBUTES lpThreadAttributes, 4686 BOOL bInheritHandles, 4687 DWORD dwCreationFlags, 4688 LPVOID lpEnvironment, 4689 LPCSTR lpCurrentDirectory, 4690 LPSTARTUPINFOA lpStartupInfo, 4691 LPPROCESS_INFORMATION lpProcessInformation, 4692 PHANDLE hNewToken) 4693 { 4694 UNICODE_STRING CommandLine; 4695 UNICODE_STRING ApplicationName; 4696 UNICODE_STRING CurrentDirectory; 4697 BOOL bRetVal; 4698 STARTUPINFOW StartupInfo; 4699 4700 DPRINT("dwCreationFlags %x, lpEnvironment %p, lpCurrentDirectory %p, " 4701 "lpStartupInfo %p, lpProcessInformation %p\n", 4702 dwCreationFlags, lpEnvironment, lpCurrentDirectory, 4703 lpStartupInfo, lpProcessInformation); 4704 4705 /* Copy Startup Info */ 4706 RtlMoveMemory(&StartupInfo, lpStartupInfo, sizeof(*lpStartupInfo)); 4707 4708 /* Initialize all strings to nothing */ 4709 CommandLine.Buffer = NULL; 4710 ApplicationName.Buffer = NULL; 4711 CurrentDirectory.Buffer = NULL; 4712 StartupInfo.lpDesktop = NULL; 4713 StartupInfo.lpReserved = NULL; 4714 StartupInfo.lpTitle = NULL; 4715 4716 /* Convert the Command line */ 4717 if (lpCommandLine) 4718 { 4719 Basep8BitStringToDynamicUnicodeString(&CommandLine, 4720 lpCommandLine); 4721 } 4722 4723 /* Convert the Name and Directory */ 4724 if (lpApplicationName) 4725 { 4726 Basep8BitStringToDynamicUnicodeString(&ApplicationName, 4727 lpApplicationName); 4728 } 4729 if (lpCurrentDirectory) 4730 { 4731 Basep8BitStringToDynamicUnicodeString(&CurrentDirectory, 4732 lpCurrentDirectory); 4733 } 4734 4735 /* Now convert Startup Strings */ 4736 if (lpStartupInfo->lpReserved) 4737 { 4738 BasepAnsiStringToHeapUnicodeString(lpStartupInfo->lpReserved, 4739 &StartupInfo.lpReserved); 4740 } 4741 if (lpStartupInfo->lpDesktop) 4742 { 4743 BasepAnsiStringToHeapUnicodeString(lpStartupInfo->lpDesktop, 4744 &StartupInfo.lpDesktop); 4745 } 4746 if (lpStartupInfo->lpTitle) 4747 { 4748 BasepAnsiStringToHeapUnicodeString(lpStartupInfo->lpTitle, 4749 &StartupInfo.lpTitle); 4750 } 4751 4752 /* Call the Unicode function */ 4753 bRetVal = CreateProcessInternalW(hToken, 4754 ApplicationName.Buffer, 4755 CommandLine.Buffer, 4756 lpProcessAttributes, 4757 lpThreadAttributes, 4758 bInheritHandles, 4759 dwCreationFlags, 4760 lpEnvironment, 4761 CurrentDirectory.Buffer, 4762 &StartupInfo, 4763 lpProcessInformation, 4764 hNewToken); 4765 4766 /* Clean up */ 4767 RtlFreeUnicodeString(&ApplicationName); 4768 RtlFreeUnicodeString(&CommandLine); 4769 RtlFreeUnicodeString(&CurrentDirectory); 4770 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo.lpDesktop); 4771 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo.lpReserved); 4772 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo.lpTitle); 4773 4774 /* Return what Unicode did */ 4775 return bRetVal; 4776 } 4777 4778 /* 4779 * FUNCTION: The CreateProcess function creates a new process and its 4780 * primary thread. The new process executes the specified executable file 4781 * ARGUMENTS: 4782 * 4783 * lpApplicationName = Pointer to name of executable module 4784 * lpCommandLine = Pointer to command line string 4785 * lpProcessAttributes = Process security attributes 4786 * lpThreadAttributes = Thread security attributes 4787 * bInheritHandles = Handle inheritance flag 4788 * dwCreationFlags = Creation flags 4789 * lpEnvironment = Pointer to new environment block 4790 * lpCurrentDirectory = Pointer to current directory name 4791 * lpStartupInfo = Pointer to startup info 4792 * lpProcessInformation = Pointer to process information 4793 * 4794 * @implemented 4795 */ 4796 BOOL 4797 WINAPI 4798 DECLSPEC_HOTPATCH 4799 CreateProcessA(LPCSTR lpApplicationName, 4800 LPSTR lpCommandLine, 4801 LPSECURITY_ATTRIBUTES lpProcessAttributes, 4802 LPSECURITY_ATTRIBUTES lpThreadAttributes, 4803 BOOL bInheritHandles, 4804 DWORD dwCreationFlags, 4805 LPVOID lpEnvironment, 4806 LPCSTR lpCurrentDirectory, 4807 LPSTARTUPINFOA lpStartupInfo, 4808 LPPROCESS_INFORMATION lpProcessInformation) 4809 { 4810 /* Call the internal (but exported) version */ 4811 return CreateProcessInternalA(NULL, 4812 lpApplicationName, 4813 lpCommandLine, 4814 lpProcessAttributes, 4815 lpThreadAttributes, 4816 bInheritHandles, 4817 dwCreationFlags, 4818 lpEnvironment, 4819 lpCurrentDirectory, 4820 lpStartupInfo, 4821 lpProcessInformation, 4822 NULL); 4823 } 4824 4825 /* 4826 * @implemented 4827 */ 4828 UINT 4829 WINAPI 4830 DECLSPEC_HOTPATCH 4831 WinExec(LPCSTR lpCmdLine, 4832 UINT uCmdShow) 4833 { 4834 STARTUPINFOA StartupInfo; 4835 PROCESS_INFORMATION ProcessInformation; 4836 DWORD dosErr; 4837 4838 RtlZeroMemory(&StartupInfo, sizeof(StartupInfo)); 4839 StartupInfo.cb = sizeof(STARTUPINFOA); 4840 StartupInfo.wShowWindow = (WORD)uCmdShow; 4841 StartupInfo.dwFlags = 0; 4842 4843 if (!CreateProcessA(NULL, 4844 (PVOID)lpCmdLine, 4845 NULL, 4846 NULL, 4847 FALSE, 4848 0, 4849 NULL, 4850 NULL, 4851 &StartupInfo, 4852 &ProcessInformation)) 4853 { 4854 dosErr = GetLastError(); 4855 return dosErr < 32 ? dosErr : ERROR_BAD_FORMAT; 4856 } 4857 4858 if (NULL != UserWaitForInputIdleRoutine) 4859 { 4860 UserWaitForInputIdleRoutine(ProcessInformation.hProcess, 4861 10000); 4862 } 4863 4864 NtClose(ProcessInformation.hProcess); 4865 NtClose(ProcessInformation.hThread); 4866 4867 return 33; /* Something bigger than 31 means success. */ 4868 } 4869 4870 /* EOF */ 4871