/*
* KProcessHacker
*
* Copyright (C) 2010-2016 wj32
*
* This file is part of Process Hacker.
*
* Process Hacker is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Process Hacker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Process Hacker. If not, see .
*/
#include
#include
#ifdef _X86_
#define KERNEL_HANDLE_BIT (0x80000000)
#else
#define KERNEL_HANDLE_BIT (0xffffffff80000000)
#endif
#define IsKernelHandle(Handle) ((LONG_PTR)(Handle) < 0)
#define MakeKernelHandle(Handle) ((HANDLE)((ULONG_PTR)(Handle) | KERNEL_HANDLE_BIT))
typedef struct _KPHP_ENUMERATE_PROCESS_HANDLES_CONTEXT
{
PVOID Buffer;
PVOID BufferLimit;
PVOID CurrentEntry;
ULONG Count;
NTSTATUS Status;
} KPHP_ENUMERATE_PROCESS_HANDLES_CONTEXT, *PKPHP_ENUMERATE_PROCESS_HANDLES_CONTEXT;
BOOLEAN KphpEnumerateProcessHandlesEnumCallback61(
__inout PHANDLE_TABLE_ENTRY HandleTableEntry,
__in HANDLE Handle,
__in PVOID Context
);
BOOLEAN KphpEnumerateProcessHandlesEnumCallback(
__in PHANDLE_TABLE HandleTable,
__inout PHANDLE_TABLE_ENTRY HandleTableEntry,
__in HANDLE Handle,
__in PVOID Context
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, KphReferenceProcessHandleTable)
#pragma alloc_text(PAGE, KphDereferenceProcessHandleTable)
#pragma alloc_text(PAGE, KphUnlockHandleTableEntry)
#pragma alloc_text(PAGE, KphpEnumerateProcessHandlesEnumCallback61)
#pragma alloc_text(PAGE, KphpEnumerateProcessHandlesEnumCallback)
#pragma alloc_text(PAGE, KpiEnumerateProcessHandles)
#pragma alloc_text(PAGE, KphQueryNameObject)
#pragma alloc_text(PAGE, KphQueryNameFileObject)
#pragma alloc_text(PAGE, KpiQueryInformationObject)
#pragma alloc_text(PAGE, KpiSetInformationObject)
#pragma alloc_text(PAGE, KphOpenNamedObject)
#endif
/**
* Gets a pointer to the handle table of a process.
*
* \param Process A process object.
*
* \return A pointer to the handle table, or NULL if the process is terminating or the request is
* not supported. You must call KphDereferenceProcessHandleTable() when the handle table is no
* longer needed.
*/
PHANDLE_TABLE KphReferenceProcessHandleTable(
__in PEPROCESS Process
)
{
PHANDLE_TABLE handleTable = NULL;
PAGED_CODE();
// Fail if we don't have an offset.
if (KphDynEpObjectTable == -1)
return NULL;
// Prevent the process from terminating and get its handle table.
if (NT_SUCCESS(PsAcquireProcessExitSynchronization(Process)))
{
handleTable = *(PHANDLE_TABLE *)((ULONG_PTR)Process + KphDynEpObjectTable);
if (!handleTable)
PsReleaseProcessExitSynchronization(Process);
}
return handleTable;
}
/**
* Dereferences the handle table of a process.
*
* \param Process A process object.
*/
VOID KphDereferenceProcessHandleTable(
__in PEPROCESS Process
)
{
PAGED_CODE();
PsReleaseProcessExitSynchronization(Process);
}
VOID KphUnlockHandleTableEntry(
__in PHANDLE_TABLE HandleTable,
__in PHANDLE_TABLE_ENTRY HandleTableEntry
)
{
PEX_PUSH_LOCK handleContentionEvent;
PAGED_CODE();
// Set the unlocked bit.
#ifdef _M_X64
InterlockedExchangeAdd64(&HandleTableEntry->Value, 1);
#else
InterlockedExchangeAdd(&HandleTableEntry->Value, 1);
#endif
// Allow waiters to wake up.
handleContentionEvent = (PEX_PUSH_LOCK)((ULONG_PTR)HandleTable + KphDynHtHandleContentionEvent);
if (*(PULONG_PTR)handleContentionEvent != 0)
ExfUnblockPushLock(handleContentionEvent, NULL);
}
BOOLEAN KphpEnumerateProcessHandlesEnumCallback61(
__inout PHANDLE_TABLE_ENTRY HandleTableEntry,
__in HANDLE Handle,
__in PVOID Context
)
{
PKPHP_ENUMERATE_PROCESS_HANDLES_CONTEXT context = Context;
KPH_PROCESS_HANDLE handleInfo;
POBJECT_HEADER objectHeader;
POBJECT_TYPE objectType;
PKPH_PROCESS_HANDLE entryInBuffer;
PAGED_CODE();
objectHeader = ObpDecodeObject(HandleTableEntry->Object);
handleInfo.Handle = Handle;
handleInfo.Object = objectHeader ? &objectHeader->Body : NULL;
handleInfo.GrantedAccess = ObpDecodeGrantedAccess(HandleTableEntry->GrantedAccess);
handleInfo.ObjectTypeIndex = -1;
handleInfo.Reserved1 = 0;
handleInfo.HandleAttributes = ObpGetHandleAttributes(HandleTableEntry);
handleInfo.Reserved2 = 0;
if (handleInfo.Object)
{
objectType = ObGetObjectType(handleInfo.Object);
if (objectType && KphDynOtIndex != -1)
handleInfo.ObjectTypeIndex = (USHORT)*(PUCHAR)((ULONG_PTR)objectType + KphDynOtIndex);
}
// Advance the current entry pointer regardless of whether the information will be written; this
// will allow the parent function to report the correct return length.
entryInBuffer = context->CurrentEntry;
context->CurrentEntry = (PVOID)((ULONG_PTR)context->CurrentEntry + sizeof(KPH_PROCESS_HANDLE));
context->Count++;
// Only write if we have not exceeded the buffer length. Also check for a potential overflow (if
// the process has an extremely large number of handles, the buffer pointer may wrap).
if (
(ULONG_PTR)entryInBuffer >= (ULONG_PTR)context->Buffer &&
(ULONG_PTR)entryInBuffer + sizeof(KPH_PROCESS_HANDLE) <= (ULONG_PTR)context->BufferLimit
)
{
__try
{
*entryInBuffer = handleInfo;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
// Report an error.
if (context->Status == STATUS_SUCCESS)
context->Status = GetExceptionCode();
}
}
else
{
// Report that the buffer is too small.
if (context->Status == STATUS_SUCCESS)
context->Status = STATUS_BUFFER_TOO_SMALL;
}
return FALSE;
}
BOOLEAN KphpEnumerateProcessHandlesEnumCallback(
__in PHANDLE_TABLE HandleTable,
__inout PHANDLE_TABLE_ENTRY HandleTableEntry,
__in HANDLE Handle,
__in PVOID Context
)
{
BOOLEAN result;
PAGED_CODE();
result = KphpEnumerateProcessHandlesEnumCallback61(HandleTableEntry, Handle, Context);
KphUnlockHandleTableEntry(HandleTable, HandleTableEntry);
return result;
}
/**
* Enumerates the handles of a process.
*
* \param ProcessHandle A handle to a process.
* \param Buffer The buffer in which the handle information will be stored.
* \param BufferLength The number of bytes available in \a Buffer.
* \param ReturnLength A variable which receives the number of bytes required to be available in
* \a Buffer.
* \param AccessMode The mode in which to perform access checks.
*/
NTSTATUS KpiEnumerateProcessHandles(
__in HANDLE ProcessHandle,
__out_bcount(BufferLength) PVOID Buffer,
__in_opt ULONG BufferLength,
__out_opt PULONG ReturnLength,
__in KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
BOOLEAN result;
PEPROCESS process;
PHANDLE_TABLE handleTable;
KPHP_ENUMERATE_PROCESS_HANDLES_CONTEXT context;
PAGED_CODE();
if (KphDynNtVersion >= PHNT_WIN8 && KphDynHtHandleContentionEvent == -1)
{
return STATUS_NOT_SUPPORTED;
}
if (AccessMode != KernelMode)
{
__try
{
ProbeForWrite(Buffer, BufferLength, sizeof(ULONG));
if (ReturnLength)
ProbeForWrite(ReturnLength, sizeof(ULONG), sizeof(ULONG));
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
}
// Reference the process object.
status = ObReferenceObjectByHandle(
ProcessHandle,
0,
*PsProcessType,
AccessMode,
&process,
NULL
);
if (!NT_SUCCESS(status))
return status;
// Get its handle table.
handleTable = KphReferenceProcessHandleTable(process);
if (!handleTable)
{
ObDereferenceObject(process);
return STATUS_UNSUCCESSFUL;
}
// Initialize the enumeration context.
context.Buffer = Buffer;
context.BufferLimit = (PVOID)((ULONG_PTR)Buffer + BufferLength);
context.CurrentEntry = ((PKPH_PROCESS_HANDLE_INFORMATION)Buffer)->Handles;
context.Count = 0;
context.Status = STATUS_SUCCESS;
// Enumerate the handles.
if (KphDynNtVersion >= PHNT_WIN8)
{
result = ExEnumHandleTable(
handleTable,
KphpEnumerateProcessHandlesEnumCallback,
&context,
NULL
);
}
else
{
result = ExEnumHandleTable(
handleTable,
(PEX_ENUM_HANDLE_CALLBACK)KphpEnumerateProcessHandlesEnumCallback61,
&context,
NULL
);
}
KphDereferenceProcessHandleTable(process);
ObDereferenceObject(process);
// Write the number of handles if we can.
if (BufferLength >= FIELD_OFFSET(KPH_PROCESS_HANDLE_INFORMATION, Handles))
{
if (AccessMode != KernelMode)
{
__try
{
((PKPH_PROCESS_HANDLE_INFORMATION)Buffer)->HandleCount = context.Count;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
}
else
{
((PKPH_PROCESS_HANDLE_INFORMATION)Buffer)->HandleCount = context.Count;
}
}
// Supply the return length if the caller wanted it.
if (ReturnLength)
{
ULONG returnLength;
// Note: if the CurrentEntry pointer wrapped, this will give the wrong return length.
returnLength = (ULONG)((ULONG_PTR)context.CurrentEntry - (ULONG_PTR)Buffer);
if (AccessMode != KernelMode)
{
__try
{
*ReturnLength = returnLength;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
}
else
{
*ReturnLength = returnLength;
}
}
return context.Status;
}
/**
* Queries the name of an object.
*
* \param Object A pointer to an object.
* \param Buffer The buffer in which the object name will be stored.
* \param BufferLength The number of bytes available in \a Buffer.
* \param ReturnLength A variable which receives the number of bytes required to be available in
* \a Buffer.
*/
NTSTATUS KphQueryNameObject(
__in PVOID Object,
__out_bcount(BufferLength) POBJECT_NAME_INFORMATION Buffer,
__in ULONG BufferLength,
__out PULONG ReturnLength
)
{
NTSTATUS status;
POBJECT_TYPE objectType;
PAGED_CODE();
objectType = ObGetObjectType(Object);
// Check if we are going to hang when querying the object, and use
// the special file object query function if needed.
if (objectType == *IoFileObjectType &&
(((PFILE_OBJECT)Object)->Busy || ((PFILE_OBJECT)Object)->Waiters))
{
status = KphQueryNameFileObject(Object, Buffer, BufferLength, ReturnLength);
dprintf("KphQueryNameFileObject: status 0x%x\n", status);
}
else
{
status = ObQueryNameString(Object, Buffer, BufferLength, ReturnLength);
dprintf("ObQueryNameString: status 0x%x\n", status);
}
// Make the error returns consistent.
if (status == STATUS_BUFFER_OVERFLOW) // returned by I/O subsystem
status = STATUS_BUFFER_TOO_SMALL;
if (status == STATUS_INFO_LENGTH_MISMATCH) // returned by ObQueryNameString
status = STATUS_BUFFER_TOO_SMALL;
if (NT_SUCCESS(status))
dprintf("KphQueryNameObject: %.*S\n", Buffer->Name.Length / sizeof(WCHAR), Buffer->Name.Buffer);
else
dprintf("KphQueryNameObject: status 0x%x\n", status);
return status;
}
/**
* Queries the name of a file object.
*
* \param FileObject A pointer to a file object.
* \param Buffer The buffer in which the object name will be stored.
* \param BufferLength The number of bytes available in \a Buffer.
* \param ReturnLength A variable which receives the number of bytes required to be available in
* \a Buffer.
*/
NTSTATUS KphQueryNameFileObject(
__in PFILE_OBJECT FileObject,
__out_bcount(BufferLength) POBJECT_NAME_INFORMATION Buffer,
__in ULONG BufferLength,
__out PULONG ReturnLength
)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG returnLength;
PCHAR objectName;
ULONG usedLength;
ULONG subNameLength;
PFILE_OBJECT relatedFileObject;
PAGED_CODE();
// We need at least the size of OBJECT_NAME_INFORMATION to continue.
if (BufferLength < sizeof(OBJECT_NAME_INFORMATION))
{
*ReturnLength = sizeof(OBJECT_NAME_INFORMATION);
return STATUS_BUFFER_TOO_SMALL;
}
// Assume failure.
Buffer->Name.Length = 0;
// We will place the object name directly after the UNICODE_STRING structure in the buffer.
Buffer->Name.Buffer = (PWSTR)((ULONG_PTR)Buffer + sizeof(OBJECT_NAME_INFORMATION));
// Retain a local pointer to the object name so we can manipulate the pointer.
objectName = (PCHAR)Buffer->Name.Buffer;
// A variable that keeps track of how much space we have used.
usedLength = sizeof(OBJECT_NAME_INFORMATION);
// Check if the file object has an associated device (e.g. "\Device\NamedPipe", "\Device\Mup").
// We can use the user-supplied buffer for this since if the buffer isn't big enough, we can't
// proceed anyway (we are going to use the name).
if (FileObject->DeviceObject)
{
status = ObQueryNameString(
FileObject->DeviceObject,
Buffer,
BufferLength,
&returnLength
);
if (!NT_SUCCESS(status))
{
if (status == STATUS_INFO_LENGTH_MISMATCH)
status = STATUS_BUFFER_TOO_SMALL;
*ReturnLength = returnLength;
return status;
}
// The UNICODE_STRING in the buffer is now filled in. We will append to the object name
// later, so we need to fix the object name pointer by adding the length, in bytes, of the
// device name string we just got.
objectName += Buffer->Name.Length;
usedLength += Buffer->Name.Length;
}
// Check if the file object has a file name component. If not, we can't do anything else, so we
// just return the name we have already.
if (!FileObject->FileName.Buffer)
{
*ReturnLength = usedLength;
return STATUS_SUCCESS;
}
// The file object has a name. We need to walk up the file object chain and append the names of
// the related file objects in reverse order. This means we need to calculate the total length
// first.
relatedFileObject = FileObject;
subNameLength = 0;
do
{
subNameLength += relatedFileObject->FileName.Length;
// Avoid infinite loops.
if (relatedFileObject == relatedFileObject->RelatedFileObject)
break;
relatedFileObject = relatedFileObject->RelatedFileObject;
}
while (relatedFileObject);
usedLength += subNameLength;
// Check if we have enough space to write the whole thing.
if (usedLength > BufferLength)
{
*ReturnLength = usedLength;
return STATUS_BUFFER_TOO_SMALL;
}
// We're ready to begin copying the names.
// Add the name length because we're copying in reverse order.
objectName += subNameLength;
relatedFileObject = FileObject;
do
{
objectName -= relatedFileObject->FileName.Length;
memcpy(objectName, relatedFileObject->FileName.Buffer, relatedFileObject->FileName.Length);
// Avoid infinite loops.
if (relatedFileObject == relatedFileObject->RelatedFileObject)
break;
relatedFileObject = relatedFileObject->RelatedFileObject;
}
while (relatedFileObject);
// Update the length.
Buffer->Name.Length += (USHORT)subNameLength;
// Pass the return length back.
*ReturnLength = usedLength;
return STATUS_SUCCESS;
}
/**
* Queries object information.
*
* \param ProcessHandle A handle to a process.
* \param Handle A handle which is present in the process referenced by \a ProcessHandle.
* \param ObjectInformationClass The type of information to retrieve.
* \param ObjectInformation The buffer in which the information will be stored.
* \param ObjectInformationLength The number of bytes available in \a ObjectInformation.
* \param ReturnLength A variable which receives the number of bytes required to be available in
* \a ObjectInformation.
* \param AccessMode The mode in which to perform access checks.
*/
NTSTATUS KpiQueryInformationObject(
__in HANDLE ProcessHandle,
__in HANDLE Handle,
__in KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass,
__out_bcount(ObjectInformationLength) PVOID ObjectInformation,
__in ULONG ObjectInformationLength,
__out_opt PULONG ReturnLength,
__in KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PEPROCESS process;
KPROCESSOR_MODE referenceMode;
KAPC_STATE apcState;
ULONG returnLength;
PAGED_CODE();
if (AccessMode != KernelMode)
{
__try
{
ProbeForWrite(ObjectInformation, ObjectInformationLength, sizeof(ULONG));
if (ReturnLength)
ProbeForWrite(ReturnLength, sizeof(ULONG), sizeof(ULONG));
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
}
status = ObReferenceObjectByHandle(
ProcessHandle,
0,
*PsProcessType,
AccessMode,
&process,
NULL
);
if (!NT_SUCCESS(status))
return status;
if (process == PsInitialSystemProcess)
{
// A check was added in Windows 7 - if we're attached to the System process, the handle must
// be a kernel handle.
Handle = MakeKernelHandle(Handle);
referenceMode = KernelMode;
}
else
{
// Make sure the handle isn't a kernel handle if we're not attached to the System process.
// This means we can avoid referencing then opening the objects later when calling
// ZwQueryObject, etc.
if (IsKernelHandle(Handle))
{
ObDereferenceObject(process);
return STATUS_INVALID_HANDLE;
}
referenceMode = AccessMode;
}
switch (ObjectInformationClass)
{
case KphObjectBasicInformation:
{
OBJECT_BASIC_INFORMATION basicInfo;
KeStackAttachProcess(process, &apcState);
status = ZwQueryObject(
Handle,
ObjectBasicInformation,
&basicInfo,
sizeof(OBJECT_BASIC_INFORMATION),
NULL
);
KeUnstackDetachProcess(&apcState);
if (NT_SUCCESS(status))
{
if (ObjectInformationLength == sizeof(OBJECT_BASIC_INFORMATION))
{
__try
{
*(POBJECT_BASIC_INFORMATION)ObjectInformation = basicInfo;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
}
}
else
{
status = STATUS_INFO_LENGTH_MISMATCH;
}
}
returnLength = sizeof(OBJECT_BASIC_INFORMATION);
}
break;
case KphObjectNameInformation:
{
PVOID object;
ULONG allocateSize;
POBJECT_NAME_INFORMATION nameInfo;
returnLength = sizeof(OBJECT_NAME_INFORMATION);
// Attach to the process a get a pointer to the object.
KeStackAttachProcess(process, &apcState);
status = ObReferenceObjectByHandle(
Handle,
0,
NULL,
referenceMode,
&object,
NULL
);
KeUnstackDetachProcess(&apcState);
if (NT_SUCCESS(status))
{
allocateSize = ObjectInformationLength;
if (allocateSize < sizeof(OBJECT_NAME_INFORMATION)) // make sure we never try to allocate 0 bytes
allocateSize = sizeof(OBJECT_NAME_INFORMATION);
nameInfo = ExAllocatePoolWithQuotaTag(PagedPool, allocateSize, 'QhpK');
if (nameInfo)
{
// Make sure we don't leak any data.
memset(nameInfo, 0, ObjectInformationLength);
status = KphQueryNameObject(
object,
nameInfo,
ObjectInformationLength,
&returnLength
);
dprintf("KpiQueryInformationObject: called KphQueryNameObject: Handle: 0x%Ix, ObjectInformationLength: %u, returnLength: %u\n",
Handle, ObjectInformationLength, returnLength);
if (NT_SUCCESS(status))
{
// Fix up the buffer pointer.
if (nameInfo->Name.Buffer)
nameInfo->Name.Buffer = (PVOID)((ULONG_PTR)nameInfo->Name.Buffer - (ULONG_PTR)nameInfo + (ULONG_PTR)ObjectInformation);
__try
{
memcpy(ObjectInformation, nameInfo, ObjectInformationLength);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
}
}
ExFreePoolWithTag(nameInfo, 'QhpK');
}
else
{
status = STATUS_INSUFFICIENT_RESOURCES;
}
ObDereferenceObject(object);
}
}
break;
case KphObjectTypeInformation:
{
ULONG allocateSize;
POBJECT_TYPE_INFORMATION typeInfo;
returnLength = sizeof(OBJECT_TYPE_INFORMATION);
allocateSize = ObjectInformationLength;
if (allocateSize < sizeof(OBJECT_TYPE_INFORMATION))
allocateSize = sizeof(OBJECT_TYPE_INFORMATION);
// ObQueryTypeInfo uses ObjectType->Name.MaximumLength instead of
// ObjectType->Name.Length + sizeof(WCHAR) to calculate the required buffer size. In
// Windows 8, certain object types (e.g. TmTx) do NOT include the null terminator in
// MaximumLength, which causes ObQueryTypeInfo to overrun the given buffer. To work
// around this bug, we add some (generous) padding to our allocation.
allocateSize += sizeof(ULONGLONG);
typeInfo = ExAllocatePoolWithQuotaTag(PagedPool, allocateSize, 'QhpK');
if (typeInfo)
{
memset(typeInfo, 0, ObjectInformationLength);
KeStackAttachProcess(process, &apcState);
status = ZwQueryObject(
Handle,
ObjectTypeInformation,
typeInfo,
ObjectInformationLength,
&returnLength
);
KeUnstackDetachProcess(&apcState);
if (NT_SUCCESS(status))
{
// Fix up the buffer pointer.
if (typeInfo->TypeName.Buffer)
typeInfo->TypeName.Buffer = (PVOID)((ULONG_PTR)typeInfo->TypeName.Buffer - (ULONG_PTR)typeInfo + (ULONG_PTR)ObjectInformation);
__try
{
memcpy(ObjectInformation, typeInfo, ObjectInformationLength);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
}
}
ExFreePoolWithTag(typeInfo, 'QhpK');
}
else
{
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
break;
case KphObjectHandleFlagInformation:
{
OBJECT_HANDLE_FLAG_INFORMATION handleFlagInfo;
KeStackAttachProcess(process, &apcState);
status = ZwQueryObject(
Handle,
ObjectHandleFlagInformation,
&handleFlagInfo,
sizeof(OBJECT_HANDLE_FLAG_INFORMATION),
NULL
);
KeUnstackDetachProcess(&apcState);
if (NT_SUCCESS(status))
{
if (ObjectInformationLength == sizeof(OBJECT_HANDLE_FLAG_INFORMATION))
{
__try
{
*(POBJECT_HANDLE_FLAG_INFORMATION)ObjectInformation = handleFlagInfo;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
}
}
else
{
status = STATUS_INFO_LENGTH_MISMATCH;
}
}
returnLength = sizeof(OBJECT_HANDLE_FLAG_INFORMATION);
}
break;
case KphObjectProcessBasicInformation:
{
PROCESS_BASIC_INFORMATION basicInfo;
KeStackAttachProcess(process, &apcState);
status = ZwQueryInformationProcess(
Handle,
ProcessBasicInformation,
&basicInfo,
sizeof(PROCESS_BASIC_INFORMATION),
NULL
);
KeUnstackDetachProcess(&apcState);
if (NT_SUCCESS(status))
{
if (ObjectInformationLength == sizeof(PROCESS_BASIC_INFORMATION))
{
__try
{
*(PPROCESS_BASIC_INFORMATION)ObjectInformation = basicInfo;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
}
}
else
{
status = STATUS_INFO_LENGTH_MISMATCH;
}
}
returnLength = sizeof(PROCESS_BASIC_INFORMATION);
}
break;
case KphObjectThreadBasicInformation:
{
THREAD_BASIC_INFORMATION basicInfo;
KeStackAttachProcess(process, &apcState);
status = ZwQueryInformationThread(
Handle,
ThreadBasicInformation,
&basicInfo,
sizeof(THREAD_BASIC_INFORMATION),
NULL
);
KeUnstackDetachProcess(&apcState);
if (NT_SUCCESS(status))
{
if (ObjectInformationLength == sizeof(THREAD_BASIC_INFORMATION))
{
__try
{
*(PTHREAD_BASIC_INFORMATION)ObjectInformation = basicInfo;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
}
}
else
{
status = STATUS_INFO_LENGTH_MISMATCH;
}
}
returnLength = sizeof(THREAD_BASIC_INFORMATION);
}
break;
case KphObjectEtwRegBasicInformation:
{
PVOID etwReg;
PVOID objectType;
PUNICODE_STRING objectTypeName;
UNICODE_STRING etwRegistrationName;
PVOID guidEntry;
ETWREG_BASIC_INFORMATION basicInfo;
// Check dynamic data requirements.
if (KphDynEgeGuid != -1 &&
KphDynEreGuidEntry != -1 &&
KphDynOtName != -1)
{
// Attach to the process and get a pointer to the object. We don't have a pointer to
// the EtwRegistration object type, so we'll just have to check the type name.
KeStackAttachProcess(process, &apcState);
status = ObReferenceObjectByHandle(
Handle,
0,
NULL,
referenceMode,
&etwReg,
NULL
);
KeUnstackDetachProcess(&apcState);
if (NT_SUCCESS(status))
{
// Check the type name.
objectType = ObGetObjectType(etwReg);
if (objectType)
{
objectTypeName = (PUNICODE_STRING)((ULONG_PTR)objectType + KphDynOtName);
RtlInitUnicodeString(&etwRegistrationName, L"EtwRegistration");
if (!RtlEqualUnicodeString(objectTypeName, &etwRegistrationName, FALSE))
{
status = STATUS_OBJECT_TYPE_MISMATCH;
}
}
else
{
status = STATUS_NOT_SUPPORTED;
}
if (NT_SUCCESS(status))
{
guidEntry = *(PVOID *)((ULONG_PTR)etwReg + KphDynEreGuidEntry);
if (guidEntry)
basicInfo.Guid = *(GUID *)((ULONG_PTR)guidEntry + KphDynEgeGuid);
else
memset(&basicInfo.Guid, 0, sizeof(GUID));
basicInfo.SessionId = 0; // not implemented
if (ObjectInformationLength == sizeof(ETWREG_BASIC_INFORMATION))
{
__try
{
*(PETWREG_BASIC_INFORMATION)ObjectInformation = basicInfo;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
}
}
else
{
status = STATUS_INFO_LENGTH_MISMATCH;
}
}
ObDereferenceObject(etwReg);
}
}
else
{
status = STATUS_NOT_SUPPORTED;
}
returnLength = sizeof(ETWREG_BASIC_INFORMATION);
}
break;
case KphObjectFileObjectInformation:
{
PFILE_OBJECT fileObject;
KPH_FILE_OBJECT_INFORMATION objectInfo;
KeStackAttachProcess(process, &apcState);
status = ObReferenceObjectByHandle(
Handle,
0,
*IoFileObjectType,
referenceMode,
&fileObject,
NULL
);
KeUnstackDetachProcess(&apcState);
if (NT_SUCCESS(status))
{
objectInfo.LockOperation = fileObject->LockOperation;
objectInfo.DeletePending = fileObject->DeletePending;
objectInfo.ReadAccess = fileObject->ReadAccess;
objectInfo.WriteAccess = fileObject->WriteAccess;
objectInfo.DeleteAccess = fileObject->DeleteAccess;
objectInfo.SharedRead = fileObject->SharedRead;
objectInfo.SharedWrite = fileObject->SharedWrite;
objectInfo.SharedDelete = fileObject->SharedDelete;
objectInfo.CurrentByteOffset = fileObject->CurrentByteOffset;
objectInfo.Flags = fileObject->Flags;
if (ObjectInformationLength == sizeof(KPH_FILE_OBJECT_INFORMATION))
{
__try
{
*(PKPH_FILE_OBJECT_INFORMATION)ObjectInformation = objectInfo;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
}
}
else
{
status = STATUS_INFO_LENGTH_MISMATCH;
}
ObDereferenceObject(fileObject);
}
returnLength = sizeof(KPH_FILE_OBJECT_INFORMATION);
}
break;
case KphObjectFileObjectDriver:
{
PFILE_OBJECT fileObject;
HANDLE driverHandle;
if (ObjectInformationLength == sizeof(KPH_FILE_OBJECT_DRIVER))
{
KeStackAttachProcess(process, &apcState);
status = ObReferenceObjectByHandle(
Handle,
0,
*IoFileObjectType,
referenceMode,
&fileObject,
NULL
);
KeUnstackDetachProcess(&apcState);
if (NT_SUCCESS(status))
{
if (fileObject->DeviceObject && fileObject->DeviceObject->DriverObject)
{
status = ObOpenObjectByPointer(
fileObject->DeviceObject->DriverObject,
0,
NULL,
SYNCHRONIZE,
*IoDriverObjectType,
AccessMode,
&driverHandle
);
}
else
{
driverHandle = NULL;
}
if (NT_SUCCESS(status))
{
__try
{
((PKPH_FILE_OBJECT_DRIVER)ObjectInformation)->DriverHandle = driverHandle;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
}
}
ObDereferenceObject(fileObject);
}
}
else
{
status = STATUS_INFO_LENGTH_MISMATCH;
}
}
break;
default:
status = STATUS_INVALID_INFO_CLASS;
returnLength = 0;
break;
}
ObDereferenceObject(process);
if (ReturnLength)
{
if (AccessMode != KernelMode)
{
__try
{
*ReturnLength = returnLength;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
NOTHING;
}
}
else
{
*ReturnLength = returnLength;
}
}
return status;
}
/**
* Sets object information.
*
* \param ProcessHandle A handle to a process.
* \param Handle A handle which is present in the process referenced by \a ProcessHandle.
* \param ObjectInformationClass The type of information to set.
* \param ObjectInformation A buffer which contains the information to set.
* \param ObjectInformationLength The number of bytes present in \a ObjectInformation.
* \param AccessMode The mode in which to perform access checks.
*/
NTSTATUS KpiSetInformationObject(
__in HANDLE ProcessHandle,
__in HANDLE Handle,
__in KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass,
__in_bcount(ObjectInformationLength) PVOID ObjectInformation,
__in ULONG ObjectInformationLength,
__in KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
PEPROCESS process;
KAPC_STATE apcState;
PAGED_CODE();
if (AccessMode != KernelMode)
{
ULONG alignment;
switch (ObjectInformationClass)
{
case KphObjectHandleFlagInformation:
alignment = sizeof(BOOLEAN);
break;
default:
alignment = sizeof(ULONG);
break;
}
__try
{
ProbeForRead(ObjectInformation, ObjectInformationLength, alignment);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return GetExceptionCode();
}
}
status = ObReferenceObjectByHandle(
ProcessHandle,
0,
*PsProcessType,
AccessMode,
&process,
NULL
);
if (!NT_SUCCESS(status))
return status;
if (process == PsInitialSystemProcess)
{
Handle = MakeKernelHandle(Handle);
}
else
{
if (IsKernelHandle(Handle))
{
ObDereferenceObject(process);
return STATUS_INVALID_HANDLE;
}
}
switch (ObjectInformationClass)
{
case KphObjectHandleFlagInformation:
{
OBJECT_HANDLE_FLAG_INFORMATION handleFlagInfo;
if (ObjectInformationLength == sizeof(OBJECT_HANDLE_FLAG_INFORMATION))
{
__try
{
handleFlagInfo = *(POBJECT_HANDLE_FLAG_INFORMATION)ObjectInformation;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
}
}
else
{
status = STATUS_INFO_LENGTH_MISMATCH;
}
if (NT_SUCCESS(status))
{
KeStackAttachProcess(process, &apcState);
status = ObSetHandleAttributes(Handle, &handleFlagInfo, KernelMode);
KeUnstackDetachProcess(&apcState);
}
}
break;
default:
status = STATUS_INVALID_INFO_CLASS;
break;
}
ObDereferenceObject(process);
return status;
}
NTSTATUS KphOpenNamedObject(
__out PHANDLE ObjectHandle,
__in ACCESS_MASK DesiredAccess,
__in POBJECT_ATTRIBUTES ObjectAttributes,
__in POBJECT_TYPE ObjectType,
__in KPROCESSOR_MODE AccessMode
)
{
NTSTATUS status;
HANDLE objectHandle;
PAGED_CODE();
// Open the object.
status = ObOpenObjectByName(
ObjectAttributes,
ObjectType,
AccessMode,
NULL,
DesiredAccess,
NULL,
&objectHandle
);
// Pass the handle back.
if (AccessMode != KernelMode)
{
__try
{
*ObjectHandle = objectHandle;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
}
}
else
{
*ObjectHandle = objectHandle;
}
return status;
}