mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2483 lines
66 KiB
2483 lines
66 KiB
/*++
|
|
|
|
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;
|
|
}
|