implement timer objects + some stuf

This commit is contained in:
lhodges1 2024-01-12 16:40:33 +11:00
parent 2a4a5e344d
commit 19619119df
8 changed files with 228 additions and 93 deletions

View file

@ -280,7 +280,7 @@ ImageLoadNotifyRoutineCallback(_In_opt_ PUNICODE_STRING FullImageName,
if (ImageInfo->SystemModeImage == FALSE)
return;
FindDriverEntryByBaseAddress(ImageInfo->ImageBase, &entry);
if (entry)
@ -555,6 +555,9 @@ ObPreOpCallbackRoutine(_In_ PVOID RegistrationContext,
/*
* WerFault is some windows 11 application that cries when it cant get a handle,
* so well allow it for now... todo; learn more about it
*
* todo: perform stricter checks rather then the image name. perhapds check some
* certificate or something.
*/
if (!strcmp(process_creator_name, "lsass.exe") ||
!strcmp(process_creator_name, "csrss.exe") ||
@ -818,4 +821,105 @@ EnumerateProcessHandles(_In_ PPROCESS_LIST_ENTRY ProcessListEntry, _In_opt_ PVOI
#pragma warning(pop)
return STATUS_SUCCESS;
}
#define REPEAT_TIME_10_SEC 10000
VOID
TimerObjectWorkItemRoutine(_In_ PDEVICE_OBJECT DeviceObject, _In_opt_ PVOID Context)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
PTIMER_OBJECT timer = (PTIMER_OBJECT)Context;
DEBUG_VERBOSE("Integrity check timer callback invoked.");
if (!ValidateOurDriversDispatchRoutines())
{
DEBUG_VERBOSE("l");
}
status = ValidateOurDriverImage();
if (!NT_SUCCESS(status))
DEBUG_ERROR("ValidateOurDriverImage failed with status %x", status);
InterlockedDecrement(&timer->state);
}
/*
* This routine is executed every x seconds, and is run at IRQL = DISPATCH_LEVEL
*/
VOID
TimerObjectCallbackRoutine(_In_ PKDPC Dpc,
_In_opt_ PVOID DeferredContext,
_In_opt_ PVOID SystemArgument1,
_In_opt_ PVOID SystemArgument2)
{
PTIMER_OBJECT timer = (PTIMER_OBJECT)DeferredContext;
/* we dont want to queue our work item if it hasnt executed */
if (timer->state)
return;
/* we queue a work item because DPCs run at IRQL = DISPATCH_LEVEL and we need certain
* routines which cannot be run at an IRQL this high.*/
InterlockedIncrement(&timer->state);
IoQueueWorkItem(timer->work_item, TimerObjectWorkItemRoutine, BackgroundWorkQueue, timer);
}
NTSTATUS
InitialiseTimerObject(_Out_ PTIMER_OBJECT Timer)
{
LARGE_INTEGER due_time = {0};
LONG period = 0;
due_time.QuadPart = ABSOLUTE(SECONDS(5));
Timer->timer = ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(KTIMER), POOL_TAG_TIMER);
if (!Timer->timer)
return STATUS_MEMORY_NOT_ALLOCATED;
Timer->dpc = ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(KDPC), POOL_TAG_DPC);
if (!Timer->dpc)
{
ExFreePoolWithTag(Timer->timer, POOL_TAG_TIMER);
return STATUS_MEMORY_NOT_ALLOCATED;
}
Timer->work_item = IoAllocateWorkItem(GetDriverDeviceObject());
if (!Timer->work_item)
{
ExFreePoolWithTag(Timer->dpc, POOL_TAG_DPC);
ExFreePoolWithTag(Timer->timer, POOL_TAG_TIMER);
return STATUS_MEMORY_NOT_ALLOCATED;
}
KeInitializeDpc(Timer->dpc, TimerObjectCallbackRoutine, Timer);
KeInitializeTimer(Timer->timer);
KeSetTimerEx(Timer->timer, due_time, REPEAT_TIME_10_SEC, Timer->dpc);
DEBUG_VERBOSE("Successfully initialised global timer callback.");
return STATUS_SUCCESS;
}
VOID
CleanupDriverTimerObjects(_Out_ PTIMER_OBJECT Timer)
{
/* this routine blocks until all queued DPCs on all processors have executed. */
KeFlushQueuedDpcs();
/* wait for our work item to complete */
while (Timer->state)
YieldProcessor();
/* now its safe to free and cancel our timers, pools etc. */
KeCancelTimer(Timer->timer);
IoFreeWorkItem(Timer->work_item);
ExFreePoolWithTag(Timer->timer, POOL_TAG_TIMER);
ExFreePoolWithTag(Timer->dpc, POOL_TAG_DPC);
DEBUG_VERBOSE("Freed timer objects.");
}

View file

@ -5,6 +5,7 @@
#include <wdftypes.h>
#include <wdf.h>
#include "common.h"
#include "driver.h"
#define HANDLE_REPORT_PROCESS_NAME_MAX_LENGTH 64
@ -134,4 +135,10 @@ ImageLoadNotifyRoutineCallback(_In_opt_ PUNICODE_STRING FullImageName,
_In_ HANDLE ProcessId,
_In_ PIMAGE_INFO ImageInfo);
NTSTATUS
InitialiseTimerObject(_Out_ PTIMER_OBJECT Timer);
VOID
CleanupDriverTimerObjects(_Out_ PTIMER_OBJECT Timer);
#endif

View file

@ -69,6 +69,7 @@
#define POOL_TAG_THREAD_LIST 'list'
#define POOL_TAG_DRIVER_LIST 'drvl'
#define POOL_TAG_IRP_QUEUE 'irpp'
#define POOL_TAG_TIMER 'time'
#define IA32_APERF_MSR 0x000000E8
@ -1492,4 +1493,16 @@ PETHREAD
NTAPI
PsGetNextProcessThread(IN PEPROCESS Process, IN PETHREAD Thread OPTIONAL);
#define ABSOLUTE(wait) (wait)
#define RELATIVE(wait) (-(wait))
#define NANOSECONDS(nanos) (((signed __int64)(nanos)) / 100L)
#define MICROSECONDS(micros) (((signed __int64)(micros)) * NANOSECONDS(1000L))
#define MILLISECONDS(milli) (((signed __int64)(milli)) * MICROSECONDS(1000L))
#define SECONDS(seconds) (((signed __int64)(seconds)) * MILLISECONDS(1000L))
#endif

View file

@ -120,6 +120,7 @@ typedef struct _DRIVER_CONFIG
KGUARDED_MUTEX lock;
SYS_MODULE_VAL_CONTEXT sys_val_context;
IRP_QUEUE_HEAD irp_queue;
TIMER_OBJECT timer;
} DRIVER_CONFIG, *PDRIVER_CONFIG;
@ -774,6 +775,14 @@ DrvUnloadFreeDriverList()
CleanupDriverListOnDriverUnload();
}
STATIC
VOID
DrvUnloadFreeTimerObject()
{
PAGED_CODE();
CleanupDriverTimerObjects(&driver_config.timer);
}
STATIC
VOID
DrvUnloadFreeProcessList()
@ -814,6 +823,7 @@ DriverUnload(_In_ PDRIVER_OBJECT DriverObject)
while (DrvUnloadFreeAllApcContextStructures() == FALSE)
YieldProcessor();
DrvUnloadFreeTimerObject();
DrvUnloadFreeModuleValidationContext();
DrvUnloadUnregisterObCallbacks();
DrvUnloadFreeThreadList();
@ -1197,6 +1207,15 @@ DrvLoadInitialiseDriverConfig(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_ST
return status;
}
status = InitialiseTimerObject(&driver_config.timer);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("InitialiseTimerObject failed with status %x", status);
DrvUnloadFreeConfigStrings();
return status;
}
DEBUG_VERBOSE("driver name: %s", driver_config.ansi_driver_name.Buffer);
return status;
@ -1208,6 +1227,11 @@ DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
BOOLEAN flag = FALSE;
NTSTATUS status = STATUS_UNSUCCESSFUL;
DriverObject->MajorFunction[IRP_MJ_CREATE] = DeviceCreate;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DeviceClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceControl;
DriverObject->DriverUnload = DriverUnload;
/* store the driver object here as we need to access it in ResolveNtImports */
driver_config.driver_object = DriverObject;
@ -1218,16 +1242,6 @@ DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
DEBUG_VERBOSE("Beginning driver entry routine...");
status = DrvLoadInitialiseDriverConfig(DriverObject, RegistryPath);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("InitialiseDriverConfigOnDriverEntry failed with status %x", status);
return status;
}
DrvLoadInitialiseProcessConfig();
status = ImpIoCreateDevice(DriverObject,
NULL,
&driver_config.device_name,
@ -1239,13 +1253,23 @@ DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("IoCreateDevice failed with status %x", status);
DrvUnloadFreeConfigStrings();
return STATUS_FAILED_DRIVER_ENTRY;
return status;
}
driver_config.driver_object = DriverObject;
driver_config.device_object = DriverObject->DeviceObject;
status = DrvLoadInitialiseDriverConfig(DriverObject, RegistryPath);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("InitialiseDriverConfigOnDriverEntry failed with status %x", status);
ImpIoDeleteDevice(DriverObject->DeviceObject);
return status;
}
DrvLoadInitialiseProcessConfig();
status = ImpIoCreateSymbolicLink(&driver_config.device_symbolic_link,
&driver_config.device_name);
@ -1257,11 +1281,6 @@ DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
return STATUS_FAILED_DRIVER_ENTRY;
}
DriverObject->MajorFunction[IRP_MJ_CREATE] = DeviceCreate;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DeviceClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceControl;
DriverObject->DriverUnload = DriverUnload;
DrvLoadInitialiseReportQueue(&flag);
if (!flag)

View file

@ -22,6 +22,20 @@
#define MAXIMUM_APC_CONTEXTS 10
typedef struct _TIMER_OBJECT
{
/*
* state = 1: callback in progress
* state = 0: no callback in progress (i.e safe to free and unregister)
*/
volatile LONG state;
PKTIMER timer;
PKDPC dpc;
PIO_WORKITEM work_item;
} TIMER_OBJECT, *PTIMER_OBJECT;
typedef enum _ENVIRONMENT_TYPE
{
NativeWindows = 0,

View file

@ -398,15 +398,15 @@ MapDiskImageIntoVirtualAddressSpace(_Inout_ PHANDLE Sec
* will be identical to the in memory image.
*/
status = ImpZwMapViewOfSection(*SectionHandle,
ZwCurrentProcess(),
Section,
NULL,
NULL,
NULL,
Size,
ViewUnmap,
MEM_TOP_DOWN,
PAGE_READONLY);
ZwCurrentProcess(),
Section,
NULL,
NULL,
NULL,
Size,
ViewUnmap,
MEM_TOP_DOWN,
PAGE_READONLY);
if (!NT_SUCCESS(status))
{
@ -1558,7 +1558,8 @@ ValidateSystemModule(_In_ PRTL_MODULE_EXTENDED_INFO Module)
if (CompareHashes(hash, entry->text_hash, SHA_256_HASH_LENGTH))
DEBUG_VERBOSE("Module: %s text regions are valid.", Module->FullPathName);
else
DEBUG_WARNING("**!!** Module: %s text regions are NOT valid **!!**", Module->FullPathName);
DEBUG_WARNING("**!!** Module: %s text regions are NOT valid **!!**",
Module->FullPathName);
end:
if (hash)
@ -1568,23 +1569,14 @@ end:
NTSTATUS
ValidateOurDriverImage()
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
LPCSTR driver_name = NULL;
UNICODE_STRING path = {0};
SYSTEM_MODULES modules = {0};
PRTL_MODULE_EXTENDED_INFO module_info = NULL;
PVOID section = NULL;
HANDLE section_handle = NULL;
ULONG section_size = 0;
PVAL_INTEGRITY_HEADER disk_buffer = NULL;
ULONG disk_buffer_size = 0;
PVOID disk_hash = NULL;
ULONG disk_hash_size = 0;
ULONG memory_text_size = 0;
PVOID memory_hash = NULL;
ULONG memory_hash_size = 0;
PVAL_INTEGRITY_HEADER memory_buffer = NULL;
ULONG memory_buffer_size = 0;
NTSTATUS status = STATUS_UNSUCCESSFUL;
LPCSTR driver_name = NULL;
UNICODE_STRING path = {0};
SYSTEM_MODULES modules = {0};
PRTL_MODULE_EXTENDED_INFO module_info = NULL;
PVOID memory_hash = NULL;
ULONG memory_hash_size = 0;
PDRIVER_LIST_ENTRY entry = NULL;
GetDriverPath(&path);
GetDriverName(&driver_name);
@ -1611,46 +1603,24 @@ ValidateOurDriverImage()
goto end;
}
/* here we need to map our disk image, like the previous integ checks */
status =
MapDiskImageIntoVirtualAddressSpace(&section_handle, &section, &path, &section_size);
memory_hash = ExAllocatePool2(POOL_FLAG_NON_PAGED, SHA_256_HASH_LENGTH, POOL_TAG_INTEGRITY);
if (!NT_SUCCESS(status))
if (!memory_hash)
goto end;
FindDriverEntryByBaseAddress(module_info->ImageBase, &entry);
if (!entry)
{
DEBUG_ERROR("MapDiskImageIntoVirtualAddressSpace failed with status %x", status);
DEBUG_ERROR("FindDriverEntryByBaseAddress failed with no status.");
goto end;
}
status = StoreModuleExecutableRegionsInBuffer(
(PVOID)&disk_buffer, section, section_size, &disk_buffer_size);
status = HashModule(module_info, memory_hash);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("StoreModuleExecutableRegionsInBuffer failed with status %x", status);
goto end;
}
status = StoreModuleExecutableRegionsInBuffer((PVOID)&memory_buffer,
module_info->ImageBase,
module_info->ImageSize,
&memory_buffer_size);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("StoreModuleExecutableRegionsInBuffer 2 failed with status %x", status);
goto end;
}
status = ComputeHashOfSections(&memory_buffer->section_header,
&disk_buffer->section_header,
&disk_hash,
&disk_hash_size,
&memory_hash,
&memory_hash_size);
if (!NT_SUCCESS(status))
{
DEBUG_VERBOSE("ComputeHashOfSections failed with status %x", status);
DEBUG_ERROR("HashModule failed with status %x", status);
goto end;
}
@ -1658,30 +1628,16 @@ ValidateOurDriverImage()
* Since we don't pass a return value, I think we would raise an invalid module error and
* stop the users game session ? since module .text section error would be a large red flag
*/
if (CompareHashes(disk_hash, memory_hash, SHA_256_HASH_LENGTH))
if (CompareHashes(memory_hash, entry->text_hash, SHA_256_HASH_LENGTH))
DEBUG_VERBOSE("Driver image is valid. Integrity check complete");
else
DEBUG_WARNING("Drive image is NOT valid. !!!");
DEBUG_WARNING("**!!** Driver image is NOT valid. **!!**");
end:
if (memory_buffer)
ExFreePoolWithTag(memory_buffer, POOL_TAG_INTEGRITY);
if (disk_buffer)
ExFreePoolWithTag(disk_buffer, POOL_TAG_INTEGRITY);
if (memory_hash)
ExFreePoolWithTag(memory_hash, POOL_TAG_INTEGRITY);
if (disk_hash)
ExFreePoolWithTag(disk_hash, POOL_TAG_INTEGRITY);
if (section_handle)
ZwClose(section_handle);
if (section)
ZwUnmapViewOfSection(ZwCurrentProcess(), section);
if (modules.address)
ExFreePoolWithTag(modules.address, SYSTEM_MODULES_POOL);
@ -1929,3 +1885,18 @@ CalculateCpuCoreUsage(_In_ UINT32 Core)
return (100 - (UINT32)(UInt32x32To64(idle_time, 100) / (UINT64)(kernel_time + user_time)));
}
BOOLEAN
ValidateOurDriversDispatchRoutines()
{
PDRIVER_OBJECT driver = GetDriverObject();
if (driver->MajorFunction[IRP_MJ_CREATE] != DeviceCreate ||
driver->MajorFunction[IRP_MJ_CLOSE] != DeviceClose ||
driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] != DeviceControl)
{
DEBUG_WARNING("**!!** Drivers dispatch routine has been tampered with. **!!**");
return FALSE;
}
return TRUE;
}

View file

@ -130,4 +130,7 @@ HashModule(_In_ PRTL_MODULE_EXTENDED_INFO Module, _Out_ PVOID Hash);
VOID
ValidateSystemModule(_In_ PRTL_MODULE_EXTENDED_INFO Module);
BOOLEAN
ValidateOurDriversDispatchRoutines();
#endif

View file

@ -647,6 +647,10 @@ CheckIfProcessAllocationIsInProcessList(_In_ PPROCESS_LIST_ENTRY ProcessListEntr
}
}
/*
* This is actually broken right now since changing to use our process list, will need to fix at
* somepoint.
*/
NTSTATUS
FindUnlinkedProcesses()
{