diff --git a/driver/ioctl.c b/driver/ioctl.c index 1801500..774805c 100644 --- a/driver/ioctl.c +++ b/driver/ioctl.c @@ -30,6 +30,7 @@ NTSTATUS DeviceControl( case IOCTL_VALIDATE_DRIVER_OBJECTS: + /* KeWaitForSingleObject with infinite time must be called from IRQL <= APC_LEVEL */ PAGED_CODE(); /* diff --git a/driver/modules.c b/driver/modules.c index 8787f26..ece771c 100644 --- a/driver/modules.c +++ b/driver/modules.c @@ -3,6 +3,47 @@ #include "nmi.h" #include "common.h" +NTSTATUS ValidateDriverIOCTLDispatchRegion( + _In_ PDRIVER_OBJECT Driver, + _In_ PSYSTEM_MODULES Modules, + _In_ PBOOLEAN Flag +) +{ + NTSTATUS status; + UINT64 current_function; + + UINT64 base = ( UINT64 )Driver->DriverStart; + UINT64 end = base + Driver->DriverSize; + + *Flag = TRUE; + + /* + * If the dispatch routine points to a location that is not in the confines of + * the module, report it. Basic check but every effective for catching driver + * dispatch hooking. + */ + + for ( INT index = 0; index < IRP_MJ_MAXIMUM_FUNCTION + 1; index++ ) + { + current_function = *(UINT64*) + ( ( UINT64 )Driver->MajorFunction + index * sizeof( PVOID ) ); + + DEBUG_LOG( "Current function: %llx", current_function ); + + if ( current_function == NULL ) + continue; + + if ( current_function >= base && current_function <= end ) + { + DEBUG_LOG( "THIS ADDRESS IS INSIDE ITS REGIUON :)" ); + continue; + } + + DEBUG_ERROR( "Driver with invalid IOCTL dispatch routine found" ); + *Flag = FALSE; + } +} + VOID InitDriverList( _In_ PINVALID_DRIVERS_HEAD ListHead ) @@ -13,7 +54,8 @@ VOID InitDriverList( VOID AddDriverToList( _In_ PINVALID_DRIVERS_HEAD InvalidDriversHead, - _In_ PDRIVER_OBJECT Driver + _In_ PDRIVER_OBJECT Driver, + _In_ INT Reason ) { PINVALID_DRIVER new_entry = ExAllocatePool2( @@ -26,6 +68,7 @@ VOID AddDriverToList( return; new_entry->driver = Driver; + new_entry->reason = Reason; new_entry->next = InvalidDriversHead->first_entry; InvalidDriversHead->first_entry = new_entry; } @@ -215,6 +258,8 @@ NTSTATUS ValidateDriverObjects( PDRIVER_OBJECT current_driver = sub_entry->Object; BOOLEAN flag; + /* validate driver has backing module */ + if ( !NT_SUCCESS( ValidateDriverObjectHasBackingModule( SystemModules, current_driver, @@ -231,7 +276,32 @@ NTSTATUS ValidateDriverObjects( if ( !flag ) { InvalidDriverListHead->count += 1; - AddDriverToList( InvalidDriverListHead, current_driver ); + AddDriverToList( InvalidDriverListHead, current_driver, REASON_NO_BACKING_MODULE ); + } + + /* validate drivers IOCTL dispatch routines */ + + if ( !NT_SUCCESS( ValidateDriverIOCTLDispatchRegion( + current_driver, + SystemModules, + &flag + ) ) ) + { + DEBUG_LOG( "Error validating drivers IOCTL routines" ); + ExReleasePushLockExclusiveEx( &directory_object->Lock, 0 ); + ObDereferenceObject( directory ); + ZwClose( handle ); + return STATUS_ABANDONED; + } + + if ( !flag ) + { + InvalidDriverListHead->count += 1; + AddDriverToList( InvalidDriverListHead, current_driver, REASON_INVALID_IOCTL_DISPATCH ); + } + else + { + DEBUG_LOG( "All drivers have valid dispatch routines :)" ); } sub_entry = sub_entry->ChainLink; @@ -310,6 +380,7 @@ NTSTATUS HandleValidateDriversIOCTL( MODULE_VALIDATION_FAILURE report; report.report_code = REPORT_MODULE_VALIDATION_FAILURE; + report.report_type = head->first_entry->reason; report.driver_base_address = head->first_entry->driver->DriverStart; report.driver_size = head->first_entry->driver->Size; diff --git a/driver/modules.h b/driver/modules.h index b9bca67..3716bb5 100644 --- a/driver/modules.h +++ b/driver/modules.h @@ -9,6 +9,9 @@ #define MODULE_REPORT_DRIVER_NAME_BUFFER_SIZE 128 +#define REASON_NO_BACKING_MODULE 1 +#define REASON_INVALID_IOCTL_DISPATCH 2 + typedef struct _MODULE_VALIDATION_FAILURE_HEADER { INT module_count; @@ -18,6 +21,7 @@ typedef struct _MODULE_VALIDATION_FAILURE_HEADER typedef struct _MODULE_VALIDATION_FAILURE { INT report_code; + INT report_type; UINT64 driver_base_address; UINT64 driver_size; PCHAR driver_name[ 128 ]; @@ -27,6 +31,7 @@ typedef struct _MODULE_VALIDATION_FAILURE typedef struct _INVALID_DRIVER { struct _INVALID_DRIVER* next; + INT reason; PDRIVER_OBJECT driver; }INVALID_DRIVER, * PINVALID_DRIVER; diff --git a/service/Types.cs b/service/Types.cs index f183297..0216e2f 100644 --- a/service/Types.cs +++ b/service/Types.cs @@ -44,5 +44,28 @@ namespace service public int SignatureId; public UInt64 Address; } + + public struct NMI_CALLBACK_FAILURE + { + public int ReportCode; + public int WereNmisDisabled; + public UInt64 KThreadAddress; + public UInt64 InvalidRip; + } + + [StructLayout(LayoutKind.Explicit)] + public unsafe struct MODULE_VALIDATION_FAILURE + { + [FieldOffset(0)] + public int ReportCode; + [FieldOffset(0)] + public int ReportType; + [FieldOffset(0)] + public UInt64 DriverBaseAddress; + [FieldOffset(0)] + public UInt64 DriverSize; + [FieldOffset(0)] + public fixed char ModuleName[128]; + } } } diff --git a/service/Worker.cs b/service/Worker.cs index b8e8fbf..4c4733c 100644 --- a/service/Worker.cs +++ b/service/Worker.cs @@ -16,10 +16,12 @@ namespace service private byte[] _headerBuf; private int _headerBufSize; - private const int REPORT_CODE_MODULE_VERIFICATION = 10; - private const int REPORT_CODE_START_ADDRESS_VERIFICATION = 20; + private const int REPORT_PROCESS_MODULE_FAILURE = 10; + private const int REPORT_PROCESS_THREAD_START_ADDRESS_FAILURE = 20; private const int REPORT_PAGE_PROTECTION_VERIFICATION = 30; private const int REPORT_PATTERN_SCAN_FAILURE = 40; + private const int REPORT_NMI_CALLBACK_FAILURE = 50; + private const int REPORT_KERNEL_MODULE_FAILURE = 60; private const int MESSAGE_TYPE_REPORT = 1; private const int MESSAGE_TYPE_REQUEST = 2; @@ -94,7 +96,7 @@ namespace service switch (reportCode) { - case REPORT_CODE_MODULE_VERIFICATION: + case REPORT_PROCESS_MODULE_FAILURE: var checksumFailurePacket = BytesToStructure(); @@ -108,7 +110,7 @@ namespace service goto end; - case REPORT_CODE_START_ADDRESS_VERIFICATION: + case REPORT_PROCESS_THREAD_START_ADDRESS_FAILURE: var startAddressFailurePacket = BytesToStructure(); @@ -143,6 +145,29 @@ namespace service goto end; + case REPORT_NMI_CALLBACK_FAILURE: + + var nmiCallbackFailure = BytesToStructure(); + + _logger.LogInformation("Report code: {0}, WereNmisDisabled: {1}, KThreadAddress: {2}, InvalidRip: {3}", + nmiCallbackFailure.ReportCode, + nmiCallbackFailure.WereNmisDisabled, + nmiCallbackFailure.KThreadAddress, + nmiCallbackFailure.InvalidRip); + + goto end; + + case REPORT_KERNEL_MODULE_FAILURE: + + var kernelModuleFailure = BytesToStructure(); + + _logger.LogInformation("Report code: {0}, DriverBaseAddress: {1}, DriverSize: {2}", + kernelModuleFailure.ReportCode, + kernelModuleFailure.DriverBaseAddress, + kernelModuleFailure.DriverSize); + + goto end; + default: _logger.LogError("Invalid report code received"); goto end; diff --git a/user/km/driver.cpp b/user/km/driver.cpp index 87df6ab..94f44e1 100644 --- a/user/km/driver.cpp +++ b/user/km/driver.cpp @@ -53,6 +53,11 @@ void kernelmode::Driver::RunNmiCallbacks() this->report_interface->ReportViolation( &report ); } +/* +* 1. Checks that every device object has a system module to back it +* 2. Checks the IOCTL dispatch routines to ensure they lie within the module +*/ + void kernelmode::Driver::VerifySystemModules() { BOOLEAN status; @@ -154,10 +159,6 @@ void kernelmode::Driver::CheckForHypervisor() { } -void kernelmode::Driver::VerifySystemModulesIOCTLDispatchHandler() -{ -} - void kernelmode::Driver::CheckDriverHeartbeat() { } diff --git a/user/km/driver.h b/user/km/driver.h index 155b759..dd20d00 100644 --- a/user/km/driver.h +++ b/user/km/driver.h @@ -28,7 +28,6 @@ namespace kernelmode void DisableProcessLoadNotifyCallbacks(); void ValidateKPRCBThreads(); void CheckForHypervisor(); - void VerifySystemModulesIOCTLDispatchHandler(); void CheckDriverHeartbeat(); /* todo: driver integrity check */ }; diff --git a/user/report.h b/user/report.h index 9b4ea46..1dc76cf 100644 --- a/user/report.h +++ b/user/report.h @@ -100,6 +100,7 @@ namespace global struct MODULE_VALIDATION_FAILURE { INT report_code; + INT report_type; UINT64 driver_base_address; UINT64 driver_size; BYTE driver_name[ 128 ];