c8e370d382c9b523b50dedc043dfb691c309ca5f
[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, Status1, 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 = TRUE; // HACK for making .bat/.cmd launch working again.
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 /* Cleanup in preparation for failure or success */
2927 RtlReleaseRelativeName(&SxsWin32RelativePath);
2928 if (!NT_SUCCESS(Status))
2929 {
2930 /* Failure path, try to understand why */
2931 DPRINT1("Open file failed: %lx\n", Status);
2932 if (RtlIsDosDeviceName_U(lpApplicationName))
2933 {
2934 /* If a device is being executed, return this special error code */
2935 SetLastError(ERROR_BAD_DEVICE);
2936 Result = FALSE;
2937 goto Quickie;
2938 }
2939 else
2940 {
2941 /* Otherwise return the converted NT error code */
2942 BaseSetLastNTError(Status);
2943 Result = FALSE;
2944 goto Quickie;
2945 }
2946 }
2947
2948 /* Did the caller specify a desktop? */
2949 if (!StartupInfo.lpDesktop)
2950 {
2951 /* Use the one from the current process */
2952 StartupInfo.lpDesktop = Peb->ProcessParameters->DesktopInfo.Buffer;
2953 }
2954
2955 /* Create a section for this file */
2956 Status = NtCreateSection(&SectionHandle,
2957 SECTION_ALL_ACCESS,
2958 NULL,
2959 NULL,
2960 PAGE_EXECUTE,
2961 SEC_IMAGE,
2962 FileHandle);
2963 DPRINT("Section status: %lx\n", Status);
2964 if (NT_SUCCESS(Status))
2965 {
2966 /* Are we running on Windows Embedded, Datacenter, Blade or Starter? */
2967 if (SharedUserData->SuiteMask & (VER_SUITE_EMBEDDEDNT |
2968 VER_SUITE_DATACENTER |
2969 VER_SUITE_PERSONAL |
2970 VER_SUITE_BLADE))
2971 {
2972 /* These SKUs do not allow running certain applications */
2973 Status = BasepCheckWebBladeHashes(FileHandle);
2974 if (Status == STATUS_ACCESS_DENIED)
2975 {
2976 /* And this is one of them! */
2977 DPRINT1("Invalid Blade hashes!\n");
2978 SetLastError(ERROR_ACCESS_DISABLED_WEBBLADE);
2979 Result = FALSE;
2980 goto Quickie;
2981 }
2982
2983 /* Did we get some other failure? */
2984 if (!NT_SUCCESS(Status))
2985 {
2986 /* If we couldn't check the hashes, assume nefariousness */
2987 DPRINT1("Tampered Blade hashes!\n");
2988 SetLastError(ERROR_ACCESS_DISABLED_WEBBLADE_TAMPER);
2989 Result = FALSE;
2990 goto Quickie;
2991 }
2992 }
2993
2994 /* Now do Winsafer, etc, checks */
2995 Status = BasepIsProcessAllowed((LPWSTR)lpApplicationName);
2996 if (!NT_SUCCESS(Status))
2997 {
2998 /* Fail if we're not allowed to launch the process */
2999 DPRINT1("Process not allowed to launch: %lx\n", Status);
3000 BaseSetLastNTError(Status);
3001 if (SectionHandle)
3002 {
3003 NtClose(SectionHandle);
3004 SectionHandle = NULL;
3005 }
3006 Result = FALSE;
3007 goto Quickie;
3008 }
3009
3010 /* Is a DOS VDM being forced, but we already have a WOW32 instance ready? */
3011 if ((dwCreationFlags & CREATE_FORCEDOS) &&
3012 (BaseStaticServerData->IsWowTaskReady))
3013 {
3014 /* This request can't be satisfied, instead, a separate VDM is needed */
3015 dwCreationFlags &= ~(CREATE_FORCEDOS | CREATE_SHARED_WOW_VDM);
3016 dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
3017
3018 /* Set a failure code, ask for VDM reservation */
3019 Status = STATUS_INVALID_IMAGE_WIN_16;
3020 UseVdmReserve = TRUE;
3021
3022 /* Close the current handle */
3023 NtClose(SectionHandle);
3024 SectionHandle = NULL;
3025
3026 /* Don't query the section later */
3027 QuerySection = FALSE;
3028 }
3029 }
3030
3031 /* Did we already do these checks? */
3032 if (!SkipSaferAndAppCompat)
3033 {
3034 /* Is everything OK so far, OR do we have an non-MZ, non-DOS app? */
3035 if ((NT_SUCCESS(Status)) ||
3036 ((Status == STATUS_INVALID_IMAGE_NOT_MZ) &&
3037 !(BaseIsDosApplication(&PathName, Status))))
3038 {
3039 /* Clear the machine type in case of failure */
3040 ImageMachine = 0;
3041
3042 /* Clean any app compat data that may have accumulated */
3043 BasepFreeAppCompatData(AppCompatData, AppCompatSxsData);
3044 AppCompatData = NULL;
3045 AppCompatSxsData = NULL;
3046
3047 /* Do we have a section? */
3048 if (SectionHandle)
3049 {
3050 /* Have we already queried it? */
3051 if (QuerySection)
3052 {
3053 /* Nothing to do */
3054 Status = STATUS_SUCCESS;
3055 }
3056 else
3057 {
3058 /* Get some information about the executable */
3059 Status = NtQuerySection(SectionHandle,
3060 SectionImageInformation,
3061 &ImageInformation,
3062 sizeof(ImageInformation),
3063 NULL);
3064 }
3065
3066 /* Do we have section information now? */
3067 if (NT_SUCCESS(Status))
3068 {
3069 /* Don't ask for it again, save the machine type */
3070 QuerySection = TRUE;
3071 ImageMachine = ImageInformation.Machine;
3072 }
3073 }
3074
3075 /* Is there a reason/Shim we shouldn't run this application? */
3076 Status = BasepCheckBadapp(FileHandle,
3077 FreeBuffer,
3078 lpEnvironment,
3079 ImageMachine,
3080 &AppCompatData,
3081 &AppCompatDataSize,
3082 &AppCompatSxsData,
3083 &AppCompatSxsDataSize,
3084 &FusionFlags);
3085 if (!NT_SUCCESS(Status))
3086 {
3087 /* This is usually the status we get back */
3088 DPRINT1("App compat launch failure: %lx\n", Status);
3089 if (Status == STATUS_ACCESS_DENIED)
3090 {
3091 /* Convert it to something more Win32-specific */
3092 SetLastError(ERROR_CANCELLED);
3093 }
3094 else
3095 {
3096 /* Some other error */
3097 BaseSetLastNTError(Status);
3098 }
3099
3100 /* Did we have a section? */
3101 if (SectionHandle)
3102 {
3103 /* Clean it up */
3104 NtClose(SectionHandle);
3105 SectionHandle = NULL;
3106 }
3107
3108 /* Fail the call */
3109 Result = FALSE;
3110 goto Quickie;
3111 }
3112 }
3113 }
3114
3115 //ASSERT((dwFusionFlags & ~SXS_APPCOMPACT_FLAG_APP_RUNNING_SAFEMODE) == 0);
3116
3117 /* Have we already done, and do we need to do, SRP (WinSafer) checks? */
3118 if (!(SkipSaferAndAppCompat) &&
3119 ~(dwCreationFlags & CREATE_PRESERVE_CODE_AUTHZ_LEVEL))
3120 {
3121 /* Assume yes */
3122 SaferNeeded = TRUE;
3123 switch (Status)
3124 {
3125 case STATUS_INVALID_IMAGE_NE_FORMAT:
3126 case STATUS_INVALID_IMAGE_PROTECT:
3127 case STATUS_INVALID_IMAGE_WIN_16:
3128 case STATUS_FILE_IS_OFFLINE:
3129 /* For all DOS, 16-bit, OS/2 images, we do*/
3130 break;
3131
3132 case STATUS_INVALID_IMAGE_NOT_MZ:
3133 /* For invalid files, we don't, unless it's a .BAT file */
3134 if (BaseIsDosApplication(&PathName, Status)) break;
3135
3136 default:
3137 /* Any other error codes we also don't */
3138 if (!NT_SUCCESS(Status))
3139 {
3140 SaferNeeded = FALSE;
3141 }
3142
3143 /* But for success, we do */
3144 break;
3145 }
3146
3147 /* Okay, so what did the checks above result in? */
3148 if (SaferNeeded)
3149 {
3150 /* We have to call into the WinSafer library and actually check */
3151 Status = BasepCheckWinSaferRestrictions(hUserToken,
3152 (LPWSTR)lpApplicationName,
3153 FileHandle,
3154 &InJob,
3155 &TokenHandle,
3156 &JobHandle);
3157 if (Status == 0xFFFFFFFF)
3158 {
3159 /* Back in 2003, they didn't have an NTSTATUS for this... */
3160 DPRINT1("WinSafer blocking process launch\n");
3161 SetLastError(ERROR_ACCESS_DISABLED_BY_POLICY);
3162 Result = FALSE;
3163 goto Quickie;
3164 }
3165
3166 /* Other status codes are not-Safer related, just convert them */
3167 if (!NT_SUCCESS(Status))
3168 {
3169 DPRINT1("Error checking WinSafer: %lx\n", Status);
3170 BaseSetLastNTError(Status);
3171 Result = FALSE;
3172 goto Quickie;
3173 }
3174 }
3175 }
3176
3177 /* The last step is to figure out why the section object was not created */
3178 switch (Status)
3179 {
3180 case STATUS_INVALID_IMAGE_WIN_16:
3181 {
3182 /* 16-bit binary. Should we use WOW or does the caller force VDM? */
3183 if (!(dwCreationFlags & CREATE_FORCEDOS))
3184 {
3185 /* Remember that we're launching WOW */
3186 IsWowApp = TRUE;
3187
3188 /* Create the VDM environment, it's valid for WOW too */
3189 Result = BaseCreateVDMEnvironment(lpEnvironment,
3190 &VdmAnsiEnv,
3191 &VdmUnicodeEnv);
3192 if (!Result)
3193 {
3194 DPRINT1("VDM environment for WOW app failed\n");
3195 goto Quickie;
3196 }
3197
3198 /* We're going to try this twice, so do a loop */
3199 while (TRUE)
3200 {
3201 /* Pick which kind of WOW mode we want to run in */
3202 VdmBinaryType = (dwCreationFlags &
3203 CREATE_SEPARATE_WOW_VDM) ?
3204 BINARY_TYPE_SEPARATE_WOW : BINARY_TYPE_WOW;
3205
3206 /* Get all the VDM settings and current status */
3207 Status = BaseCheckVDM(VdmBinaryType,
3208 lpApplicationName,
3209 lpCommandLine,
3210 lpCurrentDirectory,
3211 &VdmAnsiEnv,
3212 &CsrMsg[1],
3213 &VdmTask,
3214 dwCreationFlags,
3215 &StartupInfo,
3216 hUserToken);
3217
3218 /* If it worked, no need to try again */
3219 if (NT_SUCCESS(Status)) break;
3220
3221 /* Check if it's disallowed or if it's our second time */
3222 BaseSetLastNTError(Status);
3223 if ((Status == STATUS_VDM_DISALLOWED) ||
3224 (VdmBinaryType == BINARY_TYPE_SEPARATE_WOW) ||
3225 (GetLastError() == ERROR_ACCESS_DENIED))
3226 {
3227 /* Fail the call -- we won't try again */
3228 DPRINT1("VDM message failure for WOW: %lx\n", Status);
3229 Result = FALSE;
3230 goto Quickie;
3231 }
3232
3233 /* Try one more time, but with a separate WOW instance */
3234 dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
3235 }
3236
3237 /* Check which VDM state we're currently in */
3238 switch (CheckVdmMsg->VDMState & (VDM_NOT_LOADED |
3239 VDM_NOT_READY |
3240 VDM_READY))
3241 {
3242 case VDM_NOT_LOADED:
3243 /* VDM is not fully loaded, so not that much to undo */
3244 VdmUndoLevel = VDM_UNDO_PARTIAL;
3245
3246 /* Reset VDM reserve if needed */
3247 if (UseVdmReserve) VdmReserve = 1;
3248
3249 /* Get the required parameters and names for launch */
3250 Result = BaseGetVdmConfigInfo(lpCommandLine,
3251 VdmTask,
3252 VdmBinaryType,
3253 &VdmString,
3254 &VdmReserve);
3255 if (!Result)
3256 {
3257 DPRINT1("VDM Configuration failed for WOW\n");
3258 BaseSetLastNTError(Status);
3259 goto Quickie;
3260 }
3261
3262 /* Update the command-line with the VDM one instead */
3263 lpCommandLine = VdmString.Buffer;
3264 lpApplicationName = NULL;
3265
3266 /* We don't want a console, detachment, nor a window */
3267 dwCreationFlags |= CREATE_NO_WINDOW;
3268 dwCreationFlags &= ~(CREATE_NEW_CONSOLE | DETACHED_PROCESS);
3269
3270 /* Force feedback on */
3271 StartupInfo.dwFlags |= STARTF_FORCEONFEEDBACK;
3272 break;
3273
3274
3275 case VDM_READY:
3276 /* VDM is ready, so we have to undo everything */
3277 VdmUndoLevel = VDM_UNDO_REUSE;
3278
3279 /* Check if CSRSS wants us to wait on VDM */
3280 VdmWaitObject = CheckVdmMsg->WaitObjectForParent;
3281 break;
3282
3283 case VDM_NOT_READY:
3284 /* Something is wrong with VDM, we'll fail the call */
3285 DPRINT1("VDM is not ready for WOW\n");
3286 SetLastError(ERROR_NOT_READY);
3287 Result = FALSE;
3288 goto Quickie;
3289
3290 default:
3291 break;
3292 }
3293
3294 /* Since to get NULL, we allocate from 0x1, account for this */
3295 VdmReserve--;
3296
3297 /* This implies VDM is ready, so skip everything else */
3298 if (VdmWaitObject) goto VdmShortCircuit;
3299
3300 /* Don't inherit handles since we're doing VDM now */
3301 bInheritHandles = FALSE;
3302
3303 /* Had the user passed in environment? If so, destroy it */
3304 if ((lpEnvironment) &&
3305 !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
3306 {
3307 RtlDestroyEnvironment(lpEnvironment);
3308 }
3309
3310 /* We've already done all these checks, don't do them again */
3311 SkipSaferAndAppCompat = TRUE;
3312 goto AppNameRetry;
3313 }
3314
3315 // There is no break here on purpose, so FORCEDOS drops down!
3316 }
3317
3318 case STATUS_INVALID_IMAGE_PROTECT:
3319 case STATUS_INVALID_IMAGE_NOT_MZ:
3320 case STATUS_INVALID_IMAGE_NE_FORMAT:
3321 {
3322 /* We're launching an executable application */
3323 BinarySubType = BINARY_TYPE_EXE;
3324
3325 /* We can drop here from other "cases" above too, so check */
3326 if ((Status == STATUS_INVALID_IMAGE_PROTECT) ||
3327 (Status == STATUS_INVALID_IMAGE_NE_FORMAT) ||
3328 (BinarySubType = BaseIsDosApplication(&PathName, Status)))
3329 {
3330 /* We're launching a DOS application */
3331 VdmBinaryType = BINARY_TYPE_DOS;
3332
3333 /* Based on the caller environment, create a VDM one */
3334 Result = BaseCreateVDMEnvironment(lpEnvironment,
3335 &VdmAnsiEnv,
3336 &VdmUnicodeEnv);
3337 if (!Result)
3338 {
3339 DPRINT1("VDM environment for DOS failed\n");
3340 goto Quickie;
3341 }
3342
3343 /* Check the current state of the VDM subsystem */
3344 Status = BaseCheckVDM(VdmBinaryType | BinarySubType,
3345 lpApplicationName,
3346 lpCommandLine,
3347 lpCurrentDirectory,
3348 &VdmAnsiEnv,
3349 &CsrMsg[1],
3350 &VdmTask,
3351 dwCreationFlags,
3352 &StartupInfo,
3353 NULL);
3354 if (!NT_SUCCESS(Status))
3355 {
3356 /* Failed to inquire about VDM, fail the call */
3357 DPRINT1("VDM message failure for DOS: %lx\n", Status);
3358 BaseSetLastNTError(Status);
3359 Result = FALSE;
3360 goto Quickie;
3361 };
3362
3363 /* Handle possible VDM states */
3364 switch (CheckVdmMsg->VDMState & (VDM_NOT_LOADED |
3365 VDM_NOT_READY |
3366 VDM_READY))
3367 {
3368 case VDM_NOT_LOADED:
3369 /* If VDM is not loaded, we'll do a partial undo */
3370 VdmUndoLevel = VDM_UNDO_PARTIAL;
3371
3372 /* A VDM process can't also be detached, so fail */
3373 if (dwCreationFlags & DETACHED_PROCESS)
3374 {
3375 DPRINT1("Detached process but no VDM, not allowed\n");
3376 SetLastError(ERROR_ACCESS_DENIED);
3377 return FALSE;
3378 }
3379
3380 /* Get the required parameters and names for launch */
3381 Result = BaseGetVdmConfigInfo(lpCommandLine,
3382 VdmTask,
3383 VdmBinaryType,
3384 &VdmString,
3385 &VdmReserve);
3386 if (!Result)
3387 {
3388 DPRINT1("VDM Configuration failed for DOS\n");
3389 BaseSetLastNTError(Status);
3390 goto Quickie;
3391 }
3392
3393 /* Update the command-line to launch VDM instead */
3394 lpCommandLine = VdmString.Buffer;
3395 lpApplicationName = NULL;
3396 break;
3397
3398 case VDM_READY:
3399 /* VDM is ready, so we have to undo everything */
3400 VdmUndoLevel = VDM_UNDO_REUSE;
3401
3402 /* Check if CSRSS wants us to wait on VDM */
3403 VdmWaitObject = CheckVdmMsg->WaitObjectForParent;
3404 break;
3405
3406 case VDM_NOT_READY:
3407 /* Something is wrong with VDM, we'll fail the call */
3408 DPRINT1("VDM is not ready for DOS\n");
3409 SetLastError(ERROR_NOT_READY);
3410 Result = FALSE;
3411 goto Quickie;
3412
3413 default:
3414 break;
3415 }
3416
3417 /* Since to get NULL, we allocate from 0x1, account for this */
3418 VdmReserve--;
3419
3420 /* This implies VDM is ready, so skip everything else */
3421 if (VdmWaitObject) goto VdmShortCircuit;
3422
3423 /* Don't inherit handles since we're doing VDM now */
3424 bInheritHandles = FALSE;
3425
3426 /* Had the user passed in environment? If so, destroy it */
3427 if ((lpEnvironment) &&
3428 !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
3429 {
3430 RtlDestroyEnvironment(lpEnvironment);
3431 }
3432
3433 /* Use our VDM Unicode environment instead */
3434 lpEnvironment = VdmUnicodeEnv.Buffer;
3435 }
3436 else
3437 {
3438 /* It's a batch file, get the extension */
3439 ExtBuffer = &PathName.Buffer[PathName.Length / sizeof(WCHAR) - 4];
3440
3441 /* Make sure the extensions are correct */
3442 if ((PathName.Length < (4 * sizeof(WCHAR))) ||
3443 ((_wcsnicmp(ExtBuffer, L".bat", 4)) &&
3444 (_wcsnicmp(ExtBuffer, L".cmd", 4))))
3445 {
3446 DPRINT1("'%wZ': Invalid EXE, and not a batch or script file\n", &PathName);
3447 SetLastError(ERROR_BAD_EXE_FORMAT);
3448 Result = FALSE;
3449 goto Quickie;
3450 }
3451
3452 /* Check if we need to account for quotes around the path */
3453 CmdQuoteLength = CmdLineIsAppName || HasQuotes;
3454 if (!CmdLineIsAppName)
3455 {
3456 if (HasQuotes) CmdQuoteLength++;
3457 }
3458 else
3459 {
3460 CmdQuoteLength++;
3461 }
3462
3463 /* Calculate the length of the command line */
3464 CmdLineLength = wcslen(lpCommandLine);
3465 CmdLineLength += wcslen(CMD_STRING);
3466 CmdLineLength += CmdQuoteLength + sizeof(ANSI_NULL);
3467 CmdLineLength *= sizeof(WCHAR);
3468
3469 /* Allocate space for the new command line */
3470 AnsiCmdCommand = RtlAllocateHeap(RtlGetProcessHeap(),
3471 0,
3472 CmdLineLength);
3473 if (!AnsiCmdCommand)
3474 {
3475 BaseSetLastNTError(STATUS_NO_MEMORY);
3476 Result = FALSE;
3477 goto Quickie;
3478 }
3479
3480 /* Build it */
3481 wcscpy(AnsiCmdCommand, CMD_STRING);
3482 if ((CmdLineIsAppName) || (HasQuotes))
3483 {
3484 wcscat(AnsiCmdCommand, L"\"");
3485 }
3486 wcscat(AnsiCmdCommand, lpCommandLine);
3487 if ((CmdLineIsAppName) || (HasQuotes))
3488 {
3489 wcscat(AnsiCmdCommand, L"\"");
3490 }
3491
3492 /* Create it as a Unicode String */
3493 RtlInitUnicodeString(&DebuggerString, AnsiCmdCommand);
3494
3495 /* Set the command line to this */
3496 lpCommandLine = DebuggerString.Buffer;
3497 lpApplicationName = NULL;
3498 DPRINT1("Retrying with: %S\n", lpCommandLine);
3499 }
3500
3501 /* We've already done all these checks, don't do them again */
3502 SkipSaferAndAppCompat = TRUE;
3503 goto AppNameRetry;
3504 }
3505
3506 case STATUS_INVALID_IMAGE_WIN_64:
3507 {
3508 /* 64-bit binaries are not allowed to run on 32-bit ReactOS */
3509 DPRINT1("64-bit binary, failing\n");
3510 SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH);
3511 Result = FALSE;
3512 goto Quickie;
3513 }
3514
3515 case STATUS_FILE_IS_OFFLINE:
3516 {
3517 /* Set the correct last error for this */
3518 DPRINT1("File is offline, failing\n");
3519 SetLastError(ERROR_FILE_OFFLINE);
3520 break;
3521 }
3522
3523 default:
3524 {
3525 /* Any other error, convert it to a generic Win32 error */
3526 if (!NT_SUCCESS(Status))
3527 {
3528 DPRINT1("Failed to create section: %lx\n", Status);
3529 SetLastError(ERROR_BAD_EXE_FORMAT);
3530 Result = FALSE;
3531 goto Quickie;
3532 }
3533
3534 /* Otherwise, this must be success */
3535 ASSERT(Status == STATUS_SUCCESS);
3536 break;
3537 }
3538 }
3539
3540 /* Is this not a WOW application, but a WOW32 VDM was requested for it? */
3541 if (!(IsWowApp) && (dwCreationFlags & CREATE_SEPARATE_WOW_VDM))
3542 {
3543 /* Ignore the nonsensical request */
3544 dwCreationFlags &= ~CREATE_SEPARATE_WOW_VDM;
3545 }
3546
3547 /* Did we already check information for the section? */
3548 if (!QuerySection)
3549 {
3550 /* Get some information about the executable */
3551 Status = NtQuerySection(SectionHandle,
3552 SectionImageInformation,
3553 &ImageInformation,
3554 sizeof(ImageInformation),
3555 NULL);
3556 if (!NT_SUCCESS(Status))
3557 {
3558 /* We failed, bail out */
3559 DPRINT1("Section query failed\n");
3560 BaseSetLastNTError(Status);
3561 Result = FALSE;
3562 goto Quickie;
3563 }
3564
3565 /* Don't check this later */
3566 QuerySection = TRUE;
3567 }
3568
3569 /* Check if this was linked as a DLL */
3570 if (ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL)
3571 {
3572 /* These aren't valid images to try to execute! */
3573 DPRINT1("Trying to launch a DLL, failing\n");
3574 SetLastError(ERROR_BAD_EXE_FORMAT);
3575 Result = FALSE;
3576 goto Quickie;
3577 }
3578
3579 /* Don't let callers pass in this flag -- we'll only get it from IFRO */
3580 Flags &= ~PROCESS_CREATE_FLAGS_LARGE_PAGES;
3581
3582 /* Clear the IFEO-missing flag, before we know for sure... */
3583 ParameterFlags &= ~2;
3584
3585 /* If the process is being debugged, only read IFEO if the PEB says so */
3586 if (!(dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) ||
3587 (NtCurrentPeb()->ReadImageFileExecOptions))
3588 {
3589 /* Let's do this! Attempt to open IFEO */
3590 Status1 = LdrOpenImageFileOptionsKey(&PathName, 0, &KeyHandle);
3591 if (!NT_SUCCESS(Status1))
3592 {
3593 /* We failed, set the flag so we store this in the parameters */
3594 if (Status1 == STATUS_OBJECT_NAME_NOT_FOUND) ParameterFlags |= 2;
3595 }
3596 else
3597 {
3598 /* Was this our first time going through this path? */
3599 if (!DebuggerCmdLine)
3600 {
3601 /* Allocate a buffer for the debugger path */
3602 DebuggerCmdLine = RtlAllocateHeap(RtlGetProcessHeap(),
3603 0,
3604 MAX_PATH * sizeof(WCHAR));
3605 if (!DebuggerCmdLine)
3606 {
3607 /* Close IFEO on failure */
3608 Status1 = NtClose(KeyHandle);
3609 ASSERT(NT_SUCCESS(Status1));
3610
3611 /* Fail the call */
3612 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3613 Result = FALSE;
3614 goto Quickie;
3615 }
3616 }
3617
3618 /* Now query for the debugger */
3619 Status1 = LdrQueryImageFileKeyOption(KeyHandle,
3620 L"Debugger",
3621 REG_SZ,
3622 DebuggerCmdLine,
3623 MAX_PATH * sizeof(WCHAR),
3624 &ResultSize);
3625 if (!(NT_SUCCESS(Status1)) ||
3626 (ResultSize < sizeof(WCHAR)) ||
3627 (DebuggerCmdLine[0] == UNICODE_NULL))
3628 {
3629 /* If it's not there, or too small, or invalid, ignore it */
3630 RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine);
3631 DebuggerCmdLine = NULL;
3632 }
3633
3634 /* Also query if we should map with large pages */
3635 Status1 = LdrQueryImageFileKeyOption(KeyHandle,
3636 L"UseLargePages",
3637 REG_DWORD,
3638 &UseLargePages,
3639 sizeof(UseLargePages),
3640 NULL);
3641 if ((NT_SUCCESS(Status1)) && (UseLargePages))
3642 {
3643 /* Do it! This is the only way this flag can be set */
3644 Flags |= PROCESS_CREATE_FLAGS_LARGE_PAGES;
3645 }
3646
3647 /* We're done with IFEO, can close it now */
3648 Status1 = NtClose(KeyHandle);
3649 ASSERT(NT_SUCCESS(Status1));
3650 }
3651 }
3652
3653 /* Make sure the image was compiled for this processor */
3654 if ((ImageInformation.Machine < SharedUserData->ImageNumberLow) ||
3655 (ImageInformation.Machine > SharedUserData->ImageNumberHigh))
3656 {
3657 /* It was not -- raise a hard error */
3658 ErrorResponse = ResponseOk;
3659 ErrorParameters[0] = (ULONG_PTR)&PathName;
3660 NtRaiseHardError(STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE,
3661 1,
3662 1,
3663 ErrorParameters,
3664 OptionOk,
3665 &ErrorResponse);
3666 if (Peb->ImageSubsystemMajorVersion <= 3)
3667 {
3668 /* If it's really old, return this error */
3669 SetLastError(ERROR_BAD_EXE_FORMAT);
3670 }
3671 else
3672 {
3673 /* Otherwise, return a more modern error */
3674 SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH);
3675 }
3676
3677 /* Go to the failure path */
3678 DPRINT1("Invalid image architecture: %lx\n", ImageInformation.Machine);
3679 Result = FALSE;
3680 goto Quickie;
3681 }
3682
3683 /* Check if this isn't a Windows image */
3684 if ((ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_GUI) &&
3685 (ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_CUI))
3686 {
3687 /* Get rid of section-related information since we'll retry */
3688 NtClose(SectionHandle);
3689 SectionHandle = NULL;
3690 QuerySection = FALSE;
3691
3692 /* The only other non-Windows image type we support here is POSIX */
3693 if (ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_POSIX_CUI)
3694 {
3695 /* Bail out if it's something else */
3696 SetLastError(ERROR_CHILD_NOT_COMPLETE);
3697 Result = FALSE;
3698 goto Quickie;
3699 }
3700
3701 /* Now build the command-line to have posix launch this image */
3702 Result = BuildSubSysCommandLine(L"POSIX /P ",
3703 lpApplicationName,
3704 lpCommandLine,
3705 &DebuggerString);
3706 if (!Result)
3707 {
3708 /* Bail out if that failed */
3709 DPRINT1("Subsystem command line failed\n");
3710 goto Quickie;
3711 }
3712
3713 /* And re-try launching the process, with the new command-line now */
3714 lpCommandLine = DebuggerString.Buffer;
3715 lpApplicationName = NULL;
3716
3717 /* We've already done all these checks, don't do them again */
3718 SkipSaferAndAppCompat = TRUE;
3719 DPRINT1("Retrying with: %S\n", lpCommandLine);
3720 goto AppNameRetry;
3721 }
3722
3723 /* Was this image built for a version of Windows whose images we can run? */
3724 Result = BasepIsImageVersionOk(ImageInformation.SubSystemMajorVersion,
3725 ImageInformation.SubSystemMinorVersion);
3726 if (!Result)
3727 {
3728 /* It was not, bail out */
3729 DPRINT1("Invalid subsystem version: %hu.%hu\n",
3730 ImageInformation.SubSystemMajorVersion,
3731 ImageInformation.SubSystemMinorVersion);
3732 SetLastError(ERROR_BAD_EXE_FORMAT);
3733 goto Quickie;
3734 }
3735
3736 /* Check if there is a debugger associated with the application */
3737 if (DebuggerCmdLine)
3738 {
3739 /* Get the length of the command line */
3740 n = wcslen(lpCommandLine);
3741 if (!n)
3742 {
3743 /* There's no command line, use the application name instead */
3744 lpCommandLine = (LPWSTR)lpApplicationName;
3745 n = wcslen(lpCommandLine);
3746 }
3747
3748 /* Protect against overflow */
3749 if (n > UNICODE_STRING_MAX_CHARS)
3750 {
3751 BaseSetLastNTError(STATUS_NAME_TOO_LONG);
3752 Result = FALSE;
3753 goto Quickie;
3754 }
3755
3756 /* Now add the length of the debugger command-line */
3757 n += wcslen(DebuggerCmdLine);
3758
3759 /* Again make sure we don't overflow */
3760 if (n > UNICODE_STRING_MAX_CHARS)
3761 {
3762 BaseSetLastNTError(STATUS_NAME_TOO_LONG);
3763 Result = FALSE;
3764 goto Quickie;
3765 }
3766
3767 /* Account for the quotes and space between the two */
3768 n += sizeof("\" \"") - sizeof(ANSI_NULL);
3769
3770 /* Convert to bytes, and make sure we don't overflow */
3771 n *= sizeof(WCHAR);
3772 if (n > UNICODE_STRING_MAX_BYTES)
3773 {
3774 BaseSetLastNTError(STATUS_NAME_TOO_LONG);
3775 Result = FALSE;
3776 goto Quickie;
3777 }
3778
3779 /* Allocate space for the string */
3780 DebuggerString.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, n);
3781 if (!DebuggerString.Buffer)
3782 {
3783 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3784 Result = FALSE;
3785 goto Quickie;
3786 }
3787
3788 /* Set the length */
3789 RtlInitEmptyUnicodeString(&DebuggerString,
3790 DebuggerString.Buffer,
3791 (USHORT)n);
3792
3793 /* Now perform the command line creation */
3794 ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString,
3795 DebuggerCmdLine);
3796 ASSERT(NT_SUCCESS(ImageDbgStatus));
3797 ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString, L" ");
3798 ASSERT(NT_SUCCESS(ImageDbgStatus));
3799 ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString, lpCommandLine);
3800 ASSERT(NT_SUCCESS(ImageDbgStatus));
3801
3802 /* Make sure it all looks nice */
3803 DbgPrint("BASE: Calling debugger with '%wZ'\n", &DebuggerString);
3804
3805 /* Update the command line and application name */
3806 lpCommandLine = DebuggerString.Buffer;
3807 lpApplicationName = NULL;
3808
3809 /* Close all temporary state */
3810 NtClose(SectionHandle);
3811 SectionHandle = NULL;
3812 QuerySection = FALSE;
3813
3814 /* Free all temporary memory */
3815 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
3816 NameBuffer = NULL;
3817 RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer);
3818 FreeBuffer = NULL;
3819 RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine);
3820 DebuggerCmdLine = NULL;
3821 DPRINT1("Retrying with: %S\n", lpCommandLine);
3822 goto AppNameRetry;
3823 }
3824
3825 /* Initialize the process object attributes */
3826 ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
3827 lpProcessAttributes,
3828 NULL);
3829 if ((hUserToken) && (lpProcessAttributes))
3830 {
3831 /* Augment them with information from the user */
3832
3833 LocalProcessAttributes = *lpProcessAttributes;
3834 LocalProcessAttributes.lpSecurityDescriptor = NULL;
3835 ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
3836 &LocalProcessAttributes,
3837 NULL);
3838 }
3839
3840 /* Check if we're going to be debugged */
3841 if (dwCreationFlags & DEBUG_PROCESS)
3842 {
3843 /* Set process flag */
3844 Flags |= PROCESS_CREATE_FLAGS_BREAKAWAY;
3845 }
3846
3847 /* Check if we're going to be debugged */
3848 if (dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS))
3849 {