mirror of
https://github.com/reactos/reactos.git
synced 2025-08-10 22:55:38 +00:00
1122 lines
30 KiB
C
1122 lines
30 KiB
C
/* $Id: create.c,v 1.65 2003/04/07 23:10:07 gvg Exp $
|
|
*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS system libraries
|
|
* FILE: lib/kernel32/process/create.c
|
|
* PURPOSE: Process functions
|
|
* PROGRAMMER: Ariadne ( ariadne@xs4all.nl)
|
|
* UPDATE HISTORY:
|
|
* Created 01/11/98
|
|
*/
|
|
|
|
/* INCLUDES ****************************************************************/
|
|
|
|
#include <k32.h>
|
|
|
|
#define NDEBUG
|
|
#include <kernel32/kernel32.h>
|
|
|
|
/* FUNCTIONS ****************************************************************/
|
|
|
|
__declspec(dllimport)
|
|
PRTL_BASE_PROCESS_START_ROUTINE RtlBaseProcessStartRoutine;
|
|
|
|
WINBOOL STDCALL
|
|
CreateProcessA (LPCSTR lpApplicationName,
|
|
LPSTR lpCommandLine,
|
|
LPSECURITY_ATTRIBUTES lpProcessAttributes,
|
|
LPSECURITY_ATTRIBUTES lpThreadAttributes,
|
|
WINBOOL bInheritHandles,
|
|
DWORD dwCreationFlags,
|
|
LPVOID lpEnvironment,
|
|
LPCSTR lpCurrentDirectory,
|
|
LPSTARTUPINFOA lpStartupInfo,
|
|
LPPROCESS_INFORMATION lpProcessInformation)
|
|
/*
|
|
* FUNCTION: The CreateProcess function creates a new process and its
|
|
* primary thread. The new process executes the specified executable file
|
|
* ARGUMENTS:
|
|
*
|
|
* lpApplicationName = Pointer to name of executable module
|
|
* lpCommandLine = Pointer to command line string
|
|
* lpProcessAttributes = Process security attributes
|
|
* lpThreadAttributes = Thread security attributes
|
|
* bInheritHandles = Handle inheritance flag
|
|
* dwCreationFlags = Creation flags
|
|
* lpEnvironment = Pointer to new environment block
|
|
* lpCurrentDirectory = Pointer to current directory name
|
|
* lpStartupInfo = Pointer to startup info
|
|
* lpProcessInformation = Pointer to process information
|
|
*/
|
|
{
|
|
PWCHAR lpEnvironmentW = NULL;
|
|
UNICODE_STRING ApplicationNameU;
|
|
UNICODE_STRING CurrentDirectoryU;
|
|
UNICODE_STRING CommandLineU;
|
|
ANSI_STRING ApplicationName;
|
|
ANSI_STRING CurrentDirectory;
|
|
ANSI_STRING CommandLine;
|
|
WINBOOL Result;
|
|
CHAR TempCurrentDirectoryA[256];
|
|
|
|
DPRINT("CreateProcessA(%s)\n", lpApplicationName);
|
|
DPRINT("dwCreationFlags %x, lpEnvironment %x, lpCurrentDirectory %x, "
|
|
"lpStartupInfo %x, lpProcessInformation %x\n", dwCreationFlags,
|
|
lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation);
|
|
|
|
if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
|
|
{
|
|
PCHAR ptr = lpEnvironment;
|
|
ULONG len = 0;
|
|
UNICODE_STRING EnvironmentU;
|
|
ANSI_STRING EnvironmentA;
|
|
while (*ptr)
|
|
{
|
|
RtlInitAnsiString(&EnvironmentA, ptr);
|
|
if (bIsFileApiAnsi)
|
|
len += RtlAnsiStringToUnicodeSize(&EnvironmentA) + sizeof(WCHAR);
|
|
else
|
|
len += RtlOemStringToUnicodeSize(&EnvironmentA) + sizeof(WCHAR);
|
|
ptr += EnvironmentA.MaximumLength;
|
|
}
|
|
len += sizeof(WCHAR);
|
|
lpEnvironmentW = (PWCHAR)RtlAllocateHeap(GetProcessHeap(),
|
|
HEAP_GENERATE_EXCEPTIONS|HEAP_ZERO_MEMORY,
|
|
len);
|
|
if (lpEnvironmentW == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
ptr = lpEnvironment;
|
|
EnvironmentU.Buffer = lpEnvironmentW;
|
|
EnvironmentU.Length = 0;
|
|
EnvironmentU.MaximumLength = len;
|
|
while (*ptr)
|
|
{
|
|
RtlInitAnsiString(&EnvironmentA, ptr);
|
|
if (bIsFileApiAnsi)
|
|
RtlAnsiStringToUnicodeString(&EnvironmentU, &EnvironmentA, FALSE);
|
|
else
|
|
RtlOemStringToUnicodeString(&EnvironmentU, &EnvironmentA, FALSE);
|
|
ptr += EnvironmentA.MaximumLength;
|
|
EnvironmentU.Buffer += (EnvironmentU.Length / sizeof(WCHAR) + 1);
|
|
EnvironmentU.MaximumLength -= (EnvironmentU.Length + sizeof(WCHAR));
|
|
EnvironmentU.Length = 0;
|
|
}
|
|
|
|
EnvironmentU.Buffer[0] = 0;
|
|
}
|
|
|
|
RtlInitAnsiString (&CommandLine,
|
|
lpCommandLine);
|
|
RtlInitAnsiString (&ApplicationName,
|
|
(LPSTR)lpApplicationName);
|
|
if (lpCurrentDirectory != NULL)
|
|
{
|
|
RtlInitAnsiString (&CurrentDirectory,
|
|
(LPSTR)lpCurrentDirectory);
|
|
}
|
|
|
|
/* convert ansi (or oem) strings to unicode */
|
|
if (bIsFileApiAnsi)
|
|
{
|
|
RtlAnsiStringToUnicodeString (&CommandLineU, &CommandLine, TRUE);
|
|
RtlAnsiStringToUnicodeString (&ApplicationNameU, &ApplicationName, TRUE);
|
|
if (lpCurrentDirectory != NULL)
|
|
RtlAnsiStringToUnicodeString (&CurrentDirectoryU, &CurrentDirectory, TRUE);
|
|
}
|
|
else
|
|
{
|
|
RtlOemStringToUnicodeString (&CommandLineU, &CommandLine, TRUE);
|
|
RtlOemStringToUnicodeString (&ApplicationNameU, &ApplicationName, TRUE);
|
|
if (lpCurrentDirectory != NULL)
|
|
RtlOemStringToUnicodeString (&CurrentDirectoryU, &CurrentDirectory, TRUE);
|
|
}
|
|
|
|
Result = CreateProcessW (ApplicationNameU.Buffer,
|
|
CommandLineU.Buffer,
|
|
lpProcessAttributes,
|
|
lpThreadAttributes,
|
|
bInheritHandles,
|
|
dwCreationFlags,
|
|
dwCreationFlags & CREATE_UNICODE_ENVIRONMENT ? lpEnvironment : lpEnvironmentW,
|
|
(lpCurrentDirectory == NULL) ? NULL : CurrentDirectoryU.Buffer,
|
|
(LPSTARTUPINFOW)lpStartupInfo,
|
|
lpProcessInformation);
|
|
|
|
RtlFreeUnicodeString (&ApplicationNameU);
|
|
RtlFreeUnicodeString (&CommandLineU);
|
|
if (lpCurrentDirectory != NULL)
|
|
RtlFreeUnicodeString (&CurrentDirectoryU);
|
|
|
|
if (lpEnvironmentW)
|
|
{
|
|
RtlFreeHeap(GetProcessHeap(), 0, lpEnvironmentW);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
static int _except_recursion_trap = 0;
|
|
|
|
struct _CONTEXT;
|
|
struct __EXCEPTION_RECORD;
|
|
|
|
static
|
|
EXCEPTION_DISPOSITION
|
|
__cdecl
|
|
_except_handler(
|
|
struct _EXCEPTION_RECORD *ExceptionRecord,
|
|
void * EstablisherFrame,
|
|
struct _CONTEXT *ContextRecord,
|
|
void * DispatcherContext )
|
|
{
|
|
DPRINT1("Process terminated abnormally due to unhandled exception\n");
|
|
|
|
if (3 < ++_except_recursion_trap)
|
|
{
|
|
DPRINT1("_except_handler(...) appears to be recursing.\n");
|
|
DPRINT1("Process HALTED.\n");
|
|
for (;;)
|
|
{
|
|
}
|
|
}
|
|
|
|
if (/* FIXME: */ TRUE) /* Not a service */
|
|
{
|
|
DPRINT(" calling ExitProcess(0) no, lets try ExitThread . . .\n");
|
|
/* ExitProcess(0); */
|
|
ExitThread(0);
|
|
}
|
|
else
|
|
{
|
|
DPRINT(" calling ExitThread(0) . . .\n");
|
|
ExitThread(0);
|
|
}
|
|
|
|
DPRINT1(" We should not get to here !!!\n");
|
|
/* We should not get to here */
|
|
return ExceptionContinueSearch;
|
|
}
|
|
|
|
VOID STDCALL
|
|
BaseProcessStart(LPTHREAD_START_ROUTINE lpStartAddress,
|
|
DWORD lpParameter)
|
|
{
|
|
UINT uExitCode = 0;
|
|
|
|
DPRINT("BaseProcessStart(..) - setting up exception frame.\n");
|
|
|
|
__try1(_except_handler)
|
|
{
|
|
uExitCode = (lpStartAddress)((PVOID)lpParameter);
|
|
} __except1
|
|
{
|
|
}
|
|
|
|
DPRINT("BaseProcessStart(..) - cleaned up exception frame.\n");
|
|
|
|
ExitThread(uExitCode);
|
|
}
|
|
|
|
|
|
HANDLE STDCALL
|
|
KlCreateFirstThread(HANDLE ProcessHandle,
|
|
LPSECURITY_ATTRIBUTES lpThreadAttributes,
|
|
PSECTION_IMAGE_INFORMATION Sii,
|
|
LPTHREAD_START_ROUTINE lpStartAddress,
|
|
DWORD dwCreationFlags,
|
|
LPDWORD lpThreadId)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE ThreadHandle;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
CLIENT_ID ClientId;
|
|
CONTEXT ThreadContext;
|
|
INITIAL_TEB InitialTeb;
|
|
BOOLEAN CreateSuspended = FALSE;
|
|
ULONG OldPageProtection;
|
|
ULONG ResultLength;
|
|
ULONG ThreadStartAddress;
|
|
ULONG InitialStack[6];
|
|
|
|
ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);
|
|
ObjectAttributes.RootDirectory = NULL;
|
|
ObjectAttributes.ObjectName = NULL;
|
|
ObjectAttributes.Attributes = 0;
|
|
if (lpThreadAttributes != NULL)
|
|
{
|
|
if (lpThreadAttributes->bInheritHandle)
|
|
ObjectAttributes.Attributes = OBJ_INHERIT;
|
|
ObjectAttributes.SecurityDescriptor =
|
|
lpThreadAttributes->lpSecurityDescriptor;
|
|
}
|
|
ObjectAttributes.SecurityQualityOfService = NULL;
|
|
|
|
if ((dwCreationFlags & CREATE_SUSPENDED) == CREATE_SUSPENDED)
|
|
CreateSuspended = TRUE;
|
|
else
|
|
CreateSuspended = FALSE;
|
|
|
|
InitialTeb.StackReserve = (Sii->StackReserve < 0x100000) ? 0x100000 : Sii->StackReserve;
|
|
/* FIXME: use correct commit size */
|
|
#if 0
|
|
InitialTeb.StackCommit = (Sii->StackCommit < PAGE_SIZE) ? PAGE_SIZE : Sii->StackCommit;
|
|
#endif
|
|
InitialTeb.StackCommit = InitialTeb.StackReserve - PAGE_SIZE;
|
|
|
|
/* size of guard page */
|
|
InitialTeb.StackCommit += PAGE_SIZE;
|
|
|
|
/* Reserve stack */
|
|
InitialTeb.StackAllocate = NULL;
|
|
Status = NtAllocateVirtualMemory(ProcessHandle,
|
|
&InitialTeb.StackAllocate,
|
|
0,
|
|
&InitialTeb.StackReserve,
|
|
MEM_RESERVE,
|
|
PAGE_READWRITE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("Error reserving stack space!\n");
|
|
SetLastErrorByStatus(Status);
|
|
return(INVALID_HANDLE_VALUE);
|
|
}
|
|
|
|
DPRINT("StackAllocate: %p ReserveSize: 0x%lX\n",
|
|
InitialTeb.StackAllocate, InitialTeb.StackReserve);
|
|
|
|
InitialTeb.StackBase = (PVOID)((ULONG)InitialTeb.StackAllocate + InitialTeb.StackReserve);
|
|
InitialTeb.StackLimit = (PVOID)((ULONG)InitialTeb.StackBase - InitialTeb.StackCommit);
|
|
|
|
DPRINT("StackBase: %p StackCommit: %p\n",
|
|
InitialTeb.StackBase, InitialTeb.StackCommit);
|
|
|
|
/* Commit stack page(s) */
|
|
Status = NtAllocateVirtualMemory(ProcessHandle,
|
|
&InitialTeb.StackLimit,
|
|
0,
|
|
&InitialTeb.StackCommit,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* release the stack space */
|
|
NtFreeVirtualMemory(ProcessHandle,
|
|
InitialTeb.StackAllocate,
|
|
&InitialTeb.StackReserve,
|
|
MEM_RELEASE);
|
|
|
|
DPRINT("Error comitting stack page(s)!\n");
|
|
SetLastErrorByStatus(Status);
|
|
return(INVALID_HANDLE_VALUE);
|
|
}
|
|
|
|
DPRINT("StackLimit: %p\n",
|
|
InitialTeb.StackLimit);
|
|
|
|
/* Protect guard page */
|
|
Status = NtProtectVirtualMemory(ProcessHandle,
|
|
InitialTeb.StackLimit,
|
|
PAGE_SIZE,
|
|
PAGE_GUARD | PAGE_READWRITE,
|
|
&OldPageProtection);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* release the stack space */
|
|
NtFreeVirtualMemory(ProcessHandle,
|
|
InitialTeb.StackAllocate,
|
|
&InitialTeb.StackReserve,
|
|
MEM_RELEASE);
|
|
|
|
DPRINT("Error comitting guard page!\n");
|
|
SetLastErrorByStatus(Status);
|
|
return(INVALID_HANDLE_VALUE);
|
|
}
|
|
|
|
if (Sii->Subsystem != IMAGE_SUBSYSTEM_NATIVE)
|
|
{
|
|
ThreadStartAddress = (ULONG) BaseProcessStart;
|
|
}
|
|
else
|
|
{
|
|
ThreadStartAddress = (ULONG) RtlBaseProcessStartRoutine;
|
|
}
|
|
|
|
memset(&ThreadContext,0,sizeof(CONTEXT));
|
|
ThreadContext.Eip = ThreadStartAddress;
|
|
ThreadContext.SegGs = USER_DS;
|
|
ThreadContext.SegFs = USER_DS;
|
|
ThreadContext.SegEs = USER_DS;
|
|
ThreadContext.SegDs = USER_DS;
|
|
ThreadContext.SegCs = USER_CS;
|
|
ThreadContext.SegSs = USER_DS;
|
|
ThreadContext.Esp = (ULONG)InitialTeb.StackBase - 6*4;
|
|
ThreadContext.EFlags = (1<<1) + (1<<9);
|
|
|
|
DPRINT("ThreadContext.Eip %x\n",ThreadContext.Eip);
|
|
|
|
/*
|
|
* Write in the initial stack.
|
|
*/
|
|
InitialStack[0] = 0;
|
|
InitialStack[1] = (DWORD)lpStartAddress;
|
|
InitialStack[2] = PEB_BASE;
|
|
|
|
Status = ZwWriteVirtualMemory(ProcessHandle,
|
|
(PVOID)ThreadContext.Esp,
|
|
InitialStack,
|
|
sizeof(InitialStack),
|
|
&ResultLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to write initial stack.\n");
|
|
return(INVALID_HANDLE_VALUE);
|
|
}
|
|
|
|
Status = NtCreateThread(&ThreadHandle,
|
|
THREAD_ALL_ACCESS,
|
|
&ObjectAttributes,
|
|
ProcessHandle,
|
|
&ClientId,
|
|
&ThreadContext,
|
|
&InitialTeb,
|
|
CreateSuspended);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
NtFreeVirtualMemory(ProcessHandle,
|
|
InitialTeb.StackAllocate,
|
|
&InitialTeb.StackReserve,
|
|
MEM_RELEASE);
|
|
SetLastErrorByStatus(Status);
|
|
return(INVALID_HANDLE_VALUE);
|
|
}
|
|
|
|
if (lpThreadId != NULL)
|
|
{
|
|
memcpy(lpThreadId, &ClientId.UniqueThread,sizeof(ULONG));
|
|
}
|
|
|
|
return(ThreadHandle);
|
|
}
|
|
|
|
HANDLE
|
|
KlMapFile(LPCWSTR lpApplicationName)
|
|
{
|
|
HANDLE hFile;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
UNICODE_STRING ApplicationNameString;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
|
|
NTSTATUS Status;
|
|
HANDLE hSection;
|
|
|
|
hFile = NULL;
|
|
|
|
/*
|
|
* Find the application name
|
|
*/
|
|
|
|
if (!RtlDosPathNameToNtPathName_U ((LPWSTR)lpApplicationName,
|
|
&ApplicationNameString,
|
|
NULL,
|
|
NULL))
|
|
return NULL;
|
|
|
|
DPRINT("ApplicationName %S\n",ApplicationNameString.Buffer);
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&ApplicationNameString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
SecurityDescriptor);
|
|
|
|
/*
|
|
* Try to open the executable
|
|
*/
|
|
|
|
Status = NtOpenFile(&hFile,
|
|
SYNCHRONIZE|FILE_EXECUTE|FILE_READ_DATA,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_DELETE|FILE_SHARE_READ,
|
|
FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE);
|
|
|
|
RtlFreeUnicodeString (&ApplicationNameString);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("Failed to open file\n");
|
|
SetLastErrorByStatus (Status);
|
|
return(NULL);
|
|
}
|
|
|
|
Status = NtCreateSection(&hSection,
|
|
SECTION_ALL_ACCESS,
|
|
NULL,
|
|
NULL,
|
|
PAGE_EXECUTE,
|
|
SEC_IMAGE,
|
|
hFile);
|
|
NtClose(hFile);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("Failed to create section\n");
|
|
SetLastErrorByStatus (Status);
|
|
return(NULL);
|
|
}
|
|
|
|
return(hSection);
|
|
}
|
|
|
|
static NTSTATUS
|
|
KlInitPeb (HANDLE ProcessHandle,
|
|
PRTL_USER_PROCESS_PARAMETERS Ppb,
|
|
PVOID* ImageBaseAddress)
|
|
{
|
|
NTSTATUS Status;
|
|
PVOID PpbBase;
|
|
ULONG PpbSize;
|
|
ULONG BytesWritten;
|
|
ULONG Offset;
|
|
PVOID ParentEnv = NULL;
|
|
PVOID EnvPtr = NULL;
|
|
PWCHAR ptr;
|
|
ULONG EnvSize = 0, EnvSize1 = 0;
|
|
|
|
/* create the Environment */
|
|
if (Ppb->Environment != NULL)
|
|
{
|
|
ParentEnv = Ppb->Environment;
|
|
ptr = ParentEnv;
|
|
while (*ptr)
|
|
{
|
|
while(*ptr++);
|
|
}
|
|
ptr++;
|
|
EnvSize = (PVOID)ptr - ParentEnv;
|
|
}
|
|
else if (NtCurrentPeb()->ProcessParameters->Environment != NULL)
|
|
{
|
|
MEMORY_BASIC_INFORMATION MemInfo;
|
|
ParentEnv = NtCurrentPeb()->ProcessParameters->Environment;
|
|
|
|
Status = NtQueryVirtualMemory (NtCurrentProcess (),
|
|
ParentEnv,
|
|
MemoryBasicInformation,
|
|
&MemInfo,
|
|
sizeof(MEMORY_BASIC_INFORMATION),
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return Status;
|
|
}
|
|
EnvSize = MemInfo.RegionSize;
|
|
}
|
|
DPRINT("EnvironmentSize %ld\n", EnvSize);
|
|
|
|
/* allocate and initialize new environment block */
|
|
if (EnvSize != 0)
|
|
{
|
|
EnvSize1 = EnvSize;
|
|
Status = NtAllocateVirtualMemory(ProcessHandle,
|
|
&EnvPtr,
|
|
0,
|
|
&EnvSize1,
|
|
MEM_RESERVE | MEM_COMMIT,
|
|
PAGE_READWRITE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return(Status);
|
|
}
|
|
|
|
NtWriteVirtualMemory(ProcessHandle,
|
|
EnvPtr,
|
|
ParentEnv,
|
|
EnvSize,
|
|
&BytesWritten);
|
|
}
|
|
|
|
/* create the PPB */
|
|
PpbBase = NULL;
|
|
PpbSize = Ppb->AllocationSize;
|
|
Status = NtAllocateVirtualMemory(ProcessHandle,
|
|
&PpbBase,
|
|
0,
|
|
&PpbSize,
|
|
MEM_RESERVE | MEM_COMMIT,
|
|
PAGE_READWRITE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return(Status);
|
|
}
|
|
|
|
//DPRINT("Ppb->MaximumLength %x\n", Ppb->MaximumLength);
|
|
NtWriteVirtualMemory(ProcessHandle,
|
|
PpbBase,
|
|
Ppb,
|
|
Ppb->AllocationSize,
|
|
&BytesWritten);
|
|
|
|
/* write pointer to environment */
|
|
Offset = FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, Environment);
|
|
NtWriteVirtualMemory(ProcessHandle,
|
|
(PVOID)(PpbBase + Offset),
|
|
&EnvPtr,
|
|
sizeof(EnvPtr),
|
|
&BytesWritten);
|
|
|
|
/* write pointer to process parameter block */
|
|
Offset = FIELD_OFFSET(PEB, ProcessParameters);
|
|
NtWriteVirtualMemory(ProcessHandle,
|
|
(PVOID)(PEB_BASE + Offset),
|
|
&PpbBase,
|
|
sizeof(PpbBase),
|
|
&BytesWritten);
|
|
|
|
/* Read image base address. */
|
|
Offset = FIELD_OFFSET(PEB, ImageBaseAddress);
|
|
NtReadVirtualMemory(ProcessHandle,
|
|
(PVOID)(PEB_BASE + Offset),
|
|
ImageBaseAddress,
|
|
sizeof(PVOID),
|
|
&BytesWritten);
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
WINBOOL STDCALL
|
|
CreateProcessW(LPCWSTR lpApplicationName,
|
|
LPWSTR lpCommandLine,
|
|
LPSECURITY_ATTRIBUTES lpProcessAttributes,
|
|
LPSECURITY_ATTRIBUTES lpThreadAttributes,
|
|
WINBOOL bInheritHandles,
|
|
DWORD dwCreationFlags,
|
|
LPVOID lpEnvironment,
|
|
LPCWSTR lpCurrentDirectory,
|
|
LPSTARTUPINFOW lpStartupInfo,
|
|
LPPROCESS_INFORMATION lpProcessInformation)
|
|
{
|
|
HANDLE hSection, hProcess, hThread;
|
|
NTSTATUS Status;
|
|
LPTHREAD_START_ROUTINE lpStartAddress = NULL;
|
|
WCHAR ImagePathName[256];
|
|
UNICODE_STRING ImagePathName_U;
|
|
PROCESS_BASIC_INFORMATION ProcessBasicInfo;
|
|
ULONG retlen;
|
|
PRTL_USER_PROCESS_PARAMETERS Ppb;
|
|
UNICODE_STRING CommandLine_U;
|
|
CSRSS_API_REQUEST CsrRequest;
|
|
CSRSS_API_REPLY CsrReply;
|
|
CHAR ImageFileName[8];
|
|
PWCHAR s, e;
|
|
ULONG i, len;
|
|
ANSI_STRING ProcedureName;
|
|
UNICODE_STRING CurrentDirectory_U;
|
|
SECTION_IMAGE_INFORMATION Sii;
|
|
WCHAR TempCurrentDirectoryW[256];
|
|
WCHAR TempApplicationNameW[256];
|
|
WCHAR TempCommandLineNameW[256];
|
|
UNICODE_STRING RuntimeInfo_U;
|
|
PVOID ImageBaseAddress;
|
|
|
|
DPRINT("CreateProcessW(lpApplicationName '%S', lpCommandLine '%S')\n",
|
|
lpApplicationName, lpCommandLine);
|
|
|
|
if (lpApplicationName != NULL && lpApplicationName[0] != 0)
|
|
{
|
|
wcscpy (TempApplicationNameW, lpApplicationName);
|
|
i = wcslen(TempApplicationNameW);
|
|
if (TempApplicationNameW[i - 1] == L'.')
|
|
{
|
|
TempApplicationNameW[i - 1] = 0;
|
|
}
|
|
else
|
|
{
|
|
s = max(wcsrchr(TempApplicationNameW, L'\\'), wcsrchr(TempApplicationNameW, L'/'));
|
|
if (s == NULL)
|
|
{
|
|
s = TempApplicationNameW;
|
|
}
|
|
else
|
|
{
|
|
s++;
|
|
}
|
|
e = wcsrchr(s, L'.');
|
|
if (e == NULL)
|
|
{
|
|
wcscat(s, L".exe");
|
|
e = wcsrchr(s, L'.');
|
|
}
|
|
}
|
|
}
|
|
else if (lpCommandLine != NULL && lpCommandLine[0] != 0)
|
|
{
|
|
if (lpCommandLine[0] == L'"')
|
|
{
|
|
wcscpy(TempApplicationNameW, lpCommandLine + 1);
|
|
s = wcschr(TempApplicationNameW, L'"');
|
|
if (s == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
*s = 0;
|
|
}
|
|
else
|
|
{
|
|
wcscpy(TempApplicationNameW, lpCommandLine);
|
|
s = wcschr(TempApplicationNameW, L' ');
|
|
if (s != NULL)
|
|
{
|
|
*s = 0;
|
|
}
|
|
}
|
|
s = max(wcsrchr(TempApplicationNameW, L'\\'), wcsrchr(TempApplicationNameW, L'/'));
|
|
if (s == NULL)
|
|
{
|
|
s = TempApplicationNameW;
|
|
}
|
|
s = wcsrchr(s, L'.');
|
|
if (s == NULL)
|
|
wcscat(TempApplicationNameW, L".exe");
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!SearchPathW(NULL, TempApplicationNameW, NULL, sizeof(ImagePathName)/sizeof(WCHAR), ImagePathName, &s))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
e = wcsrchr(s, L'.');
|
|
if (e != NULL && (!_wcsicmp(e, L".bat") || !_wcsicmp(e, L".cmd")))
|
|
{
|
|
// the command is a batch file
|
|
if (lpApplicationName != NULL && lpApplicationName[0])
|
|
{
|
|
// FIXME: use COMSPEC for the command interpreter
|
|
wcscpy(TempCommandLineNameW, L"cmd /c ");
|
|
wcscat(TempCommandLineNameW, lpApplicationName);
|
|
lpCommandLine = TempCommandLineNameW;
|
|
wcscpy(TempApplicationNameW, L"cmd.exe");
|
|
if (!SearchPathW(NULL, TempApplicationNameW, NULL, sizeof(ImagePathName)/sizeof(WCHAR), ImagePathName, &s))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Store the image file name for the process
|
|
*/
|
|
e = wcschr(s, L'.');
|
|
if (e != NULL)
|
|
{
|
|
*e = 0;
|
|
}
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
ImageFileName[i] = (CHAR)(s[i]);
|
|
}
|
|
if (e != NULL)
|
|
{
|
|
*e = '.';
|
|
}
|
|
|
|
/*
|
|
* Process the application name and command line
|
|
*/
|
|
RtlInitUnicodeString(&ImagePathName_U, ImagePathName);
|
|
RtlInitUnicodeString(&CommandLine_U, lpCommandLine);
|
|
|
|
DPRINT("ImagePathName_U %S\n", ImagePathName_U.Buffer);
|
|
DPRINT("CommandLine_U %S\n", CommandLine_U.Buffer);
|
|
|
|
/* Initialize the current directory string */
|
|
if (lpCurrentDirectory != NULL)
|
|
{
|
|
RtlInitUnicodeString(&CurrentDirectory_U,
|
|
lpCurrentDirectory);
|
|
}
|
|
else
|
|
{
|
|
GetCurrentDirectoryW(256, TempCurrentDirectoryW);
|
|
RtlInitUnicodeString(&CurrentDirectory_U,
|
|
TempCurrentDirectoryW);
|
|
}
|
|
|
|
/*
|
|
* Create a section for the executable
|
|
*/
|
|
|
|
hSection = KlMapFile (ImagePathName);
|
|
if (hSection == NULL)
|
|
{
|
|
/////////////////////////////////////////
|
|
/*
|
|
* Inspect the image to determine executable flavour
|
|
*/
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
UNICODE_STRING ApplicationNameString;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
|
|
IMAGE_DOS_HEADER DosHeader;
|
|
IO_STATUS_BLOCK Iosb;
|
|
LARGE_INTEGER Offset;
|
|
HANDLE hFile = NULL;
|
|
|
|
DPRINT("Inspecting Image Header for image type id\n");
|
|
|
|
// Find the application name
|
|
if (!RtlDosPathNameToNtPathName_U((LPWSTR)lpApplicationName,
|
|
&ApplicationNameString, NULL, NULL)) {
|
|
return FALSE;
|
|
}
|
|
DPRINT("ApplicationName %S\n",ApplicationNameString.Buffer);
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&ApplicationNameString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
SecurityDescriptor);
|
|
|
|
// Try to open the executable
|
|
Status = NtOpenFile(&hFile,
|
|
SYNCHRONIZE|FILE_EXECUTE|FILE_READ_DATA,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_DELETE|FILE_SHARE_READ,
|
|
FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE);
|
|
|
|
RtlFreeUnicodeString(&ApplicationNameString);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DPRINT("Failed to open file\n");
|
|
SetLastErrorByStatus(Status);
|
|
return FALSE;
|
|
}
|
|
|
|
// Read the dos header
|
|
Offset.QuadPart = 0;
|
|
Status = ZwReadFile(hFile,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&Iosb,
|
|
&DosHeader,
|
|
sizeof(DosHeader),
|
|
&Offset,
|
|
0);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
DPRINT("Failed to read from file\n");
|
|
SetLastErrorByStatus(Status);
|
|
return FALSE;
|
|
}
|
|
if (Iosb.Information != sizeof(DosHeader)) {
|
|
DPRINT("Failed to read dos header from file\n");
|
|
SetLastErrorByStatus(STATUS_INVALID_IMAGE_FORMAT);
|
|
return FALSE;
|
|
}
|
|
|
|
// Check the DOS signature
|
|
if (DosHeader.e_magic != IMAGE_DOS_SIGNATURE) {
|
|
DPRINT("Failed dos magic check\n");
|
|
SetLastErrorByStatus(STATUS_INVALID_IMAGE_FORMAT);
|
|
return FALSE;
|
|
}
|
|
NtClose(hFile);
|
|
|
|
DPRINT("Launching VDM...\n");
|
|
return CreateProcessW(L"ntvdm.exe",
|
|
(LPWSTR)lpApplicationName,
|
|
lpProcessAttributes,
|
|
lpThreadAttributes,
|
|
bInheritHandles,
|
|
dwCreationFlags,
|
|
lpEnvironment,
|
|
lpCurrentDirectory,
|
|
lpStartupInfo,
|
|
lpProcessInformation);
|
|
}
|
|
/////////////////////////////////////////
|
|
/*
|
|
* Create a new process
|
|
*/
|
|
Status = NtCreateProcess(&hProcess,
|
|
PROCESS_ALL_ACCESS,
|
|
NULL,
|
|
NtCurrentProcess(),
|
|
bInheritHandles,
|
|
hSection,
|
|
NULL,
|
|
NULL);
|
|
if (lpStartupInfo)
|
|
{
|
|
if (lpStartupInfo->lpReserved2)
|
|
{
|
|
ULONG i, Count = *(ULONG*)lpStartupInfo->lpReserved2;
|
|
HANDLE * hFile;
|
|
HANDLE hTemp;
|
|
PRTL_USER_PROCESS_PARAMETERS CurrPpb = NtCurrentPeb()->ProcessParameters;
|
|
|
|
|
|
/* FIXME:
|
|
* ROUND_UP(xxx,2) + 2 is a dirty hack. RtlCreateProcessParameters assumes that
|
|
* the runtimeinfo is a unicode string and use RtlCopyUnicodeString for duplication.
|
|
* If is possible that this function overwrite the last information in runtimeinfo
|
|
* with the null terminator for the unicode string.
|
|
*/
|
|
RuntimeInfo_U.Length = RuntimeInfo_U.MaximumLength = ROUND_UP(lpStartupInfo->cbReserved2, 2) + 2;
|
|
RuntimeInfo_U.Buffer = RtlAllocateHeap(GetProcessHeap(), 0, RuntimeInfo_U.Length);
|
|
memcpy(RuntimeInfo_U.Buffer, lpStartupInfo->lpReserved2, lpStartupInfo->cbReserved2);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Create the PPB
|
|
*/
|
|
RtlCreateProcessParameters(&Ppb,
|
|
&ImagePathName_U,
|
|
NULL,
|
|
lpCurrentDirectory ? &CurrentDirectory_U : NULL,
|
|
&CommandLine_U,
|
|
lpEnvironment,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
lpStartupInfo && lpStartupInfo->lpReserved2 ? &RuntimeInfo_U : NULL);
|
|
|
|
if (lpStartupInfo && lpStartupInfo->lpReserved2)
|
|
RtlFreeHeap(GetProcessHeap(), 0, RuntimeInfo_U.Buffer);
|
|
|
|
|
|
/*
|
|
* Translate some handles for the new process
|
|
*/
|
|
if (Ppb->CurrentDirectoryHandle)
|
|
{
|
|
Status = NtDuplicateObject (NtCurrentProcess(),
|
|
Ppb->CurrentDirectoryHandle,
|
|
hProcess,
|
|
&Ppb->CurrentDirectoryHandle,
|
|
0,
|
|
TRUE,
|
|
DUPLICATE_SAME_ACCESS);
|
|
}
|
|
|
|
if (Ppb->hConsole)
|
|
{
|
|
Status = NtDuplicateObject (NtCurrentProcess(),
|
|
Ppb->hConsole,
|
|
hProcess,
|
|
&Ppb->hConsole,
|
|
0,
|
|
TRUE,
|
|
DUPLICATE_SAME_ACCESS);
|
|
}
|
|
|
|
/*
|
|
* Get some information about the executable
|
|
*/
|
|
Status = ZwQuerySection(hSection,
|
|
SectionImageInformation,
|
|
&Sii,
|
|
sizeof(Sii),
|
|
&i);
|
|
/*
|
|
* Close the section
|
|
*/
|
|
NtClose(hSection);
|
|
|
|
/*
|
|
* Get some information about the process
|
|
*/
|
|
NtQueryInformationProcess(hProcess,
|
|
ProcessBasicInformation,
|
|
&ProcessBasicInfo,
|
|
sizeof(ProcessBasicInfo),
|
|
&retlen);
|
|
DPRINT("ProcessBasicInfo.UniqueProcessId %d\n",
|
|
ProcessBasicInfo.UniqueProcessId);
|
|
lpProcessInformation->dwProcessId = ProcessBasicInfo.UniqueProcessId;
|
|
|
|
/*
|
|
* Tell the csrss server we are creating a new process
|
|
*/
|
|
CsrRequest.Type = CSRSS_CREATE_PROCESS;
|
|
CsrRequest.Data.CreateProcessRequest.NewProcessId =
|
|
ProcessBasicInfo.UniqueProcessId;
|
|
if (Sii.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI)
|
|
{
|
|
/* Do not create a console for GUI applications */
|
|
dwCreationFlags &= ~CREATE_NEW_CONSOLE;
|
|
dwCreationFlags |= DETACHED_PROCESS;
|
|
}
|
|
CsrRequest.Data.CreateProcessRequest.Flags = dwCreationFlags;
|
|
Status = CsrClientCallServer(&CsrRequest,
|
|
&CsrReply,
|
|
sizeof(CSRSS_API_REQUEST),
|
|
sizeof(CSRSS_API_REPLY));
|
|
if (!NT_SUCCESS(Status) || !NT_SUCCESS(CsrReply.Status))
|
|
{
|
|
DbgPrint("Failed to tell csrss about new process. Expect trouble.\n");
|
|
}
|
|
|
|
// Set the child console handles
|
|
Ppb->hStdInput = NtCurrentPeb()->ProcessParameters->hStdInput;
|
|
Ppb->hStdOutput = NtCurrentPeb()->ProcessParameters->hStdOutput;
|
|
Ppb->hStdError = NtCurrentPeb()->ProcessParameters->hStdError;
|
|
|
|
if (lpStartupInfo && (lpStartupInfo->dwFlags & STARTF_USESTDHANDLES))
|
|
{
|
|
if (lpStartupInfo->hStdInput)
|
|
Ppb->hStdInput = lpStartupInfo->hStdInput;
|
|
if (lpStartupInfo->hStdOutput)
|
|
Ppb->hStdOutput = lpStartupInfo->hStdOutput;
|
|
if (lpStartupInfo->hStdError)
|
|
Ppb->hStdError = lpStartupInfo->hStdError;
|
|
}
|
|
|
|
if (IsConsoleHandle(Ppb->hStdInput))
|
|
{
|
|
Ppb->hStdInput = CsrReply.Data.CreateProcessReply.InputHandle;
|
|
}
|
|
else
|
|
{
|
|
DPRINT("Duplicate input handle\n");
|
|
Status = NtDuplicateObject (NtCurrentProcess(),
|
|
Ppb->hStdInput,
|
|
hProcess,
|
|
&Ppb->hStdInput,
|
|
0,
|
|
TRUE,
|
|
DUPLICATE_SAME_ACCESS);
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("NtDuplicateObject failed, status %x\n", Status);
|
|
}
|
|
}
|
|
|
|
if (IsConsoleHandle(Ppb->hStdOutput))
|
|
{
|
|
Ppb->hStdOutput = CsrReply.Data.CreateProcessReply.OutputHandle;
|
|
}
|
|
else
|
|
{
|
|
DPRINT("Duplicate output handle\n");
|
|
Status = NtDuplicateObject (NtCurrentProcess(),
|
|
Ppb->hStdOutput,
|
|
hProcess,
|
|
&Ppb->hStdOutput,
|
|
0,
|
|
TRUE,
|
|
DUPLICATE_SAME_ACCESS);
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("NtDuplicateObject failed, status %x\n", Status);
|
|
}
|
|
}
|
|
if (IsConsoleHandle(Ppb->hStdError))
|
|
{
|
|
CsrRequest.Type = CSRSS_DUPLICATE_HANDLE;
|
|
CsrRequest.Data.DuplicateHandleRequest.ProcessId = ProcessBasicInfo.UniqueProcessId;
|
|
CsrRequest.Data.DuplicateHandleRequest.Handle = CsrReply.Data.CreateProcessReply.OutputHandle;
|
|
Status = CsrClientCallServer(&CsrRequest,
|
|
&CsrReply,
|
|
sizeof(CSRSS_API_REQUEST),
|
|
sizeof(CSRSS_API_REPLY));
|
|
if (!NT_SUCCESS(Status) || !NT_SUCCESS(CsrReply.Status))
|
|
{
|
|
Ppb->hStdError = INVALID_HANDLE_VALUE;
|
|
}
|
|
else
|
|
{
|
|
Ppb->hStdError = CsrReply.Data.DuplicateHandleReply.Handle;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPRINT("Duplicate error handle\n");
|
|
Status = NtDuplicateObject (NtCurrentProcess(),
|
|
Ppb->hStdError,
|
|
hProcess,
|
|
&Ppb->hStdError,
|
|
0,
|
|
TRUE,
|
|
DUPLICATE_SAME_ACCESS);
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT("NtDuplicateObject failed, status %x\n", Status);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Initialize some other fields in the PPB
|
|
*/
|
|
if (lpStartupInfo)
|
|
{
|
|
Ppb->dwFlags = lpStartupInfo->dwFlags;
|
|
if (Ppb->dwFlags & STARTF_USESHOWWINDOW)
|
|
{
|
|
Ppb->wShowWindow = lpStartupInfo->wShowWindow;
|
|
}
|
|
else
|
|
{
|
|
Ppb->wShowWindow = SW_SHOWDEFAULT;
|
|
}
|
|
Ppb->dwX = lpStartupInfo->dwX;
|
|
Ppb->dwY = lpStartupInfo->dwY;
|
|
Ppb->dwXSize = lpStartupInfo->dwXSize;
|
|
Ppb->dwYSize = lpStartupInfo->dwYSize;
|
|
Ppb->dwFillAttribute = lpStartupInfo->dwFillAttribute;
|
|
}
|
|
else
|
|
{
|
|
Ppb->Flags = 0;
|
|
}
|
|
|
|
/*
|
|
* Create Process Environment Block
|
|
*/
|
|
DPRINT("Creating peb\n");
|
|
|
|
KlInitPeb(hProcess, Ppb, &ImageBaseAddress);
|
|
|
|
RtlDestroyProcessParameters (Ppb);
|
|
|
|
Status = NtSetInformationProcess(hProcess,
|
|
ProcessImageFileName,
|
|
ImageFileName,
|
|
8);
|
|
/*
|
|
* Create the thread for the kernel
|
|
*/
|
|
DPRINT("Creating thread for process (EntryPoint = 0x%.08x)\n",
|
|
ImageBaseAddress + (ULONG)Sii.EntryPoint);
|
|
hThread = KlCreateFirstThread(hProcess,
|
|
lpThreadAttributes,
|
|
&Sii,
|
|
ImageBaseAddress + (ULONG)Sii.EntryPoint,
|
|
dwCreationFlags,
|
|
&lpProcessInformation->dwThreadId);
|
|
if (hThread == INVALID_HANDLE_VALUE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
lpProcessInformation->hProcess = hProcess;
|
|
lpProcessInformation->hThread = hThread;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* EOF */
|