[KERNEL32] Accept all images for NT 3.1 and higher instead of bailing out at image...
[reactos.git] / dll / win32 / kernel32 / client / proc.c
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)Handles->ViewBase.LowPart);
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 = 0;
1399 StartupInfo->lpDesktop = 0;
1400 StartupInfo->lpTitle = 0;
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, RegionSize, StackSize, ErrorCode, Flags;
2314 USHORT ImageMachine;
2315 ULONG ParameterFlags, PrivilegeValue, HardErrorMode, ErrorResponse;
2316 ULONG_PTR ErrorParameters[2];
2317 BOOLEAN InJob, SaferNeeded, UseLargePages, HavePrivilege;
2318 BOOLEAN QuerySection, SkipSaferAndAppCompat;
2319 CONTEXT Context;
2320 BASE_API_MESSAGE CsrMsg[2];
2321 PBASE_CREATE_PROCESS CreateProcessMsg;
2322 PCSR_CAPTURE_BUFFER CaptureBuffer;
2323 PVOID BaseAddress, PrivilegeState, RealTimePrivilegeState;
2324 HANDLE DebugHandle, TokenHandle, JobHandle, KeyHandle, ThreadHandle;
2325 HANDLE FileHandle, SectionHandle, ProcessHandle;
2326 ULONG ResumeCount;
2327 PROCESS_PRIORITY_CLASS PriorityClass;
2328 NTSTATUS Status, AppCompatStatus, SaferStatus, IFEOStatus, ImageDbgStatus;
2329 PPEB Peb, RemotePeb;
2330 PTEB Teb;
2331 INITIAL_TEB InitialTeb;
2332 PVOID TibValue;
2333 PIMAGE_NT_HEADERS NtHeaders;
2334 STARTUPINFOW StartupInfo;
2335 PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
2336 UNICODE_STRING DebuggerString;
2337 BOOL Result;
2338 //
2339 // Variables used for command-line and argument parsing
2340 //
2341 PCHAR pcScan;
2342 SIZE_T n;
2343 WCHAR SaveChar;
2344 ULONG Length, FileAttribs, CmdQuoteLength;
2345 ULONG CmdLineLength, ResultSize;
2346 PWCHAR QuotedCmdLine, AnsiCmdCommand, ExtBuffer, CurrentDirectory;
2347 PWCHAR NullBuffer, ScanString, NameBuffer, SearchPath, DebuggerCmdLine;
2348 ANSI_STRING AnsiEnv;
2349 UNICODE_STRING UnicodeEnv, PathName;
2350 BOOLEAN SearchRetry, QuotesNeeded, CmdLineIsAppName, HasQuotes;
2351
2352 //
2353 // Variables used for Fusion/SxS (Side-by-Side Assemblies)
2354 //
2355 RTL_PATH_TYPE SxsPathType, PathType;
2356 #if _SXS_SUPPORT_ENABLED_
2357 PRTL_BUFFER ByteBuffer;
2358 PRTL_UNICODE_STRING_BUFFER ThisBuffer, Buffer, SxsStaticBuffers[5];
2359 PRTL_UNICODE_STRING_BUFFER* BufferHead, SxsStringBuffer;
2360 RTL_UNICODE_STRING_BUFFER SxsWin32ManifestPath, SxsNtManifestPath;
2361 RTL_UNICODE_STRING_BUFFER SxsWin32PolicyPath, SxsNtPolicyPath;
2362 RTL_UNICODE_STRING_BUFFER SxsWin32AssemblyDirectory;
2363 BASE_MSG_SXS_HANDLES MappedHandles, Handles, FileHandles;
2364 PVOID CapturedStrings[3];
2365 SXS_WIN32_NT_PATH_PAIR ExePathPair, ManifestPathPair, PolicyPathPair;
2366 SXS_OVERRIDE_MANIFEST OverrideManifest;
2367 UNICODE_STRING FreeString, SxsNtExePath;
2368 PWCHAR SxsConglomeratedBuffer, StaticBuffer;
2369 ULONG ConglomeratedBufferSizeBytes, StaticBufferSize, i;
2370 #endif
2371 ULONG FusionFlags;
2372
2373 //
2374 // Variables used for path conversion (and partially Fusion/SxS)
2375 //
2376 PWCHAR FilePart, PathBuffer, FreeBuffer;
2377 BOOLEAN TranslationStatus;
2378 RTL_RELATIVE_NAME_U SxsWin32RelativePath;
2379 UNICODE_STRING PathBufferString, SxsWin32ExePath;
2380
2381 //
2382 // Variables used by Application Compatibility (and partially Fusion/SxS)
2383 //
2384 PVOID AppCompatSxsData, AppCompatData;
2385 ULONG AppCompatSxsDataSize, AppCompatDataSize;
2386 //
2387 // Variables used by VDM (Virtual Dos Machine) and WOW32 (16-bit Support)
2388 //
2389 ULONG BinarySubType, VdmBinaryType, VdmTask, VdmReserve;
2390 ULONG VdmUndoLevel;
2391 BOOLEAN UseVdmReserve;
2392 HANDLE VdmWaitObject;
2393 ANSI_STRING VdmAnsiEnv;
2394 UNICODE_STRING VdmString, VdmUnicodeEnv;
2395 BOOLEAN IsWowApp;
2396 PBASE_CHECK_VDM CheckVdmMsg;
2397
2398 /* Zero out the initial core variables and handles */
2399 QuerySection = FALSE;
2400 InJob = FALSE;
2401 SkipSaferAndAppCompat = FALSE;
2402 ParameterFlags = 0;
2403 Flags = 0;
2404 DebugHandle = NULL;
2405 JobHandle = NULL;
2406 TokenHandle = NULL;
2407 FileHandle = NULL;
2408 SectionHandle = NULL;
2409 ProcessHandle = NULL;
2410 ThreadHandle = NULL;
2411 BaseAddress = (PVOID)1;
2412
2413 /* Zero out initial SxS and Application Compatibility state */
2414 AppCompatData = NULL;
2415 AppCompatDataSize = 0;
2416 AppCompatSxsData = NULL;
2417 AppCompatSxsDataSize = 0;
2418 CaptureBuffer = NULL;
2419 #if _SXS_SUPPORT_ENABLED_
2420 SxsConglomeratedBuffer = NULL;
2421 #endif
2422 FusionFlags = 0;
2423
2424 /* Zero out initial parsing variables -- others are initialized later */
2425 DebuggerCmdLine = NULL;
2426 PathBuffer = NULL;
2427 SearchPath = NULL;
2428 NullBuffer = 0;
2429 FreeBuffer = NULL;
2430 NameBuffer = NULL;
2431 CurrentDirectory = NULL;
2432 FilePart = NULL;
2433 DebuggerString.Buffer = NULL;
2434 HasQuotes = FALSE;
2435 QuotedCmdLine = NULL;
2436
2437 /* Zero out initial VDM state */
2438 VdmAnsiEnv.Buffer = NULL;
2439 VdmUnicodeEnv.Buffer = NULL;
2440 VdmString.Buffer = NULL;
2441 VdmTask = 0;
2442 VdmUndoLevel = 0;
2443 VdmBinaryType = 0;
2444 VdmReserve = 0;
2445 VdmWaitObject = NULL;
2446 UseVdmReserve = FALSE;
2447 IsWowApp = FALSE;
2448
2449 /* Set message structures */
2450 CreateProcessMsg = &CsrMsg[0].Data.CreateProcessRequest;
2451 CheckVdmMsg = &CsrMsg[1].Data.CheckVDMRequest;
2452
2453 /* Clear the more complex structures by zeroing out their entire memory */
2454 RtlZeroMemory(&Context, sizeof(Context));
2455 #if _SXS_SUPPORT_ENABLED_
2456 RtlZeroMemory(&FileHandles, sizeof(FileHandles));
2457 RtlZeroMemory(&MappedHandles, sizeof(MappedHandles));
2458 RtlZeroMemory(&Handles, sizeof(Handles));
2459 #endif
2460 RtlZeroMemory(&CreateProcessMsg->Sxs, sizeof(CreateProcessMsg->Sxs));
2461 RtlZeroMemory(&LocalProcessAttributes, sizeof(LocalProcessAttributes));
2462 RtlZeroMemory(&LocalThreadAttributes, sizeof(LocalThreadAttributes));
2463
2464 /* Zero out output arguments as well */
2465 RtlZeroMemory(lpProcessInformation, sizeof(*lpProcessInformation));
2466 if (hNewToken) *hNewToken = NULL;
2467
2468 /* Capture the special window flag */
2469 NoWindow = dwCreationFlags & CREATE_NO_WINDOW;
2470 dwCreationFlags &= ~CREATE_NO_WINDOW;
2471
2472 #if _SXS_SUPPORT_ENABLED_
2473 /* Setup the SxS static string arrays and buffers */
2474 SxsStaticBuffers[0] = &SxsWin32ManifestPath;
2475 SxsStaticBuffers[1] = &SxsWin32PolicyPath;
2476 SxsStaticBuffers[2] = &SxsWin32AssemblyDirectory;
2477 SxsStaticBuffers[3] = &SxsNtManifestPath;
2478 SxsStaticBuffers[4] = &SxsNtPolicyPath;
2479 ExePathPair.Win32 = &SxsWin32ExePath;
2480 ExePathPair.Nt = &SxsNtExePath;
2481 ManifestPathPair.Win32 = &SxsWin32ManifestPath.String;
2482 ManifestPathPair.Nt = &SxsNtManifestPath.String;
2483 PolicyPathPair.Win32 = &SxsWin32PolicyPath.String;
2484 PolicyPathPair.Nt = &SxsNtPolicyPath.String;
2485 #endif
2486
2487 DPRINT("CreateProcessInternalW: '%S' '%S' %lx\n", lpApplicationName, lpCommandLine, dwCreationFlags);
2488
2489 /* Finally, set our TEB and PEB */
2490 Teb = NtCurrentTeb();
2491 Peb = NtCurrentPeb();
2492
2493 /* This combination is illegal (see MSDN) */
2494 if ((dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) ==
2495 (DETACHED_PROCESS | CREATE_NEW_CONSOLE))
2496 {
2497 DPRINT1("Invalid flag combo used\n");
2498 SetLastError(ERROR_INVALID_PARAMETER);
2499 return FALSE;
2500 }
2501
2502 /* Convert the priority class */
2503 if (dwCreationFlags & IDLE_PRIORITY_CLASS)
2504 {
2505 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE;
2506 }
2507 else if (dwCreationFlags & BELOW_NORMAL_PRIORITY_CLASS)
2508 {
2509 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
2510 }
2511 else if (dwCreationFlags & NORMAL_PRIORITY_CLASS)
2512 {
2513 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
2514 }
2515 else if (dwCreationFlags & ABOVE_NORMAL_PRIORITY_CLASS)
2516 {
2517 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
2518 }
2519 else if (dwCreationFlags & HIGH_PRIORITY_CLASS)
2520 {
2521 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
2522 }
2523 else if (dwCreationFlags & REALTIME_PRIORITY_CLASS)
2524 {
2525 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
2526 PriorityClass.PriorityClass += (BasepIsRealtimeAllowed(FALSE) != NULL);
2527 }
2528 else
2529 {
2530 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_INVALID;
2531 }
2532
2533 /* Done with the priority masks, so get rid of them */
2534 PriorityClass.Foreground = FALSE;
2535 dwCreationFlags &= ~(NORMAL_PRIORITY_CLASS |
2536 IDLE_PRIORITY_CLASS |
2537 HIGH_PRIORITY_CLASS |
2538 REALTIME_PRIORITY_CLASS |
2539 BELOW_NORMAL_PRIORITY_CLASS |
2540 ABOVE_NORMAL_PRIORITY_CLASS);
2541
2542 /* You cannot request both a shared and a separate WoW VDM */
2543 if ((dwCreationFlags & CREATE_SEPARATE_WOW_VDM) &&
2544 (dwCreationFlags & CREATE_SHARED_WOW_VDM))
2545 {
2546 /* Fail such nonsensical attempts */
2547 DPRINT1("Invalid WOW flags\n");
2548 SetLastError(ERROR_INVALID_PARAMETER);
2549 return FALSE;
2550 }
2551 else if (!(dwCreationFlags & CREATE_SHARED_WOW_VDM) &&
2552 (BaseStaticServerData->DefaultSeparateVDM))
2553 {
2554 /* A shared WoW VDM was not requested but system enforces separation */
2555 dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
2556 }
2557
2558 /* If a shared WoW VDM is used, make sure the process isn't in a job */
2559 if (!(dwCreationFlags & CREATE_SEPARATE_WOW_VDM) &&
2560 (NtIsProcessInJob(NtCurrentProcess(), NULL)))
2561 {
2562 /* Remove the shared flag and add the separate flag */
2563 dwCreationFlags = (dwCreationFlags &~ CREATE_SHARED_WOW_VDM) |
2564 CREATE_SEPARATE_WOW_VDM;
2565 }
2566
2567 /* Convert the environment */
2568 if ((lpEnvironment) && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
2569 {
2570 /* Scan the environment to calculate its Unicode size */
2571 AnsiEnv.Buffer = pcScan = (PCHAR)lpEnvironment;
2572 while ((*pcScan) || (*(pcScan + 1))) ++pcScan;
2573
2574 /* Create our ANSI String */
2575 AnsiEnv.Length = pcScan - (PCHAR)lpEnvironment + sizeof(ANSI_NULL);
2576 AnsiEnv.MaximumLength = AnsiEnv.Length + sizeof(ANSI_NULL);
2577
2578 /* Allocate memory for the Unicode Environment */
2579 UnicodeEnv.Buffer = NULL;
2580 RegionSize = AnsiEnv.MaximumLength * sizeof(WCHAR);
2581 Status = NtAllocateVirtualMemory(NtCurrentProcess(),
2582 (PVOID)&UnicodeEnv.Buffer,
2583 0,
2584 &RegionSize,
2585 MEM_COMMIT,
2586 PAGE_READWRITE);
2587 if (!NT_SUCCESS(Status))
2588 {
2589 /* Fail */
2590 BaseSetLastNTError(Status);
2591 return FALSE;
2592 }
2593
2594 /* Use the allocated size and convert */
2595 UnicodeEnv.MaximumLength = (USHORT)RegionSize;
2596 Status = RtlAnsiStringToUnicodeString(&UnicodeEnv, &AnsiEnv, FALSE);
2597 if (!NT_SUCCESS(Status))
2598 {
2599 /* Fail */
2600 NtFreeVirtualMemory(NtCurrentProcess(),
2601 (PVOID)&UnicodeEnv.Buffer,
2602 &RegionSize,
2603 MEM_RELEASE);
2604 BaseSetLastNTError(Status);
2605 return FALSE;
2606 }
2607
2608 /* Now set the Unicode environment as the environment string pointer */
2609 lpEnvironment = UnicodeEnv.Buffer;
2610 }
2611
2612 /* Make a copy of the caller's startup info since we'll modify it */
2613 StartupInfo = *lpStartupInfo;
2614
2615 /* Check if private data is being sent on the same channel as std handles */
2616 if ((StartupInfo.dwFlags & STARTF_USESTDHANDLES) &&
2617 (StartupInfo.dwFlags & (STARTF_USEHOTKEY | STARTF_SHELLPRIVATE)))
2618 {
2619 /* Cannot use the std handles since we have monitor/hotkey values */
2620 StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
2621 }
2622
2623 /* If there's a debugger, or we have to launch cmd.exe, we go back here */
2624 AppNameRetry:
2625 /* New iteration -- free any existing name buffer */
2626 if (NameBuffer)
2627 {
2628 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
2629 NameBuffer = NULL;
2630 }
2631
2632 /* New iteration -- free any existing free buffer */
2633 if (FreeBuffer)
2634 {
2635 RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer);
2636 FreeBuffer = NULL;
2637 }
2638
2639 /* New iteration -- close any existing file handle */
2640 if (FileHandle)
2641 {
2642 NtClose(FileHandle);
2643 FileHandle = NULL;
2644 }
2645
2646 /* Set the initial parsing state. This code can loop -- don't move this! */
2647 ErrorCode = 0;
2648 SearchRetry = TRUE;
2649 QuotesNeeded = FALSE;
2650 CmdLineIsAppName = FALSE;
2651
2652 /* First check if we don't have an application name */
2653 if (!lpApplicationName)
2654 {
2655 /* This should be the first time we attempt creating one */
2656 ASSERT(NameBuffer == NULL);
2657
2658 /* Allocate a buffer to hold it */
2659 NameBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
2660 0,
2661 MAX_PATH * sizeof(WCHAR));
2662 if (!NameBuffer)
2663 {
2664 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2665 Result = FALSE;
2666 goto Quickie;
2667 }
2668
2669 /* Initialize the application name and our parsing parameters */
2670 lpApplicationName = NullBuffer = ScanString = lpCommandLine;
2671
2672 /* Check for an initial quote*/
2673 if (*lpCommandLine == L'\"')
2674 {
2675 /* We found a quote, keep searching for another one */
2676 SearchRetry = FALSE;
2677 ScanString++;
2678 lpApplicationName = ScanString;
2679 while (*ScanString)
2680 {
2681 /* Have we found the terminating quote? */
2682 if (*ScanString == L'\"')
2683 {
2684 /* We're done, get out of here */
2685 NullBuffer = ScanString;
2686 HasQuotes = TRUE;
2687 break;
2688 }
2689
2690 /* Keep searching for the quote */
2691 ScanString++;
2692 NullBuffer = ScanString;
2693 }
2694 }
2695 else
2696 {
2697 StartScan:
2698 /* We simply make the application name be the command line*/
2699 lpApplicationName = lpCommandLine;
2700 while (*ScanString)
2701 {
2702 /* Check if it starts with a space or tab */
2703 if ((*ScanString == L' ') || (*ScanString == L'\t'))
2704 {
2705 /* Break out of the search loop */
2706 NullBuffer = ScanString;
2707 break;
2708 }
2709
2710 /* Keep searching for a space or tab */
2711 ScanString++;
2712 NullBuffer = ScanString;
2713 }
2714 }
2715
2716 /* We have found the end of the application name, terminate it */
2717 SaveChar = *NullBuffer;
2718 *NullBuffer = UNICODE_NULL;
2719
2720 /* New iteration -- free any existing saved path */
2721 if (SearchPath)
2722 {
2723 RtlFreeHeap(RtlGetProcessHeap(), 0, SearchPath);
2724 SearchPath = NULL;
2725 }
2726
2727 /* Now compute the final EXE path based on the name */
2728 SearchPath = BaseComputeProcessExePath((LPWSTR)lpApplicationName);
2729 DPRINT("Search Path: %S\n", SearchPath);
2730 if (!SearchPath)
2731 {
2732 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2733 Result = FALSE;
2734 goto Quickie;
2735 }
2736
2737 /* And search for the executable in the search path */
2738 Length = SearchPathW(SearchPath,
2739 lpApplicationName,
2740 L".exe",
2741 MAX_PATH,
2742 NameBuffer,
2743 NULL);
2744
2745 /* Did we find it? */
2746 if ((Length) && (Length < MAX_PATH))
2747 {
2748 /* Get file attributes */
2749 FileAttribs = GetFileAttributesW(NameBuffer);
2750 if ((FileAttribs != INVALID_FILE_ATTRIBUTES) &&
2751 (FileAttribs & FILE_ATTRIBUTE_DIRECTORY))
2752 {
2753 /* This was a directory, fail later on */
2754 Length = 0;
2755 }
2756 else
2757 {
2758 /* It's a file! */
2759 Length++;
2760 }
2761 }
2762
2763 DPRINT("Length: %lu Buffer: %S\n", Length, NameBuffer);
2764
2765 /* Check if there was a failure in SearchPathW */
2766 if ((Length) && (Length < MAX_PATH))
2767 {
2768 /* Everything looks good, restore the name */
2769 *NullBuffer = SaveChar;
2770 lpApplicationName = NameBuffer;
2771 }
2772 else
2773 {
2774 /* Check if this was a relative path, which would explain it */
2775 PathType = RtlDetermineDosPathNameType_U(lpApplicationName);
2776 if (PathType != RtlPathTypeRelative)
2777 {
2778 /* This should fail, and give us a detailed LastError */
2779 FileHandle = CreateFileW(lpApplicationName,
2780 GENERIC_READ,
2781 FILE_SHARE_READ |
2782 FILE_SHARE_WRITE,
2783 NULL,
2784 OPEN_EXISTING,
2785 FILE_ATTRIBUTE_NORMAL,
2786 NULL);
2787 if (FileHandle != INVALID_HANDLE_VALUE)
2788 {
2789 /* It worked? Return a generic error */
2790 CloseHandle(FileHandle);
2791 FileHandle = NULL;
2792 BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
2793 }
2794 }
2795 else
2796 {
2797 /* Path was absolute, which means it doesn't exist */
2798 BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
2799 }
2800
2801 /* Did we already fail once? */
2802 if (ErrorCode)
2803 {
2804 /* Set the error code */
2805 SetLastError(ErrorCode);
2806 }
2807 else
2808 {
2809 /* Not yet, cache it */
2810 ErrorCode = GetLastError();
2811 }
2812
2813 /* Put back the command line */
2814 *NullBuffer = SaveChar;
2815 lpApplicationName = NameBuffer;
2816
2817 /* It's possible there's whitespace in the directory name */
2818 if (!(*ScanString) || !(SearchRetry))
2819 {
2820 /* Not the case, give up completely */
2821 Result = FALSE;
2822 goto Quickie;
2823 }
2824
2825 /* There are spaces, so keep trying the next possibility */
2826 ScanString++;
2827 NullBuffer = ScanString;
2828
2829 /* We will have to add a quote, since there is a space */
2830 QuotesNeeded = TRUE;
2831 HasQuotes = TRUE;
2832 goto StartScan;
2833 }
2834 }
2835 else if (!(lpCommandLine) || !(*lpCommandLine))
2836 {
2837 /* We don't have a command line, so just use the application name */
2838 CmdLineIsAppName = TRUE;
2839 lpCommandLine = (LPWSTR)lpApplicationName;
2840 }
2841
2842 /* Convert the application name to its NT path */
2843 TranslationStatus = RtlDosPathNameToRelativeNtPathName_U(lpApplicationName,
2844 &PathName,
2845 NULL,
2846 &SxsWin32RelativePath);
2847 if (!TranslationStatus)
2848 {
2849 /* Path must be invalid somehow, bail out */
2850 DPRINT1("Path translation for SxS failed\n");
2851 SetLastError(ERROR_PATH_NOT_FOUND);
2852 Result = FALSE;
2853 goto Quickie;
2854 }
2855
2856 /* Setup the buffer that needs to be freed at the end */
2857 ASSERT(FreeBuffer == NULL);
2858 FreeBuffer = PathName.Buffer;
2859
2860 /* Check what kind of path the application is, for SxS (Fusion) purposes */
2861 RtlInitUnicodeString(&SxsWin32ExePath, lpApplicationName);
2862 SxsPathType = RtlDetermineDosPathNameType_U(lpApplicationName);
2863 if ((SxsPathType != RtlPathTypeDriveAbsolute) &&
2864 (SxsPathType != RtlPathTypeLocalDevice) &&
2865 (SxsPathType != RtlPathTypeRootLocalDevice) &&
2866 (SxsPathType != RtlPathTypeUncAbsolute))
2867 {
2868 /* Relative-type path, get the full path */
2869 RtlInitEmptyUnicodeString(&PathBufferString, NULL, 0);
2870 Status = RtlGetFullPathName_UstrEx(&SxsWin32ExePath,
2871 NULL,
2872 &PathBufferString,
2873 NULL,
2874 NULL,
2875 NULL,
2876 &SxsPathType,
2877 NULL);
2878 if (!NT_SUCCESS(Status))
2879 {
2880 /* Fail the rest of the create */
2881 RtlReleaseRelativeName(&SxsWin32RelativePath);
2882 BaseSetLastNTError(Status);
2883 Result = FALSE;
2884 goto Quickie;
2885 }
2886
2887 /* Use this full path as the SxS path */
2888 SxsWin32ExePath = PathBufferString;
2889 PathBuffer = PathBufferString.Buffer;
2890 PathBufferString.Buffer = NULL;
2891 DPRINT("SxS Path: %S\n", PathBuffer);
2892 }
2893
2894 /* Also set the .EXE path based on the path name */
2895 #if _SXS_SUPPORT_ENABLED_
2896 SxsNtExePath = PathName;
2897 #endif
2898 if (SxsWin32RelativePath.RelativeName.Length)
2899 {
2900 /* If it's relative, capture the relative name */
2901 PathName = SxsWin32RelativePath.RelativeName;
2902 }
2903 else
2904 {
2905 /* Otherwise, it's absolute, make sure no relative dir is used */
2906 SxsWin32RelativePath.ContainingDirectory = NULL;
2907 }
2908
2909 /* Now use the path name, and the root path, to try opening the app */
2910 DPRINT("Path: %wZ. Dir: %p\n", &PathName, SxsWin32RelativePath.ContainingDirectory);
2911 InitializeObjectAttributes(&LocalObjectAttributes,
2912 &PathName,
2913 OBJ_CASE_INSENSITIVE,
2914 SxsWin32RelativePath.ContainingDirectory,
2915 NULL);
2916 Status = NtOpenFile(&FileHandle,
2917 SYNCHRONIZE |
2918 FILE_READ_ATTRIBUTES |
2919 FILE_READ_DATA |
2920 FILE_EXECUTE,
2921 &LocalObjectAttributes,
2922 &IoStatusBlock,
2923 FILE_SHARE_READ | FILE_SHARE_DELETE,
2924 FILE_SYNCHRONOUS_IO_NONALERT |
2925 FILE_NON_DIRECTORY_FILE);
2926 if (!NT_SUCCESS(Status))
2927 {
2928 /* Try to open the app just for execute purposes instead */
2929 Status = NtOpenFile(&FileHandle,
2930 SYNCHRONIZE | FILE_EXECUTE,
2931 &LocalObjectAttributes,
2932 &IoStatusBlock,
2933 FILE_SHARE_READ | FILE_SHARE_DELETE,
2934 FILE_SYNCHRONOUS_IO_NONALERT |
2935 FILE_NON_DIRECTORY_FILE);
2936 }
2937
2938 /* Failure path, display which file failed to open */
2939 if (!NT_SUCCESS(Status))
2940 DPRINT1("Open file failed: %lx (%wZ)\n", Status, &PathName);
2941
2942 /* Cleanup in preparation for failure or success */
2943 RtlReleaseRelativeName(&SxsWin32RelativePath);
2944
2945 if (!NT_SUCCESS(Status))
2946 {
2947 /* Failure path, try to understand why */
2948 if (RtlIsDosDeviceName_U(lpApplicationName))
2949 {
2950 /* If a device is being executed, return this special error code */
2951 SetLastError(ERROR_BAD_DEVICE);
2952 Result = FALSE;
2953 goto Quickie;
2954 }
2955 else
2956 {
2957 /* Otherwise return the converted NT error code */
2958 BaseSetLastNTError(Status);
2959 Result = FALSE;
2960 goto Quickie;
2961 }
2962 }
2963
2964 /* Did the caller specify a desktop? */
2965 if (!StartupInfo.lpDesktop)
2966 {
2967 /* Use the one from the current process */
2968 StartupInfo.lpDesktop = Peb->ProcessParameters->DesktopInfo.Buffer;
2969 }
2970
2971 /* Create a section for this file */
2972 Status = NtCreateSection(&SectionHandle,
2973 SECTION_ALL_ACCESS,
2974 NULL,
2975 NULL,
2976 PAGE_EXECUTE,
2977 SEC_IMAGE,
2978 FileHandle);
2979 DPRINT("Section status: %lx\n", Status);
2980 if (NT_SUCCESS(Status))
2981 {
2982 /* Are we running on Windows Embedded, Datacenter, Blade or Starter? */
2983 if (SharedUserData->SuiteMask & (VER_SUITE_EMBEDDEDNT |
2984 VER_SUITE_DATACENTER |
2985 VER_SUITE_PERSONAL |
2986 VER_SUITE_BLADE))
2987 {
2988 /* These SKUs do not allow running certain applications */
2989 Status = BasepCheckWebBladeHashes(FileHandle);
2990 if (Status == STATUS_ACCESS_DENIED)
2991 {
2992 /* And this is one of them! */
2993 DPRINT1("Invalid Blade hashes!\n");
2994 SetLastError(ERROR_ACCESS_DISABLED_WEBBLADE);
2995 Result = FALSE;
2996 goto Quickie;
2997 }
2998
2999 /* Did we get some other failure? */
3000 if (!NT_SUCCESS(Status))
3001 {
3002 /* If we couldn't check the hashes, assume nefariousness */
3003 DPRINT1("Tampered Blade hashes!\n");
3004 SetLastError(ERROR_ACCESS_DISABLED_WEBBLADE_TAMPER);
3005 Result = FALSE;
3006 goto Quickie;
3007 }
3008 }
3009
3010 /* Now do Winsafer, etc, checks */
3011 Status = BasepIsProcessAllowed((LPWSTR)lpApplicationName);
3012 if (!NT_SUCCESS(Status))
3013 {
3014 /* Fail if we're not allowed to launch the process */
3015 DPRINT1("Process not allowed to launch: %lx\n", Status);
3016 BaseSetLastNTError(Status);
3017 if (SectionHandle)
3018 {
3019 NtClose(SectionHandle);
3020 SectionHandle = NULL;
3021 }
3022 Result = FALSE;
3023 goto Quickie;
3024 }
3025
3026 /* Is a DOS VDM being forced, but we already have a WOW32 instance ready? */
3027 if ((dwCreationFlags & CREATE_FORCEDOS) &&
3028 (BaseStaticServerData->IsWowTaskReady))
3029 {
3030 /* This request can't be satisfied, instead, a separate VDM is needed */
3031 dwCreationFlags &= ~(CREATE_FORCEDOS | CREATE_SHARED_WOW_VDM);
3032 dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
3033
3034 /* Set a failure code, ask for VDM reservation */
3035 Status = STATUS_INVALID_IMAGE_WIN_16;
3036 UseVdmReserve = TRUE;
3037
3038 /* Close the current handle */
3039 NtClose(SectionHandle);
3040 SectionHandle = NULL;
3041
3042 /* Don't query the section later */
3043 QuerySection = FALSE;
3044 }
3045 }
3046
3047 /* Did we already do these checks? */
3048 if (!SkipSaferAndAppCompat)
3049 {
3050 /* Is everything OK so far, OR do we have an non-MZ, non-DOS app? */
3051 if ((NT_SUCCESS(Status)) ||
3052 ((Status == STATUS_INVALID_IMAGE_NOT_MZ) &&
3053 !(BaseIsDosApplication(&PathName, Status))))
3054 {
3055 /* Clear the machine type in case of failure */
3056 ImageMachine = 0;
3057
3058 /* Clean any app compat data that may have accumulated */
3059 BasepFreeAppCompatData(AppCompatData, AppCompatSxsData);
3060 AppCompatData = NULL;
3061 AppCompatSxsData = NULL;
3062
3063 /* Do we have a section? */
3064 if (SectionHandle)
3065 {
3066 /* Have we already queried it? */
3067 if (QuerySection)
3068 {
3069 /* Nothing to do */
3070 AppCompatStatus = STATUS_SUCCESS;
3071 }
3072 else
3073 {
3074 /* Get some information about the executable */
3075 AppCompatStatus = NtQuerySection(SectionHandle,
3076 SectionImageInformation,
3077 &ImageInformation,
3078 sizeof(ImageInformation),
3079 NULL);
3080 }
3081
3082 /* Do we have section information now? */
3083 if (NT_SUCCESS(AppCompatStatus))
3084 {
3085 /* Don't ask for it again, save the machine type */
3086 QuerySection = TRUE;
3087 ImageMachine = ImageInformation.Machine;
3088 }
3089 }
3090
3091 /* Is there a reason/Shim we shouldn't run this application? */
3092 AppCompatStatus = BasepCheckBadapp(FileHandle,
3093 FreeBuffer,
3094 lpEnvironment,
3095 ImageMachine,
3096 &AppCompatData,
3097 &AppCompatDataSize,
3098 &AppCompatSxsData,
3099 &AppCompatSxsDataSize,
3100 &FusionFlags);
3101 if (!NT_SUCCESS(AppCompatStatus))
3102 {
3103 /* This is usually the status we get back */
3104 DPRINT1("App compat launch failure: %lx\n", AppCompatStatus);
3105 if (AppCompatStatus == STATUS_ACCESS_DENIED)
3106 {
3107 /* Convert it to something more Win32-specific */
3108 SetLastError(ERROR_CANCELLED);
3109 }
3110 else
3111 {
3112 /* Some other error */
3113 BaseSetLastNTError(AppCompatStatus);
3114 }
3115
3116 /* Did we have a section? */
3117 if (SectionHandle)
3118 {
3119 /* Clean it up */
3120 NtClose(SectionHandle);
3121 SectionHandle = NULL;
3122 }
3123
3124 /* Fail the call */
3125 Result = FALSE;
3126 goto Quickie;
3127 }
3128 }
3129 }
3130
3131 //ASSERT((dwFusionFlags & ~SXS_APPCOMPACT_FLAG_APP_RUNNING_SAFEMODE) == 0);
3132
3133 /* Have we already done, and do we need to do, SRP (WinSafer) checks? */
3134 if (!(SkipSaferAndAppCompat) &&
3135 ~(dwCreationFlags & CREATE_PRESERVE_CODE_AUTHZ_LEVEL))
3136 {
3137 /* Assume yes */
3138 SaferNeeded = TRUE;
3139 switch (Status)
3140 {
3141 case STATUS_INVALID_IMAGE_NE_FORMAT:
3142 case STATUS_INVALID_IMAGE_PROTECT:
3143 case STATUS_INVALID_IMAGE_WIN_16:
3144 case STATUS_FILE_IS_OFFLINE:
3145 /* For all DOS, 16-bit, OS/2 images, we do*/
3146 break;
3147
3148 case STATUS_INVALID_IMAGE_NOT_MZ:
3149 /* For invalid files, we don't, unless it's a .BAT file */
3150 if (BaseIsDosApplication(&PathName, Status)) break;
3151
3152 default:
3153 /* Any other error codes we also don't */
3154 if (!NT_SUCCESS(Status))
3155 {
3156 SaferNeeded = FALSE;
3157 }
3158
3159 /* But for success, we do */
3160 break;
3161 }
3162
3163 /* Okay, so what did the checks above result in? */
3164 if (SaferNeeded)
3165 {
3166 /* We have to call into the WinSafer library and actually check */
3167 SaferStatus = BasepCheckWinSaferRestrictions(hUserToken,
3168 (LPWSTR)lpApplicationName,
3169 FileHandle,
3170 &InJob,
3171 &TokenHandle,
3172 &JobHandle);
3173 if (SaferStatus == 0xFFFFFFFF)
3174 {
3175 /* Back in 2003, they didn't have an NTSTATUS for this... */
3176 DPRINT1("WinSafer blocking process launch\n");
3177 SetLastError(ERROR_ACCESS_DISABLED_BY_POLICY);
3178 Result = FALSE;
3179 goto Quickie;
3180 }
3181
3182 /* Other status codes are not-Safer related, just convert them */
3183 if (!NT_SUCCESS(SaferStatus))
3184 {
3185 DPRINT1("Error checking WinSafer: %lx\n", SaferStatus);
3186 BaseSetLastNTError(SaferStatus);
3187 Result = FALSE;
3188 goto Quickie;
3189 }
3190 }
3191 }
3192
3193 /* The last step is to figure out why the section object was not created */
3194 switch (Status)
3195 {
3196 case STATUS_INVALID_IMAGE_WIN_16:
3197 {
3198 /* 16-bit binary. Should we use WOW or does the caller force VDM? */
3199 if (!(dwCreationFlags & CREATE_FORCEDOS))
3200 {
3201 /* Remember that we're launching WOW */
3202 IsWowApp = TRUE;
3203
3204 /* Create the VDM environment, it's valid for WOW too */
3205 Result = BaseCreateVDMEnvironment(lpEnvironment,
3206 &VdmAnsiEnv,
3207 &VdmUnicodeEnv);
3208 if (!Result)
3209 {
3210 DPRINT1("VDM environment for WOW app failed\n");
3211 goto Quickie;
3212 }
3213
3214 /* We're going to try this twice, so do a loop */
3215 while (TRUE)
3216 {
3217 /* Pick which kind of WOW mode we want to run in */
3218 VdmBinaryType = (dwCreationFlags &
3219 CREATE_SEPARATE_WOW_VDM) ?
3220 BINARY_TYPE_SEPARATE_WOW : BINARY_TYPE_WOW;
3221
3222 /* Get all the VDM settings and current status */
3223 Status = BaseCheckVDM(VdmBinaryType,
3224 lpApplicationName,
3225 lpCommandLine,
3226 lpCurrentDirectory,
3227 &VdmAnsiEnv,
3228 &CsrMsg[1],
3229 &VdmTask,
3230 dwCreationFlags,
3231 &StartupInfo,
3232 hUserToken);
3233
3234 /* If it worked, no need to try again */
3235 if (NT_SUCCESS(Status)) break;
3236
3237 /* Check if it's disallowed or if it's our second time */
3238 BaseSetLastNTError(Status);
3239 if ((Status == STATUS_VDM_DISALLOWED) ||
3240 (VdmBinaryType == BINARY_TYPE_SEPARATE_WOW) ||
3241 (GetLastError() == ERROR_ACCESS_DENIED))
3242 {
3243 /* Fail the call -- we won't try again */
3244 DPRINT1("VDM message failure for WOW: %lx\n", Status);
3245 Result = FALSE;
3246 goto Quickie;
3247 }
3248
3249 /* Try one more time, but with a separate WOW instance */
3250 dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
3251 }
3252
3253 /* Check which VDM state we're currently in */
3254 switch (CheckVdmMsg->VDMState & (VDM_NOT_LOADED |
3255 VDM_NOT_READY |
3256 VDM_READY))
3257 {
3258 case VDM_NOT_LOADED:
3259 /* VDM is not fully loaded, so not that much to undo */
3260 VdmUndoLevel = VDM_UNDO_PARTIAL;
3261
3262 /* Reset VDM reserve if needed */
3263 if (UseVdmReserve) VdmReserve = 1;
3264
3265 /* Get the required parameters and names for launch */
3266 Result = BaseGetVdmConfigInfo(lpCommandLine,
3267 VdmTask,
3268 VdmBinaryType,
3269 &VdmString,
3270 &VdmReserve);
3271 if (!Result)
3272 {
3273 DPRINT1("VDM Configuration failed for WOW\n");
3274 BaseSetLastNTError(Status);
3275 goto Quickie;
3276 }
3277
3278 /* Update the command-line with the VDM one instead */
3279 lpCommandLine = VdmString.Buffer;
3280 lpApplicationName = NULL;
3281
3282 /* We don't want a console, detachment, nor a window */
3283 dwCreationFlags |= CREATE_NO_WINDOW;
3284 dwCreationFlags &= ~(CREATE_NEW_CONSOLE | DETACHED_PROCESS);
3285
3286 /* Force feedback on */
3287 StartupInfo.dwFlags |= STARTF_FORCEONFEEDBACK;
3288 break;
3289
3290
3291 case VDM_READY:
3292 /* VDM is ready, so we have to undo everything */
3293 VdmUndoLevel = VDM_UNDO_REUSE;
3294
3295 /* Check if CSRSS wants us to wait on VDM */
3296 VdmWaitObject = CheckVdmMsg->WaitObjectForParent;
3297 break;
3298
3299 case VDM_NOT_READY:
3300 /* Something is wrong with VDM, we'll fail the call */
3301 DPRINT1("VDM is not ready for WOW\n");
3302 SetLastError(ERROR_NOT_READY);
3303 Result = FALSE;
3304 goto Quickie;
3305
3306 default:
3307 break;
3308 }
3309
3310 /* Since to get NULL, we allocate from 0x1, account for this */
3311 VdmReserve--;
3312
3313 /* This implies VDM is ready, so skip everything else */
3314 if (VdmWaitObject) goto VdmShortCircuit;
3315
3316 /* Don't inherit handles since we're doing VDM now */
3317 bInheritHandles = FALSE;
3318
3319 /* Had the user passed in environment? If so, destroy it */
3320 if ((lpEnvironment) &&
3321 !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
3322 {
3323 RtlDestroyEnvironment(lpEnvironment);
3324 }
3325
3326 /* We've already done all these checks, don't do them again */
3327 SkipSaferAndAppCompat = TRUE;
3328 goto AppNameRetry;
3329 }
3330
3331 // There is no break here on purpose, so FORCEDOS drops down!
3332 }
3333
3334 case STATUS_INVALID_IMAGE_PROTECT:
3335 case STATUS_INVALID_IMAGE_NOT_MZ:
3336 case STATUS_INVALID_IMAGE_NE_FORMAT:
3337 {
3338 /* We're launching an executable application */
3339 BinarySubType = BINARY_TYPE_EXE;
3340
3341 /* We can drop here from other "cases" above too, so check */
3342 if ((Status == STATUS_INVALID_IMAGE_PROTECT) ||
3343 (Status == STATUS_INVALID_IMAGE_NE_FORMAT) ||
3344 (BinarySubType = BaseIsDosApplication(&PathName, Status)))
3345 {
3346 /* We're launching a DOS application */
3347 VdmBinaryType = BINARY_TYPE_DOS;
3348
3349 /* Based on the caller environment, create a VDM one */
3350 Result = BaseCreateVDMEnvironment(lpEnvironment,
3351 &VdmAnsiEnv,
3352 &VdmUnicodeEnv);
3353 if (!Result)
3354 {
3355 DPRINT1("VDM environment for DOS failed\n");
3356 goto Quickie;
3357 }
3358
3359 /* Check the current state of the VDM subsystem */
3360 Status = BaseCheckVDM(VdmBinaryType | BinarySubType,
3361 lpApplicationName,
3362 lpCommandLine,
3363 lpCurrentDirectory,
3364 &VdmAnsiEnv,
3365 &CsrMsg[1],
3366 &VdmTask,
3367 dwCreationFlags,
3368 &StartupInfo,
3369 NULL);
3370 if (!NT_SUCCESS(Status))
3371 {
3372 /* Failed to inquire about VDM, fail the call */
3373 DPRINT1("VDM message failure for DOS: %lx\n", Status);
3374 BaseSetLastNTError(Status);
3375 Result = FALSE;
3376 goto Quickie;
3377 };
3378
3379 /* Handle possible VDM states */
3380 switch (CheckVdmMsg->VDMState & (VDM_NOT_LOADED |
3381 VDM_NOT_READY |
3382 VDM_READY))
3383 {
3384 case VDM_NOT_LOADED:
3385 /* If VDM is not loaded, we'll do a partial undo */
3386 VdmUndoLevel = VDM_UNDO_PARTIAL;
3387
3388 /* A VDM process can't also be detached, so fail */
3389 if (dwCreationFlags & DETACHED_PROCESS)
3390 {
3391 DPRINT1("Detached process but no VDM, not allowed\n");
3392 SetLastError(ERROR_ACCESS_DENIED);
3393 return FALSE;
3394 }
3395
3396 /* Get the required parameters and names for launch */
3397 Result = BaseGetVdmConfigInfo(lpCommandLine,
3398 VdmTask,
3399 VdmBinaryType,
3400 &VdmString,
3401 &VdmReserve);
3402 if (!Result)
3403 {
3404 DPRINT1("VDM Configuration failed for DOS\n");
3405 BaseSetLastNTError(Status);
3406 goto Quickie;
3407 }
3408
3409 /* Update the command-line to launch VDM instead */
3410 lpCommandLine = VdmString.Buffer;
3411 lpApplicationName = NULL;
3412 break;
3413
3414 case VDM_READY:
3415 /* VDM is ready, so we have to undo everything */
3416 VdmUndoLevel = VDM_UNDO_REUSE;
3417
3418 /* Check if CSRSS wants us to wait on VDM */
3419 VdmWaitObject = CheckVdmMsg->WaitObjectForParent;
3420 break;
3421
3422 case VDM_NOT_READY:
3423 /* Something is wrong with VDM, we'll fail the call */
3424 DPRINT1("VDM is not ready for DOS\n");
3425 SetLastError(ERROR_NOT_READY);
3426 Result = FALSE;
3427 goto Quickie;
3428
3429 default:
3430 break;
3431 }
3432
3433 /* Since to get NULL, we allocate from 0x1, account for this */
3434 VdmReserve--;
3435
3436 /* This implies VDM is ready, so skip everything else */
3437 if (VdmWaitObject) goto VdmShortCircuit;
3438
3439 /* Don't inherit handles since we're doing VDM now */
3440 bInheritHandles = FALSE;
3441
3442 /* Had the user passed in environment? If so, destroy it */
3443 if ((lpEnvironment) &&
3444 !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
3445 {
3446 RtlDestroyEnvironment(lpEnvironment);
3447 }
3448
3449 /* Use our VDM Unicode environment instead */
3450 lpEnvironment = VdmUnicodeEnv.Buffer;
3451 }
3452 else
3453 {
3454 /* It's a batch file, get the extension */
3455 ExtBuffer = &PathName.Buffer[PathName.Length / sizeof(WCHAR) - 4];
3456
3457 /* Make sure the extensions are correct */
3458 if ((PathName.Length < (4 * sizeof(WCHAR))) ||
3459 ((_wcsnicmp(ExtBuffer, L".bat", 4)) &&
3460 (_wcsnicmp(ExtBuffer, L".cmd", 4))))
3461 {
3462 DPRINT1("'%wZ': Invalid EXE, and not a batch or script file\n", &PathName);
3463 SetLastError(ERROR_BAD_EXE_FORMAT);
3464 Result = FALSE;
3465 goto Quickie;
3466 }
3467
3468 /* Check if we need to account for quotes around the path */
3469 CmdQuoteLength = CmdLineIsAppName || HasQuotes;
3470 if (!CmdLineIsAppName)
3471 {
3472 if (HasQuotes) CmdQuoteLength++;
3473 }
3474 else
3475 {
3476 CmdQuoteLength++;
3477 }
3478
3479 /* Calculate the length of the command line */
3480 CmdLineLength = wcslen(lpCommandLine);
3481 CmdLineLength += wcslen(CMD_STRING);
3482 CmdLineLength += CmdQuoteLength + sizeof(ANSI_NULL);
3483 CmdLineLength *= sizeof(WCHAR);
3484
3485 /* Allocate space for the new command line */
3486 AnsiCmdCommand = RtlAllocateHeap(RtlGetProcessHeap(),
3487 0,
3488 CmdLineLength);
3489 if (!AnsiCmdCommand)
3490 {
3491 BaseSetLastNTError(STATUS_NO_MEMORY);
3492 Result = FALSE;
3493 goto Quickie;
3494 }
3495
3496 /* Build it */
3497 wcscpy(AnsiCmdCommand, CMD_STRING);
3498 if ((CmdLineIsAppName) || (HasQuotes))
3499 {
3500 wcscat(AnsiCmdCommand, L"\"");
3501 }
3502 wcscat(AnsiCmdCommand, lpCommandLine);
3503 if ((CmdLineIsAppName) || (HasQuotes))
3504 {
3505 wcscat(AnsiCmdCommand, L"\"");
3506 }
3507
3508 /* Create it as a Unicode String */
3509 RtlInitUnicodeString(&DebuggerString, AnsiCmdCommand);
3510
3511 /* Set the command line to this */
3512 lpCommandLine = DebuggerString.Buffer;
3513 lpApplicationName = NULL;
3514 DPRINT1("Retrying with: %S\n", lpCommandLine);
3515 }
3516
3517 /* We've already done all these checks, don't do them again */
3518 SkipSaferAndAppCompat = TRUE;
3519 goto AppNameRetry;
3520 }
3521
3522 case STATUS_INVALID_IMAGE_WIN_64:
3523 {
3524 /* 64-bit binaries are not allowed to run on 32-bit ReactOS */
3525 DPRINT1("64-bit binary, failing\n");
3526 SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH);
3527 Result = FALSE;
3528 goto Quickie;
3529 }
3530
3531 case STATUS_FILE_IS_OFFLINE:
3532 {
3533 /* Set the correct last error for this */
3534 DPRINT1("File is offline, failing\n");
3535 SetLastError(ERROR_FILE_OFFLINE);
3536 break;
3537 }
3538
3539 default:
3540 {
3541 /* Any other error, convert it to a generic Win32 error */
3542 if (!NT_SUCCESS(Status))
3543 {
3544 DPRINT1("Failed to create section: %lx\n", Status);
3545 SetLastError(ERROR_BAD_EXE_FORMAT);
3546 Result = FALSE;
3547 goto Quickie;
3548 }
3549
3550 /* Otherwise, this must be success */
3551 ASSERT(Status == STATUS_SUCCESS);
3552 break;
3553 }
3554 }
3555
3556 /* Is this not a WOW application, but a WOW32 VDM was requested for it? */
3557 if (!(IsWowApp) && (dwCreationFlags & CREATE_SEPARATE_WOW_VDM))
3558 {
3559 /* Ignore the nonsensical request */
3560 dwCreationFlags &= ~CREATE_SEPARATE_WOW_VDM;
3561 }
3562
3563 /* Did we already check information for the section? */
3564 if (!QuerySection)
3565 {
3566 /* Get some information about the executable */
3567 Status = NtQuerySection(SectionHandle,
3568 SectionImageInformation,
3569 &ImageInformation,
3570 sizeof(ImageInformation),
3571 NULL);
3572 if (!NT_SUCCESS(Status))
3573 {
3574 /* We failed, bail out */
3575 DPRINT1("Section query failed\n");
3576 BaseSetLastNTError(Status);
3577 Result = FALSE;
3578 goto Quickie;
3579 }
3580
3581 /* Don't check this later */
3582 QuerySection = TRUE;
3583 }
3584
3585 /* Check if this was linked as a DLL */
3586 if (ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL)
3587 {
3588 /* These aren't valid images to try to execute! */
3589 DPRINT1("Trying to launch a DLL, failing\n");
3590 SetLastError(ERROR_BAD_EXE_FORMAT);
3591 Result = FALSE;
3592 goto Quickie;
3593 }
3594
3595 /* Don't let callers pass in this flag -- we'll only get it from IFEO */
3596 Flags &= ~PROCESS_CREATE_FLAGS_LARGE_PAGES;
3597
3598 /* Clear the IFEO-missing flag, before we know for sure... */
3599 ParameterFlags &= ~2;
3600
3601 /* If the process is being debugged, only read IFEO if the PEB says so */
3602 if (!(dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) ||
3603 (NtCurrentPeb()->ReadImageFileExecOptions))
3604 {
3605 /* Let's do this! Attempt to open IFEO */
3606 IFEOStatus = LdrOpenImageFileOptionsKey(&PathName, 0, &KeyHandle);
3607 if (!NT_SUCCESS(IFEOStatus))
3608 {
3609 /* We failed, set the flag so we store this in the parameters */
3610 if (IFEOStatus == STATUS_OBJECT_NAME_NOT_FOUND) ParameterFlags |= 2;
3611 }
3612 else
3613 {
3614 /* Was this our first time going through this path? */
3615 if (!DebuggerCmdLine)
3616 {
3617 /* Allocate a buffer for the debugger path */
3618 DebuggerCmdLine = RtlAllocateHeap(RtlGetProcessHeap(),
3619 0,
3620 MAX_PATH * sizeof(WCHAR));
3621 if (!DebuggerCmdLine)
3622 {
3623 /* Close IFEO on failure */
3624 IFEOStatus = NtClose(KeyHandle);
3625 ASSERT(NT_SUCCESS(IFEOStatus));
3626
3627 /* Fail the call */
3628 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3629 Result = FALSE;
3630 goto Quickie;
3631 }
3632 }
3633
3634 /* Now query for the debugger */
3635 IFEOStatus = LdrQueryImageFileKeyOption(KeyHandle,
3636 L"Debugger",
3637 REG_SZ,
3638 DebuggerCmdLine,
3639 MAX_PATH * sizeof(WCHAR),
3640 &ResultSize);
3641 if (!(NT_SUCCESS(IFEOStatus)) ||
3642 (ResultSize < sizeof(WCHAR)) ||
3643 (DebuggerCmdLine[0] == UNICODE_NULL))
3644 {
3645 /* If it's not there, or too small, or invalid, ignore it */
3646 RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine);
3647 DebuggerCmdLine = NULL;
3648 }
3649
3650 /* Also query if we should map with large pages */
3651 IFEOStatus = LdrQueryImageFileKeyOption(KeyHandle,
3652 L"UseLargePages",
3653 REG_DWORD,
3654 &UseLargePages,
3655 sizeof(UseLargePages),
3656 NULL);
3657 if ((NT_SUCCESS(IFEOStatus)) && (UseLargePages))
3658 {
3659 /* Do it! This is the only way this flag can be set */
3660 Flags |= PROCESS_CREATE_FLAGS_LARGE_PAGES;
3661 }
3662
3663 /* We're done with IFEO, can close it now */
3664 IFEOStatus = NtClose(KeyHandle);
3665 ASSERT(NT_SUCCESS(IFEOStatus));
3666 }
3667 }
3668
3669 /* Make sure the image was compiled for this processor */
3670 if ((ImageInformation.Machine < SharedUserData->ImageNumberLow) ||
3671 (ImageInformation.Machine > SharedUserData->ImageNumberHigh))
3672 {
3673 /* It was not -- raise a hard error */
3674 ErrorResponse = ResponseOk;
3675 ErrorParameters[0] = (ULONG_PTR)&PathName;
3676 NtRaiseHardError(STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE,
3677 1,
3678 1,
3679 ErrorParameters,
3680 OptionOk,
3681 &ErrorResponse);
3682 if (Peb->ImageSubsystemMajorVersion <= 3)
3683 {
3684 /* If it's really old, return this error */
3685 SetLastError(ERROR_BAD_EXE_FORMAT);
3686 }
3687 else
3688 {
3689 /* Otherwise, return a more modern error */
3690 SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH);
3691 }
3692
3693 /* Go to the failure path */
3694 DPRINT1("Invalid image architecture: %lx\n", ImageInformation.Machine);
3695 Result = FALSE;
3696 goto Quickie;
3697 }
3698
3699 /* Check if this isn't a Windows image */
3700 if ((ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_GUI) &&
3701 (ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_CUI))
3702 {
3703 /* Get rid of section-related information since we'll retry */
3704 NtClose(SectionHandle);
3705 SectionHandle = NULL;
3706 QuerySection = FALSE;
3707
3708 /* The only other non-Windows image type we support here is POSIX */
3709 if (ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_POSIX_CUI)
3710 {
3711 /* Bail out if it's something else */
3712 SetLastError(ERROR_CHILD_NOT_COMPLETE);
3713 Result = FALSE;
3714 goto Quickie;
3715 }
3716
3717 /* Now build the command-line to have posix launch this image */
3718 Result = BuildSubSysCommandLine(L"POSIX /P ",
3719 lpApplicationName,
3720 lpCommandLine,
3721 &DebuggerString);
3722 if (!Result)
3723 {
3724 /* Bail out if that failed */
3725 DPRINT1("Subsystem command line failed\n");
3726 goto Quickie;
3727 }
3728
3729 /* And re-try launching the process, with the new command-line now */
3730 lpCommandLine = DebuggerString.Buffer;
3731 lpApplicationName = NULL;
3732
3733 /* We've already done all these checks, don't do them again */
3734 SkipSaferAndAppCompat = TRUE;
3735 DPRINT1("Retrying with: %S\n", lpCommandLine);
3736 goto AppNameRetry;
3737 }
3738
3739 /* Was this image built for a version of Windows whose images we can run? */
3740 Result = BasepIsImageVersionOk(ImageInformation.SubSystemMajorVersion,
3741 ImageInformation.SubSystemMinorVersion);
3742 if (!Result)
3743 {
3744 /* It was not, bail out */
3745 DPRINT1("Invalid subsystem version: %hu.%hu\n",
3746 ImageInformation.SubSystemMajorVersion,
3747 ImageInformation.SubSystemMinorVersion);
3748 SetLastError(ERROR_BAD_EXE_FORMAT);
3749 goto Quickie;
3750 }
3751
3752 /* Check if there is a debugger associated with the application */
3753 if (DebuggerCmdLine)
3754 {
3755 /* Get the length of the command line */
3756 n = wcslen(lpCommandLine);
3757 if (!n)
3758 {
3759 /* There's no command line, use the application name instead */
3760 lpCommandLine = (LPWSTR)lpApplicationName;
3761 n = wcslen(lpCommandLine);
3762 }
3763
3764 /* Protect against overflow */
3765 if (n > UNICODE_STRING_MAX_CHARS)
3766 {
3767 BaseSetLastNTError(STATUS_NAME_TOO_LONG);
3768 Result = FALSE;
3769 goto Quickie;
3770 }
3771
3772 /* Now add the length of the debugger command-line */
3773 n += wcslen(DebuggerCmdLine);
3774
3775 /* Again make sure we don't overflow */
3776 if (n > UNICODE_STRING_MAX_CHARS)
3777 {
3778 BaseSetLastNTError(STATUS_NAME_TOO_LONG);
3779 Result = FALSE;
3780 goto Quickie;
3781 }
3782
3783 /* Account for the quotes and space between the two */
3784 n += sizeof("\" \"") - sizeof(ANSI_NULL);
3785
3786 /* Convert to bytes, and make sure we don't overflow */
3787 n *= sizeof(WCHAR);
3788 if (n > UNICODE_STRING_MAX_BYTES)
3789 {
3790 BaseSetLastNTError(STATUS_NAME_TOO_LONG);
3791 Result = FALSE;
3792 goto Quickie;
3793 }
3794
3795 /* Allocate space for the string */
3796 DebuggerString.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, n);
3797 if (!DebuggerString.Buffer)
3798 {
3799 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3800 Result = FALSE;
3801 goto Quickie;
3802 }
3803
3804 /* Set the length */
3805 RtlInitEmptyUnicodeString(&DebuggerString,
3806 DebuggerString.Buffer,
3807 (USHORT)n);
3808
3809 /* Now perform the command line creation */
3810 ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString,
3811 DebuggerCmdLine);
3812 ASSERT(NT_SUCCESS(ImageDbgStatus));
3813 ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString, L" ");
3814 ASSERT(NT_SUCCESS(ImageDbgStatus));
3815 ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString, lpCommandLine);
3816 ASSERT(NT_SUCCESS(ImageDbgStatus));
3817
3818 /* Make sure it all looks nice */
3819 DbgPrint("BASE: Calling debugger with '%wZ'\n", &DebuggerString);
3820
3821 /* Update the command line and application name */
3822 lpCommandLine = DebuggerString.Buffer;
3823 lpApplicationName = NULL;
3824
3825 /* Close all temporary state */
3826 NtClose(SectionHandle);
3827 SectionHandle = NULL;
3828 QuerySection = FALSE;
3829
3830 /* Free all temporary memory */
3831 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
3832 NameBuffer = NULL;
3833 RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer);
3834 FreeBuffer = NULL;
3835 RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine);
3836 DebuggerCmdLine = NULL;
3837 DPRINT1("Retrying with: %S\n", lpCommandLine);
3838 goto AppNameRetry;
3839 }
3840
3841 /* Initialize the process object attributes */
3842 ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
3843 lpProcessAttributes,
3844 NULL);
3845 if ((hUserToken) && (lpProcessAttributes))
3846 {
3847 /* Augment them with information from the user */
3848
3849 LocalProcessAttributes = *lpProcessAttributes;
3850 LocalProcessAttributes.lpSecurityDescriptor = NULL;
3851 ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
3852 &LocalProcessAttributes,
3853 NULL);
3854 }
3855
3856 /* Check if we're going to be debugged */
3857 if (dwCreationFlags & DEBUG_PROCESS)
3858 {
3859 /* Set process flag */
3860 Flags |= PROCESS_CREATE_FLAGS_BREAKAWAY;
3861 }
3862
3863 /* Check if we're going to be debugged */
3864 if (dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS))
3865 {
3866 /* Connect to DbgUi */
3867 Status = DbgUiConnectToDbg();
3868 if (!NT_SUCCESS(Status))
3869 {
3870 DPRINT1("Failed to connect to DbgUI!\n");
3871 BaseSetLastNTError(Status);
3872 Result = FALSE;
3873 goto Quickie;
3874 }
3875
3876 /* Get the debug object */
3877 DebugHandle = DbgUiGetThreadDebugObject();
3878
3879 /* Check if only this process will be debugged */
3880 if (dwCreationFlags & DEBUG_ONLY_THIS_PROCESS)
3881 {
3882 /* Set process flag */
3883 Flags |= PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT;
3884 }
3885 }
3886
3887 /* Set inherit flag */
3888 if (bInheritHandles) Flags |= PROCESS_CREATE_FLAGS_INHERIT_HANDLES;
3889
3890 /* Check if the process should be created with large pages */
3891 HavePrivilege = FALSE;
3892 PrivilegeState = NULL;
3893 if (Flags & PROCESS_CREATE_FLAGS_LARGE_PAGES)
3894 {
3895 /* Acquire the required privilege so that the kernel won't fail the call */
3896 PrivilegeValue = SE_LOCK_MEMORY_PRIVILEGE;
3897 Status = RtlAcquirePrivilege(&PrivilegeValue, 1, 0, &PrivilegeState);
3898 if (NT_SUCCESS(Status))
3899 {
3900 /* Remember to release it later */
3901 HavePrivilege = TRUE;
3902 }
3903 }
3904
3905 /* Save the current TIB value since kernel overwrites it to store PEB */
3906 TibValue = Teb->NtTib.ArbitraryUserPointer;
3907
3908 /* Tell the kernel to create the process */
3909 Status = NtCreateProcessEx(&ProcessHandle,
3910 PROCESS_ALL_ACCESS,
3911 ObjectAttributes,
3912 NtCurrentProcess(),
3913 Flags,
3914 SectionHandle,
3915 DebugHandle,
3916 NULL,
3917 InJob);
3918
3919 /* Load the PEB address from the hacky location where the kernel stores it */
3920 RemotePeb = Teb->NtTib.ArbitraryUserPointer;
3921
3922 /* And restore the old TIB value */
3923 Teb->NtTib.ArbitraryUserPointer = TibValue;
3924
3925 /* Release the large page privilege if we had acquired it */
3926 if (HavePrivilege) RtlReleasePrivilege(PrivilegeState);
3927
3928 /* And now check if the kernel failed to create the process */
3929 if (!NT_SUCCESS(Status))
3930 {
3931 /* Go to failure path */
3932 DPRINT1("Failed to create process: %lx\n", Status);
3933 BaseSetLastNTError(Status);
3934 Result = FALSE;
3935 goto Quickie;
3936 }
3937
3938 /* Check if there is a priority class to set */
3939 if (PriorityClass.PriorityClass)
3940 {
3941 /* Reset current privilege state */
3942 RealTimePrivilegeState = NULL;
3943
3944 /* Is realtime priority being requested? */
3945 if (PriorityClass.PriorityClass == PROCESS_PRIORITY_CLASS_REALTIME)
3946 {
3947 /* Check if the caller has real-time access, and enable it if so */
3948 RealTimePrivilegeState = BasepIsRealtimeAllowed(TRUE);
3949 }
3950
3951 /* Set the new priority class and release the privilege */
3952 Status = NtSetInformationProcess(ProcessHandle,
3953 ProcessPriorityClass,
3954 &PriorityClass,
3955 sizeof(PROCESS_PRIORITY_CLASS));
3956 if (RealTimePrivilegeState) RtlReleasePrivilege(RealTimePrivilegeState);
3957
3958 /* Check if we failed to set the priority class */
3959 if (!NT_SUCCESS(Status))
3960 {
3961 /* Bail out on failure */
3962 DPRINT1("Failed to set priority class: %lx\n", Status);
3963 BaseSetLastNTError(Status);
3964 Result = FALSE;
3965 goto Quickie;
3966 }
3967 }
3968
3969 /* Check if the caller wants the default error mode */
3970 if (dwCreationFlags & CREATE_DEFAULT_ERROR_MODE)
3971 {
3972 /* Set Error Mode to only fail on critical errors */
3973 HardErrorMode = SEM_FAILCRITICALERRORS;
3974 NtSetInformationProcess(ProcessHandle,
3975 ProcessDefaultHardErrorMode,
3976 &HardErrorMode,
3977 sizeof(ULONG));
3978 }
3979
3980 /* Check if this was a VDM binary */
3981 if (VdmBinaryType)
3982 {
3983 /* Update VDM by telling it the process has now been created */
3984 VdmWaitObject = ProcessHandle;
3985 Result = BaseUpdateVDMEntry(VdmEntryUpdateProcess,
3986 &VdmWaitObject,
3987 VdmTask,
3988 VdmBinaryType);
3989
3990 if (!Result)
3991 {
3992 /* Bail out on failure */
3993 DPRINT1("Failed to update VDM with wait object\n");
3994 VdmWaitObject = NULL;
3995 goto Quickie;
3996 }
3997
3998 /* At this point, a failure means VDM has to undo all the state */
3999 VdmUndoLevel |= VDM_UNDO_FULL;
4000 }
4001
4002 /* Check if VDM needed reserved low-memory */
4003 if (VdmReserve)
4004 {
4005 /* Reserve the requested allocation */
4006 Status = NtAllocateVirtualMemory(ProcessHandle,
4007 &BaseAddress,
4008 0,
4009 &VdmReserve,
4010 MEM_RESERVE,
4011 PAGE_EXECUTE_READWRITE);
4012 if (!NT_SUCCESS(Status))
4013 {
4014 /* Bail out on failure */
4015 DPRINT1("Failed to reserve memory for VDM: %lx\n", Status);
4016 BaseSetLastNTError(Status);
4017 Result = FALSE;
4018 goto Quickie;
4019 }
4020 }
4021
4022 /* Check if we've already queried information on the section */
4023 if (!QuerySection)
4024 {
4025 /* We haven't, so get some information about the executable */
4026 Status = NtQuerySection(SectionHandle,
4027 SectionImageInformation,
4028 &ImageInformation,
4029 sizeof(ImageInformation),
4030 NULL);
4031 if (!NT_SUCCESS(Status))
4032 {
4033 /* Bail out on failure */
4034 DPRINT1("Failed to query section: %lx\n", Status);
4035 BaseSetLastNTError(Status);
4036 Result = FALSE;
4037 goto Quickie;
4038 }
4039
4040 /* If we encounter a restart, don't re-query this information again */
4041 QuerySection = TRUE;
4042 }
4043
4044 /* Do we need to apply SxS to this image? */
4045 if (!(ImageInformation.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NO_ISOLATION))
4046 {
4047 /* Too bad, we don't support this yet */
4048 DPRINT1("Image should receive SxS Fusion Isolation\n");
4049 }
4050
4051 /* There's some SxS flag that we need to set if fusion flags have 1 set */
4052 if (FusionFlags & 1) CreateProcessMsg->Sxs.Flags |= 0x10;
4053
4054 /* Check if we have a current directory */
4055 if (lpCurrentDirectory)
4056 {
4057 /* Allocate a buffer so we can keep a Unicode copy */
4058 DPRINT("Current directory: %S\n", lpCurrentDirectory);
4059 CurrentDirectory = RtlAllocateHeap(RtlGetProcessHeap(),
4060 0,
4061 (MAX_PATH * sizeof(WCHAR)) +
4062 sizeof(UNICODE_NULL));
4063 if (!CurrentDirectory)
4064 {
4065 /* Bail out if this failed */
4066 BaseSetLastNTError(STATUS_NO_MEMORY);
4067 Result = FALSE;
4068 goto Quickie;
4069 }
4070
4071 /* Get the length in Unicode */
4072 Length = GetFullPathNameW(lpCurrentDirectory,
4073 MAX_PATH,
4074 CurrentDirectory,
4075 &FilePart);
4076 if (Length > MAX_PATH)
4077 {
4078 /* The directory is too long, so bail out */
4079 SetLastError(ERROR_DIRECTORY);
4080 Result = FALSE;
4081 goto Quickie;
4082 }
4083
4084 /* Make sure the directory is actually valid */
4085 FileAttribs = GetFileAttributesW(CurrentDirectory);
4086 if ((FileAttribs == INVALID_FILE_ATTRIBUTES) ||
4087 !(FileAttribs & FILE_ATTRIBUTE_DIRECTORY))
4088 {
4089 /* It isn't, so bail out */
4090 DPRINT1("Current directory is invalid\n");
4091 SetLastError(ERROR_DIRECTORY);
4092 Result = FALSE;
4093 goto Quickie;
4094 }
4095 }
4096
4097 /* Insert quotes if needed */
4098 if ((QuotesNeeded) || (CmdLineIsAppName))
4099 {
4100 /* Allocate our buffer, plus enough space for quotes and a NULL */
4101 QuotedCmdLine = RtlAllocateHeap(RtlGetProcessHeap(),
4102 0,
4103 (wcslen(lpCommandLine) * sizeof(WCHAR)) +
4104 (2 * sizeof(L'\"') + sizeof(UNICODE_NULL)));
4105 if (QuotedCmdLine)
4106 {
4107 /* Copy the first quote */
4108 wcscpy(QuotedCmdLine, L"\"");
4109
4110 /* Save the current null-character */
4111 if (QuotesNeeded)
4112 {
4113 SaveChar = *NullBuffer;
4114 *NullBuffer = UNICODE_NULL;
4115 }
4116
4117 /* Copy the command line and the final quote */
4118 wcscat(QuotedCmdLine, lpCommandLine);
4119 wcscat(QuotedCmdLine, L"\"");
4120
4121 /* Copy the null-char back */
4122 if (QuotesNeeded)
4123 {
4124 *NullBuffer = SaveChar;
4125 wcscat(QuotedCmdLine, NullBuffer);
4126 }
4127 }
4128 else
4129 {
4130 /* We can't put quotes around the thing, so try it anyway */
4131 if (QuotesNeeded) QuotesNeeded = FALSE;
4132 if (CmdLineIsAppName) CmdLineIsAppName = FALSE;
4133 }
4134 }
4135
4136 /* Use isolation if needed */
4137 if (CreateProcessMsg->Sxs.Flags & 1) ParameterFlags |= 1;
4138
4139 /* Set the new command-line if needed */
4140 if ((QuotesNeeded) || (CmdLineIsAppName)) lpCommandLine = QuotedCmdLine;
4141
4142 /* Call the helper function in charge of RTL_USER_PROCESS_PARAMETERS */
4143 Result = BasePushProcessParameters(ParameterFlags,
4144 ProcessHandle,
4145 RemotePeb,
4146 lpApplicationName,
4147 CurrentDirectory,
4148 lpCommandLine,
4149 lpEnvironment,
4150 &StartupInfo,
4151 dwCreationFlags | NoWindow,
4152 bInheritHandles,
4153 IsWowApp ? IMAGE_SUBSYSTEM_WINDOWS_GUI: 0,
4154 AppCompatData,
4155 AppCompatDataSize);
4156 if (!Result)
4157 {
4158 /* The remote process would have an undefined state, so fail the call */
4159 DPRINT1("BasePushProcessParameters failed\n");
4160 goto Quickie;
4161 }
4162
4163 /* Free the VDM command line string as it's no longer needed */
4164 RtlFreeUnicodeString(&VdmString);
4165 VdmString.Buffer = NULL;
4166
4167 /* Non-VDM console applications usually inherit handles unless specified */
4168 if (!(VdmBinaryType) &&
4169 !(bInheritHandles) &&
4170 !(StartupInfo.dwFlags & STARTF_USESTDHANDLES) &&
4171 !(dwCreationFlags & (CREATE_NO_WINDOW |
4172 CREATE_NEW_CONSOLE |
4173 DETACHED_PROCESS)) &&
4174 (ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_CUI))
4175 {
4176 /* Get the remote parameters */
4177 Status = NtReadVirtualMemory(ProcessHandle,
4178 &RemotePeb->ProcessParameters,
4179 &ProcessParameters,
4180 sizeof(PRTL_USER_PROCESS_PARAMETERS),
4181 NULL);
4182 if (NT_SUCCESS(Status))
4183 {
4184 /* Duplicate standard input unless it's a console handle */
4185 if (!IsConsoleHandle(Peb->ProcessParameters->StandardInput))
4186 {
4187 StuffStdHandle(ProcessHandle,
4188 Peb->ProcessParameters->StandardInput,
4189 &ProcessParameters->StandardInput);
4190 }
4191
4192 /* Duplicate standard output unless it's a console handle */
4193 if (!IsConsoleHandle(Peb->ProcessParameters->StandardOutput))
4194 {
4195 StuffStdHandle(ProcessHandle,
4196 Peb->ProcessParameters->StandardOutput,
4197 &ProcessParameters->StandardOutput);
4198 }
4199
4200 /* Duplicate standard error unless it's a console handle */
4201 if (!IsConsoleHandle(Peb->ProcessParameters->StandardError))
4202 {
4203 StuffStdHandle(ProcessHandle,
4204 Peb->ProcessParameters->StandardError,
4205 &ProcessParameters->StandardError);
4206 }
4207 }
4208 }
4209
4210 /* Create the Thread's Stack */
4211 StackSize = max(256 * 1024, ImageInformation.MaximumStackSize);
4212 Status = BaseCreateStack(ProcessHandle,
4213 ImageInformation.CommittedStackSize,
4214 StackSize,
4215 &InitialTeb);
4216 if (!NT_SUCCESS(Status))
4217 {
4218 DPRINT1("Creating the thread stack failed: %lx\n", Status);
4219 BaseSetLastNTError(Status);
4220 Result = FALSE;
4221 goto Quickie;
4222 }
4223
4224 /* Create the Thread's Context */
4225 BaseInitializeContext(&Context,
4226 Peb,
4227 ImageInformation.TransferAddress,
4228 InitialTeb.StackBase,
4229 0);
4230
4231 /* Convert the thread attributes */
4232 ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
4233 lpThreadAttributes,
4234 NULL);
4235 if ((hUserToken) && (lpThreadAttributes))
4236 {
4237 /* If the caller specified a user token, zero the security descriptor */
4238 LocalThreadAttributes = *lpThreadAttributes;
4239 LocalThreadAttributes.lpSecurityDescriptor = NULL;
4240 ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
4241 &LocalThreadAttributes,
4242 NULL);
4243 }
4244
4245 /* Create the Kernel Thread Object */
4246 Status = NtCreateThread(&ThreadHandle,
4247 THREAD_ALL_ACCESS,
4248 ObjectAttributes,
4249 ProcessHandle,
4250 &ClientId,
4251 &Context,
4252 &InitialTeb,
4253 TRUE);
4254 if (!NT_SUCCESS(Status))
4255 {
4256 /* A process is not allowed to exist without a main thread, so fail */
4257 DPRINT1("Creating the main thread failed: %lx\n", Status);
4258 BaseSetLastNTError(Status);
4259 Result = FALSE;
4260 goto Quickie;
4261 }
4262
4263 /* Begin filling out the CSRSS message, first with our IDs and handles */
4264 CreateProcessMsg->ProcessHandle = ProcessHandle;
4265 CreateProcessMsg->ThreadHandle = ThreadHandle;
4266 CreateProcessMsg->ClientId = ClientId;
4267
4268 /* Write the remote PEB address and clear it locally, we no longer use it */
4269 CreateProcessMsg->PebAddressNative = RemotePeb;
4270 CreateProcessMsg->PebAddressWow64 = (ULONG)RemotePeb;
4271 RemotePeb = NULL;
4272
4273 /* Now check what kind of architecture this image was made for */
4274 switch (ImageInformation.Machine)
4275 {
4276 /* IA32, IA64 and AMD64 are supported in Server 2003 */
4277 case IMAGE_FILE_MACHINE_I386:
4278 CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
4279 break;
4280 case IMAGE_FILE_MACHINE_IA64:
4281 CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_IA64;
4282 break;
4283 case IMAGE_FILE_MACHINE_AMD64:
4284 CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64;
4285 break;
4286
4287 /* Anything else results in image unknown -- but no failure */
4288 default:
4289 DbgPrint("kernel32: No mapping for ImageInformation.Machine == %04x\n",
4290 ImageInformation.Machine);
4291 CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_UNKNOWN;
4292 break;
4293 }
4294
4295 /* Write the input creation flags except any debugger-related flags */
4296 CreateProcessMsg->CreationFlags = dwCreationFlags &
4297 ~(DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS);
4298
4299 /* CSRSS needs to know if this is a GUI app or not */
4300 if ((ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_GUI) ||
4301 (IsWowApp))
4302 {
4303 /*
4304 * For GUI apps we turn on the 2nd bit. This allow CSRSS server dlls
4305 * (basesrv in particular) to know whether or not this is a GUI or a
4306 * TUI application.
4307 */
4308 AddToHandle(CreateProcessMsg->ProcessHandle, 2);
4309
4310 /* Also check if the parent is also a GUI process */
4311 NtHeaders = RtlImageNtHeader(GetModuleHandle(NULL));
4312 if ((NtHeaders) &&
4313 (NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI))
4314 {
4315 /* Let it know that it should display the hourglass mouse cursor */
4316 AddToHandle(CreateProcessMsg->ProcessHandle, 1);
4317 }
4318 }
4319
4320 /* For all apps, if this flag is on, the hourglass mouse cursor is shown */
4321 if (StartupInfo.dwFlags & STARTF_FORCEONFEEDBACK)
4322 {
4323 AddToHandle(CreateProcessMsg->ProcessHandle, 1);
4324 }
4325
4326 /* Likewise, the opposite holds as well */
4327 if (StartupInfo.dwFlags & STARTF_FORCEOFFFEEDBACK)
4328 {
4329 RemoveFromHandle(CreateProcessMsg->ProcessHandle, 1);
4330 }
4331
4332 /* Also store which kind of VDM app (if any) this is */
4333 CreateProcessMsg->VdmBinaryType = VdmBinaryType;
4334
4335 /* And if it really is a VDM app... */
4336 if (VdmBinaryType)
4337 {
4338 /* Store the task ID and VDM console handle */
4339 CreateProcessMsg->hVDM = VdmTask ? 0 : Peb->ProcessParameters->ConsoleHandle;
4340 CreateProcessMsg->VdmTask = VdmTask;
4341 }
4342 else if (VdmReserve)
4343 {
4344 /* Extended VDM, set a flag */
4345 CreateProcessMsg->VdmBinaryType |= BINARY_TYPE_WOW_EX;
4346 }
4347
4348 /* Check if there's side-by-side assembly data associated with the process */
4349 if (CreateProcessMsg->Sxs.Flags)
4350 {
4351 /* This should not happen in ReactOS yet */
4352 DPRINT1("This is an SxS Message -- should not happen yet\n");
4353 BaseSetLastNTError(STATUS_NOT_IMPLEMENTED);
4354 NtTerminateProcess(ProcessHandle, STATUS_NOT_IMPLEMENTED);
4355 Result = FALSE;
4356 goto Quickie;
4357 }
4358
4359 /* We are finally ready to call CSRSS to tell it about our new process! */
4360 CsrClientCallServer((PCSR_API_MESSAGE)&CsrMsg[0],
4361 CaptureBuffer,
4362 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX,
4363 BasepCreateProcess),
4364 sizeof(*CreateProcessMsg));
4365
4366 /* CSRSS has returned, free the capture buffer now if we had one */
4367 if (CaptureBuffer)
4368 {
4369 CsrFreeCaptureBuffer(CaptureBuffer);
4370 CaptureBuffer = NULL;
4371 }
4372
4373 /* Check if CSRSS failed to accept ownership of the new Windows process */
4374 if (!NT_SUCCESS(CsrMsg[0].Status))
4375 {
4376 /* Terminate the process and enter failure path with the CSRSS status */
4377 DPRINT1("Failed to tell csrss about new process\n");
4378 BaseSetLastNTError(CsrMsg[0].Status);
4379 NtTerminateProcess(ProcessHandle, CsrMsg[0].Status);
4380 Result = FALSE;
4381 goto Quickie;
4382 }
4383
4384 /* Check if we have a token due to Authz/Safer, not passed by the user */
4385 if ((TokenHandle) && !(hUserToken))
4386 {
4387 /* Replace the process and/or thread token with the one from Safer */
4388 Status = BasepReplaceProcessThreadTokens(TokenHandle,
4389 ProcessHandle,
4390 ThreadHandle);
4391 if (!NT_SUCCESS(Status))
4392 {
4393 /* If this failed, kill the process and enter the failure path */
4394 DPRINT1("Failed to update process token: %lx\n", Status);
4395 NtTerminateProcess(ProcessHandle, Status);
4396 BaseSetLastNTError(Status);
4397 Result = FALSE;
4398 goto Quickie;
4399 }
4400 }
4401
4402 /* Check if a job was associated with this process */
4403 if (JobHandle)
4404 {
4405 /* Bind the process and job together now */
4406 Status = NtAssignProcessToJobObject(JobHandle, ProcessHandle);
4407 if (!NT_SUCCESS(Status))
4408 {
4409 /* Kill the process and enter the failure path if binding failed */
4410 DPRINT1("Failed to assign process to job: %lx\n", Status);
4411 NtTerminateProcess(ProcessHandle, STATUS_ACCESS_DENIED);
4412 BaseSetLastNTError(Status);
4413 Result = FALSE;
4414 goto Quickie;
4415 }
4416 }
4417
4418 /* Finally, resume the thread to actually get the process started */
4419 if (!(dwCreationFlags & CREATE_SUSPENDED))
4420 {
4421 NtResumeThread(ThreadHandle, &ResumeCount);
4422 }
4423
4424 VdmShortCircuit:
4425 /* We made it this far, meaning we have a fully created process and thread */
4426 Result = TRUE;
4427
4428 /* Anyone doing a VDM undo should now undo everything, since we are done */
4429 if (VdmUndoLevel) VdmUndoLevel |= VDM_UNDO_COMPLETED;
4430
4431 /* Having a VDM wait object implies this must be a VDM process */
4432 if (VdmWaitObject)
4433 {
4434 /* Check if it's a 16-bit separate WOW process */
4435 if (VdmBinaryType == BINARY_TYPE_SEPARATE_WOW)
4436 {
4437 /* OR-in the special flag to indicate this, and return to caller */
4438 AddToHandle(VdmWaitObject, 2);
4439 lpProcessInformation->hProcess = VdmWaitObject;
4440
4441 /* Check if this was a re-used VDM */
4442 if (VdmUndoLevel & VDM_UNDO_REUSE)
4443 {
4444 /* No Client ID should be returned in this case */
4445 ClientId.UniqueProcess = 0;
4446 ClientId.UniqueThread = 0;
4447 }
4448 }
4449 else
4450 {
4451 /* OR-in the special flag to indicate this is not a separate VDM */
4452 AddToHandle(VdmWaitObject, 1);
4453
4454 /* Return handle to the caller */
4455 lpProcessInformation->hProcess = VdmWaitObject;
4456 }
4457
4458 /* Close the original process handle, since it's not needed for VDM */
4459 if (ProcessHandle) NtClose(ProcessHandle);
4460 }
4461 else
4462 {
4463 /* This is a regular process, so return the real process handle */
4464 lpProcessInformation->hProcess = ProcessHandle;
4465 }
4466
4467 /* Return the rest of the process information based on what we have so far */
4468 lpProcessInformation->hThread = ThreadHandle;
4469 lpProcessInformation->dwProcessId = HandleToUlong(ClientId.UniqueProcess);
4470 lpProcessInformation->dwThreadId = HandleToUlong(ClientId.UniqueThread);
4471
4472 /* NULL these out here so we know to treat this as a success scenario */
4473 ProcessHandle = NULL;
4474 ThreadHandle = NULL;
4475
4476 Quickie:
4477 /* Free the debugger command line if one was allocated */
4478 if (DebuggerCmdLine) RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine);
4479
4480 /* Check if an SxS full path as queried */
4481 if (PathBuffer)
4482 {
4483 /* Reinitialize the executable path */
4484 RtlInitEmptyUnicodeString(&SxsWin32ExePath, NULL, 0);
4485 SxsWin32ExePath.Length = 0;
4486
4487 /* Free the path buffer */
4488 RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer);
4489 }
4490
4491 #if _SXS_SUPPORT_ENABLED_
4492 /* Check if this was a non-VDM process */
4493 if (!VdmBinaryType)
4494 {
4495 /* Then it must've had SxS data, so close the handles used for it */
4496 BasepSxsCloseHandles(&Handles);
4497 BasepSxsCloseHandles(&FileHandles);
4498
4499 /* Check if we built SxS byte buffers for this create process request */
4500 if (SxsConglomeratedBuffer)
4501 {
4502 /* Loop all of them */
4503 for (i = 0; i < 5; i++)
4504 {
4505 /* Check if this one was allocated */
4506 ThisBuffer = SxsStaticBuffers[i];
4507 if (ThisBuffer)
4508 {
4509 /* Get the underlying RTL_BUFFER structure */
4510 ByteBuffer = &ThisBuffer->ByteBuffer;
4511 if ((ThisBuffer != (PVOID)-8) && (ByteBuffer->Buffer))
4512 {
4513 /* Check if it was dynamic */
4514 if (ByteBuffer->Buffer != ByteBuffer->StaticBuffer)
4515 {
4516 /* Free it from the heap */
4517 FreeString.Buffer = (PWCHAR)ByteBuffer->Buffer;
4518 RtlFreeUnicodeString(&FreeString);
4519 }
4520
4521 /* Reset the buffer to its static data */
4522 ByteBuffer->Buffer = ByteBuffer->StaticBuffer;
4523 ByteBuffer->Size = ByteBuffer->StaticSize;
4524 }
4525
4526 /* Reset the string to the static buffer */
4527 RtlInitEmptyUnicodeString(&ThisBuffer->String,
4528 (PWCHAR)ByteBuffer->StaticBuffer,
4529 ByteBuffer->StaticSize);
4530 if (ThisBuffer->String.Buffer)
4531 {
4532 /* Also NULL-terminate it */
4533 *ThisBuffer->String.Buffer = UNICODE_NULL;
4534 }
4535 }
4536 }
4537 }
4538 }
4539 #endif
4540 /* Check if an environment was passed in */
4541 if ((lpEnvironment) && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
4542 {
4543 /* Destroy it */
4544 RtlDestroyEnvironment(lpEnvironment);
4545
4546 /* If this was the VDM environment too, clear that as well */
4547 if (VdmUnicodeEnv.Buffer == lpEnvironment) VdmUnicodeEnv.Buffer = NULL;
4548 lpEnvironment = NULL;
4549 }
4550
4551 /* Unconditionally free all the name parsing buffers we always allocate */
4552 RtlFreeHeap(RtlGetProcessHeap(), 0, QuotedCmdLine);
4553 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
4554 RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentDirectory);
4555 RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer);
4556
4557 /* Close open file/section handles */
4558 if (FileHandle) NtClose(FileHandle);
4559 if (SectionHandle) NtClose(SectionHandle);
4560
4561 /* If we have a thread handle, this was a failure path */
4562 if (ThreadHandle)
4563 {
4564 /* So kill the process and close the thread handle */
4565 NtTerminateProcess(ProcessHandle, 0);
4566 NtClose(ThreadHandle);
4567 }
4568
4569 /* If we have a process handle, this was a failure path, so close it */
4570 if (ProcessHandle) NtClose(ProcessHandle);
4571
4572 /* Thread/process handles, if any, are now processed. Now close this one. */
4573 if (JobHandle) NtClose(JobHandle);
4574
4575 /* Check if we had created a token */
4576 if (TokenHandle)
4577 {
4578 /* And if the user asked for one */
4579 if (hUserToken)
4580 {
4581 /* Then return it */
4582 *hNewToken = TokenHandle;
4583 }
4584 else
4585 {
4586 /* User didn't want it, so we used it temporarily -- close it */
4587 NtClose(TokenHandle);
4588 }
4589 }
4590
4591 /* Free any temporary app compatibility data, it's no longer needed */
4592 BasepFreeAppCompatData(AppCompatData, AppCompatSxsData);
4593
4594 /* Free a few strings. The API takes care of these possibly being NULL */
4595 RtlFreeUnicodeString(&VdmString);
4596 RtlFreeUnicodeString(&DebuggerString);
4597
4598 /* Check if we had built any sort of VDM environment */
4599 if ((VdmAnsiEnv.Buffer) || (VdmUnicodeEnv.Buffer))
4600 {
4601 /* Free it */
4602 BaseDestroyVDMEnvironment(&VdmAnsiEnv, &VdmUnicodeEnv);
4603 }
4604
4605 /* Check if this was any kind of VDM application that we ended up creating */
4606 if ((VdmUndoLevel) && (!(VdmUndoLevel & VDM_UNDO_COMPLETED)))
4607 {
4608 /* Send an undo */
4609 BaseUpdateVDMEntry(VdmEntryUndo,
4610 (PHANDLE)&VdmTask,
4611 VdmUndoLevel,
4612 VdmBinaryType);
4613
4614 /* And close whatever VDM handle we were using for notifications */
4615 if (VdmWaitObject) NtClose(VdmWaitObject);
4616 }
4617
4618 /* Check if we ended up here with an allocated search path, and free it */
4619 if (SearchPath) RtlFreeHeap(RtlGetProcessHeap(), 0, SearchPath);
4620
4621 /* Finally, return the API's result */
4622 return Result;
4623 }
4624
4625 /*
4626 * @implemented
4627 */
4628 BOOL
4629 WINAPI
4630 DECLSPEC_HOTPATCH
4631 CreateProcessW(LPCWSTR lpApplicationName,
4632 LPWSTR lpCommandLine,
4633 LPSECURITY_ATTRIBUTES lpProcessAttributes,
4634 LPSECURITY_ATTRIBUTES lpThreadAttributes,
4635 BOOL bInheritHandles,
4636 DWORD dwCreationFlags,
4637 LPVOID lpEnvironment,
4638 LPCWSTR lpCurrentDirectory,
4639 LPSTARTUPINFOW lpStartupInfo,
4640 LPPROCESS_INFORMATION lpProcessInformation)
4641 {
4642 /* Call the internal (but exported) version */
4643 return CreateProcessInternalW(NULL,
4644 lpApplicationName,
4645 lpCommandLine,
4646 lpProcessAttributes,
4647 lpThreadAttributes,
4648 bInheritHandles,
4649 dwCreationFlags,
4650 lpEnvironment,
4651 lpCurrentDirectory,
4652 lpStartupInfo,
4653 lpProcessInformation,
4654 NULL);
4655 }
4656
4657 /*
4658 * @implemented
4659 */
4660 BOOL
4661 WINAPI
4662 CreateProcessInternalA(HANDLE hToken,
4663 LPCSTR lpApplicationName,
4664 LPSTR lpCommandLine,
4665 LPSECURITY_ATTRIBUTES lpProcessAttributes,
4666 LPSECURITY_ATTRIBUTES lpThreadAttributes,
4667 BOOL bInheritHandles,
4668 DWORD dwCreationFlags,
4669 LPVOID lpEnvironment,
4670 LPCSTR lpCurrentDirectory,
4671 LPSTARTUPINFOA lpStartupInfo,
4672 LPPROCESS_INFORMATION lpProcessInformation,
4673 PHANDLE hNewToken)
4674 {
4675 UNICODE_STRING CommandLine;
4676 UNICODE_STRING ApplicationName;
4677 UNICODE_STRING CurrentDirectory;
4678 BOOL bRetVal;
4679 STARTUPINFOW StartupInfo;
4680
4681 DPRINT("dwCreationFlags %x, lpEnvironment %p, lpCurrentDirectory %p, "
4682 "lpStartupInfo %p, lpProcessInformation %p\n",
4683 dwCreationFlags, lpEnvironment, lpCurrentDirectory,
4684 lpStartupInfo, lpProcessInformation);
4685
4686 /* Copy Startup Info */
4687 RtlMoveMemory(&StartupInfo, lpStartupInfo, sizeof(*lpStartupInfo));
4688
4689 /* Initialize all strings to nothing */
4690 CommandLine.Buffer = NULL;
4691 ApplicationName.Buffer = NULL;
4692 CurrentDirectory.Buffer = NULL;
4693 StartupInfo.lpDesktop = NULL;
4694 StartupInfo.lpReserved = NULL;
4695 StartupInfo.lpTitle = NULL;
4696
4697 /* Convert the Command line */
4698 if (lpCommandLine)
4699 {
4700 Basep8BitStringToDynamicUnicodeString(&CommandLine,
4701 lpCommandLine);
4702 }
4703
4704 /* Convert the Name and Directory */
4705 if (lpApplicationName)
4706 {
4707 Basep8BitStringToDynamicUnicodeString(&ApplicationName,
4708 lpApplicationName);
4709 }
4710 if (lpCurrentDirectory)
4711 {
4712 Basep8BitStringToDynamicUnicodeString(&CurrentDirectory,
4713 lpCurrentDirectory);
4714 }
4715
4716 /* Now convert Startup Strings */
4717 if (lpStartupInfo->lpReserved)
4718 {
4719 BasepAnsiStringToHeapUnicodeString(lpStartupInfo->lpReserved,
4720 &StartupInfo.lpReserved);
4721 }
4722 if (lpStartupInfo->lpDesktop)
4723 {
4724 BasepAnsiStringToHeapUnicodeString(lpStartupInfo->lpDesktop,
4725 &StartupInfo.lpDesktop);
4726 }
4727 if (lpStartupInfo->lpTitle)
4728 {
4729 BasepAnsiStringToHeapUnicodeString(lpStartupInfo->lpTitle,
4730 &StartupInfo.lpTitle);
4731 }
4732
4733 /* Call the Unicode function */
4734 bRetVal = CreateProcessInternalW(hToken,
4735 ApplicationName.Buffer,
4736 CommandLine.Buffer,
4737 lpProcessAttributes,
4738 lpThreadAttributes,
4739 bInheritHandles,
4740 dwCreationFlags,
4741 lpEnvironment,
4742 CurrentDirectory.Buffer,
4743 &StartupInfo,
4744 lpProcessInformation,
4745 hNewToken);
4746
4747 /* Clean up */
4748 RtlFreeUnicodeString(&ApplicationName);
4749 RtlFreeUnicodeString(&CommandLine);
4750 RtlFreeUnicodeString(&CurrentDirectory);
4751 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo.lpDesktop);
4752 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo.lpReserved);
4753 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo.lpTitle);
4754
4755 /* Return what Unicode did */
4756 return bRetVal;
4757 }
4758
4759 /*
4760 * FUNCTION: The CreateProcess function creates a new process and its
4761 * primary thread. The new process executes the specified executable file
4762 * ARGUMENTS:
4763 *
4764 * lpApplicationName = Pointer to name of executable module
4765 * lpCommandLine = Pointer to command line string
4766 * lpProcessAttributes = Process security attributes
4767 * lpThreadAttributes = Thread security attributes
4768 * bInheritHandles = Handle inheritance flag
4769 * dwCreationFlags = Creation flags
4770 * lpEnvironment = Pointer to new environment block
4771 * lpCurrentDirectory = Pointer to current directory name
4772 * lpStartupInfo = Pointer to startup info
4773 * lpProcessInformation = Pointer to process information
4774 *
4775 * @implemented
4776 */
4777 BOOL
4778 WINAPI
4779 DECLSPEC_HOTPATCH
4780 CreateProcessA(LPCSTR lpApplicationName,
4781 LPSTR lpCommandLine,
4782 LPSECURITY_ATTRIBUTES lpProcessAttributes,
4783 LPSECURITY_ATTRIBUTES lpThreadAttributes,
4784 BOOL bInheritHandles,
4785 DWORD dwCreationFlags,
4786 LPVOID lpEnvironment,
4787 LPCSTR lpCurrentDirectory,
4788 LPSTARTUPINFOA lpStartupInfo,
4789 LPPROCESS_INFORMATION lpProcessInformation)
4790 {
4791 /* Call the internal (but exported) version */
4792 return CreateProcessInternalA(NULL,
4793 lpApplicationName,
4794 lpCommandLine,
4795 lpProcessAttributes,
4796 lpThreadAttributes,
4797 bInheritHandles,
4798 dwCreationFlags,
4799 lpEnvironment,
4800 lpCurrentDirectory,
4801 lpStartupInfo,
4802 lpProcessInformation,
4803 NULL);
4804 }
4805
4806 /*
4807 * @implemented
4808 */
4809 UINT
4810 WINAPI
4811 DECLSPEC_HOTPATCH
4812 WinExec(LPCSTR lpCmdLine,
4813 UINT uCmdShow)
4814 {
4815 STARTUPINFOA StartupInfo;
4816 PROCESS_INFORMATION ProcessInformation;
4817 DWORD dosErr;
4818
4819 RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
4820 StartupInfo.cb = sizeof(STARTUPINFOA);
4821 StartupInfo.wShowWindow = (WORD)uCmdShow;
4822 StartupInfo.dwFlags = 0;
4823
4824 if (!CreateProcessA(NULL,
4825 (PVOID)lpCmdLine,
4826 NULL,
4827 NULL,
4828 FALSE,
4829 0,
4830 NULL,
4831 NULL,
4832 &StartupInfo,
4833 &ProcessInformation))
4834 {
4835 dosErr = GetLastError();
4836 return dosErr < 32 ? dosErr : ERROR_BAD_FORMAT;
4837 }
4838
4839 if (NULL != UserWaitForInputIdleRoutine)
4840 {
4841 UserWaitForInputIdleRoutine(ProcessInformation.hProcess,
4842 10000);
4843 }
4844
4845 NtClose(ProcessInformation.hProcess);
4846 NtClose(ProcessInformation.hThread);
4847
4848 return 33; /* Something bigger than 31 means success. */
4849 }
4850
4851 /* EOF */