MalwareSourceCode/Win32/Proof of Concepts/HookDeviceIocontrlFile/HookDeviceIoControlFileDrv/HookDeviceIoControlFile/handlers.c

564 lines
16 KiB
C
Raw Normal View History

2022-04-12 01:00:13 +00:00
#include "stdafx.h"
NT_DEVICE_IO_CONTROL_FILE old_NtDeviceIoControlFile = NULL;
/**
* Fuzzing settings
*/
ULONG m_FuzzOptions = 0;
FUZZING_TYPE m_FuzzingType = FuzzingType_Random;
/**
* Exported variables for acessing to the
* last IOCTL request information from the kernel debugger.
*/
PDEVICE_OBJECT currentDeviceObject = NULL;
PDRIVER_OBJECT currentDriverObject = NULL;
ULONG currentIoControlCode = 0;
PVOID currentInputBuffer = NULL;
ULONG currentInputBufferLength = 0;
PVOID currentOutputBuffer = NULL;
ULONG currentOutputBufferLength = 0;
/**
* Handle and objetc pointer of the fuzzer's process (uses for fair fuzzing mode)
*/
HANDLE m_FuzzThreadId = 0;
PEPROCESS m_FuzzProcess = NULL;
PUSER_MODE_DATA m_UserModeData = NULL;
/**
* Some fuzzing parameters
*/
#define RANDOM_FUZZING_ITERATIONS 10
#define BUFFERED_FUZZING_ITERATIONS 5
#define DWORD_FUZZING_MAX_LENGTH 0x2000
#define DWORD_FUZZING_DELTA 4
ULONG g_newDeviceIoControlFileCallCount = 0;
#ifdef _X86_
// pointer values for invalid kernel and user buffers
#define KERNEL_BUFFER_ADDRESS (PVOID)(0xFFFF0000)
#define USER_BUFFER_ADDRESS (PVOID)(0x00001000)
#elif _AMD64_
#define KERNEL_BUFFER_ADDRESS (PVOID)(0xFFFFFFFFFFFF0000)
#define USER_BUFFER_ADDRESS (PVOID)(0x0000000000001000)
#endif
// constants for dword fuzzing
ULONG m_DwordFuzzingConstants[] =
{
0x00000000,
0x00001000,
0xFFFF0000,
0xFFFFFFFF
};
// defined in driver.cpp
extern PDEVICE_OBJECT m_DeviceObject;
extern KMUTEX m_CommonMutex;
extern PCOMMON_LST m_ProcessesList;
//--------------------------------------------------------------------------------------
PCOMMON_LST_ENTRY LookupProcessInfo(PEPROCESS Process)
{
PCOMMON_LST_ENTRY process_entry = NULL;
KIRQL OldIrql;
KeAcquireSpinLock(&m_ProcessesList->ListLock, &OldIrql);
__try
{
PCOMMON_LST_ENTRY e = m_ProcessesList->list_head;
// enumerate all processes
while (e)
{
if (e->Data && e->DataSize == sizeof(LST_PROCESS_INFO))
{
PLST_PROCESS_INFO Info = (PLST_PROCESS_INFO)e->Data;
if (Info->Process == Process)
{
process_entry = e;
break;
}
}
e = e->next;
}
}
__finally
{
KeReleaseSpinLock(&m_ProcessesList->ListLock, OldIrql);
}
return process_entry;
}
//--------------------------------------------------------------------------------------
void FreeProcessInfo(void)
{
KIRQL OldIrql;
KeAcquireSpinLock(&m_ProcessesList->ListLock, &OldIrql);
__try
{
PCOMMON_LST_ENTRY e = m_ProcessesList->list_head;
// enumerate all processes
while (e)
{
if (e->Data && e->DataSize == sizeof(LST_PROCESS_INFO))
{
PLST_PROCESS_INFO Info = (PLST_PROCESS_INFO)e->Data;
if (Info->usImagePath.Buffer)
{
// free process image path
RtlFreeUnicodeString(&Info->usImagePath);
}
}
e = e->next;
}
}
__finally
{
KeReleaseSpinLock(&m_ProcessesList->ListLock, OldIrql);
}
}
//--------------------------------------------------------------------------------------
void NTAPI ProcessNotifyRoutine(HANDLE ParentId, HANDLE ProcessId, BOOLEAN Create)
{
PEPROCESS Process;
NTSTATUS ns = PsLookupProcessByProcessId(ProcessId, &Process);
if (NT_SUCCESS(ns))
{
KeWaitForMutexObject(&m_CommonMutex, UserRequest, KernelMode, FALSE, NULL);
__try
{
if (Create)
{
// process has been created
UNICODE_STRING ImagePath;
// get full image path for this process
if (GetProcessFullImagePath(Process, &ImagePath))
{
WCHAR wcProcess[0x200];
UNICODE_STRING usProcess;
LST_PROCESS_INFO Info;
LogData("Process "IFMT" started: '%wZ' (PID: %d)\r\n\r\n", Process, &ImagePath, ProcessId);
swprintf(wcProcess, L"'%wZ' (" IFMT_W L")", &ImagePath, Process);
RtlInitUnicodeString(&usProcess, wcProcess);
Info.Process = Process;
Info.ProcessId = ProcessId;
Info.usImagePath.Buffer = ImagePath.Buffer;
Info.usImagePath.Length = ImagePath.Length;
Info.usImagePath.MaximumLength = ImagePath.MaximumLength;
// add process information into the list
if (LstAddEntry(m_ProcessesList, &usProcess, &Info, sizeof(Info)) == NULL)
{
RtlFreeUnicodeString(&ImagePath);
}
}
}
else
{
PCOMMON_LST_ENTRY process_entry = NULL;
LogData("Process "IFMT" terminated\r\n\r\n", Process);
// process terminating
process_entry = LookupProcessInfo(Process);
if (process_entry)
{
if (process_entry->Data &&
process_entry->DataSize == sizeof(LST_PROCESS_INFO))
{
PLST_PROCESS_INFO Info = (PLST_PROCESS_INFO)process_entry->Data;
if (Info->usImagePath.Buffer)
{
// free process image path
RtlFreeUnicodeString(&Info->usImagePath);
}
}
// delete information about this process from list
LstDelEntry(m_ProcessesList, process_entry);
}
}
}
__finally
{
KeReleaseMutex(&m_CommonMutex, FALSE);
}
ObDereferenceObject(Process);
}
else
{
DbgMsg(__FILE__, __LINE__, "PsLookupProcessByProcessId() fails; status: 0x%.8x\n", ns);
}
}
//--------------------------------------------------------------------------------------
PUNICODE_STRING LookupProcessName(PEPROCESS TargetProcess)
{
PEPROCESS Process = TargetProcess;
PCOMMON_LST_ENTRY process_entry = NULL;
HANDLE ProcessId = NULL;
UNICODE_STRING ImagePath;
PUNICODE_STRING Ret = NULL;
if (Process == NULL)
{
// lookup current process information entry
Process = PsGetCurrentProcess();
}
process_entry = LookupProcessInfo(Process);
if (process_entry)
{
if (process_entry->Data &&
process_entry->DataSize == sizeof(LST_PROCESS_INFO))
{
PLST_PROCESS_INFO Info = (PLST_PROCESS_INFO)process_entry->Data;
if (Info->usImagePath.Buffer)
{
// return process image path
return &Info->usImagePath;
}
}
return NULL;
}
// information entry for current process is not found, allocate it
ProcessId = PsGetCurrentProcessId();
// get full image path for this process
if (GetProcessFullImagePath(Process, &ImagePath))
{
WCHAR wcProcess[0x200];
UNICODE_STRING usProcess;
LST_PROCESS_INFO Info;
swprintf(wcProcess, L"'%wZ' (" IFMT_W L")", &ImagePath, Process);
RtlInitUnicodeString(&usProcess, wcProcess);
Info.Process = Process;
Info.ProcessId = ProcessId;
Info.usImagePath.Buffer = ImagePath.Buffer;
Info.usImagePath.Length = ImagePath.Length;
Info.usImagePath.MaximumLength = ImagePath.MaximumLength;
// add process information into the list
if (process_entry = LstAddEntry(m_ProcessesList, &usProcess, &Info, sizeof(Info)))
{
PLST_PROCESS_INFO pInfo = (PLST_PROCESS_INFO)process_entry->Data;
Ret = &pInfo->usImagePath;
}
else
{
RtlFreeUnicodeString(&ImagePath);
}
}
return Ret;
}
//--------------------------------------------------------------------------------------
BOOLEAN ValidateUnicodeString(PUNICODE_STRING usStr)
{
ULONG i = 0;
if (!MmIsAddressValid(usStr))
{
return FALSE;
}
if (usStr->Buffer == NULL || usStr->Length == 0)
{
return FALSE;
}
for (i = 0; i < usStr->Length; i++)
{
if (!MmIsAddressValid((PUCHAR)usStr->Buffer + i))
{
return FALSE;
}
}
return TRUE;
}
//--------------------------------------------------------------------------------------
NTSTATUS NTAPI new_NtDeviceIoControlFile(
HANDLE FileHandle,
HANDLE Event,
PIO_APC_ROUTINE ApcRoutine,
PVOID ApcContext,
PIO_STATUS_BLOCK IoStatusBlock,
ULONG IoControlCode,
PVOID InputBuffer,
ULONG InputBufferLength,
PVOID OutputBuffer,
ULONG OutputBufferLength)
{
KPROCESSOR_MODE PrevMode = ExGetPreviousMode();
BOOLEAN bLogOutputBuffer = FALSE;
NTSTATUS status = STATUS_UNSUCCESSFUL;
POBJECT_NAME_INFORMATION DeviceObjectName = NULL, DriverObjectName = NULL;
PFILE_OBJECT pFileObject = NULL;
NTSTATUS ns = 0;
BOOLEAN bProcessEvent = FALSE;
LARGE_INTEGER Timeout;
PVOID pDeviceObject = NULL;
PLDR_DATA_TABLE_ENTRY pModuleEntry = NULL;
PEPROCESS Process;
HANDLE ProcessId;
_InterlockedIncrement(&g_newDeviceIoControlFileCallCount);
// get device object by handle
ns = ObReferenceObjectByHandle(
FileHandle,
0, 0,
KernelMode,
(PVOID *)&pFileObject,
NULL
);
if(!NT_SUCCESS(ns))
goto end;
// validate pointer to device object
if (MmIsAddressValid(pFileObject->DeviceObject))
{
pDeviceObject = pFileObject->DeviceObject;
}
else
{
goto end;
}
if (pDeviceObject == m_DeviceObject)
{
// don't handle requests to our driver
goto end;
}
// validate pointer to driver object
if (!MmIsAddressValid(pFileObject->DeviceObject->DriverObject))
{
goto end;
}
// get loader information entry for the driver module
pModuleEntry = (PLDR_DATA_TABLE_ENTRY)
pFileObject->DeviceObject->DriverObject->DriverSection;
if (pModuleEntry == NULL)
{
goto end;
}
// validate pointer to loader's table and data from it
if (!MmIsAddressValid(pModuleEntry) ||
!ValidateUnicodeString(&pModuleEntry->FullDllName))
{
goto end;
}
// get device name by poinet
DeviceObjectName = GetObjectName(pDeviceObject);
if(!DeviceObjectName)
goto end;
DriverObjectName = GetObjectName(pFileObject->DeviceObject->DriverObject);
if(!DriverObjectName)
goto end;
Process = PsGetCurrentProcess();
ProcessId = PsGetCurrentProcessId();
Timeout.QuadPart = RELATIVE(SECONDS(5));
ns = KeWaitForMutexObject(&m_CommonMutex, Executive, KernelMode, FALSE, &Timeout);
if (ns == STATUS_TIMEOUT)
{
DbgMsg(__FILE__, __LINE__, __FUNCTION__"(): Wait timeout\n");
goto end;
}
__try
{
PWSTR Methods[] =
{
L"METHOD_BUFFERED",
L"METHOD_IN_DIRECT",
L"METHOD_OUT_DIRECT",
L"METHOD_NEITHER"
};
PWSTR lpwcMethod = Methods[IoControlCode & 3];
char *lpszKdCommand = NULL;
LARGE_INTEGER Time;
// get process image path
PUNICODE_STRING ProcessImagePath = LookupProcessName(NULL);
if(!ProcessImagePath)
__leave;
KeQuerySystemTime(&Time);
// get text name of the method
currentDeviceObject = pFileObject->DeviceObject;
currentDriverObject = pFileObject->DeviceObject->DriverObject;
currentIoControlCode = IoControlCode;
currentInputBuffer = InputBuffer;
currentInputBufferLength = InputBufferLength;
currentOutputBuffer = OutputBuffer;
currentOutputBufferLength = OutputBufferLength;
// if (m_FuzzOptions & FUZZ_OPT_LOG_IOCTL_GLOBAL)
// {
// // log IOCTL information into the global log
// LogDataIoctls("timestamp=0x%.8x%.8x\r\n", Time.HighPart, Time.LowPart);
// LogDataIoctls("process_id=%d\r\n", ProcessId);
// LogDataIoctls("process_path=%wZ\r\n", ProcessImagePath);
// LogDataIoctls("device=%wZ\r\n", &DeviceObjectName->Name);
// LogDataIoctls("driver=%wZ\r\n", &DriverObjectName->Name);
// LogDataIoctls("image_file=%wZ\r\n", &pModuleEntry->FullDllName);
// LogDataIoctls("code=0x%.8x\r\n", IoControlCode);
// LogDataIoctls("method=%ws\r\n", lpwcMethod);
// LogDataIoctls("in_size=%d\r\n", InputBufferLength);
// LogDataIoctls("out_size=%d\r\n", OutputBufferLength);
// LogDataIoctls("\r\n");
// }
// get debugger command, that can be associated with this IOCTL
lpszKdCommand = FltGetKdCommand(
&DeviceObjectName->Name,
&DriverObjectName->Name/*pModuleEntry->FullDllName*/,
IoControlCode,
ProcessImagePath
);
bProcessEvent = FltIsMatchedRequest(
&DeviceObjectName->Name,
&pModuleEntry->FullDllName,
IoControlCode,
ProcessImagePath
);
if ((bProcessEvent || lpszKdCommand) &&
(m_FuzzOptions & FUZZ_OPT_LOG_IOCTL))
{
LogDataIoctls(
"timestamp=0x%.8x%.8x\r\n \
process_id=%d\r\n \
process_path=%wZ\r\n \
device=%wZ\r\n \
driver=%wZ\r\n \
image_file=%wZ\r\n \
code=0x%.8x\r\n \
method=%ws\r\n \
in_size=%d\r\n \
out_size=%d\r\n \
\r\n",
Time.HighPart, Time.LowPart,
ProcessId,
ProcessImagePath,
&DeviceObjectName->Name,
&DriverObjectName->Name,
&pModuleEntry->FullDllName,
IoControlCode,
lpwcMethod,
InputBufferLength,
OutputBufferLength);
if (m_FuzzOptions & FUZZ_OPT_LOG_IOCTL_BUFFERS)
{
// log output buffer information
LogDataIoctls(" OutBuff: "IFMT", OutSize: 0x%.8x\r\n",
OutputBuffer,
OutputBufferLength);
// log input buffer information
LogDataIoctls(" InBuff: "IFMT", InSize: 0x%.8x\r\n",
InputBuffer,
InputBufferLength);
// print input buffer contents
LogDataIoctls("--------------------------------------------------------------------\r\n");
LogDataHexdump((PUCHAR)InputBuffer, min(InputBufferLength, MAX_IOCTL_BUFFER_LEGTH));
LogDataIoctls("\r\n");
}
}
}
__finally
{
KeReleaseMutex(&m_CommonMutex, FALSE);
}
end:
if(pFileObject)
ObDereferenceObject(pFileObject);
if(DriverObjectName)
ExFreePool(DriverObjectName);
if(DeviceObjectName)
ExFreePool(DeviceObjectName);
// restore KTHREAD::PreviousMode
SetPreviousMode(PrevMode);
// call original function
status = old_NtDeviceIoControlFile(
FileHandle,
Event,
ApcRoutine,
ApcContext,
IoStatusBlock,
IoControlCode,
InputBuffer,
InputBufferLength,
OutputBuffer,
OutputBufferLength
);
_InterlockedDecrement( &g_newDeviceIoControlFileCallCount );
return status;
}
VOID WaitHookRemoveComplete()
{
LONG Count = 0;
const LARGE_INTEGER WaitTime = {(ULONG)(-50 * 1000 * 10), -1};
do
{
KeDelayExecutionThread( KernelMode , FALSE , (PLARGE_INTEGER)&WaitTime );
_InterlockedExchange( &Count , g_newDeviceIoControlFileCallCount );
} while (Count != 0 );
return;
}
//--------------------------------------------------------------------------------------
// EoF