示例#1
0
VOID
SepServerSpawnClientProcess(VOID)

{


    RTL_USER_PROCESS_INFORMATION ProcessInformation;
    STRING ImagePathName, ProgramName;
    UNICODE_STRING UnicodeImagePathName, UnicodeProgramName;
    PRTL_USER_PROCESS_PARAMETERS ProcessParameters;

    RtlInitString( &ProgramName, "\\SystemRoot\\Bin\\utlpcqos.exe" );
    Status = RtlAnsiStringToUnicodeString(
                 &UnicodeProgramName,
                 &ProgramName,
                 TRUE );  SEASSERT_SUCCESS( NT_SUCCESS(Status) );
    RtlInitString( &ImagePathName, "utlpcqos.exe");
    Status = RtlAnsiStringToUnicodeString(
                 &UnicodeImagePathName,
                 &ImagePathName,
                 TRUE );  SEASSERT_SUCCESS( NT_SUCCESS(Status) );

    Status = RtlCreateProcessParameters(
                 &ProcessParameters,
                 &ImagePathName,        //UNICODEFIX &UnicodeImagePathName,
                 NULL,
                 NULL,
                 NULL,
                 NULL,
                 NULL,
                 NULL,
                 NULL,
                 NULL
                 );

    SEASSERT_SUCCESS(Status);


    Status = RtlCreateUserProcess(
                 &ProgramName,                   // UNICODEFIX &UnicodeProgramName,
                 ProcessParameters,              // ProcessParameters
                 NULL,                           // ProcessSecurityDescriptor
                 NULL,                           // ThreadSecurityDescriptor
                 NtCurrentProcess(),             // ParentProcess
                 FALSE,                          // InheritHandles
                 NULL,                           // DebugPort
                 NULL,                           // ExceptionPort
                 &ProcessInformation             // ProcessInformation
                 ); SEASSERT_SUCCESS(Status);

    Status = NtResumeThread(
                  ProcessInformation.Thread,
                  NULL
                  ); SEASSERT_SUCCESS(Status);

    RtlDestroyProcessParameters( ProcessParameters );
    RtlFreeUnicodeString( &UnicodeProgramName );
    RtlFreeUnicodeString( &UnicodeImagePathName );

}
示例#2
0
文件: smss.c 项目: rmallof/reactos
NTSTATUS
NTAPI
SmpExecuteImage(IN PUNICODE_STRING FileName,
                IN PUNICODE_STRING Directory,
                IN PUNICODE_STRING CommandLine,
                IN ULONG MuSessionId,
                IN ULONG Flags,
                IN PRTL_USER_PROCESS_INFORMATION ProcessInformation)
{
    PRTL_USER_PROCESS_INFORMATION ProcessInfo;
    NTSTATUS Status;
    RTL_USER_PROCESS_INFORMATION LocalProcessInfo;
    PRTL_USER_PROCESS_PARAMETERS ProcessParameters;

    /* Use the input process information if we have it, otherwise use local */
    ProcessInfo = ProcessInformation;
    if (!ProcessInfo) ProcessInfo = &LocalProcessInfo;

    /* Create parameters for the target process */
    Status = RtlCreateProcessParameters(&ProcessParameters,
                                        FileName,
                                        SmpDefaultLibPath.Length ?
                                        &SmpDefaultLibPath : NULL,
                                        Directory,
                                        CommandLine,
                                        SmpDefaultEnvironment,
                                        NULL,
                                        NULL,
                                        NULL,
                                        0);
    if (!NT_SUCCESS(Status))
    {
        /* This is a pretty bad failure. ASSERT on checked builds and exit */
        ASSERTMSG("RtlCreateProcessParameters", NT_SUCCESS(Status));
        DPRINT1("SMSS: RtlCreateProcessParameters failed for %wZ - Status == %lx\n",
                FileName, Status);
        return Status;
    }

    /* Set the size field as required */
    ProcessInfo->Size = sizeof(RTL_USER_PROCESS_INFORMATION);

    /* Check if the debug flag was requested */
    if (Flags & SMP_DEBUG_FLAG)
    {
        /* Write it in the process parameters */
        ProcessParameters->DebugFlags = 1;
    }
    else
    {
        /* Otherwise inherit the flag that was passed to SMSS itself */
        ProcessParameters->DebugFlags = SmpDebug;
    }

    /* Subsystems get the first 1MB of memory reserved for DOS/IVT purposes */
    if (Flags & SMP_SUBSYSTEM_FLAG)
    {
        ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_RESERVE_1MB;
    }

    /* And always force NX for anything that SMSS launches */
    ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_NX;

    /* Now create the process */
    Status = RtlCreateUserProcess(FileName,
                                  OBJ_CASE_INSENSITIVE,
                                  ProcessParameters,
                                  NULL,
                                  NULL,
                                  NULL,
                                  FALSE,
                                  NULL,
                                  NULL,
                                  ProcessInfo);
    RtlDestroyProcessParameters(ProcessParameters);
    if (!NT_SUCCESS(Status))
    {
        /* If we couldn't create it, fail back to the caller */
        DPRINT1("SMSS: Failed load of %wZ - Status  == %lx\n",
                FileName, Status);
        return Status;
    }

    /* Associate a session with this process */
    Status = SmpSetProcessMuSessionId(ProcessInfo->ProcessHandle, MuSessionId);

    /* If the application is deferred (suspended), there's nothing to do */
    if (Flags & SMP_DEFERRED_FLAG) return Status;

    /* Otherwise, get ready to start it, but make sure it's a native app */
    if (ProcessInfo->ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_NATIVE)
    {
        /* Resume it */
        NtResumeThread(ProcessInfo->ThreadHandle, NULL);
        if (!(Flags & SMP_ASYNC_FLAG))
        {
            /* Block on it unless Async was requested */
            NtWaitForSingleObject(ProcessInfo->ThreadHandle, FALSE, NULL);
        }

        /* It's up and running now, close our handles */
        NtClose(ProcessInfo->ThreadHandle);
        NtClose(ProcessInfo->ProcessHandle);
    }
    else
    {
        /* This image is invalid, so kill it, close our handles, and fail */
        Status = STATUS_INVALID_IMAGE_FORMAT;
        NtTerminateProcess(ProcessInfo->ProcessHandle, Status);
        NtWaitForSingleObject(ProcessInfo->ThreadHandle, 0, 0);
        NtClose(ProcessInfo->ThreadHandle);
        NtClose(ProcessInfo->ProcessHandle);
        DPRINT1("SMSS: Not an NT image - %wZ\n", FileName);
    }

    /* Return the outcome of the process create */
    return Status;
}
示例#3
0
/**********************************************************************
 * SmCreateUserProcess/5
 *
 * DESCRIPTION
 *
 * ARGUMENTS
 *	ImagePath: absolute path of the image to run;
 *	CommandLine: arguments and options for ImagePath;
 *	Flags: Wait flag: Set for boot time processes and unset for
 *			subsystems bootstrapping;
 *		1Mb reserve flag: Set for subsystems, unset for everything
*			else
 *	Timeout: optional: used if WaitForIt==TRUE;
 *	ProcessHandle: optional: a duplicated handle for
 		the child process (storage provided by the caller).
 *
 * RETURN VALUE
 * 	NTSTATUS:
 *
 */
NTSTATUS NTAPI
SmCreateUserProcess (LPWSTR ImagePath,
		     LPWSTR CommandLine,
		     ULONG Flags,
		     PLARGE_INTEGER Timeout OPTIONAL,
		     PRTL_USER_PROCESS_INFORMATION UserProcessInfo OPTIONAL)
{
	UNICODE_STRING			ImagePathString   = { 0, 0, NULL };
	UNICODE_STRING			CommandLineString = { 0, 0, NULL };
	UNICODE_STRING			SystemDirectory   = { 0, 0, NULL };
	PRTL_USER_PROCESS_PARAMETERS	ProcessParameters = NULL;
	RTL_USER_PROCESS_INFORMATION	ProcessInfo  = {0};
	PRTL_USER_PROCESS_INFORMATION	pProcessInfo = & ProcessInfo;
	NTSTATUS			Status = STATUS_SUCCESS;

	DPRINT("SM: %s called\n", __FUNCTION__);

	if (NULL != UserProcessInfo)
	{
		pProcessInfo = UserProcessInfo;
	}

	RtlInitUnicodeString (& ImagePathString, ImagePath);
	RtlInitUnicodeString (& CommandLineString, CommandLine);

	SystemDirectory.MaximumLength = (wcslen(SharedUserData->NtSystemRoot) * sizeof(WCHAR)) + sizeof(szSystemDirectory);
	SystemDirectory.Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
						 0,
						 SystemDirectory.MaximumLength);
	if (SystemDirectory.Buffer == NULL)
	{
		Status = STATUS_NO_MEMORY;
		DPRINT1("SM: %s: Allocating system directory string failed (Status=0x%08lx)\n",
			__FUNCTION__, Status);
		return Status;
	}

	Status = RtlAppendUnicodeToString(& SystemDirectory,
					  SharedUserData->NtSystemRoot);
	if (!NT_SUCCESS(Status))
	{
		goto FailProcParams;
	}

	Status = RtlAppendUnicodeToString(& SystemDirectory,
					  szSystemDirectory);
	if (!NT_SUCCESS(Status))
	{
		goto FailProcParams;
	}


	Status = RtlCreateProcessParameters(& ProcessParameters,
					    & ImagePathString,
					    NULL,
					    & SystemDirectory,
					    & CommandLineString,
					    SmSystemEnvironment,
					    NULL,
					    NULL,
					    NULL,
					    NULL);

	RtlFreeHeap(RtlGetProcessHeap(),
		    0,
		    SystemDirectory.Buffer);

	if (!NT_SUCCESS(Status))
	{
FailProcParams:
		DPRINT1("SM: %s: Creating process parameters failed (Status=0x%08lx)\n",
			__FUNCTION__, Status);
		return Status;
	}

	/* Reserve lower 1Mb, if requested */
	if (Flags & SM_CREATE_FLAG_RESERVE_1MB)
		ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_RESERVE_1MB;

	/* Create the user process */
	Status = RtlCreateUserProcess (& ImagePathString,
				       OBJ_CASE_INSENSITIVE,
				       ProcessParameters,
				       NULL,
				       NULL,
				       NULL,
				       FALSE,
				       NULL,
				       NULL,
				       pProcessInfo);

	RtlDestroyProcessParameters (ProcessParameters);

	if (!NT_SUCCESS(Status))
	{
		DPRINT1("SM: %s: Running \"%S\" failed (Status=0x%08lx)\n",
			__FUNCTION__, ImagePathString.Buffer, Status);
		return Status;
	}
	/*
	 * It the caller is *not* interested in the child info,
	 * resume it immediately.
	 */
	if (NULL == UserProcessInfo)
	{
		Status = NtResumeThread (ProcessInfo.ThreadHandle, NULL);
		if(!NT_SUCCESS(Status))
		{
			DPRINT1("SM: %s: NtResumeThread failed (Status=0x%08lx)\n",
				__FUNCTION__, Status);
		}
	}

	/* Wait for process termination */
	if (Flags & SM_CREATE_FLAG_WAIT)
	{
		Status = NtWaitForSingleObject (pProcessInfo->ProcessHandle,
						FALSE,
						Timeout);
		if (!NT_SUCCESS(Status))
		{
			DPRINT1("SM: %s: NtWaitForSingleObject failed with Status=0x%08lx\n",
				__FUNCTION__, Status);
		}
	}

    if (NULL == UserProcessInfo)
    {
        NtClose(pProcessInfo->ProcessHandle);
        NtClose(pProcessInfo->ThreadHandle);
    }
	return Status;
}
示例#4
0
NTSTATUS
RtlCreateProcessParameters(
    OUT PRTL_USER_PROCESS_PARAMETERS *pProcessParameters,
    IN PUNICODE_STRING ImagePathName,
    IN PUNICODE_STRING DllPath OPTIONAL,
    IN PUNICODE_STRING CurrentDirectory OPTIONAL,
    IN PUNICODE_STRING CommandLine OPTIONAL,
    IN PVOID Environment OPTIONAL,
    IN PUNICODE_STRING WindowTitle OPTIONAL,
    IN PUNICODE_STRING DesktopInfo OPTIONAL,
    IN PUNICODE_STRING ShellInfo OPTIONAL,
    IN PUNICODE_STRING RuntimeData OPTIONAL
    )

/*++

Routine Description:

    This function formats NT style RTL_USER_PROCESS_PARAMETERS
    record.  The record is self-contained in a single block of memory
    allocated by this function.  The allocation method is opaque and
    thus the record must be freed by calling the
    RtlDestroyProcessParameters function.

    The process parameters record is created in a de-normalized form,
    thus making it suitable for passing to the RtlCreateUserProcess
    function.  It is expected that the caller will fill in additional
    fields in the process parameters record after this function returns,
    but prior to calling RtlCreateUserProcess.

Arguments:

    pProcessParameters - Pointer to a variable that will receive the address
        of the process parameter structure created by this routine.  The
        memory for the structure is allocated in an opaque manner and must
        be freed by calling RtlDestroyProcessParameters.

    ImagePathName - Required parameter that is the fully qualified NT
        path name of the image file that will be used to create the process
        that will received these parameters.

    DllPath - An optional parameter that is an NT String variable pointing
        to the search path the NT Loader is to use in the target process
        when searching for Dll modules.  If not specified, then the Dll
        search path is filled in from the current process's Dll search
        path.

    CurrentDirectory - An optional parameter that is an NT String variable
        pointing to the default directory string for the target process.
        If not specified, then the current directory string is filled in
        from the current process's current directory string.

    CommandLine - An optional parameter that is an NT String variable that
        will be passed to the target process as its command line.  If not
        specified, then the command line passed to the target process will
        be a null string.

    Environment - An optional parameter that is an opaque pointer to an
        environment variable block of the type created by
        RtlCreateEnvironment routine.  If not specified, then the target
        process will receive a copy of the calling process's environment
        variable block.

    WindowTitle - An optional parameter that is an NT String variable that
        points to the title string the target process is to use for its
        main window.  If not specified, then a null string will be passed
        to the target process as its default window title.

    DesktopInfo - An optional parameter that is an NT String variable that
        contains uninterpreted data that is passed as is to the target
        process.  If not specified, the target process will receive a
        pointer to an empty string.

    ShellInfo - An optional parameter that is an NT String variable that
        contains uninterpreted data that is passed as is to the target
        process.  If not specified, the target process will receive a
        pointer to an empty string.

    RuntimeData - An optional parameter that is an NT String variable that
        contains uninterpreted data that is passed as is to the target
        process.  If not specified, the target process will receive a
        pointer to an empty string.

Return Value:

    STATUS_SUCCESS - The process parameters is De-Normalized and
        contains entries for each of the specified argument and variable
        strings.

    STATUS_BUFFER_TOO_SMALL - The specified process parameters buffer is
        too small to contain the argument and environment strings. The value
        of ProcessParameters->Length is modified to contain the buffer
        size needed to contain the argument and variable strings.

--*/

{
    PRTL_USER_PROCESS_PARAMETERS p;
    NTSTATUS Status;
    ULONG ByteCount;
    PWSTR pDst;
    PPEB Peb;
    PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
    HANDLE CurDirHandle;
    BOOLEAN PebLockAcquired = FALSE;

    //
    // Acquire the Peb Lock for the duration while we copy information out
    // of it.
    //

    Peb = NtCurrentPeb();
    ProcessParameters = Peb->ProcessParameters;

    Status = STATUS_SUCCESS;
    p = NULL;
    CurDirHandle = NULL;
    try {
        //
        //  Validate input parameters
        //

#define VALIDATE_STRING_PARAMETER(_x) \
    do { \
        ASSERT(ARGUMENT_PRESENT((_x))); \
        if (!ARGUMENT_PRESENT((_x))) { \
            Status = STATUS_INVALID_PARAMETER; \
            leave; \
        } \
        if (ARGUMENT_PRESENT((_x))) { \
            ASSERT((_x)->MaximumLength >= (_x)->Length); \
            ASSERT(((_x)->Length == 0) || ((_x)->Buffer != NULL)); \
            if (((_x)->MaximumLength < (_x)->Length) || \
                (((_x)->Length != 0) && ((_x)->Buffer == NULL))) { \
                Status = STATUS_INVALID_PARAMETER; \
                leave; \
            } \
        } \
    } while (0)

#define VALIDATE_OPTIONAL_STRING_PARAMETER(_x) \
    do { \
        if (ARGUMENT_PRESENT((_x))) { \
            ASSERT((_x)->MaximumLength >= (_x)->Length); \
            ASSERT(((_x)->Length == 0) || ((_x)->Buffer != NULL)); \
            if (((_x)->MaximumLength < (_x)->Length) || \
                (((_x)->Length != 0) && ((_x)->Buffer == NULL))) { \
                Status = STATUS_INVALID_PARAMETER; \
                leave; \
            } \
        } \
    } while (0)

        VALIDATE_STRING_PARAMETER (ImagePathName);
        VALIDATE_OPTIONAL_STRING_PARAMETER (DllPath);
        VALIDATE_OPTIONAL_STRING_PARAMETER (CurrentDirectory);
        VALIDATE_OPTIONAL_STRING_PARAMETER (CommandLine);
        VALIDATE_OPTIONAL_STRING_PARAMETER (WindowTitle);
        VALIDATE_OPTIONAL_STRING_PARAMETER (DesktopInfo);
        VALIDATE_OPTIONAL_STRING_PARAMETER (ShellInfo);
        VALIDATE_OPTIONAL_STRING_PARAMETER (RuntimeData);

#undef VALIDATE_STRING_PARAMETER
#undef VALIDATE_OPTIONAL_STRING_PARAMETER

        if (!ARGUMENT_PRESENT (CommandLine)) {
            CommandLine = ImagePathName;
        }

        if (!ARGUMENT_PRESENT (WindowTitle)) {
            WindowTitle = (PUNICODE_STRING)&NullString;
        }

        if (!ARGUMENT_PRESENT (DesktopInfo)) {
            DesktopInfo = (PUNICODE_STRING)&NullString;
        }

        if (!ARGUMENT_PRESENT (ShellInfo)) {
            ShellInfo = (PUNICODE_STRING)&NullString;
        }

        if (!ARGUMENT_PRESENT (RuntimeData)) {
            RuntimeData = (PUNICODE_STRING)&NullString;
        }

        //
        // Determine size need to contain the process parameter record
        // structure and all of the strings it will point to.  Each string
        // will be aligned on a ULONG byte boundary.
        // We do the ones we can outside of the peb lock.
        //

        ByteCount = sizeof (*ProcessParameters);
        ByteCount += ROUND_UP (DOS_MAX_PATH_LENGTH*2,        sizeof( ULONG ) );
        ByteCount += ROUND_UP (ImagePathName->Length + sizeof(UNICODE_NULL),    sizeof( ULONG ) );
        ByteCount += ROUND_UP (CommandLine->Length + sizeof(UNICODE_NULL),      sizeof( ULONG ) );
        ByteCount += ROUND_UP (WindowTitle->MaximumLength,   sizeof( ULONG ) );
        ByteCount += ROUND_UP (DesktopInfo->MaximumLength,   sizeof( ULONG ) );
        ByteCount += ROUND_UP (ShellInfo->MaximumLength,     sizeof( ULONG ) );
        ByteCount += ROUND_UP (RuntimeData->MaximumLength,   sizeof( ULONG ) );

        PebLockAcquired = TRUE;
        RtlAcquirePebLock ();

        //
        // For optional pointer parameters, default them to point to their
        // corresponding field in the current process's process parameter
        // structure or to a null string.
        //

        if (!ARGUMENT_PRESENT (DllPath)) {
            DllPath = &ProcessParameters->DllPath;
        }

        if (!ARGUMENT_PRESENT (CurrentDirectory)) {

            if (ProcessParameters->CurrentDirectory.Handle) {
                CurDirHandle = (HANDLE)((ULONG_PTR)ProcessParameters->CurrentDirectory.Handle & ~OBJ_HANDLE_TAGBITS);
                CurDirHandle = (HANDLE)((ULONG_PTR)CurDirHandle | RTL_USER_PROC_CURDIR_INHERIT);
            }
            CurrentDirectory = &ProcessParameters->CurrentDirectory.DosPath;
        } else {
            ASSERT(CurrentDirectory->MaximumLength >= CurrentDirectory->Length);
            ASSERT((CurrentDirectory->Length == 0) || (CurrentDirectory->Buffer != NULL));

            if (ProcessParameters->CurrentDirectory.Handle) {
                CurDirHandle = (HANDLE)((ULONG_PTR)ProcessParameters->CurrentDirectory.Handle & ~OBJ_HANDLE_TAGBITS);
                CurDirHandle = (HANDLE)((ULONG_PTR)CurDirHandle | RTL_USER_PROC_CURDIR_CLOSE);
            }
        }


        if (!ARGUMENT_PRESENT (Environment)) {
            Environment = ProcessParameters->Environment;
        }

        ByteCount += ROUND_UP (DllPath->MaximumLength,       sizeof( ULONG ) );

        //
        // Allocate memory for the process parameter record.
        //
        p = RtlAllocateHeap (RtlProcessHeap (), 0, ByteCount);

        if (p == NULL) {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            __leave;
        }

        RtlZeroMemory (p, sizeof (*p));

        p->MaximumLength = ByteCount;
        p->Length = ByteCount;
        p->Flags = RTL_USER_PROC_PARAMS_NORMALIZED;
        p->DebugFlags = 0;
        p->Environment = Environment;
        p->CurrentDirectory.Handle = CurDirHandle;

        //
        // Inherits ^C inhibit information
        //

        p->ConsoleFlags = ProcessParameters->ConsoleFlags;

        pDst = (PWSTR)(p + 1);
        RtlpCopyProcString (&pDst,
                            &p->CurrentDirectory.DosPath,
                            CurrentDirectory,
                            DOS_MAX_PATH_LENGTH*2);

        RtlpCopyProcString (&pDst, &p->DllPath, DllPath, 0);
        RtlpCopyProcString (&pDst, &p->ImagePathName, ImagePathName, ImagePathName->Length + sizeof (UNICODE_NULL));
        if (CommandLine->Length == CommandLine->MaximumLength) {
            RtlpCopyProcString (&pDst, &p->CommandLine, CommandLine, 0);
        } else {
            RtlpCopyProcString (&pDst, &p->CommandLine, CommandLine, CommandLine->Length + sizeof (UNICODE_NULL));
        }

        RtlpCopyProcString (&pDst, &p->WindowTitle, WindowTitle, 0);
        RtlpCopyProcString (&pDst, &p->DesktopInfo, DesktopInfo, 0);
        RtlpCopyProcString (&pDst, &p->ShellInfo,   ShellInfo, 0);
        if (RuntimeData->Length != 0) {
            RtlpCopyProcString (&pDst, &p->RuntimeData, RuntimeData, 0);
        }
        *pProcessParameters = RtlDeNormalizeProcessParams (p);
        p = NULL;
    } finally {
        if (PebLockAcquired) {
            RtlReleasePebLock();
        }

        if (AbnormalTermination ()) {
            Status = STATUS_ACCESS_VIOLATION;
        }

        if (p != NULL) {
            RtlDestroyProcessParameters (p);
        }

    }

    return Status;
}
示例#5
0
NTSTATUS
Nt_CreateProcess(
    LPCWSTR                 ApplicationName,
    LPWSTR                  CommandLine,
    ULONG                   CreationFlags,
    LPCWSTR                 CurrentDirectory,
    LPSTARTUPINFO           StartupInfo,
    LPPROCESS_INFORMATION   ProcessInformation
)
{
    NTSTATUS                        Status;
    WCHAR                           FullPathBuffer[MAX_PATH *2], DllPathBuffer[0x3000];
    UNICODE_STRING                  PathVariableName, DllPath, ImagePathName;
    UNICODE_STRING                  _CommandLine, _CurrentDirectory;
    PRTL_USER_PROCESS_PARAMETERS    ProcessParameters;
    RTL_USER_PROCESS_INFORMATION    ProcessInfo;

    if (!RtlDosPathNameToNtPathName_U(ApplicationName, &ImagePathName, NULL, NULL))
        return STATUS_OBJECT_PATH_NOT_FOUND;

    RTL_CONST_STRING(PathVariableName, L"Path");
    DllPath.Length = 0;
    DllPath.MaximumLength = sizeof(DllPathBuffer);
    DllPath.Buffer = DllPathBuffer;
    RtlQueryEnvironmentVariable_U(NULL, &PathVariableName, &DllPath);

    if (CommandLine != NULL)
        RtlInitUnicodeString(&_CommandLine, CommandLine);
    if (CurrentDirectory != NULL)
        RtlInitUnicodeString(&_CurrentDirectory, CurrentDirectory);

    Status = RtlCreateProcessParameters(
                &ProcessParameters,
                &ImagePathName,
                &DllPath,
                CurrentDirectory == NULL ? NULL : &_CurrentDirectory,
                CommandLine == NULL ? NULL : &_CommandLine,
                NULL,
                NULL,
                NULL,
                NULL,
                NULL
             );

    if (!NT_SUCCESS(Status))
    {
        RtlFreeUnicodeString(&ImagePathName);
        return Status;
    }

    ProcessInfo.Size = sizeof(ProcessInfo);
    Status = RtlCreateUserProcess(
                &ImagePathName,
                OBJ_CASE_INSENSITIVE,
                ProcessParameters,
                NULL,
                NULL,
                NULL,
                FALSE,
                NULL,
                NULL,
                &ProcessInfo
             );

    RtlDestroyProcessParameters(ProcessParameters);
    RtlFreeUnicodeString(&ImagePathName);

    if (!NT_SUCCESS(Status))
        return Status;

    if (!FLAG_ON(CreationFlags, CREATE_SUSPENDED))
    {
        NtResumeThread(ProcessInfo.ThreadHandle, NULL);
    }

    if (ProcessInformation == NULL)
    {
        NtClose(ProcessInfo.ProcessHandle);
        NtClose(ProcessInfo.ThreadHandle);
        return Status;
    }

    ProcessInformation->dwProcessId = (ULONG)ProcessInfo.ClientId.UniqueProcess;
    ProcessInformation->dwThreadId  = (ULONG)ProcessInfo.ClientId.UniqueThread;
    ProcessInformation->hProcess    = ProcessInfo.ProcessHandle;
    ProcessInformation->hThread     = ProcessInfo.ThreadHandle;

    return Status;
}