/*++ Copyright (c) 1989 Microsoft Corporation Module Name: rtlexec.c Abstract: User Mode routines for creating user mode processes and threads. Author: Steve Wood (stevewo) 18-Aug-1989 Environment: User Mode or Kernel Mode Revision History: --*/ #include "ntrtlp.h" #include #include #include "init.h" #include "ntos.h" #define ROUND_UP( x, y ) ((ULONG)(x) + ((y)-1) & ~((y)-1)) #ifdef KERNEL #define ISTERMINALSERVER() (SharedUserData->SuiteMask & (1 << TerminalServer)) #else #define ISTERMINALSERVER() (USER_SHARED_DATA->SuiteMask & (1 << TerminalServer)) #endif VOID RtlpCopyProcString( IN OUT PWSTR *pDst, OUT PUNICODE_STRING DestString, IN PUNICODE_STRING SourceString, IN ULONG DstAlloc OPTIONAL ); NTSTATUS RtlpOpenImageFile( IN PUNICODE_STRING ImagePathName, IN ULONG Attributes, OUT PHANDLE FileHandle, IN BOOLEAN ReportErrors ); NTSTATUS RtlpFreeStack( IN HANDLE Process, IN PINITIAL_TEB InitialTeb ); NTSTATUS RtlpCreateStack( IN HANDLE Process, IN SIZE_T MaximumStackSize OPTIONAL, IN SIZE_T CommittedStackSize OPTIONAL, IN ULONG ZeroBits OPTIONAL, OUT PINITIAL_TEB InitialTeb ); #if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME) #pragma alloc_text(INIT,RtlpCopyProcString ) #pragma alloc_text(INIT,RtlCreateProcessParameters ) #pragma alloc_text(INIT,RtlDestroyProcessParameters ) #pragma alloc_text(INIT,RtlNormalizeProcessParams ) #pragma alloc_text(INIT,RtlDeNormalizeProcessParams ) #pragma alloc_text(INIT,RtlpOpenImageFile ) #pragma alloc_text(PAGE,RtlpCreateStack ) #pragma alloc_text(PAGE,RtlpFreeStack ) #pragma alloc_text(INIT,RtlCreateUserProcess ) #pragma alloc_text(PAGE,RtlCreateUserThread ) #pragma alloc_text(INIT,RtlExitUserThread ) #endif #if defined(ALLOC_DATA_PRAGMA) && defined(NTOS_KERNEL_RUNTIME) #pragma const_seg("INITCONST") #endif const UNICODE_STRING NullString = {0, 1, L""}; VOID RtlpCopyProcString( IN OUT PWSTR *pDst, OUT PUNICODE_STRING DestString, IN PUNICODE_STRING SourceString, IN ULONG DstAlloc OPTIONAL ) { if (!ARGUMENT_PRESENT( DstAlloc )) { DstAlloc = SourceString->MaximumLength; } ASSERT((SourceString->Length == 0) || (SourceString->Buffer != NULL)); if (SourceString->Buffer != NULL && SourceString->Length != 0) { RtlCopyMemory (*pDst, SourceString->Buffer, SourceString->Length); } DestString->Buffer = *pDst; DestString->Length = SourceString->Length; DestString->MaximumLength = (USHORT)DstAlloc; if (DestString->Length < DestString->MaximumLength) { RtlZeroMemory (((PUCHAR)DestString->Buffer) + DestString->Length, DestString->MaximumLength - DestString->Length); } *pDst = (PWSTR)((PCHAR)(*pDst) + ROUND_UP( DstAlloc, sizeof( ULONG ) ) ); return; } 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 routinue. 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; } NTSTATUS RtlDestroyProcessParameters( IN PRTL_USER_PROCESS_PARAMETERS ProcessParameters ) { RtlFreeHeap (RtlProcessHeap (), 0, ProcessParameters); return STATUS_SUCCESS; } #define RtlpNormalizeProcessParam( Base, p ) \ if ((p) != NULL) { \ (p) = (PWSTR)((PCHAR)(p) + (ULONG_PTR)(Base)); \ } \ #define RtlpDeNormalizeProcessParam( Base, p ) \ if ((p) != NULL) { \ (p) = (PWSTR)((PCHAR)(p) - (ULONG_PTR)(Base)); \ } \ PRTL_USER_PROCESS_PARAMETERS RtlNormalizeProcessParams( IN OUT PRTL_USER_PROCESS_PARAMETERS ProcessParameters ) { if (!ARGUMENT_PRESENT( ProcessParameters )) { return( NULL ); } if (ProcessParameters->Flags & RTL_USER_PROC_PARAMS_NORMALIZED) { return( ProcessParameters ); } RtlpNormalizeProcessParam( ProcessParameters, ProcessParameters->CurrentDirectory.DosPath.Buffer ); RtlpNormalizeProcessParam( ProcessParameters, ProcessParameters->DllPath.Buffer ); RtlpNormalizeProcessParam( ProcessParameters, ProcessParameters->ImagePathName.Buffer ); RtlpNormalizeProcessParam( ProcessParameters, ProcessParameters->CommandLine.Buffer ); RtlpNormalizeProcessParam( ProcessParameters, ProcessParameters->WindowTitle.Buffer ); RtlpNormalizeProcessParam( ProcessParameters, ProcessParameters->DesktopInfo.Buffer ); RtlpNormalizeProcessParam( ProcessParameters, ProcessParameters->ShellInfo.Buffer ); RtlpNormalizeProcessParam( ProcessParameters, ProcessParameters->RuntimeData.Buffer ); ProcessParameters->Flags |= RTL_USER_PROC_PARAMS_NORMALIZED; return( ProcessParameters ); } PRTL_USER_PROCESS_PARAMETERS RtlDeNormalizeProcessParams( IN OUT PRTL_USER_PROCESS_PARAMETERS ProcessParameters ) { if (!ARGUMENT_PRESENT( ProcessParameters )) { return( NULL ); } if (!(ProcessParameters->Flags & RTL_USER_PROC_PARAMS_NORMALIZED)) { return( ProcessParameters ); } RtlpDeNormalizeProcessParam( ProcessParameters, ProcessParameters->CurrentDirectory.DosPath.Buffer ); RtlpDeNormalizeProcessParam( ProcessParameters, ProcessParameters->DllPath.Buffer ); RtlpDeNormalizeProcessParam( ProcessParameters, ProcessParameters->ImagePathName.Buffer ); RtlpDeNormalizeProcessParam( ProcessParameters, ProcessParameters->CommandLine.Buffer ); RtlpDeNormalizeProcessParam( ProcessParameters, ProcessParameters->WindowTitle.Buffer ); RtlpDeNormalizeProcessParam( ProcessParameters, ProcessParameters->DesktopInfo.Buffer ); RtlpDeNormalizeProcessParam( ProcessParameters, ProcessParameters->ShellInfo.Buffer ); RtlpDeNormalizeProcessParam( ProcessParameters, ProcessParameters->RuntimeData.Buffer ); ProcessParameters->Flags &= ~RTL_USER_PROC_PARAMS_NORMALIZED; return( ProcessParameters ); } NTSTATUS RtlpOpenImageFile( IN PUNICODE_STRING ImagePathName, IN ULONG Attributes, OUT PHANDLE FileHandle, IN BOOLEAN ReportErrors ) { NTSTATUS Status; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE File; IO_STATUS_BLOCK IoStatus; *FileHandle = NULL; InitializeObjectAttributes( &ObjectAttributes, ImagePathName, Attributes, NULL, NULL ); Status = ZwOpenFile( &File, SYNCHRONIZE | FILE_EXECUTE, &ObjectAttributes, &IoStatus, FILE_SHARE_READ | FILE_SHARE_DELETE, FILE_NON_DIRECTORY_FILE ); if (!NT_SUCCESS( Status )) { #if DBG if (ReportErrors) { DbgPrint( "NTRTL: RtlpOpenImageFile - NtCreateFile( %wZ ) failed. Status == %X\n", ImagePathName, Status ); } #endif // DBG return( Status ); } *FileHandle = File; return( STATUS_SUCCESS ); } NTSTATUS RtlpCreateStack( IN HANDLE Process, IN SIZE_T MaximumStackSize OPTIONAL, IN SIZE_T CommittedStackSize OPTIONAL, IN ULONG ZeroBits OPTIONAL, OUT PINITIAL_TEB InitialTeb ) { NTSTATUS Status; PCH Stack; SYSTEM_BASIC_INFORMATION SysInfo; BOOLEAN GuardPage; SIZE_T RegionSize; ULONG OldProtect; #if defined(_IA64_) PCH Bstore; SIZE_T CommittedBstoreSize; SIZE_T MaximumBstoreSize; SIZE_T MstackPlusBstoreSize; #endif Status = ZwQuerySystemInformation( SystemBasicInformation, (PVOID)&SysInfo, sizeof( SysInfo ), NULL ); if ( !NT_SUCCESS( Status ) ) { return( Status ); } // // if stack is in the current process, then default to // the parameters from the image // if ( Process == NtCurrentProcess() ) { PPEB Peb; PIMAGE_NT_HEADERS NtHeaders; Peb = NtCurrentPeb(); NtHeaders = RtlImageNtHeader(Peb->ImageBaseAddress); if (!NtHeaders) { return STATUS_INVALID_IMAGE_FORMAT; } if (!MaximumStackSize) { MaximumStackSize = NtHeaders->OptionalHeader.SizeOfStackReserve; } if (!CommittedStackSize) { CommittedStackSize = NtHeaders->OptionalHeader.SizeOfStackCommit; } } else { if (!CommittedStackSize) { CommittedStackSize = SysInfo.PageSize; } if (!MaximumStackSize) { MaximumStackSize = SysInfo.AllocationGranularity; } } // // Enforce a minimal stack commit if there is a PEB setting // for this. // #if !defined(NTOS_KERNEL_RUNTIME) { SIZE_T MinimumStackCommit; MinimumStackCommit = NtCurrentPeb()->MinimumStackCommit; if (MinimumStackCommit != 0 && CommittedStackSize < MinimumStackCommit) { CommittedStackSize = MinimumStackCommit; } } #endif if ( CommittedStackSize >= MaximumStackSize ) { MaximumStackSize = ROUND_UP(CommittedStackSize, (1024*1024)); } CommittedStackSize = ROUND_UP( CommittedStackSize, SysInfo.PageSize ); MaximumStackSize = ROUND_UP( MaximumStackSize, SysInfo.AllocationGranularity ); Stack = NULL; #if defined(_IA64_) // // Piggyback the backing store with the memory stack // CommittedBstoreSize = CommittedStackSize; MaximumBstoreSize = MaximumStackSize; MstackPlusBstoreSize = MaximumBstoreSize + MaximumStackSize; Status = ZwAllocateVirtualMemory( Process, (PVOID *)&Stack, ZeroBits, &MstackPlusBstoreSize, MEM_RESERVE, PAGE_READWRITE ); #else Status = ZwAllocateVirtualMemory( Process, (PVOID *)&Stack, ZeroBits, &MaximumStackSize, MEM_RESERVE, PAGE_READWRITE ); #endif // defined(_IA64_) if ( !NT_SUCCESS( Status ) ) { #if DBG DbgPrint( "NTRTL: RtlpCreateStack( %lx ) failed. Stack Reservation Status == %X\n", Process, Status ); #endif // DBG return( Status ); } #if defined(_IA64_) InitialTeb->OldInitialTeb.OldBStoreLimit = NULL; #endif // defined(_IA64_) InitialTeb->OldInitialTeb.OldStackBase = NULL; InitialTeb->OldInitialTeb.OldStackLimit = NULL; InitialTeb->StackAllocationBase = Stack; InitialTeb->StackBase = Stack + MaximumStackSize; Stack += MaximumStackSize - CommittedStackSize; if (MaximumStackSize > CommittedStackSize) { Stack -= SysInfo.PageSize; CommittedStackSize += SysInfo.PageSize; GuardPage = TRUE; } else { GuardPage = FALSE; } Status = ZwAllocateVirtualMemory( Process, (PVOID *)&Stack, 0, &CommittedStackSize, MEM_COMMIT, PAGE_READWRITE ); InitialTeb->StackLimit = Stack; if ( !NT_SUCCESS( Status ) ) { #if DBG DbgPrint( "NTRTL: RtlpCreateStack( %lx ) failed. Stack Commit Status == %X\n", Process, Status ); #endif // DBG return( Status ); } // // if we have space, create a guard page. // if (GuardPage) { RegionSize = SysInfo.PageSize; Status = ZwProtectVirtualMemory( Process, (PVOID *)&Stack, &RegionSize, PAGE_GUARD | PAGE_READWRITE, &OldProtect); if ( !NT_SUCCESS( Status ) ) { #if DBG DbgPrint( "NTRTL: RtlpCreateStack( %lx ) failed. Guard Page Creation Status == %X\n", Process, Status ); #endif // DBG return( Status ); } InitialTeb->StackLimit = (PVOID)((PUCHAR)InitialTeb->StackLimit + RegionSize); } #if defined(_IA64_) // // Commit backing store pages and create guard pages if there is space // Bstore = InitialTeb->StackBase; if (MaximumBstoreSize > CommittedBstoreSize) { CommittedBstoreSize += SysInfo.PageSize; GuardPage = TRUE; } else { GuardPage = FALSE; } Status = ZwAllocateVirtualMemory( Process, (PVOID *)&Bstore, 0, &CommittedBstoreSize, MEM_COMMIT, PAGE_READWRITE ); InitialTeb->BStoreLimit = Bstore + CommittedBstoreSize; if ( !NT_SUCCESS(Status) ) { #if DBG DbgPrint("NTRTL: RtlpCreateStack( %lx ) failed. Backing Store Commit Status == %X\n", Process, Status ); #endif // DBG return (Status); } if (GuardPage) { Bstore = (PCH)InitialTeb->BStoreLimit - SysInfo.PageSize; RegionSize = SysInfo.PageSize; Status = ZwProtectVirtualMemory(Process, (PVOID *)&Bstore, &RegionSize, PAGE_GUARD | PAGE_READWRITE, &OldProtect ); if ( !NT_SUCCESS(Status) ) { #if DBG DbgPrint("NTRTL: RtlpCreateStack( %lx ) failed. Backing Store Guard Page Creation Status == %X\n", Process, Status ); #endif // DBG return (Status); } InitialTeb->BStoreLimit = (PVOID)((PUCHAR)InitialTeb->BStoreLimit - RegionSize); } #endif // defined(_IA64_) return( STATUS_SUCCESS ); } NTSTATUS RtlpFreeStack( IN HANDLE Process, IN PINITIAL_TEB InitialTeb ) { NTSTATUS Status; SIZE_T Zero; Zero = 0; Status = ZwFreeVirtualMemory( Process, &InitialTeb->StackAllocationBase, &Zero, MEM_RELEASE ); if ( !NT_SUCCESS( Status ) ) { #if DBG DbgPrint( "NTRTL: RtlpFreeStack( %lx ) failed. Stack DeCommit Status == %X\n", Process, Status ); #endif // DBG return( Status ); } RtlZeroMemory( InitialTeb, sizeof( *InitialTeb ) ); return( STATUS_SUCCESS ); } NTSTATUS RtlCreateUserProcess( IN PUNICODE_STRING NtImagePathName, IN ULONG Attributes, IN PRTL_USER_PROCESS_PARAMETERS ProcessParameters, IN PSECURITY_DESCRIPTOR ProcessSecurityDescriptor OPTIONAL, IN PSECURITY_DESCRIPTOR ThreadSecurityDescriptor OPTIONAL, IN HANDLE ParentProcess OPTIONAL, IN BOOLEAN InheritHandles, IN HANDLE DebugPort OPTIONAL, IN HANDLE ExceptionPort OPTIONAL, OUT PRTL_USER_PROCESS_INFORMATION ProcessInformation ) /*++ Routine Description: This function creates a user mode process with a single thread with a suspend count of one. The address space of the new process is initialized with the contents of specified image file. The caller can specify the Access Control List for the new process and thread. The caller can also specify the parent process to inherit process priority and processor affinity from. The default is to inherit these from the current process. Finally the caller can specify whether the new process is to inherit any of the object handles from the specified parent process or not. Information about the new process and thread is returned via the ProcessInformation parameter. Arguments: NtImagePathName - A required pointer that points to the NT Path string that identifies the image file that is to be loaded into the child process. ProcessParameters - A required pointer that points to parameters that are to passed to the child process. ProcessSecurityDescriptor - An optional pointer to the Security Descriptor give to the new process. ThreadSecurityDescriptor - An optional pointer to the Security Descriptor give to the new thread. ParentProcess - An optional process handle that will used to inherit certain properties from. InheritHandles - A boolean value. TRUE specifies that object handles associated with the specified parent process are to be inherited by the new process, provided they have the OBJ_INHERIT attribute. FALSE specifies that the new process is to inherit no handles. DebugPort - An optional handle to the debug port associated with this process. ExceptionPort - An optional handle to the exception port associated with this process. ProcessInformation - A pointer to a variable that receives information about the new process and thread. Return Value: TBS. --*/ { NTSTATUS Status; HANDLE Section, File; OBJECT_ATTRIBUTES ObjectAttributes; PRTL_USER_PROCESS_PARAMETERS Parameters; SIZE_T ParameterLength; PVOID Environment; PWCHAR s; SIZE_T EnvironmentLength; SIZE_T RegionSize; PROCESS_BASIC_INFORMATION ProcessInfo; PPEB Peb; UNICODE_STRING Unicode; // // Zero output parameter and probe the addresses at the same time // RtlZeroMemory( ProcessInformation, sizeof( *ProcessInformation ) ); ProcessInformation->Length = sizeof( *ProcessInformation ); // // Open the specified image file. // Status = RtlpOpenImageFile( NtImagePathName, Attributes & (OBJ_INHERIT | OBJ_CASE_INSENSITIVE), &File, TRUE ); if (!NT_SUCCESS( Status )) { return( Status ); } // // Create a memory section backed by the opened image file // Status = ZwCreateSection( &Section, SECTION_ALL_ACCESS, NULL, NULL, PAGE_EXECUTE, SEC_IMAGE, File ); ZwClose( File ); if ( !NT_SUCCESS( Status ) ) { return( Status ); } // // Create the user mode process, defaulting the parent process to the // current process if one is not specified. The new process will not // have a name nor will the handle be inherited by other processes. // if (!ARGUMENT_PRESENT( ParentProcess )) { ParentProcess = NtCurrentProcess(); } InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL, ProcessSecurityDescriptor ); if ( RtlGetNtGlobalFlags() & FLG_ENABLE_CSRDEBUG ) { if ( wcsstr(NtImagePathName->Buffer,L"csrss") || wcsstr(NtImagePathName->Buffer,L"CSRSS") ) { // // For Hydra we don't name the CSRSS process to avoid name // collissions when multiple CSRSS's are started // if (ISTERMINALSERVER()) { InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL, ProcessSecurityDescriptor ); } else { RtlInitUnicodeString(&Unicode,L"\\WindowsSS"); InitializeObjectAttributes( &ObjectAttributes, &Unicode, 0, NULL, ProcessSecurityDescriptor ); } } } if ( !InheritHandles ) { ProcessParameters->CurrentDirectory.Handle = NULL; } Status = ZwCreateProcess( &ProcessInformation->Process, PROCESS_ALL_ACCESS, &ObjectAttributes, ParentProcess, InheritHandles, Section, DebugPort, ExceptionPort ); if ( !NT_SUCCESS( Status ) ) { ZwClose( Section ); return( Status ); } // // Retreive the interesting information from the image header // Status = ZwQuerySection( Section, SectionImageInformation, &ProcessInformation->ImageInformation, sizeof( ProcessInformation->ImageInformation ), NULL ); if ( !NT_SUCCESS( Status ) ) { ZwClose( ProcessInformation->Process ); ZwClose( Section ); return( Status ); } Status = ZwQueryInformationProcess( ProcessInformation->Process, ProcessBasicInformation, &ProcessInfo, sizeof( ProcessInfo ), NULL ); if ( !NT_SUCCESS( Status ) ) { ZwClose( ProcessInformation->Process ); ZwClose( Section ); return( Status ); } Peb = ProcessInfo.PebBaseAddress; // // Duplicate Native handles into new process if any specified. // Note that the duplicated handles will overlay the input values. // try { Status = STATUS_SUCCESS; if ( ProcessParameters->StandardInput ) { Status = ZwDuplicateObject( ParentProcess, ProcessParameters->StandardInput, ProcessInformation->Process, &ProcessParameters->StandardInput, 0L, 0L, DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES ); if ( !NT_SUCCESS(Status) ) { __leave; } } if ( ProcessParameters->StandardOutput ) { Status = ZwDuplicateObject( ParentProcess, ProcessParameters->StandardOutput, ProcessInformation->Process, &ProcessParameters->StandardOutput, 0L, 0L, DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES ); if ( !NT_SUCCESS(Status) ) { __leave; } } if ( ProcessParameters->StandardError ) { Status = ZwDuplicateObject( ParentProcess, ProcessParameters->StandardError, ProcessInformation->Process, &ProcessParameters->StandardError, 0L, 0L, DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES ); if ( !NT_SUCCESS(Status) ) { __leave; } } } finally { if ( !NT_SUCCESS(Status) ) { ZwClose( ProcessInformation->Process ); ZwClose( Section ); } } if ( !NT_SUCCESS(Status) ) { return Status; } // // Possibly reserve some address space in the new process // if (ProcessInformation->ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_NATIVE ) { if ( ProcessParameters->Flags & RTL_USER_PROC_RESERVE_1MB ) { #if defined(_IA64_) Environment = (PVOID)(UADDRESS_BASE+4); #else Environment = (PVOID)(4); #endif RegionSize = (1024*1024)-(256); Status = ZwAllocateVirtualMemory( ProcessInformation->Process, (PVOID *)&Environment, 0, &RegionSize, MEM_RESERVE, PAGE_READWRITE ); if ( !NT_SUCCESS( Status ) ) { ZwClose( ProcessInformation->Process ); ZwClose( Section ); return( Status ); } } } // // Allocate virtual memory in the new process and use NtWriteVirtualMemory // to write a copy of the process environment block into the address // space of the new process. Save the address of the allocated block in // the process parameter block so the new process can access it. // if (s = (PWCHAR)ProcessParameters->Environment) { while (*s++) { while (*s++) { } } EnvironmentLength = (SIZE_T)(s - (PWCHAR)ProcessParameters->Environment) * sizeof(WCHAR); Environment = NULL; RegionSize = EnvironmentLength; Status = ZwAllocateVirtualMemory( ProcessInformation->Process, (PVOID *)&Environment, 0, &RegionSize, MEM_COMMIT, PAGE_READWRITE ); if ( !NT_SUCCESS( Status ) ) { ZwClose( ProcessInformation->Process ); ZwClose( Section ); return( Status ); } Status = ZwWriteVirtualMemory( ProcessInformation->Process, Environment, ProcessParameters->Environment, EnvironmentLength, NULL ); if ( !NT_SUCCESS( Status ) ) { ZwClose( ProcessInformation->Process ); ZwClose( Section ); return( Status ); } ProcessParameters->Environment = Environment; } // // Allocate virtual memory in the new process and use NtWriteVirtualMemory // to write a copy of the process parameters block into the address // space of the new process. Set the initial parameter to the new thread // to be the address of the block in the new process's address space. // Parameters = NULL; ParameterLength = ProcessParameters->MaximumLength; Status = ZwAllocateVirtualMemory( ProcessInformation->Process, (PVOID *)&Parameters, 0, &ParameterLength, MEM_COMMIT, PAGE_READWRITE ); if ( !NT_SUCCESS( Status ) ) { ZwClose( ProcessInformation->Process ); ZwClose( Section ); return( Status ); } Status = ZwWriteVirtualMemory( ProcessInformation->Process, Parameters, ProcessParameters, ProcessParameters->Length, NULL ); if ( !NT_SUCCESS( Status ) ) { ZwClose( ProcessInformation->Process ); ZwClose( Section ); return( Status ); } Status = ZwWriteVirtualMemory( ProcessInformation->Process, &Peb->ProcessParameters, &Parameters, sizeof( Parameters ), NULL ); if ( !NT_SUCCESS( Status ) ) { ZwClose( ProcessInformation->Process ); ZwClose( Section ); return( Status ); } // // Create a suspended thread in the new process. Specify the size and // position of the stack, along with the start address, initial parameter // and an SECURITY_DESCRIPTOR. The new thread will not have a name and its handle will // not be inherited by other processes. // Status = RtlCreateUserThread( ProcessInformation->Process, ThreadSecurityDescriptor, TRUE, ProcessInformation->ImageInformation.ZeroBits, ProcessInformation->ImageInformation.MaximumStackSize, ProcessInformation->ImageInformation.CommittedStackSize, (PUSER_THREAD_START_ROUTINE) ProcessInformation->ImageInformation.TransferAddress, (PVOID)Peb, &ProcessInformation->Thread, &ProcessInformation->ClientId ); if ( !NT_SUCCESS( Status ) ) { ZwClose( ProcessInformation->Process ); ZwClose( Section ); return( Status ); } // // Now close the section and file handles. The objects they represent // will not actually go away until the process is destroyed. // ZwClose( Section ); // // Return success status // return( STATUS_SUCCESS ); } NTSTATUS RtlCreateUserThread( IN HANDLE Process, IN PSECURITY_DESCRIPTOR ThreadSecurityDescriptor OPTIONAL, IN BOOLEAN CreateSuspended, IN ULONG ZeroBits OPTIONAL, IN SIZE_T MaximumStackSize OPTIONAL, IN SIZE_T CommittedStackSize OPTIONAL, IN PUSER_THREAD_START_ROUTINE StartAddress, IN PVOID Parameter OPTIONAL, OUT PHANDLE Thread OPTIONAL, OUT PCLIENT_ID ClientId OPTIONAL ) /*++ Routine Description: This function creates a user mode thread in a user process. The caller specifies the attributes of the new thread. A handle to the thread, along with its Client Id are returned to the caller. Arguments: Process - Handle to the target process in which to create the new thread. ThreadSecurityDescriptor - An optional pointer to the Security Descriptor give to the new thread. CreateSuspended - A boolean parameter that specifies whether or not the new thread is to be created suspended or not. If TRUE, the new thread will be created with an initial suspend count of 1. If FALSE then the new thread will be ready to run when this call returns. ZeroBits - This parameter is passed to the virtual memory manager when the stack is allocated. Stacks are always allocated with the MEM_TOP_DOWN allocation attribute. MaximumStackSize - This is the maximum size of the stack. This size will be rounded up to the next highest page boundary. If zero is specified, then the default size will be 64K bytes. CommittedStackSize - This is the initial committed size of the stack. This size is rounded up to the next highest page boundary and then an additional page is added for the guard page. The resulting size will then be commited and the guard page protection initialized for the last committed page in the stack. StartAddress - The initial starting address of the thread. Parameter - An optional pointer to a 32-bit pointer parameter that is passed as a single argument to the procedure at the start address location. Thread - An optional pointer that, if specified, points to a variable that will receive the handle of the new thread. ClientId - An optional pointer that, if specified, points to a variable that will receive the Client Id of the new thread. Return Value: TBS --*/ { NTSTATUS Status; CONTEXT ThreadContext={0}; OBJECT_ATTRIBUTES ObjectAttributes; INITIAL_TEB InitialTeb; HANDLE ThreadHandle; CLIENT_ID ThreadClientId; // // Allocate a stack for this thread in the address space of the target // process. // Status = RtlpCreateStack( Process, MaximumStackSize, CommittedStackSize, ZeroBits, &InitialTeb ); if ( !NT_SUCCESS( Status ) ) { return( Status ); } // // Create an initial context for the new thread. // try { RtlInitializeContext( Process, &ThreadContext, Parameter, (PVOID)StartAddress, InitialTeb.StackBase ); } except (EXCEPTION_EXECUTE_HANDLER) { RtlpFreeStack( Process, &InitialTeb ); return GetExceptionCode (); } // // Now create a thread in the target process. The new thread will // not have a name and its handle will not be inherited by other // processes. // InitializeObjectAttributes( &ObjectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, ThreadSecurityDescriptor ); Status = ZwCreateThread( &ThreadHandle, THREAD_ALL_ACCESS, &ObjectAttributes, Process, &ThreadClientId, &ThreadContext, &InitialTeb, CreateSuspended ); if (!NT_SUCCESS( Status )) { #if DBG DbgPrint( "NTRTL: RtlCreateUserThread Failed. NtCreateThread Status == %X\n", Status ); #endif // DBG RtlpFreeStack( Process, &InitialTeb ); } else { if (ARGUMENT_PRESENT( Thread )) { *Thread = ThreadHandle; } else { ZwClose (ThreadHandle); } if (ARGUMENT_PRESENT( ClientId )) { *ClientId = ThreadClientId; } } // // Return status // return( Status ); } DECLSPEC_NORETURN NTSYSAPI VOID NTAPI RtlExitUserThread ( IN NTSTATUS ExitStatus ) /*++ Routine Description: This function exits a thread created by RtlCreateUserThread in such a way as the stack is deallocated as well as the thread terminated. Arguments: ExitStatus - Final exit status Return Value: None --*/ { NtCurrentTeb ()->FreeStackOnTermination = TRUE; NtTerminateThread (NtCurrentThread (), ExitStatus); } VOID RtlFreeUserThreadStack( HANDLE hProcess, HANDLE hThread ) { NTSTATUS Status; PTEB Teb; THREAD_BASIC_INFORMATION ThreadInfo; PVOID StackDeallocationBase; SIZE_T Size; Status = NtQueryInformationThread (hThread, ThreadBasicInformation, &ThreadInfo, sizeof (ThreadInfo), NULL); Teb = ThreadInfo.TebBaseAddress; if (!NT_SUCCESS (Status) || !Teb) { return; } Status = NtReadVirtualMemory (hProcess, &Teb->DeallocationStack, &StackDeallocationBase, sizeof (StackDeallocationBase), NULL); if (!NT_SUCCESS (Status) || !StackDeallocationBase) { return; } Size = 0; NtFreeVirtualMemory (hProcess, &StackDeallocationBase, &Size, MEM_RELEASE); return; } #if defined(ALLOC_DATA_PRAGMA) && defined(NTOS_KERNEL_RUNTIME) #pragma const_seg() #endif