/*++ Copyright (c) 1990 Microsoft Corporation Module Name: support.c Abstract: This module implements various conversion routines that transform Win32 parameters into NT parameters. Author: Mark Lucovsky (markl) 20-Sep-1990 Revision History: --*/ #include "basedll.h" #if defined(BUILD_WOW6432) #include "wow64reg.h" #include #endif PCLDR_DATA_TABLE_ENTRY BasepExeLdrEntry = NULL; // N.B. These are the registry values we check for SafeDllSearchMode, // and MUST match the entries in BasepDllSearchPaths typedef enum { BasepCurrentDirUninitialized = -1, BasepCurrentDirAtStart = 0, BasepCurrentDirAfterSystem32 = 1, MaxBasepCurrentDir } BASEP_CURDIR_PLACEMENT; #define BASEP_DEFAULT_DLL_CURDIR_PLACEMENT (BasepCurrentDirAfterSystem32) #define BASEP_VALID_CURDIR_PLACEMENT_P(c) (BasepCurrentDirUninitialized < (c) \ && (c) < MaxBasepCurrentDir) LONG BasepDllCurrentDirPlacement = BasepCurrentDirUninitialized; typedef enum { BasepSearchPathEnd, // end of path BasepSearchPathDlldir, // use the dll dir; fallback to nothing BasepSearchPathAppdir, // use the exe dir; fallback to base exe dir BasepSearchPathDefaultDirs, // use the default system dirs BasepSearchPathEnvPath, // use %PATH% BasepSearchPathCurdir, // use "." MaxBasepSearchPath } BASEP_SEARCH_PATH_ELEMENT; // N.B. The ordering of these must match the definitions for // BASEP_CURDIR_PLACEMENT. static const BASEP_SEARCH_PATH_ELEMENT BasepDllSearchPaths[MaxBasepCurrentDir][7] = { { // BasepCurrentDirAtStart BasepSearchPathAppdir, BasepSearchPathCurdir, BasepSearchPathDefaultDirs, BasepSearchPathEnvPath, BasepSearchPathEnd }, { // BasepCurrentDirAfterSystem32 BasepSearchPathAppdir, BasepSearchPathDefaultDirs, BasepSearchPathCurdir, BasepSearchPathEnvPath, BasepSearchPathEnd } }; POBJECT_ATTRIBUTES BaseFormatObjectAttributes( OUT POBJECT_ATTRIBUTES ObjectAttributes, IN PSECURITY_ATTRIBUTES SecurityAttributes, IN PUNICODE_STRING ObjectName ) /*++ Routine Description: This function transforms a Win32 security attributes structure into an NT object attributes structure. It returns the address of the resulting structure (or NULL if SecurityAttributes was not specified). Arguments: ObjectAttributes - Returns an initialized NT object attributes structure that contains a superset of the information provided by the security attributes structure. SecurityAttributes - Supplies the address of a security attributes structure that needs to be transformed into an NT object attributes structure. ObjectName - Supplies a name for the object relative to the BaseNamedObjectDirectory object directory. Return Value: NULL - A value of null should be used to mimic the behavior of the specified SecurityAttributes structure. NON-NULL - Returns the ObjectAttributes value. The structure is properly initialized by this function. --*/ { HANDLE RootDirectory; ULONG Attributes; PVOID SecurityDescriptor; if ( ARGUMENT_PRESENT(SecurityAttributes) || ARGUMENT_PRESENT(ObjectName) ) { if ( SecurityAttributes ) { Attributes = (SecurityAttributes->bInheritHandle ? OBJ_INHERIT : 0); SecurityDescriptor = SecurityAttributes->lpSecurityDescriptor; } else { Attributes = 0; SecurityDescriptor = NULL; } if ( ARGUMENT_PRESENT(ObjectName) ) { Attributes |= OBJ_OPENIF; RootDirectory = BaseGetNamedObjectDirectory(); } else { RootDirectory = NULL; } InitializeObjectAttributes( ObjectAttributes, ObjectName, Attributes, RootDirectory, SecurityDescriptor ); return ObjectAttributes; } else { return NULL; } } PLARGE_INTEGER BaseFormatTimeOut( OUT PLARGE_INTEGER TimeOut, IN DWORD Milliseconds ) /*++ Routine Description: This function translates a Win32 style timeout to an NT relative timeout value. Arguments: TimeOut - Returns an initialized NT timeout value that is equivalent to the Milliseconds parameter. Milliseconds - Supplies the timeout value in milliseconds. A value of -1 indicates indefinite timeout. Return Value: NULL - A value of null should be used to mimic the behavior of the specified Milliseconds parameter. NON-NULL - Returns the TimeOut value. The structure is properly initialized by this function. --*/ { if ( (LONG) Milliseconds == -1 ) { return( NULL ); } TimeOut->QuadPart = UInt32x32To64( Milliseconds, 10000 ); TimeOut->QuadPart *= -1; return TimeOut; } NTSTATUS BaseCreateStack( IN HANDLE Process, IN SIZE_T StackSize, IN SIZE_T MaximumStackSize, OUT PINITIAL_TEB InitialTeb ) /*++ Routine Description: This function creates a stack for the specified process. Arguments: Process - Supplies a handle to the process that the stack will be allocated within. StackSize - An optional parameter, that if specified, supplies the initial commit size for the stack. MaximumStackSize - Supplies the maximum size for the new threads stack. If this parameter is not specified, then the reserve size of the current images stack descriptor is used. InitialTeb - Returns a populated InitialTeb that contains the stack size and limits. Return Value: TRUE - A stack was successfully created. FALSE - The stack counld not be created. --*/ { NTSTATUS Status; PCH Stack; BOOLEAN GuardPage; SIZE_T RegionSize; ULONG OldProtect; SIZE_T ImageStackSize, ImageStackCommit; PIMAGE_NT_HEADERS NtHeaders; PPEB Peb; ULONG PageSize; Peb = NtCurrentPeb(); BaseStaticServerData = BASE_SHARED_SERVER_DATA; PageSize = BASE_SYSINFO.PageSize; // // If the stack size was not supplied, then use the sizes from the // image header. // NtHeaders = RtlImageNtHeader(Peb->ImageBaseAddress); if (!NtHeaders) { return STATUS_INVALID_IMAGE_FORMAT; } ImageStackSize = NtHeaders->OptionalHeader.SizeOfStackReserve; ImageStackCommit = NtHeaders->OptionalHeader.SizeOfStackCommit; if ( !MaximumStackSize ) { MaximumStackSize = ImageStackSize; } if (!StackSize) { StackSize = ImageStackCommit; } else { // // Now Compute how much additional stack space is to be // reserved. This is done by... If the StackSize is <= // Reserved size in the image, then reserve whatever the image // specifies. Otherwise, round up to 1Mb. // if ( StackSize >= MaximumStackSize ) { MaximumStackSize = ROUND_UP(StackSize, (1024*1024)); } } // // Align the stack size to a page boundry and the reserved size // to an allocation granularity boundry. // StackSize = ROUND_UP( StackSize, PageSize ); MaximumStackSize = ROUND_UP( MaximumStackSize, BASE_SYSINFO.AllocationGranularity ); // // Enforce a minimal stack commit if there is a PEB setting // for this. // { SIZE_T MinimumStackCommit; MinimumStackCommit = NtCurrentPeb()->MinimumStackCommit; if (MinimumStackCommit != 0 && StackSize < MinimumStackCommit) { StackSize = MinimumStackCommit; } // // Recheck and realign reserve size // if ( StackSize >= MaximumStackSize ) { MaximumStackSize = ROUND_UP (StackSize, (1024*1024)); } StackSize = ROUND_UP (StackSize, PageSize); MaximumStackSize = ROUND_UP (MaximumStackSize, BASE_SYSINFO.AllocationGranularity); } #if !defined (_IA64_) // // Reserve address space for the stack // Stack = NULL; Status = NtAllocateVirtualMemory( Process, (PVOID *)&Stack, 0, &MaximumStackSize, MEM_RESERVE, PAGE_READWRITE ); #else // // Take RseStack into consideration. // RSE stack has same size as memory stack, has same StackBase, // has a guard page at the end, and grows upwards towards higher // memory addresses // // // Reserve address space for the two stacks // { SIZE_T TotalStackSize = MaximumStackSize * 2; Stack = NULL; Status = NtAllocateVirtualMemory( Process, (PVOID *)&Stack, 0, &TotalStackSize, MEM_RESERVE, PAGE_READWRITE ); } #endif // IA64 if ( !NT_SUCCESS( Status ) ) { return Status; } InitialTeb->OldInitialTeb.OldStackBase = NULL; InitialTeb->OldInitialTeb.OldStackLimit = NULL; InitialTeb->StackAllocationBase = Stack; InitialTeb->StackBase = Stack + MaximumStackSize; #if defined (_IA64_) InitialTeb->OldInitialTeb.OldBStoreLimit = NULL; #endif //IA64 Stack += MaximumStackSize - StackSize; if (MaximumStackSize > StackSize) { Stack -= PageSize; StackSize += PageSize; GuardPage = TRUE; } else { GuardPage = FALSE; } // // Commit the initially valid portion of the stack // #if !defined(_IA64_) Status = NtAllocateVirtualMemory( Process, (PVOID *)&Stack, 0, &StackSize, MEM_COMMIT, PAGE_READWRITE ); #else { // // memory and rse stacks are expected to be contiguous // reserver virtual memory for both stack at once // SIZE_T NewCommittedStackSize = StackSize * 2; Status = NtAllocateVirtualMemory( Process, (PVOID *)&Stack, 0, &NewCommittedStackSize, MEM_COMMIT, PAGE_READWRITE ); } #endif //IA64 if ( !NT_SUCCESS( Status ) ) { // // If the commit fails, then delete the address space for the stack // RegionSize = 0; NtFreeVirtualMemory( Process, (PVOID *)&Stack, &RegionSize, MEM_RELEASE ); return Status; } InitialTeb->StackLimit = Stack; #if defined(_IA64_) InitialTeb->BStoreLimit = Stack + 2 * StackSize; #endif // // if we have space, create a guard page. // if (GuardPage) { RegionSize = PageSize; Status = NtProtectVirtualMemory( Process, (PVOID *)&Stack, &RegionSize, PAGE_GUARD | PAGE_READWRITE, &OldProtect ); if ( !NT_SUCCESS( Status ) ) { return Status; } InitialTeb->StackLimit = (PVOID)((PUCHAR)InitialTeb->StackLimit + RegionSize); #if defined(_IA64_) // // additional code to Create RSE stack guard page // Stack = ((PCH)InitialTeb->StackBase) + StackSize - PageSize; RegionSize = PageSize; Status = NtProtectVirtualMemory( Process, (PVOID *)&Stack, &RegionSize, PAGE_GUARD | PAGE_READWRITE, &OldProtect ); if ( !NT_SUCCESS( Status ) ) { return Status; } InitialTeb->BStoreLimit = (PVOID)Stack; #endif // IA64 } return STATUS_SUCCESS; } VOID BaseThreadStart( IN LPTHREAD_START_ROUTINE lpStartAddress, IN LPVOID lpParameter ) /*++ Routine Description: This function is called to start a Win32 thread. Its purpose is to call the thread, and if the thread returns, to terminate the thread and delete its stack. Arguments: lpStartAddress - Supplies the starting address of the new thread. The address is logically a procedure that never returns and that accepts a single 32-bit pointer argument. lpParameter - Supplies a single parameter value passed to the thread. Return Value: None. --*/ { try { // // test for fiber start or new thread // // // WARNING WARNING DO NOT CHANGE INIT OF NtTib.Version. There is // external code depending on this initialization ! // if ( NtCurrentTeb()->NtTib.Version == OS2_VERSION ) { if ( !BaseRunningInServerProcess ) { CsrNewThread(); } } ExitThread((lpStartAddress)(lpParameter)); } except(UnhandledExceptionFilter( GetExceptionInformation() )) { if ( !BaseRunningInServerProcess ) { ExitProcess(GetExceptionCode()); } else { ExitThread(GetExceptionCode()); } } } VOID BaseProcessStart( PPROCESS_START_ROUTINE lpStartAddress ) /*++ Routine Description: This function is called to start a Win32 process. Its purpose is to call the initial thread of the process, and if the thread returns, to terminate the thread and delete its stack. Arguments: lpStartAddress - Supplies the starting address of the new thread. The address is logically a procedure that never returns. Return Value: None. --*/ { try { #if defined(BUILD_WOW6432) void Report32bitAppLaunching ( ); Report32bitAppLaunching (); #endif NtSetInformationThread( NtCurrentThread(), ThreadQuerySetWin32StartAddress, &lpStartAddress, sizeof( lpStartAddress ) ); ExitThread((lpStartAddress)()); } except(UnhandledExceptionFilter( GetExceptionInformation() )) { if ( !BaseRunningInServerProcess ) { ExitProcess(GetExceptionCode()); } else { ExitThread(GetExceptionCode()); } } } VOID BaseFreeStackAndTerminate( IN PVOID OldStack, IN DWORD ExitCode ) /*++ Routine Description: This API is called during thread termination to delete a thread's stack and then terminate. Arguments: OldStack - Supplies the address of the stack to free. ExitCode - Supplies the termination status that the thread is to exit with. Return Value: None. --*/ { NTSTATUS Status; SIZE_T Zero; PVOID BaseAddress; #if defined (WX86) PWX86TIB Wx86Tib; PTEB Teb; #endif Zero = 0; BaseAddress = OldStack; Status = NtFreeVirtualMemory( NtCurrentProcess(), &BaseAddress, &Zero, MEM_RELEASE ); ASSERT(NT_SUCCESS(Status)); #if defined (WX86) Teb = NtCurrentTeb(); if (Teb && (Wx86Tib = Wx86CurrentTib())) { BaseAddress = Wx86Tib->DeallocationStack; Zero = 0; Status = NtFreeVirtualMemory( NtCurrentProcess(), &BaseAddress, &Zero, MEM_RELEASE ); ASSERT(NT_SUCCESS(Status)); if (Teb->Wx86Thread.DeallocationCpu) { BaseAddress = Teb->Wx86Thread.DeallocationCpu; Zero = 0; Status = NtFreeVirtualMemory( NtCurrentProcess(), &BaseAddress, &Zero, MEM_RELEASE ); ASSERT(NT_SUCCESS(Status)); } } #endif // // Don't worry, no commenting precedent has been set by SteveWo. this // comment was added by an innocent bystander. // // NtTerminateThread will return if this thread is the last one in // the process. So ExitProcess will only be called if that is the // case. // NtTerminateThread(NULL,(NTSTATUS)ExitCode); ExitProcess(ExitCode); } #if defined(WX86) || defined(_AXP64_) NTSTATUS BaseCreateWx86Tib( HANDLE Process, HANDLE Thread, ULONG InitialPc, ULONG CommittedStackSize, ULONG MaximumStackSize, BOOLEAN EmulateInitialPc ) /*++ Routine Description: This API is called to create a Wx86Tib for Wx86 emulated threads Arguments: Process - Target Process Thread - Target Thread Parameter - Supplies the thread's parameter. InitialPc - Supplies an initial program counter value. StackSize - BaseCreateStack parameters MaximumStackSize - BaseCreateStack parameters BOOLEAN Return Value: NtStatus from mem allocations --*/ { NTSTATUS Status; PTEB Teb; ULONG Size, SizeWx86Tib; PVOID TargetWx86Tib; PIMAGE_NT_HEADERS NtHeaders; WX86TIB Wx86Tib; INITIAL_TEB InitialTeb; THREAD_BASIC_INFORMATION ThreadInfo; Status = NtQueryInformationThread( Thread, ThreadBasicInformation, &ThreadInfo, sizeof( ThreadInfo ), NULL ); if (!NT_SUCCESS(Status)) { return Status; } Teb = ThreadInfo.TebBaseAddress; // // if stack size not supplied, get from current image // NtHeaders = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress); if (!NtHeaders) { return STATUS_INVALID_IMAGE_FORMAT; } if (!MaximumStackSize) { MaximumStackSize = (ULONG)NtHeaders->OptionalHeader.SizeOfStackReserve; } if (!CommittedStackSize) { CommittedStackSize = (ULONG)NtHeaders->OptionalHeader.SizeOfStackCommit; } // // Increase stack size for Wx86Tib, which sits at the top of the stack. // // // x86 Borland C++ 4.1 (and perhaps other versions) Rudely assumes that // it can use the top of the stack. Even tho this is completly bogus, // leave some space on the top of the stack, to avoid problems. // SizeWx86Tib = sizeof(WX86TIB) + 16; SizeWx86Tib = ROUND_UP(SizeWx86Tib, sizeof(ULONG)); Size = (ULONG)ROUND_UP_TO_PAGES(SizeWx86Tib + 4096); if (CommittedStackSize < 1024 * 1024) { // 1 MB CommittedStackSize += Size; } if (MaximumStackSize < 1024 * 1024 * 16) { // 10 MB MaximumStackSize += Size; } if (MaximumStackSize < 256 * 1024) { // Enforce a minimum stack size of 256k since the CPU emulator // grabs several pages of the x86 stack for itself MaximumStackSize = 256 * 1024; } Status = BaseCreateStack( Process, CommittedStackSize, MaximumStackSize, &InitialTeb ); if (!NT_SUCCESS(Status)) { return Status; } // // Fill in the Teb->Vdm with pWx86Tib // TargetWx86Tib = (PVOID)((ULONG_PTR)InitialTeb.StackBase - SizeWx86Tib); Status = NtWriteVirtualMemory(Process, &Teb->Vdm, &TargetWx86Tib, sizeof(TargetWx86Tib), NULL ); if (NT_SUCCESS(Status)) { // // Write the initial Wx86Tib information // RtlZeroMemory(&Wx86Tib, sizeof(WX86TIB)); Wx86Tib.Size = sizeof(WX86TIB); Wx86Tib.InitialPc = InitialPc; Wx86Tib.InitialSp = (ULONG)((ULONG_PTR)TargetWx86Tib); Wx86Tib.StackBase = (VOID * POINTER_32) InitialTeb.StackBase; Wx86Tib.StackLimit = (VOID * POINTER_32) InitialTeb.StackLimit; Wx86Tib.DeallocationStack = (VOID * POINTER_32) InitialTeb.StackAllocationBase; Wx86Tib.EmulateInitialPc = EmulateInitialPc; Status = NtWriteVirtualMemory(Process, TargetWx86Tib, &Wx86Tib, sizeof(WX86TIB), NULL ); } if (!NT_SUCCESS(Status)) { BaseFreeThreadStack(Process, NULL, &InitialTeb); } return Status; } #endif VOID BaseFreeThreadStack( HANDLE hProcess, HANDLE hThread, PINITIAL_TEB InitialTeb ) /*++ Routine Description: Deletes a thread's stack Arguments: Process - Target process Thread - Target thread OPTIONAL InitialTeb - stack paremeters Return Value: VOID --*/ { NTSTATUS Status; DWORD dwStackSize; SIZE_T stStackSize; PVOID BaseAddress; stStackSize = 0; dwStackSize = 0; BaseAddress = InitialTeb->StackAllocationBase; NtFreeVirtualMemory( hProcess, &BaseAddress, &stStackSize, MEM_RELEASE ); #if defined (WX86) if (hThread) { PTEB Teb; PWX86TIB pWx86Tib; WX86TIB Wx86Tib; THREAD_BASIC_INFORMATION ThreadInfo; Status = NtQueryInformationThread( hThread, ThreadBasicInformation, &ThreadInfo, sizeof( ThreadInfo ), NULL ); Teb = ThreadInfo.TebBaseAddress; if (!NT_SUCCESS(Status) || !Teb) { return; } Status = NtReadVirtualMemory( hProcess, &Teb->Vdm, &pWx86Tib, sizeof(pWx86Tib), NULL ); if (!NT_SUCCESS(Status) || !pWx86Tib) { return; } Status = NtReadVirtualMemory( hProcess, pWx86Tib, &Wx86Tib, sizeof(Wx86Tib), NULL ); if (NT_SUCCESS(Status) && Wx86Tib.Size == sizeof(WX86TIB)) { // release the wx86tib stack dwStackSize = 0; stStackSize = 0; BaseAddress = Wx86Tib.DeallocationStack; NtFreeVirtualMemory(hProcess, &BaseAddress, &stStackSize, MEM_RELEASE ); // set Teb->Vdm = NULL; dwStackSize = 0; Status = NtWriteVirtualMemory( hProcess, &Teb->Vdm, &dwStackSize, sizeof(pWx86Tib), NULL ); } } #endif } #if defined(BUILD_WOW6432) typedef HANDLE (WINAPI* __imp_RegisterEventSourceWType) (HANDLE, PWCHAR ); typedef HANDLE (WINAPI* __imp_DeregisterEventSourceType) (HANDLE); typedef HANDLE (WINAPI* __imp_ReportEventType)( HANDLE hEventLog, // handle to event log WORD wType, // event type WORD wCategory, // event category DWORD dwEventID, // event identifier PVOID lpUserSid, // user security identifier WORD wNumStrings, // number of strings to merge DWORD dwDataSize, // size of binary data PWCHAR *lpStrings, // array of strings to merge LPVOID lpRawData // binary data buffer ); void Wow64LogMessageInEventLogger( PWCHAR *szMsg ) /*++ Routine Description: This function logs an event into the application log. Arguments: szMsg - event-log message pointer. Return Value: None. --*/ { HMODULE hMod; HANDLE h; __imp_RegisterEventSourceWType __imp_RegisterEventSourceW; __imp_DeregisterEventSourceType __imp_DeregisterEventSource; __imp_ReportEventType __imp_ReportEvent; hMod =LoadLibraryW (L"advapi32.dll"); if (hMod != NULL) { __imp_RegisterEventSourceW = (__imp_RegisterEventSourceWType)GetProcAddress (hMod, "RegisterEventSourceW"); __imp_DeregisterEventSource = (__imp_DeregisterEventSourceType)GetProcAddress (hMod, "DeregisterEventSource"); __imp_ReportEvent = (__imp_ReportEventType)GetProcAddress (hMod, "ReportEventW"); if ((__imp_RegisterEventSourceW != NULL) && (__imp_DeregisterEventSource != NULL) && (__imp_ReportEvent != NULL)) { h = __imp_RegisterEventSourceW (NULL, L"Wow64 Emulation Layer"); if (h != NULL) { if (!__imp_ReportEvent ( h, // event log handle EVENTLOG_INFORMATION_TYPE, // EVENTLOG_WARNING_TYPE 0, // category zero EVENT_WOW64_RUNNING32BIT_APPLICATION , // event identifier NULL, // no user security identifier 1, // one substitution string 0, // no data szMsg, // pointer to string array NULL)) { // pointer to data DbgPrint ("Wow64-EventLog: Couldn't report event - %lx\n", GetLastError ()); } __imp_DeregisterEventSource (h); } } FreeLibrary (hMod); } } void Report32bitAppLaunching () /*++ Routine Description: This routine is called whenever a 32-bit is launched on win64 (inside Wow64). It will check to see if application loggin is enabled, and if so, will log an event to the application log. Arguments: None. Return Value: None. --*/ { const static UNICODE_STRING KeyName = RTL_CONSTANT_STRING (L"\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft\\wow64\\config"); static UNICODE_STRING ValueName = RTL_CONSTANT_STRING (L"LogAppLaunchingEvent"); static OBJECT_ATTRIBUTES ObjA = RTL_CONSTANT_OBJECT_ATTRIBUTES (&KeyName, OBJ_CASE_INSENSITIVE); WCHAR Buffer [MAX_PATH]; const PWCHAR Msg [2] = {Buffer, NULL}; PWCHAR pAppName; HKEY hKey; PUNICODE_STRING ImageName; PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation; NTSTATUS Status; PPEB Peb; PWSTR PtrToCopy; DWORD CopyLength; Status = NtOpenKey (&hKey, KEY_READ | KEY_WOW64_64KEY, &ObjA); if (NT_SUCCESS(Status)) { KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)Buffer; Status = NtQueryValueKey( hKey, &ValueName, KeyValuePartialInformation, KeyValueInformation, sizeof (Buffer), &CopyLength ); NtClose (hKey); if ((NT_SUCCESS (Status)) && (KeyValueInformation->Type == REG_DWORD) && (KeyValueInformation->DataLength == sizeof (DWORD))) { if (*(LONG *)KeyValueInformation->Data == 0x1) { Peb = NtCurrentPeb (); PtrToCopy = NULL; if (Peb->ProcessParameters != NULL) { ImageName = &Peb->ProcessParameters->ImagePathName; ASSERT (ImageName->Buffer != NULL); if (ImageName->Length > (sizeof (Buffer) - sizeof (UNICODE_NULL))) { CopyLength = (sizeof (Buffer) - sizeof (UNICODE_NULL)); PtrToCopy = (PWSTR)((PSTR)ImageName->Buffer + (ImageName->Length - sizeof (Buffer)) + sizeof (UNICODE_NULL)); } else { CopyLength = ImageName->Length; PtrToCopy = ImageName->Buffer; } if ( CopyLength){ RtlCopyMemory (Buffer, PtrToCopy, CopyLength); Buffer [(CopyLength >> 1)] = UNICODE_NULL; } else swprintf (Buffer, L"PID=%d",NtCurrentTeb()->ClientId.UniqueProcess); } Wow64LogMessageInEventLogger ((PWCHAR *)Msg); } } } } typedef struct _ENVIRONMENT_THUNK_TABLE { WCHAR *Native; WCHAR *X86; WCHAR *FakeName; } ENVIRONMENT_THUNK_TABLE, *PENVIRONMENT_THUNK_TABLE; ENVIRONMENT_THUNK_TABLE ProgramFilesEnvironment[] = { { L"ProgramFiles", L"ProgramFiles(x86)", L"ProgramW6432" }, { L"CommonProgramFiles", L"CommonProgramFiles(x86)", L"CommonProgramW6432" }, { L"PROCESSOR_ARCHITECTURE", L"PROCESSOR_ARCHITECTURE", L"PROCESSOR_ARCHITEW6432" } }; NTSTATUS Wow64pThunkEnvironmentVariables ( IN OUT PVOID *Environment ) /*++ Routine Description: This routine is called when we are about to create a 64-bit process for a 32-bit process. It thunks back the ProgramFiles environment variables so that they point to the native directory. This routine must stay in sync with what's in \base\wow64\wow64\init.c. Arguments: Environment - Address of pointer of environment variable to thunk. Return Value: NTSTATUS. --*/ { UNICODE_STRING Name, Value; WCHAR Buffer [ MAX_PATH ]; NTSTATUS NtStatus; ULONG i=0; while (i < (sizeof(ProgramFilesEnvironment) / sizeof(ProgramFilesEnvironment[0]))) { RtlInitUnicodeString (&Name, ProgramFilesEnvironment[i].FakeName); Value.Length = 0; Value.MaximumLength = sizeof (Buffer); Value.Buffer = Buffer; NtStatus = RtlQueryEnvironmentVariable_U (*Environment, &Name, &Value ); if (NT_SUCCESS (NtStatus)) { RtlSetEnvironmentVariable (Environment, &Name, NULL ); RtlInitUnicodeString (&Name, ProgramFilesEnvironment[i].Native); NtStatus = RtlSetEnvironmentVariable (Environment, &Name, &Value ); } if (!NT_SUCCESS (NtStatus)) { break; } i++; } return NtStatus; } #endif BOOL BasePushProcessParameters( DWORD dwFlags, HANDLE Process, PPEB NewPeb, LPCWSTR ApplicationPathName, LPCWSTR CurrentDirectory, LPCWSTR CommandLine, LPVOID Environment, LPSTARTUPINFOW lpStartupInfo, DWORD dwCreationFlags, BOOL bInheritHandles, DWORD dwSubsystem, PVOID pAppCompatData, DWORD cbAppCompatData ) /*++ Routine Description: This function allocates a process parameters record and formats it. The parameter record is then written into the address space of the specified process. Arguments: dwFlags - bitmask of flags to affect the behavior of BasePushProcessParameters. BASE_PUSH_PROCESS_PARAMETERS_FLAG_APP_MANIFEST_PRESENT Set to indicate that an application manifest was found/used for the given executable. Process - Supplies a handle to the process that is to get the parameters. Peb - Supplies the address of the new processes PEB. ApplicationPathName - Supplies the application path name for the process. CurrentDirectory - Supplies an optional current directory for the process. If not specified, then the current directory is used. CommandLine - Supplies a command line for the new process. Environment - Supplies an optional environment variable list for the process. If not specified, then the current processes arguments are passed. lpStartupInfo - Supplies the startup information for the processes main window. dwCreationFlags - Supplies creation flags for the process bInheritHandles - TRUE if child process inherited handles from parent dwSubsystem - if non-zero, then value will be stored in child process PEB. Only non-zero for separate VDM applications, where the child process has NTVDM.EXE subsystem type, not the 16-bit application type, which is what we want. pAppCompatData - data that is needed for appcompat backend cbAppCompatData - data size in bytes Return Value: TRUE - The operation was successful. FALSE - The operation Failed. --*/ { BOOL bStatus; UNICODE_STRING ImagePathName; UNICODE_STRING CommandLineString; UNICODE_STRING CurrentDirString; UNICODE_STRING DllPath; UNICODE_STRING WindowTitle; UNICODE_STRING DesktopInfo; UNICODE_STRING ShellInfo; UNICODE_STRING RuntimeInfo; PRTL_USER_PROCESS_PARAMETERS ProcessParameters; PRTL_USER_PROCESS_PARAMETERS ParametersInNewProcess; ULONG ParameterLength, EnvironmentLength; SIZE_T RegionSize; PWCHAR s; NTSTATUS Status; WCHAR FullPathBuffer[MAX_PATH+5]; WCHAR *fp; DWORD Rvalue; LPWSTR DllPathData; LPVOID pAppCompatDataInNewProcess; BOOLEAN PebLocked = FALSE; PPEB Peb; #if defined(BUILD_WOW6432) ULONG_PTR Peb32; PVOID TempEnvironment = NULL; #endif Peb = NtCurrentPeb (); Rvalue = GetFullPathNameW(ApplicationPathName,MAX_PATH+4,FullPathBuffer,&fp); if ( Rvalue == 0 || Rvalue > MAX_PATH+4 ) { DllPathData = BaseComputeProcessDllPath( ApplicationPathName, Environment); if (!DllPathData) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } RtlInitUnicodeString( &DllPath, DllPathData ); RtlInitUnicodeString( &ImagePathName, ApplicationPathName ); } else { DllPathData = BaseComputeProcessDllPath( FullPathBuffer, Environment); if ( !DllPathData ) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } RtlInitUnicodeString( &DllPath, DllPathData ); RtlInitUnicodeString( &ImagePathName, FullPathBuffer ); } RtlInitUnicodeString( &CommandLineString, CommandLine ); RtlInitUnicodeString( &CurrentDirString, CurrentDirectory ); if ( lpStartupInfo->lpDesktop ) { RtlInitUnicodeString( &DesktopInfo, lpStartupInfo->lpDesktop ); } else { RtlInitUnicodeString( &DesktopInfo, L""); } if ( lpStartupInfo->lpReserved ) { RtlInitUnicodeString( &ShellInfo, lpStartupInfo->lpReserved ); } else { RtlInitUnicodeString( &ShellInfo, L""); } RuntimeInfo.Buffer = (PWSTR)lpStartupInfo->lpReserved2; RuntimeInfo.Length = lpStartupInfo->cbReserved2; RuntimeInfo.MaximumLength = RuntimeInfo.Length; if (NULL == pAppCompatData) { cbAppCompatData = 0; } if ( lpStartupInfo->lpTitle ) { RtlInitUnicodeString( &WindowTitle, lpStartupInfo->lpTitle ); } else { RtlInitUnicodeString( &WindowTitle, ApplicationPathName ); } Status = RtlCreateProcessParameters( &ProcessParameters, &ImagePathName, &DllPath, (ARGUMENT_PRESENT(CurrentDirectory) ? &CurrentDirString : NULL), &CommandLineString, Environment, &WindowTitle, &DesktopInfo, &ShellInfo, &RuntimeInfo ); if (!NT_SUCCESS( Status )) { BaseSetLastNTError(Status); return FALSE; } if ( !bInheritHandles ) { ProcessParameters->CurrentDirectory.Handle = NULL; } try { if (Environment == NULL) { PebLocked = TRUE; RtlAcquirePebLock (); Environment = Peb->ProcessParameters->Environment; } else { Environment = ProcessParameters->Environment; } if (s = Environment) { while (s[0] != L'\0' || s[1] != L'\0') { s++; } s += 2; EnvironmentLength = (ULONG)((PUCHAR)s - (PUCHAR)Environment); ProcessParameters->Environment = NULL; RegionSize = EnvironmentLength; Status = NtAllocateVirtualMemory( Process, (PVOID *)&ProcessParameters->Environment, 0, &RegionSize, MEM_COMMIT, PAGE_READWRITE ); if ( !NT_SUCCESS( Status ) ) { BaseSetLastNTError(Status); bStatus = FALSE; leave; } #if defined(BUILD_WOW6432) // // Let's try and thunk back some environment variables if we are about to // launch a 64-bit process // Status = NtQueryInformationProcess (Process, ProcessWow64Information, &Peb32, sizeof (Peb32), NULL ); if (NT_SUCCESS (Status) && (Peb32 == 0)) { RegionSize = EnvironmentLength; Status = NtAllocateVirtualMemory (NtCurrentProcess(), &TempEnvironment, 0, &RegionSize, MEM_COMMIT, PAGE_READWRITE ); if (NT_SUCCESS (Status)) { try { RtlCopyMemory (TempEnvironment, Environment, EnvironmentLength ); } except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode (); } if (NT_SUCCESS (Status)) { // // Thunk back special environment variables so that they won't be inherited // for 64-bit processes // Status = Wow64pThunkEnvironmentVariables (&TempEnvironment); if (NT_SUCCESS (Status)) { Environment = TempEnvironment; } } } } #endif Status = NtWriteVirtualMemory( Process, ProcessParameters->Environment, Environment, EnvironmentLength, NULL ); if (PebLocked) { RtlReleasePebLock (); PebLocked = FALSE; } #if defined(BUILD_WOW6432) if (TempEnvironment != NULL) { RegionSize = 0; NtFreeVirtualMemory(NtCurrentProcess(), &TempEnvironment, &RegionSize, MEM_RELEASE ); } #endif if ( !NT_SUCCESS( Status ) ) { BaseSetLastNTError(Status); bStatus = FALSE; leave; } } // // Push the parameters into the new process // ProcessParameters->StartingX = lpStartupInfo->dwX; ProcessParameters->StartingY = lpStartupInfo->dwY; ProcessParameters->CountX = lpStartupInfo->dwXSize; ProcessParameters->CountY = lpStartupInfo->dwYSize; ProcessParameters->CountCharsX = lpStartupInfo->dwXCountChars; ProcessParameters->CountCharsY = lpStartupInfo->dwYCountChars; ProcessParameters->FillAttribute = lpStartupInfo->dwFillAttribute; ProcessParameters->WindowFlags = lpStartupInfo->dwFlags; ProcessParameters->ShowWindowFlags = lpStartupInfo->wShowWindow; if (lpStartupInfo->dwFlags & (STARTF_USESTDHANDLES | STARTF_USEHOTKEY | STARTF_HASSHELLDATA)) { ProcessParameters->StandardInput = lpStartupInfo->hStdInput; ProcessParameters->StandardOutput = lpStartupInfo->hStdOutput; ProcessParameters->StandardError = lpStartupInfo->hStdError; } if (dwCreationFlags & DETACHED_PROCESS) { ProcessParameters->ConsoleHandle = (HANDLE)CONSOLE_DETACHED_PROCESS; } else if (dwCreationFlags & CREATE_NEW_CONSOLE) { ProcessParameters->ConsoleHandle = (HANDLE)CONSOLE_NEW_CONSOLE; } else if (dwCreationFlags & CREATE_NO_WINDOW) { ProcessParameters->ConsoleHandle = (HANDLE)CONSOLE_CREATE_NO_WINDOW; } else { ProcessParameters->ConsoleHandle = Peb->ProcessParameters->ConsoleHandle; if (!(lpStartupInfo->dwFlags & (STARTF_USESTDHANDLES | STARTF_USEHOTKEY | STARTF_HASSHELLDATA))) { if (bInheritHandles || CONSOLE_HANDLE( Peb->ProcessParameters->StandardInput ) ) { ProcessParameters->StandardInput = Peb->ProcessParameters->StandardInput; } if (bInheritHandles || CONSOLE_HANDLE( Peb->ProcessParameters->StandardOutput ) ) { ProcessParameters->StandardOutput = Peb->ProcessParameters->StandardOutput; } if (bInheritHandles || CONSOLE_HANDLE( Peb->ProcessParameters->StandardError ) ) { ProcessParameters->StandardError = Peb->ProcessParameters->StandardError; } } } // // CREATE_NEW_PROCESS_GROUP, in the absence of CREATE_NEW_CONSOLE, // means that CTRL+C events should be ignored. This is solely for // appcompat. // if ((dwCreationFlags & CREATE_NEW_PROCESS_GROUP) != 0 && (dwCreationFlags & CREATE_NEW_CONSOLE) == 0) { ProcessParameters->ConsoleFlags |= CONSOLE_IGNORE_CTRL_C; } ProcessParameters->Flags |= (Peb->ProcessParameters->Flags & RTL_USER_PROC_DISABLE_HEAP_DECOMMIT); ParameterLength = ProcessParameters->Length; if (dwFlags & BASE_PUSH_PROCESS_PARAMETERS_FLAG_APP_MANIFEST_PRESENT) ProcessParameters->Flags |= RTL_USER_PROC_APP_MANIFEST_PRESENT; // // Allocate memory in the new process to push the parameters // ParametersInNewProcess = NULL; RegionSize = ParameterLength; Status = NtAllocateVirtualMemory( Process, (PVOID *)&ParametersInNewProcess, 0, &RegionSize, MEM_COMMIT, PAGE_READWRITE ); ParameterLength = (ULONG)RegionSize; if ( !NT_SUCCESS( Status ) ) { BaseSetLastNTError(Status); bStatus = FALSE; leave; } ProcessParameters->MaximumLength = ParameterLength; if ( dwCreationFlags & PROFILE_USER ) { ProcessParameters->Flags |= RTL_USER_PROC_PROFILE_USER; } if ( dwCreationFlags & PROFILE_KERNEL ) { ProcessParameters->Flags |= RTL_USER_PROC_PROFILE_KERNEL; } if ( dwCreationFlags & PROFILE_SERVER ) { ProcessParameters->Flags |= RTL_USER_PROC_PROFILE_SERVER; } // // Push the parameters // Status = NtWriteVirtualMemory( Process, ParametersInNewProcess, ProcessParameters, ProcessParameters->Length, NULL ); if ( !NT_SUCCESS( Status ) ) { BaseSetLastNTError(Status); bStatus = FALSE; leave; } // // Make the processes PEB point to the parameters. // Status = NtWriteVirtualMemory( Process, &NewPeb->ProcessParameters, &ParametersInNewProcess, sizeof( ParametersInNewProcess ), NULL ); if ( !NT_SUCCESS( Status ) ) { BaseSetLastNTError(Status); bStatus = FALSE; leave; } // // allocate and write appcompat data for the new process // pAppCompatDataInNewProcess = NULL; if ( NULL != pAppCompatData ) { RegionSize = cbAppCompatData; Status = NtAllocateVirtualMemory( Process, (PVOID*)&pAppCompatDataInNewProcess, 0, &RegionSize, MEM_COMMIT, PAGE_READWRITE ); if ( !NT_SUCCESS( Status ) ) { BaseSetLastNTError(Status); bStatus = FALSE; leave; } // // write the data itself // Status = NtWriteVirtualMemory( Process, pAppCompatDataInNewProcess, pAppCompatData, cbAppCompatData, NULL ); if ( !NT_SUCCESS( Status ) ) { BaseSetLastNTError(Status); bStatus = FALSE; leave; } } // // save the pointer to appcompat data in peb // Status = NtWriteVirtualMemory( Process, &NewPeb->pShimData, &pAppCompatDataInNewProcess, sizeof( pAppCompatDataInNewProcess ), NULL ); if ( !NT_SUCCESS( Status ) ) { BaseSetLastNTError(Status); bStatus = FALSE; leave; } // // Set subsystem type in PEB if requested by caller. Ignore error // if (dwSubsystem != 0) { NtWriteVirtualMemory( Process, &NewPeb->ImageSubsystem, &dwSubsystem, sizeof( NewPeb->ImageSubsystem ), NULL ); } bStatus = TRUE; } finally { if (PebLocked) { RtlReleasePebLock (); } RtlFreeHeap (RtlProcessHeap(), 0,DllPath.Buffer); if ( ProcessParameters ) { RtlDestroyProcessParameters(ProcessParameters); } } return bStatus; } LPCWSTR BasepEndOfDirName( IN LPCWSTR FileName ) { LPCWSTR FileNameEnd, FileNameFirstWhack = wcschr(FileName, L'\\'); if (FileNameFirstWhack) { FileNameEnd = wcsrchr(FileNameFirstWhack, L'\\'); ASSERT(FileNameEnd); if (FileNameEnd == FileNameFirstWhack) FileNameEnd++; } else { FileNameEnd = NULL; } return FileNameEnd; } VOID BasepLocateExeLdrEntry( IN PCLDR_DATA_TABLE_ENTRY Entry, IN PVOID Context, IN OUT BOOLEAN *StopEnumeration ) /*++ Routine Description: This function is a LDR_LOADED_MODULE_ENUMBERATION_CALLBACK_FUNCTION which locates the exe's loader data table entry. Arguments: Entry - the entry currently being enumerated. Context - the image base address (NtCurrentPeb()->ImageBaseAddress). StopEnumeration - used to stop the enumeration. Return Value: None. The exe's loader data table entry, if found, is stored in the global BasepExeLdrEntry. --*/ { ASSERT(Entry); ASSERT(Context); ASSERT(StopEnumeration); if (BasepExeLdrEntry) { *StopEnumeration = TRUE; } else if (Entry->DllBase == Context) { BasepExeLdrEntry = Entry; *StopEnumeration = TRUE; } } LPWSTR BasepComputeProcessPath( IN const BASEP_SEARCH_PATH_ELEMENT *Elements, IN LPCWSTR AppName, IN LPVOID Environment ) /*++ Routine Description: This function computes a process path. Arguments: Elements - The elements to build into a path. AppName - An optional argument that specifies the name of the application. If this parameter is not specified, then the current application is used. Environment - Supplies the environment block to be used to calculate the path variable value. Return Value: The return value is the value of the requested path. --*/ { LPCWSTR AppNameEnd; const BASEP_SEARCH_PATH_ELEMENT *Element; UNICODE_STRING EnvPath; LPWSTR EnvPathBuffer = NULL; LPWSTR PathBuffer = NULL, PathCurrent; ULONG PathLengthInBytes; NTSTATUS Status = STATUS_SUCCESS; __try { // First, figure out how much space we'll need. PathLengthInBytes = 0; for (Element = Elements; *Element != BasepSearchPathEnd; Element++) { switch (*Element) { case BasepSearchPathCurdir: PathLengthInBytes += 2 * sizeof(UNICODE_NULL); // .; break; case BasepSearchPathDlldir: ASSERT(BaseDllDirectory.Buffer != NULL); PathLengthInBytes += BaseDllDirectory.Length; if (BaseDllDirectory.Length) { PathLengthInBytes += sizeof(UNICODE_NULL); } break; case BasepSearchPathAppdir: if (AppName) { // Try to use the passed-in appname AppNameEnd = BasepEndOfDirName(AppName); } if (!AppName || !AppNameEnd) { // We didn't have or were unable to use the passed-in // appname -- so attempt to use the current exe's name if (RtlGetPerThreadCurdir() && RtlGetPerThreadCurdir()->ImageName) { AppName = RtlGetPerThreadCurdir()->ImageName->Buffer; } else { BasepCheckExeLdrEntry(); if (BasepExeLdrEntry) { AppName = BasepExeLdrEntry->FullDllName.Buffer; } } if (AppName) { AppNameEnd = BasepEndOfDirName(AppName); } } if (AppName && AppNameEnd) { // Either we had a passed-in appname which worked, or // we found the current exe's name and that worked. // // AppNameEnd points to the end of the base of the exe // name -- so the difference is the number of // characters in the base name, and we add one for the // trailing semicolon / NULL. PathLengthInBytes += ((AppNameEnd - AppName + 1) * sizeof(UNICODE_NULL)); } break; case BasepSearchPathDefaultDirs: ASSERT(! (BaseDefaultPath.Length & 1)); // We don't need an extra UNICODE_NULL here -- baseinit.c // appends our trailing semicolon for us. PathLengthInBytes += BaseDefaultPath.Length; break; case BasepSearchPathEnvPath: if (! Environment) { RtlAcquirePebLock(); } __try { EnvPath.MaximumLength = 0; Status = RtlQueryEnvironmentVariable_U(Environment, &BasePathVariableName, &EnvPath); if (Status == STATUS_BUFFER_TOO_SMALL) { // Now that we know how much to allocate, attempt // to alloc a buffer that's actually big enough. EnvPath.MaximumLength = EnvPath.Length + sizeof(UNICODE_NULL); EnvPathBuffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), EnvPath.MaximumLength); if (! EnvPathBuffer) { Status = STATUS_NO_MEMORY; __leave; } EnvPath.Buffer = EnvPathBuffer; Status = RtlQueryEnvironmentVariable_U(Environment, &BasePathVariableName, &EnvPath); } } __finally { if (! Environment) { RtlReleasePebLock(); } } if (Status == STATUS_VARIABLE_NOT_FOUND) { EnvPath.Length = 0; Status = STATUS_SUCCESS; } else if (! NT_SUCCESS(Status)) { __leave; } else { // The final tally is the length, in bytes, of whatever // we're using for our path, plus a character for the // trailing whack or NULL. ASSERT(! (EnvPath.Length & 1)); PathLengthInBytes += EnvPath.Length + sizeof(UNICODE_NULL); } break; DEFAULT_UNREACHABLE; } // switch (*Element) } // foreach Element (Elements) -- size loop ASSERT(PathLengthInBytes > 0); ASSERT(! (PathLengthInBytes & 1)); // Now we have the length, in bytes, of the buffer we'll need for // our path. Time to allocate it... PathBuffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), PathLengthInBytes); if (! PathBuffer) { Status = STATUS_NO_MEMORY; __leave; } // Now go through the loop again, this time appending onto the // PathBuffer. PathCurrent = PathBuffer; for (Element = Elements; *Element != BasepSearchPathEnd; Element++) { switch (*Element) { case BasepSearchPathCurdir: ASSERT(((PathCurrent - PathBuffer + 2) * sizeof(UNICODE_NULL)) <= PathLengthInBytes); *PathCurrent++ = L'.'; *PathCurrent++ = L';'; break; case BasepSearchPathDlldir: if (BaseDllDirectory.Length) { ASSERT((((PathCurrent - PathBuffer + 1) * sizeof(UNICODE_NULL)) + BaseDllDirectory.Length) <= PathLengthInBytes); RtlCopyMemory(PathCurrent, BaseDllDirectory.Buffer, BaseDllDirectory.Length); PathCurrent += (BaseDllDirectory.Length >> 1); *PathCurrent++ = L';'; } break; case BasepSearchPathAppdir: if (AppName && AppNameEnd) { ASSERT(((PathCurrent - PathBuffer + 1 + (AppNameEnd - AppName)) * sizeof(UNICODE_NULL)) <= PathLengthInBytes); RtlCopyMemory(PathCurrent, AppName, ((AppNameEnd - AppName) * sizeof(UNICODE_NULL))); PathCurrent += AppNameEnd - AppName; *PathCurrent++ = L';'; } break; case BasepSearchPathDefaultDirs: ASSERT((((PathCurrent - PathBuffer) * sizeof(UNICODE_NULL)) + BaseDefaultPath.Length) <= PathLengthInBytes); RtlCopyMemory(PathCurrent, BaseDefaultPath.Buffer, BaseDefaultPath.Length); PathCurrent += (BaseDefaultPath.Length >> 1); // We don't need to add a semicolon here -- baseinit.c // appends our trailing semicolon for us. break; case BasepSearchPathEnvPath: if (EnvPath.Length) { ASSERT((((PathCurrent - PathBuffer + 1) * sizeof(UNICODE_NULL)) + EnvPath.Length) <= PathLengthInBytes); RtlCopyMemory(PathCurrent, EnvPath.Buffer, EnvPath.Length); PathCurrent += (EnvPath.Length >> 1); *PathCurrent++ = L';'; } break; DEFAULT_UNREACHABLE; } // switch (*Element) } // foreach Element (Elements) -- append loop // At this point, PathCurrent points just beyond PathBuffer. // Let's assert that... ASSERT((PathCurrent - PathBuffer) * sizeof(UNICODE_NULL) == PathLengthInBytes); // ... and turn the final ';' into the string terminator. ASSERT(PathCurrent > PathBuffer); PathCurrent[-1] = UNICODE_NULL; } __finally { if (EnvPathBuffer) { RtlFreeHeap(RtlProcessHeap(), 0, EnvPathBuffer); } if (PathBuffer && (AbnormalTermination() || ! NT_SUCCESS(Status))) { RtlFreeHeap(RtlProcessHeap(), 0, PathBuffer); PathBuffer = NULL; } } return PathBuffer; } LPWSTR BaseComputeProcessDllPath( IN LPCWSTR AppName, IN LPVOID Environment ) /*++ Routine Description: This function computes a process DLL path. Arguments: AppName - An optional argument that specifies the name of the application. If this parameter is not specified, then the current application is used. Environment - Supplies the environment block to be used to calculate the path variable value. Return Value: The return value is the value of the processes DLL path. --*/ { NTSTATUS Status; HANDLE Key; static UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\MACHINE\\System\\CurrentControlSet\\Control\\Session Manager"), ValueName = RTL_CONSTANT_STRING(L"SafeDllSearchMode"); static OBJECT_ATTRIBUTES ObjA = RTL_CONSTANT_OBJECT_ATTRIBUTES(&KeyName, OBJ_CASE_INSENSITIVE); CHAR Buffer[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + sizeof(DWORD)]; PKEY_VALUE_PARTIAL_INFORMATION Info; ULONG ResultLength; LONG CurrentDirPlacement, PrevCurrentDirPlacement; LPWSTR Result; static const BASEP_SEARCH_PATH_ELEMENT DllDirSearchPath[] = { BasepSearchPathAppdir, BasepSearchPathDlldir, BasepSearchPathDefaultDirs, BasepSearchPathEnvPath, BasepSearchPathEnd }; RtlEnterCriticalSection(&BaseDllDirectoryLock); if (BaseDllDirectory.Buffer) { Result = BasepComputeProcessPath(DllDirSearchPath, AppName, Environment); RtlLeaveCriticalSection(&BaseDllDirectoryLock); return Result; } RtlLeaveCriticalSection(&BaseDllDirectoryLock); CurrentDirPlacement = BasepDllCurrentDirPlacement; if (CurrentDirPlacement == BasepCurrentDirUninitialized) { Status = NtOpenKey(&Key, KEY_QUERY_VALUE, &ObjA); if (! NT_SUCCESS(Status)) { goto compute_path; } Info = (PKEY_VALUE_PARTIAL_INFORMATION) Buffer; Status = NtQueryValueKey(Key, &ValueName, KeyValuePartialInformation, Info, sizeof(Buffer), &ResultLength); if (! NT_SUCCESS(Status)) { goto close_key; } if (ResultLength != sizeof(Buffer)) { goto close_key; } RtlCopyMemory(&CurrentDirPlacement, Info->Data, sizeof(DWORD)); close_key: NtClose(Key); compute_path: if (! BASEP_VALID_CURDIR_PLACEMENT_P(CurrentDirPlacement)) { CurrentDirPlacement = BASEP_DEFAULT_DLL_CURDIR_PLACEMENT; } PrevCurrentDirPlacement = InterlockedCompareExchange(&BasepDllCurrentDirPlacement, CurrentDirPlacement, BasepCurrentDirUninitialized); if (PrevCurrentDirPlacement != BasepCurrentDirUninitialized) { CurrentDirPlacement = PrevCurrentDirPlacement; } } if (! BASEP_VALID_CURDIR_PLACEMENT_P(CurrentDirPlacement)) { CurrentDirPlacement = BASEP_DEFAULT_DLL_CURDIR_PLACEMENT; } return BasepComputeProcessPath(BasepDllSearchPaths[CurrentDirPlacement], AppName, Environment); } LPWSTR BaseComputeProcessSearchPath( VOID ) /*++ Routine Description: This function computes a process search path. Arguments: None Return Value: The return value is the value of the processes search path. --*/ { static const BASEP_SEARCH_PATH_ELEMENT SearchPath[] = { BasepSearchPathAppdir, BasepSearchPathCurdir, BasepSearchPathDefaultDirs, BasepSearchPathEnvPath, BasepSearchPathEnd }; return BasepComputeProcessPath(SearchPath, NULL, NULL); } LPWSTR BaseComputeProcessExePath( LPCWSTR ExeName ) /*++ Routine Description: This function computes a process exe path. Arguments: ExeName - The name of the exe which will be looked for. Return Value: The return value is the value of the processes exe path. --*/ { static const BASEP_SEARCH_PATH_ELEMENT UseDotSearchPath[] = { BasepSearchPathAppdir, BasepSearchPathCurdir, BasepSearchPathDefaultDirs, BasepSearchPathEnvPath, BasepSearchPathEnd }; static const BASEP_SEARCH_PATH_ELEMENT NoDotSearchPath[] = { BasepSearchPathAppdir, BasepSearchPathDefaultDirs, BasepSearchPathEnvPath, BasepSearchPathEnd }; return BasepComputeProcessPath((NeedCurrentDirectoryForExePathW(ExeName) ? UseDotSearchPath : NoDotSearchPath), NULL, NULL); } PUNICODE_STRING Basep8BitStringToStaticUnicodeString( IN LPCSTR lpSourceString ) /*++ Routine Description: Captures and converts a 8-bit (OEM or ANSI) string into the Teb Static Unicode String Arguments: lpSourceString - string in OEM or ANSI Return Value: Pointer to the Teb static string if conversion was successful, NULL otherwise. If a failure occurred, the last error is set. --*/ { PUNICODE_STRING StaticUnicode; ANSI_STRING AnsiString; NTSTATUS Status; // // Get pointer to static per-thread string // StaticUnicode = &NtCurrentTeb()->StaticUnicodeString; // // Convert input string into unicode string // Status = RtlInitAnsiStringEx( &AnsiString, lpSourceString ); if( NT_SUCCESS(Status) ) { Status = Basep8BitStringToUnicodeString( StaticUnicode, &AnsiString, FALSE ); } else { Status = STATUS_BUFFER_OVERFLOW; } // // If we couldn't convert the string // if ( !NT_SUCCESS( Status ) ) { if ( Status == STATUS_BUFFER_OVERFLOW ) { SetLastError( ERROR_FILENAME_EXCED_RANGE ); } else { BaseSetLastNTError( Status ); } return NULL; } else { return StaticUnicode; } } BOOL Basep8BitStringToDynamicUnicodeString( OUT PUNICODE_STRING UnicodeString, IN LPCSTR lpSourceString ) /*++ Routine Description: Captures and converts a 8-bit (OEM or ANSI) string into a heap-allocated UNICODE string Arguments: UnicodeString - location where UNICODE_STRING is stored lpSourceString - string in OEM or ANSI Return Value: TRUE if string is correctly stored, FALSE if an error occurred. In the error case, the last error is correctly set. --*/ { ANSI_STRING AnsiString; NTSTATUS Status; // // Convert input into dynamic unicode string // Status = RtlInitAnsiStringEx( &AnsiString, lpSourceString ); if( NT_SUCCESS(Status) ) { Status = Basep8BitStringToUnicodeString( UnicodeString, &AnsiString, TRUE ); } else { Status = STATUS_BUFFER_OVERFLOW; } // // If we couldn't do this, fail // if (!NT_SUCCESS( Status )){ if ( Status == STATUS_BUFFER_OVERFLOW ) { SetLastError( ERROR_FILENAME_EXCED_RANGE ); } else { BaseSetLastNTError( Status ); } return FALSE; } return TRUE; } // // Thunks for converting between ANSI/OEM and UNICODE // ULONG BasepAnsiStringToUnicodeSize( PANSI_STRING AnsiString ) /*++ Routine Description: Determines the size of a UNICODE version of an ANSI string Arguments: AnsiString - string to examine Return Value: Byte size of UNICODE version of string including a trailing L'\0'. --*/ { return RtlAnsiStringToUnicodeSize( AnsiString ); } ULONG BasepOemStringToUnicodeSize( PANSI_STRING OemString ) /*++ Routine Description: Determines the size of a UNICODE version of an OEM string Arguments: OemString - string to examine Return Value: Byte size of UNICODE version of string including a trailing L'\0'. --*/ { return RtlOemStringToUnicodeSize( OemString ); } ULONG BasepUnicodeStringToOemSize( PUNICODE_STRING UnicodeString ) /*++ Routine Description: Determines the size of an OEM version of a UNICODE string Arguments: UnicodeString - string to examine Return Value: Byte size of OEM version of string including a trailing '\0'. --*/ { return RtlUnicodeStringToOemSize( UnicodeString ); } ULONG BasepUnicodeStringToAnsiSize( PUNICODE_STRING UnicodeString ) /*++ Routine Description: Determines the size of an ANSI version of a UNICODE string Arguments: UnicodeString - string to examine Return Value: Byte size of ANSI version of string including a trailing '\0'. --*/ { return RtlUnicodeStringToAnsiSize( UnicodeString ); } typedef struct _BASEP_ACQUIRE_STATE { HANDLE Token; PTOKEN_PRIVILEGES OldPrivileges; PTOKEN_PRIVILEGES NewPrivileges; ULONG Revert; ULONG Spare; BYTE OldPrivBuffer[ 1024 ]; } BASEP_ACQUIRE_STATE, *PBASEP_ACQUIRE_STATE; // // This function does the correct thing - it checks for the thread token // before opening the process token. // NTSTATUS BasepAcquirePrivilegeEx( ULONG Privilege, PVOID *ReturnedState ) { PBASEP_ACQUIRE_STATE State; ULONG cbNeeded; LUID LuidPrivilege; NTSTATUS Status, Status1; BOOL St; // // Make sure we have access to adjust and to get the old token privileges // *ReturnedState = NULL; State = RtlAllocateHeap (RtlProcessHeap(), MAKE_TAG( TMP_TAG ), sizeof(BASEP_ACQUIRE_STATE) + sizeof(TOKEN_PRIVILEGES) + (1 - ANYSIZE_ARRAY) * sizeof(LUID_AND_ATTRIBUTES)); if (State == NULL) { return STATUS_NO_MEMORY; } State->Revert = 0; if (RtlIsImpersonating()) { // // We're impersonating. So make sure we use the specified // impersonation token. // Status = NtOpenThreadToken (NtCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &State->Token); if (! NT_SUCCESS(Status)) { RtlFreeHeap(RtlProcessHeap(), 0, State); return Status; } } else { // // We're not impersonating. So make a copy of the process // token, and impersonate that, so that we're monkeying about // with our own privs instead of everyone's. // Status = RtlImpersonateSelf (SecurityDelegation); if (!NT_SUCCESS (Status)) { RtlFreeHeap (RtlProcessHeap(), 0, State); return Status; } Status = NtOpenThreadToken (NtCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &State->Token); if (!NT_SUCCESS (Status)) { State->Token = NULL; Status1 = NtSetInformationThread (NtCurrentThread(), ThreadImpersonationToken, &State->Token, sizeof (State->Token)); ASSERT (NT_SUCCESS (Status1)); RtlFreeHeap( RtlProcessHeap(), 0, State ); return Status; } State->Revert = 1; } State->NewPrivileges = (PTOKEN_PRIVILEGES)(State+1); State->OldPrivileges = (PTOKEN_PRIVILEGES)(State->OldPrivBuffer); // // Initialize the privilege adjustment structure // LuidPrivilege = RtlConvertUlongToLuid(Privilege); State->NewPrivileges->PrivilegeCount = 1; State->NewPrivileges->Privileges[0].Luid = LuidPrivilege; State->NewPrivileges->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // // Enable the privilege // cbNeeded = sizeof( State->OldPrivBuffer ); Status = NtAdjustPrivilegesToken (State->Token, FALSE, State->NewPrivileges, cbNeeded, State->OldPrivileges, &cbNeeded); if (Status == STATUS_BUFFER_TOO_SMALL) { State->OldPrivileges = RtlAllocateHeap (RtlProcessHeap(), MAKE_TAG( TMP_TAG ), cbNeeded); if (State->OldPrivileges == NULL) { Status = STATUS_NO_MEMORY; } else { Status = NtAdjustPrivilegesToken (State->Token, FALSE, State->NewPrivileges, cbNeeded, State->OldPrivileges, &cbNeeded); } } // // STATUS_NOT_ALL_ASSIGNED means that the privilege isn't // in the token, so we can't proceed. // // This is a warning level status, so map it to an error status. // if (Status == STATUS_NOT_ALL_ASSIGNED) { Status = STATUS_PRIVILEGE_NOT_HELD; } if (!NT_SUCCESS( Status )) { if (State->OldPrivileges != (PTOKEN_PRIVILEGES)State->OldPrivBuffer) { RtlFreeHeap( RtlProcessHeap(), 0, State->OldPrivileges ); } St = CloseHandle (State->Token); ASSERT (St); State->Token = NULL; if (State->Revert) { Status1 = NtSetInformationThread (NtCurrentThread(), ThreadImpersonationToken, &State->Token, sizeof (State->Token)); ASSERT (NT_SUCCESS (Status1)); } RtlFreeHeap( RtlProcessHeap(), 0, State ); return Status; } *ReturnedState = State; return STATUS_SUCCESS; } VOID BasepReleasePrivilege( PVOID StatePointer ) { BOOL St; NTSTATUS Status; PBASEP_ACQUIRE_STATE State = (PBASEP_ACQUIRE_STATE)StatePointer; if (!State->Revert) { NtAdjustPrivilegesToken (State->Token, FALSE, State->OldPrivileges, 0, NULL, NULL); } if (State->OldPrivileges != (PTOKEN_PRIVILEGES)State->OldPrivBuffer) { RtlFreeHeap( RtlProcessHeap(), 0, State->OldPrivileges ); } St = CloseHandle( State->Token ); ASSERT (St); State->Token = NULL; if (State->Revert) { Status = NtSetInformationThread (NtCurrentThread(), ThreadImpersonationToken, &State->Token, sizeof (State->Token)); ASSERT (NT_SUCCESS (Status)); } RtlFreeHeap( RtlProcessHeap(), 0, State ); return; }