mirror of
https://github.com/donnaskiez/ac.git
synced 2024-11-21 22:24:08 +01:00
big money changes to driver
This commit is contained in:
parent
9356803b33
commit
222191ffea
16 changed files with 1135 additions and 15 deletions
107
driver/common.h
107
driver/common.h
|
@ -8,4 +8,111 @@
|
||||||
#define DEBUG_LOG(fmt, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, 0, "[+] " fmt "\n", ##__VA_ARGS__)
|
#define DEBUG_LOG(fmt, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, 0, "[+] " fmt "\n", ##__VA_ARGS__)
|
||||||
#define DEBUG_ERROR(fmt, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, 0, "[-] " fmt "\n", ##__VA_ARGS__)
|
#define DEBUG_ERROR(fmt, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, 0, "[-] " fmt "\n", ##__VA_ARGS__)
|
||||||
|
|
||||||
|
#define NMI_CONTEXT_POOL '7331'
|
||||||
|
#define STACK_FRAMES_POOL 'loop'
|
||||||
|
#define INVALID_DRIVER_LIST_HEAD_POOL 'rwar'
|
||||||
|
#define INVALID_DRIVER_LIST_ENTRY_POOL 'gaah'
|
||||||
|
#define SYSTEM_MODULES_POOL 'halb'
|
||||||
|
#define THREAD_DATA_POOL 'doof'
|
||||||
|
#define PROC_AFFINITY_POOL 'eeee'
|
||||||
|
|
||||||
|
#define ERROR -1
|
||||||
|
#define STACK_FRAME_POOL_SIZE 0x200
|
||||||
|
#define NUMBER_HASH_BUCKETS 37
|
||||||
|
|
||||||
|
#define KTHREAD_STACK_BASE_OFFSET 0x030
|
||||||
|
#define KTHREAD_STACK_LIMIT_OFFSET 0x038
|
||||||
|
#define KTHREAD_START_ADDRESS_OFFSET 0x450
|
||||||
|
|
||||||
|
typedef struct _KAFFINITY_EX
|
||||||
|
{
|
||||||
|
USHORT Count;
|
||||||
|
USHORT Size;
|
||||||
|
ULONG Reserved;
|
||||||
|
ULONGLONG Bitmap[ 20 ];
|
||||||
|
|
||||||
|
} KAFFINITY_EX, * PKAFFINITY_EX;
|
||||||
|
|
||||||
|
typedef struct _OBJECT_DIRECTORY_ENTRY
|
||||||
|
{
|
||||||
|
struct _OBJECT_DIRECTORY_ENTRY* ChainLink;
|
||||||
|
PVOID Object;
|
||||||
|
ULONG HashValue;
|
||||||
|
|
||||||
|
} OBJECT_DIRECTORY_ENTRY, * POBJECT_DIRECTORY_ENTRY;
|
||||||
|
|
||||||
|
typedef struct _OBJECT_DIRECTORY
|
||||||
|
{
|
||||||
|
POBJECT_DIRECTORY_ENTRY HashBuckets[ NUMBER_HASH_BUCKETS ];
|
||||||
|
EX_PUSH_LOCK Lock;
|
||||||
|
struct _DEVICE_MAP* DeviceMap;
|
||||||
|
ULONG SessionId;
|
||||||
|
PVOID NamespaceEntry;
|
||||||
|
ULONG Flags;
|
||||||
|
|
||||||
|
} OBJECT_DIRECTORY, * POBJECT_DIRECTORY;
|
||||||
|
|
||||||
|
typedef struct _DEVICE_MAP
|
||||||
|
{
|
||||||
|
struct _OBJECT_DIRECTORY* DosDevicesDirectory;
|
||||||
|
struct _OBJECT_DIRECTORY* GlobalDosDevicesDirectory;
|
||||||
|
ULONG ReferenceCount;
|
||||||
|
ULONG DriveMap;
|
||||||
|
UCHAR DriveType[ 32 ];
|
||||||
|
|
||||||
|
} DEVICE_MAP, * PDEVICE_MAP;
|
||||||
|
|
||||||
|
typedef struct _RTL_MODULE_EXTENDED_INFO
|
||||||
|
{
|
||||||
|
PVOID ImageBase;
|
||||||
|
ULONG ImageSize;
|
||||||
|
USHORT FileNameOffset;
|
||||||
|
CHAR FullPathName[ 0x100 ];
|
||||||
|
|
||||||
|
} RTL_MODULE_EXTENDED_INFO, * PRTL_MODULE_EXTENDED_INFO;
|
||||||
|
|
||||||
|
/* undocumented functions */
|
||||||
|
|
||||||
|
EXTERN_C VOID KeInitializeAffinityEx(
|
||||||
|
PKAFFINITY_EX affinity
|
||||||
|
);
|
||||||
|
|
||||||
|
EXTERN_C VOID KeAddProcessorAffinityEx(
|
||||||
|
PKAFFINITY_EX affinity,
|
||||||
|
INT num
|
||||||
|
);
|
||||||
|
|
||||||
|
EXTERN_C VOID HalSendNMI(
|
||||||
|
PKAFFINITY_EX affinity
|
||||||
|
);
|
||||||
|
|
||||||
|
NTSTATUS
|
||||||
|
RtlQueryModuleInformation(
|
||||||
|
ULONG* InformationLength,
|
||||||
|
ULONG SizePerModule,
|
||||||
|
PVOID InformationBuffer );
|
||||||
|
|
||||||
|
/*
|
||||||
|
Thread Information Block: (GS register)
|
||||||
|
|
||||||
|
SEH frame: 0x00
|
||||||
|
Stack Base: 0x08
|
||||||
|
Stack Limit: 0x10
|
||||||
|
SubSystemTib: 0x18
|
||||||
|
Fiber Data: 0x20
|
||||||
|
Arbitrary Data: 0x28
|
||||||
|
TEB: 0x30
|
||||||
|
Environment Pointer: 0x38
|
||||||
|
Process ID: 0x40
|
||||||
|
Current Thread ID: 0x48
|
||||||
|
Active RPC Handle: 0x50
|
||||||
|
Thread Local Storage Array: 0x58
|
||||||
|
PEB: 0x60
|
||||||
|
Last error number: 0x68
|
||||||
|
Count Owned Critical Sections: 0x6C
|
||||||
|
CSR Client Thread: 0x70
|
||||||
|
Win32 Thread Information: 0x78
|
||||||
|
...
|
||||||
|
*/
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -119,11 +119,15 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="driver.c" />
|
<ClCompile Include="driver.c" />
|
||||||
<ClCompile Include="ioctl.c" />
|
<ClCompile Include="ioctl.c" />
|
||||||
|
<ClCompile Include="modules.c" />
|
||||||
|
<ClCompile Include="nmi.c" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="common.h" />
|
<ClInclude Include="common.h" />
|
||||||
<ClInclude Include="driver.h" />
|
<ClInclude Include="driver.h" />
|
||||||
<ClInclude Include="ioctl.h" />
|
<ClInclude Include="ioctl.h" />
|
||||||
|
<ClInclude Include="modules.h" />
|
||||||
|
<ClInclude Include="nmi.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
|
|
@ -30,6 +30,12 @@
|
||||||
<ClCompile Include="ioctl.c">
|
<ClCompile Include="ioctl.c">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="nmi.c">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="modules.c">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="driver.h">
|
<ClInclude Include="driver.h">
|
||||||
|
@ -41,5 +47,11 @@
|
||||||
<ClInclude Include="ioctl.h">
|
<ClInclude Include="ioctl.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="nmi.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="modules.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
|
@ -2,14 +2,71 @@
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
#include "nmi.h"
|
||||||
|
#include "modules.h"
|
||||||
|
|
||||||
NTSTATUS DeviceControl(
|
NTSTATUS DeviceControl(
|
||||||
_In_ PDRIVER_OBJECT DriverObject,
|
_In_ PDRIVER_OBJECT DriverObject,
|
||||||
_In_ PIRP Irp
|
_In_ PIRP Irp
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
DEBUG_LOG( "Handle opened to DonnaAC" );
|
UNREFERENCED_PARAMETER( DriverObject );
|
||||||
|
|
||||||
|
NTSTATUS status = STATUS_SUCCESS;
|
||||||
|
PIO_STACK_LOCATION stack_location = IoGetCurrentIrpStackLocation( Irp );
|
||||||
|
HANDLE handle;
|
||||||
|
|
||||||
|
switch ( stack_location->Parameters.DeviceIoControl.IoControlCode )
|
||||||
|
{
|
||||||
|
case IOCCTL_RUN_NMI_CALLBACKS:
|
||||||
|
|
||||||
|
status = HandleNmiIOCTL( Irp );
|
||||||
|
|
||||||
|
if ( !NT_SUCCESS( status ) )
|
||||||
|
DEBUG_ERROR( "RunNmiCallbacks failed with status %lx", status );
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IOCTL_VALIDATE_DRIVER_OBJECTS:
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The reason this function is run in a new thread and not the thread
|
||||||
|
* issuing the IOCTL is because ZwOpenDirectoryObject issues a
|
||||||
|
* user mode handle if called on the user mode thread calling DeviceIoControl.
|
||||||
|
* This is a problem because when we pass said handle to ObReferenceObjectByHandle
|
||||||
|
* it will issue a bug check under windows driver verifier.
|
||||||
|
*/
|
||||||
|
|
||||||
|
status = PsCreateSystemThread(
|
||||||
|
&handle,
|
||||||
|
PROCESS_ALL_ACCESS,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
HandleValidateDriversIOCTL,
|
||||||
|
Irp
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( !NT_SUCCESS( status ) )
|
||||||
|
DEBUG_ERROR( "Failed to start thread to validate system drivers" );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* wait on our thread so we dont complete the IRP before we've filled the
|
||||||
|
* buffer with information and prevent any weird IRP multithreaded interactions
|
||||||
|
*/
|
||||||
|
KeWaitForSingleObject( handle, Executive, KernelMode, FALSE, NULL );
|
||||||
|
|
||||||
|
ZwClose( handle );
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
DEBUG_ERROR( "Invalid IOCTL passed to driver" );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
IoCompleteRequest( Irp, IO_NO_INCREMENT );
|
||||||
return Irp->IoStatus.Status;
|
Irp->IoStatus.Status = status;
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
NTSTATUS DeviceClose(
|
NTSTATUS DeviceClose(
|
||||||
|
|
|
@ -5,6 +5,11 @@
|
||||||
#include <wdftypes.h>
|
#include <wdftypes.h>
|
||||||
#include <wdf.h>
|
#include <wdf.h>
|
||||||
|
|
||||||
|
#include "nmi.h"
|
||||||
|
|
||||||
|
#define IOCCTL_RUN_NMI_CALLBACKS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2001, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
#define IOCTL_VALIDATE_DRIVER_OBJECTS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2002, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
NTSTATUS DeviceControl(
|
NTSTATUS DeviceControl(
|
||||||
_In_ PDRIVER_OBJECT DriverObject,
|
_In_ PDRIVER_OBJECT DriverObject,
|
||||||
_In_ PIRP Irp
|
_In_ PIRP Irp
|
||||||
|
|
337
driver/modules.c
Normal file
337
driver/modules.c
Normal file
|
@ -0,0 +1,337 @@
|
||||||
|
#include "modules.h"
|
||||||
|
|
||||||
|
#include "nmi.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
VOID InitDriverList(
|
||||||
|
_In_ PINVALID_DRIVERS_HEAD ListHead
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ListHead->count = 0;
|
||||||
|
ListHead->first_entry = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
VOID AddDriverToList(
|
||||||
|
_In_ PINVALID_DRIVERS_HEAD InvalidDriversHead,
|
||||||
|
_In_ PDRIVER_OBJECT Driver
|
||||||
|
)
|
||||||
|
{
|
||||||
|
PINVALID_DRIVER new_entry = ExAllocatePool2(
|
||||||
|
POOL_FLAG_NON_PAGED,
|
||||||
|
sizeof( INVALID_DRIVER ),
|
||||||
|
INVALID_DRIVER_LIST_ENTRY_POOL
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( !new_entry )
|
||||||
|
return;
|
||||||
|
|
||||||
|
new_entry->driver = Driver;
|
||||||
|
new_entry->next = InvalidDriversHead->first_entry;
|
||||||
|
InvalidDriversHead->first_entry = new_entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
VOID RemoveInvalidDriverFromList(
|
||||||
|
_In_ PINVALID_DRIVERS_HEAD InvalidDriversHead
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if ( InvalidDriversHead->first_entry )
|
||||||
|
{
|
||||||
|
PINVALID_DRIVER entry = InvalidDriversHead->first_entry;
|
||||||
|
InvalidDriversHead->first_entry = InvalidDriversHead->first_entry->next;
|
||||||
|
ExFreePoolWithTag( entry, INVALID_DRIVER_LIST_ENTRY_POOL );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VOID EnumerateInvalidDrivers(
|
||||||
|
_In_ PINVALID_DRIVERS_HEAD InvalidDriversHead
|
||||||
|
)
|
||||||
|
{
|
||||||
|
PINVALID_DRIVER entry = InvalidDriversHead->first_entry;
|
||||||
|
|
||||||
|
while ( entry != NULL )
|
||||||
|
{
|
||||||
|
DEBUG_LOG( "Invalid Driver: %wZ", entry->driver->DriverName );
|
||||||
|
entry = entry->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NTSTATUS ValidateDriverObjectHasBackingModule(
|
||||||
|
_In_ PSYSTEM_MODULES ModuleInformation,
|
||||||
|
_In_ PDRIVER_OBJECT DriverObject,
|
||||||
|
_Out_ PBOOLEAN Result
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if ( !ModuleInformation || !DriverObject || !Result )
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
for ( INT i = 0; i < ModuleInformation->module_count; i++ )
|
||||||
|
{
|
||||||
|
PRTL_MODULE_EXTENDED_INFO system_module = ( PRTL_MODULE_EXTENDED_INFO )(
|
||||||
|
( uintptr_t )ModuleInformation->address + i * sizeof( RTL_MODULE_EXTENDED_INFO ) );
|
||||||
|
|
||||||
|
if ( system_module->ImageBase == DriverObject->DriverStart )
|
||||||
|
{
|
||||||
|
*Result = TRUE;
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_LOG( "invalid driver found" );
|
||||||
|
*Result = FALSE;
|
||||||
|
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
//https://imphash.medium.com/windows-process-internals-a-few-concepts-to-know-before-jumping-on-memory-forensics-part-3-4a0e195d947b
|
||||||
|
NTSTATUS GetSystemModuleInformation(
|
||||||
|
_Out_ PSYSTEM_MODULES ModuleInformation
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if ( !ModuleInformation )
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
ULONG size = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* query system module information without an output buffer to get
|
||||||
|
* number of bytes required to store all module info structures
|
||||||
|
*/
|
||||||
|
if ( !NT_SUCCESS( RtlQueryModuleInformation(
|
||||||
|
&size,
|
||||||
|
sizeof( RTL_MODULE_EXTENDED_INFO ),
|
||||||
|
NULL
|
||||||
|
) ) )
|
||||||
|
{
|
||||||
|
DEBUG_ERROR( "Failed to query module information" );
|
||||||
|
return STATUS_ABANDONED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate a pool equal to the output size of RtlQueryModuleInformation */
|
||||||
|
PRTL_MODULE_EXTENDED_INFO driver_information = ExAllocatePool2(
|
||||||
|
POOL_FLAG_NON_PAGED,
|
||||||
|
size,
|
||||||
|
SYSTEM_MODULES_POOL
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( !driver_information )
|
||||||
|
{
|
||||||
|
DEBUG_ERROR( "Failed to allocate pool LOL" );
|
||||||
|
return STATUS_ABANDONED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Query the modules again this time passing a pointer to the allocated buffer */
|
||||||
|
if ( !NT_SUCCESS( RtlQueryModuleInformation(
|
||||||
|
&size,
|
||||||
|
sizeof( RTL_MODULE_EXTENDED_INFO ),
|
||||||
|
driver_information
|
||||||
|
) ) )
|
||||||
|
{
|
||||||
|
DEBUG_ERROR( "Failed lolz" );
|
||||||
|
ExFreePoolWithTag( driver_information, SYSTEM_MODULES_POOL );
|
||||||
|
return STATUS_ABANDONED;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModuleInformation->address = driver_information;
|
||||||
|
ModuleInformation->module_count = size / sizeof( RTL_MODULE_EXTENDED_INFO );
|
||||||
|
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
NTSTATUS ValidateDriverObjects(
|
||||||
|
_In_ PSYSTEM_MODULES SystemModules,
|
||||||
|
_In_ PINVALID_DRIVERS_HEAD InvalidDriverListHead
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if ( !SystemModules || !InvalidDriverListHead )
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
HANDLE handle;
|
||||||
|
OBJECT_ATTRIBUTES attributes = { 0 };
|
||||||
|
PVOID directory = { 0 };
|
||||||
|
UNICODE_STRING directory_name;
|
||||||
|
|
||||||
|
RtlInitUnicodeString( &directory_name, L"\\Driver" );
|
||||||
|
|
||||||
|
InitializeObjectAttributes(
|
||||||
|
&attributes,
|
||||||
|
&directory_name,
|
||||||
|
OBJ_CASE_INSENSITIVE,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( !NT_SUCCESS( ZwOpenDirectoryObject(
|
||||||
|
&handle,
|
||||||
|
DIRECTORY_ALL_ACCESS,
|
||||||
|
&attributes
|
||||||
|
) ) )
|
||||||
|
{
|
||||||
|
DEBUG_ERROR( "Failed to query directory object" );
|
||||||
|
return STATUS_ABANDONED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !NT_SUCCESS( ObReferenceObjectByHandle(
|
||||||
|
handle,
|
||||||
|
DIRECTORY_ALL_ACCESS,
|
||||||
|
NULL,
|
||||||
|
KernelMode,
|
||||||
|
&directory,
|
||||||
|
NULL
|
||||||
|
) ) )
|
||||||
|
{
|
||||||
|
DEBUG_ERROR( "Failed to reference directory by handle" );
|
||||||
|
ZwClose( handle );
|
||||||
|
return STATUS_ABANDONED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Windows organises its drivers in object directories (not the same as
|
||||||
|
* files directories). For the driver directory, there are 37 entries,
|
||||||
|
* each driver is hashed and indexed. If there is a driver with a duplicate
|
||||||
|
* index, it is inserted into same index in a linked list using the
|
||||||
|
* _OBJECT_DIRECTORY_ENTRY struct. So to enumerate all drivers we visit
|
||||||
|
* each entry in the hashmap, enumerate all objects in the linked list
|
||||||
|
* at entry j then we increment the hashmap index i. The motivation behind
|
||||||
|
* this is that when a driver is accessed, it is brought to the first index
|
||||||
|
* in the linked list, so drivers that are accessed the most can be
|
||||||
|
* accessed quickly
|
||||||
|
*/
|
||||||
|
|
||||||
|
POBJECT_DIRECTORY directory_object = ( POBJECT_DIRECTORY )directory;
|
||||||
|
|
||||||
|
ExAcquirePushLockExclusiveEx( &directory_object->Lock, NULL );
|
||||||
|
|
||||||
|
for ( INT i = 0; i < NUMBER_HASH_BUCKETS; i++ )
|
||||||
|
{
|
||||||
|
POBJECT_DIRECTORY_ENTRY entry = directory_object->HashBuckets[ i ];
|
||||||
|
|
||||||
|
if ( !entry )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
POBJECT_DIRECTORY_ENTRY sub_entry = entry;
|
||||||
|
|
||||||
|
while ( sub_entry )
|
||||||
|
{
|
||||||
|
PDRIVER_OBJECT current_driver = sub_entry->Object;
|
||||||
|
BOOLEAN flag;
|
||||||
|
|
||||||
|
if ( !NT_SUCCESS( ValidateDriverObjectHasBackingModule(
|
||||||
|
SystemModules,
|
||||||
|
current_driver,
|
||||||
|
&flag
|
||||||
|
) ) )
|
||||||
|
{
|
||||||
|
DEBUG_LOG( "Error validating driver object" );
|
||||||
|
ExReleasePushLockExclusiveEx( &directory_object->Lock, 0 );
|
||||||
|
ObDereferenceObject( directory );
|
||||||
|
ZwClose( handle );
|
||||||
|
return STATUS_ABANDONED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !flag )
|
||||||
|
{
|
||||||
|
InvalidDriverListHead->count += 1;
|
||||||
|
AddDriverToList( InvalidDriverListHead, current_driver );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub_entry = sub_entry->ChainLink;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ExReleasePushLockExclusiveEx( &directory_object->Lock, 0 );
|
||||||
|
ObDereferenceObject( directory );
|
||||||
|
ZwClose( handle );
|
||||||
|
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
NTSTATUS HandleValidateDriversIOCTL(
|
||||||
|
_In_ PIRP Irp
|
||||||
|
)
|
||||||
|
{
|
||||||
|
NTSTATUS status = STATUS_SUCCESS;
|
||||||
|
SYSTEM_MODULES system_modules = { 0 };
|
||||||
|
|
||||||
|
/* Fix annoying visual studio linting error */
|
||||||
|
RtlZeroMemory( &system_modules, sizeof( SYSTEM_MODULES ) );
|
||||||
|
|
||||||
|
status = GetSystemModuleInformation( &system_modules );
|
||||||
|
|
||||||
|
if ( !NT_SUCCESS( status ) )
|
||||||
|
{
|
||||||
|
DEBUG_ERROR( "Error retriving system module information" );
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
PINVALID_DRIVERS_HEAD head =
|
||||||
|
ExAllocatePool2( POOL_FLAG_NON_PAGED, sizeof( INVALID_DRIVERS_HEAD ), INVALID_DRIVER_LIST_HEAD_POOL );
|
||||||
|
|
||||||
|
if ( !head )
|
||||||
|
{
|
||||||
|
ExFreePoolWithTag( system_modules.address, SYSTEM_MODULES_POOL );
|
||||||
|
return STATUS_ABANDONED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use a linked list here so that so we have easy access to the invalid drivers
|
||||||
|
* which we can then use to copy the drivers logic for further analysis in
|
||||||
|
* identifying drivers specifically used for the purpose of cheating
|
||||||
|
*/
|
||||||
|
|
||||||
|
InitDriverList( head );
|
||||||
|
|
||||||
|
if ( !NT_SUCCESS( ValidateDriverObjects( &system_modules, head ) ) )
|
||||||
|
{
|
||||||
|
DEBUG_ERROR( "Failed to validate driver objects" );
|
||||||
|
ExFreePoolWithTag( system_modules.address, SYSTEM_MODULES_POOL );
|
||||||
|
return STATUS_ABANDONED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( head->count > 0 )
|
||||||
|
{
|
||||||
|
DEBUG_LOG( "found INVALID drivers with count: %i", head->count );
|
||||||
|
|
||||||
|
Irp->IoStatus.Information = sizeof( MODULE_VALIDATION_FAILURE_HEADER ) +
|
||||||
|
MODULE_VALIDATION_FAILURE_MAX_REPORT_COUNT * sizeof( MODULE_VALIDATION_FAILURE );
|
||||||
|
|
||||||
|
MODULE_VALIDATION_FAILURE_HEADER header;
|
||||||
|
header.module_count = head->count;
|
||||||
|
|
||||||
|
RtlCopyMemory(
|
||||||
|
Irp->AssociatedIrp.SystemBuffer,
|
||||||
|
&header,
|
||||||
|
sizeof( MODULE_VALIDATION_FAILURE_HEADER ) );
|
||||||
|
|
||||||
|
for ( INT i = 0; i < head->count; i++ )
|
||||||
|
{
|
||||||
|
/* make sure we free any non reported modules */
|
||||||
|
if ( i >= MODULE_VALIDATION_FAILURE_MAX_REPORT_COUNT )
|
||||||
|
RemoveInvalidDriverFromList( head );
|
||||||
|
|
||||||
|
MODULE_VALIDATION_FAILURE report;
|
||||||
|
report.report_code = REPORT_MODULE_VALIDATION_FAILURE;
|
||||||
|
report.driver_base_address = head->first_entry->driver->DriverStart;
|
||||||
|
report.driver_size = head->first_entry->driver->Size;
|
||||||
|
|
||||||
|
RtlCopyMemory(
|
||||||
|
&report.driver_name,
|
||||||
|
head->first_entry->driver->DriverName.Buffer,
|
||||||
|
MODULE_REPORT_DRIVER_NAME_BUFFER_SIZE );
|
||||||
|
|
||||||
|
RtlCopyMemory(
|
||||||
|
(UINT64)Irp->AssociatedIrp.SystemBuffer + sizeof( MODULE_VALIDATION_FAILURE_HEADER ) + i * sizeof( MODULE_VALIDATION_FAILURE ),
|
||||||
|
&report,
|
||||||
|
sizeof( MODULE_VALIDATION_FAILURE ));
|
||||||
|
|
||||||
|
RemoveInvalidDriverFromList( head );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DEBUG_LOG( "No INVALID drivers found :)" );
|
||||||
|
}
|
||||||
|
|
||||||
|
ExFreePoolWithTag( head, INVALID_DRIVER_LIST_HEAD_POOL );
|
||||||
|
ExFreePoolWithTag( system_modules.address, SYSTEM_MODULES_POOL );
|
||||||
|
return status;
|
||||||
|
}
|
58
driver/modules.h
Normal file
58
driver/modules.h
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
#ifndef MODULES_H
|
||||||
|
#define MODULES_H
|
||||||
|
|
||||||
|
#include <ntifs.h>
|
||||||
|
#include <intrin.h>
|
||||||
|
|
||||||
|
#define REPORT_MODULE_VALIDATION_FAILURE 60
|
||||||
|
#define MODULE_VALIDATION_FAILURE_MAX_REPORT_COUNT 5
|
||||||
|
|
||||||
|
#define MODULE_REPORT_DRIVER_NAME_BUFFER_SIZE 128
|
||||||
|
|
||||||
|
typedef struct _MODULE_VALIDATION_FAILURE_HEADER
|
||||||
|
{
|
||||||
|
INT module_count;
|
||||||
|
|
||||||
|
}MODULE_VALIDATION_FAILURE_HEADER, *PMODULE_VALIDATION_FAILURE_HEADER;
|
||||||
|
|
||||||
|
typedef struct _MODULE_VALIDATION_FAILURE
|
||||||
|
{
|
||||||
|
INT report_code;
|
||||||
|
UINT64 driver_base_address;
|
||||||
|
UINT64 driver_size;
|
||||||
|
PCHAR driver_name[ 128 ];
|
||||||
|
|
||||||
|
}MODULE_VALIDATION_FAILURE, *PMODULE_VALIDATION_FAILURE;
|
||||||
|
|
||||||
|
typedef struct _INVALID_DRIVER
|
||||||
|
{
|
||||||
|
struct _INVALID_DRIVER* next;
|
||||||
|
PDRIVER_OBJECT driver;
|
||||||
|
|
||||||
|
}INVALID_DRIVER, * PINVALID_DRIVER;
|
||||||
|
|
||||||
|
typedef struct _INVALID_DRIVERS_HEAD
|
||||||
|
{
|
||||||
|
PINVALID_DRIVER first_entry;
|
||||||
|
INT count; //keeps track of the number of drivers in the list
|
||||||
|
|
||||||
|
}INVALID_DRIVERS_HEAD, * PINVALID_DRIVERS_HEAD;
|
||||||
|
|
||||||
|
/* system modules information */
|
||||||
|
|
||||||
|
typedef struct _SYSTEM_MODULES
|
||||||
|
{
|
||||||
|
PVOID address;
|
||||||
|
INT module_count;
|
||||||
|
|
||||||
|
}SYSTEM_MODULES, * PSYSTEM_MODULES;
|
||||||
|
|
||||||
|
NTSTATUS GetSystemModuleInformation(
|
||||||
|
_Out_ PSYSTEM_MODULES ModuleInformation
|
||||||
|
);
|
||||||
|
|
||||||
|
NTSTATUS HandleValidateDriversIOCTL(
|
||||||
|
_In_ PIRP Irp
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif
|
297
driver/nmi.c
Normal file
297
driver/nmi.c
Normal file
|
@ -0,0 +1,297 @@
|
||||||
|
#include "nmi.h"
|
||||||
|
|
||||||
|
#include "modules.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
typedef struct _NMI_POOLS
|
||||||
|
{
|
||||||
|
PVOID thread_data_pool;
|
||||||
|
PVOID stack_frames;
|
||||||
|
PVOID nmi_context;
|
||||||
|
|
||||||
|
}NMI_POOLS, * PNMI_POOLS;
|
||||||
|
|
||||||
|
PVOID nmi_callback_handle = NULL;
|
||||||
|
|
||||||
|
/* Global structure to hold pointers to required memory for the NMI's */
|
||||||
|
NMI_POOLS nmi_pools = { 0 };
|
||||||
|
|
||||||
|
NTSTATUS IsInstructionPointerInInvalidRegion(
|
||||||
|
_In_ UINT64 RIP,
|
||||||
|
_In_ PSYSTEM_MODULES SystemModules,
|
||||||
|
_Out_ PBOOLEAN Result
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if ( !RIP || !SystemModules || !Result )
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
/* Note that this does not check for HAL or PatchGuard Execution */
|
||||||
|
for ( INT i = 0; i < SystemModules->module_count; i++ )
|
||||||
|
{
|
||||||
|
PRTL_MODULE_EXTENDED_INFO system_module = ( PRTL_MODULE_EXTENDED_INFO )(
|
||||||
|
( uintptr_t )SystemModules->address + i * sizeof( RTL_MODULE_EXTENDED_INFO ) );
|
||||||
|
|
||||||
|
UINT64 base = ( UINT64 )system_module->ImageBase;
|
||||||
|
UINT64 end = base + system_module->ImageSize;
|
||||||
|
|
||||||
|
if ( RIP >= base && RIP <= end )
|
||||||
|
{
|
||||||
|
*Result = TRUE;
|
||||||
|
return STATUS_SUCCESS;;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*Result = FALSE;
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
NTSTATUS AnalyseNmiData(
|
||||||
|
_In_ INT NumCores,
|
||||||
|
_In_ PSYSTEM_MODULES SystemModules,
|
||||||
|
_In_ PIRP Irp
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if ( !NumCores || !SystemModules )
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
for ( INT core = 0; core < NumCores; core++ )
|
||||||
|
{
|
||||||
|
PNMI_CONTEXT context = ( PNMI_CONTEXT )( ( uintptr_t )nmi_pools.nmi_context + core * sizeof( NMI_CONTEXT ) );
|
||||||
|
|
||||||
|
/* Make sure our NMIs were run */
|
||||||
|
if ( !context->nmi_callbacks_run )
|
||||||
|
{
|
||||||
|
NMI_CALLBACK_FAILURE report;
|
||||||
|
report.report_code = REPORT_NMI_CALLBACK_FAILURE;
|
||||||
|
report.kthread_address = NULL;
|
||||||
|
report.invalid_rip = NULL;
|
||||||
|
report.were_nmis_disabled = TRUE;
|
||||||
|
|
||||||
|
Irp->IoStatus.Information = sizeof( NMI_CALLBACK_FAILURE );
|
||||||
|
|
||||||
|
RtlCopyMemory(
|
||||||
|
Irp->AssociatedIrp.SystemBuffer,
|
||||||
|
&report,
|
||||||
|
sizeof( NMI_CALLBACK_FAILURE )
|
||||||
|
);
|
||||||
|
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
PNMI_CALLBACK_DATA thread_data = ( PNMI_CALLBACK_DATA )(
|
||||||
|
( uintptr_t )nmi_pools.thread_data_pool + core * sizeof( NMI_CALLBACK_DATA ) );
|
||||||
|
|
||||||
|
DEBUG_LOG( "cpu number: %i callback count: %i", core, context->nmi_callbacks_run );
|
||||||
|
|
||||||
|
/* Walk the stack */
|
||||||
|
for ( INT frame = 0; frame < thread_data->num_frames_captured; frame++ )
|
||||||
|
{
|
||||||
|
BOOLEAN flag;
|
||||||
|
DWORD64 stack_frame = *( DWORD64* )(
|
||||||
|
( ( uintptr_t )nmi_pools.stack_frames + thread_data->stack_frames_offset + frame * sizeof( PVOID ) ) );
|
||||||
|
|
||||||
|
if ( !NT_SUCCESS( IsInstructionPointerInInvalidRegion( stack_frame, SystemModules, &flag ) ) )
|
||||||
|
{
|
||||||
|
DEBUG_ERROR( "errro checking RIP for current stack address" );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( flag == FALSE )
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Note: for now, we only handle 1 report at a time so we stop the
|
||||||
|
* analysis once we receive a report since we only send a buffer
|
||||||
|
* large enough for 1 report. In the future this should be changed
|
||||||
|
* to a buffer that can hold atleast 4 reports (since the chance we
|
||||||
|
* get 4 reports with a single NMI would be impossible) so we can
|
||||||
|
* continue parsing the rest of the stack frames after receiving a
|
||||||
|
* single report.
|
||||||
|
*/
|
||||||
|
|
||||||
|
NMI_CALLBACK_FAILURE report;
|
||||||
|
report.report_code = REPORT_NMI_CALLBACK_FAILURE;
|
||||||
|
report.kthread_address = thread_data->kthread_address;
|
||||||
|
report.invalid_rip = stack_frame;
|
||||||
|
report.were_nmis_disabled = FALSE;
|
||||||
|
|
||||||
|
Irp->IoStatus.Information = sizeof( NMI_CALLBACK_FAILURE );
|
||||||
|
|
||||||
|
RtlCopyMemory(
|
||||||
|
Irp->AssociatedIrp.SystemBuffer,
|
||||||
|
&report,
|
||||||
|
sizeof( NMI_CALLBACK_FAILURE )
|
||||||
|
);
|
||||||
|
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOLEAN NmiCallback(
|
||||||
|
_In_ PVOID Context,
|
||||||
|
_In_ BOOLEAN Handled
|
||||||
|
)
|
||||||
|
{
|
||||||
|
UNREFERENCED_PARAMETER( Handled );
|
||||||
|
|
||||||
|
ULONG proc_num = KeGetCurrentProcessorNumber();
|
||||||
|
PVOID current_thread = KeGetCurrentThread();
|
||||||
|
NMI_CALLBACK_DATA thread_data = { 0 };
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cannot allocate pool in this function as it runs at IRQL >= dispatch level
|
||||||
|
* so ive just allocated a global pool with size equal to 0x200 * num_procs
|
||||||
|
*/
|
||||||
|
INT num_frames_captured = RtlCaptureStackBackTrace(
|
||||||
|
NULL,
|
||||||
|
STACK_FRAME_POOL_SIZE,
|
||||||
|
( uintptr_t )nmi_pools.stack_frames + proc_num * STACK_FRAME_POOL_SIZE,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function is run in the context of the interrupted thread hence we can
|
||||||
|
* gather any and all information regarding the thread that may be useful for analysis
|
||||||
|
*/
|
||||||
|
thread_data.kthread_address = ( UINT64 )current_thread;
|
||||||
|
thread_data.kprocess_address = ( UINT64 )PsGetCurrentProcess();
|
||||||
|
thread_data.stack_base = *( ( UINT64* )( ( uintptr_t )current_thread + KTHREAD_STACK_BASE_OFFSET ) );
|
||||||
|
thread_data.stack_limit = *( ( UINT64* )( ( uintptr_t )current_thread + KTHREAD_STACK_LIMIT_OFFSET ) );
|
||||||
|
thread_data.start_address = *( ( UINT64* )( ( uintptr_t )current_thread + KTHREAD_START_ADDRESS_OFFSET ) );
|
||||||
|
thread_data.cr3 = __readcr3();
|
||||||
|
thread_data.stack_frames_offset = proc_num * STACK_FRAME_POOL_SIZE;
|
||||||
|
thread_data.num_frames_captured = num_frames_captured;
|
||||||
|
|
||||||
|
RtlCopyMemory(
|
||||||
|
( ( uintptr_t )nmi_pools.thread_data_pool ) + proc_num * sizeof( thread_data ),
|
||||||
|
&thread_data,
|
||||||
|
sizeof( thread_data )
|
||||||
|
);
|
||||||
|
|
||||||
|
PNMI_CONTEXT context = ( PNMI_CONTEXT )( ( uintptr_t )Context + proc_num * sizeof( NMI_CONTEXT ) );
|
||||||
|
context->nmi_callbacks_run += 1;
|
||||||
|
DEBUG_LOG( "num nmis called: %i from addr: %llx", context->nmi_callbacks_run, ( uintptr_t )context );
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
NTSTATUS LaunchNonMaskableInterrupt(
|
||||||
|
_In_ ULONG NumCores
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if ( !NumCores )
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
PKAFFINITY_EX ProcAffinityPool = ExAllocatePool2( POOL_FLAG_NON_PAGED, sizeof( KAFFINITY_EX ), PROC_AFFINITY_POOL );
|
||||||
|
|
||||||
|
if ( !ProcAffinityPool )
|
||||||
|
return STATUS_ABANDONED;
|
||||||
|
|
||||||
|
nmi_pools.stack_frames = ExAllocatePool2( POOL_FLAG_NON_PAGED, NumCores * STACK_FRAME_POOL_SIZE, STACK_FRAMES_POOL );
|
||||||
|
|
||||||
|
if ( !nmi_pools.stack_frames )
|
||||||
|
{
|
||||||
|
ExFreePoolWithTag( ProcAffinityPool, PROC_AFFINITY_POOL );
|
||||||
|
return STATUS_ABANDONED;
|
||||||
|
}
|
||||||
|
|
||||||
|
nmi_pools.thread_data_pool = ExAllocatePool2( POOL_FLAG_NON_PAGED, NumCores * sizeof( NMI_CALLBACK_DATA ), THREAD_DATA_POOL );
|
||||||
|
|
||||||
|
if ( !nmi_pools.thread_data_pool )
|
||||||
|
{
|
||||||
|
ExFreePoolWithTag( nmi_pools.stack_frames, STACK_FRAMES_POOL );
|
||||||
|
ExFreePoolWithTag( ProcAffinityPool, PROC_AFFINITY_POOL );
|
||||||
|
return STATUS_ABANDONED;
|
||||||
|
}
|
||||||
|
|
||||||
|
LARGE_INTEGER delay = { 0 };
|
||||||
|
delay.QuadPart -= 100 * 10000;
|
||||||
|
|
||||||
|
for ( ULONG core = 0; core < NumCores; core++ )
|
||||||
|
{
|
||||||
|
KeInitializeAffinityEx( ProcAffinityPool );
|
||||||
|
KeAddProcessorAffinityEx( ProcAffinityPool, core );
|
||||||
|
|
||||||
|
DEBUG_LOG( "Sending NMI" );
|
||||||
|
HalSendNMI( ProcAffinityPool );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only a single NMI can be active at any given time, so arbitrarily
|
||||||
|
* delay execution to allow time for the NMI to be processed
|
||||||
|
*/
|
||||||
|
KeDelayExecutionThread( KernelMode, FALSE, &delay );
|
||||||
|
}
|
||||||
|
|
||||||
|
ExFreePoolWithTag( ProcAffinityPool, PROC_AFFINITY_POOL );
|
||||||
|
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
NTSTATUS HandleNmiIOCTL(
|
||||||
|
_In_ PIRP Irp
|
||||||
|
)
|
||||||
|
{
|
||||||
|
NTSTATUS status = STATUS_SUCCESS;
|
||||||
|
SYSTEM_MODULES system_modules = { 0 };
|
||||||
|
ULONG num_cores = KeQueryActiveProcessorCountEx( 0 );
|
||||||
|
|
||||||
|
/* Fix annoying visual studio linting error */
|
||||||
|
RtlZeroMemory( &system_modules, sizeof( SYSTEM_MODULES ) );
|
||||||
|
RtlZeroMemory( &nmi_pools, sizeof( NMI_POOLS ) );
|
||||||
|
|
||||||
|
nmi_pools.nmi_context = ExAllocatePool2( POOL_FLAG_NON_PAGED, num_cores * sizeof( NMI_CONTEXT ), NMI_CONTEXT_POOL );
|
||||||
|
|
||||||
|
if ( !nmi_pools.nmi_context )
|
||||||
|
{
|
||||||
|
DEBUG_ERROR( "nmi_context ExAllocatePool2 failed" );
|
||||||
|
return STATUS_ABANDONED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We want to register and unregister our callback each time so it becomes harder
|
||||||
|
* for people to hook our callback and get up to some funny business
|
||||||
|
*/
|
||||||
|
nmi_callback_handle = KeRegisterNmiCallback( NmiCallback, nmi_pools.nmi_context );
|
||||||
|
|
||||||
|
if ( !nmi_callback_handle )
|
||||||
|
{
|
||||||
|
DEBUG_ERROR( "KeRegisterNmiCallback failed" );
|
||||||
|
ExFreePoolWithTag( nmi_pools.nmi_context, NMI_CONTEXT_POOL );
|
||||||
|
return STATUS_ABANDONED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We query the system modules each time since they can potentially
|
||||||
|
* change at any time
|
||||||
|
*/
|
||||||
|
status = GetSystemModuleInformation( &system_modules );
|
||||||
|
|
||||||
|
if ( !NT_SUCCESS( status ) )
|
||||||
|
{
|
||||||
|
DEBUG_ERROR( "Error retriving system module information" );
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
status = LaunchNonMaskableInterrupt( num_cores );
|
||||||
|
|
||||||
|
if ( !NT_SUCCESS( status ) )
|
||||||
|
{
|
||||||
|
DEBUG_ERROR( "Error running NMI callbacks" );
|
||||||
|
ExFreePoolWithTag( system_modules.address, SYSTEM_MODULES_POOL );
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
status = AnalyseNmiData( num_cores, &system_modules, Irp );
|
||||||
|
|
||||||
|
if ( !NT_SUCCESS( status ) )
|
||||||
|
DEBUG_ERROR( "Error analysing nmi data" );
|
||||||
|
|
||||||
|
ExFreePoolWithTag( system_modules.address, SYSTEM_MODULES_POOL );
|
||||||
|
ExFreePoolWithTag( nmi_pools.stack_frames, STACK_FRAMES_POOL );
|
||||||
|
ExFreePoolWithTag( nmi_pools.thread_data_pool, THREAD_DATA_POOL );
|
||||||
|
ExFreePoolWithTag( nmi_pools.nmi_context, NMI_CONTEXT_POOL );
|
||||||
|
KeDeregisterNmiCallback( nmi_callback_handle );
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
41
driver/nmi.h
Normal file
41
driver/nmi.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#ifndef NMI_H
|
||||||
|
#define NMI_H
|
||||||
|
|
||||||
|
#include <ntifs.h>
|
||||||
|
#include <intrin.h>
|
||||||
|
|
||||||
|
#define REPORT_NMI_CALLBACK_FAILURE 50
|
||||||
|
|
||||||
|
NTSTATUS HandleNmiIOCTL(
|
||||||
|
_In_ PIRP Irp
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef struct NMI_CALLBACK_FAILURE
|
||||||
|
{
|
||||||
|
INT report_code;
|
||||||
|
INT were_nmis_disabled;
|
||||||
|
UINT64 kthread_address;
|
||||||
|
UINT64 invalid_rip;
|
||||||
|
|
||||||
|
}NMI_CALLBACK_FAILURE, *PNMI_CALLBACK_FAILURE;
|
||||||
|
|
||||||
|
typedef struct _NMI_CONTEXT
|
||||||
|
{
|
||||||
|
INT nmi_callbacks_run;
|
||||||
|
|
||||||
|
}NMI_CONTEXT, * PNMI_CONTEXT;
|
||||||
|
|
||||||
|
typedef struct _NMI_CALLBACK_DATA
|
||||||
|
{
|
||||||
|
UINT64 kthread_address;
|
||||||
|
UINT64 kprocess_address;
|
||||||
|
UINT64 start_address;
|
||||||
|
UINT64 stack_limit;
|
||||||
|
UINT64 stack_base;
|
||||||
|
uintptr_t stack_frames_offset;
|
||||||
|
INT num_frames_captured;
|
||||||
|
UINT64 cr3;
|
||||||
|
|
||||||
|
}NMI_CALLBACK_DATA, * PNMI_CALLBACK_DATA;
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,7 +1,155 @@
|
||||||
#include "driver.h"
|
#include "driver.h"
|
||||||
|
|
||||||
|
#include "../common.h"
|
||||||
|
|
||||||
kernelmode::Driver::Driver(LPCWSTR DriverName, std::shared_ptr<global::Report> ReportInterface )
|
kernelmode::Driver::Driver(LPCWSTR DriverName, std::shared_ptr<global::Report> ReportInterface )
|
||||||
{
|
{
|
||||||
this->driver_name = DriverName;
|
this->driver_name = DriverName;
|
||||||
this->report_interface = ReportInterface;
|
this->report_interface = ReportInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kernelmode::Driver::RunNmiCallbacks()
|
||||||
|
{
|
||||||
|
BOOLEAN status;
|
||||||
|
DWORD bytes_returned;
|
||||||
|
global::report_structures::NMI_CALLBACK_FAILURE report;
|
||||||
|
|
||||||
|
status = DeviceIoControl(
|
||||||
|
this->driver_handle,
|
||||||
|
IOCCTL_RUN_NMI_CALLBACKS,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&report,
|
||||||
|
sizeof( global::report_structures::NMI_CALLBACK_FAILURE ),
|
||||||
|
&bytes_returned,
|
||||||
|
( LPOVERLAPPED )NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( status == NULL )
|
||||||
|
{
|
||||||
|
LOG_ERROR( "DeviceIoControl failed with status code 0x%x", GetLastError() );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( bytes_returned == NULL )
|
||||||
|
{
|
||||||
|
LOG_INFO( "All threads valid, nmis fine." );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* else, report */
|
||||||
|
this->report_interface->ReportViolation( &report );
|
||||||
|
}
|
||||||
|
|
||||||
|
void kernelmode::Driver::VerifySystemModules()
|
||||||
|
{
|
||||||
|
BOOLEAN status;
|
||||||
|
DWORD bytes_returned;
|
||||||
|
PVOID buffer;
|
||||||
|
SIZE_T buffer_size;
|
||||||
|
SIZE_T header_size;
|
||||||
|
global::report_structures::MODULE_VALIDATION_FAILURE_HEADER header;
|
||||||
|
global::report_structures::MODULE_VALIDATION_FAILURE report;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* allocate enough to report 5 invalid driver objects + header. The reason we use a raw
|
||||||
|
* pointer here is so we can pass the address to DeviceIoControl. You are not able (atleast
|
||||||
|
* as far as im concerned) to pass a shared ptr to DeviceIoControl.
|
||||||
|
*/
|
||||||
|
header_size = sizeof( global::report_structures::MODULE_VALIDATION_FAILURE_HEADER );
|
||||||
|
|
||||||
|
buffer_size = sizeof( global::report_structures::MODULE_VALIDATION_FAILURE ) *
|
||||||
|
MODULE_VALIDATION_FAILURE_MAX_REPORT_COUNT +
|
||||||
|
header_size;
|
||||||
|
|
||||||
|
buffer = malloc( buffer_size );
|
||||||
|
|
||||||
|
status = DeviceIoControl(
|
||||||
|
this->driver_handle,
|
||||||
|
IOCTL_VALIDATE_DRIVER_OBJECTS,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
buffer,
|
||||||
|
buffer_size,
|
||||||
|
&bytes_returned,
|
||||||
|
( LPOVERLAPPED )NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( status == NULL )
|
||||||
|
{
|
||||||
|
LOG_ERROR( "DeviceIoControl failed with status code 0x%x", GetLastError() );
|
||||||
|
free( buffer );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( bytes_returned == NULL )
|
||||||
|
{
|
||||||
|
LOG_INFO( "All threads valid, nmis fine." );
|
||||||
|
free( buffer );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy( &header, buffer, sizeof( header_size ));
|
||||||
|
|
||||||
|
if ( header.module_count == NULL )
|
||||||
|
{
|
||||||
|
LOG_ERROR( "weird error with module report" );
|
||||||
|
free( buffer );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We are splitting up each packet here and passing them on one by one since
|
||||||
|
* if I am being honest it is just easier in c++ and that way the process
|
||||||
|
* is streamlined just like all other report packets.
|
||||||
|
*/
|
||||||
|
UINT64 base = ( UINT64 )buffer + sizeof( header_size );
|
||||||
|
|
||||||
|
for ( int i = 0; i < header.module_count; i++ )
|
||||||
|
{
|
||||||
|
memcpy(
|
||||||
|
&report,
|
||||||
|
PVOID( base + i * sizeof( global::report_structures::MODULE_VALIDATION_FAILURE ) ),
|
||||||
|
sizeof( global::report_structures::MODULE_VALIDATION_FAILURE )
|
||||||
|
);
|
||||||
|
|
||||||
|
this->report_interface->ReportViolation( &report );
|
||||||
|
|
||||||
|
/* sanity clear just in case ;) */
|
||||||
|
RtlZeroMemory( &report, sizeof( global::report_structures::MODULE_VALIDATION_FAILURE ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
free( buffer );
|
||||||
|
}
|
||||||
|
|
||||||
|
void kernelmode::Driver::EnableObRegisterCallbacks()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void kernelmode::Driver::DisableObRegisterCallbacks()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void kernelmode::Driver::EnableProcessLoadNotifyCallbacks()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void kernelmode::Driver::DisableProcessLoadNotifyCallbacks()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void kernelmode::Driver::ValidateKPRCBThreads()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void kernelmode::Driver::CheckForHypervisor()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void kernelmode::Driver::VerifySystemModulesIOCTLDispatchHandler()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void kernelmode::Driver::CheckDriverHeartbeat()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
#include "../threadpool.h"
|
#include "../threadpool.h"
|
||||||
#include "../report.h"
|
#include "../report.h"
|
||||||
|
|
||||||
|
#define IOCCTL_RUN_NMI_CALLBACKS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2001, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
#define IOCTL_VALIDATE_DRIVER_OBJECTS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x2002, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
|
|
||||||
namespace kernelmode
|
namespace kernelmode
|
||||||
{
|
{
|
||||||
class Driver
|
class Driver
|
||||||
|
@ -15,9 +18,19 @@ namespace kernelmode
|
||||||
std::shared_ptr<global::Report> report_interface;
|
std::shared_ptr<global::Report> report_interface;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
std::shared_ptr<global::ThreadPool> thread_pool;
|
|
||||||
|
|
||||||
Driver(LPCWSTR DriverName, std::shared_ptr<global::Report> ReportInterface );
|
Driver(LPCWSTR DriverName, std::shared_ptr<global::Report> ReportInterface );
|
||||||
|
|
||||||
|
void RunNmiCallbacks();
|
||||||
|
void VerifySystemModules();
|
||||||
|
void EnableObRegisterCallbacks();
|
||||||
|
void DisableObRegisterCallbacks();
|
||||||
|
void EnableProcessLoadNotifyCallbacks();
|
||||||
|
void DisableProcessLoadNotifyCallbacks();
|
||||||
|
void ValidateKPRCBThreads();
|
||||||
|
void CheckForHypervisor();
|
||||||
|
void VerifySystemModulesIOCTLDispatchHandler();
|
||||||
|
void CheckDriverHeartbeat();
|
||||||
|
/* todo: driver integrity check */
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,3 +5,13 @@ kernelmode::KManager::KManager( LPCWSTR DriverName, std::shared_ptr<global::Thre
|
||||||
this->driver_interface = std::make_unique<Driver>(DriverName, ReportInterface);
|
this->driver_interface = std::make_unique<Driver>(DriverName, ReportInterface);
|
||||||
this->thread_pool = ThreadPool;
|
this->thread_pool = ThreadPool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kernelmode::KManager::RunNmiCallbacks()
|
||||||
|
{
|
||||||
|
this->thread_pool->QueueJob( [ this ]() {this->RunNmiCallbacks(); } );
|
||||||
|
}
|
||||||
|
|
||||||
|
void kernelmode::KManager::VerifySystemModules()
|
||||||
|
{
|
||||||
|
this->thread_pool->QueueJob( [ this ]() {this->VerifySystemModules(); } );
|
||||||
|
}
|
||||||
|
|
|
@ -16,6 +16,9 @@ namespace kernelmode
|
||||||
std::shared_ptr<global::ThreadPool> thread_pool;
|
std::shared_ptr<global::ThreadPool> thread_pool;
|
||||||
public:
|
public:
|
||||||
KManager( LPCWSTR DriverName, std::shared_ptr<global::ThreadPool> ThreadPool, std::shared_ptr<global::Report> ReportInterface);
|
KManager( LPCWSTR DriverName, std::shared_ptr<global::ThreadPool> ThreadPool, std::shared_ptr<global::Report> ReportInterface);
|
||||||
|
|
||||||
|
void RunNmiCallbacks();
|
||||||
|
void VerifySystemModules();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,9 +25,13 @@ DWORD WINAPI Init(HINSTANCE hinstDLL)
|
||||||
std::shared_ptr<global::Report> report_interface = std::make_shared<global::Report>( thread_pool, pipe_name );
|
std::shared_ptr<global::Report> report_interface = std::make_shared<global::Report>( thread_pool, pipe_name );
|
||||||
|
|
||||||
usermode::UManager umanager( thread_pool, report_interface );
|
usermode::UManager umanager( thread_pool, report_interface );
|
||||||
//kernelmode::KManager kmanager( L"DonnaAC", thread_pool);
|
kernelmode::KManager kmanager( L"DonnaAC", thread_pool, report_interface);
|
||||||
umanager.ValidateProcessModules();
|
|
||||||
umanager.ValidateProcessMemory();
|
kmanager.RunNmiCallbacks();
|
||||||
|
kmanager.VerifySystemModules();
|
||||||
|
|
||||||
|
//umanager.ValidateProcessModules();
|
||||||
|
//umanager.ValidateProcessMemory();
|
||||||
|
|
||||||
while ( !GetAsyncKeyState( VK_DELETE ) )
|
while ( !GetAsyncKeyState( VK_DELETE ) )
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,11 +9,14 @@
|
||||||
|
|
||||||
#define REPORT_BUFFER_SIZE 1024
|
#define REPORT_BUFFER_SIZE 1024
|
||||||
#define MAX_SIGNATURE_SIZE 256
|
#define MAX_SIGNATURE_SIZE 256
|
||||||
|
#define MODULE_VALIDATION_FAILURE_MAX_REPORT_COUNT 5
|
||||||
|
|
||||||
#define REPORT_CODE_MODULE_VERIFICATION 10
|
#define REPORT_CODE_MODULE_VERIFICATION 10
|
||||||
#define REPORT_CODE_START_ADDRESS_VERIFICATION 20
|
#define REPORT_CODE_START_ADDRESS_VERIFICATION 20
|
||||||
#define REPORT_PAGE_PROTECTION_VERIFICATION 30
|
#define REPORT_PAGE_PROTECTION_VERIFICATION 30
|
||||||
#define REPORT_PATTERN_SCAN_FAILURE 40
|
#define REPORT_PATTERN_SCAN_FAILURE 40
|
||||||
|
#define REPORT_NMI_CALLBACK_FAILURE 50
|
||||||
|
#define REPORT_MODULE_VALIDATION_FAILURE 60
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -80,6 +83,27 @@ namespace global
|
||||||
INT signature_id;
|
INT signature_id;
|
||||||
UINT64 address;
|
UINT64 address;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct NMI_CALLBACK_FAILURE
|
||||||
|
{
|
||||||
|
INT report_code;
|
||||||
|
INT were_nmis_disabled;
|
||||||
|
UINT64 kthread_address;
|
||||||
|
UINT64 invalid_rip;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MODULE_VALIDATION_FAILURE_HEADER
|
||||||
|
{
|
||||||
|
INT module_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MODULE_VALIDATION_FAILURE
|
||||||
|
{
|
||||||
|
INT report_code;
|
||||||
|
UINT64 driver_base_address;
|
||||||
|
UINT64 driver_size;
|
||||||
|
BYTE driver_name[ 128 ];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,14 +15,14 @@ usermode::UManager::UManager( std::shared_ptr<global::ThreadPool> ThreadPool, st
|
||||||
usermode::UManager::~UManager()
|
usermode::UManager::~UManager()
|
||||||
{
|
{
|
||||||
/* Wait for our jobs to be finished, then safely stop our pool */
|
/* Wait for our jobs to be finished, then safely stop our pool */
|
||||||
while ( true )
|
//while ( true )
|
||||||
{
|
//{
|
||||||
if ( this->thread_pool->Busy() == FALSE )
|
// if ( this->thread_pool->Busy() == FALSE )
|
||||||
{
|
// {
|
||||||
this->thread_pool->Stop();
|
// this->thread_pool->Stop();
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
void usermode::UManager::ValidateProcessThreads()
|
void usermode::UManager::ValidateProcessThreads()
|
||||||
|
|
Loading…
Reference in a new issue