BOOL
WINAPI
CreateProcessInternalW(
HANDLE hUserToken,
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation,
PHANDLE hRestrictedUserToken
)
/*++
Routine Description:
This is the worker routine for CreateProcess and CreateProcessAsUser.
CreateProcessAsUser supplies a User token to be stamped on the new process.
CreateProcess supplies NULL and the current process token is used.
A process and thread object are created and a handle opened to each
object using CreateProcessInternal. Note that WinExec and LoadModule are
still supported, but are implemented as a call to CreateProcess.
Arguments:
hUserToken - Supplies an optional token handle to be set on the new process.
Process token is used is this parameter is NULL.
lpApplicationName - Supplies an optional pointer to a null terminated
character string that contains the name of the image file to
execute. This is a fully qualified DOS path name. If not
specified, then the image file name is the first whitespace
delimited token on the command line.
lpCommandLine - Supplies a null terminated character string that
contains the command line for the application to be executed.
The entire command line is made available to the new process
using GetCommandLine. If the lpApplicationName parameter was
not specified, then the first token of the command line
specifies file name of the application (note that this token
begins at the beginning of the command line and ends at the
first "white space" character). If the file name does not
contain an extension (the presence of a "."), then .EXE is
assumed. If the file name does not contain a directory path,
Windows will search for the executable file in:
- The current directory
- The windows directory
- The windows system directory
- The directories listed in the path environment variable
This parameter is optional onlu if the lpApplicationName
parameter is specified. In this case the command line the
application receives will be the application name.
lpProcessAttributes - An optional parameter that may be used to
specify the attributes of the new process. If the parameter is
not specified, then the process is created without a security
descriptor, and the resulting handle is not inherited on process
creation:
SECURITY_ATTRIBUTES Structure:
DWORD nLength - Specifies the length of this structure. Must be
set to sizeof( SECURITY_ATTRUBUTES ).
LPVOID lpSecurityDescriptor - Points to a security descriptor for
the object (must be NULL for Win32, used on NT/Win32). The
security descriptor controls the sharing of an object.
BOOL bInheritHandle - Supplies a flag that indicates whether
or not the returned handle is to be inherited by a new
process during process creation. A value of TRUE
indicates that the new process will inherit the handle.
lpThreadAttributes - An optional parameter that may be used to specify
the attributes of the new thread. If the parameter is not
specified, then the thread is created without a security
descriptor, and the resulting handle is not inherited on
process creation.
dwCreationFlags - Supplies additional flags that control the creation
of the process.
dwCreationFlags Flags:
DEBUG_PROCESS - If this flag bit is set, then the creating
process is treated as a debugger, and the process being
created is created as a debugee. All debug events occuring
in the debugee are reported to the debugger. If this bit is
clear, but the calling process is a debugee, then the
process becomes a debugee of the calling processes debugger.
If this bit is clear and the calling processes is not a
debugee then no debug related actions occur.
DEBUG_ONLY_THIS_PROCESS - If this flag is set, then the
DEBUG_PROCESS flag bit must also be set. The calling
process is is treated as a debugger, and the new process is
created as its debuggee. If the new process creates
additional processes, no debug related activities (with
respect to the debugger) occur.
CREATE_SUSPENDED - The process is created, but the initial thread
of the process remains suspended. The creator can resume this
thread using ResumeThread. Until this is done, code in the
process will not execute.
CREATE_UNICODE_ENVIRONMENT - If set, the environment pointer
points to a Unicode environment block. Otherwise, the
block is ANSI (actually OEM.)
bInheritHandles - Supplies a flag that specifies whether or not the
new process is to inherit handles to objects visible to the
calling process. A value of TRUE causes handles to be inherited
by the new process. If TRUE was specified, then for each handle
visible to the calling process, if the handle was created with
the inherit handle option, the handle is inherited to the new
process. The handle has the same granted access in the new
process as it has in the calling process, and the value of the
handle is the same.
lpEnvironment - An optional parameter, that if specified, supplies a
pointer to an environment block. If the parameter is not
specified, the environment block of the current process is used.
This environment block is made available to the new process
using GetEnvironmentStrings.
lpCurrentDirectory - An optional parameter, that if specified,
supplies a string representing the current drive and directory
for the new process. The string must be a fully qualified
pathname that includes a drive letter. If the parameter is not
specified, then the new process is created with the same current
drive and directory as the calling process. This option is
provided primarily for shells that want to start an application
and specify its initial drive and working directory.
lpStartupInfo - Supplies information that specified how the
applications window is to be shown. This structure is described
in the Win32 User Interface API Book.
lpProcessInformation - Returns identification information about the
new process.
PROCESS_INFORMATION Structure:
HANDLE hProcess - Returns a handle to the newly created process.
Through the handle, all operations on process objects are
allowed.
HANDLE hThread - Returns a handle to the newly created thread.
Through the handle, all operations on thread objects are
allowed.
DWORD dwProcessId - Returns a global process id that may be used
to identify a process. The value is valid from the time the
process is created until the time the process is terminated.
DWORD dwThreadId - Returns a global thread id that may be used
to identify a thread. The value is valid from the time the
thread is created until the time the thread is terminated.
hRestrictedUserToken - Returns a restricted token if a UsetToken was
supplied. This is applicable for the CreateProcessAsUser case.
The token is released by CreateProcessAsUser.
Return Value:
TRUE - The operation was successful
FALSE/NULL - The operation failed. Extended error status is available
using GetLastError.
--*/
{
NTSTATUS Status;
OBJECT_ATTRIBUTES Obja;
POBJECT_ATTRIBUTES pObja;
HANDLE ProcessHandle, ThreadHandle, VdmWaitHandle = NULL;
HANDLE FileHandle, SectionHandle;
CLIENT_ID ClientId;
UNICODE_STRING PathName;
IO_STATUS_BLOCK IoStatusBlock;
BOOLEAN TranslationStatus;
RTL_RELATIVE_NAME RelativeName;
PVOID FreeBuffer;
LPWSTR NameBuffer;
LPWSTR WhiteScan;
ULONG Length,i;
PROCESS_BASIC_INFORMATION ProcessInfo;
SECTION_IMAGE_INFORMATION ImageInformation;
NTSTATUS StackStatus;
BOOLEAN bStatus;
INITIAL_TEB InitialTeb;
CONTEXT ThreadContext;
PPEB Peb;
BASE_API_MSG m;
PBASE_CREATEPROCESS_MSG a = &m.u.CreateProcess;
PBASE_CHECKVDM_MSG b = &m.u.CheckVDM;
PWCH TempNull = NULL;
WCHAR TempChar;
UNICODE_STRING VdmNameString;
PVOID BaseAddress;
ULONG VdmReserve;
SIZE_T BigVdmReserve;
ULONG iTask=0;
LPWSTR CurdirBuffer, CurdirFilePart;
DWORD CurdirLength,CurdirLength2;
ULONG VDMCreationState=0;
ULONG VdmBinaryType = 0;
BOOL bMeowBinary = FALSE;
UNICODE_STRING SubSysCommandLine;
PIMAGE_NT_HEADERS NtHeaders;
DWORD dwNoWindow = (dwCreationFlags & CREATE_NO_WINDOW);
ANSI_STRING AnsiStringVDMEnv;
UNICODE_STRING UnicodeStringVDMEnv;
WCHAR ImageFileDebuggerCommand[ MAX_PATH ];
LPWSTR QuotedBuffer;
BOOLEAN QuoteInsert;
BOOLEAN QuoteCmdLine = FALSE;
BOOLEAN QuoteFound;
BOOL bSaferChecksNeeded = FALSE;
BOOLEAN SearchRetry;
BOOLEAN IsWowBinary = FALSE;
STARTUPINFOW StartupInfo;
DWORD LastError;
DWORD fileattr;
PROCESS_PRIORITY_CLASS PriClass;
PVOID State;
HANDLE DebugPortHandle = NULL;
PVOID pAppCompatDataTemp;
PVOID pAppCompatData = NULL;
DWORD cbAppCompatData = 0; // for the future
BOOLEAN bVdmRetry = FALSE;
DWORD Flags;
PVOID pAppCompatSxsData = NULL;
DWORD cbAppCompatSxsData = 0;
SXS_OVERRIDE_STREAM AppCompatSxsManifest;
PCSR_CAPTURE_HEADER CaptureBuffer = NULL;
SIZE_T SxsConglomeratedBufferSizeBytes;
PBYTE SxsConglomeratedByteBuffer = NULL; // this contains all the of the below in one large right-sized heap allocation
// if we compute its size wrong, other code (if it gets it right..) should
// do more heap allocation
ULONG sxsi; // for loop counter
RTL_UNICODE_STRING_BUFFER SxsWin32ManifestPathBuffer;
RTL_UNICODE_STRING_BUFFER SxsWin32PolicyPathBuffer;
RTL_UNICODE_STRING_BUFFER SxsWin32AssemblyDirectoryBuffer;
RTL_UNICODE_STRING_BUFFER SxsNtManifestPathBuffer;
RTL_UNICODE_STRING_BUFFER SxsNtPolicyPathBuffer;
const PRTL_UNICODE_STRING_BUFFER SxsStringBuffers[] = {
// The order here does not matter.
&SxsWin32ManifestPathBuffer,
&SxsWin32PolicyPathBuffer,
&SxsWin32AssemblyDirectoryBuffer,
&SxsNtManifestPathBuffer,
&SxsNtPolicyPathBuffer
};
UNICODE_STRING SxsWin32ExePath;
UNICODE_STRING SxsNtExePath;
BASE_MSG_SXS_HANDLES SxsExeHandles = {0};
BASE_MSG_SXS_HANDLES SxsManifestFileHandles = {0};
CONST SXS_CONSTANT_WIN32_NT_PATH_PAIR SxsExePathPair = { &SxsWin32ExePath, &SxsNtExePath };
CONST SXS_WIN32_NT_PATH_PAIR SxsManifestPathPair = { &SxsWin32ManifestPathBuffer, &SxsNtManifestPathBuffer };
CONST SXS_WIN32_NT_PATH_PAIR SxsPolicyPathPair = { &SxsWin32PolicyPathBuffer, &SxsNtPolicyPathBuffer };
BASE_MSG_SXS_HANDLES SxsPolicyHandles = {0};
PWSTR ExePathFullBuffer = NULL;
DWORD dwJobMemberLevel = 0;
HANDLE hSaferAssignmentJob = NULL;
HANDLE hSaferRestrictedToken = NULL;
DWORD dwBasePushProcessParametersFlags = 0;
#if defined(BUILD_WOW6432) || defined(_WIN64)
BOOLEAN ComPlusILImage;
LPCWSTR lpOriginalApplicationName = lpApplicationName;
LPWSTR lpOriginalCommandLine = lpCommandLine;
#endif
#if defined(WX86)
HANDLE Wx86Info = NULL;
#endif
#if defined WX86
BOOLEAN UseKnownWx86Dll;
UseKnownWx86Dll = NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll;
NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = FALSE;
#endif
RtlZeroMemory(&a->Sxs, sizeof(a->Sxs));
RtlZeroMemory(lpProcessInformation,sizeof(*lpProcessInformation));
if (ARGUMENT_PRESENT( hRestrictedUserToken )) {
*hRestrictedUserToken = NULL;
}
// Private VDM flag should be ignored; Its meant for internal use only.
dwCreationFlags &= (ULONG)~CREATE_NO_WINDOW;
if ((dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) ==
(DETACHED_PROCESS | CREATE_NEW_CONSOLE)) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
AnsiStringVDMEnv.Buffer = NULL;
UnicodeStringVDMEnv.Buffer = NULL;
//
// the lowest specified priority class is used.
//
if (dwCreationFlags & IDLE_PRIORITY_CLASS ) {
PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE;
}
else if (dwCreationFlags & BELOW_NORMAL_PRIORITY_CLASS ) {
PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
}
else if (dwCreationFlags & NORMAL_PRIORITY_CLASS ) {
PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
}
else if (dwCreationFlags & ABOVE_NORMAL_PRIORITY_CLASS ) {
PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
}
else if (dwCreationFlags & HIGH_PRIORITY_CLASS ) {
PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
}
else if (dwCreationFlags & REALTIME_PRIORITY_CLASS ) {
if ( BasepIsRealtimeAllowed(FALSE) ) {
PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_REALTIME;
}
else {
PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
}
}
else {
PriClass.PriorityClass = PROCESS_PRIORITY_CLASS_UNKNOWN;
}
PriClass.Foreground = FALSE;
dwCreationFlags = (dwCreationFlags & ~PRIORITY_CLASS_MASK );
//
// Default separate/shared VDM option if not explicitly specified.
//
if (dwCreationFlags & CREATE_SEPARATE_WOW_VDM) {
if (dwCreationFlags & CREATE_SHARED_WOW_VDM) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
}
else
if ((dwCreationFlags & CREATE_SHARED_WOW_VDM) == 0) {
if (BaseStaticServerData->DefaultSeparateVDM) {
dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
}
}
if ((dwCreationFlags & CREATE_SEPARATE_WOW_VDM) == 0) {
//
// If the creator is running inside a job object, always
// set SEPERATE_WOW_VDM so the VDM is part of the job.
//
JOBOBJECT_BASIC_UI_RESTRICTIONS UiRestrictions;
Status = NtQueryInformationJobObject(NULL,
JobObjectBasicUIRestrictions,
&UiRestrictions,
sizeof(UiRestrictions),
NULL);
if (Status != STATUS_ACCESS_DENIED) {
//
// Anything other than STATUS_ACCESS_DENIED indicates the
// current process is inside a job.
//
dwCreationFlags = (dwCreationFlags & (~CREATE_SHARED_WOW_VDM)) |
CREATE_SEPARATE_WOW_VDM;
}
}
//
// If ANSI environment, convert to Unicode
//
if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT) ) {
PUCHAR s;
STRING Ansi;
UNICODE_STRING Unicode;
MEMORY_BASIC_INFORMATION MemoryInformation;
Ansi.Buffer = s = lpEnvironment;
while (*s || *(s+1)) // find end of block
s++;
Ansi.Length = (USHORT)(s - Ansi.Buffer) + 1;
Ansi.MaximumLength = Ansi.Length + 1;
MemoryInformation.RegionSize = Ansi.MaximumLength * sizeof(WCHAR);
Unicode.Buffer = NULL;
Status = NtAllocateVirtualMemory( NtCurrentProcess(),
&Unicode.Buffer,
0,
&MemoryInformation.RegionSize,
MEM_COMMIT,
PAGE_READWRITE
);
if (!NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
return FALSE;
}
Unicode.MaximumLength = (USHORT)MemoryInformation.RegionSize;
Status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, FALSE);
if (!NT_SUCCESS(Status) ) {
NtFreeVirtualMemory( NtCurrentProcess(),
&Unicode.Buffer,
&MemoryInformation.RegionSize,
MEM_RELEASE
);
BaseSetLastNTError(Status);
return FALSE;
}
lpEnvironment = Unicode.Buffer;
}
FileHandle = NULL;
SectionHandle = NULL;
ProcessHandle = NULL;
ThreadHandle = NULL;
FreeBuffer = NULL;
NameBuffer = NULL;
VdmNameString.Buffer = NULL;
BaseAddress = (PVOID)1;
VdmReserve = 0;
CurdirBuffer = NULL;
CurdirFilePart = NULL;
SubSysCommandLine.Buffer = NULL;
QuoteFound = FALSE;
QuoteInsert = FALSE;
QuotedBuffer = NULL;
try {
//
// Make a copy of the startup info so we can change it.
//
StartupInfo = *lpStartupInfo;
//
// STARTF_USEHOTKEY means hStdInput is really the hotkey value.
// STARTF_HASSHELLDATA means std handles are used for shell-private
// data. This flag is used if an icon is passed to ShellExecuteEx.
// As a result they cannot be specified with STARTF_USESTDHANDLES.
// Consistent with Win95, USESTDHANDLES is ignored.
//
if (StartupInfo.dwFlags & STARTF_USESTDHANDLES &&
StartupInfo.dwFlags & (STARTF_USEHOTKEY | STARTF_HASSHELLDATA)) {
StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
}
VdmRetry:
//
// None of this cleanup/reinit occurs for launching a Win32 or Win64 .exe,
// but they generally do occur for launching 16bit, .bat, etc.
//
if (NameBuffer) {
RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer);
NameBuffer = NULL;
}
if (FreeBuffer) {
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
FreeBuffer = NULL;
}
if (FileHandle) {
NtClose(FileHandle);
FileHandle = NULL;
}
LastError = 0;
SearchRetry = TRUE;
QuoteInsert = FALSE;
QuoteCmdLine = FALSE;
if (!ARGUMENT_PRESENT( lpApplicationName )) {
//
// Locate the image
//
// forgot to free NameBuffer before goto VdmRetry???
ASSERT(NameBuffer == NULL);
NameBuffer = RtlAllocateHeap( RtlProcessHeap(),
MAKE_TAG( TMP_TAG ),
MAX_PATH * sizeof( WCHAR ));
if ( !NameBuffer ) {
BaseSetLastNTError(STATUS_NO_MEMORY);
return FALSE;
}
lpApplicationName = lpCommandLine;
TempNull = (PWCH)lpApplicationName;
WhiteScan = (LPWSTR)lpApplicationName;
//
// check for lead quote
//
if ( *WhiteScan == L'\"' ) {
SearchRetry = FALSE;
WhiteScan++;
lpApplicationName = WhiteScan;
while(*WhiteScan) {
if ( *WhiteScan == (WCHAR)'\"' ) {
TempNull = (PWCH)WhiteScan;
QuoteFound = TRUE;
break;
}
WhiteScan++;
TempNull = (PWCH)WhiteScan;
}
}
else {
retrywsscan:
lpApplicationName = lpCommandLine;
while(*WhiteScan) {
if ( *WhiteScan == (WCHAR)' ' ||
*WhiteScan == (WCHAR)'\t' ) {
TempNull = (PWCH)WhiteScan;
break;
}
WhiteScan++;
TempNull = (PWCH)WhiteScan;
}
}
TempChar = *TempNull;
*TempNull = UNICODE_NULL;
#ifdef WX86
//
// Wx86 applications must use x86 version of known exes
// for compatibility.
//
if (UseKnownWx86Dll) {
LPWSTR KnownName;
NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = FALSE;
KnownName = BasepWx86KnownExe(lpApplicationName);
if (KnownName) {
lpApplicationName = KnownName;
}
}
#endif
Length = SearchPathW(
NULL,
lpApplicationName,
L".exe",
MAX_PATH,
NameBuffer,
NULL
)*2;
if (Length != 0 && Length < MAX_PATH * sizeof( WCHAR )) {
//
// SearchPathW worked, but file might be a directory
// if this happens, we need to keep trying
//
fileattr = GetFileAttributesW(NameBuffer);
if ( fileattr != 0xffffffff &&
(fileattr & FILE_ATTRIBUTE_DIRECTORY) ) {
Length = 0;
} else {
Length++;
Length++;
}
}
if ( !Length || Length >= MAX_PATH<<1 ) {
//
// If we search pathed, then return file not found.
// otherwise, try to be more specific.
//
RTL_PATH_TYPE PathType;
HANDLE hFile;
PathType = RtlDetermineDosPathNameType_U(lpApplicationName);
if ( PathType != RtlPathTypeRelative ) {
//
// The failed open should set get last error properly.
//
hFile = CreateFileW(
lpApplicationName,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if ( hFile != INVALID_HANDLE_VALUE ) {
CloseHandle(hFile);
BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
}
}
else {
BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
}
//
// remember initial last error value for the retry scan path
//
if ( LastError ) {
SetLastError(LastError);
}
else {
LastError = GetLastError();
}
//
// restore the command line
//
*TempNull = TempChar;
lpApplicationName = NameBuffer;
//
// If we still have command line left, then keep going
// the point is to march through the command line looking
// for whitespace so we can try to find an image name
// launches of things like:
// c:\word 95\winword.exe /embedding -automation
// require this. Our first iteration will stop at c:\word, our next
// will stop at c:\word 95\winword.exe
//
if (*WhiteScan && SearchRetry) {
WhiteScan++;
TempNull = WhiteScan;
QuoteInsert = TRUE;
QuoteFound = TRUE;
goto retrywsscan;
}
return FALSE;
}
//
// restore the command line
//
*TempNull = TempChar;
lpApplicationName = NameBuffer;
//
// check whether it is setup.exe started by winlogon.exe
//
if (BasepIsSetupInvokedByWinLogon(lpApplicationName))
{
// validate the flag
if (!(dwCreationFlags & CREATE_IGNORE_SYSTEM_DEFAULT))
{
//
// BUGBUBGUBGUBUGBUGBUGUBGBUGUBGUB
// Winlogon does not set the flag correctly
// in phase1, ignore it(now)
// in phase2, ASSERT it
// BUGBUBGUBGUBUGBUGBUGUBGBUGUBGUB
//
dwCreationFlags |= CREATE_IGNORE_SYSTEM_DEFAULT;
}
}
}
else
if (!ARGUMENT_PRESENT( lpCommandLine ) || *lpCommandLine == UNICODE_NULL ) {
QuoteCmdLine = TRUE;
lpCommandLine = (LPWSTR)lpApplicationName;
}
#ifdef WX86
//
// Wx86 applications must use x86 version of known exes
// for compatibility.
//
if (UseKnownWx86Dll) {
LPWSTR KnownName;
NtCurrentTeb()->Wx86Thread.UseKnownWx86Dll = FALSE;
KnownName = BasepWx86KnownExe(lpApplicationName);
if (KnownName) {
RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer);
NameBuffer = KnownName;
lpApplicationName = KnownName;
}
}
#endif
//
// Translate to an NT name.
//
TranslationStatus = RtlDosPathNameToNtPathName_U(
lpApplicationName,
&PathName,
NULL,
&RelativeName
);
if ( !TranslationStatus ) {
SetLastError(ERROR_PATH_NOT_FOUND);
return FALSE;
}
// forgot to free FreeBuffer before goto VdmRetry????
ASSERT(FreeBuffer == NULL);
FreeBuffer = PathName.Buffer;
RtlInitUnicodeString(&SxsWin32ExePath, lpApplicationName);
{
RTL_PATH_TYPE SxsWin32ExePathType = RtlDetermineDosPathNameType_U(lpApplicationName);
if ((SxsWin32ExePathType != RtlPathTypeDriveAbsolute) &&
(SxsWin32ExePathType != RtlPathTypeUncAbsolute)) {
if (ExePathFullBuffer == NULL) {
// It seems that with VDM things, we can rerun this code with a new lpApplication, so
// we protect against double-allocating the buffer and just allocate a big
// MAX_PATH one the first time through, assuming it's good enough for the 2ndary times
// too.
ExePathFullBuffer = RtlAllocateHeap(RtlProcessHeap(), 0, (MAX_PATH + 1) * sizeof(WCHAR));
if (ExePathFullBuffer == NULL) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
}
RtlGetFullPathName_U(lpApplicationName, (MAX_PATH + 1) * sizeof(WCHAR), ExePathFullBuffer, NULL);
RtlInitUnicodeString(&SxsWin32ExePath, ExePathFullBuffer);
}
}
SxsNtExePath = PathName;
if ( RelativeName.RelativeName.Length ) {
PathName = *(PUNICODE_STRING)&RelativeName.RelativeName;
}
else {
RelativeName.ContainingDirectory = NULL;
}
InitializeObjectAttributes(
&Obja,
&PathName,
OBJ_CASE_INSENSITIVE,
RelativeName.ContainingDirectory,
NULL
);
//
// Open the file for red and execute access
//
Status = NtOpenFile(
&FileHandle,
SYNCHRONIZE | FILE_EXECUTE | FILE_READ_ATTRIBUTES | FILE_READ_DATA,
&Obja,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_DELETE,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
);
if (!NT_SUCCESS(Status) ) {
//
// We failed. Open the file for lesser access.
//
Status = NtOpenFile(
&FileHandle,
SYNCHRONIZE | FILE_EXECUTE,
&Obja,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_DELETE,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
);
if (!NT_SUCCESS(Status) ) {
//
// if we failed, see if this is a device. If it is a device,
// then just return invalid image format
//
if ( RtlIsDosDeviceName_U(RTL_CONST_CAST(PWSTR)(lpApplicationName)) ) {
SetLastError(ERROR_BAD_DEVICE);
}
else {
BaseSetLastNTError(Status);
}
return FALSE;
}
}
//
// If no desktop has been specified, use the caller's
// desktop.
//
if (StartupInfo.lpDesktop == NULL) {
StartupInfo.lpDesktop =
(LPWSTR)((PRTL_USER_PROCESS_PARAMETERS)NtCurrentPeb()->
ProcessParameters)->DesktopInfo.Buffer;
}
//
// Create a section object backed by the file
//
Status = NtCreateSection(
&SectionHandle,
SECTION_ALL_ACCESS,
NULL,
NULL,
PAGE_EXECUTE,
SEC_IMAGE,
FileHandle
);
//
// App Certification DLL
//
if (NT_SUCCESS(Status)) {
Status = BasepIsProcessAllowed(lpApplicationName);
if (!NT_SUCCESS(Status)) {
BaseSetLastNTError(Status);
NtClose(SectionHandle);
return FALSE;
}
//
// If Meow subsystem is enabled and caller specified CREATE_FORECEDOS for a win32 image
// push it into the meow subsystem
//
if ((dwCreationFlags & CREATE_FORCEDOS) && BaseStaticServerData->ForceDos) {
dwCreationFlags &= ~(CREATE_SHARED_WOW_VDM | CREATE_FORCEDOS);
dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
Status = STATUS_INVALID_IMAGE_WIN_16;
bMeowBinary = TRUE;
NtClose(SectionHandle);
SectionHandle = NULL;
}
}
//
// check appcompat (aka apphelp)
//
// if we are running under debugger, bVdmRetry will be FALSE
// yet pAppCompatData may have some data (from the app itself)
// debugger will do a separate CreateProcess on debugee
//
// apphelp gets called if it is win32 app or if it is a .bat or .cmd
if(!bVdmRetry &&
(NT_SUCCESS(Status) ||
(Status == STATUS_INVALID_IMAGE_NOT_MZ && !BaseIsDosApplication(&PathName,Status)))
) {
NTSTATUS BadAppStatus;
if (NULL != pAppCompatData) {
RtlFreeHeap(RtlProcessHeap(), 0, pAppCompatData);
pAppCompatData = NULL;
}
if (NULL != pAppCompatSxsData) {
RtlFreeHeap(RtlProcessHeap(), 0, pAppCompatSxsData);
pAppCompatSxsData = NULL;
}
//
// we only check ONCE --
// the second time around is rather meaningless - to check for posix/ntvdm/os2 emulation
//
BadAppStatus = BasepCheckBadapp(FileHandle,
PathName.Buffer,
(WCHAR*)lpEnvironment,
&pAppCompatData,
&cbAppCompatData,
&pAppCompatSxsData,
&cbAppCompatSxsData);
if (!NT_SUCCESS(BadAppStatus)) {
if (BadAppStatus == STATUS_ACCESS_DENIED) {
SetLastError(ERROR_CANCELLED);
}
else {
BaseSetLastNTError(BadAppStatus);
}
if (SectionHandle) {
NtClose(SectionHandle);
SectionHandle = NULL;
}
return FALSE;
}
}
//
// Winsafer code
//
// If this is the first time then we will have to do Safer checks.
// Note that we do not impose any restrictions on the interpreter
// itself since it is part of OS.
//
if ((!bVdmRetry) &&
( (dwCreationFlags & CREATE_PRESERVE_CODE_AUTHZ_LEVEL) == 0 )) {
NTSTATUS SaferStatus;
{
//
// WinSafer process sandbox restrictions handling.
// Should be done for non .NET images only.
//
SaferStatus = BasepCheckWinSaferRestrictions(
hUserToken,
lpApplicationName, // same as PathName.Buffer
FileHandle,
&dwJobMemberLevel,
&hSaferRestrictedToken,
&hSaferAssignmentJob);
if (SaferStatus == -1) {
SetLastError(ERROR_ACCESS_DISABLED_BY_POLICY);
bStatus = FALSE;
leave;
} else if (!NT_SUCCESS(SaferStatus)) {
BaseSetLastNTError(SaferStatus);
bStatus = FALSE;
leave;
}
}
}
if (!NT_SUCCESS(Status)) {
switch (Status) {
// 16 bit OS/2 exe
case STATUS_INVALID_IMAGE_NE_FORMAT:
#if defined(i386) && defined(OS2_SUPPORT_ENABLED)
//
// Use OS/2 if x86 (OS/2 not supported on risc),
// and CreationFlags don't have forcedos bit
// and Registry didn't specify ForceDos
//
// else execute as a DOS bound app.
//
//
if (!(dwCreationFlags & CREATE_FORCEDOS) &&
!BaseStaticServerData->ForceDos)
{
if ( !BuildSubSysCommandLine( L"OS2 /P ",
lpApplicationName,
lpCommandLine,
&SubSysCommandLine
) ) {
return FALSE;
}
lpCommandLine = SubSysCommandLine.Buffer;
lpApplicationName = NULL;
bVdmRetry = TRUE;
goto VdmRetry;
}
#endif
// Falls into Dos case, so that stub message will be
// printed, and bound apps will run w/o OS/2 subsytem
// Dos .exe or .com
case STATUS_INVALID_IMAGE_PROTECT:
case STATUS_INVALID_IMAGE_NOT_MZ:
ForceDos:
{
ULONG BinarySubType;
BinarySubType = BINARY_TYPE_DOS_EXE;
if (Status == STATUS_INVALID_IMAGE_PROTECT ||
Status == STATUS_INVALID_IMAGE_NE_FORMAT ||
(BinarySubType = BaseIsDosApplication(&PathName,Status)) )
{
#if defined(_WIN64) || defined(BUILD_WOW6432)
//
// If this a DOS application, then we need to pop up a dialog
// saying that this an invalid win32 application.
//
goto RaiseInvalidWin32Error;
#endif
VdmBinaryType = BINARY_TYPE_DOS;
// create the environment before going to the
// server. This was done becuase we want NTVDM
// to have the new environment when it gets
// created.
if (!BaseCreateVDMEnvironment(
lpEnvironment,
&AnsiStringVDMEnv,
&UnicodeStringVDMEnv
)) {
return FALSE;
}
if(!BaseCheckVDM(VdmBinaryType | BinarySubType,
lpApplicationName,
lpCommandLine,
lpCurrentDirectory,
&AnsiStringVDMEnv,
&m,
&iTask,
dwCreationFlags,
&StartupInfo
)) {
return FALSE;
}
// Check the return value from the server
switch (b->VDMState & VDM_STATE_MASK) {
case VDM_NOT_PRESENT:
// mark this so the server can undo
// creation if something goes wrong.
// We marked it "partially created" because
// the NTVDM has yet not been fully created.
// a call to UpdateVdmEntry to update
// process handle will signal the NTVDM
// process completed creation
VDMCreationState = VDM_PARTIALLY_CREATED;
// fail the call if NTVDM process is being
// created DETACHED.
// note that, we let it go if NTVDM process
// is already running.
if (dwCreationFlags & DETACHED_PROCESS) {
SetLastError(ERROR_ACCESS_DENIED);
return FALSE;
}
if (!BaseGetVdmConfigInfo(lpCommandLine,
iTask,
VdmBinaryType,
&VdmNameString,
&VdmReserve)) {
BaseSetLastNTError(Status);
return FALSE;
}
lpCommandLine = VdmNameString.Buffer;
lpApplicationName = NULL;
break;
case VDM_PRESENT_NOT_READY:
SetLastError (ERROR_NOT_READY);
return FALSE;
case VDM_PRESENT_AND_READY:
VDMCreationState = VDM_BEING_REUSED;
VdmWaitHandle = b->WaitObjectForParent;
break;
}
VdmReserve--; // we reserve from addr 1
if(VdmWaitHandle)
goto VdmExists;
else{
bInheritHandles = FALSE;
if (lpEnvironment &&
!(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT)){
RtlDestroyEnvironment(lpEnvironment);
}
lpEnvironment = UnicodeStringVDMEnv.Buffer;
bVdmRetry = TRUE;
goto VdmRetry;
}
}
else {
//
// must be a .bat or .cmd file
//
static PWCHAR CmdPrefix = L"cmd /c ";
PWCHAR NewCommandLine;
ULONG Length;
PWCHAR Last4 = &PathName.Buffer[PathName.Length / sizeof( WCHAR )-4];
if ( PathName.Length < 8 ) {
SetLastError(ERROR_BAD_EXE_FORMAT);
return FALSE;
}
if (_wcsnicmp( Last4, L".bat", 4 ) && _wcsnicmp( Last4, L".cmd", 4 )) {
SetLastError(ERROR_BAD_EXE_FORMAT);
return FALSE;
}
Length = wcslen( CmdPrefix )
+ (QuoteCmdLine || QuoteFound )
+ wcslen( lpCommandLine )
+ (QuoteCmdLine || QuoteFound)
+ 1;
NewCommandLine = RtlAllocateHeap( RtlProcessHeap( ),
MAKE_TAG( TMP_TAG ),
Length * sizeof( WCHAR ) );
if (NewCommandLine == NULL) {
BaseSetLastNTError(STATUS_NO_MEMORY);
return FALSE;
}
wcscpy( NewCommandLine, CmdPrefix );
if (QuoteCmdLine || QuoteFound) {
wcscat( NewCommandLine, L"\"" );
}
wcscat( NewCommandLine, lpCommandLine );
if (QuoteCmdLine || QuoteFound) {
wcscat( NewCommandLine, L"\"" );
}
RtlInitUnicodeString( &SubSysCommandLine, NewCommandLine );
lpCommandLine = SubSysCommandLine.Buffer;
lpApplicationName = NULL;
bVdmRetry = TRUE;
goto VdmRetry;
}
}
// 16 bit windows exe
case STATUS_INVALID_IMAGE_WIN_16:
#if defined(BUILD_WOW6432) || defined(_WIN64)
if (lpOriginalApplicationName == NULL) {
// pass in the part of the command line after the exe name
// including whitespace
lpCommandLine = ((*TempNull == '\"') ? TempNull + 1 : TempNull);
} else {
lpCommandLine = lpOriginalCommandLine;
}
return NtVdm64CreateProcess(lpOriginalApplicationName == NULL,
lpApplicationName, // this is now the real file name we've loaded
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
(dwCreationFlags & ~CREATE_UNICODE_ENVIRONMENT), // the environment has already been converted to unicode
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation
);
#endif
if (dwCreationFlags & CREATE_FORCEDOS) {
goto ForceDos;
}
IsWowBinary = TRUE;
if (!BaseCreateVDMEnvironment(lpEnvironment,
&AnsiStringVDMEnv,
&UnicodeStringVDMEnv)) {
return FALSE;
}
RetrySepWow:
VdmBinaryType = dwCreationFlags & CREATE_SEPARATE_WOW_VDM
? BINARY_TYPE_SEPWOW : BINARY_TYPE_WIN16;
if (!BaseCheckVDM(VdmBinaryType,
lpApplicationName,
lpCommandLine,
lpCurrentDirectory,
&AnsiStringVDMEnv,
&m,
&iTask,
dwCreationFlags,
&StartupInfo
))
{
//
// If we failed with access denied, caller may not
// be allowed allowed to access the shared wow's
// desktop, so retry as a separate wow
//
if (VdmBinaryType == BINARY_TYPE_WIN16 &&
GetLastError() == ERROR_ACCESS_DENIED)
{
dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
}
else {
return FALSE;
}
goto RetrySepWow;
}
// Check the return value from the server
switch (b->VDMState & VDM_STATE_MASK){
case VDM_NOT_PRESENT:
// mark this so the server can undo
// creation if something goes wrong.
// We marked it "partitially created" because
// the NTVDM has yet not been fully created.
// a call to UpdateVdmEntry to update
// process handle will signal the NTVDM
// process completed creation
VDMCreationState = VDM_PARTIALLY_CREATED;
// jarbats: 1/8/2001
// Tell BaseGetVdmConfigInfo to create
// vdm commandline for meow
//
if (bMeowBinary)
{
VdmReserve = 1;
}
if (!BaseGetVdmConfigInfo(
lpCommandLine,
iTask,
VdmBinaryType,
&VdmNameString,
&VdmReserve
)) {
BaseSetLastNTError(Status);
return FALSE;
}
lpCommandLine = VdmNameString.Buffer;
lpApplicationName = NULL;
//
// Wow must have a hidden console
// Throw away DETACHED_PROCESS flag which isn't
// meaningful for Win16 apps.
//
dwCreationFlags |= CREATE_NO_WINDOW;
dwCreationFlags &= ~(CREATE_NEW_CONSOLE | DETACHED_PROCESS);
//
// We're starting a WOW VDM, turn on feedback unless
// the creator passed STARTF_FORCEOFFFEEDBACK.
//
StartupInfo.dwFlags |= STARTF_FORCEONFEEDBACK;
break;
case VDM_PRESENT_NOT_READY:
SetLastError (ERROR_NOT_READY);
return FALSE;
case VDM_PRESENT_AND_READY:
VDMCreationState = VDM_BEING_REUSED;
VdmWaitHandle = b->WaitObjectForParent;
break;
}
VdmReserve--; // we reserve from addr 1
if(VdmWaitHandle)
goto VdmExists;
else {
bInheritHandles = FALSE;
// replace the environment with ours
if (lpEnvironment &&
!(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT)) {
RtlDestroyEnvironment(lpEnvironment);
}
lpEnvironment = UnicodeStringVDMEnv.Buffer;
bVdmRetry = TRUE;
goto VdmRetry;
}
case STATUS_FILE_IS_OFFLINE:
SetLastError(ERROR_FILE_OFFLINE);
break;
default :
SetLastError(ERROR_BAD_EXE_FORMAT);
return FALSE;
}
}
//
// Make sure only WOW apps can have the CREATE_SEPARATE_WOW_VDM flag.
//
if (!IsWowBinary && (dwCreationFlags & CREATE_SEPARATE_WOW_VDM)) {
dwCreationFlags &= ~CREATE_SEPARATE_WOW_VDM;
}
//
// Query the section to determine the stack parameters and
// image entrypoint.
//
Status = NtQuerySection(
SectionHandle,
SectionImageInformation,
&ImageInformation,
sizeof( ImageInformation ),
NULL
);
if (!NT_SUCCESS( Status )) {
BaseSetLastNTError(Status);
return FALSE;
}
if (ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL) {
SetLastError(ERROR_BAD_EXE_FORMAT);
return FALSE;
}
ImageFileDebuggerCommand[ 0 ] = UNICODE_NULL;
if (!(dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) ||
NtCurrentPeb()->ReadImageFileExecOptions
) {
LdrQueryImageFileExecutionOptions( &PathName,
L"Debugger",
REG_SZ,
ImageFileDebuggerCommand,
sizeof( ImageFileDebuggerCommand ),
NULL
);
}
if ((ImageInformation.Machine < USER_SHARED_DATA->ImageNumberLow) ||
(ImageInformation.Machine > USER_SHARED_DATA->ImageNumberHigh)) {
#if defined(_WIN64) || defined(BUILD_WOW6432)
if (ImageInformation.Machine == IMAGE_FILE_MACHINE_I386) {
// Fall through since this is a valid machine type.
}
else
#endif
{
ULONG_PTR ErrorParameters[2];
ULONG ErrorResponse;
#if defined(_WIN64) || defined(BUILD_WOW6432)
RaiseInvalidWin32Error:
#endif
ErrorResponse = ResponseOk;
ErrorParameters[0] = (ULONG_PTR)&PathName;
NtRaiseHardError( STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE,
1,
1,
ErrorParameters,
OptionOk,
&ErrorResponse
);
if ( NtCurrentPeb()->ImageSubsystemMajorVersion <= 3 ) {
SetLastError(ERROR_BAD_EXE_FORMAT);
}
else {
SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH);
}
return FALSE;
}
}
if ( ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_GUI &&
ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_CUI ) {
// POSIX exe
NtClose(SectionHandle);
SectionHandle = NULL;
if ( ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_POSIX_CUI ) {
if ( !BuildSubSysCommandLine( L"POSIX /P ",
lpApplicationName,
lpCommandLine,
&SubSysCommandLine
) ) {
return FALSE;
}
lpCommandLine = SubSysCommandLine.Buffer;
lpApplicationName = NULL;
bVdmRetry = TRUE;
goto VdmRetry;
}
else {
SetLastError(ERROR_CHILD_NOT_COMPLETE);
return FALSE;
}
}
else {
if (!BasepIsImageVersionOk( ImageInformation.SubSystemMajorVersion,
ImageInformation.SubSystemMinorVersion) ) {
SetLastError(ERROR_BAD_EXE_FORMAT);
return FALSE;
}
}
if (ImageFileDebuggerCommand[ 0 ] != UNICODE_NULL) {
SIZE_T n;
n = wcslen( lpCommandLine );
if (n == 0) {
lpCommandLine = (LPWSTR)lpApplicationName;
n = wcslen( lpCommandLine );
}
n += wcslen( ImageFileDebuggerCommand ) + 1 + 2;
n *= sizeof( WCHAR );
SubSysCommandLine.Buffer = RtlAllocateHeap( RtlProcessHeap(), MAKE_TAG( TMP_TAG ), n );
SubSysCommandLine.Length = 0;
SubSysCommandLine.MaximumLength = (USHORT)n;
RtlAppendUnicodeToString( &SubSysCommandLine, ImageFileDebuggerCommand );
RtlAppendUnicodeToString( &SubSysCommandLine, L" " );
RtlAppendUnicodeToString( &SubSysCommandLine, lpCommandLine );
#if DBG
DbgPrint( "BASE: Calling debugger with '%wZ'\n", &SubSysCommandLine );
#endif
lpCommandLine = SubSysCommandLine.Buffer;
lpApplicationName = NULL;
NtClose(SectionHandle);
SectionHandle = NULL;
RtlFreeHeap(RtlProcessHeap(), 0, NameBuffer);
NameBuffer = NULL;
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
FreeBuffer = NULL;
goto VdmRetry;
}
//
// Create the process object
//
pObja = BaseFormatObjectAttributes(&Obja,lpProcessAttributes,NULL);
Flags = 0;
if (dwCreationFlags & CREATE_BREAKAWAY_FROM_JOB ) {
Flags |= PROCESS_CREATE_FLAGS_BREAKAWAY;
}
if ( dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS) ) {
Status = DbgUiConnectToDbg();
if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
return FALSE;
}
DebugPortHandle = DbgUiGetThreadDebugObject ();
if (dwCreationFlags & DEBUG_ONLY_THIS_PROCESS) {
Flags |= PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT;
}
}
if (bInheritHandles) {
Flags |= PROCESS_CREATE_FLAGS_INHERIT_HANDLES;
}
if (((ImageInformation.LoaderFlags & IMAGE_LOADER_FLAGS_COMPLUS) != 0)) {
#if defined(_WIN64) || defined(BUILD_WOW6432)
//
// Check if this is a 32-bit IL_ONLY COM+ image that needs to run natively
// on Win64.
//
if ( ImageInformation.Machine == IMAGE_FILE_MACHINE_I386 ) {
Status = BasepIsComplusILImage(
SectionHandle,
&ImageInformation,
&ComPlusILImage
);
if ((NT_SUCCESS (Status)) && (ComPlusILImage != FALSE)) {
Flags |= PROCESS_CREATE_FLAGS_OVERRIDE_ADDRESS_SPACE;
}
}
#endif
}
//
// This is temporary till we get Shim dlls support for native Win64 applications.
//
if (ImageInformation.Machine != IMAGE_FILE_MACHINE_I386) {
pAppCompatDataTemp = NULL;
} else {
pAppCompatDataTemp = pAppCompatData;
}
Status = NtCreateProcessEx(
&ProcessHandle,
PROCESS_ALL_ACCESS,
pObja,
NtCurrentProcess(),
Flags,
SectionHandle,
DebugPortHandle,
NULL,
dwJobMemberLevel // Job member level
);
if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
return FALSE;
}
//
// NtCreateProcess will set to normal OR inherit if parent is IDLE or Below
// only override if a mask is given during the create.
//
if ( PriClass.PriorityClass != PROCESS_PRIORITY_CLASS_UNKNOWN ) {
State = NULL;
if ( PriClass.PriorityClass == PROCESS_PRIORITY_CLASS_REALTIME ) {
State = BasepIsRealtimeAllowed(TRUE);
}
Status = NtSetInformationProcess(
ProcessHandle,
ProcessPriorityClass,
(PVOID)&PriClass,
sizeof(PriClass)
);
if ( State ) {
BasepReleasePrivilege( State );
}
if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
return FALSE;
}
}
if (dwCreationFlags & CREATE_DEFAULT_ERROR_MODE) {
UINT NewMode;
NewMode = SEM_FAILCRITICALERRORS;
NtSetInformationProcess(
ProcessHandle,
ProcessDefaultHardErrorMode,
&NewMode,
sizeof(NewMode)
);
}
//
// If the process is being created for a VDM call the server with
// process handle.
//
if (VdmBinaryType) {
VdmWaitHandle = ProcessHandle;
if (!BaseUpdateVDMEntry(UPDATE_VDM_PROCESS_HANDLE,
&VdmWaitHandle,
iTask,
VdmBinaryType
))
{
//make sure we don't close the handle twice --
//(VdmWaitHandle == ProcessHandle) if we don't do this.
VdmWaitHandle = NULL;
return FALSE;
}
//
// For Sep wow the VdmWaitHandle = NULL (there is none!)
//
VDMCreationState |= VDM_FULLY_CREATED;
}
#if defined(i386)
//
// Reserve memory in the new process' address space if necessary
// (for vdms). This is required only for x86 system.
//
if ( VdmReserve ) {
BigVdmReserve = VdmReserve;
Status = NtAllocateVirtualMemory(
ProcessHandle,
&BaseAddress,
0L,
&BigVdmReserve,
MEM_RESERVE,
PAGE_EXECUTE_READWRITE
);
if ( !NT_SUCCESS(Status) ){
BaseSetLastNTError(Status);
return FALSE;
}
}
#endif
//
// Abuse the StaticSize fields temporarily.
// They are somewhat private, but we are using them for temporary space, sort of.
// These are the right values for them, but we need to call the proper initialization function,
// which will store the values a second time (possibly optimized away).
//
SxsWin32ManifestPathBuffer.ByteBuffer.StaticSize = SxsWin32ExePath.Length + sizeof(SXS_MANIFEST_SUFFIX);
SxsWin32PolicyPathBuffer.ByteBuffer.StaticSize = SxsWin32ExePath.Length + sizeof(SXS_POLICY_SUFFIX);
SxsWin32AssemblyDirectoryBuffer.ByteBuffer.StaticSize = SxsWin32ExePath.Length + sizeof(WCHAR); // Win32AssemblyDirectory overestimate
SxsNtManifestPathBuffer.ByteBuffer.StaticSize = SxsNtExePath.Length + sizeof(SXS_MANIFEST_SUFFIX);
SxsNtPolicyPathBuffer.ByteBuffer.StaticSize = SxsNtExePath.Length + sizeof(SXS_POLICY_SUFFIX);
//
// now add them up as BYTE sizes
//
SxsConglomeratedBufferSizeBytes = 0;
for (sxsi = 0 ; sxsi != RTL_NUMBER_OF(SxsStringBuffers) ; ++sxsi) {
SxsConglomeratedBufferSizeBytes += SxsStringBuffers[sxsi]->ByteBuffer.StaticSize;
}
#if DBG
DbgPrintEx(
DPFLTR_SXS_ID,
DPFLTR_INFO_LEVEL,
"SXS: SxsConglomeratedBufferSizeBytes:%Id\n",
SxsConglomeratedBufferSizeBytes
);
#endif
//
// one honking heap allocation
//
SxsConglomeratedByteBuffer = (PBYTE)RtlAllocateHeap(RtlProcessHeap(), 0, SxsConglomeratedBufferSizeBytes);
if (SxsConglomeratedByteBuffer == NULL) {
BaseSetLastNTError(STATUS_NO_MEMORY);
return FALSE;
}
//
// now dole out pieces, calling the proper initialization function
//
for (sxsi= 0 ; sxsi != RTL_NUMBER_OF(SxsStringBuffers) ; ++sxsi) {
RtlInitUnicodeStringBuffer(
SxsStringBuffers[sxsi],
(sxsi != 0) ? SxsStringBuffers[sxsi - 1]->ByteBuffer.Buffer + SxsStringBuffers[sxsi- 1]->ByteBuffer.StaticSize
: SxsConglomeratedByteBuffer,
SxsStringBuffers[sxsi]->ByteBuffer.StaticSize
);
}
SxsExeHandles.Process = ProcessHandle;
SxsExeHandles.File = FileHandle;
// The 1 bit here means something different than in the loader.
ASSERT((((ULONG_PTR)SectionHandle) & (ULONG_PTR)1) == 0);
SxsExeHandles.Section = SectionHandle;
// if we have an override stream, use it
if (NULL != pAppCompatSxsData) {
AppCompatSxsManifest.Name = SxsWin32ExePath; // unicode string
AppCompatSxsManifest.Address = pAppCompatSxsData; // pointer to unicode manifest
AppCompatSxsManifest.Size = cbAppCompatSxsData; // byte count
}
Status = BasepSxsCreateProcessCsrMessage(
(NULL != pAppCompatSxsData) ? &AppCompatSxsManifest : NULL, // override manifest (appcompat hook)
NULL, // override policy (appcompat hook)
&SxsManifestPathPair,
&SxsManifestFileHandles,
&SxsExePathPair,
&SxsExeHandles,
&SxsPolicyPathPair,
&SxsPolicyHandles,
&SxsWin32AssemblyDirectoryBuffer,
&a->Sxs
);
#if DBG
// verify the buffer size calculation
for (sxsi = 0 ; sxsi != RTL_NUMBER_OF(SxsStringBuffers) ; ++sxsi)
{
if (SxsStringBuffers[sxsi]->ByteBuffer.Buffer != SxsStringBuffers[sxsi]->ByteBuffer.StaticBuffer)
{
DbgPrintEx(
DPFLTR_SXS_ID,
DPFLTR_WARNING_LEVEL,
"SXS: SxsStringBuffers[%lu]'s StaticSize was computed too small (%Id, %Id)\n",
sxsi,
SxsStringBuffers[sxsi]->ByteBuffer.StaticSize,
SxsStringBuffers[sxsi]->ByteBuffer.Size
);
}
}
#endif
if ( !NT_SUCCESS( Status ) ) {
BaseSetLastNTError(Status);
return FALSE;
}
//
// Determine the location of the
// processes PEB.
//
Status = NtQueryInformationProcess(
ProcessHandle,
ProcessBasicInformation,
&ProcessInfo,
sizeof( ProcessInfo ),
NULL
);
if ( !NT_SUCCESS( Status ) ) {
BaseSetLastNTError(Status);
return FALSE;
}
Peb = ProcessInfo.PebBaseAddress;
//
// Push the parameters into the address space of the new process
//
if ( ARGUMENT_PRESENT(lpCurrentDirectory) ) {
CurdirBuffer = RtlAllocateHeap( RtlProcessHeap(),
MAKE_TAG( TMP_TAG ),
(MAX_PATH + 1) * sizeof( WCHAR ) );
if ( !CurdirBuffer ) {
BaseSetLastNTError(STATUS_NO_MEMORY);
return FALSE;
}
CurdirLength2 = GetFullPathNameW(
lpCurrentDirectory,
MAX_PATH,
CurdirBuffer,
&CurdirFilePart
);
if ( CurdirLength2 > MAX_PATH ) {
SetLastError(ERROR_DIRECTORY);
return FALSE;
}
//
// now make sure the directory exists
//
CurdirLength = GetFileAttributesW(CurdirBuffer);
if ( (CurdirLength == 0xffffffff) ||
!(CurdirLength & FILE_ATTRIBUTE_DIRECTORY) ) {
SetLastError(ERROR_DIRECTORY);
return FALSE;
}
}
if ( QuoteInsert || QuoteCmdLine) {
QuotedBuffer = RtlAllocateHeap(RtlProcessHeap(),0,wcslen(lpCommandLine)*2+6);
if ( QuotedBuffer ) {
wcscpy(QuotedBuffer,L"\"");
if ( QuoteInsert ) {
TempChar = *TempNull;
*TempNull = UNICODE_NULL;
}
wcscat(QuotedBuffer,lpCommandLine);
wcscat(QuotedBuffer,L"\"");
if ( QuoteInsert ) {
*TempNull = TempChar;
wcscat(QuotedBuffer,TempNull);
}
}
else {
if ( QuoteInsert ) {
QuoteInsert = FALSE;
}
if ( QuoteCmdLine ) {
QuoteCmdLine = FALSE;
}
}
}
// If we found a manifest, we want to push that fact to the new process.
if (a->Sxs.Flags & BASE_MSG_SXS_MANIFEST_PRESENT)
dwBasePushProcessParametersFlags |= BASE_PUSH_PROCESS_PARAMETERS_FLAG_APP_MANIFEST_PRESENT;
if (!BasePushProcessParameters(
dwBasePushProcessParametersFlags,
ProcessHandle,
Peb,
lpApplicationName,
CurdirBuffer,
QuoteInsert || QuoteCmdLine ? QuotedBuffer : lpCommandLine,
lpEnvironment,
&StartupInfo,
dwCreationFlags | dwNoWindow,
bInheritHandles,
IsWowBinary ? IMAGE_SUBSYSTEM_WINDOWS_GUI : 0,
pAppCompatDataTemp,
cbAppCompatData
) ) {
return FALSE;
}
RtlFreeUnicodeString(&VdmNameString);
VdmNameString.Buffer = NULL;
//
// Stuff in the standard handles if needed
//
if (!VdmBinaryType &&
!bInheritHandles &&
!(StartupInfo.dwFlags & STARTF_USESTDHANDLES) &&
!(dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE | CREATE_NO_WINDOW)) &&
ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_CUI
) {
PRTL_USER_PROCESS_PARAMETERS ParametersInNewProcess;
Status = NtReadVirtualMemory( ProcessHandle,
&Peb->ProcessParameters,
&ParametersInNewProcess,
sizeof( ParametersInNewProcess ),
NULL
);
if (NT_SUCCESS( Status )) {
if (!CONSOLE_HANDLE( NtCurrentPeb()->ProcessParameters->StandardInput )) {
StuffStdHandle( ProcessHandle,
NtCurrentPeb()->ProcessParameters->StandardInput,
&ParametersInNewProcess->StandardInput
);
}
if (!CONSOLE_HANDLE( NtCurrentPeb()->ProcessParameters->StandardOutput )) {
StuffStdHandle( ProcessHandle,
NtCurrentPeb()->ProcessParameters->StandardOutput,
&ParametersInNewProcess->StandardOutput
);
}
if (!CONSOLE_HANDLE( NtCurrentPeb()->ProcessParameters->StandardError )) {
StuffStdHandle( ProcessHandle,
NtCurrentPeb()->ProcessParameters->StandardError,
&ParametersInNewProcess->StandardError
);
}
}
}
//
// Create the thread...
//
//
// Allocate a stack for this thread in the address space of the target
// process.
//
StackStatus = BaseCreateStack(
ProcessHandle,
ImageInformation.CommittedStackSize,
(ImageInformation.MaximumStackSize < 256*1024) ? 256*1024 : ImageInformation.MaximumStackSize,
&InitialTeb
);
if ( !NT_SUCCESS(StackStatus) ) {
BaseSetLastNTError(StackStatus);
return FALSE;
}
//
// Create an initial context for the new thread.
//
BaseInitializeContext(
&ThreadContext,
Peb,
ImageInformation.TransferAddress,
InitialTeb.StackBase,
BaseContextTypeProcess
);
//
// Create the actual thread object
//
pObja = BaseFormatObjectAttributes(&Obja,lpThreadAttributes,NULL);
Status = NtCreateThread(
&ThreadHandle,
THREAD_ALL_ACCESS,
pObja,
ProcessHandle,
&ClientId,
&ThreadContext,
&InitialTeb,
TRUE
);
if (!NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
return FALSE;
}
a->Peb = (ULONGLONG) Peb;
//
// From here on out, do not modify the address space of the
// new process. WOW64's implementation of NtCreateThread()
// reshuffles the new process' address space if the current
// process is 32-bit and the new process is 64-bit.
//
#if DBG
Peb = NULL;
#endif
#if defined(WX86)
//
// if this is a Wx86 Process, setup for a Wx86 emulated Thread
//
if (Wx86Info) {
//
// create a WX86Tib and initialize it's Teb->Vdm.
//
Status = BaseCreateWx86Tib(ProcessHandle,
ThreadHandle,
(ULONG)((ULONG_PTR)ImageInformation.TransferAddress),
(ULONG)ImageInformation.CommittedStackSize,
(ULONG)ImageInformation.MaximumStackSize,
TRUE
);
if (!NT_SUCCESS(Status)) {
BaseSetLastNTError(Status);
return( FALSE );
}
//
// Mark Process as WX86
//
Status = NtSetInformationProcess (ProcessHandle,
ProcessWx86Information,
&Wx86Info,
sizeof(Wx86Info)
);
if (!NT_SUCCESS(Status)) {
BaseSetLastNTError(Status);
return( FALSE );
}
}
#endif
//
// Call the Windows server to let it know about the
// process.
//
a->ProcessHandle = ProcessHandle;
a->ThreadHandle = ThreadHandle;
a->ClientId = ClientId;
switch (ImageInformation.Machine) {
case IMAGE_FILE_MACHINE_I386:
#if defined(_WIN64) || defined(BUILD_WOW6432)
a->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_IA32_ON_WIN64;
#else
a->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
#endif
break;
case IMAGE_FILE_MACHINE_IA64:
a->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_IA64;
break;
case IMAGE_FILE_MACHINE_AMD64:
a->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64;
break;
default:
DbgPrint("kernel32: No mapping for ImageInformation.Machine == %04x\n", ImageInformation.Machine);
a->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_UNKNOWN;
break;
}
//
// remove debug flags now its not being done by CSR
//
a->CreationFlags = dwCreationFlags & ~ (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS);
a->DebuggerClientId.UniqueProcess = NULL;
a->DebuggerClientId.UniqueThread = NULL;
//
// Set the 2 bit if a gui app is starting. The window manager needs to
// know this so it can synchronize the startup of this app
// (WaitForInputIdle api). This info is passed using the process
// handle tag bits. The 1 bit asks the window manager to turn on
// or turn off the application start cursor (hourglass/pointer).
//
// When starting a WOW process, lie and tell UserSrv NTVDM.EXE is a GUI
// process. We also turn on bit 0x8 so that UserSrv can ignore the
// UserNotifyConsoleApplication call made by the console during startup.
//
if ( ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_GUI ||
IsWowBinary ) {
a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle | 2);
//
// If the creating process is a GUI app, turn on the app. start cursor
// by default. This can be overridden by STARTF_FORCEOFFFEEDBACK.
//
NtHeaders = RtlImageNtHeader((PVOID)GetModuleHandle(NULL));
if ( NtHeaders
&& (NtHeaders->OptionalHeader.Subsystem
== IMAGE_SUBSYSTEM_WINDOWS_GUI ) ) {
a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle | 1);
}
}
//
// If feedback is forced on, turn it on. If forced off, turn it off.
// Off overrides on.
//
if (StartupInfo.dwFlags & STARTF_FORCEONFEEDBACK)
a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle | 1);
if (StartupInfo.dwFlags & STARTF_FORCEOFFFEEDBACK)
a->ProcessHandle = (HANDLE)((ULONG_PTR)a->ProcessHandle & ~1);
a->VdmBinaryType = VdmBinaryType; // just tell server the truth
if (VdmBinaryType){
a->hVDM = iTask ? 0 : NtCurrentPeb()->ProcessParameters->ConsoleHandle;
a->VdmTask = iTask;
}
#if defined(BUILD_WOW6432)
m.ReturnValue = CsrBasepCreateProcess(a);
#else
m.u.CreateProcess = *a;
if (m.u.CreateProcess.Sxs.Flags != 0)
{
const PUNICODE_STRING StringsToCapture[] =
{
&m.u.CreateProcess.Sxs.Manifest.Path,
&m.u.CreateProcess.Sxs.Policy.Path,
&m.u.CreateProcess.Sxs.AssemblyDirectory
};
Status =
CsrCaptureMessageMultiUnicodeStringsInPlace(
&CaptureBuffer,
RTL_NUMBER_OF(StringsToCapture),
StringsToCapture
);
if (!NT_SUCCESS(Status)) {
BaseSetLastNTError(Status);
return FALSE;
}
}
CsrClientCallServer( (PCSR_API_MSG)&m,
CaptureBuffer,
CSR_MAKE_API_NUMBER( BASESRV_SERVERDLL_INDEX,
BasepCreateProcess
),
sizeof( *a )
);
if ( CaptureBuffer ) {
CsrFreeCaptureBuffer( CaptureBuffer );
CaptureBuffer = NULL;
}
#endif
if (!NT_SUCCESS((NTSTATUS)m.ReturnValue)) {
BaseSetLastNTError((NTSTATUS)m.ReturnValue);
NtTerminateProcess(ProcessHandle, (NTSTATUS)m.ReturnValue);
return FALSE;
}
//
// If the WinSafer sandboxing policy indicates that the
// process needs to be run with a restricted token or placed
// into a restricted job object, then do those actions now.
// Do not replace the token if the restricted token was created
// from a caller supplied token i.e. the CreateProcessAsUser case.
//
if ((hSaferRestrictedToken != NULL) && (hUserToken == NULL)) {
Status = BasepReplaceProcessThreadTokens(
hSaferRestrictedToken,
ProcessHandle,
ThreadHandle);
if (!NT_SUCCESS(Status)) {
// kill and cleanup.
NtTerminateProcess(ProcessHandle, Status);
BaseSetLastNTError(Status);
return FALSE;
}
}
if (hSaferAssignmentJob != NULL) {
Status = NtAssignProcessToJobObject(
hSaferAssignmentJob, ProcessHandle);
if (!NT_SUCCESS(Status)) {
// kill and cleanup.
NtTerminateProcess(ProcessHandle, STATUS_ACCESS_DENIED);
BaseSetLastNTError(Status);
return FALSE;
}
}
//
// Make the thread start execution if we are allowed to.
//
if (!( dwCreationFlags & CREATE_SUSPENDED) ) {
NtResumeThread(ThreadHandle,&i);
}
VdmExists:
bStatus = TRUE;
if (VDMCreationState)
VDMCreationState |= VDM_CREATION_SUCCESSFUL;
try {
if (VdmWaitHandle) {
//
// tag Shared WOW VDM handles so that wait for input idle has a
// chance to work. Shared WOW VDM "process" handles are actually
// event handles, Separate WOW VDM handles are real process
// handles. Also mark DOS handles with 0x1 so WaitForInputIdle
// has a way to distinguish DOS apps and not block forever.
//
if (VdmBinaryType == BINARY_TYPE_WIN16) {
lpProcessInformation->hProcess =
(HANDLE)((ULONG_PTR)VdmWaitHandle | 0x2);
//
// Shared WOW doesn't always start a process, so
// we don't have a process ID or thread ID to
// return if the VDM already existed.
//
// Separate WOW doesn't hit this codepath
// (no VdmWaitHandle).
//
if (VDMCreationState & VDM_BEING_REUSED) {
ClientId.UniqueProcess = 0;
ClientId.UniqueThread = 0;
}
}
else {
lpProcessInformation->hProcess =
(HANDLE)((ULONG_PTR)VdmWaitHandle | 0x1);
}
//
// Close the ProcessHandle, since we are returning the
// VdmProcessHandle instead.
//
if (ProcessHandle != NULL)
NtClose(ProcessHandle);
}
else{
lpProcessInformation->hProcess = ProcessHandle;
}
lpProcessInformation->hThread = ThreadHandle;
lpProcessInformation->dwProcessId = HandleToUlong(ClientId.UniqueProcess);
lpProcessInformation->dwThreadId = HandleToUlong(ClientId.UniqueThread);
ProcessHandle = NULL;
ThreadHandle = NULL;
}
__except ( EXCEPTION_EXECUTE_HANDLER ) {
NtClose( ProcessHandle );
NtClose( ThreadHandle );
ProcessHandle = NULL;
ThreadHandle = NULL;
if (VDMCreationState)
VDMCreationState &= ~VDM_CREATION_SUCCESSFUL;
}
}
__finally {
if (ExePathFullBuffer != NULL) {
SxsWin32ExePath.Buffer = NULL;
SxsWin32ExePath.Length = 0;
SxsWin32ExePath.MaximumLength = 0;
RtlFreeHeap(RtlProcessHeap(), 0, ExePathFullBuffer);
ExePathFullBuffer = NULL;
}
if (!VdmBinaryType) {
NTSTATUS Status1;
BasepSxsCloseHandles(&SxsManifestFileHandles);
BasepSxsCloseHandles(&SxsPolicyHandles);
//
// don't close SxsExeHandles, they are
// aliases of other variables that are either closed
// or returned to the caller
//
//
// This loop only really frees any memory if our computation
// of the overall buffer size was too low, which it is not supposed to be.
//
if (SxsConglomeratedByteBuffer != NULL) {
for (sxsi= 0 ; sxsi != RTL_NUMBER_OF(SxsStringBuffers) ; ++sxsi) {
RtlFreeUnicodeStringBuffer(SxsStringBuffers[sxsi]);
}
RtlFreeHeap(RtlProcessHeap(), 0,SxsConglomeratedByteBuffer);
}
}
if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT) ) {
RtlDestroyEnvironment(lpEnvironment);
lpEnvironment = NULL;
}
RtlFreeHeap(RtlProcessHeap(), 0,QuotedBuffer);
RtlFreeHeap(RtlProcessHeap(), 0,NameBuffer);
RtlFreeHeap(RtlProcessHeap(), 0,CurdirBuffer);
RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
if ( FileHandle ) {
NtClose(FileHandle);
}
if ( SectionHandle ) {
NtClose(SectionHandle);
}
if ( ThreadHandle ) {
NtTerminateProcess(ProcessHandle,STATUS_SUCCESS);
NtClose(ThreadHandle);
}
if ( ProcessHandle ) {
NtClose(ProcessHandle);
}
if ( hSaferAssignmentJob ) {
NtClose(hSaferAssignmentJob);
}
if ( hSaferRestrictedToken ) {
if (hUserToken == NULL) {
// CreateProcess case
NtClose(hSaferRestrictedToken);
}
else{
// CreateProcessAsUser case
*hRestrictedUserToken = hSaferRestrictedToken;
}
}
if (NULL != pAppCompatData) {
RtlFreeHeap(RtlProcessHeap(), 0, pAppCompatData);
}
if (NULL != pAppCompatSxsData) {
RtlFreeHeap(RtlProcessHeap(), 0, pAppCompatSxsData);
}
RtlFreeUnicodeString(&VdmNameString);
RtlFreeUnicodeString(&SubSysCommandLine);
if (AnsiStringVDMEnv.Buffer || UnicodeStringVDMEnv.Buffer)
BaseDestroyVDMEnvironment(&AnsiStringVDMEnv, &UnicodeStringVDMEnv);
if (VDMCreationState && !(VDMCreationState & VDM_CREATION_SUCCESSFUL)){
BaseUpdateVDMEntry (
UPDATE_VDM_UNDO_CREATION,
(HANDLE *)&iTask,
VDMCreationState,
VdmBinaryType
);
if(VdmWaitHandle) {
NtClose(VdmWaitHandle);
}
}
}
if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT) ) {
RtlDestroyEnvironment(lpEnvironment);
}
return bStatus;
}
BOOL
WINAPI
CreateProcessW(
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
)
/*++
Routine Description:
A process and thread object are created and a handle opened to each
object using CreateProcess. Note that WinExec and LoadModule are
still supported, but are implemented as a call to CreateProcess.
Arguments:
lpApplicationName - Supplies an optional pointer to a null terminated
character string that contains the name of the image file to
execute. This is a fully qualified DOS path name. If not
specified, then the image file name is the first whitespace
delimited token on the command line.
lpCommandLine - Supplies a null terminated character string that
contains the command line for the application to be executed.
The entire command line is made available to the new process
using GetCommandLine. If the lpApplicationName parameter was
not specified, then the first token of the command line
specifies file name of the application (note that this token
begins at the beginning of the command line and ends at the
first "white space" character). If the file name does not
contain an extension (the presence of a "."), then .EXE is
assumed. If the file name does not contain a directory path,
Windows will search for the executable file in:
- The current directory
- The windows directory
- The windows system directory
- The directories listed in the path environment variable
This parameter is optional onlu if the lpApplicationName
parameter is specified. In this case the command line the
application receives will be the application name.
lpProcessAttributes - An optional parameter that may be used to
specify the attributes of the new process. If the parameter is
not specified, then the process is created without a security
descriptor, and the resulting handle is not inherited on process
creation:
SECURITY_ATTRIBUTES Structure:
DWORD nLength - Specifies the length of this structure. Must be
set to sizeof( SECURITY_ATTRUBUTES ).
LPVOID lpSecurityDescriptor - Points to a security descriptor for
the object (must be NULL for Win32, used on NT/Win32). The
security descriptor controls the sharing of an object.
BOOL bInheritHandle - Supplies a flag that indicates whether
or not the returned handle is to be inherited by a new
process during process creation. A value of TRUE
indicates that the new process will inherit the handle.
lpThreadAttributes - An optional parameter that may be used to specify
the attributes of the new thread. If the parameter is not
specified, then the thread is created without a security
descriptor, and the resulting handle is not inherited on
process creation.
dwCreationFlags - Supplies additional flags that control the creation
of the process.
dwCreationFlags Flags:
DEBUG_PROCESS - If this flag bit is set, then the creating
process is treated as a debugger, and the process being
created is created as a debugee. All debug events occuring
in the debugee are reported to the debugger. If this bit is
clear, but the calling process is a debugee, then the
process becomes a debugee of the calling processes debugger.
If this bit is clear and the calling processes is not a
debugee then no debug related actions occur.
DEBUG_ONLY_THIS_PROCESS - If this flag is set, then the
DEBUG_PROCESS flag bit must also be set. The calling
process is is treated as a debugger, and the new process is
created as its debuggee. If the new process creates
additional processes, no debug related activities (with
respect to the debugger) occur.
CREATE_SUSPENDED - The process is created, but the initial thread
of the process remains suspended. The creator can resume this
thread using ResumeThread. Until this is done, code in the
process will not execute.
CREATE_UNICODE_ENVIRONMENT - If set, the environment pointer
points to a Unicode environment block. Otherwise, the
block is ANSI (actually OEM.)
bInheritHandles - Supplies a flag that specifies whether or not the
new process is to inherit handles to objects visible to the
calling process. A value of TRUE causes handles to be inherited
by the new process. If TRUE was specified, then for each handle
visible to the calling process, if the handle was created with
the inherit handle option, the handle is inherited to the new
process. The handle has the same granted access in the new
process as it has in the calling process, and the value of the
handle is the same.
lpEnvironment - An optional parameter, that if specified, supplies a
pointer to an environment block. If the parameter is not
specified, the environment block of the current process is used.
This environment block is made available to the new process
using GetEnvironmentStrings.
lpCurrentDirectory - An optional parameter, that if specified,
supplies a string representing the current drive and directory
for the new process. The string must be a fully qualified
pathname that includes a drive letter. If the parameter is not
specified, then the new process is created with the same current
drive and directory as the calling process. This option is
provided primarily for shells that want to start an application
and specify its initial drive and working directory.
lpStartupInfo - Supplies information that specified how the
applications window is to be shown. This structure is described
in the Win32 User Interface API Book.
lpProcessInformation - Returns identification information about the
new process.
PROCESS_INFORMATION Structure:
HANDLE hProcess - Returns a handle to the newly created process.
Through the handle, all operations on process objects are
allowed.
HANDLE hThread - Returns a handle to the newly created thread.
Through the handle, all operations on thread objects are
allowed.
DWORD dwProcessId - Returns a global process id that may be used
to identify a process. The value is valid from the time the
process is created until the time the process is terminated.
DWORD dwThreadId - Returns a global thread id that may be used
to identify a thread. The value is valid from the time the
thread is created until the time the thread is terminated.
Return Value:
TRUE - The operation was successful
FALSE/NULL - The operation failed. Extended error status is available
using GetLastError.
--*/
{
return CreateProcessInternalW(
NULL, // Create new process with the token on the creator process
lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation,
NULL // Do not return the restricted token
);
}