2020-09-30 17:12:29 +02:00

1618 lines
44 KiB
C

/*++
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"
PLDR_DATA_TABLE_ENTRY BasepExeLdrEntry;
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.
--*/
{
LONG Multiplier;
if ( (LONG) Milliseconds < 0 ) {
if ( Milliseconds == -1 ) {
return( NULL );
}
else {
Multiplier = 10000;
}
}
else {
Multiplier = -10000;
}
TimeOut->QuadPart = Int32x32To64( Milliseconds, Multiplier );
return TimeOut;
}
NTSTATUS
BaseCreateStack(
IN HANDLE Process,
IN ULONG StackSize,
IN ULONG 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;
ULONG RegionSize;
ULONG OldProtect;
ULONG ImageStackSize, ImageStackCommit;
PIMAGE_NT_HEADERS NtHeaders;
PPEB Peb;
ULONG PageSize;
Peb = NtCurrentPeb();
BaseStaticServerData = Peb->ReadOnlyStaticServerData[BASESRV_SERVERDLL_INDEX];
PageSize = BaseStaticServerData->SysInfo.PageSize;
//
// If the stack size was not supplied, then use the sizes from the
// image header.
//
NtHeaders = RtlImageNtHeader(Peb->ImageBaseAddress);
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,
BaseStaticServerData->SysInfo.AllocationGranularity
);
//
// Reserve address space for the stack
//
Stack = NULL,
Status = NtAllocateVirtualMemory(
Process,
(PVOID *)&Stack,
0,
&MaximumStackSize,
MEM_RESERVE,
PAGE_READWRITE
);
if ( !NT_SUCCESS( Status ) ) {
return Status;
}
InitialTeb->OldInitialTeb.OldStackBase = NULL;
InitialTeb->OldInitialTeb.OldStackLimit = NULL;
InitialTeb->StackAllocationBase = Stack;
InitialTeb->StackBase = Stack + MaximumStackSize;
Stack += MaximumStackSize - StackSize;
if (MaximumStackSize > StackSize) {
Stack -= PageSize;
StackSize += PageSize;
GuardPage = TRUE;
}
else {
GuardPage = FALSE;
}
//
// Commit the initially valid portion of the stack
//
Status = NtAllocateVirtualMemory(
Process,
(PVOID *)&Stack,
0,
&StackSize,
MEM_COMMIT,
PAGE_READWRITE
);
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 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);
}
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 it's 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
//
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 it's 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;
ULONG Zero;
PVOID BaseAddress;
#if defined (WX86)
PWX86TIB Wx86Tib;
#endif
Zero = 0;
BaseAddress = OldStack;
Status = NtFreeVirtualMemory(
NtCurrentProcess(),
&BaseAddress,
&Zero,
MEM_RELEASE
);
ASSERT(NT_SUCCESS(Status));
#if defined (WX86)
if (NtCurrentTeb() && (Wx86Tib = Wx86CurrentTib())) {
BaseAddress = Wx86Tib->DeallocationStack;
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)
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 (!MaximumStackSize) {
MaximumStackSize = NtHeaders->OptionalHeader.SizeOfStackReserve;
}
if (!CommittedStackSize) {
CommittedStackSize = 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 = ROUND_UP_TO_PAGES(SizeWx86Tib + 4096);
if (CommittedStackSize < 1024 * 1024) { // 1 MB
CommittedStackSize += Size;
}
if (MaximumStackSize < 1024 * 1024 * 16) { // 10 MB
MaximumStackSize += Size;
}
Status = BaseCreateStack( Process,
CommittedStackSize,
MaximumStackSize,
&InitialTeb
);
if (!NT_SUCCESS(Status)) {
return Status;
}
//
// Fill in the Teb->Vdm with pWx86Tib
//
TargetWx86Tib = (PVOID)((ULONG)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)TargetWx86Tib;
Wx86Tib.StackBase = InitialTeb.StackBase;
Wx86Tib.StackLimit = InitialTeb.StackLimit;
Wx86Tib.DeallocationStack = 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;
PVOID BaseAddress;
dwStackSize = 0;
BaseAddress = InitialTeb->StackAllocationBase;
NtFreeVirtualMemory( hProcess,
&BaseAddress,
&dwStackSize,
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;
BaseAddress = Wx86Tib.DeallocationStack;
NtFreeVirtualMemory(hProcess,
&BaseAddress,
&dwStackSize,
MEM_RELEASE
);
// set Teb->Vdm = NULL;
dwStackSize = 0;
Status = NtWriteVirtualMemory(
hProcess,
&Teb->Vdm,
&dwStackSize,
sizeof(pWx86Tib),
NULL
);
}
}
#endif
}
BOOL
BasePushProcessParameters(
HANDLE Process,
PPEB Peb,
LPCWSTR ApplicationPathName,
LPCWSTR CurrentDirectory,
LPCWSTR CommandLine,
LPVOID Environment,
LPSTARTUPINFOW lpStartupInfo,
DWORD dwCreationFlags,
BOOL bInheritHandles,
DWORD dwSubsystem
)
/*++
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:
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.
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, RegionSize;
PWCHAR s;
NTSTATUS Status;
WCHAR FullPathBuffer[MAX_PATH+5];
WCHAR *fp;
DWORD Rvalue;
Rvalue = GetFullPathNameW(ApplicationPathName,MAX_PATH+4,FullPathBuffer,&fp);
if ( Rvalue == 0 || Rvalue > MAX_PATH+4 ) {
RtlInitUnicodeString( &DllPath, BaseComputeProcessDllPath( ApplicationPathName,
Environment
)
);
RtlInitUnicodeString( &ImagePathName, ApplicationPathName );
}
else {
RtlInitUnicodeString( &DllPath, BaseComputeProcessDllPath( FullPathBuffer,
Environment
)
);
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 ( 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 = (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 );
}
Status = NtWriteVirtualMemory( Process,
ProcessParameters->Environment,
Environment,
EnvironmentLength,
NULL
);
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)) {
ProcessParameters->StandardInput =
NtCurrentPeb()->ProcessParameters->StandardInput;
ProcessParameters->StandardOutput =
NtCurrentPeb()->ProcessParameters->StandardOutput;
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;
//
// Allocate memory in the new process to push the parameters
//
ParametersInNewProcess = NULL;
Status = NtAllocateVirtualMemory(
Process,
(PVOID *)&ParametersInNewProcess,
0,
&ParameterLength,
MEM_COMMIT,
PAGE_READWRITE
);
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;
}
//
// 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;
}
LPWSTR
BaseComputeProcessDllPath(
IN LPCWSTR ApplicationName,
IN LPVOID Environment
)
/*++
Routine Description:
This function computes a process DLL path.
Arguments:
ApplicationName - 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;
LPCWSTR p;
LPCWSTR pbase;
LPWSTR AllocatedPath;
ULONG AllocatedPathLength;
ULONG DefaultPathLength;
ULONG ApplicationPathLength;
PLDR_DATA_TABLE_ENTRY Entry;
PLIST_ENTRY Head,Next;
PVOID DllHandle;
LPWSTR pw;
LPWSTR DynamicAppendBuffer;
UNICODE_STRING PathEnv;
BOOLEAN GetLoaderLock;
DynamicAppendBuffer = NULL;
DefaultPathLength = BaseDefaultPath.Length;
if (DefaultPathLength == 0) {
return( NULL );
}
Status = RtlQueryEnvironmentVariable_U( Environment,
&BasePathVariableName,
&BaseDefaultPathAppend
);
if (NT_SUCCESS( Status )) {
DefaultPathLength += BaseDefaultPathAppend.Length;
}
else if ( Status == STATUS_BUFFER_TOO_SMALL ) {
//
// for large paths (~2k), the default path append buffer is
// too small so dynamically allocate a new path buffer
//
DynamicAppendBuffer = RtlAllocateHeap(
RtlProcessHeap(),
MAKE_TAG( TMP_TAG ),
BaseDefaultPathAppend.Length+sizeof(UNICODE_NULL)
);
if ( DynamicAppendBuffer ) {
PathEnv.Buffer = DynamicAppendBuffer;
PathEnv.Length = BaseDefaultPathAppend.Length+sizeof(UNICODE_NULL);
PathEnv.MaximumLength = BaseDefaultPathAppend.Length+sizeof(UNICODE_NULL);
Status = RtlQueryEnvironmentVariable_U( Environment,
&BasePathVariableName,
&PathEnv
);
if (NT_SUCCESS( Status )) {
DefaultPathLength += PathEnv.Length;
}
else {
RtlFreeHeap( RtlProcessHeap(), 0, DynamicAppendBuffer );
DynamicAppendBuffer = NULL;
}
}
}
//
// Determine the path that the program was created from
//
if ( ARGUMENT_PRESENT(ApplicationName) || BasepExeLdrEntry || (RtlGetPerThreadCurdir() && RtlGetPerThreadCurdir()->ImageName) ) {
GetLoaderLock = FALSE;
}
else {
GetLoaderLock = TRUE;
}
try {
if ( GetLoaderLock ) {
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
}
p = NULL;
if (!ARGUMENT_PRESENT(ApplicationName) ) {
if ( RtlGetPerThreadCurdir() && RtlGetPerThreadCurdir()->ImageName ) {
p = RtlGetPerThreadCurdir()->ImageName->Buffer;
}
else {
if ( BasepExeLdrEntry ) {
p = BasepExeLdrEntry->FullDllName.Buffer;
}
else {
DllHandle = (PVOID)NtCurrentPeb()->ImageBaseAddress;
Head = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
Next = Head->Flink;
while ( Next != Head ) {
Entry = CONTAINING_RECORD(Next,LDR_DATA_TABLE_ENTRY,InLoadOrderLinks);
if (DllHandle == (PVOID)Entry->DllBase ){
p = Entry->FullDllName.Buffer;
BasepExeLdrEntry = Entry;
break;
}
Next = Next->Flink;
}
}
}
}
else {
p = ApplicationName;
}
ApplicationPathLength = 0;
if ( p ) {
pbase = p;
while(*p) {
if ( *p == (WCHAR)'\\' ) {
ApplicationPathLength = (ULONG)p - (ULONG)pbase + sizeof(UNICODE_NULL);
}
p++;
}
}
AllocatedPathLength = DefaultPathLength + ApplicationPathLength + 2*sizeof(UNICODE_NULL);
AllocatedPath = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( TMP_TAG ), AllocatedPathLength);
if ( !AllocatedPath ) {
if ( DynamicAppendBuffer ) {
RtlFreeHeap( RtlProcessHeap(), 0, DynamicAppendBuffer );
}
return NULL;
}
if ( ApplicationPathLength != 0 ) {
if ( ApplicationPathLength != 6 ) {
ApplicationPathLength--; // strip trailing slash if not root
ApplicationPathLength--; // strip trailing slash if not root
}
RtlMoveMemory(AllocatedPath,pbase,ApplicationPathLength);
}
}
finally {
if ( GetLoaderLock ) {
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
}
}
pw = &AllocatedPath[ApplicationPathLength>>1];
if ( ApplicationPathLength != 0 ) {
*pw++ = (WCHAR)';';
}
if ( !DynamicAppendBuffer ) {
RtlMoveMemory( pw,
BaseDefaultPath.Buffer,
DefaultPathLength
);
}
else {
RtlMoveMemory( pw,
BaseDefaultPath.Buffer,
BaseDefaultPath.Length
);
RtlMoveMemory( &pw[BaseDefaultPath.Length>>1],
DynamicAppendBuffer,
PathEnv.Length
);
RtlFreeHeap( RtlProcessHeap(), 0, DynamicAppendBuffer );
}
pw[ DefaultPathLength>>1 ] = UNICODE_NULL;
return AllocatedPath;
}
NTSTATUS
NTAPI
Basep8BitStringToUnicodeString(
PUNICODE_STRING DestinationString,
PANSI_STRING SourceString,
BOOLEAN AllocateDestinationString
)
{
if ( BasepFileApisAreOem ) {
return RtlOemStringToUnicodeString(DestinationString,SourceString,AllocateDestinationString);
}
else {
return RtlAnsiStringToUnicodeString(DestinationString,SourceString,AllocateDestinationString);
}
}
NTSTATUS
NTAPI
BasepUnicodeStringTo8BitString(
PANSI_STRING DestinationString,
PUNICODE_STRING SourceString,
BOOLEAN AllocateDestinationString
)
{
if ( BasepFileApisAreOem ) {
return RtlUnicodeStringToOemString(DestinationString,SourceString,AllocateDestinationString);
}
else {
return RtlUnicodeStringToAnsiString(DestinationString,SourceString,AllocateDestinationString);
}
}
ULONG
BasepUnicodeStringTo8BitSize(
PUNICODE_STRING UnicodeString
)
{
if ( BasepFileApisAreOem ) {
return RtlUnicodeStringToOemSize(UnicodeString);
}
else {
return RtlUnicodeStringToAnsiSize(UnicodeString);
}
}
ULONG
Basep8BitStringToUnicodeSize(
PANSI_STRING AnsiString
)
{
if ( BasepFileApisAreOem ) {
return RtlOemStringToUnicodeSize((POEM_STRING)AnsiString);
}
else {
return RtlAnsiStringToUnicodeSize(AnsiString);
}
}
typedef struct _BASEP_ACQUIRE_STATE {
HANDLE Token;
PTOKEN_PRIVILEGES OldPrivileges;
PTOKEN_PRIVILEGES NewPrivileges;
BYTE OldPrivBuffer[ 1024 ];
} BASEP_ACQUIRE_STATE, *PBASEP_ACQUIRE_STATE;
//
// BUGBUG: this function is broken because it opens the process token
// instead of checking for a thread token. It is being left unfixed
// for the 3.51 release to avoid introducing instabilities in the
// CreateProcess and other APIs dealing with realtime priority.
// This routine should be removed and BasepAcquirePrivilegeEx should
// be renamed BasepAcquirePrivilege as soon as NT 3.51 ships.
// Mike Swift 5/19/95.
//
NTSTATUS
BasepAcquirePrivilege(
ULONG Privilege,
PVOID *ReturnedState
)
{
PBASEP_ACQUIRE_STATE State;
ULONG cbNeeded;
LUID LuidPrivilege;
NTSTATUS Status;
//
// 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;
}
Status = NtOpenProcessToken(
NtCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&State->Token
);
if ( !NT_SUCCESS( Status )) {
RtlFreeHeap( RtlProcessHeap(), 0, State );
return Status;
}
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 );
}
CloseHandle( State->Token );
RtlFreeHeap( RtlProcessHeap(), 0, State );
return Status;
}
*ReturnedState = State;
return STATUS_SUCCESS;
}
//
// 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;
//
// 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;
}
//
// 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 = NtOpenProcessToken(
NtCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&State->Token
);
if ( !NT_SUCCESS( Status )) {
RtlFreeHeap( RtlProcessHeap(), 0, State );
return Status;
}
}
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 );
}
CloseHandle( State->Token );
RtlFreeHeap( RtlProcessHeap(), 0, State );
return Status;
}
*ReturnedState = State;
return STATUS_SUCCESS;
}
VOID
BasepReleasePrivilege(
PVOID StatePointer
)
{
PBASEP_ACQUIRE_STATE State = (PBASEP_ACQUIRE_STATE)StatePointer;
NtAdjustPrivilegesToken( State->Token,
FALSE,
State->OldPrivileges,
0,
NULL,
NULL
);
if (State->OldPrivileges != (PTOKEN_PRIVILEGES)State->OldPrivBuffer) {
RtlFreeHeap( RtlProcessHeap(), 0, State->OldPrivileges );
}
CloseHandle( State->Token );
RtlFreeHeap( RtlProcessHeap(), 0, State );
return;
}