b6eadb093273b11cb296a073506256becf163b83
[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: lib/kernel32/proc/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 NTSTATUS Status;
503 BASE_API_MESSAGE ApiMessage;
504 PBASE_CREATE_THREAD CreateThreadRequest = &ApiMessage.Data.CreateThreadRequest;
505
506 DPRINT("BasepNotifyCsrOfThread: Thread: %p, Handle %p\n",
507 ClientId->UniqueThread, ThreadHandle);
508
509 /* Fill out the request */
510 CreateThreadRequest->ClientId = *ClientId;
511 CreateThreadRequest->ThreadHandle = ThreadHandle;
512
513 /* Call CSR */
514 Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
515 NULL,
516 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepCreateThread),
517 sizeof(BASE_CREATE_THREAD));
518 if (!NT_SUCCESS(Status))
519 {
520 DPRINT1("Failed to tell CSRSS about new thread: %lx\n", Status);
521 return Status;
522 }
523
524 /* Return Success */
525 return STATUS_SUCCESS;
526 }
527
528 BOOLEAN
529 WINAPI
530 BasePushProcessParameters(IN ULONG ParameterFlags,
531 IN HANDLE ProcessHandle,
532 IN PPEB RemotePeb,
533 IN LPCWSTR ApplicationPathName,
534 IN LPWSTR lpCurrentDirectory,
535 IN LPWSTR lpCommandLine,
536 IN LPVOID lpEnvironment,
537 IN LPSTARTUPINFOW StartupInfo,
538 IN DWORD CreationFlags,
539 IN BOOL InheritHandles,
540 IN ULONG ImageSubsystem,
541 IN PVOID AppCompatData,
542 IN ULONG AppCompatDataSize)
543 {
544 WCHAR FullPath[MAX_PATH + 5];
545 PWCHAR Remaining, DllPathString, ScanChar;
546 PRTL_USER_PROCESS_PARAMETERS ProcessParameters, RemoteParameters;
547 PVOID RemoteAppCompatData;
548 UNICODE_STRING DllPath, ImageName, CommandLine, CurrentDirectory;
549 UNICODE_STRING Desktop, Shell, Runtime, Title;
550 NTSTATUS Status;
551 ULONG EnviroSize;
552 SIZE_T Size;
553 BOOLEAN HavePebLock = FALSE, Result;
554 PPEB Peb = NtCurrentPeb();
555
556 /* Get the full path name */
557 Size = GetFullPathNameW(ApplicationPathName,
558 MAX_PATH + 4,
559 FullPath,
560 &Remaining);
561 if ((Size) && (Size <= (MAX_PATH + 4)))
562 {
563 /* Get the DLL Path */
564 DllPathString = BaseComputeProcessDllPath(FullPath, lpEnvironment);
565 if (!DllPathString)
566 {
567 /* Fail */
568 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
569 return FALSE;
570 }
571
572 /* Initialize Strings */
573 RtlInitUnicodeString(&DllPath, DllPathString);
574 RtlInitUnicodeString(&ImageName, FullPath);
575 }
576 else
577 {
578 /* Couldn't get the path name. Just take the original path */
579 DllPathString = BaseComputeProcessDllPath((LPWSTR)ApplicationPathName,
580 lpEnvironment);
581 if (!DllPathString)
582 {
583 /* Fail */
584 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
585 return FALSE;
586 }
587
588 /* Initialize Strings */
589 RtlInitUnicodeString(&DllPath, DllPathString);
590 RtlInitUnicodeString(&ImageName, ApplicationPathName);
591 }
592
593 /* Initialize Strings */
594 RtlInitUnicodeString(&CommandLine, lpCommandLine);
595 RtlInitUnicodeString(&CurrentDirectory, lpCurrentDirectory);
596
597 /* Initialize more Strings from the Startup Info */
598 if (StartupInfo->lpDesktop)
599 {
600 RtlInitUnicodeString(&Desktop, StartupInfo->lpDesktop);
601 }
602 else
603 {
604 RtlInitUnicodeString(&Desktop, L"");
605 }
606 if (StartupInfo->lpReserved)
607 {
608 RtlInitUnicodeString(&Shell, StartupInfo->lpReserved);
609 }
610 else
611 {
612 RtlInitUnicodeString(&Shell, L"");
613 }
614 if (StartupInfo->lpTitle)
615 {
616 RtlInitUnicodeString(&Title, StartupInfo->lpTitle);
617 }
618 else
619 {
620 RtlInitUnicodeString(&Title, ApplicationPathName);
621 }
622
623 /* This one is special because the length can differ */
624 Runtime.Buffer = (LPWSTR)StartupInfo->lpReserved2;
625 Runtime.MaximumLength = Runtime.Length = StartupInfo->cbReserved2;
626
627 /* Enforce no app compat data if the pointer was NULL */
628 if (!AppCompatData) AppCompatDataSize = 0;
629
630 /* Create the Parameter Block */
631 ProcessParameters = NULL;
632 DPRINT("ImageName: '%wZ'\n", &ImageName);
633 DPRINT("DllPath : '%wZ'\n", &DllPath);
634 DPRINT("CurDir : '%wZ'\n", &CurrentDirectory);
635 DPRINT("CmdLine : '%wZ'\n", &CommandLine);
636 DPRINT("Title : '%wZ'\n", &Title);
637 DPRINT("Desktop : '%wZ'\n", &Desktop);
638 DPRINT("Shell : '%wZ'\n", &Shell);
639 DPRINT("Runtime : '%wZ'\n", &Runtime);
640 Status = RtlCreateProcessParameters(&ProcessParameters,
641 &ImageName,
642 &DllPath,
643 lpCurrentDirectory ?
644 &CurrentDirectory : NULL,
645 &CommandLine,
646 lpEnvironment,
647 &Title,
648 &Desktop,
649 &Shell,
650 &Runtime);
651 if (!NT_SUCCESS(Status)) goto FailPath;
652
653 /* Clear the current directory handle if not inheriting */
654 if (!InheritHandles) ProcessParameters->CurrentDirectory.Handle = NULL;
655
656 /* Check if the user passed in an environment */
657 if (lpEnvironment)
658 {
659 /* We should've made it part of the parameters block, enforce this */
660 ASSERT(ProcessParameters->Environment == lpEnvironment);
661 lpEnvironment = ProcessParameters->Environment;
662 }
663 else
664 {
665 /* The user did not, so use the one from the current PEB */
666 HavePebLock = TRUE;
667 RtlAcquirePebLock();
668 lpEnvironment = Peb->ProcessParameters->Environment;
669 }
670
671 /* Save pointer and start lookup */
672 ScanChar = lpEnvironment;
673 if (lpEnvironment)
674 {
675 /* Find the environment size */
676 while ((ScanChar[0]) || (ScanChar[1])) ++ScanChar;
677 ScanChar += (2 * sizeof(UNICODE_NULL));
678 EnviroSize = (ULONG_PTR)ScanChar - (ULONG_PTR)lpEnvironment;
679
680 /* Allocate and Initialize new Environment Block */
681 Size = EnviroSize;
682 ProcessParameters->Environment = NULL;
683 Status = NtAllocateVirtualMemory(ProcessHandle,
684 (PVOID*)&ProcessParameters->Environment,
685 0,
686 &Size,
687 MEM_COMMIT,
688 PAGE_READWRITE);
689 if (!NT_SUCCESS(Status)) goto FailPath;
690
691 /* Write the Environment Block */
692 Status = NtWriteVirtualMemory(ProcessHandle,
693 ProcessParameters->Environment,
694 lpEnvironment,
695 EnviroSize,
696 NULL);
697
698 /* No longer need the PEB lock anymore */
699 if (HavePebLock)
700 {
701 /* Release it */
702 RtlReleasePebLock();
703 HavePebLock = FALSE;
704 }
705
706 /* Check if the write failed */
707 if (!NT_SUCCESS(Status)) goto FailPath;
708 }
709
710 /* Write new parameters */
711 ProcessParameters->StartingX = StartupInfo->dwX;
712 ProcessParameters->StartingY = StartupInfo->dwY;
713 ProcessParameters->CountX = StartupInfo->dwXSize;
714 ProcessParameters->CountY = StartupInfo->dwYSize;
715 ProcessParameters->CountCharsX = StartupInfo->dwXCountChars;
716 ProcessParameters->CountCharsY = StartupInfo->dwYCountChars;
717 ProcessParameters->FillAttribute = StartupInfo->dwFillAttribute;
718 ProcessParameters->WindowFlags = StartupInfo->dwFlags;
719 ProcessParameters->ShowWindowFlags = StartupInfo->wShowWindow;
720
721 /* Write the handles only if we have to */
722 if (StartupInfo->dwFlags &
723 (STARTF_USESTDHANDLES | STARTF_USEHOTKEY | STARTF_SHELLPRIVATE))
724 {
725 ProcessParameters->StandardInput = StartupInfo->hStdInput;
726 ProcessParameters->StandardOutput = StartupInfo->hStdOutput;
727 ProcessParameters->StandardError = StartupInfo->hStdError;
728 }
729
730 /* Use Special Flags for BasepInitConsole in Kernel32 */
731 if (CreationFlags & DETACHED_PROCESS)
732 {
733 ProcessParameters->ConsoleHandle = HANDLE_DETACHED_PROCESS;
734 }
735 else if (CreationFlags & CREATE_NEW_CONSOLE)
736 {
737 ProcessParameters->ConsoleHandle = HANDLE_CREATE_NEW_CONSOLE;
738 }
739 else if (CreationFlags & CREATE_NO_WINDOW)
740 {
741 ProcessParameters->ConsoleHandle = HANDLE_CREATE_NO_WINDOW;
742 }
743 else
744 {
745 /* Inherit our Console Handle */
746 ProcessParameters->ConsoleHandle = Peb->ProcessParameters->ConsoleHandle;
747
748 /* Make sure that the shell isn't trampling on our handles first */
749 if (!(StartupInfo->dwFlags &
750 (STARTF_USESTDHANDLES | STARTF_USEHOTKEY | STARTF_SHELLPRIVATE)))
751 {
752 /* Copy the handle if we are inheriting or if it's a console handle */
753 if ((InheritHandles) ||
754 (IsConsoleHandle(Peb->ProcessParameters->StandardInput)))
755 {
756 ProcessParameters->StandardInput = Peb->ProcessParameters->StandardInput;
757 }
758 if ((InheritHandles) ||
759 (IsConsoleHandle(Peb->ProcessParameters->StandardOutput)))
760 {
761 ProcessParameters->StandardOutput = Peb->ProcessParameters->StandardOutput;
762 }
763 if ((InheritHandles) ||
764 (IsConsoleHandle(Peb->ProcessParameters->StandardError)))
765 {
766 ProcessParameters->StandardError = Peb->ProcessParameters->StandardError;
767 }
768 }
769 }
770
771 /* Also set the Console Flag */
772 if ((CreationFlags & CREATE_NEW_PROCESS_GROUP) &&
773 (!(CreationFlags & CREATE_NEW_CONSOLE)))
774 {
775 ProcessParameters->ConsoleFlags = 1;
776 }
777
778 /* Check if there's a .local file present */
779 if (ParameterFlags & 1)
780 {
781 ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_LOCAL_DLL_PATH;
782 }
783
784 /* Check if we failed to open the IFEO key */
785 if (ParameterFlags & 2)
786 {
787 ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_IMAGE_KEY_MISSING;
788 }
789
790 /* Allocate memory for the parameter block */
791 Size = ProcessParameters->Length;
792 RemoteParameters = NULL;
793 Status = NtAllocateVirtualMemory(ProcessHandle,
794 (PVOID*)&RemoteParameters,
795 0,
796 &Size,
797 MEM_COMMIT,
798 PAGE_READWRITE);
799 if (!NT_SUCCESS(Status)) goto FailPath;
800
801 /* Set the allocated size */
802 ProcessParameters->MaximumLength = Size;
803
804 /* Handle some Parameter Flags */
805 ProcessParameters->Flags |= (CreationFlags & PROFILE_USER) ?
806 RTL_USER_PROCESS_PARAMETERS_PROFILE_USER : 0;
807 ProcessParameters->Flags |= (CreationFlags & PROFILE_KERNEL) ?
808 RTL_USER_PROCESS_PARAMETERS_PROFILE_KERNEL : 0;
809 ProcessParameters->Flags |= (CreationFlags & PROFILE_SERVER) ?
810 RTL_USER_PROCESS_PARAMETERS_PROFILE_SERVER : 0;
811 ProcessParameters->Flags |= (Peb->ProcessParameters->Flags &
812 RTL_USER_PROCESS_PARAMETERS_DISABLE_HEAP_CHECKS);
813
814 /* Write the Parameter Block */
815 Status = NtWriteVirtualMemory(ProcessHandle,
816 RemoteParameters,
817 ProcessParameters,
818 ProcessParameters->Length,
819 NULL);
820 if (!NT_SUCCESS(Status)) goto FailPath;
821
822 /* Write the PEB Pointer */
823 Status = NtWriteVirtualMemory(ProcessHandle,
824 &RemotePeb->ProcessParameters,
825 &RemoteParameters,
826 sizeof(PVOID),
827 NULL);
828 if (!NT_SUCCESS(Status)) goto FailPath;
829
830 /* Check if there's any app compat data to write */
831 RemoteAppCompatData = NULL;
832 if (AppCompatData)
833 {
834 /* Allocate some space for the application compatibility data */
835 Size = AppCompatDataSize;
836 Status = NtAllocateVirtualMemory(ProcessHandle,
837 &RemoteAppCompatData,
838 0,
839 &Size,
840 MEM_COMMIT,
841 PAGE_READWRITE);
842 if (!NT_SUCCESS(Status)) goto FailPath;
843
844 /* Write the application compatibility data */
845 Status = NtWriteVirtualMemory(ProcessHandle,
846 RemoteAppCompatData,
847 AppCompatData,
848 AppCompatDataSize,
849 NULL);
850 if (!NT_SUCCESS(Status)) goto FailPath;
851 }
852
853 /* Write the PEB Pointer to the app compat data (might be NULL) */
854 Status = NtWriteVirtualMemory(ProcessHandle,
855 &RemotePeb->pShimData,
856 &RemoteAppCompatData,
857 sizeof(PVOID),
858 NULL);
859 if (!NT_SUCCESS(Status)) goto FailPath;
860
861 /* Now write Peb->ImageSubSystem */
862 if (ImageSubsystem)
863 {
864 NtWriteVirtualMemory(ProcessHandle,
865 &RemotePeb->ImageSubsystem,
866 &ImageSubsystem,
867 sizeof(ImageSubsystem),
868 NULL);
869 }
870
871 /* Success path */
872 Result = TRUE;
873
874 Quickie:
875 /* Cleanup */
876 if (HavePebLock) RtlReleasePebLock();
877 RtlFreeHeap(RtlGetProcessHeap(), 0, DllPath.Buffer);
878 if (ProcessParameters) RtlDestroyProcessParameters(ProcessParameters);
879 return Result;
880 FailPath:
881 DPRINT1("Failure to create process parameters: %lx\n", Status);
882 BaseSetLastNTError(Status);
883 Result = FALSE;
884 goto Quickie;
885 }
886
887 VOID
888 WINAPI
889 InitCommandLines(VOID)
890 {
891 NTSTATUS Status;
892
893 /* Read the UNICODE_STRING from the PEB */
894 BaseUnicodeCommandLine = NtCurrentPeb()->ProcessParameters->CommandLine;
895
896 /* Convert to ANSI_STRING for the *A callers */
897 Status = RtlUnicodeStringToAnsiString(&BaseAnsiCommandLine,
898 &BaseUnicodeCommandLine,
899 TRUE);
900 if (!NT_SUCCESS(Status)) RtlInitEmptyAnsiString(&BaseAnsiCommandLine, 0, 0);
901 }
902
903 /* PUBLIC FUNCTIONS ***********************************************************/
904
905 /*
906 * @implemented
907 */
908 BOOL
909 WINAPI
910 GetProcessAffinityMask(IN HANDLE hProcess,
911 OUT PDWORD_PTR lpProcessAffinityMask,
912 OUT PDWORD_PTR lpSystemAffinityMask)
913 {
914 PROCESS_BASIC_INFORMATION ProcessInfo;
915 NTSTATUS Status;
916
917 /* Query information on the process from the kernel */
918 Status = NtQueryInformationProcess(hProcess,
919 ProcessBasicInformation,
920 (PVOID)&ProcessInfo,
921 sizeof(PROCESS_BASIC_INFORMATION),
922 NULL);
923 if (!NT_SUCCESS(Status))
924 {
925 /* Fail */
926 BaseSetLastNTError(Status);
927 return FALSE;
928 }
929
930 /* Copy the affinity mask, and get the system one from our shared data */
931 *lpProcessAffinityMask = (DWORD)ProcessInfo.AffinityMask;
932 *lpSystemAffinityMask = (DWORD)BaseStaticServerData->SysInfo.ActiveProcessorsAffinityMask;
933 return TRUE;
934 }
935
936 /*
937 * @implemented
938 */
939 BOOL
940 WINAPI
941 SetProcessAffinityMask(IN HANDLE hProcess,
942 IN DWORD_PTR dwProcessAffinityMask)
943 {
944 NTSTATUS Status;
945
946 /* Directly set the affinity mask */
947 Status = NtSetInformationProcess(hProcess,
948 ProcessAffinityMask,
949 (PVOID)&dwProcessAffinityMask,
950 sizeof(DWORD));
951 if (!NT_SUCCESS(Status))
952 {
953 /* Handle failure */
954 BaseSetLastNTError(Status);
955 return FALSE;
956 }
957
958 /* Everything was ok */
959 return TRUE;
960 }
961
962 /*
963 * @implemented
964 */
965 BOOL
966 WINAPI
967 GetProcessShutdownParameters(OUT LPDWORD lpdwLevel,
968 OUT LPDWORD lpdwFlags)
969 {
970 NTSTATUS Status;
971 BASE_API_MESSAGE ApiMessage;
972 PBASE_GETSET_PROCESS_SHUTDOWN_PARAMS ShutdownParametersRequest = &ApiMessage.Data.ShutdownParametersRequest;
973
974 /* Ask CSRSS for shutdown information */
975 Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
976 NULL,
977 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepGetProcessShutdownParam),
978 sizeof(BASE_GETSET_PROCESS_SHUTDOWN_PARAMS));
979 if (!NT_SUCCESS(Status))
980 {
981 /* Return the failure from CSRSS */
982 BaseSetLastNTError(Status);
983 return FALSE;
984 }
985
986 /* Get the data back */
987 *lpdwLevel = ShutdownParametersRequest->ShutdownLevel;
988 *lpdwFlags = ShutdownParametersRequest->ShutdownFlags;
989 return TRUE;
990 }
991
992 /*
993 * @implemented
994 */
995 BOOL
996 WINAPI
997 SetProcessShutdownParameters(IN DWORD dwLevel,
998 IN DWORD dwFlags)
999 {
1000 NTSTATUS Status;
1001 BASE_API_MESSAGE ApiMessage;
1002 PBASE_GETSET_PROCESS_SHUTDOWN_PARAMS ShutdownParametersRequest = &ApiMessage.Data.ShutdownParametersRequest;
1003
1004 /* Write the data into the CSRSS request and send it */
1005 ShutdownParametersRequest->ShutdownLevel = dwLevel;
1006 ShutdownParametersRequest->ShutdownFlags = dwFlags;
1007 Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1008 NULL,
1009 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepSetProcessShutdownParam),
1010 sizeof(BASE_GETSET_PROCESS_SHUTDOWN_PARAMS));
1011 if (!NT_SUCCESS(Status))
1012 {
1013 /* Return the failure from CSRSS */
1014 BaseSetLastNTError(Status);
1015 return FALSE;
1016 }
1017
1018 /* All went well */
1019 return TRUE;
1020 }
1021
1022 /*
1023 * @implemented
1024 */
1025 BOOL
1026 WINAPI
1027 GetProcessWorkingSetSizeEx(IN HANDLE hProcess,
1028 OUT PSIZE_T lpMinimumWorkingSetSize,
1029 OUT PSIZE_T lpMaximumWorkingSetSize,
1030 OUT PDWORD Flags)
1031 {
1032 QUOTA_LIMITS_EX QuotaLimits;
1033 NTSTATUS Status;
1034
1035 /* Query the kernel about this */
1036 Status = NtQueryInformationProcess(hProcess,
1037 ProcessQuotaLimits,
1038 &QuotaLimits,
1039 sizeof(QUOTA_LIMITS_EX),
1040 NULL);
1041 if (!NT_SUCCESS(Status))
1042 {
1043 /* Return error */
1044 BaseSetLastNTError(Status);
1045 return FALSE;
1046 }
1047
1048 /* Copy the quota information out */
1049 *lpMinimumWorkingSetSize = QuotaLimits.MinimumWorkingSetSize;
1050 *lpMaximumWorkingSetSize = QuotaLimits.MaximumWorkingSetSize;
1051 *Flags = QuotaLimits.Flags;
1052 return TRUE;
1053 }
1054
1055 /*
1056 * @implemented
1057 */
1058 BOOL
1059 WINAPI
1060 GetProcessWorkingSetSize(IN HANDLE hProcess,
1061 OUT PSIZE_T lpMinimumWorkingSetSize,
1062 OUT PSIZE_T lpMaximumWorkingSetSize)
1063 {
1064 DWORD Dummy;
1065 return GetProcessWorkingSetSizeEx(hProcess,
1066 lpMinimumWorkingSetSize,
1067 lpMaximumWorkingSetSize,
1068 &Dummy);
1069 }
1070
1071 /*
1072 * @implemented
1073 */
1074 BOOL
1075 WINAPI
1076 SetProcessWorkingSetSizeEx(IN HANDLE hProcess,
1077 IN SIZE_T dwMinimumWorkingSetSize,
1078 IN SIZE_T dwMaximumWorkingSetSize,
1079 IN DWORD Flags)
1080 {
1081 QUOTA_LIMITS_EX QuotaLimits;
1082 NTSTATUS Status, ReturnStatus;
1083 BOOL Result;
1084 PVOID State;
1085 ULONG Privilege = SE_INC_BASE_PRIORITY_PRIVILEGE;
1086
1087 /* Zero out the input structure */
1088 RtlZeroMemory(&QuotaLimits, sizeof(QuotaLimits));
1089
1090 /* Check if the caller sent any limits */
1091 if ((dwMinimumWorkingSetSize) && (dwMaximumWorkingSetSize))
1092 {
1093 /* Write the quota information */
1094 QuotaLimits.MinimumWorkingSetSize = dwMinimumWorkingSetSize;
1095 QuotaLimits.MaximumWorkingSetSize = dwMaximumWorkingSetSize;
1096 QuotaLimits.Flags = Flags;
1097
1098 /* Acquire the required privilege */
1099 Status = RtlAcquirePrivilege(&Privilege, 1, 0, &State);
1100
1101 /* Request the new quotas */
1102 ReturnStatus = NtSetInformationProcess(hProcess,
1103 ProcessQuotaLimits,
1104 &QuotaLimits,
1105 sizeof(QuotaLimits));
1106 Result = NT_SUCCESS(ReturnStatus);
1107 if (NT_SUCCESS(Status))
1108 {
1109 /* Release the privilege and set succes code */
1110 ASSERT(State != NULL);
1111 RtlReleasePrivilege(State);
1112 State = NULL;
1113 }
1114 }
1115 else
1116 {
1117 /* No limits, fail the call */
1118 ReturnStatus = STATUS_INVALID_PARAMETER;
1119 Result = FALSE;
1120 }
1121
1122 /* Return result code, set error code if this was a failure */
1123 if (!Result) BaseSetLastNTError(ReturnStatus);
1124 return Result;
1125 }
1126
1127 /*
1128 * @implemented
1129 */
1130 BOOL
1131 WINAPI
1132 SetProcessWorkingSetSize(IN HANDLE hProcess,
1133 IN SIZE_T dwMinimumWorkingSetSize,
1134 IN SIZE_T dwMaximumWorkingSetSize)
1135 {
1136 /* Call the newer API */
1137 return SetProcessWorkingSetSizeEx(hProcess,
1138 dwMinimumWorkingSetSize,
1139 dwMaximumWorkingSetSize,
1140 0);
1141 }
1142
1143 /*
1144 * @implemented
1145 */
1146 BOOL
1147 WINAPI
1148 GetProcessTimes(IN HANDLE hProcess,
1149 IN LPFILETIME lpCreationTime,
1150 IN LPFILETIME lpExitTime,
1151 IN LPFILETIME lpKernelTime,
1152 IN LPFILETIME lpUserTime)
1153 {
1154 KERNEL_USER_TIMES Kut;
1155 NTSTATUS Status;
1156
1157 /* Query the times */
1158 Status = NtQueryInformationProcess(hProcess,
1159 ProcessTimes,
1160 &Kut,
1161 sizeof(Kut),
1162 NULL);
1163 if (!NT_SUCCESS(Status))
1164 {
1165 /* Handle failure */
1166 BaseSetLastNTError(Status);
1167 return FALSE;
1168 }
1169
1170 /* Copy all the times and return success */
1171 lpCreationTime->dwLowDateTime = Kut.CreateTime.u.LowPart;
1172 lpCreationTime->dwHighDateTime = Kut.CreateTime.u.HighPart;
1173 lpExitTime->dwLowDateTime = Kut.ExitTime.u.LowPart;
1174 lpExitTime->dwHighDateTime = Kut.ExitTime.u.HighPart;
1175 lpKernelTime->dwLowDateTime = Kut.KernelTime.u.LowPart;
1176 lpKernelTime->dwHighDateTime = Kut.KernelTime.u.HighPart;
1177 lpUserTime->dwLowDateTime = Kut.UserTime.u.LowPart;
1178 lpUserTime->dwHighDateTime = Kut.UserTime.u.HighPart;
1179 return TRUE;
1180 }
1181
1182 /*
1183 * @implemented
1184 */
1185 HANDLE
1186 WINAPI
1187 GetCurrentProcess(VOID)
1188 {
1189 return (HANDLE)NtCurrentProcess();
1190 }
1191
1192 /*
1193 * @implemented
1194 */
1195 HANDLE
1196 WINAPI
1197 GetCurrentThread(VOID)
1198 {
1199 return (HANDLE)NtCurrentThread();
1200 }
1201
1202 /*
1203 * @implemented
1204 */
1205 DWORD
1206 WINAPI
1207 GetCurrentProcessId(VOID)
1208 {
1209 return HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess);
1210 }
1211
1212 /*
1213 * @implemented
1214 */
1215 BOOL
1216 WINAPI
1217 GetExitCodeProcess(IN HANDLE hProcess,
1218 IN LPDWORD lpExitCode)
1219 {
1220 PROCESS_BASIC_INFORMATION ProcessBasic;
1221 NTSTATUS Status;
1222
1223 /* Ask the kernel */
1224 Status = NtQueryInformationProcess(hProcess,
1225 ProcessBasicInformation,
1226 &ProcessBasic,
1227 sizeof(PROCESS_BASIC_INFORMATION),
1228 NULL);
1229 if (!NT_SUCCESS(Status))
1230 {
1231 /* We failed, was this because this is a VDM process? */
1232 if (BaseCheckForVDM(hProcess, lpExitCode) == TRUE) return TRUE;
1233
1234 /* Not a VDM process, fail the call */
1235 BaseSetLastNTError(Status);
1236 return FALSE;
1237 }
1238
1239 /* Succes case, return the exit code */
1240 *lpExitCode = (DWORD)ProcessBasic.ExitStatus;
1241 return TRUE;
1242 }
1243
1244 /*
1245 * @implemented
1246 */
1247 DWORD
1248 WINAPI
1249 GetProcessId(IN HANDLE Process)
1250 {
1251 PROCESS_BASIC_INFORMATION ProcessBasic;
1252 NTSTATUS Status;
1253
1254 /* Query the kernel */
1255 Status = NtQueryInformationProcess(Process,
1256 ProcessBasicInformation,
1257 &ProcessBasic,
1258 sizeof(PROCESS_BASIC_INFORMATION),
1259 NULL);
1260 if (!NT_SUCCESS(Status))
1261 {
1262 /* Handle failure */
1263 BaseSetLastNTError(Status);
1264 return 0;
1265 }
1266
1267 /* Return the PID */
1268 return (DWORD)ProcessBasic.UniqueProcessId;
1269 }
1270
1271 /*
1272 * @implemented
1273 */
1274 HANDLE
1275 WINAPI
1276 OpenProcess(IN DWORD dwDesiredAccess,
1277 IN BOOL bInheritHandle,
1278 IN DWORD dwProcessId)
1279 {
1280 NTSTATUS Status;
1281 HANDLE ProcessHandle;
1282 OBJECT_ATTRIBUTES ObjectAttributes;
1283 CLIENT_ID ClientId;
1284
1285 /* Setup the input client ID structure */
1286 ClientId.UniqueProcess = UlongToHandle(dwProcessId);
1287 ClientId.UniqueThread = 0;
1288
1289 /* This is needed just to define the inheritance flags */
1290 InitializeObjectAttributes(&ObjectAttributes,
1291 NULL,
1292 (bInheritHandle ? OBJ_INHERIT : 0),
1293 NULL,
1294 NULL);
1295
1296 /* Now try to open the process */
1297 Status = NtOpenProcess(&ProcessHandle,
1298 dwDesiredAccess,
1299 &ObjectAttributes,
1300 &ClientId);
1301 if (!NT_SUCCESS(Status))
1302 {
1303 /* Handle failure */
1304 BaseSetLastNTError(Status);
1305 return NULL;
1306 }
1307
1308 /* Otherwise return a handle to the process */
1309 return ProcessHandle;
1310 }
1311
1312 /*
1313 * @implemented
1314 */
1315 VOID
1316 WINAPI
1317 RegisterWaitForInputIdle(IN WaitForInputIdleType lpfnRegisterWaitForInputIdle)
1318 {
1319 /* Write the global function pointer */
1320 UserWaitForInputIdleRoutine = lpfnRegisterWaitForInputIdle;
1321 }
1322
1323 /*
1324 * @implemented
1325 */
1326 VOID
1327 WINAPI
1328 GetStartupInfoW(IN LPSTARTUPINFOW lpStartupInfo)
1329 {
1330 PRTL_USER_PROCESS_PARAMETERS Params;
1331
1332 /* Get the process parameters */
1333 Params = NtCurrentPeb()->ProcessParameters;
1334
1335 /* Copy the data out of there */
1336 lpStartupInfo->cb = sizeof(STARTUPINFOW);
1337 lpStartupInfo->lpReserved = Params->ShellInfo.Buffer;
1338 lpStartupInfo->lpDesktop = Params->DesktopInfo.Buffer;
1339 lpStartupInfo->lpTitle = Params->WindowTitle.Buffer;
1340 lpStartupInfo->dwX = Params->StartingX;
1341 lpStartupInfo->dwY = Params->StartingY;
1342 lpStartupInfo->dwXSize = Params->CountX;
1343 lpStartupInfo->dwYSize = Params->CountY;
1344 lpStartupInfo->dwXCountChars = Params->CountCharsX;
1345 lpStartupInfo->dwYCountChars = Params->CountCharsY;
1346 lpStartupInfo->dwFillAttribute = Params->FillAttribute;
1347 lpStartupInfo->dwFlags = Params->WindowFlags;
1348 lpStartupInfo->wShowWindow = (WORD)Params->ShowWindowFlags;
1349 lpStartupInfo->cbReserved2 = Params->RuntimeData.Length;
1350 lpStartupInfo->lpReserved2 = (LPBYTE)Params->RuntimeData.Buffer;
1351
1352 /* Check if the standard handles are being used for other features */
1353 if (lpStartupInfo->dwFlags & (STARTF_USESTDHANDLES |
1354 STARTF_USEHOTKEY |
1355 STARTF_SHELLPRIVATE))
1356 {
1357 /* These are, so copy the standard handles too */
1358 lpStartupInfo->hStdInput = Params->StandardInput;
1359 lpStartupInfo->hStdOutput = Params->StandardOutput;
1360 lpStartupInfo->hStdError = Params->StandardError;
1361 }
1362 }
1363
1364 /*
1365 * @implemented
1366 */
1367 VOID
1368 WINAPI
1369 GetStartupInfoA(IN LPSTARTUPINFOA lpStartupInfo)
1370 {
1371 PRTL_USER_PROCESS_PARAMETERS Params;
1372 ANSI_STRING TitleString, ShellString, DesktopString;
1373 LPSTARTUPINFOA StartupInfo;
1374 NTSTATUS Status;
1375
1376 /* Get the cached information as well as the PEB parameters */
1377 StartupInfo = BaseAnsiStartupInfo;
1378 Params = NtCurrentPeb()->ProcessParameters;
1379
1380 /* Check if this is the first time we have to get the cached version */
1381 while (!StartupInfo)
1382 {
1383 /* Create new ANSI startup info */
1384 StartupInfo = RtlAllocateHeap(RtlGetProcessHeap(),
1385 0,
1386 sizeof(*StartupInfo));
1387 if (StartupInfo)
1388 {
1389 /* Zero out string pointers in case we fail to create them */
1390 StartupInfo->lpReserved = 0;
1391 StartupInfo->lpDesktop = 0;
1392 StartupInfo->lpTitle = 0;
1393
1394 /* Set the size */
1395 StartupInfo->cb = sizeof(*StartupInfo);
1396
1397 /* Copy what's already stored in the PEB */
1398 StartupInfo->dwX = Params->StartingX;
1399 StartupInfo->dwY = Params->StartingY;
1400 StartupInfo->dwXSize = Params->CountX;
1401 StartupInfo->dwYSize = Params->CountY;
1402 StartupInfo->dwXCountChars = Params->CountCharsX;
1403 StartupInfo->dwYCountChars = Params->CountCharsY;
1404 StartupInfo->dwFillAttribute = Params->FillAttribute;
1405 StartupInfo->dwFlags = Params->WindowFlags;
1406 StartupInfo->wShowWindow = (WORD)Params->ShowWindowFlags;
1407 StartupInfo->cbReserved2 = Params->RuntimeData.Length;
1408 StartupInfo->lpReserved2 = (LPBYTE)Params->RuntimeData.Buffer;
1409 StartupInfo->hStdInput = Params->StandardInput;
1410 StartupInfo->hStdOutput = Params->StandardOutput;
1411 StartupInfo->hStdError = Params->StandardError;
1412
1413 /* Copy shell info string */
1414 Status = RtlUnicodeStringToAnsiString(&ShellString,
1415 &Params->ShellInfo,
1416 TRUE);
1417 if (NT_SUCCESS(Status))
1418 {
1419 /* Save it */
1420 StartupInfo->lpReserved = ShellString.Buffer;
1421
1422 /* Copy desktop info string */
1423 Status = RtlUnicodeStringToAnsiString(&DesktopString,
1424 &Params->DesktopInfo,
1425 TRUE);
1426 if (NT_SUCCESS(Status))
1427 {
1428 /* Save it */
1429 StartupInfo->lpDesktop = DesktopString.Buffer;
1430
1431 /* Copy window title string */
1432 Status = RtlUnicodeStringToAnsiString(&TitleString,
1433 &Params->WindowTitle,
1434 TRUE);
1435 if (NT_SUCCESS(Status))
1436 {
1437 /* Save it */
1438 StartupInfo->lpTitle = TitleString.Buffer;
1439
1440 /* We finished with the ANSI version, try to cache it */
1441 if (!InterlockedCompareExchangePointer(&BaseAnsiStartupInfo,
1442 StartupInfo,
1443 NULL))
1444 {
1445 /* We were the first thread through, use the data */
1446 break;
1447 }
1448
1449 /* Someone beat us to it, use their data instead */
1450 StartupInfo = BaseAnsiStartupInfo;
1451 Status = STATUS_SUCCESS;
1452
1453 /* We're going to free our own stuff, but not raise */
1454 RtlFreeAnsiString(&TitleString);
1455 }
1456 RtlFreeAnsiString(&DesktopString);
1457 }
1458 RtlFreeAnsiString(&ShellString);
1459 }
1460 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo);
1461 }
1462 else
1463 {
1464 /* No memory, fail */
1465 Status = STATUS_NO_MEMORY;
1466 }
1467
1468 /* Raise an error unless we got here due to the race condition */
1469 if (!NT_SUCCESS(Status)) RtlRaiseStatus(Status);
1470 }
1471
1472 /* Now copy from the cached ANSI version */
1473 lpStartupInfo->cb = StartupInfo->cb;
1474 lpStartupInfo->lpReserved = StartupInfo->lpReserved;
1475 lpStartupInfo->lpDesktop = StartupInfo->lpDesktop;
1476 lpStartupInfo->lpTitle = StartupInfo->lpTitle;
1477 lpStartupInfo->dwX = StartupInfo->dwX;
1478 lpStartupInfo->dwY = StartupInfo->dwY;
1479 lpStartupInfo->dwXSize = StartupInfo->dwXSize;
1480 lpStartupInfo->dwYSize = StartupInfo->dwYSize;
1481 lpStartupInfo->dwXCountChars = StartupInfo->dwXCountChars;
1482 lpStartupInfo->dwYCountChars = StartupInfo->dwYCountChars;
1483 lpStartupInfo->dwFillAttribute = StartupInfo->dwFillAttribute;
1484 lpStartupInfo->dwFlags = StartupInfo->dwFlags;
1485 lpStartupInfo->wShowWindow = StartupInfo->wShowWindow;
1486 lpStartupInfo->cbReserved2 = StartupInfo->cbReserved2;
1487 lpStartupInfo->lpReserved2 = StartupInfo->lpReserved2;
1488
1489 /* Check if the shell is hijacking the handles for other features */
1490 if (lpStartupInfo->dwFlags &
1491 (STARTF_USESTDHANDLES | STARTF_USEHOTKEY | STARTF_SHELLPRIVATE))
1492 {
1493 /* It isn't, so we can return the raw values */
1494 lpStartupInfo->hStdInput = StartupInfo->hStdInput;
1495 lpStartupInfo->hStdOutput = StartupInfo->hStdOutput;
1496 lpStartupInfo->hStdError = StartupInfo->hStdError;
1497 }
1498 else
1499 {
1500 /* It is, so make sure nobody uses these as console handles */
1501 lpStartupInfo->hStdInput = INVALID_HANDLE_VALUE;
1502 lpStartupInfo->hStdOutput = INVALID_HANDLE_VALUE;
1503 lpStartupInfo->hStdError = INVALID_HANDLE_VALUE;
1504 }
1505 }
1506
1507 /*
1508 * @implemented
1509 */
1510 BOOL
1511 WINAPI
1512 FlushInstructionCache(IN HANDLE hProcess,
1513 IN LPCVOID lpBaseAddress,
1514 IN SIZE_T dwSize)
1515 {
1516 NTSTATUS Status;
1517
1518 /* Call the native function */
1519 Status = NtFlushInstructionCache(hProcess, (PVOID)lpBaseAddress, dwSize);
1520 if (!NT_SUCCESS(Status))
1521 {
1522 /* Handle failure case */
1523 BaseSetLastNTError(Status);
1524 return FALSE;
1525 }
1526
1527 /* All good */
1528 return TRUE;
1529 }
1530
1531 /*
1532 * @implemented
1533 */
1534 VOID
1535 WINAPI
1536 ExitProcess(IN UINT uExitCode)
1537 {
1538 BASE_API_MESSAGE ApiMessage;
1539 PBASE_EXIT_PROCESS ExitProcessRequest = &ApiMessage.Data.ExitProcessRequest;
1540
1541 ASSERT(!BaseRunningInServerProcess);
1542
1543 _SEH2_TRY
1544 {
1545 /* Acquire the PEB lock */
1546 RtlAcquirePebLock();
1547
1548 /* Kill all the threads */
1549 NtTerminateProcess(NULL, 0);
1550
1551 /* Unload all DLLs */
1552 LdrShutdownProcess();
1553
1554 /* Notify Base Server of process termination */
1555 ExitProcessRequest->uExitCode = uExitCode;
1556 CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1557 NULL,
1558 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepExitProcess),
1559 sizeof(BASE_EXIT_PROCESS));
1560
1561 /* Now do it again */
1562 NtTerminateProcess(NtCurrentProcess(), uExitCode);
1563 }
1564 _SEH2_FINALLY
1565 {
1566 /* Release the PEB lock */
1567 RtlReleasePebLock();
1568 }
1569 _SEH2_END;
1570
1571 /* should never get here */
1572 ASSERT(0);
1573 while(1);
1574 }
1575
1576 /*
1577 * @implemented
1578 */
1579 BOOL
1580 WINAPI
1581 TerminateProcess(IN HANDLE hProcess,
1582 IN UINT uExitCode)
1583 {
1584 NTSTATUS Status;
1585
1586 /* Check if no handle was passed in */
1587 if (!hProcess)
1588 {
1589 /* Set error code */
1590 SetLastError(ERROR_INVALID_HANDLE);
1591 }
1592 else
1593 {
1594 /* Otherwise, try to terminate the process */
1595 Status = NtTerminateProcess(hProcess, uExitCode);
1596 if (NT_SUCCESS(Status)) return TRUE;
1597
1598 /* It failed, convert error code */
1599 BaseSetLastNTError(Status);
1600 }
1601
1602 /* This is the failure path */
1603 return FALSE;
1604 }
1605
1606 /*
1607 * @implemented
1608 */
1609 VOID
1610 WINAPI
1611 FatalAppExitA(UINT uAction,
1612 LPCSTR lpMessageText)
1613 {
1614 PUNICODE_STRING MessageTextU;
1615 ANSI_STRING MessageText;
1616 NTSTATUS Status;
1617
1618 /* Initialize the string using the static TEB pointer */
1619 MessageTextU = &NtCurrentTeb()->StaticUnicodeString;
1620 RtlInitAnsiString(&MessageText, (LPSTR)lpMessageText);
1621
1622 /* Convert to unicode and just exit normally if this failed */
1623 Status = RtlAnsiStringToUnicodeString(MessageTextU, &MessageText, FALSE);
1624 if (!NT_SUCCESS(Status)) ExitProcess(0);
1625
1626 /* Call the Wide function */
1627 FatalAppExitW(uAction, MessageTextU->Buffer);
1628 }
1629
1630 /*
1631 * @implemented
1632 */
1633 VOID
1634 WINAPI
1635 FatalAppExitW(IN UINT uAction,
1636 IN LPCWSTR lpMessageText)
1637 {
1638 UNICODE_STRING UnicodeString;
1639 ULONG Response;
1640 NTSTATUS Status;
1641
1642 /* Setup the string to print out */
1643 RtlInitUnicodeString(&UnicodeString, lpMessageText);
1644
1645 /* Display the hard error no matter what */
1646 Status = NtRaiseHardError(STATUS_FATAL_APP_EXIT | HARDERROR_OVERRIDE_ERRORMODE,
1647 1,
1648 1,
1649 (PULONG_PTR)&UnicodeString,
1650 OptionOkCancel,
1651 &Response);
1652
1653 /* Give the user a chance to abort */
1654 if ((NT_SUCCESS(Status)) && (Response == ResponseCancel)) return;
1655
1656 /* Otherwise kill the process */
1657 ExitProcess(0);
1658 }
1659
1660 /*
1661 * @implemented
1662 */
1663 VOID
1664 WINAPI
1665 FatalExit(IN int ExitCode)
1666 {
1667 #if DBG
1668 /* On Checked builds, Windows gives you a nice little debugger UI */
1669 CHAR ch[2];
1670 DbgPrint("FatalExit...\n");
1671 DbgPrint("\n");
1672
1673 while (TRUE)
1674 {
1675 DbgPrompt( "A (Abort), B (Break), I (Ignore)? ", ch, sizeof(ch));
1676 switch (ch[0])
1677 {
1678 case 'B': case 'b':
1679 DbgBreakPoint();
1680 break;
1681
1682 case 'A': case 'a':
1683 ExitProcess(ExitCode);
1684
1685 case 'I': case 'i':
1686 return;
1687 }
1688 }
1689 #endif
1690 /* On other builds, just kill the process */
1691 ExitProcess(ExitCode);
1692 }
1693
1694 /*
1695 * @implemented
1696 */
1697 DWORD
1698 WINAPI
1699 GetPriorityClass(IN HANDLE hProcess)
1700 {
1701 NTSTATUS Status;
1702 PROCESS_PRIORITY_CLASS PriorityClass;
1703
1704 /* Query the kernel */
1705 Status = NtQueryInformationProcess(hProcess,
1706 ProcessPriorityClass,
1707 &PriorityClass,
1708 sizeof(PROCESS_PRIORITY_CLASS),
1709 NULL);
1710 if (NT_SUCCESS(Status))
1711 {
1712 /* Handle the conversion from NT to Win32 classes */
1713 switch (PriorityClass.PriorityClass)
1714 {
1715 case PROCESS_PRIORITY_CLASS_IDLE: return IDLE_PRIORITY_CLASS;
1716 case PROCESS_PRIORITY_CLASS_BELOW_NORMAL: return BELOW_NORMAL_PRIORITY_CLASS;
1717 case PROCESS_PRIORITY_CLASS_ABOVE_NORMAL: return ABOVE_NORMAL_PRIORITY_CLASS;
1718 case PROCESS_PRIORITY_CLASS_HIGH: return HIGH_PRIORITY_CLASS;
1719 case PROCESS_PRIORITY_CLASS_REALTIME: return REALTIME_PRIORITY_CLASS;
1720 case PROCESS_PRIORITY_CLASS_NORMAL: default: return NORMAL_PRIORITY_CLASS;
1721 }
1722 }
1723
1724 /* Failure path */
1725 BaseSetLastNTError(Status);
1726 return FALSE;
1727 }
1728
1729 /*
1730 * @implemented
1731 */
1732 BOOL
1733 WINAPI
1734 SetPriorityClass(IN HANDLE hProcess,
1735 IN DWORD dwPriorityClass)
1736 {
1737 NTSTATUS Status;
1738 PVOID State = NULL;
1739 PROCESS_PRIORITY_CLASS PriorityClass;
1740
1741 /* Handle conversion from Win32 to NT priority classes */
1742 switch (dwPriorityClass)
1743 {
1744 case IDLE_PRIORITY_CLASS:
1745 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE;
1746 break;
1747
1748 case BELOW_NORMAL_PRIORITY_CLASS:
1749 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
1750 break;
1751
1752 case NORMAL_PRIORITY_CLASS:
1753 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
1754 break;
1755
1756 case ABOVE_NORMAL_PRIORITY_CLASS:
1757 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
1758 break;
1759
1760 case HIGH_PRIORITY_CLASS:
1761 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
1762 break;
1763
1764 case REALTIME_PRIORITY_CLASS:
1765 /* Try to acquire the privilege. If it fails, just use HIGH */
1766 State = BasepIsRealtimeAllowed(TRUE);
1767 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
1768 PriorityClass.PriorityClass += (State != NULL);
1769 break;
1770
1771 default:
1772 /* Unrecognized priority classes don't make it to the kernel */
1773 SetLastError(ERROR_INVALID_PARAMETER);
1774 return FALSE;
1775 }
1776
1777 /* Send the request to the kernel, and don't touch the foreground flag */
1778 PriorityClass.Foreground = FALSE;
1779 Status = NtSetInformationProcess(hProcess,
1780 ProcessPriorityClass,
1781 &PriorityClass,
1782 sizeof(PROCESS_PRIORITY_CLASS));
1783
1784 /* Release the privilege if we had it */
1785 if (State) RtlReleasePrivilege(State);
1786 if (!NT_SUCCESS(Status))
1787 {
1788 /* Handle error path */
1789 BaseSetLastNTError(Status);
1790 return FALSE;
1791 }
1792
1793 /* All done */
1794 return TRUE;
1795 }
1796
1797 /*
1798 * @implemented
1799 */
1800 DWORD
1801 WINAPI
1802 GetProcessVersion(IN DWORD ProcessId)
1803 {
1804 DWORD Version = 0;
1805 PIMAGE_NT_HEADERS NtHeader;
1806 PIMAGE_DOS_HEADER DosHeader;
1807 PPEB Peb;
1808 PROCESS_BASIC_INFORMATION ProcessBasicInfo;
1809 PVOID BaseAddress;
1810 ULONG e_lfanew;
1811 HANDLE ProcessHandle = NULL;
1812 NTSTATUS Status;
1813 USHORT VersionData[2];
1814 BOOLEAN Result;
1815
1816 /* We'll be accessing stuff that can fault, so protect everything with SEH */
1817 _SEH2_TRY
1818 {
1819 /* It this an in-process or out-of-process request? */
1820 if (!(ProcessId) || (GetCurrentProcessId() == ProcessId))
1821 {
1822 /* It's in-process, so just read our own header */
1823 NtHeader = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
1824 if (!NtHeader)
1825 {
1826 /* Unable to read the NT header, something is wrong here... */
1827 Status = STATUS_INVALID_IMAGE_FORMAT;
1828 goto Error;
1829 }
1830
1831 /* Get the version straight out of the NT header */
1832 Version = MAKELONG(NtHeader->OptionalHeader.MinorSubsystemVersion,
1833 NtHeader->OptionalHeader.MajorSubsystemVersion);
1834 }
1835 else
1836 {
1837 /* Out-of-process, so open it */
1838 ProcessHandle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
1839 FALSE,
1840 ProcessId);
1841 if (!ProcessHandle) _SEH2_YIELD(return 0);
1842
1843 /* Try to find out where its PEB lives */
1844 Status = NtQueryInformationProcess(ProcessHandle,
1845 ProcessBasicInformation,
1846 &ProcessBasicInfo,
1847 sizeof(ProcessBasicInfo),
1848 NULL);
1849
1850 if (!NT_SUCCESS(Status)) goto Error;
1851 Peb = ProcessBasicInfo.PebBaseAddress;
1852
1853 /* Now that we have the PEB, read the image base address out of it */
1854 Result = ReadProcessMemory(ProcessHandle,
1855 &Peb->ImageBaseAddress,
1856 &BaseAddress,
1857 sizeof(BaseAddress),
1858 NULL);
1859 if (!Result) goto Error;
1860
1861 /* Now read the e_lfanew (offset to NT header) from the base */
1862 DosHeader = BaseAddress;
1863 Result = ReadProcessMemory(ProcessHandle,
1864 &DosHeader->e_lfanew,
1865 &e_lfanew,
1866 sizeof(e_lfanew),
1867 NULL);
1868 if (!Result) goto Error;
1869
1870 /* And finally, read the NT header itself by adding the offset */
1871 NtHeader = (PVOID)((ULONG_PTR)BaseAddress + e_lfanew);
1872 Result = ReadProcessMemory(ProcessHandle,
1873 &NtHeader->OptionalHeader.MajorSubsystemVersion,
1874 &VersionData,
1875 sizeof(VersionData),
1876 NULL);
1877 if (!Result) goto Error;
1878
1879 /* Get the version straight out of the NT header */
1880 Version = MAKELONG(VersionData[0], VersionData[1]);
1881
1882 Error:
1883 /* If there was an error anywhere, set the last error */
1884 if (!NT_SUCCESS(Status)) BaseSetLastNTError(Status);
1885 }
1886 }
1887 _SEH2_FINALLY
1888 {
1889 /* Close the process handle */
1890 if (ProcessHandle) CloseHandle(ProcessHandle);
1891 }
1892 _SEH2_END;
1893
1894 /* And return the version data */
1895 return Version;
1896 }
1897
1898 /*
1899 * @implemented
1900 */
1901 BOOL
1902 WINAPI
1903 GetProcessIoCounters(IN HANDLE hProcess,
1904 OUT PIO_COUNTERS lpIoCounters)
1905 {
1906 NTSTATUS Status;
1907
1908 /* Query the kernel. Structures are identical, so let it do the copy too. */
1909 Status = NtQueryInformationProcess(hProcess,
1910 ProcessIoCounters,
1911 lpIoCounters,
1912 sizeof(IO_COUNTERS),
1913 NULL);
1914 if (!NT_SUCCESS(Status))
1915 {
1916 /* Handle error path */
1917 BaseSetLastNTError(Status);
1918 return FALSE;
1919 }
1920
1921 /* All done */
1922 return TRUE;
1923 }
1924
1925 /*
1926 * @implemented
1927 */
1928 BOOL
1929 WINAPI
1930 GetProcessPriorityBoost(IN HANDLE hProcess,
1931 OUT PBOOL pDisablePriorityBoost)
1932 {
1933 NTSTATUS Status;
1934 ULONG PriorityBoost;
1935
1936 /* Query the kernel */
1937 Status = NtQueryInformationProcess(hProcess,
1938 ProcessPriorityBoost,
1939 &PriorityBoost,
1940 sizeof(ULONG),
1941 NULL);
1942 if (NT_SUCCESS(Status))
1943 {
1944 /* Convert from ULONG to a BOOL */
1945 *pDisablePriorityBoost = PriorityBoost ? TRUE : FALSE;
1946 return TRUE;
1947 }
1948
1949 /* Handle error path */
1950 BaseSetLastNTError(Status);
1951 return FALSE;
1952 }
1953
1954 /*
1955 * @implemented
1956 */
1957 BOOL
1958 WINAPI
1959 SetProcessPriorityBoost(IN HANDLE hProcess,
1960 IN BOOL bDisablePriorityBoost)
1961 {
1962 NTSTATUS Status;
1963 ULONG PriorityBoost;
1964
1965 /* Enforce that this is a BOOL, and send it to the kernel as a ULONG */
1966 PriorityBoost = (bDisablePriorityBoost ? TRUE : FALSE);
1967 Status = NtSetInformationProcess(hProcess,
1968 ProcessPriorityBoost,
1969 &PriorityBoost,
1970 sizeof(ULONG));
1971 if (!NT_SUCCESS(Status))
1972 {
1973 /* Handle error path */
1974 BaseSetLastNTError(Status);
1975 return FALSE;
1976 }
1977
1978 /* All done */
1979 return TRUE;
1980 }
1981
1982 /*
1983 * @implemented
1984 */
1985 BOOL
1986 WINAPI
1987 GetProcessHandleCount(IN HANDLE hProcess,
1988 OUT PDWORD pdwHandleCount)
1989 {
1990 ULONG phc;
1991 NTSTATUS Status;
1992
1993 /* Query the kernel */
1994 Status = NtQueryInformationProcess(hProcess,
1995 ProcessHandleCount,
1996 &phc,
1997 sizeof(ULONG),
1998 NULL);
1999 if (NT_SUCCESS(Status))
2000 {
2001 /* Copy the count and return sucecss */
2002 *pdwHandleCount = phc;
2003 return TRUE;
2004 }
2005
2006 /* Handle error path */
2007 BaseSetLastNTError(Status);
2008 return FALSE;
2009 }
2010
2011 /*
2012 * @implemented
2013 */
2014 BOOL
2015 WINAPI
2016 IsWow64Process(IN HANDLE hProcess,
2017 OUT PBOOL Wow64Process)
2018 {
2019 ULONG_PTR pbi;
2020 NTSTATUS Status;
2021
2022 /* Query the kernel */
2023 Status = NtQueryInformationProcess(hProcess,
2024 ProcessWow64Information,
2025 &pbi,
2026 sizeof(pbi),
2027 NULL);
2028 if (!NT_SUCCESS(Status))
2029 {
2030 /* Handle error path */
2031 BaseSetLastNTError(Status);
2032 return FALSE;
2033 }
2034
2035 /* Enforce this is a BOOL, and return success */
2036 *Wow64Process = (pbi != 0);
2037 return TRUE;
2038 }
2039
2040 /*
2041 * @implemented
2042 */
2043 LPSTR
2044 WINAPI
2045 GetCommandLineA(VOID)
2046 {
2047 return BaseAnsiCommandLine.Buffer;
2048 }
2049
2050 /*
2051 * @implemented
2052 */
2053 LPWSTR
2054 WINAPI
2055 GetCommandLineW(VOID)
2056 {
2057 return BaseUnicodeCommandLine.Buffer;
2058 }
2059
2060 /*
2061 * @implemented
2062 */
2063 BOOL
2064 NTAPI
2065 ReadProcessMemory(IN HANDLE hProcess,
2066 IN LPCVOID lpBaseAddress,
2067 IN LPVOID lpBuffer,
2068 IN SIZE_T nSize,
2069 OUT SIZE_T* lpNumberOfBytesRead)
2070 {
2071 NTSTATUS Status;
2072
2073 /* Do the read */
2074 Status = NtReadVirtualMemory(hProcess,
2075 (PVOID)lpBaseAddress,
2076 lpBuffer,
2077 nSize,
2078 &nSize);
2079
2080 /* In user-mode, this parameter is optional */
2081 if (lpNumberOfBytesRead) *lpNumberOfBytesRead = nSize;
2082 if (!NT_SUCCESS(Status))
2083 {
2084 /* We failed */
2085 BaseSetLastNTError(Status);
2086 return FALSE;
2087 }
2088
2089 /* Return success */
2090 return TRUE;
2091 }
2092
2093 /*
2094 * @implemented
2095 */
2096 BOOL
2097 NTAPI
2098 WriteProcessMemory(IN HANDLE hProcess,
2099 IN LPVOID lpBaseAddress,
2100 IN LPCVOID lpBuffer,
2101 IN SIZE_T nSize,
2102 OUT SIZE_T *lpNumberOfBytesWritten)
2103 {
2104 NTSTATUS Status;
2105 ULONG OldValue;
2106 SIZE_T RegionSize;
2107 PVOID Base;
2108 BOOLEAN UnProtect;
2109
2110 /* Set parameters for protect call */
2111 RegionSize = nSize;
2112 Base = lpBaseAddress;
2113
2114 /* Check the current status */
2115 Status = NtProtectVirtualMemory(hProcess,
2116 &Base,
2117 &RegionSize,
2118 PAGE_EXECUTE_READWRITE,
2119 &OldValue);
2120 if (NT_SUCCESS(Status))
2121 {
2122 /* Check if we are unprotecting */
2123 UnProtect = OldValue & (PAGE_READWRITE |
2124 PAGE_WRITECOPY |
2125 PAGE_EXECUTE_READWRITE |
2126 PAGE_EXECUTE_WRITECOPY) ? FALSE : TRUE;
2127 if (!UnProtect)
2128 {
2129 /* Set the new protection */
2130 Status = NtProtectVirtualMemory(hProcess,
2131 &Base,
2132 &RegionSize,
2133 OldValue,
2134 &OldValue);
2135
2136 /* Write the memory */
2137 Status = NtWriteVirtualMemory(hProcess,
2138 lpBaseAddress,
2139 (LPVOID)lpBuffer,
2140 nSize,
2141 &nSize);
2142
2143 /* In Win32, the parameter is optional, so handle this case */
2144 if (lpNumberOfBytesWritten) *lpNumberOfBytesWritten = nSize;
2145
2146 if (!NT_SUCCESS(Status))
2147 {
2148 /* We failed */
2149 BaseSetLastNTError(Status);
2150 return FALSE;
2151 }
2152
2153 /* Flush the ITLB */
2154 NtFlushInstructionCache(hProcess, lpBaseAddress, nSize);
2155 return TRUE;
2156 }
2157 else
2158 {
2159 /* Check if we were read only */
2160 if (OldValue & (PAGE_NOACCESS | PAGE_READONLY))
2161 {
2162 /* Restore protection and fail */
2163 NtProtectVirtualMemory(hProcess,
2164 &Base,
2165 &RegionSize,
2166 OldValue,
2167 &OldValue);
2168 BaseSetLastNTError(STATUS_ACCESS_VIOLATION);
2169
2170 /* Note: This is what Windows returns and code depends on it */
2171 return STATUS_ACCESS_VIOLATION;
2172 }
2173
2174 /* Otherwise, do the write */
2175 Status = NtWriteVirtualMemory(hProcess,
2176 lpBaseAddress,
2177 (LPVOID)lpBuffer,
2178 nSize,
2179 &nSize);
2180
2181 /* In Win32, the parameter is optional, so handle this case */
2182 if (lpNumberOfBytesWritten) *lpNumberOfBytesWritten = nSize;
2183
2184 /* And restore the protection */
2185 NtProtectVirtualMemory(hProcess,
2186 &Base,
2187 &RegionSize,
2188 OldValue,
2189 &OldValue);
2190 if (!NT_SUCCESS(Status))
2191 {
2192 /* We failed */
2193 BaseSetLastNTError(STATUS_ACCESS_VIOLATION);
2194
2195 /* Note: This is what Windows returns and code depends on it */
2196 return STATUS_ACCESS_VIOLATION;
2197 }
2198
2199 /* Flush the ITLB */
2200 NtFlushInstructionCache(hProcess, lpBaseAddress, nSize);
2201 return TRUE;
2202 }
2203 }
2204 else
2205 {
2206 /* We failed */
2207 BaseSetLastNTError(Status);
2208 return FALSE;
2209 }
2210 }
2211
2212 /*
2213 * @implemented
2214 */
2215 BOOL
2216 WINAPI
2217 ProcessIdToSessionId(IN DWORD dwProcessId,
2218 OUT PDWORD pSessionId)
2219 {
2220 PROCESS_SESSION_INFORMATION SessionInformation;
2221 OBJECT_ATTRIBUTES ObjectAttributes;
2222 CLIENT_ID ClientId;
2223 HANDLE ProcessHandle;
2224 NTSTATUS Status;
2225
2226 /* Do a quick check if the pointer is not writable */
2227 if (IsBadWritePtr(pSessionId, sizeof(DWORD)))
2228 {
2229 /* Fail fast */
2230 SetLastError(ERROR_INVALID_PARAMETER);
2231 return FALSE;
2232 }
2233
2234 /* Open the process passed in by ID */
2235 ClientId.UniqueProcess = UlongToHandle(dwProcessId);
2236 ClientId.UniqueThread = 0;
2237 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
2238 Status = NtOpenProcess(&ProcessHandle,
2239 PROCESS_QUERY_INFORMATION,
2240 &ObjectAttributes,
2241 &ClientId);
2242 if (NT_SUCCESS(Status))
2243 {
2244 /* Query the session ID from the kernel */
2245 Status = NtQueryInformationProcess(ProcessHandle,
2246 ProcessSessionInformation,
2247 &SessionInformation,
2248 sizeof(SessionInformation),
2249 NULL);
2250
2251 /* Close the handle and check if we suceeded */
2252 NtClose(ProcessHandle);
2253 if (NT_SUCCESS(Status))
2254 {
2255 /* Return the session ID */
2256 *pSessionId = SessionInformation.SessionId;
2257 return TRUE;
2258 }
2259 }
2260
2261 /* Set error code and fail */
2262 BaseSetLastNTError(Status);
2263 return FALSE;
2264 }
2265
2266
2267 #define AddToHandle(x,y) (x) = (HANDLE)((ULONG_PTR)(x) | (y));
2268 #define RemoveFromHandle(x,y) (x) = (HANDLE)((ULONG_PTR)(x) & ~(y));
2269 C_ASSERT(PROCESS_PRIORITY_CLASS_REALTIME == (PROCESS_PRIORITY_CLASS_HIGH + 1));
2270
2271 /*
2272 * @implemented
2273 */
2274 BOOL
2275 WINAPI
2276 CreateProcessInternalW(IN HANDLE hUserToken,
2277 IN LPCWSTR lpApplicationName,
2278 IN LPWSTR lpCommandLine,
2279 IN LPSECURITY_ATTRIBUTES lpProcessAttributes,
2280 IN LPSECURITY_ATTRIBUTES lpThreadAttributes,
2281 IN BOOL bInheritHandles,
2282 IN DWORD dwCreationFlags,
2283 IN LPVOID lpEnvironment,
2284 IN LPCWSTR lpCurrentDirectory,
2285 IN LPSTARTUPINFOW lpStartupInfo,
2286 IN LPPROCESS_INFORMATION lpProcessInformation,
2287 OUT PHANDLE hNewToken)
2288 {
2289 //
2290 // Core variables used for creating the initial process and thread
2291 //
2292 SECURITY_ATTRIBUTES LocalThreadAttributes, LocalProcessAttributes;
2293 OBJECT_ATTRIBUTES LocalObjectAttributes;
2294 POBJECT_ATTRIBUTES ObjectAttributes;
2295 SECTION_IMAGE_INFORMATION ImageInformation;
2296 IO_STATUS_BLOCK IoStatusBlock;
2297 CLIENT_ID ClientId;
2298 ULONG NoWindow, RegionSize, StackSize, ErrorCode, Flags;
2299 USHORT ImageMachine;
2300 ULONG ParameterFlags, PrivilegeValue, HardErrorMode, ErrorResponse;
2301 ULONG_PTR ErrorParameters[2];
2302 BOOLEAN InJob, SaferNeeded, UseLargePages, HavePrivilege;
2303 BOOLEAN QuerySection, SkipSaferAndAppCompat;
2304 CONTEXT Context;
2305 BASE_API_MESSAGE CsrMsg;
2306 PBASE_CREATE_PROCESS CreateProcessMsg;
2307 PCSR_CAPTURE_BUFFER CaptureBuffer;
2308 PVOID BaseAddress, PrivilegeState, RealTimePrivilegeState;
2309 HANDLE DebugHandle, TokenHandle, JobHandle, KeyHandle, ThreadHandle;
2310 HANDLE FileHandle, SectionHandle, ProcessHandle;
2311 ULONG ResumeCount;
2312 PROCESS_PRIORITY_CLASS PriorityClass;
2313 NTSTATUS Status, Status1, ImageDbgStatus;
2314 PPEB Peb, RemotePeb;
2315 PTEB Teb;
2316 INITIAL_TEB InitialTeb;
2317 PVOID TibValue;
2318 PIMAGE_NT_HEADERS NtHeaders;
2319 STARTUPINFOW StartupInfo;
2320 PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
2321 UNICODE_STRING DebuggerString;
2322 BOOL Result;
2323 //
2324 // Variables used for command-line and argument parsing
2325 //
2326 PCHAR pcScan;
2327 SIZE_T n;
2328 WCHAR SaveChar;
2329 ULONG Length, CurdirLength, CmdQuoteLength;
2330 ULONG CmdLineLength, ResultSize;
2331 PWCHAR QuotedCmdLine, AnsiCmdCommand, ExtBuffer, CurrentDirectory;
2332 PWCHAR NullBuffer, ScanString, NameBuffer, SearchPath, DebuggerCmdLine;
2333 ANSI_STRING AnsiEnv;
2334 UNICODE_STRING UnicodeEnv, PathName;
2335 BOOLEAN SearchRetry, QuotesNeeded, CmdLineIsAppName, HasQuotes;
2336
2337 //
2338 // Variables used for Fusion/SxS (Side-by-Side Assemblies)
2339 //
2340 RTL_PATH_TYPE SxsPathType, PathType;
2341 #if _SXS_SUPPORT_ENABLED_
2342 PRTL_BUFFER ByteBuffer;
2343 PRTL_UNICODE_STRING_BUFFER ThisBuffer, Buffer, SxsStaticBuffers[5];
2344 PRTL_UNICODE_STRING_BUFFER* BufferHead, SxsStringBuffer;
2345 RTL_UNICODE_STRING_BUFFER SxsWin32ManifestPath, SxsNtManifestPath;
2346 RTL_UNICODE_STRING_BUFFER SxsWin32PolicyPath, SxsNtPolicyPath;
2347 RTL_UNICODE_STRING_BUFFER SxsWin32AssemblyDirectory;
2348 BASE_MSG_SXS_HANDLES MappedHandles, Handles, FileHandles;
2349 PVOID CapturedStrings[3];
2350 SXS_WIN32_NT_PATH_PAIR ExePathPair, ManifestPathPair, PolicyPathPair;
2351 SXS_OVERRIDE_MANIFEST OverrideMannifest;
2352 UNICODE_STRING FreeString, SxsNtExePath;
2353 PWCHAR SxsConglomeratedBuffer, StaticBuffer;
2354 ULONG ConglomeratedBufferSizeBytes, StaticBufferSize, i;
2355 #endif
2356 ULONG FusionFlags;
2357
2358 //
2359 // Variables used for path conversion (and partially Fusion/SxS)
2360 //
2361 PWCHAR FilePart, PathBuffer, FreeBuffer;
2362 BOOLEAN TranslationStatus;
2363 RTL_RELATIVE_NAME_U SxsWin32RelativePath;
2364 UNICODE_STRING PathBufferString, SxsWin32ExePath;
2365
2366 //
2367 // Variables used by Application Compatibility (and partially Fusion/SxS)
2368 //
2369 PVOID AppCompatSxsData, AppCompatData;
2370 ULONG AppCompatSxsDataSize, AppCompatDataSize;
2371 //
2372 // Variables used by VDM (Virtual Dos Machine) and WOW32 (16-bit Support)
2373 //
2374 ULONG BinarySubType, VdmBinaryType, VdmTask, VdmReserve;
2375 ULONG VdmUndoLevel;
2376 BOOLEAN UseVdmReserve;
2377 HANDLE VdmWaitObject;
2378 ANSI_STRING VdmAnsiEnv;
2379 UNICODE_STRING VdmString, VdmUnicodeEnv;
2380 BOOLEAN IsWowApp;
2381 PBASE_CHECK_VDM CheckVdmMsg;
2382
2383 /* Zero out the initial core variables and handles */
2384 QuerySection = FALSE;
2385 InJob = FALSE;
2386 SkipSaferAndAppCompat = TRUE; // HACK for making .bat/.cmd launch working again.
2387 ParameterFlags = 0;
2388 Flags = 0;
2389 DebugHandle = NULL;
2390 JobHandle = NULL;
2391 TokenHandle = NULL;
2392 FileHandle = NULL;
2393 SectionHandle = NULL;
2394 ProcessHandle = NULL;
2395 ThreadHandle = NULL;
2396 BaseAddress = (PVOID)1;
2397
2398 /* Zero out initial SxS and Application Compatibility state */
2399 AppCompatData = NULL;
2400 AppCompatDataSize = 0;
2401 AppCompatSxsData = NULL;
2402 AppCompatSxsDataSize = 0;
2403 CaptureBuffer = NULL;
2404 #if _SXS_SUPPORT_ENABLED_
2405 SxsConglomeratedBuffer = NULL;
2406 #endif
2407 FusionFlags = 0;
2408
2409 /* Zero out initial parsing variables -- others are initialized later */
2410 DebuggerCmdLine = NULL;
2411 PathBuffer = NULL;
2412 SearchPath = NULL;
2413 NullBuffer = 0;
2414 FreeBuffer = NULL;
2415 NameBuffer = NULL;
2416 CurrentDirectory = NULL;
2417 FilePart = NULL;
2418 DebuggerString.Buffer = NULL;
2419 HasQuotes = FALSE;
2420 QuotedCmdLine = NULL;
2421
2422 /* Zero out initial VDM state */
2423 VdmAnsiEnv.Buffer = NULL;
2424 VdmUnicodeEnv.Buffer = NULL;
2425 VdmString.Buffer = NULL;
2426 VdmTask = 0;
2427 VdmUndoLevel = 0;
2428 VdmBinaryType = 0;
2429 VdmReserve = 0;
2430 VdmWaitObject = NULL;
2431 UseVdmReserve = FALSE;
2432 IsWowApp = FALSE;
2433
2434 /* Set message structures */
2435 CreateProcessMsg = &CsrMsg.Data.CreateProcessRequest;
2436 CheckVdmMsg = &CsrMsg.Data.CheckVDMRequest;
2437
2438 /* Clear the more complex structures by zeroing out their entire memory */
2439 RtlZeroMemory(&Context, sizeof(Context));
2440 #if _SXS_SUPPORT_ENABLED_
2441 RtlZeroMemory(&FileHandles, sizeof(FileHandles));
2442 RtlZeroMemory(&MappedHandles, sizeof(MappedHandles));
2443 RtlZeroMemory(&Handles, sizeof(Handles));
2444 #endif
2445 RtlZeroMemory(&CreateProcessMsg->Sxs, sizeof(CreateProcessMsg->Sxs));
2446 RtlZeroMemory(&LocalProcessAttributes, sizeof(LocalProcessAttributes));
2447 RtlZeroMemory(&LocalThreadAttributes, sizeof(LocalThreadAttributes));
2448
2449 /* Zero out output arguments as well */
2450 RtlZeroMemory(lpProcessInformation, sizeof(*lpProcessInformation));
2451 if (hNewToken) *hNewToken = NULL;
2452
2453 /* Capture the special window flag */
2454 NoWindow = dwCreationFlags & CREATE_NO_WINDOW;
2455 dwCreationFlags &= ~CREATE_NO_WINDOW;
2456
2457 #if _SXS_SUPPORT_ENABLED_
2458 /* Setup the SxS static string arrays and buffers */
2459 SxsStaticBuffers[0] = &SxsWin32ManifestPath;
2460 SxsStaticBuffers[1] = &SxsWin32PolicyPath;
2461 SxsStaticBuffers[2] = &SxsWin32AssemblyDirectory;
2462 SxsStaticBuffers[3] = &SxsNtManifestPath;
2463 SxsStaticBuffers[4] = &SxsNtPolicyPath;
2464 ExePathPair.Win32 = &SxsWin32ExePath;
2465 ExePathPair.Nt = &SxsNtExePath;
2466 ManifestPathPair.Win32 = &SxsWin32ManifestPath.String;
2467 ManifestPathPair.Nt = &SxsNtManifestPath.String;
2468 PolicyPathPair.Win32 = &SxsWin32PolicyPath.String;
2469 PolicyPathPair.Nt = &SxsNtPolicyPath.String;
2470 #endif
2471
2472 DPRINT("CreateProcessInternalW: %S %S %lx\n", lpApplicationName, lpCommandLine, dwCreationFlags);
2473
2474 /* Finally, set our TEB and PEB */
2475 Teb = NtCurrentTeb();
2476 Peb = NtCurrentPeb();
2477
2478 /* This combination is illegal (see MSDN) */
2479 if ((dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) ==
2480 (DETACHED_PROCESS | CREATE_NEW_CONSOLE))
2481 {
2482 DPRINT1("Invalid flag combo used\n");
2483 SetLastError(ERROR_INVALID_PARAMETER);
2484 return FALSE;
2485 }
2486
2487 /* Convert the priority class */
2488 if (dwCreationFlags & IDLE_PRIORITY_CLASS)
2489 {
2490 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE;
2491 }
2492 else if (dwCreationFlags & BELOW_NORMAL_PRIORITY_CLASS)
2493 {
2494 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
2495 }
2496 else if (dwCreationFlags & NORMAL_PRIORITY_CLASS)
2497 {
2498 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
2499 }
2500 else if (dwCreationFlags & ABOVE_NORMAL_PRIORITY_CLASS)
2501 {
2502 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
2503 }
2504 else if (dwCreationFlags & HIGH_PRIORITY_CLASS)
2505 {
2506 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
2507 }
2508 else if (dwCreationFlags & REALTIME_PRIORITY_CLASS)
2509 {
2510 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
2511 PriorityClass.PriorityClass += (BasepIsRealtimeAllowed(FALSE) != NULL);
2512 }
2513 else
2514 {
2515 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_INVALID;
2516 }
2517
2518 /* Done with the priority masks, so get rid of them */
2519 PriorityClass.Foreground = FALSE;
2520 dwCreationFlags &= ~(NORMAL_PRIORITY_CLASS |
2521 IDLE_PRIORITY_CLASS |
2522 HIGH_PRIORITY_CLASS |
2523 REALTIME_PRIORITY_CLASS |
2524 BELOW_NORMAL_PRIORITY_CLASS |
2525 ABOVE_NORMAL_PRIORITY_CLASS);
2526
2527 /* You cannot request both a shared and a separate WoW VDM */
2528 if ((dwCreationFlags & CREATE_SEPARATE_WOW_VDM) &&
2529 (dwCreationFlags & CREATE_SHARED_WOW_VDM))
2530 {
2531 /* Fail such nonsensical attempts */
2532 DPRINT1("Invalid WOW flags\n");
2533 SetLastError(ERROR_INVALID_PARAMETER);
2534 return FALSE;
2535 }
2536 else if (!(dwCreationFlags & CREATE_SHARED_WOW_VDM) &&
2537 (BaseStaticServerData->DefaultSeparateVDM))
2538 {
2539 /* A shared WoW VDM was not requested but system enforces separation */
2540 dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
2541 }
2542
2543 /* If a shared WoW VDM is used, make sure the process isn't in a job */
2544 if (!(dwCreationFlags & CREATE_SEPARATE_WOW_VDM) &&
2545 (NtIsProcessInJob(NtCurrentProcess(), NULL)))
2546 {
2547 /* Remove the shared flag and add the separate flag */
2548 dwCreationFlags = (dwCreationFlags &~ CREATE_SHARED_WOW_VDM) |
2549 CREATE_SEPARATE_WOW_VDM;
2550 }
2551
2552 /* Convert the environment */
2553 if ((lpEnvironment) && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
2554 {
2555 /* Scan the environment to calculate its Unicode size */
2556 AnsiEnv.Buffer = pcScan = (PCHAR)lpEnvironment;
2557 while ((*pcScan) || (*(pcScan + 1))) ++pcScan;
2558
2559 /* Create our ANSI String */
2560 AnsiEnv.Length = pcScan - (PCHAR)lpEnvironment + sizeof(ANSI_NULL);
2561 AnsiEnv.MaximumLength = AnsiEnv.Length + sizeof(ANSI_NULL);
2562
2563 /* Allocate memory for the Unicode Environment */
2564 UnicodeEnv.Buffer = NULL;
2565 RegionSize = AnsiEnv.MaximumLength * sizeof(WCHAR);
2566 Status = NtAllocateVirtualMemory(NtCurrentProcess(),
2567 (PVOID)&UnicodeEnv.Buffer,
2568 0,
2569 &RegionSize,
2570 MEM_COMMIT,
2571 PAGE_READWRITE);
2572 if (!NT_SUCCESS(Status))
2573 {
2574 /* Fail */
2575 BaseSetLastNTError(Status);
2576 return FALSE;
2577 }
2578
2579 /* Use the allocated size and convert */
2580 UnicodeEnv.MaximumLength = (USHORT)RegionSize;
2581 Status = RtlAnsiStringToUnicodeString(&UnicodeEnv, &AnsiEnv, FALSE);
2582 if (!NT_SUCCESS(Status))
2583 {
2584 /* Fail */
2585 NtFreeVirtualMemory(NtCurrentProcess(),
2586 (PVOID)&UnicodeEnv.Buffer,
2587 &RegionSize,
2588 MEM_RELEASE);
2589 BaseSetLastNTError(Status);
2590 return FALSE;
2591 }
2592
2593 /* Now set the Unicode environment as the environment string pointer */
2594 lpEnvironment = UnicodeEnv.Buffer;
2595 }
2596
2597 /* Make a copy of the caller's startup info since we'll modify it */
2598 StartupInfo = *lpStartupInfo;
2599
2600 /* Check if private data is being sent on the same channel as std handles */
2601 if ((StartupInfo.dwFlags & STARTF_USESTDHANDLES) &&
2602 (StartupInfo.dwFlags & (STARTF_USEHOTKEY | STARTF_SHELLPRIVATE)))
2603 {
2604 /* Cannot use the std handles since we have monitor/hotkey values */
2605 StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
2606 }
2607
2608 /* If there's a debugger, or we have to launch cmd.exe, we go back here */
2609 AppNameRetry:
2610 /* New iteration -- free any existing name buffer */
2611 if (NameBuffer)
2612 {
2613 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
2614 NameBuffer = NULL;
2615 }
2616
2617 /* New iteration -- free any existing free buffer */
2618 if (FreeBuffer)
2619 {
2620 RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer);
2621 FreeBuffer = NULL;
2622 }
2623
2624 /* New iteration -- close any existing file handle */
2625 if (FileHandle)
2626 {
2627 NtClose(FileHandle);
2628 FileHandle = NULL;
2629 }
2630
2631 /* Set the initial parsing state. This code can loop -- don't move this! */
2632 ErrorCode = 0;
2633 SearchRetry = TRUE;
2634 QuotesNeeded = FALSE;
2635 CmdLineIsAppName = FALSE;
2636
2637 /* First check if we don't have an application name */
2638 if (!lpApplicationName)
2639 {
2640 /* This should be the first time we attempt creating one */
2641 ASSERT(NameBuffer == NULL);
2642
2643 /* Allocate a buffer to hold it */
2644 NameBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
2645 0,
2646 MAX_PATH * sizeof(WCHAR));
2647 if (!NameBuffer)
2648 {
2649 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2650 Result = FALSE;
2651 goto Quickie;
2652 }
2653
2654 /* Initialize the application name and our parsing parameters */
2655 lpApplicationName = NullBuffer = ScanString = lpCommandLine;
2656
2657 /* Check for an initial quote*/
2658 if (*lpCommandLine == L'\"')
2659 {
2660 /* We found a quote, keep searching for another one */
2661 SearchRetry = FALSE;
2662 ScanString++;
2663 lpApplicationName = ScanString;
2664 while (*ScanString)
2665 {
2666 /* Have we found the terminating quote? */
2667 if (*ScanString == L'\"')
2668 {
2669 /* We're done, get out of here */
2670 NullBuffer = ScanString;
2671 HasQuotes = TRUE;
2672 break;
2673 }
2674
2675 /* Keep searching for the quote */
2676 ScanString++;
2677 NullBuffer = ScanString;
2678 }
2679 }
2680 else
2681 {
2682 StartScan:
2683 /* We simply make the application name be the command line*/
2684 lpApplicationName = lpCommandLine;
2685 while (*ScanString)
2686 {
2687 /* Check if it starts with a space or tab */
2688 if ((*ScanString == L' ') || (*ScanString == L'\t'))
2689 {
2690 /* Break out of the search loop */
2691 NullBuffer = ScanString;
2692 break;
2693 }
2694
2695 /* Keep searching for a space or tab */
2696 ScanString++;
2697 NullBuffer = ScanString;
2698 }
2699 }
2700
2701 /* We have found the end of the application name, terminate it */
2702 SaveChar = *NullBuffer;
2703 *NullBuffer = UNICODE_NULL;
2704
2705 /* New iteration -- free any existing saved path */
2706 if (SearchPath)
2707 {
2708 RtlFreeHeap(RtlGetProcessHeap(), 0, SearchPath);
2709 SearchPath = NULL;
2710 }
2711
2712 /* Now compute the final EXE path based on the name */
2713 SearchPath = BaseComputeProcessExePath((LPWSTR)lpApplicationName);
2714 DPRINT("Search Path: %S\n", SearchPath);
2715 if (!SearchPath)
2716 {
2717 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2718 Result = FALSE;
2719 goto Quickie;
2720 }
2721
2722 /* And search for the executable in the search path */
2723 Length = SearchPathW(SearchPath,
2724 lpApplicationName,
2725 L".exe",
2726 MAX_PATH,
2727 NameBuffer,
2728 NULL);
2729
2730 /* Did we find it? */
2731 if ((Length) && (Length < MAX_PATH))
2732 {
2733 /* Get file attributes */
2734 CurdirLength = GetFileAttributesW(NameBuffer);
2735 if ((CurdirLength != 0xFFFFFFFF) &&
2736 (CurdirLength & FILE_ATTRIBUTE_DIRECTORY))
2737 {
2738 /* This was a directory, fail later on */
2739 Length = 0;
2740 }
2741 else
2742 {
2743 /* It's a file! */
2744 Length++;
2745 }
2746 }
2747
2748 DPRINT("Length: %lu Buffer: %S\n", Length, NameBuffer);
2749
2750 /* Check if there was a failure in SearchPathW */
2751 if ((Length) && (Length < MAX_PATH))
2752 {
2753 /* Everything looks good, restore the name */
2754 *NullBuffer = SaveChar;
2755 lpApplicationName = NameBuffer;
2756 }
2757 else
2758 {
2759 /* Check if this was a relative path, which would explain it */
2760 PathType = RtlDetermineDosPathNameType_U(lpApplicationName);
2761 if (PathType != RtlPathTypeRelative)
2762 {
2763 /* This should fail, and give us a detailed LastError */
2764 FileHandle = CreateFileW(lpApplicationName,
2765 GENERIC_READ,
2766 FILE_SHARE_READ |
2767 FILE_SHARE_WRITE,
2768 NULL,
2769 OPEN_EXISTING,
2770 FILE_ATTRIBUTE_NORMAL,
2771 NULL);
2772 if (FileHandle != INVALID_HANDLE_VALUE)
2773 {
2774 /* It worked? Return a generic error */
2775 CloseHandle(FileHandle);
2776 FileHandle = NULL;
2777 BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
2778 }
2779 }
2780 else
2781 {
2782 /* Path was absolute, which means it doesn't exist */
2783 BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
2784 }
2785
2786 /* Did we already fail once? */
2787 if (ErrorCode)
2788 {
2789 /* Set the error code */
2790 SetLastError(ErrorCode);
2791 }
2792 else
2793 {
2794 /* Not yet, cache it */
2795 ErrorCode = GetLastError();
2796 }
2797
2798 /* Put back the command line */
2799 *NullBuffer = SaveChar;
2800 lpApplicationName = NameBuffer;
2801
2802 /* It's possible there's whitespace in the directory name */
2803 if (!(*ScanString) || !(SearchRetry))
2804 {
2805 /* Not the case, give up completely */
2806 Result = FALSE;
2807 goto Quickie;
2808 }
2809
2810 /* There are spaces, so keep trying the next possibility */
2811 ScanString++;
2812 NullBuffer = ScanString;
2813
2814 /* We will have to add a quote, since there is a space */
2815 QuotesNeeded = TRUE;
2816 HasQuotes = TRUE;
2817 goto StartScan;
2818 }
2819 }
2820 else if (!(lpCommandLine) || !(*lpCommandLine))
2821 {
2822 /* We don't have a command line, so just use the application name */
2823 CmdLineIsAppName = TRUE;
2824 lpCommandLine = (LPWSTR)lpApplicationName;
2825 }
2826
2827 /* Convert the application name to its NT path */
2828 TranslationStatus = RtlDosPathNameToRelativeNtPathName_U(lpApplicationName,
2829 &PathName,
2830 NULL,
2831 &SxsWin32RelativePath);
2832 if (!TranslationStatus)
2833 {
2834 /* Path must be invaild somehow, bail out */
2835 DPRINT1("Path translation for SxS failed\n");
2836 SetLastError(ERROR_PATH_NOT_FOUND);
2837 Result = FALSE;
2838 goto Quickie;
2839 }
2840
2841 /* Setup the buffer that needs to be freed at the end */
2842 ASSERT(FreeBuffer == NULL);
2843 FreeBuffer = PathName.Buffer;
2844
2845 /* Check what kind of path the application is, for SxS (Fusion) purposes */
2846 RtlInitUnicodeString(&SxsWin32ExePath, lpApplicationName);
2847 SxsPathType = RtlDetermineDosPathNameType_U(lpApplicationName);
2848 if ((SxsPathType != RtlPathTypeDriveAbsolute) &&
2849 (SxsPathType != RtlPathTypeLocalDevice) &&
2850 (SxsPathType != RtlPathTypeRootLocalDevice) &&
2851 (SxsPathType != RtlPathTypeUncAbsolute))
2852 {
2853 /* Relative-type path, get the full path */
2854 RtlInitEmptyUnicodeString(&PathBufferString, NULL, 0);
2855 Status = RtlGetFullPathName_UstrEx(&SxsWin32ExePath,
2856 NULL,
2857 &PathBufferString,
2858 NULL,
2859 NULL,
2860 NULL,
2861 &SxsPathType,
2862 NULL);
2863 if (!NT_SUCCESS(Status))
2864 {
2865 /* Fail the rest of the create */
2866 RtlReleaseRelativeName(&SxsWin32RelativePath);
2867 BaseSetLastNTError(Status);
2868 Result = FALSE;
2869 goto Quickie;
2870 }
2871
2872 /* Use this full path as the SxS path */
2873 SxsWin32ExePath = PathBufferString;
2874 PathBuffer = PathBufferString.Buffer;
2875 PathBufferString.Buffer = NULL;
2876 DPRINT("SxS Path: %S\n", PathBuffer);
2877 }
2878
2879 /* Also set the .EXE path based on the path name */
2880 #if _SXS_SUPPORT_ENABLED_
2881 SxsNtExePath = PathName;
2882 #endif
2883 if (SxsWin32RelativePath.RelativeName.Length)
2884 {
2885 /* If it's relative, capture the relative name */
2886 PathName = SxsWin32RelativePath.RelativeName;
2887 }
2888 else
2889 {
2890 /* Otherwise, it's absolute, make sure no relative dir is used */
2891 SxsWin32RelativePath.ContainingDirectory = NULL;
2892 }
2893
2894 /* Now use the path name, and the root path, to try opening the app */
2895 DPRINT("Path: %wZ. Dir: %p\n", &PathName, SxsWin32RelativePath.ContainingDirectory);
2896 InitializeObjectAttributes(&LocalObjectAttributes,
2897 &PathName,
2898 OBJ_CASE_INSENSITIVE,
2899 SxsWin32RelativePath.ContainingDirectory,
2900 NULL);
2901 Status = NtOpenFile(&FileHandle,
2902 SYNCHRONIZE |
2903 FILE_READ_ATTRIBUTES |
2904 FILE_READ_DATA |
2905 FILE_EXECUTE,
2906 &LocalObjectAttributes,
2907 &IoStatusBlock,
2908 FILE_SHARE_READ | FILE_SHARE_DELETE,
2909 FILE_SYNCHRONOUS_IO_NONALERT |
2910 FILE_NON_DIRECTORY_FILE);
2911 if (!NT_SUCCESS(Status))
2912 {
2913 /* Try to open the app just for execute purposes instead */
2914 Status = NtOpenFile(&FileHandle,
2915 SYNCHRONIZE | FILE_EXECUTE,
2916 &LocalObjectAttributes,
2917 &IoStatusBlock,
2918 FILE_SHARE_READ | FILE_SHARE_DELETE,
2919 FILE_SYNCHRONOUS_IO_NONALERT |
2920 FILE_NON_DIRECTORY_FILE);
2921 }
2922
2923 /* Cleanup in preparation for failure or success */
2924 RtlReleaseRelativeName(&SxsWin32RelativePath);
2925 if (!NT_SUCCESS(Status))
2926 {
2927 /* Failure path, try to understand why */
2928 DPRINT1("Open file failed: %lx\n", Status);
2929 if (RtlIsDosDeviceName_U(lpApplicationName))
2930 {
2931 /* If a device is being executed, return this special error code */
2932 SetLastError(ERROR_BAD_DEVICE);
2933 Result = FALSE;
2934 goto Quickie;
2935 }
2936 else
2937 {
2938 /* Otherwise return the converted NT error code */
2939 BaseSetLastNTError(Status);
2940 Result = FALSE;
2941 goto Quickie;
2942 }
2943 }
2944
2945 /* Did the caller specify a desktop? */
2946 if (!StartupInfo.lpDesktop)
2947 {
2948 /* Use the one from the current process */
2949 StartupInfo.lpDesktop = Peb->ProcessParameters->DesktopInfo.Buffer;
2950 }
2951
2952 /* Create a section for this file */
2953 Status = NtCreateSection(&SectionHandle,
2954 SECTION_ALL_ACCESS,
2955 NULL,
2956 NULL,
2957 PAGE_EXECUTE,
2958 SEC_IMAGE,
2959 FileHandle);
2960 DPRINT("Section status: %lx\n", Status);
2961 if (NT_SUCCESS(Status))
2962 {
2963 /* Are we running on Windows Embedded, Datacenter, Blade or Starter? */
2964 if (SharedUserData->SuiteMask & (VER_SUITE_EMBEDDEDNT |
2965 VER_SUITE_DATACENTER |
2966 VER_SUITE_PERSONAL |
2967 VER_SUITE_BLADE))
2968 {
2969 /* These SKUs do not allow running certain applications */
2970 Status = BasepCheckWebBladeHashes(FileHandle);
2971 if (Status == STATUS_ACCESS_DENIED)
2972 {
2973 /* And this is one of them! */
2974 DPRINT1("Invalid Blade hashes!\n");
2975 SetLastError(ERROR_ACCESS_DISABLED_WEBBLADE);
2976 Result = FALSE;
2977 goto Quickie;
2978 }
2979
2980 /* Did we get some other failure? */
2981 if (!NT_SUCCESS(Status))
2982 {
2983 /* If we couldn't check the hashes, assume nefariousness */
2984 DPRINT1("Tampered Blade hashes!\n");
2985 SetLastError(ERROR_ACCESS_DISABLED_WEBBLADE_TAMPER);
2986 Result = FALSE;
2987 goto Quickie;
2988 }
2989 }
2990
2991 /* Now do Winsafer, etc, checks */
2992 Status = BasepIsProcessAllowed((LPWSTR)lpApplicationName);
2993 if (!NT_SUCCESS(Status))
2994 {
2995 /* Fail if we're not allowed to launch the process */
2996 DPRINT1("Process not allowed to launch: %lx\n", Status);
2997 BaseSetLastNTError(Status);
2998 if (SectionHandle)
2999 {
3000 NtClose(SectionHandle);
3001 SectionHandle = NULL;
3002 }
3003 Result = FALSE;
3004 goto Quickie;
3005 }
3006
3007 /* Is a DOS VDM being forced, but we already have a WOW32 instance ready? */
3008 if ((dwCreationFlags & CREATE_FORCEDOS) &&
3009 (BaseStaticServerData->IsWowTaskReady))
3010 {
3011 /* This request can't be satisfied, instead, a separate VDM is needed */
3012 dwCreationFlags &= ~(CREATE_FORCEDOS | CREATE_SHARED_WOW_VDM);
3013 dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
3014
3015 /* Set a failure code, ask for VDM reservation */
3016 Status = STATUS_INVALID_IMAGE_WIN_16;
3017 UseVdmReserve = TRUE;
3018
3019 /* Close the current handle */
3020 NtClose(SectionHandle);
3021 SectionHandle = NULL;
3022
3023 /* Don't query the section later */
3024 QuerySection = FALSE;
3025 }
3026 }
3027
3028 /* Did we already do these checks? */
3029 if (!SkipSaferAndAppCompat)
3030 {
3031 /* Is everything OK so far, OR do we have an non-MZ, non-DOS app? */
3032 if ((NT_SUCCESS(Status)) ||
3033 ((Status == STATUS_INVALID_IMAGE_NOT_MZ) &&
3034 !(BaseIsDosApplication(&PathName, Status))))
3035 {
3036 /* Clear the machine type in case of failure */
3037 ImageMachine = 0;
3038
3039 /* Clean any app compat data that may have accumulated */
3040 BasepFreeAppCompatData(AppCompatData, AppCompatSxsData);
3041 AppCompatData = NULL;
3042 AppCompatSxsData = NULL;
3043
3044 /* Do we have a section? */
3045 if (SectionHandle)
3046 {
3047 /* Have we already queried it? */
3048 if (QuerySection)
3049 {
3050 /* Nothing to do */
3051 Status = STATUS_SUCCESS;
3052 }
3053 else
3054 {
3055 /* Get some information about the executable */
3056 Status = NtQuerySection(SectionHandle,
3057 SectionImageInformation,
3058 &ImageInformation,
3059 sizeof(ImageInformation),
3060 NULL);
3061 }
3062
3063 /* Do we have section information now? */
3064 if (NT_SUCCESS(Status))
3065 {
3066 /* Don't ask for it again, save the machine type */
3067 QuerySection = TRUE;
3068 ImageMachine = ImageInformation.Machine;
3069 }
3070 }
3071
3072 /* Is there a reason/Shim we shouldn't run this application? */
3073 Status = BasepCheckBadapp(FileHandle,
3074 FreeBuffer,
3075 lpEnvironment,
3076 ImageMachine,
3077 &AppCompatData,
3078 &AppCompatDataSize,
3079 &AppCompatSxsData,
3080 &AppCompatSxsDataSize,
3081 &FusionFlags);
3082 if (!NT_SUCCESS(Status))
3083 {
3084 /* This is usually the status we get back */
3085 DPRINT1("App compat launch failure: %lx\n", Status);
3086 if (Status == STATUS_ACCESS_DENIED)
3087 {
3088 /* Convert it to something more Win32-specific */
3089 SetLastError(ERROR_CANCELLED);
3090 }
3091 else
3092 {
3093 /* Some other error */
3094 BaseSetLastNTError(Status);
3095 }
3096
3097 /* Did we have a section? */
3098 if (SectionHandle)
3099 {
3100 /* Clean it up */
3101 NtClose(SectionHandle);
3102 SectionHandle = NULL;
3103 }
3104
3105 /* Fail the call */
3106 Result = FALSE;
3107 goto Quickie;
3108 }
3109 }
3110 }
3111
3112 //ASSERT((dwFusionFlags & ~SXS_APPCOMPACT_FLAG_APP_RUNNING_SAFEMODE) == 0);
3113
3114 /* Have we already done, and do we need to do, SRP (WinSafer) checks? */
3115 if (!(SkipSaferAndAppCompat) &&
3116 ~(dwCreationFlags & CREATE_PRESERVE_CODE_AUTHZ_LEVEL))
3117 {
3118 /* Assume yes */
3119 SaferNeeded = TRUE;
3120 switch (Status)
3121 {
3122 case STATUS_INVALID_IMAGE_NE_FORMAT:
3123 case STATUS_INVALID_IMAGE_PROTECT:
3124 case STATUS_INVALID_IMAGE_WIN_16:
3125 case STATUS_FILE_IS_OFFLINE:
3126 /* For all DOS, 16-bit, OS/2 images, we do*/
3127 break;
3128
3129 case STATUS_INVALID_IMAGE_NOT_MZ:
3130 /* For invalid files, we don't, unless it's a .BAT file */
3131 if (BaseIsDosApplication(&PathName, Status)) break;
3132
3133 default:
3134 /* Any other error codes we also don't */
3135 if (!NT_SUCCESS(Status))
3136 {
3137 SaferNeeded = FALSE;
3138 }
3139
3140 /* But for success, we do */
3141 break;
3142 }
3143
3144 /* Okay, so what did the checks above result in? */
3145 if (SaferNeeded)
3146 {
3147 /* We have to call into the WinSafer library and actually check */
3148 Status = BasepCheckWinSaferRestrictions(hUserToken,
3149 (LPWSTR)lpApplicationName,
3150 FileHandle,
3151 &InJob,
3152 &TokenHandle,
3153 &JobHandle);
3154 if (Status == 0xFFFFFFFF)
3155 {
3156 /* Back in 2003, they didn't have an NTSTATUS for this... */
3157 DPRINT1("WinSafer blocking process launch\n");
3158 SetLastError(ERROR_ACCESS_DISABLED_BY_POLICY);
3159 Result = FALSE;
3160 goto Quickie;
3161 }
3162
3163 /* Other status codes are not-Safer related, just convert them */
3164 if (!NT_SUCCESS(Status))
3165 {
3166 DPRINT1("Error checking WinSafer: %lx\n", Status);
3167 BaseSetLastNTError(Status);
3168 Result = FALSE;
3169 goto Quickie;
3170 }
3171 }
3172 }
3173
3174 /* The last step is to figure out why the section object was not created */
3175 switch (Status)
3176 {
3177 case STATUS_INVALID_IMAGE_WIN_16:
3178 {
3179 /* 16-bit binary. Should we use WOW or does the caller force VDM? */
3180 if (!(dwCreationFlags & CREATE_FORCEDOS))
3181 {
3182 /* Remember that we're launching WOW */
3183 IsWowApp = TRUE;
3184
3185 /* Create the VDM environment, it's valid for WOW too */
3186 Result = BaseCreateVDMEnvironment(lpEnvironment,
3187 &VdmAnsiEnv,
3188 &VdmUnicodeEnv);
3189 if (!Result)
3190 {
3191 DPRINT1("VDM environment for WOW app failed\n");
3192 goto Quickie;
3193 }
3194
3195 /* We're going to try this twice, so do a loop */
3196 while (TRUE)
3197 {
3198 /* Pick which kind of WOW mode we want to run in */
3199 VdmBinaryType = (dwCreationFlags &
3200 CREATE_SEPARATE_WOW_VDM) ?
3201 BINARY_TYPE_WOW : BINARY_TYPE_SEPARATE_WOW;
3202
3203 /* Get all the VDM settings and current status */
3204 Status = BaseCheckVDM(VdmBinaryType,
3205 lpApplicationName,
3206 lpCommandLine,
3207 lpCurrentDirectory,
3208 &VdmAnsiEnv,
3209 &CsrMsg,
3210 &VdmTask,
3211 dwCreationFlags,
3212 &StartupInfo,
3213 hUserToken);
3214
3215 /* If it worked, no need to try again */
3216 if (NT_SUCCESS(Status)) break;
3217
3218 /* Check if it's disallowed or if it's our second time */
3219 BaseSetLastNTError(Status);
3220 if ((Status == STATUS_VDM_DISALLOWED) ||
3221 (VdmBinaryType == BINARY_TYPE_SEPARATE_WOW) ||
3222 (GetLastError() == ERROR_ACCESS_DENIED))
3223 {
3224 /* Fail the call -- we won't try again */
3225 DPRINT1("VDM message failure for WOW: %lx\n", Status);
3226 Result = FALSE;
3227 goto Quickie;
3228 }
3229
3230 /* Try one more time, but with a separate WOW instance */
3231 dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
3232 }
3233
3234 /* Check which VDM state we're currently in */
3235 switch (CheckVdmMsg->VDMState & (VDM_NOT_LOADED |
3236 VDM_NOT_READY |
3237 VDM_READY))
3238 {
3239 case VDM_NOT_LOADED:
3240 /* VDM is not fully loaded, so not that much to undo */
3241 VdmUndoLevel = VDM_UNDO_PARTIAL;
3242
3243 /* Reset VDM reserve if needed */
3244 if (UseVdmReserve) VdmReserve = 1;
3245
3246 /* Get the required parameters and names for launch */
3247 Result = BaseGetVdmConfigInfo(lpCommandLine,
3248 VdmTask,
3249 VdmBinaryType,
3250 &VdmString,
3251 &VdmReserve);
3252 if (!Result)
3253 {
3254 DPRINT1("VDM Configuration failed for WOW\n");
3255 BaseSetLastNTError(Status);
3256 goto Quickie;
3257 }
3258
3259 /* Update the command-line with the VDM one instead */
3260 lpCommandLine = VdmString.Buffer;
3261 lpApplicationName = NULL;
3262
3263 /* We don't want a console, detachment, nor a window */
3264 dwCreationFlags |= CREATE_NO_WINDOW;
3265 dwCreationFlags &= ~(CREATE_NEW_CONSOLE | DETACHED_PROCESS);
3266
3267 /* Force feedback on */
3268 StartupInfo.dwFlags |= STARTF_FORCEONFEEDBACK;
3269 break;
3270
3271
3272 case VDM_READY:
3273 /* VDM is ready, so we have to undo everything */
3274 VdmUndoLevel = VDM_UNDO_REUSE;
3275
3276 /* Check if CSRSS wants us to wait on VDM */
3277 VdmWaitObject = CheckVdmMsg->WaitObjectForParent;
3278 break;
3279
3280 case VDM_NOT_READY:
3281 /* Something is wrong with VDM, we'll fail the call */
3282 DPRINT1("VDM is not ready for WOW\n");
3283 SetLastError(ERROR_NOT_READY);
3284 Result = FALSE;
3285 goto Quickie;
3286
3287 default:
3288 break;
3289 }
3290
3291 /* Since to get NULL, we allocate from 0x1, account for this */
3292 VdmReserve--;
3293
3294 /* This implies VDM is ready, so skip everything else */
3295 if (VdmWaitObject) goto VdmShortCircuit;
3296
3297 /* Don't inherit handles since we're doing VDM now */
3298 bInheritHandles = FALSE;
3299
3300 /* Had the user passed in environment? If so, destroy it */
3301 if ((lpEnvironment) &&
3302 !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
3303 {
3304 RtlDestroyEnvironment(lpEnvironment);
3305 }
3306
3307 /* We've already done all these checks, don't do them again */
3308 SkipSaferAndAppCompat = TRUE;
3309 goto AppNameRetry;
3310 }
3311
3312 // There is no break here on purpose, so FORCEDOS drops down!
3313 }
3314
3315 case STATUS_INVALID_IMAGE_PROTECT:
3316 case STATUS_INVALID_IMAGE_NOT_MZ:
3317 case STATUS_INVALID_IMAGE_NE_FORMAT:
3318 {
3319 /* We're launching an executable application */
3320 BinarySubType = BINARY_TYPE_EXE;
3321
3322 /* We can drop here from other "cases" above too, so check */
3323 if ((Status == STATUS_INVALID_IMAGE_PROTECT) ||
3324 (Status == STATUS_INVALID_IMAGE_NE_FORMAT) ||
3325 (BinarySubType = BaseIsDosApplication(&PathName, Status)))
3326 {
3327 /* We're launching a DOS application */
3328 VdmBinaryType = BINARY_TYPE_DOS;
3329
3330 /* Based on the caller environment, create a VDM one */
3331 Result = BaseCreateVDMEnvironment(lpEnvironment,
3332 &VdmAnsiEnv,
3333 &VdmUnicodeEnv);
3334 if (!Result)
3335 {
3336 DPRINT1("VDM environment for DOS failed\n");
3337 goto Quickie;
3338 }
3339
3340 /* Check the current state of the VDM subsystem */
3341 Status = BaseCheckVDM(VdmBinaryType | BinarySubType,
3342 lpApplicationName,
3343 lpCommandLine,
3344 lpCurrentDirectory,
3345 &VdmAnsiEnv,
3346 &CsrMsg,
3347 &VdmTask,
3348 dwCreationFlags,
3349 &StartupInfo,
3350 NULL);
3351 if (!NT_SUCCESS(Status))
3352 {
3353 /* Failed to inquire about VDM, fail the call */
3354 DPRINT1("VDM message failure for DOS: %lx\n", Status);
3355 BaseSetLastNTError(Status);
3356 Result = FALSE;
3357 goto Quickie;
3358 };
3359
3360 /* Handle possible VDM states */
3361 switch (CheckVdmMsg->VDMState & (VDM_NOT_LOADED |
3362 VDM_NOT_READY |
3363 VDM_READY))
3364 {
3365 case VDM_NOT_LOADED:
3366 /* If VDM is not loaded, we'll do a partial undo */
3367 VdmUndoLevel = VDM_UNDO_PARTIAL;
3368
3369 /* A VDM process can't also be detached, so fail */
3370 if (dwCreationFlags & DETACHED_PROCESS)
3371 {
3372 DPRINT1("Detached process but no VDM, not allowed\n");
3373 SetLastError(ERROR_ACCESS_DENIED);
3374 return FALSE;
3375 }
3376
3377 /* Get the required parameters and names for launch */
3378 Result = BaseGetVdmConfigInfo(lpCommandLine,
3379 VdmTask,
3380 VdmBinaryType,
3381 &VdmString,
3382 &VdmReserve);
3383 if (!Result)
3384 {
3385 DPRINT1("VDM Configuration failed for DOS\n");
3386 BaseSetLastNTError(Status);
3387 goto Quickie;
3388 }
3389
3390 /* Update the command-line to launch VDM instead */
3391 lpCommandLine = VdmString.Buffer;
3392 lpApplicationName = NULL;
3393 break;
3394
3395 case VDM_READY:
3396 /* VDM is ready, so we have to undo everything */
3397 VdmUndoLevel = VDM_UNDO_REUSE;
3398
3399 /* Check if CSRSS wants us to wait on VDM */
3400 VdmWaitObject = CheckVdmMsg->WaitObjectForParent;
3401 break;
3402
3403 case VDM_NOT_READY:
3404 /* Something is wrong with VDM, we'll fail the call */
3405 DPRINT1("VDM is not ready for DOS\n");
3406 SetLastError(ERROR_NOT_READY);
3407 Result = FALSE;
3408 goto Quickie;
3409
3410 default:
3411 break;
3412 }
3413
3414 /* Since to get NULL, we allocate from 0x1, account for this */
3415 VdmReserve--;
3416
3417 /* This implies VDM is ready, so skip everything else */
3418 if (VdmWaitObject) goto VdmShortCircuit;
3419
3420 /* Don't inherit handles since we're doing VDM now */
3421 bInheritHandles = FALSE;
3422
3423 /* Had the user passed in environment? If so, destroy it */
3424 if ((lpEnvironment) &&
3425 !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
3426 {
3427 RtlDestroyEnvironment(lpEnvironment);
3428 }
3429
3430 /* Use our VDM Unicode environment instead */
3431 lpEnvironment = VdmUnicodeEnv.Buffer;
3432 }
3433 else
3434 {
3435 /* It's a batch file, get the extension */
3436 ExtBuffer = &PathName.Buffer[PathName.Length / sizeof(WCHAR) - 4];
3437
3438 /* Make sure the extensions are correct */
3439 if ((PathName.Length < (4 * sizeof(WCHAR))) ||
3440 ((_wcsnicmp(ExtBuffer, L".bat", 4)) &&
3441 (_wcsnicmp(ExtBuffer, L".cmd", 4))))
3442 {
3443 DPRINT1("'%wZ': Invalid EXE, and not a batch or script file\n", &PathName);
3444 SetLastError(ERROR_BAD_EXE_FORMAT);
3445 Result = FALSE;
3446 goto Quickie;
3447 }
3448
3449 /* Check if we need to account for quotes around the path */
3450 CmdQuoteLength = CmdLineIsAppName || HasQuotes;
3451 if (!CmdLineIsAppName)
3452 {
3453 if (HasQuotes) CmdQuoteLength++;
3454 }
3455 else
3456 {
3457 CmdQuoteLength++;
3458 }
3459
3460 /* Calculate the length of the command line */
3461 CmdLineLength = wcslen(lpCommandLine);
3462 CmdLineLength += wcslen(CMD_STRING);
3463 CmdLineLength += CmdQuoteLength + sizeof(ANSI_NULL);
3464 CmdLineLength *= sizeof(WCHAR);
3465
3466 /* Allocate space for the new command line */
3467 AnsiCmdCommand = RtlAllocateHeap(RtlGetProcessHeap(),
3468 0,
3469 CmdLineLength);
3470 if (!AnsiCmdCommand)
3471 {
3472 BaseSetLastNTError(STATUS_NO_MEMORY);
3473 Result = FALSE;
3474 goto Quickie;
3475 }
3476
3477 /* Build it */
3478 wcscpy(AnsiCmdCommand, CMD_STRING);
3479 if ((CmdLineIsAppName) || (HasQuotes))
3480 {
3481 wcscat(AnsiCmdCommand, L"\"");
3482 }
3483 wcscat(AnsiCmdCommand, lpCommandLine);
3484 if ((CmdLineIsAppName) || (HasQuotes))
3485 {
3486 wcscat(AnsiCmdCommand, L"\"");
3487 }
3488
3489 /* Create it as a Unicode String */
3490 RtlInitUnicodeString(&DebuggerString, AnsiCmdCommand);
3491
3492 /* Set the command line to this */
3493 lpCommandLine = DebuggerString.Buffer;
3494 lpApplicationName = NULL;
3495 DPRINT1("Retrying with: %S\n", lpCommandLine);
3496 }
3497
3498 /* We've already done all these checks, don't do them again */
3499 SkipSaferAndAppCompat = TRUE;
3500 goto AppNameRetry;
3501 }
3502
3503 case STATUS_INVALID_IMAGE_WIN_64:
3504 {
3505 /* 64-bit binaries are not allowed to run on 32-bit ReactOS */
3506 DPRINT1("64-bit binary, failing\n");
3507 SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH);
3508 Result = FALSE;
3509 goto Quickie;
3510 }
3511
3512 case STATUS_FILE_IS_OFFLINE:
3513 {
3514 /* Set the correct last error for this */
3515 DPRINT1("File is offline, failing\n");
3516 SetLastError(ERROR_FILE_OFFLINE);
3517 break;
3518 }
3519
3520 default:
3521 {
3522 /* Any other error, convert it to a generic Win32 error */
3523 if (!NT_SUCCESS(Status))
3524 {
3525 DPRINT1("Failed to create section: %lx\n", Status);
3526 SetLastError(ERROR_BAD_EXE_FORMAT);
3527 Result = FALSE;
3528 goto Quickie;
3529 }
3530
3531 /* Otherwise, this must be success */
3532 ASSERT(Status == STATUS_SUCCESS);
3533 break;
3534 }
3535 }
3536
3537 /* Is this not a WOW application, but a WOW32 VDM was requested for it? */
3538 if (!(IsWowApp) && (dwCreationFlags & CREATE_SEPARATE_WOW_VDM))
3539 {
3540 /* Ignore the nonsensical request */
3541 dwCreationFlags &= ~CREATE_SEPARATE_WOW_VDM;
3542 }
3543
3544 /* Did we already check information for the section? */
3545 if (!QuerySection)
3546 {
3547 /* Get some information about the executable */
3548 Status = NtQuerySection(SectionHandle,
3549 SectionImageInformation,
3550 &ImageInformation,
3551 sizeof(ImageInformation),
3552 NULL);
3553 if (!NT_SUCCESS(Status))
3554 {
3555 /* We failed, bail out */
3556 DPRINT1("Section query failed\n");
3557 BaseSetLastNTError(Status);
3558 Result = FALSE;
3559 goto Quickie;
3560 }
3561
3562 /* Don't check this later */
3563 QuerySection = TRUE;
3564 }
3565
3566 /* Check if this was linked as a DLL */
3567 if (ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL)
3568 {
3569 /* These aren't valid images to try to execute! */
3570 DPRINT1("Trying to launch a DLL, failing\n");
3571 SetLastError(ERROR_BAD_EXE_FORMAT);
3572 Result = FALSE;
3573 goto Quickie;
3574 }
3575
3576 /* Don't let callers pass in this flag -- we'll only get it from IFRO */
3577 Flags &= ~PROCESS_CREATE_FLAGS_LARGE_PAGES;
3578
3579 /* Clear the IFEO-missing flag, before we know for sure... */
3580 ParameterFlags &= ~2;
3581
3582 /* If the process is being debugged, only read IFEO if the PEB says so */
3583 if (!(dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) ||
3584 (NtCurrentPeb()->ReadImageFileExecOptions))
3585 {
3586 /* Let's do this! Attempt to open IFEO */
3587 Status1 = LdrOpenImageFileOptionsKey(&PathName, 0, &KeyHandle);
3588 if (!NT_SUCCESS(Status1))
3589 {
3590 /* We failed, set the flag so we store this in the parameters */
3591 if (Status1 == STATUS_OBJECT_NAME_NOT_FOUND) ParameterFlags |= 2;
3592 }
3593 else
3594 {
3595 /* Was this our first time going through this path? */
3596 if (!DebuggerCmdLine)
3597 {
3598 /* Allocate a buffer for the debugger path */
3599 DebuggerCmdLine = RtlAllocateHeap(RtlGetProcessHeap(),
3600 0,
3601 MAX_PATH * sizeof(WCHAR));
3602 if (!DebuggerCmdLine)
3603 {
3604 /* Close IFEO on failure */
3605 Status1 = NtClose(KeyHandle);
3606 ASSERT(NT_SUCCESS(Status1));
3607
3608 /* Fail the call */
3609 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3610 Result = FALSE;
3611 goto Quickie;
3612 }
3613 }
3614
3615 /* Now query for the debugger */
3616 Status1 = LdrQueryImageFileKeyOption(KeyHandle,
3617 L"Debugger",
3618 REG_SZ,
3619 DebuggerCmdLine,
3620 MAX_PATH * sizeof(WCHAR),
3621 &ResultSize);
3622 if (!(NT_SUCCESS(Status1)) ||
3623 (ResultSize < sizeof(WCHAR)) ||
3624 (DebuggerCmdLine[0] == UNICODE_NULL))
3625 {
3626 /* If it's not there, or too small, or invalid, ignore it */
3627 RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine);
3628 DebuggerCmdLine = NULL;
3629 }
3630
3631 /* Also query if we should map with large pages */
3632 Status1 = LdrQueryImageFileKeyOption(KeyHandle,
3633 L"UseLargePages",
3634 REG_DWORD,
3635 &UseLargePages,
3636 sizeof(UseLargePages),
3637 NULL);
3638 if ((NT_SUCCESS(Status1)) && (UseLargePages))
3639 {
3640 /* Do it! This is the only way this flag can be set */
3641 Flags |= PROCESS_CREATE_FLAGS_LARGE_PAGES;
3642 }
3643
3644 /* We're done with IFEO, can close it now */
3645 Status1 = NtClose(KeyHandle);
3646 ASSERT(NT_SUCCESS(Status1));
3647 }
3648 }
3649
3650 /* Make sure the image was compiled for this processor */
3651 if ((ImageInformation.Machine < SharedUserData->ImageNumberLow) ||
3652 (ImageInformation.Machine > SharedUserData->ImageNumberHigh))
3653 {
3654 /* It was not -- raise a hard error */
3655 ErrorResponse = ResponseOk;
3656 ErrorParameters[0] = (ULONG_PTR)&PathName;
3657 NtRaiseHardError(STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE,
3658 1,
3659 1,
3660 ErrorParameters,
3661 OptionOk,
3662 &ErrorResponse);
3663 if (Peb->ImageSubsystemMajorVersion <= 3)
3664 {
3665 /* If it's really old, return this error */
3666 SetLastError(ERROR_BAD_EXE_FORMAT);
3667 }
3668 else
3669 {
3670 /* Otherwise, return a more modern error */
3671 SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH);
3672 }
3673
3674 /* Go to the failure path */
3675 DPRINT1("Invalid image architecture: %lx\n", ImageInformation.Machine);
3676 Result = FALSE;
3677 goto Quickie;
3678 }
3679
3680 /* Check if this isn't a Windows image */
3681 if ((ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_GUI) &&
3682 (ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_CUI))
3683 {
3684 /* Get rid of section-related information since we'll retry */
3685 NtClose(SectionHandle);
3686 SectionHandle = NULL;
3687 QuerySection = FALSE;
3688
3689 /* The only other non-Windows image type we support here is POSIX */
3690 if (ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_POSIX_CUI)
3691 {
3692 /* Bail out if it's something else */
3693 SetLastError(ERROR_CHILD_NOT_COMPLETE);
3694 Result = FALSE;
3695 goto Quickie;
3696 }
3697
3698 /* Now build the command-line to have posix launch this image */
3699 Result = BuildSubSysCommandLine(L"POSIX /P ",
3700 lpApplicationName,
3701 lpCommandLine,
3702 &DebuggerString);
3703 if (!Result)
3704 {
3705 /* Bail out if that failed */
3706 DPRINT1("Subsystem command line failed\n");
3707 goto Quickie;
3708 }
3709
3710 /* And re-try launching the process, with the new command-line now */
3711 lpCommandLine = DebuggerString.Buffer;
3712 lpApplicationName = NULL;
3713
3714 /* We've already done all these checks, don't do them again */
3715 SkipSaferAndAppCompat = TRUE;
3716 DPRINT1("Retrying with: %S\n", lpCommandLine);
3717 goto AppNameRetry;
3718 }
3719
3720 /* Was this image built for a version of Windows whose images we can run? */
3721 Result = BasepIsImageVersionOk(ImageInformation.SubSystemMajorVersion,
3722 ImageInformation.SubSystemMinorVersion);
3723 if (!Result)
3724 {
3725 /* It was not, bail out */
3726 DPRINT1("Invalid subsystem version: %hu.%hu\n",
3727 ImageInformation.SubSystemMajorVersion,
3728 ImageInformation.SubSystemMinorVersion);
3729 SetLastError(ERROR_BAD_EXE_FORMAT);
3730 goto Quickie;
3731 }
3732
3733 /* Check if there is a debugger associated with the application */
3734 if (DebuggerCmdLine)
3735 {
3736 /* Get the length of the command line */
3737 n = wcslen(lpCommandLine);
3738 if (!n)
3739 {
3740 /* There's no command line, use the application name instead */
3741 lpCommandLine = (LPWSTR)lpApplicationName;
3742 n = wcslen(lpCommandLine);
3743 }
3744
3745 /* Protect against overflow */
3746 if (n > UNICODE_STRING_MAX_CHARS)
3747 {
3748 BaseSetLastNTError(STATUS_NAME_TOO_LONG);
3749 Result = FALSE;
3750 goto Quickie;
3751 }
3752
3753 /* Now add the length of the debugger command-line */
3754 n += wcslen(DebuggerCmdLine);
3755
3756 /* Again make sure we don't overflow */
3757 if (n > UNICODE_STRING_MAX_CHARS)
3758 {
3759 BaseSetLastNTError(STATUS_NAME_TOO_LONG);
3760 Result = FALSE;
3761 goto Quickie;
3762 }
3763
3764 /* Account for the quotes and space between the two */
3765 n += sizeof("\" \"") - sizeof(ANSI_NULL);
3766
3767 /* Convert to bytes, and make sure we don't overflow */
3768 n *= sizeof(WCHAR);
3769 if (n > UNICODE_STRING_MAX_BYTES)
3770 {
3771 BaseSetLastNTError(STATUS_NAME_TOO_LONG);
3772 Result = FALSE;
3773 goto Quickie;
3774 }
3775
3776 /* Allocate space for the string */
3777 DebuggerString.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, n);
3778 if (!DebuggerString.Buffer)
3779 {
3780 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3781 Result = FALSE;
3782 goto Quickie;
3783 }
3784
3785 /* Set the length */
3786 RtlInitEmptyUnicodeString(&DebuggerString,
3787 DebuggerString.Buffer,
3788 (USHORT)n);
3789
3790 /* Now perform the command line creation */
3791 ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString,
3792 DebuggerCmdLine);
3793 ASSERT(NT_SUCCESS(ImageDbgStatus));
3794 ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString, L" ");
3795 ASSERT(NT_SUCCESS(ImageDbgStatus));
3796 ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString, lpCommandLine);
3797 ASSERT(NT_SUCCESS(ImageDbgStatus));
3798
3799 /* Make sure it all looks nice */
3800 DbgPrint("BASE: Calling debugger with '%wZ'\n", &DebuggerString);
3801
3802 /* Update the command line and application name */
3803 lpCommandLine = DebuggerString.Buffer;
3804 lpApplicationName = NULL;
3805
3806 /* Close all temporary state */
3807 NtClose(SectionHandle);
3808 SectionHandle = NULL;
3809 QuerySection = FALSE;
3810
3811 /* Free all temporary memory */
3812 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
3813 NameBuffer = NULL;
3814 RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer);
3815 FreeBuffer = NULL;
3816 RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine);
3817 DebuggerCmdLine = NULL;
3818 DPRINT1("Retrying with: %S\n", lpCommandLine);
3819 goto AppNameRetry;
3820 }
3821
3822 /* Initialize the process object attributes */
3823 ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
3824 lpProcessAttributes,
3825 NULL);
3826 if ((hUserToken) && (lpProcessAttributes))
3827 {
3828 /* Auggment them with information from the user */
3829
3830 LocalProcessAttributes = *lpProcessAttributes;
3831 LocalProcessAttributes.lpSecurityDescriptor = NULL;
3832 ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
3833 &LocalProcessAttributes,
3834 NULL);
3835 }
3836
3837 /* Check if we're going to be debugged */
3838 if (dwCreationFlags & DEBUG_PROCESS)
3839 {
3840 /* Set process flag */
3841 Flags |= PROCESS_CREATE_FLAGS_BREAKAWAY;
3842 }
3843
3844 /* Check if we're going to be debugged */
3845 if (dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS))
3846 {
3847 /* Connect to DbgUi */
3848 Status = DbgUiConnectToDbg();
3849 if (!NT_SUCCESS(Status))