mirror of
https://github.com/mirror/processhacker
synced 2026-06-08 16:03:24 +00:00
7d1115657b
git-svn-id: svn://svn.code.sf.net/p/processhacker/code@4172 21ef857c-d57f-4fe0-8362-d861dc6d29cd
405 lines
11 KiB
C
405 lines
11 KiB
C
/*
|
|
* KProcessHacker
|
|
*
|
|
* Copyright (C) 2010-2011 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <kph.h>
|
|
#include <dyndata.h>
|
|
|
|
DRIVER_INITIALIZE DriverEntry;
|
|
DRIVER_UNLOAD DriverUnload;
|
|
__drv_dispatchType(IRP_MJ_CREATE) DRIVER_DISPATCH KphDispatchCreate;
|
|
|
|
ULONG KphpReadIntegerParameter(
|
|
__in_opt HANDLE KeyHandle,
|
|
__in PUNICODE_STRING ValueName,
|
|
__in ULONG DefaultValue
|
|
);
|
|
|
|
NTSTATUS KphpReadDriverParameters(
|
|
__in PUNICODE_STRING RegistryPath
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, DriverEntry)
|
|
#pragma alloc_text(PAGE, DriverUnload)
|
|
#pragma alloc_text(PAGE, KphpReadIntegerParameter)
|
|
#pragma alloc_text(PAGE, KphpReadDriverParameters)
|
|
#pragma alloc_text(PAGE, KpiGetFeatures)
|
|
#endif
|
|
|
|
PDRIVER_OBJECT KphDriverObject;
|
|
PDEVICE_OBJECT KphDeviceObject;
|
|
ULONG KphFeatures;
|
|
KPH_PARAMETERS KphParameters;
|
|
|
|
NTSTATUS DriverEntry(
|
|
__in PDRIVER_OBJECT DriverObject,
|
|
__in PUNICODE_STRING RegistryPath
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING deviceName;
|
|
PDEVICE_OBJECT deviceObject;
|
|
|
|
PAGED_CODE();
|
|
|
|
KphDriverObject = DriverObject;
|
|
|
|
if (!NT_SUCCESS(status = KphDynamicDataInitialization()))
|
|
return status;
|
|
|
|
KphDynamicImport();
|
|
|
|
if (!NT_SUCCESS(status = KphpReadDriverParameters(RegistryPath)))
|
|
return status;
|
|
|
|
// Create the device.
|
|
|
|
RtlInitUnicodeString(&deviceName, KPH_DEVICE_NAME);
|
|
|
|
status = IoCreateDevice(
|
|
DriverObject,
|
|
0,
|
|
&deviceName,
|
|
FILE_DEVICE_UNKNOWN,
|
|
FILE_DEVICE_SECURE_OPEN,
|
|
FALSE,
|
|
&deviceObject
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
return status;
|
|
|
|
KphDeviceObject = deviceObject;
|
|
|
|
// Set up I/O.
|
|
|
|
DriverObject->MajorFunction[IRP_MJ_CREATE] = KphDispatchCreate;
|
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KphDispatchDeviceControl;
|
|
DriverObject->DriverUnload = DriverUnload;
|
|
|
|
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
dprintf("Driver loaded\n");
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID DriverUnload(
|
|
__in PDRIVER_OBJECT DriverObject
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
IoDeleteDevice(KphDeviceObject);
|
|
|
|
dprintf("Driver unloaded\n");
|
|
}
|
|
|
|
NTSTATUS KphDispatchCreate(
|
|
__in PDEVICE_OBJECT DeviceObject,
|
|
__in PIRP Irp
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PIO_STACK_LOCATION stackLocation;
|
|
PIO_SECURITY_CONTEXT securityContext;
|
|
|
|
stackLocation = IoGetCurrentIrpStackLocation(Irp);
|
|
securityContext = stackLocation->Parameters.Create.SecurityContext;
|
|
|
|
dprintf("Client (PID %Iu) is connecting\n", PsGetCurrentProcessId());
|
|
|
|
if (KphParameters.SecurityLevel == KphSecurityPrivilegeCheck)
|
|
{
|
|
UCHAR requiredPrivilegesBuffer[FIELD_OFFSET(PRIVILEGE_SET, Privilege) + sizeof(LUID_AND_ATTRIBUTES)];
|
|
PPRIVILEGE_SET requiredPrivileges;
|
|
|
|
// Check for SeDebugPrivilege.
|
|
|
|
requiredPrivileges = (PPRIVILEGE_SET)requiredPrivilegesBuffer;
|
|
requiredPrivileges->PrivilegeCount = 1;
|
|
requiredPrivileges->Control = PRIVILEGE_SET_ALL_NECESSARY;
|
|
requiredPrivileges->Privilege[0].Luid.LowPart = SE_DEBUG_PRIVILEGE;
|
|
requiredPrivileges->Privilege[0].Luid.HighPart = 0;
|
|
requiredPrivileges->Privilege[0].Attributes = 0;
|
|
|
|
if (!SePrivilegeCheck(
|
|
requiredPrivileges,
|
|
&securityContext->AccessState->SubjectSecurityContext,
|
|
Irp->RequestorMode
|
|
))
|
|
{
|
|
status = STATUS_PRIVILEGE_NOT_HELD;
|
|
dprintf("Client (PID %Iu) was rejected\n", PsGetCurrentProcessId());
|
|
}
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* Reads an integer parameter from the registry.
|
|
*
|
|
* \param KeyHandle A handle to the Parameters key. If NULL, the function
|
|
* fails immediately and returns \a DefaultValue.
|
|
* \param ValueName The name of the parameter.
|
|
* \param DefaultValue The value that is returned if the function fails
|
|
* to retrieve the parameter from the registry.
|
|
*
|
|
* \return The parameter value, or \a DefaultValue if the function failed.
|
|
*/
|
|
ULONG KphpReadIntegerParameter(
|
|
__in_opt HANDLE KeyHandle,
|
|
__in PUNICODE_STRING ValueName,
|
|
__in ULONG DefaultValue
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
UCHAR buffer[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + sizeof(ULONG)];
|
|
PKEY_VALUE_PARTIAL_INFORMATION info;
|
|
ULONG resultLength;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (!KeyHandle)
|
|
return DefaultValue;
|
|
|
|
info = (PKEY_VALUE_PARTIAL_INFORMATION)buffer;
|
|
|
|
status = ZwQueryValueKey(
|
|
KeyHandle,
|
|
ValueName,
|
|
KeyValuePartialInformation,
|
|
info,
|
|
sizeof(buffer),
|
|
&resultLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dprintf("Unable to query parameter %.*S: 0x%x", ValueName->Length / sizeof(WCHAR), ValueName->Buffer, status);
|
|
return DefaultValue;
|
|
}
|
|
|
|
return *(PULONG)info->Data;
|
|
}
|
|
|
|
/**
|
|
* Reads the driver parameters.
|
|
*
|
|
* \param RegistryPath The registry path of the driver.
|
|
*/
|
|
NTSTATUS KphpReadDriverParameters(
|
|
__in PUNICODE_STRING RegistryPath
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
HANDLE parametersKeyHandle;
|
|
UNICODE_STRING parametersString;
|
|
UNICODE_STRING parametersKeyName;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
UNICODE_STRING valueName;
|
|
|
|
PAGED_CODE();
|
|
|
|
// Open the Parameters key.
|
|
|
|
RtlInitUnicodeString(¶metersString, L"\\Parameters");
|
|
|
|
parametersKeyName.Length = RegistryPath->Length + parametersString.Length;
|
|
parametersKeyName.MaximumLength = parametersKeyName.Length;
|
|
parametersKeyName.Buffer = ExAllocatePoolWithTag(PagedPool, parametersKeyName.MaximumLength, 'ThpK');
|
|
|
|
if (!parametersKeyName.Buffer)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
memcpy(parametersKeyName.Buffer, RegistryPath->Buffer, RegistryPath->Length);
|
|
memcpy(¶metersKeyName.Buffer[RegistryPath->Length / sizeof(WCHAR)], parametersString.Buffer, parametersString.Length);
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
¶metersKeyName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
status = ZwOpenKey(
|
|
¶metersKeyHandle,
|
|
KEY_READ,
|
|
&objectAttributes
|
|
);
|
|
ExFreePoolWithTag(parametersKeyName.Buffer, 'ThpK');
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dprintf("Unable to open Parameters key: 0x%x\n", status);
|
|
status = STATUS_SUCCESS;
|
|
parametersKeyHandle = NULL;
|
|
// Continue so we can set up defaults.
|
|
}
|
|
|
|
// Read in the parameters.
|
|
|
|
RtlInitUnicodeString(&valueName, L"SecurityLevel");
|
|
KphParameters.SecurityLevel = KphpReadIntegerParameter(parametersKeyHandle, &valueName, KphSecurityNone);
|
|
|
|
RtlInitUnicodeString(&valueName, L"DisableDynamicProcedureScan");
|
|
KphParameters.DisableDynamicProcedureScan = KphpReadIntegerParameter(parametersKeyHandle, &valueName, FALSE);
|
|
|
|
if (parametersKeyHandle)
|
|
ZwClose(parametersKeyHandle);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS KpiGetFeatures(
|
|
__out PULONG Features,
|
|
__in KPROCESSOR_MODE AccessMode
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (AccessMode != KernelMode)
|
|
{
|
|
__try
|
|
{
|
|
ProbeForWrite(Features, sizeof(ULONG), sizeof(ULONG));
|
|
*Features = KphFeatures;
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
return GetExceptionCode();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*Features = KphFeatures;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Enumerates the modules loaded by the kernel.
|
|
*
|
|
* \param Modules A variable which receives a pointer
|
|
* to a structure containing information about
|
|
* the kernel modules. The structure must be freed with
|
|
* the tag 'ThpK'.
|
|
*/
|
|
NTSTATUS KphEnumerateSystemModules(
|
|
__out PRTL_PROCESS_MODULES *Modules
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PVOID buffer;
|
|
ULONG bufferSize;
|
|
ULONG attempts;
|
|
|
|
bufferSize = 2048;
|
|
attempts = 8;
|
|
|
|
do
|
|
{
|
|
buffer = ExAllocatePoolWithTag(PagedPool, bufferSize, 'ThpK');
|
|
|
|
if (!buffer)
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
status = ZwQuerySystemInformation(
|
|
SystemModuleInformation,
|
|
buffer,
|
|
bufferSize,
|
|
&bufferSize
|
|
);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
*Modules = buffer;
|
|
|
|
return status;
|
|
}
|
|
|
|
ExFreePoolWithTag(buffer, 'ThpK');
|
|
|
|
if (status != STATUS_INFO_LENGTH_MISMATCH)
|
|
{
|
|
break;
|
|
}
|
|
} while (--attempts);
|
|
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* Checks if an address range lies within a kernel module.
|
|
*
|
|
* \param Address The beginning of the address range.
|
|
* \param Length The number of bytes in the address range.
|
|
*/
|
|
NTSTATUS KphValidateAddressForSystemModules(
|
|
__in PVOID Address,
|
|
__in SIZE_T Length
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PRTL_PROCESS_MODULES modules;
|
|
ULONG i;
|
|
BOOLEAN valid;
|
|
|
|
status = KphEnumerateSystemModules(&modules);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
return status;
|
|
|
|
valid = FALSE;
|
|
|
|
for (i = 0; i < modules->NumberOfModules; i++)
|
|
{
|
|
if (
|
|
(ULONG_PTR)Address + Length >= (ULONG_PTR)Address &&
|
|
(ULONG_PTR)Address >= (ULONG_PTR)modules->Modules[i].ImageBase &&
|
|
(ULONG_PTR)Address + Length <= (ULONG_PTR)modules->Modules[i].ImageBase + modules->Modules[i].ImageSize
|
|
)
|
|
{
|
|
dprintf("Validated address 0x%Ix in %s\n", Address, modules->Modules[i].FullPathName);
|
|
valid = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ExFreePoolWithTag(modules, 'ThpK');
|
|
|
|
if (valid)
|
|
status = STATUS_SUCCESS;
|
|
else
|
|
status = STATUS_ACCESS_VIOLATION;
|
|
|
|
return status;
|
|
}
|