windows-nt/Source/XPSP1/NT/base/win32/client/support.c
2020-09-26 16:20:57 +08:00

2484 lines
66 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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"
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 ( ARGUMENT_PRESENT(ObjectName) ) {
RootDirectory = BaseGetNamedObjectDirectory();
}
else {
RootDirectory = NULL;
}
if ( SecurityAttributes ) {
Attributes = (SecurityAttributes->bInheritHandle ? OBJ_INHERIT : 0);
SecurityDescriptor = SecurityAttributes->lpSecurityDescriptor;
}
else {
Attributes = 0;
SecurityDescriptor = NULL;
}
if ( ARGUMENT_PRESENT(ObjectName) ) {
Attributes |= OBJ_OPENIF;
}
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 {
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 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 Peb,
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.
--*/
{
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;
#if defined(BUILD_WOW6432)
ULONG_PTR Peb32;
PVOID TempEnvironment = NULL;
#endif
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 (s = ProcessParameters->Environment) {
while (*s) {
while (*s++) {
}
}
s++;
Environment = ProcessParameters->Environment;
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);
return( FALSE );
}
#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 defined(BUILD_WOW6432)
if (TempEnvironment != NULL) {
RegionSize = 0;
NtFreeVirtualMemory(Process,
&TempEnvironment,
&RegionSize,
MEM_RELEASE
);
}
#endif
if ( !NT_SUCCESS( Status ) ) {
BaseSetLastNTError(Status);
return( FALSE );
}
}
//
// 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 =
NtCurrentPeb()->ProcessParameters->ConsoleHandle;
if (!(lpStartupInfo->dwFlags & (STARTF_USESTDHANDLES | STARTF_USEHOTKEY | STARTF_HASSHELLDATA))) {
if (bInheritHandles ||
CONSOLE_HANDLE( NtCurrentPeb()->ProcessParameters->StandardInput )
) {
ProcessParameters->StandardInput =
NtCurrentPeb()->ProcessParameters->StandardInput;
}
if (bInheritHandles ||
CONSOLE_HANDLE( NtCurrentPeb()->ProcessParameters->StandardOutput )
) {
ProcessParameters->StandardOutput =
NtCurrentPeb()->ProcessParameters->StandardOutput;
}
if (bInheritHandles ||
CONSOLE_HANDLE( NtCurrentPeb()->ProcessParameters->StandardError )
) {
ProcessParameters->StandardError =
NtCurrentPeb()->ProcessParameters->StandardError;
}
}
}
if (dwCreationFlags & CREATE_NEW_PROCESS_GROUP) {
ProcessParameters->ConsoleFlags = 1;
}
ProcessParameters->Flags |=
(NtCurrentPeb()->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);
return FALSE;
}
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);
return FALSE;
}
//
// Make the processes PEB point to the parameters.
//
Status = NtWriteVirtualMemory(
Process,
&Peb->ProcessParameters,
&ParametersInNewProcess,
sizeof( ParametersInNewProcess ),
NULL
);
if ( !NT_SUCCESS( Status ) ) {
BaseSetLastNTError(Status);
return FALSE;
}
//
// 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);
return FALSE;
}
//
// write the data itself
//
Status = NtWriteVirtualMemory(
Process,
pAppCompatDataInNewProcess,
pAppCompatData,
cbAppCompatData,
NULL
);
if ( !NT_SUCCESS( Status ) ) {
BaseSetLastNTError(Status);
return FALSE;
}
}
//
// save the pointer to appcompat data in peb
//
Status = NtWriteVirtualMemory(
Process,
&Peb->pShimData,
&pAppCompatDataInNewProcess,
sizeof( pAppCompatDataInNewProcess ),
NULL
);
if ( !NT_SUCCESS( Status ) ) {
BaseSetLastNTError(Status);
return FALSE;
}
//
// Set subsystem type in PEB if requested by caller. Ignore error
//
if (dwSubsystem != 0) {
NtWriteVirtualMemory(
Process,
&Peb->ImageSubsystem,
&dwSubsystem,
sizeof( Peb->ImageSubsystem ),
NULL
);
}
}
finally {
RtlFreeHeap(RtlProcessHeap(), 0,DllPath.Buffer);
if ( ProcessParameters ) {
RtlDestroyProcessParameters(ProcessParameters);
}
}
return TRUE;
}
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);
}
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
//
RtlInitAnsiString( &AnsiString, lpSourceString );
Status = Basep8BitStringToUnicodeString( StaticUnicode, &AnsiString, FALSE );
//
// 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
//
RtlInitString( &AnsiString, lpSourceString );
Status = Basep8BitStringToUnicodeString( UnicodeString, &AnsiString, TRUE );
//
// 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;
//
// Try opening the thread token first, in case we're impersonating.
//
Status = NtOpenThreadToken (NtCurrentThread(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
FALSE,
&State->Token);
if ( !NT_SUCCESS( Status )) {
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;
}