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