1618 lines
44 KiB
C
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;
|
|
}
|