mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 01:55:19 +00:00
d0d86ab588
NtQueryInformationToken is by far the only system call in NT where ReturnLength simply cannot be optional. On Windows this parameter is always probed and an argument to NULL directly leads to an access violation exception. This is due to the fact of how tokens work, as its information contents (token user, owner, primary group, et al) are dynamic and can vary throughout over time in memory. What happens on current ReactOS master however is that ReturnLength is only probed if the parameter is not NULL. On a NULL case scenario the probing checks succeed and NtQueryInformationToken fails later. For this, just get rid of CompleteProbing parameter and opt in for a bit mask flag based approach, with ICIF_FORCE_RETURN_LENGTH_PROBE being set on DefaultQueryInfoBufferCheck which NtQueryInformationToken calls it to do sanity checks. In addition to that... - Document the ICIF probe helpers - Annotate the ICIF prope helpers with SAL - With the riddance of CompleteProbing and adoption of flags based approach, add ICIF_PROBE_READ_WRITE and ICIF_PROBE_READ flags alongside with ICIF_FORCE_RETURN_LENGTH_PROBE
3045 lines
102 KiB
C
3045 lines
102 KiB
C
/*
|
|
* PROJECT: ReactOS Kernel
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: ntoskrnl/ps/query.c
|
|
* PURPOSE: Process Manager: Thread/Process Query/Set Information
|
|
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
|
* Thomas Weidenmueller (w3seek@reactos.org)
|
|
* Eric Kohl
|
|
*/
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* Debugging Level */
|
|
ULONG PspTraceLevel = 0;
|
|
|
|
/* PRIVATE FUNCTIONS *********************************************************/
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
PsReferenceProcessFilePointer(IN PEPROCESS Process,
|
|
OUT PFILE_OBJECT *FileObject)
|
|
{
|
|
PSECTION Section;
|
|
PAGED_CODE();
|
|
|
|
/* Lock the process */
|
|
if (!ExAcquireRundownProtection(&Process->RundownProtect))
|
|
{
|
|
return STATUS_PROCESS_IS_TERMINATING;
|
|
}
|
|
|
|
/* Get the section */
|
|
Section = Process->SectionObject;
|
|
if (Section)
|
|
{
|
|
/* Get the file object and reference it */
|
|
*FileObject = MmGetFileObjectForSection((PVOID)Section);
|
|
ObReferenceObject(*FileObject);
|
|
}
|
|
|
|
/* Release the protection */
|
|
ExReleaseRundownProtection(&Process->RundownProtect);
|
|
|
|
/* Return status */
|
|
return Section ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
/* PUBLIC FUNCTIONS **********************************************************/
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtQueryInformationProcess(
|
|
_In_ HANDLE ProcessHandle,
|
|
_In_ PROCESSINFOCLASS ProcessInformationClass,
|
|
_Out_ PVOID ProcessInformation,
|
|
_In_ ULONG ProcessInformationLength,
|
|
_Out_opt_ PULONG ReturnLength)
|
|
{
|
|
PEPROCESS Process;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
NTSTATUS Status;
|
|
ULONG Length = 0;
|
|
HANDLE DebugPort = 0;
|
|
PPROCESS_BASIC_INFORMATION ProcessBasicInfo =
|
|
(PPROCESS_BASIC_INFORMATION)ProcessInformation;
|
|
PKERNEL_USER_TIMES ProcessTime = (PKERNEL_USER_TIMES)ProcessInformation;
|
|
ULONG UserTime, KernelTime;
|
|
PPROCESS_PRIORITY_CLASS PsPriorityClass = (PPROCESS_PRIORITY_CLASS)ProcessInformation;
|
|
ULONG HandleCount;
|
|
PPROCESS_SESSION_INFORMATION SessionInfo =
|
|
(PPROCESS_SESSION_INFORMATION)ProcessInformation;
|
|
PVM_COUNTERS VmCounters = (PVM_COUNTERS)ProcessInformation;
|
|
PIO_COUNTERS IoCounters = (PIO_COUNTERS)ProcessInformation;
|
|
PQUOTA_LIMITS QuotaLimits = (PQUOTA_LIMITS)ProcessInformation;
|
|
PUNICODE_STRING ImageName;
|
|
ULONG Cookie, ExecuteOptions = 0;
|
|
ULONG_PTR Wow64 = 0;
|
|
PROCESS_VALUES ProcessValues;
|
|
ULONG Flags;
|
|
PAGED_CODE();
|
|
|
|
/* Verify Information Class validity */
|
|
Status = DefaultQueryInfoBufferCheck(ProcessInformationClass,
|
|
PsProcessInfoClass,
|
|
RTL_NUMBER_OF(PsProcessInfoClass),
|
|
ICIF_PROBE_READ,
|
|
ProcessInformation,
|
|
ProcessInformationLength,
|
|
ReturnLength,
|
|
NULL,
|
|
PreviousMode);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("NtQueryInformationProcess(): Information verification class failed! (Status -> 0x%lx, ProcessInformationClass -> %lx)\n", Status, ProcessInformationClass);
|
|
return Status;
|
|
}
|
|
|
|
if (((ProcessInformationClass == ProcessCookie) ||
|
|
(ProcessInformationClass == ProcessImageInformation)) &&
|
|
(ProcessHandle != NtCurrentProcess()))
|
|
{
|
|
/*
|
|
* Retrieving the process cookie is only allowed for the calling process
|
|
* itself! XP only allows NtCurrentProcess() as process handles even if
|
|
* a real handle actually represents the current process.
|
|
*/
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Check the information class */
|
|
switch (ProcessInformationClass)
|
|
{
|
|
/* Basic process information */
|
|
case ProcessBasicInformation:
|
|
|
|
if (ProcessInformationLength != sizeof(PROCESS_BASIC_INFORMATION))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Set return length */
|
|
Length = sizeof(PROCESS_BASIC_INFORMATION);
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
PROCESS_QUERY_INFORMATION,
|
|
PsProcessType,
|
|
PreviousMode,
|
|
(PVOID*)&Process,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Protect writes with SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Write all the information from the EPROCESS/KPROCESS */
|
|
ProcessBasicInfo->ExitStatus = Process->ExitStatus;
|
|
ProcessBasicInfo->PebBaseAddress = Process->Peb;
|
|
ProcessBasicInfo->AffinityMask = Process->Pcb.Affinity;
|
|
ProcessBasicInfo->UniqueProcessId = (ULONG_PTR)Process->
|
|
UniqueProcessId;
|
|
ProcessBasicInfo->InheritedFromUniqueProcessId =
|
|
(ULONG_PTR)Process->InheritedFromUniqueProcessId;
|
|
ProcessBasicInfo->BasePriority = Process->Pcb.BasePriority;
|
|
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Dereference the process */
|
|
ObDereferenceObject(Process);
|
|
break;
|
|
|
|
/* Process quota limits */
|
|
case ProcessQuotaLimits:
|
|
|
|
if (ProcessInformationLength != sizeof(QUOTA_LIMITS))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
Length = sizeof(QUOTA_LIMITS);
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
PROCESS_QUERY_INFORMATION,
|
|
PsProcessType,
|
|
PreviousMode,
|
|
(PVOID*)&Process,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Indicate success */
|
|
Status = STATUS_SUCCESS;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
/* Set max/min working set sizes */
|
|
QuotaLimits->MaximumWorkingSetSize =
|
|
Process->Vm.MaximumWorkingSetSize << PAGE_SHIFT;
|
|
QuotaLimits->MinimumWorkingSetSize =
|
|
Process->Vm.MinimumWorkingSetSize << PAGE_SHIFT;
|
|
|
|
/* Set default time limits */
|
|
QuotaLimits->TimeLimit.LowPart = MAXULONG;
|
|
QuotaLimits->TimeLimit.HighPart = MAXULONG;
|
|
|
|
/* Is quota block a default one? */
|
|
if (Process->QuotaBlock == &PspDefaultQuotaBlock)
|
|
{
|
|
/* Set default pools and pagefile limits */
|
|
QuotaLimits->PagedPoolLimit = (SIZE_T)-1;
|
|
QuotaLimits->NonPagedPoolLimit = (SIZE_T)-1;
|
|
QuotaLimits->PagefileLimit = (SIZE_T)-1;
|
|
}
|
|
else
|
|
{
|
|
/* Get limits from non-default quota block */
|
|
QuotaLimits->PagedPoolLimit =
|
|
Process->QuotaBlock->QuotaEntry[PsPagedPool].Limit;
|
|
QuotaLimits->NonPagedPoolLimit =
|
|
Process->QuotaBlock->QuotaEntry[PsNonPagedPool].Limit;
|
|
QuotaLimits->PagefileLimit =
|
|
Process->QuotaBlock->QuotaEntry[PsPageFile].Limit;
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Dereference the process */
|
|
ObDereferenceObject(Process);
|
|
break;
|
|
|
|
case ProcessIoCounters:
|
|
|
|
if (ProcessInformationLength != sizeof(IO_COUNTERS))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
Length = sizeof(IO_COUNTERS);
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
PROCESS_QUERY_INFORMATION,
|
|
PsProcessType,
|
|
PreviousMode,
|
|
(PVOID*)&Process,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Query IO counters from the process */
|
|
KeQueryValuesProcess(&Process->Pcb, &ProcessValues);
|
|
|
|
_SEH2_TRY
|
|
{
|
|
RtlCopyMemory(IoCounters, &ProcessValues.IoInfo, sizeof(IO_COUNTERS));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Ignore exception */
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Set status to success in any case */
|
|
Status = STATUS_SUCCESS;
|
|
|
|
/* Dereference the process */
|
|
ObDereferenceObject(Process);
|
|
break;
|
|
|
|
/* Timing */
|
|
case ProcessTimes:
|
|
|
|
/* Set the return length */
|
|
if (ProcessInformationLength != sizeof(KERNEL_USER_TIMES))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
Length = sizeof(KERNEL_USER_TIMES);
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
PROCESS_QUERY_INFORMATION,
|
|
PsProcessType,
|
|
PreviousMode,
|
|
(PVOID*)&Process,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Protect writes with SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Copy time information from EPROCESS/KPROCESS */
|
|
KernelTime = KeQueryRuntimeProcess(&Process->Pcb, &UserTime);
|
|
ProcessTime->CreateTime = Process->CreateTime;
|
|
ProcessTime->UserTime.QuadPart = (LONGLONG)UserTime * KeMaximumIncrement;
|
|
ProcessTime->KernelTime.QuadPart = (LONGLONG)KernelTime * KeMaximumIncrement;
|
|
ProcessTime->ExitTime = Process->ExitTime;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Dereference the process */
|
|
ObDereferenceObject(Process);
|
|
break;
|
|
|
|
/* Process Debug Port */
|
|
case ProcessDebugPort:
|
|
|
|
if (ProcessInformationLength != sizeof(HANDLE))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Set return length */
|
|
Length = sizeof(HANDLE);
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
PROCESS_QUERY_INFORMATION,
|
|
PsProcessType,
|
|
PreviousMode,
|
|
(PVOID*)&Process,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Protect write with SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Return whether or not we have a debug port */
|
|
*(PHANDLE)ProcessInformation = (Process->DebugPort ?
|
|
(HANDLE)-1 : NULL);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Dereference the process */
|
|
ObDereferenceObject(Process);
|
|
break;
|
|
|
|
case ProcessHandleCount:
|
|
|
|
if (ProcessInformationLength != sizeof(ULONG))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Set the return length*/
|
|
Length = sizeof(ULONG);
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
PROCESS_QUERY_INFORMATION,
|
|
PsProcessType,
|
|
PreviousMode,
|
|
(PVOID*)&Process,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Count the number of handles this process has */
|
|
HandleCount = ObGetProcessHandleCount(Process);
|
|
|
|
/* Protect write in SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Return the count of handles */
|
|
*(PULONG)ProcessInformation = HandleCount;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Dereference the process */
|
|
ObDereferenceObject(Process);
|
|
break;
|
|
|
|
/* Session ID for the process */
|
|
case ProcessSessionInformation:
|
|
|
|
if (ProcessInformationLength != sizeof(PROCESS_SESSION_INFORMATION))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Set the return length*/
|
|
Length = sizeof(PROCESS_SESSION_INFORMATION);
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
PROCESS_QUERY_INFORMATION,
|
|
PsProcessType,
|
|
PreviousMode,
|
|
(PVOID*)&Process,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Enter SEH for write safety */
|
|
_SEH2_TRY
|
|
{
|
|
/* Write back the Session ID */
|
|
SessionInfo->SessionId = PsGetProcessSessionId(Process);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Dereference the process */
|
|
ObDereferenceObject(Process);
|
|
break;
|
|
|
|
/* Virtual Memory Statistics */
|
|
case ProcessVmCounters:
|
|
|
|
/* Validate the input length */
|
|
if ((ProcessInformationLength != sizeof(VM_COUNTERS)) &&
|
|
(ProcessInformationLength != sizeof(VM_COUNTERS_EX)))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
PROCESS_QUERY_INFORMATION,
|
|
PsProcessType,
|
|
PreviousMode,
|
|
(PVOID*)&Process,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Enter SEH for write safety */
|
|
_SEH2_TRY
|
|
{
|
|
/* Return data from EPROCESS */
|
|
VmCounters->PeakVirtualSize = Process->PeakVirtualSize;
|
|
VmCounters->VirtualSize = Process->VirtualSize;
|
|
VmCounters->PageFaultCount = Process->Vm.PageFaultCount;
|
|
VmCounters->PeakWorkingSetSize = Process->Vm.PeakWorkingSetSize;
|
|
VmCounters->WorkingSetSize = Process->Vm.WorkingSetSize;
|
|
VmCounters->QuotaPeakPagedPoolUsage = Process->QuotaPeak[PsPagedPool];
|
|
VmCounters->QuotaPagedPoolUsage = Process->QuotaUsage[PsPagedPool];
|
|
VmCounters->QuotaPeakNonPagedPoolUsage = Process->QuotaPeak[PsNonPagedPool];
|
|
VmCounters->QuotaNonPagedPoolUsage = Process->QuotaUsage[PsNonPagedPool];
|
|
VmCounters->PagefileUsage = Process->QuotaUsage[PsPageFile] << PAGE_SHIFT;
|
|
VmCounters->PeakPagefileUsage = Process->QuotaPeak[PsPageFile] << PAGE_SHIFT;
|
|
//VmCounters->PrivateUsage = Process->CommitCharge << PAGE_SHIFT;
|
|
//
|
|
|
|
/* Set the return length */
|
|
Length = ProcessInformationLength;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Dereference the process */
|
|
ObDereferenceObject(Process);
|
|
break;
|
|
|
|
/* Hard Error Processing Mode */
|
|
case ProcessDefaultHardErrorMode:
|
|
|
|
if (ProcessInformationLength != sizeof(ULONG))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Set the return length*/
|
|
Length = sizeof(ULONG);
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
PROCESS_QUERY_INFORMATION,
|
|
PsProcessType,
|
|
PreviousMode,
|
|
(PVOID*)&Process,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Enter SEH for writing back data */
|
|
_SEH2_TRY
|
|
{
|
|
/* Write the current processing mode */
|
|
*(PULONG)ProcessInformation = Process->
|
|
DefaultHardErrorProcessing;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Dereference the process */
|
|
ObDereferenceObject(Process);
|
|
break;
|
|
|
|
/* Priority Boosting status */
|
|
case ProcessPriorityBoost:
|
|
|
|
if (ProcessInformationLength != sizeof(ULONG))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Set the return length */
|
|
Length = sizeof(ULONG);
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
PROCESS_QUERY_INFORMATION,
|
|
PsProcessType,
|
|
PreviousMode,
|
|
(PVOID*)&Process,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Enter SEH for writing back data */
|
|
_SEH2_TRY
|
|
{
|
|
/* Return boost status */
|
|
*(PULONG)ProcessInformation = Process->Pcb.DisableBoost ?
|
|
TRUE : FALSE;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Dereference the process */
|
|
ObDereferenceObject(Process);
|
|
break;
|
|
|
|
/* DOS Device Map */
|
|
case ProcessDeviceMap:
|
|
|
|
if (ProcessInformationLength == sizeof(PROCESS_DEVICEMAP_INFORMATION_EX))
|
|
{
|
|
/* Protect read in SEH */
|
|
_SEH2_TRY
|
|
{
|
|
PPROCESS_DEVICEMAP_INFORMATION_EX DeviceMapEx = ProcessInformation;
|
|
|
|
Flags = DeviceMapEx->Flags;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Only one flag is supported and it needs LUID mappings */
|
|
if ((Flags & ~PROCESS_LUID_DOSDEVICES_ONLY) != 0 ||
|
|
!ObIsLUIDDeviceMapsEnabled())
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* This has to be the size of the Query union field for x64 compatibility! */
|
|
if (ProcessInformationLength != RTL_FIELD_SIZE(PROCESS_DEVICEMAP_INFORMATION, Query))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* No flags for standard call */
|
|
Flags = 0;
|
|
}
|
|
|
|
/* Set the return length */
|
|
Length = ProcessInformationLength;
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
PROCESS_QUERY_INFORMATION,
|
|
PsProcessType,
|
|
PreviousMode,
|
|
(PVOID*)&Process,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Query the device map information */
|
|
Status = ObQueryDeviceMapInformation(Process,
|
|
ProcessInformation,
|
|
Flags);
|
|
|
|
/* Dereference the process */
|
|
ObDereferenceObject(Process);
|
|
break;
|
|
|
|
/* Priority class */
|
|
case ProcessPriorityClass:
|
|
|
|
if (ProcessInformationLength != sizeof(PROCESS_PRIORITY_CLASS))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Set the return length*/
|
|
Length = sizeof(PROCESS_PRIORITY_CLASS);
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
PROCESS_QUERY_INFORMATION,
|
|
PsProcessType,
|
|
PreviousMode,
|
|
(PVOID*)&Process,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Enter SEH for writing back data */
|
|
_SEH2_TRY
|
|
{
|
|
/* Return current priority class */
|
|
PsPriorityClass->PriorityClass = Process->PriorityClass;
|
|
PsPriorityClass->Foreground = FALSE;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Dereference the process */
|
|
ObDereferenceObject(Process);
|
|
break;
|
|
|
|
case ProcessImageFileName:
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
PROCESS_QUERY_INFORMATION,
|
|
PsProcessType,
|
|
PreviousMode,
|
|
(PVOID*)&Process,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Get the image path */
|
|
Status = SeLocateProcessImageName(Process, &ImageName);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Set return length */
|
|
Length = ImageName->MaximumLength +
|
|
sizeof(OBJECT_NAME_INFORMATION);
|
|
|
|
/* Make sure it's large enough */
|
|
if (Length <= ProcessInformationLength)
|
|
{
|
|
/* Enter SEH to protect write */
|
|
_SEH2_TRY
|
|
{
|
|
/* Copy it */
|
|
RtlCopyMemory(ProcessInformation,
|
|
ImageName,
|
|
Length);
|
|
|
|
/* Update pointer */
|
|
((PUNICODE_STRING)ProcessInformation)->Buffer =
|
|
(PWSTR)((PUNICODE_STRING)ProcessInformation + 1);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
else
|
|
{
|
|
/* Buffer too small */
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
|
|
/* Free the image path */
|
|
ExFreePoolWithTag(ImageName, TAG_SEPA);
|
|
}
|
|
/* Dereference the process */
|
|
ObDereferenceObject(Process);
|
|
break;
|
|
|
|
case ProcessDebugFlags:
|
|
|
|
if (ProcessInformationLength != sizeof(ULONG))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Set the return length*/
|
|
Length = sizeof(ULONG);
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
PROCESS_QUERY_INFORMATION,
|
|
PsProcessType,
|
|
PreviousMode,
|
|
(PVOID*)&Process,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Enter SEH for writing back data */
|
|
_SEH2_TRY
|
|
{
|
|
/* Return the debug flag state */
|
|
*(PULONG)ProcessInformation = Process->NoDebugInherit ? 0 : 1;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Dereference the process */
|
|
ObDereferenceObject(Process);
|
|
break;
|
|
|
|
case ProcessBreakOnTermination:
|
|
|
|
if (ProcessInformationLength != sizeof(ULONG))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Set the return length */
|
|
Length = sizeof(ULONG);
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
PROCESS_QUERY_INFORMATION,
|
|
PsProcessType,
|
|
PreviousMode,
|
|
(PVOID*)&Process,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Enter SEH for writing back data */
|
|
_SEH2_TRY
|
|
{
|
|
/* Return the BreakOnTermination state */
|
|
*(PULONG)ProcessInformation = Process->BreakOnTermination;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Dereference the process */
|
|
ObDereferenceObject(Process);
|
|
break;
|
|
|
|
/* Per-process security cookie */
|
|
case ProcessCookie:
|
|
|
|
if (ProcessInformationLength != sizeof(ULONG))
|
|
{
|
|
/* Length size wrong, bail out */
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Get the current process and cookie */
|
|
Process = PsGetCurrentProcess();
|
|
Cookie = Process->Cookie;
|
|
if (!Cookie)
|
|
{
|
|
LARGE_INTEGER SystemTime;
|
|
ULONG NewCookie;
|
|
PKPRCB Prcb;
|
|
|
|
/* Generate a new cookie */
|
|
KeQuerySystemTime(&SystemTime);
|
|
Prcb = KeGetCurrentPrcb();
|
|
NewCookie = Prcb->KeSystemCalls ^ Prcb->InterruptTime ^
|
|
SystemTime.u.LowPart ^ SystemTime.u.HighPart;
|
|
|
|
/* Set the new cookie or return the current one */
|
|
Cookie = InterlockedCompareExchange((LONG*)&Process->Cookie,
|
|
NewCookie,
|
|
Cookie);
|
|
if (!Cookie) Cookie = NewCookie;
|
|
|
|
/* Set return length */
|
|
Length = sizeof(ULONG);
|
|
}
|
|
|
|
/* Indicate success */
|
|
Status = STATUS_SUCCESS;
|
|
|
|
/* Enter SEH to protect write */
|
|
_SEH2_TRY
|
|
{
|
|
/* Write back the cookie */
|
|
*(PULONG)ProcessInformation = Cookie;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
break;
|
|
|
|
case ProcessImageInformation:
|
|
|
|
if (ProcessInformationLength != sizeof(SECTION_IMAGE_INFORMATION))
|
|
{
|
|
/* Break out */
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Set the length required and validate it */
|
|
Length = sizeof(SECTION_IMAGE_INFORMATION);
|
|
|
|
/* Enter SEH to protect write */
|
|
_SEH2_TRY
|
|
{
|
|
MmGetImageInformation((PSECTION_IMAGE_INFORMATION)ProcessInformation);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Indicate success */
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case ProcessDebugObjectHandle:
|
|
|
|
if (ProcessInformationLength != sizeof(HANDLE))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Set the return length */
|
|
Length = sizeof(HANDLE);
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
PROCESS_QUERY_INFORMATION,
|
|
PsProcessType,
|
|
PreviousMode,
|
|
(PVOID*)&Process,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Get the debug port */
|
|
Status = DbgkOpenProcessDebugPort(Process, PreviousMode, &DebugPort);
|
|
|
|
/* Let go of the process */
|
|
ObDereferenceObject(Process);
|
|
|
|
/* Protect write in SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Return debug port's handle */
|
|
*(PHANDLE)ProcessInformation = DebugPort;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
break;
|
|
|
|
case ProcessHandleTracing:
|
|
DPRINT1("Handle tracing Not implemented: %lx\n", ProcessInformationClass);
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
|
|
case ProcessLUIDDeviceMapsEnabled:
|
|
|
|
if (ProcessInformationLength != sizeof(ULONG))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Set the return length */
|
|
Length = sizeof(ULONG);
|
|
|
|
/* Indicate success */
|
|
Status = STATUS_SUCCESS;
|
|
|
|
/* Protect write in SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Query Ob */
|
|
*(PULONG)ProcessInformation = ObIsLUIDDeviceMapsEnabled();
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
break;
|
|
|
|
case ProcessWx86Information:
|
|
|
|
if (ProcessInformationLength != sizeof(ULONG))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Set the return length */
|
|
Length = sizeof(ULONG);
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
PROCESS_QUERY_INFORMATION,
|
|
PsProcessType,
|
|
PreviousMode,
|
|
(PVOID*)&Process,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Protect write in SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Return if the flag is set */
|
|
*(PULONG)ProcessInformation = (ULONG)Process->VdmAllowed;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Dereference the process */
|
|
ObDereferenceObject(Process);
|
|
break;
|
|
|
|
case ProcessWow64Information:
|
|
|
|
if (ProcessInformationLength != sizeof(ULONG_PTR))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Set return length */
|
|
Length = sizeof(ULONG_PTR);
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
PROCESS_QUERY_INFORMATION,
|
|
PsProcessType,
|
|
PreviousMode,
|
|
(PVOID*)&Process,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Make sure the process isn't dying */
|
|
if (ExAcquireRundownProtection(&Process->RundownProtect))
|
|
{
|
|
/* Get the WOW64 process structure */
|
|
#ifdef _WIN64
|
|
Wow64 = (ULONG_PTR)Process->Wow64Process;
|
|
#else
|
|
Wow64 = 0;
|
|
#endif
|
|
/* Release the lock */
|
|
ExReleaseRundownProtection(&Process->RundownProtect);
|
|
}
|
|
|
|
/* Protect write with SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Return whether or not we have a debug port */
|
|
*(PULONG_PTR)ProcessInformation = Wow64;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Dereference the process */
|
|
ObDereferenceObject(Process);
|
|
break;
|
|
|
|
case ProcessExecuteFlags:
|
|
|
|
if (ProcessInformationLength != sizeof(ULONG))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Set return length */
|
|
Length = sizeof(ULONG);
|
|
|
|
if (ProcessHandle != NtCurrentProcess())
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Get the options */
|
|
Status = MmGetExecuteOptions(&ExecuteOptions);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Protect write with SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Return them */
|
|
*(PULONG)ProcessInformation = ExecuteOptions;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
break;
|
|
|
|
case ProcessLdtInformation:
|
|
DPRINT1("VDM/16-bit not implemented: %lx\n", ProcessInformationClass);
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
|
|
case ProcessWorkingSetWatch:
|
|
DPRINT1("WS Watch Not implemented: %lx\n", ProcessInformationClass);
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
|
|
case ProcessPooledUsageAndLimits:
|
|
DPRINT1("Pool limits Not implemented: %lx\n", ProcessInformationClass);
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
|
|
/* Not supported by Server 2003 */
|
|
default:
|
|
DPRINT1("Unsupported info class: %lx\n", ProcessInformationClass);
|
|
Status = STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
|
|
/* Protect write with SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Check if caller wanted return length */
|
|
if ((ReturnLength) && (Length)) *ReturnLength = Length;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtSetInformationProcess(IN HANDLE ProcessHandle,
|
|
IN PROCESSINFOCLASS ProcessInformationClass,
|
|
IN PVOID ProcessInformation,
|
|
IN ULONG ProcessInformationLength)
|
|
{
|
|
PEPROCESS Process;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
ACCESS_MASK Access;
|
|
NTSTATUS Status;
|
|
HANDLE PortHandle = NULL;
|
|
HANDLE TokenHandle = NULL;
|
|
HANDLE DirectoryHandle = NULL;
|
|
PROCESS_SESSION_INFORMATION SessionInfo = {0};
|
|
PROCESS_PRIORITY_CLASS PriorityClass = {0};
|
|
PROCESS_FOREGROUND_BACKGROUND Foreground = {0};
|
|
PVOID ExceptionPort;
|
|
ULONG Break;
|
|
KAFFINITY ValidAffinity, Affinity = 0;
|
|
KPRIORITY BasePriority = 0;
|
|
UCHAR MemoryPriority = 0;
|
|
BOOLEAN DisableBoost = 0;
|
|
ULONG DefaultHardErrorMode = 0;
|
|
ULONG DebugFlags = 0, EnableFixup = 0, Boost = 0;
|
|
ULONG NoExecute = 0, VdmPower = 0;
|
|
BOOLEAN HasPrivilege;
|
|
PLIST_ENTRY Next;
|
|
PETHREAD Thread;
|
|
PAGED_CODE();
|
|
|
|
/* Verify Information Class validity */
|
|
Status = DefaultSetInfoBufferCheck(ProcessInformationClass,
|
|
PsProcessInfoClass,
|
|
RTL_NUMBER_OF(PsProcessInfoClass),
|
|
ProcessInformation,
|
|
ProcessInformationLength,
|
|
PreviousMode);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("NtSetInformationProcess(): Information verification class failed! (Status -> 0x%lx, ProcessInformationClass -> %lx)\n", Status, ProcessInformationClass);
|
|
return Status;
|
|
}
|
|
|
|
/* Check what class this is */
|
|
Access = PROCESS_SET_INFORMATION;
|
|
if (ProcessInformationClass == ProcessSessionInformation)
|
|
{
|
|
/* Setting the Session ID needs a special mask */
|
|
Access |= PROCESS_SET_SESSIONID;
|
|
}
|
|
else if (ProcessInformationClass == ProcessExceptionPort)
|
|
{
|
|
/* Setting the exception port needs a special mask */
|
|
Access |= PROCESS_SUSPEND_RESUME;
|
|
}
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ProcessHandle,
|
|
Access,
|
|
PsProcessType,
|
|
PreviousMode,
|
|
(PVOID*)&Process,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Check what kind of information class this is */
|
|
switch (ProcessInformationClass)
|
|
{
|
|
case ProcessWx86Information:
|
|
|
|
/* Check buffer length */
|
|
if (ProcessInformationLength != sizeof(ULONG))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Use SEH for capture */
|
|
_SEH2_TRY
|
|
{
|
|
/* Capture the boolean */
|
|
VdmPower = *(PULONG)ProcessInformation;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Getting VDM powers requires the SeTcbPrivilege */
|
|
if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
|
|
{
|
|
/* We don't hold the privilege, bail out */
|
|
Status = STATUS_PRIVILEGE_NOT_HELD;
|
|
DPRINT1("Need TCB privilege\n");
|
|
break;
|
|
}
|
|
|
|
/* Set or clear the flag */
|
|
if (VdmPower)
|
|
{
|
|
PspSetProcessFlag(Process, PSF_VDM_ALLOWED_BIT);
|
|
}
|
|
else
|
|
{
|
|
PspClearProcessFlag(Process, PSF_VDM_ALLOWED_BIT);
|
|
}
|
|
break;
|
|
|
|
/* Error/Exception Port */
|
|
case ProcessExceptionPort:
|
|
|
|
/* Check buffer length */
|
|
if (ProcessInformationLength != sizeof(HANDLE))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Use SEH for capture */
|
|
_SEH2_TRY
|
|
{
|
|
/* Capture the handle */
|
|
PortHandle = *(PHANDLE)ProcessInformation;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Setting the error port requires the SeTcbPrivilege */
|
|
if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
|
|
{
|
|
/* We don't hold the privilege, bail out */
|
|
Status = STATUS_PRIVILEGE_NOT_HELD;
|
|
break;
|
|
}
|
|
|
|
/* Get the LPC Port */
|
|
Status = ObReferenceObjectByHandle(PortHandle,
|
|
0,
|
|
LpcPortObjectType,
|
|
PreviousMode,
|
|
(PVOID)&ExceptionPort,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Change the pointer */
|
|
if (InterlockedCompareExchangePointer(&Process->ExceptionPort,
|
|
ExceptionPort,
|
|
NULL))
|
|
{
|
|
/* We already had one, fail */
|
|
ObDereferenceObject(ExceptionPort);
|
|
Status = STATUS_PORT_ALREADY_SET;
|
|
}
|
|
break;
|
|
|
|
/* Security Token */
|
|
case ProcessAccessToken:
|
|
|
|
/* Check buffer length */
|
|
if (ProcessInformationLength != sizeof(PROCESS_ACCESS_TOKEN))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Use SEH for capture */
|
|
_SEH2_TRY
|
|
{
|
|
/* Save the token handle */
|
|
TokenHandle = ((PPROCESS_ACCESS_TOKEN)ProcessInformation)->
|
|
Token;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Assign the actual token */
|
|
Status = PspSetPrimaryToken(Process, TokenHandle, NULL);
|
|
break;
|
|
|
|
/* Hard error processing */
|
|
case ProcessDefaultHardErrorMode:
|
|
|
|
/* Check buffer length */
|
|
if (ProcessInformationLength != sizeof(ULONG))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Enter SEH for direct buffer read */
|
|
_SEH2_TRY
|
|
{
|
|
DefaultHardErrorMode = *(PULONG)ProcessInformation;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Set the mode */
|
|
Process->DefaultHardErrorProcessing = DefaultHardErrorMode;
|
|
|
|
/* Call Ke for the update */
|
|
if (DefaultHardErrorMode & SEM_NOALIGNMENTFAULTEXCEPT)
|
|
{
|
|
KeSetAutoAlignmentProcess(&Process->Pcb, TRUE);
|
|
}
|
|
else
|
|
{
|
|
KeSetAutoAlignmentProcess(&Process->Pcb, FALSE);
|
|
}
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
/* Session ID */
|
|
case ProcessSessionInformation:
|
|
|
|
/* Check buffer length */
|
|
if (ProcessInformationLength != sizeof(PROCESS_SESSION_INFORMATION))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Enter SEH for capture */
|
|
_SEH2_TRY
|
|
{
|
|
/* Capture the caller's buffer */
|
|
SessionInfo = *(PPROCESS_SESSION_INFORMATION)ProcessInformation;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Setting the session id requires the SeTcbPrivilege */
|
|
if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
|
|
{
|
|
/* We don't hold the privilege, bail out */
|
|
Status = STATUS_PRIVILEGE_NOT_HELD;
|
|
break;
|
|
}
|
|
|
|
#if 0 // OLD AND DEPRECATED CODE!!!!
|
|
|
|
/* FIXME - update the session id for the process token */
|
|
//Status = PsLockProcess(Process, FALSE);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Write the session ID in the EPROCESS */
|
|
Process->Session = UlongToPtr(SessionInfo.SessionId); // HACK!!!
|
|
|
|
/* Check if the process also has a PEB */
|
|
if (Process->Peb)
|
|
{
|
|
/*
|
|
* Attach to the process to make sure we're in the right
|
|
* context to access the PEB structure
|
|
*/
|
|
KeAttachProcess(&Process->Pcb);
|
|
|
|
/* Enter SEH for write to user-mode PEB */
|
|
_SEH2_TRY
|
|
{
|
|
/* Write the session ID */
|
|
Process->Peb->SessionId = SessionInfo.SessionId;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Detach from the process */
|
|
KeDetachProcess();
|
|
}
|
|
|
|
/* Unlock the process */
|
|
//PsUnlockProcess(Process);
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Since we cannot change the session ID of the given
|
|
* process anymore because it is set once and for all
|
|
* at process creation time and because it is stored
|
|
* inside the Process->Session structure managed by MM,
|
|
* we fake changing it: we just return success if the
|
|
* user-defined value is the same as the session ID of
|
|
* the process, and otherwise we fail.
|
|
*/
|
|
if (SessionInfo.SessionId == PsGetProcessSessionId(Process))
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
break;
|
|
|
|
case ProcessPriorityClass:
|
|
|
|
/* Check buffer length */
|
|
if (ProcessInformationLength != sizeof(PROCESS_PRIORITY_CLASS))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Enter SEH for capture */
|
|
_SEH2_TRY
|
|
{
|
|
/* Capture the caller's buffer */
|
|
PriorityClass = *(PPROCESS_PRIORITY_CLASS)ProcessInformation;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Check for invalid PriorityClass value */
|
|
if (PriorityClass.PriorityClass > PROCESS_PRIORITY_CLASS_ABOVE_NORMAL)
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
if ((PriorityClass.PriorityClass != Process->PriorityClass) &&
|
|
(PriorityClass.PriorityClass == PROCESS_PRIORITY_CLASS_REALTIME))
|
|
{
|
|
/* Check the privilege */
|
|
HasPrivilege = SeCheckPrivilegedObject(SeIncreaseBasePriorityPrivilege,
|
|
ProcessHandle,
|
|
PROCESS_SET_INFORMATION,
|
|
PreviousMode);
|
|
if (!HasPrivilege)
|
|
{
|
|
ObDereferenceObject(Process);
|
|
DPRINT1("Privilege to change priority to realtime lacking\n");
|
|
return STATUS_PRIVILEGE_NOT_HELD;
|
|
}
|
|
}
|
|
|
|
/* Check if we have a job */
|
|
if (Process->Job)
|
|
{
|
|
DPRINT1("Jobs not yet supported\n");
|
|
}
|
|
|
|
/* Set process priority class */
|
|
Process->PriorityClass = PriorityClass.PriorityClass;
|
|
|
|
/* Set process priority mode (foreground or background) */
|
|
PsSetProcessPriorityByClass(Process,
|
|
PriorityClass.Foreground ?
|
|
PsProcessPriorityForeground :
|
|
PsProcessPriorityBackground);
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case ProcessForegroundInformation:
|
|
|
|
/* Check buffer length */
|
|
if (ProcessInformationLength != sizeof(PROCESS_FOREGROUND_BACKGROUND))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Enter SEH for capture */
|
|
_SEH2_TRY
|
|
{
|
|
/* Capture the caller's buffer */
|
|
Foreground = *(PPROCESS_FOREGROUND_BACKGROUND)ProcessInformation;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Set process priority mode (foreground or background) */
|
|
PsSetProcessPriorityByClass(Process,
|
|
Foreground.Foreground ?
|
|
PsProcessPriorityForeground :
|
|
PsProcessPriorityBackground);
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case ProcessBasePriority:
|
|
|
|
/* Validate input length */
|
|
if (ProcessInformationLength != sizeof(KPRIORITY))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Enter SEH for direct buffer read */
|
|
_SEH2_TRY
|
|
{
|
|
BasePriority = *(KPRIORITY*)ProcessInformation;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Break = 0;
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Extract the memory priority out of there */
|
|
if (BasePriority & 0x80000000)
|
|
{
|
|
MemoryPriority = MEMORY_PRIORITY_FOREGROUND;
|
|
BasePriority &= ~0x80000000;
|
|
}
|
|
else
|
|
{
|
|
MemoryPriority = MEMORY_PRIORITY_BACKGROUND;
|
|
}
|
|
|
|
/* Validate the number */
|
|
if ((BasePriority > HIGH_PRIORITY) || (BasePriority <= LOW_PRIORITY))
|
|
{
|
|
ObDereferenceObject(Process);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Check if the new base is higher */
|
|
if (BasePriority > Process->Pcb.BasePriority)
|
|
{
|
|
HasPrivilege = SeCheckPrivilegedObject(SeIncreaseBasePriorityPrivilege,
|
|
ProcessHandle,
|
|
PROCESS_SET_INFORMATION,
|
|
PreviousMode);
|
|
if (!HasPrivilege)
|
|
{
|
|
ObDereferenceObject(Process);
|
|
DPRINT1("Privilege to change priority from %lx to %lx lacking\n", BasePriority, Process->Pcb.BasePriority);
|
|
return STATUS_PRIVILEGE_NOT_HELD;
|
|
}
|
|
}
|
|
|
|
/* Call Ke */
|
|
KeSetPriorityAndQuantumProcess(&Process->Pcb, BasePriority, 0);
|
|
|
|
/* Now set the memory priority */
|
|
MmSetMemoryPriorityProcess(Process, MemoryPriority);
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case ProcessRaisePriority:
|
|
|
|
/* Validate input length */
|
|
if (ProcessInformationLength != sizeof(ULONG))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Enter SEH for direct buffer read */
|
|
_SEH2_TRY
|
|
{
|
|
Boost = *(PULONG)ProcessInformation;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Break = 0;
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Make sure the process isn't dying */
|
|
if (ExAcquireRundownProtection(&Process->RundownProtect))
|
|
{
|
|
/* Lock it */
|
|
KeEnterCriticalRegion();
|
|
ExAcquirePushLockShared(&Process->ProcessLock);
|
|
|
|
/* Loop the threads */
|
|
for (Next = Process->ThreadListHead.Flink;
|
|
Next != &Process->ThreadListHead;
|
|
Next = Next->Flink)
|
|
{
|
|
/* Call Ke for the thread */
|
|
Thread = CONTAINING_RECORD(Next, ETHREAD, ThreadListEntry);
|
|
KeBoostPriorityThread(&Thread->Tcb, Boost);
|
|
}
|
|
|
|
/* Release the lock and rundown */
|
|
ExReleasePushLockShared(&Process->ProcessLock);
|
|
KeLeaveCriticalRegion();
|
|
ExReleaseRundownProtection(&Process->RundownProtect);
|
|
|
|
/* Set success code */
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
/* Avoid race conditions */
|
|
Status = STATUS_PROCESS_IS_TERMINATING;
|
|
}
|
|
break;
|
|
|
|
case ProcessBreakOnTermination:
|
|
|
|
/* Check buffer length */
|
|
if (ProcessInformationLength != sizeof(ULONG))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Enter SEH for direct buffer read */
|
|
_SEH2_TRY
|
|
{
|
|
Break = *(PULONG)ProcessInformation;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Break = 0;
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Setting 'break on termination' requires the SeDebugPrivilege */
|
|
if (!SeSinglePrivilegeCheck(SeDebugPrivilege, PreviousMode))
|
|
{
|
|
/* We don't hold the privilege, bail out */
|
|
Status = STATUS_PRIVILEGE_NOT_HELD;
|
|
break;
|
|
}
|
|
|
|
/* Set or clear the flag */
|
|
if (Break)
|
|
{
|
|
PspSetProcessFlag(Process, PSF_BREAK_ON_TERMINATION_BIT);
|
|
}
|
|
else
|
|
{
|
|
PspClearProcessFlag(Process, PSF_BREAK_ON_TERMINATION_BIT);
|
|
}
|
|
|
|
break;
|
|
|
|
case ProcessAffinityMask:
|
|
|
|
/* Check buffer length */
|
|
if (ProcessInformationLength != sizeof(KAFFINITY))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Enter SEH for direct buffer read */
|
|
_SEH2_TRY
|
|
{
|
|
Affinity = *(PKAFFINITY)ProcessInformation;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Break = 0;
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Make sure it's valid for the CPUs present */
|
|
ValidAffinity = Affinity & KeActiveProcessors;
|
|
if (!Affinity || (ValidAffinity != Affinity))
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
/* Check if it's within job affinity limits */
|
|
if (Process->Job)
|
|
{
|
|
/* Not yet implemented */
|
|
UNIMPLEMENTED;
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
}
|
|
|
|
/* Make sure the process isn't dying */
|
|
if (ExAcquireRundownProtection(&Process->RundownProtect))
|
|
{
|
|
/* Lock it */
|
|
KeEnterCriticalRegion();
|
|
ExAcquirePushLockShared(&Process->ProcessLock);
|
|
|
|
/* Call Ke to do the work */
|
|
KeSetAffinityProcess(&Process->Pcb, ValidAffinity);
|
|
|
|
/* Release the lock and rundown */
|
|
ExReleasePushLockShared(&Process->ProcessLock);
|
|
KeLeaveCriticalRegion();
|
|
ExReleaseRundownProtection(&Process->RundownProtect);
|
|
|
|
/* Set success code */
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
/* Avoid race conditions */
|
|
Status = STATUS_PROCESS_IS_TERMINATING;
|
|
}
|
|
break;
|
|
|
|
/* Priority Boosting status */
|
|
case ProcessPriorityBoost:
|
|
|
|
/* Validate input length */
|
|
if (ProcessInformationLength != sizeof(ULONG))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Enter SEH for direct buffer read */
|
|
_SEH2_TRY
|
|
{
|
|
DisableBoost = *(PBOOLEAN)ProcessInformation;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Break = 0;
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Make sure the process isn't dying */
|
|
if (ExAcquireRundownProtection(&Process->RundownProtect))
|
|
{
|
|
/* Lock it */
|
|
KeEnterCriticalRegion();
|
|
ExAcquirePushLockShared(&Process->ProcessLock);
|
|
|
|
/* Call Ke to do the work */
|
|
KeSetDisableBoostProcess(&Process->Pcb, DisableBoost);
|
|
|
|
/* Loop the threads too */
|
|
for (Next = Process->ThreadListHead.Flink;
|
|
Next != &Process->ThreadListHead;
|
|
Next = Next->Flink)
|
|
{
|
|
/* Call Ke for the thread */
|
|
Thread = CONTAINING_RECORD(Next, ETHREAD, ThreadListEntry);
|
|
KeSetDisableBoostThread(&Thread->Tcb, DisableBoost);
|
|
}
|
|
|
|
/* Release the lock and rundown */
|
|
ExReleasePushLockShared(&Process->ProcessLock);
|
|
KeLeaveCriticalRegion();
|
|
ExReleaseRundownProtection(&Process->RundownProtect);
|
|
|
|
/* Set success code */
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
/* Avoid race conditions */
|
|
Status = STATUS_PROCESS_IS_TERMINATING;
|
|
}
|
|
break;
|
|
|
|
case ProcessDebugFlags:
|
|
|
|
/* Check buffer length */
|
|
if (ProcessInformationLength != sizeof(ULONG))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Enter SEH for direct buffer read */
|
|
_SEH2_TRY
|
|
{
|
|
DebugFlags = *(PULONG)ProcessInformation;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Set the mode */
|
|
if (DebugFlags & ~1)
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
if (DebugFlags & 1)
|
|
{
|
|
PspClearProcessFlag(Process, PSF_NO_DEBUG_INHERIT_BIT);
|
|
}
|
|
else
|
|
{
|
|
PspSetProcessFlag(Process, PSF_NO_DEBUG_INHERIT_BIT);
|
|
}
|
|
}
|
|
|
|
/* Done */
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case ProcessEnableAlignmentFaultFixup:
|
|
|
|
/* Check buffer length */
|
|
if (ProcessInformationLength != sizeof(BOOLEAN))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Enter SEH for direct buffer read */
|
|
_SEH2_TRY
|
|
{
|
|
EnableFixup = *(PULONG)ProcessInformation;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Set the mode */
|
|
if (EnableFixup)
|
|
{
|
|
Process->DefaultHardErrorProcessing |= SEM_NOALIGNMENTFAULTEXCEPT;
|
|
}
|
|
else
|
|
{
|
|
Process->DefaultHardErrorProcessing &= ~SEM_NOALIGNMENTFAULTEXCEPT;
|
|
}
|
|
|
|
/* Call Ke for the update */
|
|
KeSetAutoAlignmentProcess(&Process->Pcb, FALSE);
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case ProcessUserModeIOPL:
|
|
|
|
/* Only TCB can do this */
|
|
if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
|
|
{
|
|
/* We don't hold the privilege, bail out */
|
|
DPRINT1("Need TCB to set IOPL\n");
|
|
Status = STATUS_PRIVILEGE_NOT_HELD;
|
|
break;
|
|
}
|
|
|
|
/* Only supported on x86 */
|
|
#if defined (_X86_)
|
|
Ke386SetIOPL();
|
|
#elif defined(_M_AMD64)
|
|
/* On x64 this function isn't implemented.
|
|
On Windows 2003 it returns success.
|
|
On Vista+ it returns STATUS_NOT_IMPLEMENTED. */
|
|
if ((ExGetPreviousMode() != KernelMode) &&
|
|
(RtlRosGetAppcompatVersion() > _WIN32_WINNT_WS03))
|
|
{
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
#else
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
#endif
|
|
/* Done */
|
|
break;
|
|
|
|
case ProcessExecuteFlags:
|
|
|
|
/* Check buffer length */
|
|
if (ProcessInformationLength != sizeof(ULONG))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
if (ProcessHandle != NtCurrentProcess())
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
/* Enter SEH for direct buffer read */
|
|
_SEH2_TRY
|
|
{
|
|
NoExecute = *(PULONG)ProcessInformation;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Call Mm for the update */
|
|
Status = MmSetExecuteOptions(NoExecute);
|
|
break;
|
|
|
|
case ProcessDeviceMap:
|
|
|
|
/* Check buffer length */
|
|
if (ProcessInformationLength != sizeof(HANDLE))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Use SEH for capture */
|
|
_SEH2_TRY
|
|
{
|
|
/* Capture the handle */
|
|
DirectoryHandle = *(PHANDLE)ProcessInformation;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Call Ob to set the device map */
|
|
Status = ObSetDeviceMap(Process, DirectoryHandle);
|
|
break;
|
|
|
|
|
|
/* We currently don't implement any of these */
|
|
case ProcessLdtInformation:
|
|
case ProcessLdtSize:
|
|
case ProcessIoPortHandlers:
|
|
DPRINT1("VDM/16-bit Request not implemented: %lx\n", ProcessInformationClass);
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
|
|
case ProcessQuotaLimits:
|
|
|
|
Status = PspSetQuotaLimits(Process,
|
|
1,
|
|
ProcessInformation,
|
|
ProcessInformationLength,
|
|
PreviousMode);
|
|
break;
|
|
|
|
case ProcessWorkingSetWatch:
|
|
DPRINT1("WS watch not implemented\n");
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
|
|
case ProcessHandleTracing:
|
|
DPRINT1("Handle tracing not implemented\n");
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
|
|
/* Anything else is invalid */
|
|
default:
|
|
DPRINT1("Invalid Server 2003 Info Class: %lx\n", ProcessInformationClass);
|
|
Status = STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
|
|
/* Dereference and return status */
|
|
ObDereferenceObject(Process);
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtSetInformationThread(IN HANDLE ThreadHandle,
|
|
IN THREADINFOCLASS ThreadInformationClass,
|
|
IN PVOID ThreadInformation,
|
|
IN ULONG ThreadInformationLength)
|
|
{
|
|
PETHREAD Thread;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
NTSTATUS Status;
|
|
HANDLE TokenHandle = NULL;
|
|
KPRIORITY Priority = 0;
|
|
KAFFINITY Affinity = 0, CombinedAffinity;
|
|
PVOID Address = NULL;
|
|
PEPROCESS Process;
|
|
ULONG_PTR DisableBoost = 0;
|
|
ULONG_PTR IdealProcessor = 0;
|
|
ULONG_PTR Break = 0;
|
|
PTEB Teb;
|
|
ULONG_PTR TlsIndex = 0;
|
|
PVOID *ExpansionSlots;
|
|
PETHREAD ProcThread;
|
|
BOOLEAN HasPrivilege;
|
|
PAGED_CODE();
|
|
|
|
/* Verify Information Class validity */
|
|
Status = DefaultSetInfoBufferCheck(ThreadInformationClass,
|
|
PsThreadInfoClass,
|
|
RTL_NUMBER_OF(PsThreadInfoClass),
|
|
ThreadInformation,
|
|
ThreadInformationLength,
|
|
PreviousMode);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("NtSetInformationThread(): Information verification class failed! (Status -> 0x%lx, ThreadInformationClass -> %lx)\n", Status, ThreadInformationClass);
|
|
return Status;
|
|
}
|
|
|
|
/* Check what kind of information class this is */
|
|
switch (ThreadInformationClass)
|
|
{
|
|
/* Thread priority */
|
|
case ThreadPriority:
|
|
|
|
/* Check buffer length */
|
|
if (ThreadInformationLength != sizeof(KPRIORITY))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Use SEH for capture */
|
|
_SEH2_TRY
|
|
{
|
|
/* Get the priority */
|
|
Priority = *(PLONG)ThreadInformation;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Validate it */
|
|
if ((Priority > HIGH_PRIORITY) ||
|
|
(Priority <= LOW_PRIORITY))
|
|
{
|
|
/* Fail */
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
/* Check for the required privilege */
|
|
if (Priority >= LOW_REALTIME_PRIORITY)
|
|
{
|
|
HasPrivilege = SeCheckPrivilegedObject(SeIncreaseBasePriorityPrivilege,
|
|
ThreadHandle,
|
|
THREAD_SET_INFORMATION,
|
|
PreviousMode);
|
|
if (!HasPrivilege)
|
|
{
|
|
DPRINT1("Privilege to change priority to %lx lacking\n", Priority);
|
|
return STATUS_PRIVILEGE_NOT_HELD;
|
|
}
|
|
}
|
|
|
|
/* Reference the thread */
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
THREAD_SET_INFORMATION,
|
|
PsThreadType,
|
|
PreviousMode,
|
|
(PVOID*)&Thread,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
/* Set the priority */
|
|
KeSetPriorityThread(&Thread->Tcb, Priority);
|
|
|
|
/* Dereference the thread */
|
|
ObDereferenceObject(Thread);
|
|
break;
|
|
|
|
case ThreadBasePriority:
|
|
|
|
/* Check buffer length */
|
|
if (ThreadInformationLength != sizeof(LONG))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Use SEH for capture */
|
|
_SEH2_TRY
|
|
{
|
|
/* Get the priority */
|
|
Priority = *(PLONG)ThreadInformation;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Validate it */
|
|
if ((Priority > THREAD_BASE_PRIORITY_MAX) ||
|
|
(Priority < THREAD_BASE_PRIORITY_MIN))
|
|
{
|
|
/* These ones are OK */
|
|
if ((Priority != THREAD_BASE_PRIORITY_LOWRT + 1) &&
|
|
(Priority != THREAD_BASE_PRIORITY_IDLE - 1))
|
|
{
|
|
/* Check if the process is real time */
|
|
if (PsGetCurrentProcess()->PriorityClass !=
|
|
PROCESS_PRIORITY_CLASS_REALTIME)
|
|
{
|
|
/* It isn't, fail */
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Reference the thread */
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
THREAD_SET_INFORMATION,
|
|
PsThreadType,
|
|
PreviousMode,
|
|
(PVOID*)&Thread,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
/* Set the base priority */
|
|
KeSetBasePriorityThread(&Thread->Tcb, Priority);
|
|
|
|
/* Dereference the thread */
|
|
ObDereferenceObject(Thread);
|
|
break;
|
|
|
|
case ThreadAffinityMask:
|
|
|
|
/* Check buffer length */
|
|
if (ThreadInformationLength != sizeof(ULONG_PTR))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Use SEH for capture */
|
|
_SEH2_TRY
|
|
{
|
|
/* Get the priority */
|
|
Affinity = *(PULONG_PTR)ThreadInformation;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Validate it */
|
|
if (!Affinity)
|
|
{
|
|
/* Fail */
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
/* Reference the thread */
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
THREAD_SET_INFORMATION,
|
|
PsThreadType,
|
|
PreviousMode,
|
|
(PVOID*)&Thread,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
/* Get the process */
|
|
Process = Thread->ThreadsProcess;
|
|
|
|
/* Try to acquire rundown */
|
|
if (ExAcquireRundownProtection(&Process->RundownProtect))
|
|
{
|
|
/* Lock it */
|
|
KeEnterCriticalRegion();
|
|
ExAcquirePushLockShared(&Process->ProcessLock);
|
|
|
|
/* Combine masks */
|
|
CombinedAffinity = Affinity & Process->Pcb.Affinity;
|
|
if (CombinedAffinity != Affinity)
|
|
{
|
|
/* Fail */
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
/* Set the affinity */
|
|
KeSetAffinityThread(&Thread->Tcb, CombinedAffinity);
|
|
}
|
|
|
|
/* Release the lock and rundown */
|
|
ExReleasePushLockShared(&Process->ProcessLock);
|
|
KeLeaveCriticalRegion();
|
|
ExReleaseRundownProtection(&Process->RundownProtect);
|
|
}
|
|
else
|
|
{
|
|
/* Too late */
|
|
Status = STATUS_PROCESS_IS_TERMINATING;
|
|
}
|
|
|
|
/* Dereference the thread */
|
|
ObDereferenceObject(Thread);
|
|
break;
|
|
|
|
case ThreadImpersonationToken:
|
|
|
|
/* Check buffer length */
|
|
if (ThreadInformationLength != sizeof(HANDLE))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Use SEH for capture */
|
|
_SEH2_TRY
|
|
{
|
|
/* Save the token handle */
|
|
TokenHandle = *(PHANDLE)ThreadInformation;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Reference the thread */
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
THREAD_SET_THREAD_TOKEN,
|
|
PsThreadType,
|
|
PreviousMode,
|
|
(PVOID*)&Thread,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
/* Assign the actual token */
|
|
Status = PsAssignImpersonationToken(Thread, TokenHandle);
|
|
|
|
/* Dereference the thread */
|
|
ObDereferenceObject(Thread);
|
|
break;
|
|
|
|
case ThreadQuerySetWin32StartAddress:
|
|
|
|
/* Check buffer length */
|
|
if (ThreadInformationLength != sizeof(ULONG_PTR))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Use SEH for capture */
|
|
_SEH2_TRY
|
|
{
|
|
/* Get the priority */
|
|
Address = *(PVOID*)ThreadInformation;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Reference the thread */
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
THREAD_SET_INFORMATION,
|
|
PsThreadType,
|
|
PreviousMode,
|
|
(PVOID*)&Thread,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
/* Set the address */
|
|
Thread->Win32StartAddress = Address;
|
|
|
|
/* Dereference the thread */
|
|
ObDereferenceObject(Thread);
|
|
break;
|
|
|
|
case ThreadIdealProcessor:
|
|
|
|
/* Check buffer length */
|
|
if (ThreadInformationLength != sizeof(ULONG_PTR))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Use SEH for capture */
|
|
_SEH2_TRY
|
|
{
|
|
/* Get the priority */
|
|
IdealProcessor = *(PULONG_PTR)ThreadInformation;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Validate it */
|
|
if (IdealProcessor > MAXIMUM_PROCESSORS)
|
|
{
|
|
/* Fail */
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
/* Reference the thread */
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
THREAD_SET_INFORMATION,
|
|
PsThreadType,
|
|
PreviousMode,
|
|
(PVOID*)&Thread,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
/* Set the ideal */
|
|
Status = KeSetIdealProcessorThread(&Thread->Tcb,
|
|
(CCHAR)IdealProcessor);
|
|
|
|
/* Get the TEB and protect the thread */
|
|
Teb = Thread->Tcb.Teb;
|
|
if ((Teb) && (ExAcquireRundownProtection(&Thread->RundownProtect)))
|
|
{
|
|
/* Save the ideal processor */
|
|
Teb->IdealProcessor = Thread->Tcb.IdealProcessor;
|
|
|
|
/* Release rundown protection */
|
|
ExReleaseRundownProtection(&Thread->RundownProtect);
|
|
}
|
|
|
|
/* Dereference the thread */
|
|
ObDereferenceObject(Thread);
|
|
break;
|
|
|
|
case ThreadPriorityBoost:
|
|
|
|
/* Check buffer length */
|
|
if (ThreadInformationLength != sizeof(ULONG_PTR))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Use SEH for capture */
|
|
_SEH2_TRY
|
|
{
|
|
/* Get the priority */
|
|
DisableBoost = *(PULONG_PTR)ThreadInformation;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Reference the thread */
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
THREAD_SET_INFORMATION,
|
|
PsThreadType,
|
|
PreviousMode,
|
|
(PVOID*)&Thread,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
/* Call the kernel */
|
|
KeSetDisableBoostThread(&Thread->Tcb, (BOOLEAN)DisableBoost);
|
|
|
|
/* Dereference the thread */
|
|
ObDereferenceObject(Thread);
|
|
break;
|
|
|
|
case ThreadZeroTlsCell:
|
|
|
|
/* Check buffer length */
|
|
if (ThreadInformationLength != sizeof(ULONG))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Use SEH for capture */
|
|
_SEH2_TRY
|
|
{
|
|
/* Get the priority */
|
|
TlsIndex = *(PULONG)ThreadInformation;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Reference the thread */
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
THREAD_SET_INFORMATION,
|
|
PsThreadType,
|
|
PreviousMode,
|
|
(PVOID*)&Thread,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
/* This is only valid for the current thread */
|
|
if (Thread != PsGetCurrentThread())
|
|
{
|
|
/* Fail */
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
ObDereferenceObject(Thread);
|
|
break;
|
|
}
|
|
|
|
/* Get the process */
|
|
Process = Thread->ThreadsProcess;
|
|
|
|
/* Loop the threads */
|
|
ProcThread = PsGetNextProcessThread(Process, NULL);
|
|
while (ProcThread)
|
|
{
|
|
/* Acquire rundown */
|
|
if (ExAcquireRundownProtection(&ProcThread->RundownProtect))
|
|
{
|
|
/* Get the TEB */
|
|
Teb = ProcThread->Tcb.Teb;
|
|
if (Teb)
|
|
{
|
|
/* Check if we're in the expansion range */
|
|
if (TlsIndex > TLS_MINIMUM_AVAILABLE - 1)
|
|
{
|
|
if (TlsIndex < (TLS_MINIMUM_AVAILABLE +
|
|
TLS_EXPANSION_SLOTS) - 1)
|
|
{
|
|
/* Check if we have expansion slots */
|
|
ExpansionSlots = Teb->TlsExpansionSlots;
|
|
if (ExpansionSlots)
|
|
{
|
|
/* Clear the index */
|
|
ExpansionSlots[TlsIndex - TLS_MINIMUM_AVAILABLE] = 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Clear the index */
|
|
Teb->TlsSlots[TlsIndex] = NULL;
|
|
}
|
|
}
|
|
|
|
/* Release rundown */
|
|
ExReleaseRundownProtection(&ProcThread->RundownProtect);
|
|
}
|
|
|
|
/* Go to the next thread */
|
|
ProcThread = PsGetNextProcessThread(Process, ProcThread);
|
|
}
|
|
|
|
/* Dereference the thread */
|
|
ObDereferenceObject(Thread);
|
|
break;
|
|
|
|
case ThreadBreakOnTermination:
|
|
|
|
/* Check buffer length */
|
|
if (ThreadInformationLength != sizeof(ULONG))
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Enter SEH for direct buffer read */
|
|
_SEH2_TRY
|
|
{
|
|
Break = *(PULONG)ThreadInformation;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Break = 0;
|
|
Status = _SEH2_GetExceptionCode();
|
|
_SEH2_YIELD(break);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Setting 'break on termination' requires the SeDebugPrivilege */
|
|
if (!SeSinglePrivilegeCheck(SeDebugPrivilege, PreviousMode))
|
|
{
|
|
/* We don't hold the privilege, bail out */
|
|
Status = STATUS_PRIVILEGE_NOT_HELD;
|
|
break;
|
|
}
|
|
|
|
/* Reference the thread */
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
THREAD_SET_INFORMATION,
|
|
PsThreadType,
|
|
PreviousMode,
|
|
(PVOID*)&Thread,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
/* Set or clear the flag */
|
|
if (Break)
|
|
{
|
|
PspSetCrossThreadFlag(Thread, CT_BREAK_ON_TERMINATION_BIT);
|
|
}
|
|
else
|
|
{
|
|
PspClearCrossThreadFlag(Thread, CT_BREAK_ON_TERMINATION_BIT);
|
|
}
|
|
|
|
/* Dereference the thread */
|
|
ObDereferenceObject(Thread);
|
|
break;
|
|
|
|
case ThreadHideFromDebugger:
|
|
|
|
/* Check buffer length */
|
|
if (ThreadInformationLength != 0)
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Reference the thread */
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
THREAD_SET_INFORMATION,
|
|
PsThreadType,
|
|
PreviousMode,
|
|
(PVOID*)&Thread,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
/* Set the flag */
|
|
PspSetCrossThreadFlag(Thread, CT_HIDE_FROM_DEBUGGER_BIT);
|
|
|
|
/* Dereference the thread */
|
|
ObDereferenceObject(Thread);
|
|
break;
|
|
|
|
default:
|
|
/* We don't implement it yet */
|
|
DPRINT1("Not implemented: %d\n", ThreadInformationClass);
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtQueryInformationThread(IN HANDLE ThreadHandle,
|
|
IN THREADINFOCLASS ThreadInformationClass,
|
|
OUT PVOID ThreadInformation,
|
|
IN ULONG ThreadInformationLength,
|
|
OUT PULONG ReturnLength OPTIONAL)
|
|
{
|
|
PETHREAD Thread;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
NTSTATUS Status;
|
|
ULONG Access;
|
|
ULONG Length = 0;
|
|
PTHREAD_BASIC_INFORMATION ThreadBasicInfo =
|
|
(PTHREAD_BASIC_INFORMATION)ThreadInformation;
|
|
PKERNEL_USER_TIMES ThreadTime = (PKERNEL_USER_TIMES)ThreadInformation;
|
|
KIRQL OldIrql;
|
|
ULONG ThreadTerminated;
|
|
PAGED_CODE();
|
|
|
|
/* Verify Information Class validity */
|
|
Status = DefaultQueryInfoBufferCheck(ThreadInformationClass,
|
|
PsThreadInfoClass,
|
|
RTL_NUMBER_OF(PsThreadInfoClass),
|
|
ICIF_PROBE_READ,
|
|
ThreadInformation,
|
|
ThreadInformationLength,
|
|
ReturnLength,
|
|
NULL,
|
|
PreviousMode);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("NtQueryInformationThread(): Information verification class failed! (Status -> 0x%lx , ThreadInformationClass -> %lx)\n", Status, ThreadInformationClass);
|
|
return Status;
|
|
}
|
|
|
|
/* Check what class this is */
|
|
Access = THREAD_QUERY_INFORMATION;
|
|
|
|
/* Check what kind of information class this is */
|
|
switch (ThreadInformationClass)
|
|
{
|
|
/* Basic thread information */
|
|
case ThreadBasicInformation:
|
|
|
|
/* Set return length */
|
|
Length = sizeof(THREAD_BASIC_INFORMATION);
|
|
|
|
if (ThreadInformationLength != Length)
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
Access,
|
|
PsThreadType,
|
|
PreviousMode,
|
|
(PVOID*)&Thread,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
/* Protect writes with SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Write all the information from the ETHREAD/KTHREAD */
|
|
ThreadBasicInfo->ExitStatus = Thread->ExitStatus;
|
|
ThreadBasicInfo->TebBaseAddress = (PVOID)Thread->Tcb.Teb;
|
|
ThreadBasicInfo->ClientId = Thread->Cid;
|
|
ThreadBasicInfo->AffinityMask = Thread->Tcb.Affinity;
|
|
ThreadBasicInfo->Priority = Thread->Tcb.Priority;
|
|
ThreadBasicInfo->BasePriority = KeQueryBasePriorityThread(&Thread->Tcb);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Dereference the thread */
|
|
ObDereferenceObject(Thread);
|
|
break;
|
|
|
|
/* Thread time information */
|
|
case ThreadTimes:
|
|
|
|
/* Set the return length */
|
|
Length = sizeof(KERNEL_USER_TIMES);
|
|
|
|
if (ThreadInformationLength != Length)
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
Access,
|
|
PsThreadType,
|
|
PreviousMode,
|
|
(PVOID*)&Thread,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
/* Protect writes with SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Copy time information from ETHREAD/KTHREAD */
|
|
ThreadTime->KernelTime.QuadPart = Thread->Tcb.KernelTime * KeMaximumIncrement;
|
|
ThreadTime->UserTime.QuadPart = Thread->Tcb.UserTime * KeMaximumIncrement;
|
|
ThreadTime->CreateTime = Thread->CreateTime;
|
|
|
|
/* Exit time is in a union and only valid on actual exit! */
|
|
if (KeReadStateThread(&Thread->Tcb))
|
|
{
|
|
ThreadTime->ExitTime = Thread->ExitTime;
|
|
}
|
|
else
|
|
{
|
|
ThreadTime->ExitTime.QuadPart = 0;
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Dereference the thread */
|
|
ObDereferenceObject(Thread);
|
|
break;
|
|
|
|
case ThreadQuerySetWin32StartAddress:
|
|
|
|
/* Set the return length*/
|
|
Length = sizeof(PVOID);
|
|
|
|
if (ThreadInformationLength != Length)
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
Access,
|
|
PsThreadType,
|
|
PreviousMode,
|
|
(PVOID*)&Thread,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
/* Protect write with SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Return the Win32 Start Address */
|
|
*(PVOID*)ThreadInformation = Thread->Win32StartAddress;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Dereference the thread */
|
|
ObDereferenceObject(Thread);
|
|
break;
|
|
|
|
case ThreadPerformanceCount:
|
|
|
|
/* Set the return length*/
|
|
Length = sizeof(LARGE_INTEGER);
|
|
|
|
if (ThreadInformationLength != Length)
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
Access,
|
|
PsThreadType,
|
|
PreviousMode,
|
|
(PVOID*)&Thread,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
/* Protect write with SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* FIXME */
|
|
(*(PLARGE_INTEGER)ThreadInformation).QuadPart = 0;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Dereference the thread */
|
|
ObDereferenceObject(Thread);
|
|
break;
|
|
|
|
case ThreadAmILastThread:
|
|
|
|
/* Set the return length*/
|
|
Length = sizeof(ULONG);
|
|
|
|
if (ThreadInformationLength != Length)
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
Access,
|
|
PsThreadType,
|
|
PreviousMode,
|
|
(PVOID*)&Thread,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
/* Protect write with SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Return whether or not we are the last thread */
|
|
*(PULONG)ThreadInformation = ((Thread->ThreadsProcess->
|
|
ThreadListHead.Flink->Flink ==
|
|
&Thread->ThreadsProcess->
|
|
ThreadListHead) ?
|
|
TRUE : FALSE);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Dereference the thread */
|
|
ObDereferenceObject(Thread);
|
|
break;
|
|
|
|
case ThreadIsIoPending:
|
|
|
|
/* Set the return length*/
|
|
Length = sizeof(ULONG);
|
|
|
|
if (ThreadInformationLength != Length)
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
Access,
|
|
PsThreadType,
|
|
PreviousMode,
|
|
(PVOID*)&Thread,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
/* Raise the IRQL to protect the IRP list */
|
|
KeRaiseIrql(APC_LEVEL, &OldIrql);
|
|
|
|
/* Protect write with SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Check if the IRP list is empty or not */
|
|
*(PULONG)ThreadInformation = !IsListEmpty(&Thread->IrpList);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Lower IRQL back */
|
|
KeLowerIrql(OldIrql);
|
|
|
|
/* Dereference the thread */
|
|
ObDereferenceObject(Thread);
|
|
break;
|
|
|
|
/* LDT and GDT information */
|
|
case ThreadDescriptorTableEntry:
|
|
|
|
#if defined(_X86_)
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
Access,
|
|
PsThreadType,
|
|
PreviousMode,
|
|
(PVOID*)&Thread,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
/* Call the worker routine */
|
|
Status = PspQueryDescriptorThread(Thread,
|
|
ThreadInformation,
|
|
ThreadInformationLength,
|
|
ReturnLength);
|
|
|
|
/* Dereference the thread */
|
|
ObDereferenceObject(Thread);
|
|
#else
|
|
/* Only implemented on x86 */
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
#endif
|
|
break;
|
|
|
|
case ThreadPriorityBoost:
|
|
|
|
/* Set the return length*/
|
|
Length = sizeof(ULONG);
|
|
|
|
if (ThreadInformationLength != Length)
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
Access,
|
|
PsThreadType,
|
|
PreviousMode,
|
|
(PVOID*)&Thread,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
*(PULONG)ThreadInformation = Thread->Tcb.DisableBoost ? 1 : 0;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Dereference the thread */
|
|
ObDereferenceObject(Thread);
|
|
break;
|
|
|
|
case ThreadIsTerminated:
|
|
|
|
/* Set the return length*/
|
|
Length = sizeof(ThreadTerminated);
|
|
|
|
if (ThreadInformationLength != Length)
|
|
{
|
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
}
|
|
|
|
/* Reference the process */
|
|
Status = ObReferenceObjectByHandle(ThreadHandle,
|
|
Access,
|
|
PsThreadType,
|
|
PreviousMode,
|
|
(PVOID*)&Thread,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
ThreadTerminated = PsIsThreadTerminating(Thread);
|
|
|
|
_SEH2_TRY
|
|
{
|
|
*(PULONG)ThreadInformation = ThreadTerminated ? 1 : 0;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Dereference the thread */
|
|
ObDereferenceObject(Thread);
|
|
break;
|
|
|
|
/* Anything else */
|
|
default:
|
|
|
|
/* Not yet implemented */
|
|
DPRINT1("Not implemented: %lx\n", ThreadInformationClass);
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/* Protect write with SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Check if caller wanted return length */
|
|
if (ReturnLength) *ReturnLength = Length;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
return Status;
|
|
}
|
|
|
|
/* EOF */
|