2025-05-13 19:45:22 +03:00

567 lines
16 KiB
C

/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <kph.h>
NTSTATUS KphDispatchDeviceControl(
__in PDEVICE_OBJECT DeviceObject,
__in PIRP Irp
)
{
NTSTATUS status;
PIO_STACK_LOCATION stackLocation;
PFILE_OBJECT fileObject;
PKPH_CLIENT client;
PVOID originalInput;
ULONG inputLength;
ULONG ioControlCode;
KPROCESSOR_MODE accessMode;
UCHAR capturedInput[16 * sizeof(ULONG_PTR)];
PVOID capturedInputPointer;
#define VERIFY_INPUT_LENGTH \
do { \
/* Ensure at compile time that our local buffer fits this particular call. */ \
C_ASSERT(sizeof(*input) <= sizeof(capturedInput)); \
\
if (inputLength != sizeof(*input)) \
{ \
status = STATUS_INFO_LENGTH_MISMATCH; \
goto ControlEnd; \
} \
} while (0)
stackLocation = IoGetCurrentIrpStackLocation(Irp);
fileObject = stackLocation->FileObject;
client = fileObject->FsContext;
originalInput = stackLocation->Parameters.DeviceIoControl.Type3InputBuffer;
inputLength = stackLocation->Parameters.DeviceIoControl.InputBufferLength;
ioControlCode = stackLocation->Parameters.DeviceIoControl.IoControlCode;
accessMode = Irp->RequestorMode;
// Make sure we have a client object.
if (!client)
{
status = STATUS_INTERNAL_ERROR;
goto ControlEnd;
}
// Enforce signature requirement if necessary.
if ((ioControlCode != KPH_GETFEATURES && ioControlCode != KPH_VERIFYCLIENT) &&
(KphParameters.SecurityLevel == KphSecuritySignatureCheck ||
KphParameters.SecurityLevel == KphSecuritySignatureAndPrivilegeCheck) &&
!client->VerificationSucceeded)
{
status = STATUS_ACCESS_DENIED;
goto ControlEnd;
}
// Make sure we actually have input if the input length is non-zero.
if (inputLength != 0 && !originalInput)
{
status = STATUS_INVALID_BUFFER_SIZE;
goto ControlEnd;
}
// Make sure the caller isn't giving us a huge buffer. If they are, it can't be correct because
// we have a compile-time check that makes sure our buffer can store the arguments for all the
// calls.
if (inputLength > sizeof(capturedInput))
{
status = STATUS_INVALID_BUFFER_SIZE;
goto ControlEnd;
}
// Probe and capture the input buffer.
if (accessMode != KernelMode)
{
__try
{
ProbeForRead(originalInput, inputLength, sizeof(UCHAR));
memcpy(capturedInput, originalInput, inputLength);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
goto ControlEnd;
}
}
else
{
memcpy(capturedInput, originalInput, inputLength);
}
capturedInputPointer = capturedInput; // avoid casting below
switch (ioControlCode)
{
case KPH_GETFEATURES:
{
struct
{
PULONG Features;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiGetFeatures(
input->Features,
accessMode
);
}
break;
case KPH_VERIFYCLIENT:
{
struct
{
PVOID CodeAddress;
PVOID Signature;
ULONG SignatureSize;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
if (accessMode == UserMode)
{
status = KpiVerifyClient(
input->CodeAddress,
input->Signature,
input->SignatureSize,
client
);
}
else
{
status = STATUS_UNSUCCESSFUL;
}
}
break;
case KPH_RETRIEVEKEY:
{
struct
{
KPH_KEY_LEVEL KeyLevel;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
if (accessMode == UserMode)
{
status = KphRetrieveKeyViaApc(
client,
input->KeyLevel,
Irp
);
}
else
{
status = STATUS_UNSUCCESSFUL;
}
}
break;
case KPH_OPENPROCESS:
{
struct
{
PHANDLE ProcessHandle;
ACCESS_MASK DesiredAccess;
PCLIENT_ID ClientId;
KPH_KEY Key;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiOpenProcess(
input->ProcessHandle,
input->DesiredAccess,
input->ClientId,
input->Key,
client,
accessMode
);
}
break;
case KPH_OPENPROCESSTOKEN:
{
struct
{
HANDLE ProcessHandle;
ACCESS_MASK DesiredAccess;
PHANDLE TokenHandle;
KPH_KEY Key;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiOpenProcessToken(
input->ProcessHandle,
input->DesiredAccess,
input->TokenHandle,
input->Key,
client,
accessMode
);
}
break;
case KPH_OPENPROCESSJOB:
{
struct
{
HANDLE ProcessHandle;
ACCESS_MASK DesiredAccess;
PHANDLE JobHandle;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiOpenProcessJob(
input->ProcessHandle,
input->DesiredAccess,
input->JobHandle,
accessMode
);
}
break;
case KPH_TERMINATEPROCESS:
{
struct
{
HANDLE ProcessHandle;
NTSTATUS ExitStatus;
KPH_KEY Key;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiTerminateProcess(
input->ProcessHandle,
input->ExitStatus,
input->Key,
client,
accessMode
);
}
break;
case KPH_READVIRTUALMEMORYUNSAFE:
{
struct
{
HANDLE ProcessHandle;
PVOID BaseAddress;
PVOID Buffer;
SIZE_T BufferSize;
PSIZE_T NumberOfBytesRead;
KPH_KEY Key;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiReadVirtualMemoryUnsafe(
input->ProcessHandle,
input->BaseAddress,
input->Buffer,
input->BufferSize,
input->NumberOfBytesRead,
input->Key,
client,
accessMode
);
}
break;
case KPH_QUERYINFORMATIONPROCESS:
{
struct
{
HANDLE ProcessHandle;
KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass;
PVOID ProcessInformation;
ULONG ProcessInformationLength;
PULONG ReturnLength;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiQueryInformationProcess(
input->ProcessHandle,
input->ProcessInformationClass,
input->ProcessInformation,
input->ProcessInformationLength,
input->ReturnLength,
accessMode
);
}
break;
case KPH_SETINFORMATIONPROCESS:
{
struct
{
HANDLE ProcessHandle;
KPH_PROCESS_INFORMATION_CLASS ProcessInformationClass;
PVOID ProcessInformation;
ULONG ProcessInformationLength;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiSetInformationProcess(
input->ProcessHandle,
input->ProcessInformationClass,
input->ProcessInformation,
input->ProcessInformationLength,
accessMode
);
}
break;
case KPH_OPENTHREAD:
{
struct
{
PHANDLE ThreadHandle;
ACCESS_MASK DesiredAccess;
PCLIENT_ID ClientId;
KPH_KEY Key;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiOpenThread(
input->ThreadHandle,
input->DesiredAccess,
input->ClientId,
input->Key,
client,
accessMode
);
}
break;
case KPH_OPENTHREADPROCESS:
{
struct
{
HANDLE ThreadHandle;
ACCESS_MASK DesiredAccess;
PHANDLE ProcessHandle;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiOpenThreadProcess(
input->ThreadHandle,
input->DesiredAccess,
input->ProcessHandle,
accessMode
);
}
break;
case KPH_CAPTURESTACKBACKTRACETHREAD:
{
struct
{
HANDLE ThreadHandle;
ULONG FramesToSkip;
ULONG FramesToCapture;
PVOID *BackTrace;
PULONG CapturedFrames;
PULONG BackTraceHash;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiCaptureStackBackTraceThread(
input->ThreadHandle,
input->FramesToSkip,
input->FramesToCapture,
input->BackTrace,
input->CapturedFrames,
input->BackTraceHash,
accessMode
);
}
break;
case KPH_QUERYINFORMATIONTHREAD:
{
struct
{
HANDLE ThreadHandle;
KPH_THREAD_INFORMATION_CLASS ThreadInformationClass;
PVOID ThreadInformation;
ULONG ThreadInformationLength;
PULONG ReturnLength;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiQueryInformationThread(
input->ThreadHandle,
input->ThreadInformationClass,
input->ThreadInformation,
input->ThreadInformationLength,
input->ReturnLength,
accessMode
);
}
break;
case KPH_SETINFORMATIONTHREAD:
{
struct
{
HANDLE ThreadHandle;
KPH_THREAD_INFORMATION_CLASS ThreadInformationClass;
PVOID ThreadInformation;
ULONG ThreadInformationLength;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiSetInformationThread(
input->ThreadHandle,
input->ThreadInformationClass,
input->ThreadInformation,
input->ThreadInformationLength,
accessMode
);
}
break;
case KPH_ENUMERATEPROCESSHANDLES:
{
struct
{
HANDLE ProcessHandle;
PVOID Buffer;
ULONG BufferLength;
PULONG ReturnLength;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiEnumerateProcessHandles(
input->ProcessHandle,
input->Buffer,
input->BufferLength,
input->ReturnLength,
accessMode
);
}
break;
case KPH_QUERYINFORMATIONOBJECT:
{
struct
{
HANDLE ProcessHandle;
HANDLE Handle;
KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass;
PVOID ObjectInformation;
ULONG ObjectInformationLength;
PULONG ReturnLength;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiQueryInformationObject(
input->ProcessHandle,
input->Handle,
input->ObjectInformationClass,
input->ObjectInformation,
input->ObjectInformationLength,
input->ReturnLength,
accessMode
);
}
break;
case KPH_SETINFORMATIONOBJECT:
{
struct
{
HANDLE ProcessHandle;
HANDLE Handle;
KPH_OBJECT_INFORMATION_CLASS ObjectInformationClass;
PVOID ObjectInformation;
ULONG ObjectInformationLength;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiSetInformationObject(
input->ProcessHandle,
input->Handle,
input->ObjectInformationClass,
input->ObjectInformation,
input->ObjectInformationLength,
accessMode
);
}
break;
case KPH_OPENDRIVER:
{
struct
{
PHANDLE DriverHandle;
ACCESS_MASK DesiredAccess;
POBJECT_ATTRIBUTES ObjectAttributes;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiOpenDriver(
input->DriverHandle,
input->DesiredAccess,
input->ObjectAttributes,
accessMode
);
}
break;
case KPH_QUERYINFORMATIONDRIVER:
{
struct
{
HANDLE DriverHandle;
DRIVER_INFORMATION_CLASS DriverInformationClass;
PVOID DriverInformation;
ULONG DriverInformationLength;
PULONG ReturnLength;
} *input = capturedInputPointer;
VERIFY_INPUT_LENGTH;
status = KpiQueryInformationDriver(
input->DriverHandle,
input->DriverInformationClass,
input->DriverInformation,
input->DriverInformationLength,
input->ReturnLength,
accessMode
);
}
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
ControlEnd:
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}