From bef7c8a3b5216ed00210f61e3bc0a93945f5155c Mon Sep 17 00:00:00 2001 From: lhodges1 Date: Mon, 12 Feb 2024 01:34:28 +1100 Subject: [PATCH] implement deferred module hashing --- driver/arch.asm | 28 ------ driver/callbacks.c | 159 +++++++++++++++++++++++-------- driver/callbacks.h | 20 +++- driver/common.h | 15 +-- driver/driver.c | 33 ++++++- driver/driver.h | 12 +++ driver/integrity.c | 137 ++++++++++++++++++-------- driver/integrity.h | 3 + driver/io.c | 55 ++++++----- driver/modules.c | 44 ++++----- driver/pool.c | 2 +- module/dispatcher/dispatcher.cpp | 6 +- module/helper.cpp | 4 +- 13 files changed, 351 insertions(+), 167 deletions(-) diff --git a/driver/arch.asm b/driver/arch.asm index 62c39d3..4497405 100644 --- a/driver/arch.asm +++ b/driver/arch.asm @@ -33,32 +33,4 @@ TestINVDEmulation PROC TestINVDEmulation ENDP - -; -; Note: fild and fistp respectively are used for loading and storing integers in the FPU, -; while fld and fstp are used for floating point numbers. No need to use xmm registers -; as we dont need that level of precision and we need to be as efficient as possible -; -; compiler will take care of saving the SSE state for us and restoring it source: -; https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/using-floating-point-or-mmx-in-a-wdm-driver -; -; arguments: INT64 in RCX -; returns resulting number lol - -MySqrt PROC - - push rbp - mov rbp, rsp - sub rsp, 16 - mov [rsp + 8], rcx ; cannot directly move from a register into a fp register - fild qword ptr[rsp + 8] ; push our number onto the FPU stack - fsqrt ; perform the square root - fistp qword ptr[rsp] ; pop the value from the floating point stack into our general purpose stack - mov rax, qword ptr[rsp] ; store value in rax for return - add rsp, 16 - pop rbp - ret - -MySqrt ENDP - END \ No newline at end of file diff --git a/driver/callbacks.c b/driver/callbacks.c index c149f41..f8ddda0 100644 --- a/driver/callbacks.c +++ b/driver/callbacks.c @@ -159,6 +159,37 @@ unlock: ImpKeReleaseGuardedMutex(&list->lock); } +VOID +EnumerateDriverListWithCallbackRoutine(_In_ DRIVERLIST_CALLBACK_ROUTINE CallbackRoutine, + _In_opt_ PVOID Context) +{ + PDRIVER_LIST_HEAD list = GetDriverList(); + ImpKeAcquireGuardedMutex(&list->lock); + + if (!CallbackRoutine) + goto unlock; + + PDRIVER_LIST_ENTRY entry = list->start.Next; + + while (entry) + { + CallbackRoutine(entry, Context); + entry = entry->list.Next; + } + +unlock: + ImpKeReleaseGuardedMutex(&list->lock); +} + +VOID +DriverListEntryToExtendedModuleInfo(_In_ PDRIVER_LIST_ENTRY Entry, + _Out_ PRTL_MODULE_EXTENDED_INFO Extended) +{ + Extended->ImageBase = Entry->ImageBase; + Extended->ImageSize = Entry->ImageSize; + RtlCopyMemory(Extended->FullPathName, Entry->path, sizeof(Extended->FullPathName)); +} + NTSTATUS InitialiseDriverList() { @@ -172,6 +203,7 @@ InitialiseDriverList() InterlockedExchange(&list->active, TRUE); ListInit(&list->start, &list->lock); + InitializeListHead(&list->deferred_unhashed_x86_modules); status = GetSystemModuleInformation(&modules); @@ -205,6 +237,9 @@ InitialiseDriverList() { DEBUG_ERROR("32 bit module not hashed, will hash later. %x", status); entry->hashed = FALSE; + entry->x86 = TRUE; + InsertHeadList(&list->deferred_unhashed_x86_modules, + &entry->deferred_entry); } else if (!NT_SUCCESS(status)) { @@ -256,11 +291,12 @@ ImageLoadNotifyRoutineCallback(_In_opt_ PUNICODE_STRING FullImageName, _In_ HANDLE ProcessId, _In_ PIMAGE_INFO ImageInfo) { - NTSTATUS status = STATUS_UNSUCCESSFUL; - PDRIVER_LIST_ENTRY entry = NULL; - RTL_MODULE_EXTENDED_INFO module = {0}; - PDRIVER_LIST_HEAD list = GetDriverList(); - ANSI_STRING ansi_path = {0}; + NTSTATUS status = STATUS_UNSUCCESSFUL; + PDRIVER_LIST_ENTRY entry = NULL; + RTL_MODULE_EXTENDED_INFO module = {0}; + PDRIVER_LIST_HEAD list = GetDriverList(); + ANSI_STRING ansi_path = {0}; + UINT32 ansi_string_length = 0; if (InterlockedExchange(&list->active, list->active) == FALSE) return; @@ -273,8 +309,6 @@ ImageLoadNotifyRoutineCallback(_In_opt_ PUNICODE_STRING FullImageName, if (entry) return; - DEBUG_VERBOSE("New system image: %wZ", FullImageName); - entry = ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(DRIVER_LIST_ENTRY), POOL_TAG_DRIVER_LIST); @@ -282,26 +316,44 @@ ImageLoadNotifyRoutineCallback(_In_opt_ PUNICODE_STRING FullImageName, return; entry->hashed = TRUE; + entry->x86 = FALSE; entry->ImageBase = ImageInfo->ImageBase; entry->ImageSize = ImageInfo->ImageSize; - /*todo: unicode 2 ansi string -> store in buf */ module.ImageBase = ImageInfo->ImageBase; module.ImageSize = ImageInfo->ImageSize; - // if (FullImageName) - //{ - // status = RtlUnicodeStringToAnsiString(&ansi_path, FullImageName, TRUE); + if (FullImageName) + { + status = RtlUnicodeStringToAnsiString(&ansi_path, FullImageName, TRUE); - // if (!NT_SUCCESS(status)) - // DEBUG_ERROR("RtlUnicodeStringToAnsiString failed with status %x", status); - //} + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("RtlUnicodeStringToAnsiString failed with status %x", status); + goto hash; + } + if (ansi_path.Length > sizeof(module.FullPathName)) + { + RtlFreeAnsiString(&ansi_path); + goto hash; + } + + RtlCopyMemory(module.FullPathName, ansi_path.Buffer, ansi_path.Length); + RtlCopyMemory(entry->path, ansi_path.Buffer, ansi_path.Length); + + RtlFreeAnsiString(&ansi_path); + } + + DEBUG_VERBOSE("New system image ansi: %s", entry->path); + +hash: status = HashModule(&module, &entry->text_hash); if (status == STATUS_INVALID_IMAGE_WIN_32) { DEBUG_ERROR("32 bit module not hashed, will hash later. %x", status); + entry->x86 = TRUE; entry->hashed = FALSE; } else if (!NT_SUCCESS(status)) @@ -411,13 +463,31 @@ unlock: ImpKeReleaseGuardedMutex(&list->lock); } +VOID +Hashx86ModulesOnWinlogonLoad() +{ + NTSTATUS status = STATUS_UNSUCCESSFUL; + + status = Allocatex86HashingWorkItem(); + + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("Allocatex86HashingWorkItem failed with status %x", status); + return status; + } + + IoQueueWorkItem( + Getx86HashingWorkItem(), HashDeferredx86ModuleDeferredRoutine, NormalWorkQueue, NULL); +} + VOID ProcessCreateNotifyRoutine(_In_ HANDLE ParentId, _In_ HANDLE ProcessId, _In_ BOOLEAN Create) { - PPROCESS_LIST_ENTRY entry = NULL; - PKPROCESS parent = NULL; - PKPROCESS process = NULL; - PPROCESS_LIST_HEAD list = GetProcessList(); + PPROCESS_LIST_ENTRY entry = NULL; + PKPROCESS parent = NULL; + PKPROCESS process = NULL; + PPROCESS_LIST_HEAD list = GetProcessList(); + LPCSTR process_name = NULL; if (!list->active) return; @@ -428,6 +498,8 @@ ProcessCreateNotifyRoutine(_In_ HANDLE ParentId, _In_ HANDLE ProcessId, _In_ BOO if (!parent || !process) return; + process_name = ImpPsGetProcessImageFileName(process); + if (Create) { entry = ExAllocateFromLookasideListEx(&list->lookaside_list); @@ -442,6 +514,17 @@ ProcessCreateNotifyRoutine(_In_ HANDLE ParentId, _In_ HANDLE ProcessId, _In_ BOO entry->process = process; ListInsert(&list->start, entry, &list->lock); + + /* + * Notify to our driver that we can hash x86 modules, and hash any x86 modules that + * werent hashed. + */ + if (!strcmp(process_name, "winlogon.exe")) + { + DEBUG_VERBOSE("Winlogon process has started"); + UpdateWinlogonProcessState(TRUE); + Hashx86ModulesOnWinlogonLoad(); + } } else { @@ -609,31 +692,31 @@ ObPreOpCallbackRoutine(_In_ PVOID RegistrationContext, !strcmp(process_creator_name, "explorer.exe")) goto end; - //POPEN_HANDLE_FAILURE_REPORT report = - // ImpExAllocatePool2(POOL_FLAG_NON_PAGED, - // sizeof(OPEN_HANDLE_FAILURE_REPORT), - // REPORT_POOL_TAG); + // POPEN_HANDLE_FAILURE_REPORT report = + // ImpExAllocatePool2(POOL_FLAG_NON_PAGED, + // sizeof(OPEN_HANDLE_FAILURE_REPORT), + // REPORT_POOL_TAG); - //if (!report) - // goto end; + // if (!report) + // goto end; - //report->report_code = REPORT_ILLEGAL_HANDLE_OPERATION; - //report->is_kernel_handle = OperationInformation->KernelHandle; - //report->process_id = process_creator_id; - //report->thread_id = ImpPsGetCurrentThreadId(); - //report->access = - // OperationInformation->Parameters->CreateHandleInformation.DesiredAccess; + // report->report_code = REPORT_ILLEGAL_HANDLE_OPERATION; + // report->is_kernel_handle = OperationInformation->KernelHandle; + // report->process_id = process_creator_id; + // report->thread_id = ImpPsGetCurrentThreadId(); + // report->access = + // OperationInformation->Parameters->CreateHandleInformation.DesiredAccess; - //RtlCopyMemory(report->process_name, - // process_creator_name, - // HANDLE_REPORT_PROCESS_NAME_MAX_LENGTH); + // RtlCopyMemory(report->process_name, + // process_creator_name, + // HANDLE_REPORT_PROCESS_NAME_MAX_LENGTH); - //if (!NT_SUCCESS( - // IrpQueueCompleteIrp(report, sizeof(OPEN_HANDLE_FAILURE_REPORT)))) + // if (!NT_SUCCESS( + // IrpQueueCompleteIrp(report, sizeof(OPEN_HANDLE_FAILURE_REPORT)))) //{ - // DEBUG_ERROR("IrpQueueCompleteIrp failed with no status."); - // goto end; - //} + // DEBUG_ERROR("IrpQueueCompleteIrp failed with no status."); + // goto end; + // } } } diff --git a/driver/callbacks.h b/driver/callbacks.h index 0fc7915..43630fd 100644 --- a/driver/callbacks.h +++ b/driver/callbacks.h @@ -21,11 +21,21 @@ typedef struct _DRIVER_LIST_ENTRY PVOID ImageBase; ULONG ImageSize; BOOLEAN hashed; + BOOLEAN x86; CHAR path[DRIVER_PATH_LENGTH]; CHAR text_hash[SHA_256_HASH_LENGTH]; + /* + * This LIST_ENTRY is to be used for modules where the hashing needs to be deferred. + * For example, when x86 modules can't be hashed on driver load. + */ + LIST_ENTRY deferred_entry; + } DRIVER_LIST_ENTRY, *PDRIVER_LIST_ENTRY; +typedef void (*DRIVERLIST_CALLBACK_ROUTINE)(_In_ PDRIVER_LIST_ENTRY DriverListEntry, + _In_opt_ PVOID Context); + NTSTATUS InitialiseDriverList(); @@ -70,7 +80,7 @@ EnumerateThreadListWithCallbackRoutine(_In_ THREADLIST_CALLBACK_ROUTINE Callback VOID EnumerateProcessListWithCallbackRoutine(_In_ PROCESSLIST_CALLBACK_ROUTINE CallbackRoutine, - _In_opt_ PVOID Context); + _In_opt_ PVOID Context); VOID FindDriverEntryByBaseAddress(_In_ PVOID ImageBase, _Out_ PDRIVER_LIST_ENTRY* Entry); @@ -110,4 +120,12 @@ RegisterProcessObCallbacks(); VOID InitialiseObCallbacksConfiguration(_Out_ PACTIVE_SESSION ProcessConfig); +VOID +EnumerateDriverListWithCallbackRoutine(_In_ DRIVERLIST_CALLBACK_ROUTINE CallbackRoutine, + _In_opt_ PVOID Context); + +VOID +DriverListEntryToExtendedModuleInfo(_In_ PDRIVER_LIST_ENTRY Entry, + _Out_ PRTL_MODULE_EXTENDED_INFO Extended); + #endif diff --git a/driver/common.h b/driver/common.h index 94f30c2..8d99930 100644 --- a/driver/common.h +++ b/driver/common.h @@ -74,6 +74,7 @@ typedef struct _DRIVER_LIST_HEAD volatile ULONG count; volatile BOOLEAN active; KGUARDED_MUTEX lock; + LIST_ENTRY deferred_unhashed_x86_modules; } DRIVER_LIST_HEAD, *PDRIVER_LIST_HEAD; @@ -183,21 +184,21 @@ typedef struct _DEFERRED_REPORT } DEFERRED_REPORT, *PDEFERRED_REPORT; -typedef struct _DEFERRED_REPORTS_HEAD +typedef struct _DEFERRED_REPORTS_LIST { - LIST_ENTRY head; - UINT32 count; - KGUARDED_MUTEX lock; + LIST_ENTRY head; + UINT32 count; + KSPIN_LOCK lock; -} DEFERRED_REPORTS_HEAD, *PDEFERRED_REPORTS_HEAD; +} DEFERRED_REPORTS_LIST, *PDEFERRED_REPORTS_LIST; typedef struct _IRP_QUEUE_HEAD { LIST_ENTRY queue; volatile UINT32 count; IO_CSQ csq; - KGUARDED_MUTEX lock; - DEFERRED_REPORTS_HEAD reports; + KSPIN_LOCK lock; + DEFERRED_REPORTS_LIST deferred_reports; } IRP_QUEUE_HEAD, *PIRP_QUEUE_HEAD; diff --git a/driver/driver.c b/driver/driver.c index cc553ab..01b9aff 100644 --- a/driver/driver.c +++ b/driver/driver.c @@ -96,6 +96,8 @@ typedef struct _DRIVER_CONFIG PROCESS_LIST_HEAD process_list; SHARED_MAPPING mapping; BOOLEAN has_driver_loaded; + BOOLEAN has_winlogon_started; + PIO_WORKITEM x86_hash_workitem; } DRIVER_CONFIG, *PDRIVER_CONFIG; @@ -114,6 +116,32 @@ PDRIVER_CONFIG g_DriverConfig = NULL; #define POOL_TAG_CONFIG 'conf' +PIO_WORKITEM +Getx86HashingWorkItem() +{ + return g_DriverConfig->x86_hash_workitem; +} + +NTSTATUS +Allocatex86HashingWorkItem() +{ + g_DriverConfig->x86_hash_workitem = IoAllocateWorkItem(g_DriverConfig->device_object); + return g_DriverConfig->x86_hash_workitem != NULL ? STATUS_SUCCESS + : STATUS_INSUFFICIENT_RESOURCES; +} + +BOOLEAN +HasWinlogonProcessStarted() +{ + return g_DriverConfig->has_winlogon_started; +} + +VOID +UpdateWinlogonProcessState(_In_ BOOLEAN NewValue) +{ + g_DriverConfig->has_winlogon_started = NewValue; +} + BOOLEAN HasDriverLoaded() { @@ -886,8 +914,9 @@ DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) return status; } - g_DriverConfig->has_driver_loaded = TRUE; + g_DriverConfig->has_driver_loaded = TRUE; + g_DriverConfig->has_winlogon_started = FALSE; - DEBUG_VERBOSE("Driver Entry Complete."); + DEBUG_INFO("Driver Entry Complete."); return STATUS_SUCCESS; } diff --git a/driver/driver.h b/driver/driver.h index edb19d9..02a4d6c 100644 --- a/driver/driver.h +++ b/driver/driver.h @@ -79,4 +79,16 @@ IsNmiInProgress(); BOOLEAN HasDriverLoaded(); +BOOLEAN +HasWinlogonProcessStarted(); + +VOID +UpdateWinlogonProcessState(_In_ BOOLEAN NewValue); + +NTSTATUS +Allocatex86HashingWorkItem(); + +PIO_WORKITEM +Getx86HashingWorkItem(); + #endif \ No newline at end of file diff --git a/driver/integrity.c b/driver/integrity.c index eed25d8..cc2c5b6 100644 --- a/driver/integrity.c +++ b/driver/integrity.c @@ -46,7 +46,8 @@ NTSTATUS StoreModuleExecutableRegionsInBuffer(_Outptr_result_bytebuffer_(*BytesWritten) PVOID* Buffer, _In_ PVOID ModuleBase, _In_ SIZE_T ModuleSize, - _Out_ _Deref_out_range_(>, 0) PSIZE_T BytesWritten); + _Out_ _Deref_out_range_(>, 0) PSIZE_T BytesWritten, + _In_ BOOLEAN IsModulex86); STATIC NTSTATUS @@ -77,15 +78,6 @@ STATIC NTSTATUS GetAverageReadTimeAtRoutine(_In_ PVOID RoutineAddress, _Out_ PUINT64 AverageTime); -STATIC -NTSTATUS -RegistryPathQueryTestSigningCallback(IN PWSTR ValueName, - IN ULONG ValueType, - IN PVOID ValueData, - IN ULONG ValueLength, - IN PVOID Context, - IN PVOID EntryContext); - #ifdef ALLOC_PRAGMA # pragma alloc_text(PAGE, GetDriverImageSize) # pragma alloc_text(PAGE, GetModuleInformationByName) @@ -102,7 +94,6 @@ RegistryPathQueryTestSigningCallback(IN PWSTR ValueName, # pragma alloc_text(PAGE, ScanForSignature) # pragma alloc_text(PAGE, InitiateEptFunctionAddressArrays) # pragma alloc_text(PAGE, DetectEptHooksInKeyFunctions) -# pragma alloc_text(PAGE, RegistryPathQueryTestSigningCallback) // #pragma alloc_text(PAGE, DetermineIfTestSigningIsEnabled) #endif @@ -204,7 +195,8 @@ NTSTATUS StoreModuleExecutableRegionsInBuffer(_Outptr_result_bytebuffer_(*BytesWritten) PVOID* Buffer, _In_ PVOID ModuleBase, _In_ SIZE_T ModuleSize, - _Out_ _Deref_out_range_(>, 0) PSIZE_T BytesWritten) + _Out_ _Deref_out_range_(>, 0) PSIZE_T BytesWritten, + _In_ BOOLEAN IsModulex86) { PAGED_CODE(); @@ -244,7 +236,7 @@ StoreModuleExecutableRegionsInBuffer(_Outptr_result_bytebuffer_(*BytesWritten) P */ dos_header = (PIMAGE_DOS_HEADER)ModuleBase; - if (!MmIsAddressValid(dos_header)) + if (!MmIsAddressValid(dos_header) && !IsModulex86) { ImpExFreePoolWithTag(*Buffer, POOL_TAG_INTEGRITY); *Buffer = NULL; @@ -433,7 +425,8 @@ ComputeHashOfBuffer(_In_ PVOID Buffer, *HashResult = NULL; *HashResultSize = 0; - status = BCryptOpenAlgorithmProvider(&algo_handle, BCRYPT_SHA256_ALGORITHM, NULL, NULL); + status = BCryptOpenAlgorithmProvider( + &algo_handle, BCRYPT_SHA256_ALGORITHM, NULL, BCRYPT_PROV_DISPATCH); if (!NT_SUCCESS(status)) { @@ -566,7 +559,7 @@ RetrieveInMemoryModuleExecutableSections(_Inout_ PIRP Irp) } status = StoreModuleExecutableRegionsInBuffer( - &buffer, module_info.ImageBase, module_info.ImageSize, &bytes_written); + &buffer, module_info.ImageBase, module_info.ImageSize, &bytes_written, FALSE); if (!NT_SUCCESS(status)) { @@ -897,8 +890,11 @@ ValidateProcessLoadedModule(_Inout_ PIRP Irp) */ ImpKeStackAttachProcess(process, &apc_state); - status = StoreModuleExecutableRegionsInBuffer( - &memory_buffer, module_info->module_base, module_info->module_size, &bytes_written); + status = StoreModuleExecutableRegionsInBuffer(&memory_buffer, + module_info->module_base, + module_info->module_size, + &bytes_written, + FALSE); ImpKeUnstackDetachProcess(&apc_state); @@ -918,7 +914,7 @@ ValidateProcessLoadedModule(_Inout_ PIRP Irp) } status = StoreModuleExecutableRegionsInBuffer( - &disk_buffer, section, section_size, &bytes_written); + &disk_buffer, section, section_size, &bytes_written, FALSE); if (!NT_SUCCESS(status)) { @@ -1405,11 +1401,75 @@ FindWinLogonProcess(_In_ PPROCESS_LIST_ENTRY Entry, _In_opt_ PVOID Context) process_name = ImpPsGetProcessImageFileName(Entry->process); if (!strcmp(process_name, "winlogon.exe")) - { - DEBUG_VERBOSE("32 bit WinLogon.exe process found at address: %llx", - (UINT64)Entry->process); *process = Entry->process; +} + +STATIC +NTSTATUS +StoreModuleExecutableRegionsx86(_In_ PRTL_MODULE_EXTENDED_INFO Module, + _In_ PVOID* Buffer, + _In_ PULONG BufferSize) +{ + NTSTATUS status = STATUS_UNSUCCESSFUL; + PEPROCESS process = NULL; + KAPC_STATE apc_state = {0}; + + EnumerateProcessListWithCallbackRoutine(FindWinLogonProcess, &process); + + if (!process) + return STATUS_NOT_FOUND; + + ImpKeStackAttachProcess(process, &apc_state); + + status = StoreModuleExecutableRegionsInBuffer( + Buffer, Module->ImageBase, Module->ImageSize, BufferSize, TRUE); + + ImpKeUnstackDetachProcess(&apc_state); + + if (!NT_SUCCESS(status)) + DEBUG_ERROR("StoreModuleExecutableRegionsInBuffer-x86 failed with status %x", + status); + + return status; +} + +VOID +HashDeferredx86ModuleDeferredRoutine() +{ + NTSTATUS status = STATUS_UNSUCCESSFUL; + RTL_MODULE_EXTENDED_INFO module = {0}; + PLIST_ENTRY deferred_head = &GetDriverList()->deferred_unhashed_x86_modules; + PLIST_ENTRY list_entry = NULL; + PDRIVER_LIST_ENTRY entry = NULL; + + list_entry = RemoveHeadList(deferred_head); + + if (list_entry == deferred_head) + goto end; + + entry = CONTAINING_RECORD(list_entry, DRIVER_LIST_ENTRY, deferred_entry); + + while (list_entry != deferred_head) + { + entry = CONTAINING_RECORD(list_entry, DRIVER_LIST_ENTRY, deferred_entry); + + DriverListEntryToExtendedModuleInfo(entry, &module); + + status = HashModule(&module, &entry->text_hash); + + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("HashModule-x86 failed with status %x", status); + return; + } + + entry->hashed = TRUE; + list_entry = RemoveHeadList(deferred_head); } + +end: + DEBUG_VERBOSE("All deferred x86 modules hashed."); + ImpIoFreeWorkItem(Getx86HashingWorkItem()); } NTSTATUS @@ -1423,8 +1483,6 @@ HashModule(_In_ PRTL_MODULE_EXTENDED_INFO Module, _Out_ PVOID Hash) ULONG memory_hash_size = 0; PVAL_INTEGRITY_HEADER memory_buffer = NULL; ULONG memory_buffer_size = 0; - PEPROCESS process = NULL; - KAPC_STATE apc_state = {0}; ImpRtlInitAnsiString(&ansi_string, Module->FullPathName); @@ -1448,33 +1506,32 @@ HashModule(_In_ PRTL_MODULE_EXTENDED_INFO Module, _Out_ PVOID Hash) * 32 bit image base wont be a valid address, while this is hacky it works. * Then we simply attach to a 32 bit address space, in our case winlogon, * which will allow us to perform the copy. + * + * Since the driver loads at system startup, our driver is loaded before the WinLogon + * process has started, so to combat this return return early with a status code. This will + * mark the module as not hashed and x86. We will then queue a work item to hash these + * modules later once WinLogon has started. */ - if (!ImpMmIsAddressValid(Module->ImageBase)) + if (!ImpMmIsAddressValid(Module->ImageBase) && !HasWinlogonProcessStarted()) { - // DEBUG_VERBOSE("Win32k related module found, acquiring 32 bit address space..."); - - // EnumerateProcessListWithCallbackRoutine(FindWinLogonProcess, &process); - - // if (!process) - // goto end; - - // ImpKeStackAttachProcess(process, &apc_state); - - // status = StoreModuleExecutableRegionsInBuffer((PVOID)&memory_buffer, - // Module->ImageBase, - // Module->ImageSize, - // &memory_buffer_size); - - // ImpKeUnstackDetachProcess(&apc_state); status = STATUS_INVALID_IMAGE_WIN_32; goto end; } + else if (!ImpMmIsAddressValid(Module->ImageBase) && HasWinlogonProcessStarted()) + { + /* + * Once the WinLogon process has started, we can then hash new x86 modules. + */ + status = StoreModuleExecutableRegionsx86( + Module, (PVOID)&memory_buffer, &memory_buffer_size); + } else { status = StoreModuleExecutableRegionsInBuffer((PVOID)&memory_buffer, Module->ImageBase, Module->ImageSize, - &memory_buffer_size); + &memory_buffer_size, + FALSE); } if (!NT_SUCCESS(status)) diff --git a/driver/integrity.h b/driver/integrity.h index 23ff885..f9f4828 100644 --- a/driver/integrity.h +++ b/driver/integrity.h @@ -119,4 +119,7 @@ ValidateSystemModule(_In_ PRTL_MODULE_EXTENDED_INFO Module); BOOLEAN ValidateOurDriversDispatchRoutines(); +VOID +HashDeferredx86ModuleDeferredRoutine(); + #endif diff --git a/driver/io.c b/driver/io.c index f7df47f..9dbf0a6 100644 --- a/driver/io.c +++ b/driver/io.c @@ -75,17 +75,20 @@ DispatchApcOperation(_In_ PAPC_OPERATION_ID Operation); * * user mode program will automatically queue another irp when an irp completes, ensuring queue has * a sufficient supply. + * + * note: maybe we should use a spinlock here? Dont really want competing threads sleeping. I think + * spinlock should be used here. */ VOID IrpQueueAcquireLock(_In_ PIO_CSQ Csq, _Out_ PKIRQL Irql) { - KeAcquireGuardedMutex(&GetIrpQueueHead()->lock); + KeAcquireSpinLock(&GetIrpQueueHead()->lock, Irql); } VOID -IrpQueueReleaseLock(_In_ PIO_CSQ Csq, _Out_ PKIRQL Irql) +IrpQueueReleaseLock(_In_ PIO_CSQ Csq, _In_ KIRQL Irql) { - KeReleaseGuardedMutex(&GetIrpQueueHead()->lock); + KeReleaseSpinLock(&GetIrpQueueHead()->lock, Irql); } PIRP @@ -111,13 +114,13 @@ IrpQueueRemove(_In_ PIO_CSQ Csq, _In_ PIRP Irp) BOOLEAN IrpQueueIsThereDeferredReport(_In_ PIRP_QUEUE_HEAD Queue) { - return Queue->reports.count > 0 ? TRUE : FALSE; + return Queue->deferred_reports.count > 0 ? TRUE : FALSE; } PDEFERRED_REPORT IrpQueueRemoveDeferredReport(_In_ PIRP_QUEUE_HEAD Queue) { - return RemoveHeadList(&Queue->reports.head); + return RemoveHeadList(&Queue->deferred_reports.head); } STATIC @@ -150,6 +153,7 @@ IrpQueueQueryPendingReports(_In_ PIRP Irp) PIRP_QUEUE_HEAD queue = GetIrpQueueHead(); PDEFERRED_REPORT report = NULL; NTSTATUS status = STATUS_UNSUCCESSFUL; + KIRQL irql = 0; /* * Important we hold the lock before we call IsThereDeferredReport to prevent the race @@ -157,7 +161,7 @@ IrpQueueQueryPendingReports(_In_ PIRP Irp) * removes the last entry from the list. We then request a deferred report and will receive * a null value leading to a bugcheck in the subsequent call to CompleteDeferredReport. */ - KeAcquireGuardedMutex(&queue->reports.lock); + KeAcquireSpinLock(&GetIrpQueueHead()->deferred_reports.lock, &irql); if (IrpQueueIsThereDeferredReport(queue)) { @@ -167,16 +171,16 @@ IrpQueueQueryPendingReports(_In_ PIRP Irp) if (!NT_SUCCESS(status)) { IrpQueueFreeDeferredReport(report); - KeReleaseGuardedMutex(&queue->reports.lock); + KeReleaseSpinLock(&GetIrpQueueHead()->deferred_reports.lock, irql); return status; } - queue->reports.count--; - KeReleaseGuardedMutex(&queue->reports.lock); + queue->deferred_reports.count--; + KeReleaseSpinLock(&GetIrpQueueHead()->deferred_reports.lock, irql); return status; } - KeReleaseGuardedMutex(&queue->reports.lock); + KeReleaseSpinLock(&GetIrpQueueHead()->deferred_reports.lock, irql); return status; } @@ -216,28 +220,34 @@ IrpQueueAllocateDeferredReport(_In_ PVOID Buffer, _In_ UINT32 BufferSize) VOID IrpQueueDeferReport(_In_ PIRP_QUEUE_HEAD Queue, _In_ PVOID Buffer, _In_ UINT32 BufferSize) { + PDEFERRED_REPORT report = NULL; + KIRQL irql = 0; /* * arbitrary number, if we ever do have 100 deferred reports, theres probably a catastrophic * error somewhere else */ - if (Queue->reports.count > MAX_DEFERRED_REPORTS_COUNT) + if (Queue->deferred_reports.count > MAX_DEFERRED_REPORTS_COUNT) { ImpExFreePoolWithTag(Buffer, REPORT_POOL_TAG); return; } - PDEFERRED_REPORT report = IrpQueueAllocateDeferredReport(Buffer, BufferSize); + report = IrpQueueAllocateDeferredReport(Buffer, BufferSize); if (!report) return; - KeAcquireGuardedMutex(&Queue->reports.lock); - InsertTailList(&Queue->reports.head, &report->list_entry); - Queue->reports.count++; - KeReleaseGuardedMutex(&Queue->reports.lock); + KeAcquireSpinLock(&GetIrpQueueHead()->deferred_reports.lock, &irql); + InsertTailList(&Queue->deferred_reports.head, &report->list_entry); + Queue->deferred_reports.count++; + KeReleaseSpinLock(&GetIrpQueueHead()->deferred_reports.lock, irql); } -/* takes ownership of the buffer, and regardless of the outcome will free it. */ +/* +* takes ownership of the buffer, and regardless of the outcome will free it. +* +* IMPORTANT: All report buffers must be allocated in non paged memory. +*/ NTSTATUS IrpQueueCompleteIrp(_In_ PVOID Buffer, _In_ ULONG BufferSize) { @@ -284,9 +294,10 @@ IrpQueueFreeDeferredReports() { PIRP_QUEUE_HEAD queue = GetIrpQueueHead(); PDEFERRED_REPORT report = NULL; + KIRQL irql = 0; /* just in case... */ - KeAcquireGuardedMutex(&queue->reports.lock); + KeAcquireSpinLock(&GetIrpQueueHead()->deferred_reports.lock, &irql); while (IrpQueueIsThereDeferredReport(queue)) { @@ -294,7 +305,7 @@ IrpQueueFreeDeferredReports() IrpQueueFreeDeferredReport(report); } - KeReleaseGuardedMutex(&queue->reports.lock); + KeReleaseSpinLock(&GetIrpQueueHead()->deferred_reports.lock, irql); } NTSTATUS @@ -303,10 +314,10 @@ IrpQueueInitialise() NTSTATUS status = STATUS_UNSUCCESSFUL; PIRP_QUEUE_HEAD queue = GetIrpQueueHead(); - KeInitializeGuardedMutex(&queue->lock); - KeInitializeGuardedMutex(&queue->reports.lock); + KeInitializeSpinLock(&queue->lock); + KeInitializeSpinLock(&queue->deferred_reports.lock); InitializeListHead(&queue->queue); - InitializeListHead(&queue->reports.head); + InitializeListHead(&queue->deferred_reports.head); status = IoCsqInitialize(&queue->csq, IrpQueueInsert, diff --git a/driver/modules.c b/driver/modules.c index be7c6da..431e1ce 100644 --- a/driver/modules.c +++ b/driver/modules.c @@ -684,8 +684,10 @@ HandleValidateDriversIOCTL() continue; } - PMODULE_VALIDATION_FAILURE report = ImpExAllocatePool2( - POOL_FLAG_PAGED, sizeof(MODULE_VALIDATION_FAILURE), POOL_TAG_INTEGRITY); + PMODULE_VALIDATION_FAILURE report = + ImpExAllocatePool2(POOL_FLAG_NON_PAGED, + sizeof(MODULE_VALIDATION_FAILURE), + POOL_TAG_INTEGRITY); if (!report) continue; @@ -1236,31 +1238,11 @@ ValidateThreadViaKernelApcCallback(_In_ PTHREAD_LIST_ENTRY ThreadListEntry, PCHAR previous_mode = NULL; PUCHAR state = NULL; BOOLEAN apc_queueable = FALSE; + LPCSTR process_name = NULL; PAPC_STACKWALK_CONTEXT context = (PAPC_STACKWALK_CONTEXT)Context; - LPCSTR process_name = ImpPsGetProcessImageFileName(ThreadListEntry->owning_process); - /* - * we dont want to schedule an apc to threads owned by the kernel - * - * Actually we do... todo: fix this. - */ - if (ThreadListEntry->owning_process == PsInitialSystemProcess || !Context) - return; + process_name = ImpPsGetProcessImageFileName(ThreadListEntry->owning_process); - /* We are not interested in these processess.. for now lol */ - if (!strcmp(process_name, "svchost.exe") || !strcmp(process_name, "Registry") || - !strcmp(process_name, "smss.exe") || !strcmp(process_name, "csrss.exe") || - !strcmp(process_name, "explorer.exe") || !strcmp(process_name, "svchost.exe") || - !strcmp(process_name, "lsass.exe") || !strcmp(process_name, "MemCompression") || - !strcmp(process_name, "WerFault.exe")) - return; - - DEBUG_VERBOSE("Validating thread: %llx, process name: %s via kernel APC stackwalk.", - ThreadListEntry->thread, - process_name); - - if (ThreadListEntry->thread == KeGetCurrentThread() || !ThreadListEntry->thread) - return; /* * Its possible to set the KThread->ApcQueueable flag to false ensuring that no APCs * can be queued to the thread, as KeInsertQueueApc will check this flag before @@ -1271,6 +1253,20 @@ ValidateThreadViaKernelApcCallback(_In_ PTHREAD_LIST_ENTRY ThreadListEntry, previous_mode = (PCHAR)((UINT64)ThreadListEntry->thread + KTHREAD_PREVIOUS_MODE_OFFSET); state = (PUCHAR)((UINT64)ThreadListEntry->thread + KTHREAD_STATE_OFFSET); + /* + * For now, lets only check for system threads. However, we also want to check for threads + * executing in kernel mode, i.e KTHREAD->PreviousMode == UserMode. + */ + if (ThreadListEntry->owning_process != PsInitialSystemProcess) + return; + + if (ThreadListEntry->thread == KeGetCurrentThread() || !ThreadListEntry->thread) + return; + + DEBUG_VERBOSE("Validating thread: %llx, process name: %s via kernel APC stackwalk.", + ThreadListEntry->thread, + process_name); + /* we dont care about user mode threads */ // if (*previous_mode == UserMode) // return; diff --git a/driver/pool.c b/driver/pool.c index d2e5cf0..56b1757 100644 --- a/driver/pool.c +++ b/driver/pool.c @@ -698,7 +698,7 @@ FindUnlinkedProcesses() allocation); report_buffer = ImpExAllocatePool2( - POOL_FLAG_PAGED, sizeof(INVALID_PROCESS_ALLOCATION_REPORT), REPORT_POOL_TAG); + POOL_FLAG_NON_PAGED, sizeof(INVALID_PROCESS_ALLOCATION_REPORT), REPORT_POOL_TAG); if (!report_buffer) continue; diff --git a/module/dispatcher/dispatcher.cpp b/module/dispatcher/dispatcher.cpp index c929f4a..e66c5ee 100644 --- a/module/dispatcher/dispatcher.cpp +++ b/module/dispatcher/dispatcher.cpp @@ -47,13 +47,15 @@ void dispatcher::dispatcher::run_io_port_thread() { } void dispatcher::dispatcher::run() { - helper::generate_rand_seed(); + //helper::generate_rand_seed(); + std::srand(std::time(nullptr)); this->init_timer_callbacks(); this->run_timer_thread(); this->run_io_port_thread(); thread_pool.queue_job([this]() { k_interface.run_completion_port(); }); while (true) { - this->issue_kernel_job(); + //this->issue_kernel_job(); + this->k_interface.initiate_apc_stackwalk(); helper::sleep_thread(DISPATCH_LOOP_SLEEP_TIME); } } diff --git a/module/helper.cpp b/module/helper.cpp index ef09267..2de351d 100644 --- a/module/helper.cpp +++ b/module/helper.cpp @@ -3,9 +3,9 @@ #include #include -void helper::generate_rand_seed() { srand(time(nullptr)); } +void helper::generate_rand_seed() { srand(time(0)); } -int helper::generate_rand_int(int max) { return rand() % max; } +int helper::generate_rand_int(int max) { return std::rand() % max; } void helper::sleep_thread(int seconds) { std::this_thread::sleep_for(std::chrono::seconds(seconds));