integrate some new stuff [wip] (#9)

This commit is contained in:
donnaskiez 2024-05-05 01:43:01 +10:00 committed by GitHub
parent 23444671ea
commit 07904949a6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 1176 additions and 268 deletions

View file

@ -23,20 +23,15 @@ open source anti cheat (lol) which I made for fun.
- HalDispatch and HalPrivateDispatch routine validation
- Dynamic import resolving & encryption
- Malicious PCI device detection via configuration space scanning
- Win32kBase_DxgInterface routine validation
# architecuture
For an overview of the architecture, see architecture.md.
# planned features
- Heartbeat
- ntoskrnl integrity checks
- cr3 protection
- string, packet and other encryption
- tpm ek extraction
- tpm spoofer detection
- pcileech firmware detection
- testing program to test the features
- simple user mode logger + usermode logging overhaul
- data ptr detction (+ chained data ptr walking)
- lots more which I cant think of
Theres a long list of features I still want to implement, the question is whether I can be bothored implementing them. I would say I'd accept pull requests for new features but I would expect high quality code and thorough testing with verifier (both inside a vm and bare metal).
# example

View file

@ -49,6 +49,9 @@
#define MAX_MODULE_PATH 256
#define CONVERT_RELATIVE_ADDRESS(Cast, Base, Rel) \
((Cast)((DWORD_PTR)(Base) + (DWORD_PTR)(Rel)))
/*
* Interlocked intrinsics are only atomic with respect to other InterlockedXxx
* functions, so all reads and writes to the THREAD_LIST->active flag must be
@ -208,6 +211,24 @@ typedef struct _IRP_QUEUE_ENTRY {
#define AES_128_KEY_SIZE 16
typedef struct _HEARTBEAT_CONFIGURATION {
volatile UINT32 counter;
/* Signifies if a heartbeat callback routine is currently executing. */
volatile UINT32 active;
LARGE_INTEGER seed;
/*
* We actually want the timer and DPC objects to be allocated, so that each
* time our heartbeat callback routine is run, we can remove the timer and
* add a new timer. This makes it harder to identify our heartbeat timers.
*/
PKTIMER timer;
PKDPC dpc;
PIO_WORKITEM work_item;
} HEARTBEAT_CONFIGURATION, *PHEARTBEAT_CONFIGURATION;
typedef struct _ACTIVE_SESSION {
BOOLEAN is_session_active;
PVOID um_handle;
@ -219,12 +240,13 @@ typedef struct _ACTIVE_SESSION {
CHAR session_aes_key[AES_128_KEY_SIZE];
struct SESSION_STATISTICS {
UINT32 irps_processed;
UINT32 irps_received;
UINT32 report_count;
UINT32 heartbeat_count;
};
KGUARDED_MUTEX lock;
HEARTBEAT_CONFIGURATION heartbeat_config;
KGUARDED_MUTEX lock;
} ACTIVE_SESSION, *PACTIVE_SESSION;
@ -235,6 +257,7 @@ typedef struct _ACTIVE_SESSION {
#define POOL_TAG_APC 'apcc'
#define POOL_TAG_HW 'hwhw'
#define POOL_TAG_DPC 'apcc'
#define POOL_TAG_HEARTBEAT 'teab'
#define SYSTEM_MODULES_POOL 'halb'
#define THREAD_DATA_POOL 'doof'
#define PROC_AFFINITY_POOL 'eeee'
@ -1635,4 +1658,10 @@ PsGetNextProcessThread(IN PEPROCESS Process, IN PETHREAD Thread OPTIONAL);
#define PROCESS_VM_READ 0x0010
#define PROCESS_VM_WRITE 0x0020
typedef struct _NT_HEADER_64 {
UINT32 Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} NT_HEADER_64, *PNT_HEADER_64;
#endif

View file

@ -91,8 +91,11 @@ typedef struct _DRIVER_CONFIG {
KGUARDED_MUTEX lock;
SYS_MODULE_VAL_CONTEXT sys_val_context;
IRP_QUEUE_HEAD irp_queue;
/* terrible name..lol what is tis timer for ?? */
TIMER_OBJECT timer;
ACTIVE_SESSION active_session;
ACTIVE_SESSION session_information;
THREAD_LIST_HEAD thread_list;
DRIVER_LIST_HEAD driver_list;
PROCESS_LIST_HEAD process_list;
@ -174,7 +177,7 @@ IsDriverUnloading()
PACTIVE_SESSION
GetActiveSession()
{
return &g_DriverConfig->active_session;
return &g_DriverConfig->session_information;
}
LPCSTR

View file

@ -255,10 +255,12 @@
<ClCompile Include="list.c" />
<ClCompile Include="modules.c" />
<ClCompile Include="hw.c" />
<ClCompile Include="pe.c" />
<ClCompile Include="pool.c" />
<ClCompile Include="queue.c" />
<ClCompile Include="session.c" />
<ClCompile Include="thread.c" />
<ClCompile Include="util.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="apc.h" />
@ -274,11 +276,13 @@
<ClInclude Include="io.h" />
<ClInclude Include="list.h" />
<ClInclude Include="modules.h" />
<ClInclude Include="pe.h" />
<ClInclude Include="pool.h" />
<ClInclude Include="queue.h" />
<ClInclude Include="session.h" />
<ClInclude Include="thread.h" />
<ClInclude Include="types\types.h" />
<ClInclude Include="util.h" />
</ItemGroup>
<ItemGroup>
<MASM Include="arch.asm" />

View file

@ -69,6 +69,12 @@
<ClCompile Include="hw.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pe.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="util.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="driver.h">
@ -125,6 +131,12 @@
<ClInclude Include="hw.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="pe.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="util.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<MASM Include="arch.asm">

View file

@ -5,31 +5,6 @@
#include "crypt.h"
#include <stdarg.h>
PVOID
FindDriverBaseNoApi(_In_ PDRIVER_OBJECT DriverObject, _In_ PWCH Name)
{
PKLDR_DATA_TABLE_ENTRY first =
(PKLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection;
/* first entry contains invalid data, 2nd entry is the kernel */
PKLDR_DATA_TABLE_ENTRY entry =
((PKLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection)
->InLoadOrderLinks.Flink->Flink;
while (entry->InLoadOrderLinks.Flink != first) {
/* todo: write our own unicode string comparison function, since
* the entire point of this is to find exports with no exports.
*/
if (!wcscmp(entry->BaseDllName.Buffer, Name)) {
return entry->DllBase;
}
entry = entry->InLoadOrderLinks.Flink;
}
return NULL;
}
PVOID
ImpResolveNtImport(PDRIVER_OBJECT DriverObject, PCZPSTR ExportName)
{

View file

@ -7,6 +7,7 @@
#include "io.h"
#include "imports.h"
#include "session.h"
#include "util.h"
#include <bcrypt.h>
#include <initguid.h>
@ -41,29 +42,25 @@ GetModuleInformationByName(_Out_ PRTL_MODULE_EXTENDED_INFO ModuleInfo,
STATIC
NTSTATUS
StoreModuleExecutableRegionsInBuffer(_Outptr_result_bytebuffer_(*BytesWritten)
PVOID* Buffer,
_In_ PVOID ModuleBase,
_In_ SIZE_T ModuleSize,
_Out_ _Deref_out_range_(>, 0)
PSIZE_T BytesWritten,
_In_ BOOLEAN IsModulex86);
StoreModuleExecutableRegionsInBuffer(_Out_ PVOID* Buffer,
_In_ PVOID ModuleBase,
_In_ SIZE_T ModuleSize,
_Out_ PSIZE_T BytesWritten,
_In_ BOOLEAN IsModulex86);
STATIC
NTSTATUS
MapDiskImageIntoVirtualAddressSpace(_Inout_ PHANDLE SectionHandle,
_Outptr_result_bytebuffer_(*Size)
PVOID* Section,
MapDiskImageIntoVirtualAddressSpace(_Inout_ PHANDLE SectionHandle,
_Out_ PVOID* Section,
_In_ PUNICODE_STRING Path,
_Out_ _Deref_out_range_(>, 0) PSIZE_T Size);
_Out_ PSIZE_T Size);
STATIC
NTSTATUS
ComputeHashOfBuffer(_In_ PVOID Buffer,
_In_ ULONG BufferSize,
_Outptr_result_bytebuffer_(*HashResultSize)
PVOID* HashResult,
_Out_ _Deref_out_range_(>, 0) PULONG HashResultSize);
ComputeHashOfBuffer(_In_ PVOID Buffer,
_In_ ULONG BufferSize,
_Out_ PVOID* HashResult,
_Out_ PULONG HashResultSize);
STATIC
VOID
@ -81,6 +78,13 @@ NTSTATUS
GetAverageReadTimeAtRoutine(_In_ PVOID RoutineAddress,
_Out_ PUINT64 AverageTime);
STATIC
VOID
HeartbeatDpcRoutine(_In_ PKDPC Dpc,
_In_opt_ PVOID DeferredContext,
_In_opt_ PVOID SystemArgument1,
_In_opt_ PVOID SystemArgument2);
#ifdef ALLOC_PRAGMA
# pragma alloc_text(PAGE, GetDriverImageSize)
# pragma alloc_text(PAGE, GetModuleInformationByName)
@ -193,15 +197,21 @@ GetModuleInformationByName(_Out_ PRTL_MODULE_EXTENDED_INFO ModuleInfo,
#define PE_TYPE_32_BIT 0x10b
FORCEINLINE
STATIC
BOOLEAN
IsSectionExecutable(_In_ PIMAGE_SECTION_HEADER Section)
{
return Section->Characteristics & IMAGE_SCN_MEM_EXECUTE ? TRUE : FALSE;
}
STATIC
NTSTATUS
StoreModuleExecutableRegionsInBuffer(_Outptr_result_bytebuffer_(*BytesWritten)
PVOID* Buffer,
_In_ PVOID ModuleBase,
_In_ SIZE_T ModuleSize,
_Out_ _Deref_out_range_(>, 0)
PSIZE_T BytesWritten,
_In_ BOOLEAN IsModulex86)
StoreModuleExecutableRegionsInBuffer(_Out_ PVOID* Buffer,
_In_ PVOID ModuleBase,
_In_ SIZE_T ModuleSize,
_Out_ PSIZE_T BytesWritten,
_In_ BOOLEAN IsModulex86)
{
PAGED_CODE();
@ -267,7 +277,7 @@ StoreModuleExecutableRegionsInBuffer(_Outptr_result_bytebuffer_(*BytesWritten)
buffer_base = (UINT64)*Buffer + sizeof(INTEGRITY_CHECK_HEADER);
for (ULONG index = 0; index < num_sections - 1; index++) {
if (!(section->Characteristics & IMAGE_SCN_MEM_EXECUTE)) {
if (!IsSectionExecutable(section)) {
section++;
continue;
}
@ -305,7 +315,7 @@ StoreModuleExecutableRegionsInBuffer(_Outptr_result_bytebuffer_(*BytesWritten)
total_packet_size +=
section->SizeOfRawData + sizeof(IMAGE_SECTION_HEADER);
num_executable_sections += 1;
num_executable_sections++;
section++;
}
@ -408,11 +418,10 @@ MapDiskImageIntoVirtualAddressSpace(_Inout_ PHANDLE SectionHandle,
STATIC
NTSTATUS
ComputeHashOfBuffer(_In_ PVOID Buffer,
_In_ ULONG BufferSize,
_Outptr_result_bytebuffer_(*HashResultSize)
PVOID* HashResult,
_Out_ _Deref_out_range_(>, 0) PULONG HashResultSize)
ComputeHashOfBuffer(_In_ PVOID Buffer,
_In_ ULONG BufferSize,
_Out_ PVOID* HashResult,
_Out_ PULONG HashResultSize)
{
PAGED_CODE();
@ -693,13 +702,21 @@ GetStringAtIndexFromSMBIOSTable(_In_ PSMBIOS_TABLE_HEADER Table,
return STATUS_NOT_FOUND;
}
/* for generic intel */
// #define SMBIOS_SYSTEM_INFORMATION_TYPE_2_TABLE 2
// #define MOTHERBOARD_SERIAL_CODE_TABLE_INDEX 4
FORCEINLINE
STATIC
PRAW_SMBIOS_DATA
GetRawSmbiosData(_In_ PVOID FirmwareTable)
{
return (PRAW_SMBIOS_DATA)FirmwareTable;
}
/* for testing purposes in vmware */
// #define VMWARE_SMBIOS_TABLE 1
// #define VMWARE_SMBIOS_TABLE_INDEX 3
FORCEINLINE
STATIC
PSMBIOS_TABLE_HEADER
GetSmbiosTableHeader(_In_ PRAW_SMBIOS_DATA Data)
{
return (PSMBIOS_TABLE_HEADER)(&Data->SMBIOSTableData[0]);
}
NTSTATUS
ParseSMBIOSTable(_Out_ PVOID Buffer,
@ -750,9 +767,8 @@ ParseSMBIOSTable(_Out_ PVOID Buffer,
goto end;
}
smbios_data = (PRAW_SMBIOS_DATA)firmware_table_buffer;
smbios_table_header =
(PSMBIOS_TABLE_HEADER)(&smbios_data->SMBIOSTableData[0]);
smbios_data = GetRawSmbiosData(firmware_table_buffer);
smbios_table_header = GetSmbiosTableHeader(smbios_data);
/*
* The System Information table is equal to Type == 2 and contains the
@ -824,14 +840,12 @@ ComputeHashOfSections(_In_ PIMAGE_SECTION_HEADER DiskSection,
return status;
}
FORCEINLINE
STATIC
BOOLEAN
CompareHashes(_In_ PVOID Hash1, _In_ PVOID Hash2, _In_ UINT32 Length)
{
if (RtlCompareMemory(Hash1, Hash2, Length) == Length)
return TRUE;
else
return FALSE;
return RtlCompareMemory(Hash1, Hash2, Length) == Length ? TRUE : FALSE;
}
typedef struct _VAL_INTEGRITY_HEADER {
@ -994,6 +1008,33 @@ end:
return status;
}
FORCEINLINE
STATIC
PCHAR
GetStorageDescriptorSerialNumber(_In_ PSTORAGE_DEVICE_DESCRIPTOR Descriptor)
{
return (PCHAR)((UINT64)Descriptor + Descriptor->SerialNumberOffset);
}
FORCEINLINE
STATIC
SIZE_T
GetStorageDescriptorSerialLength(_In_ PCHAR SerialNumber)
{
return strnlen_s(SerialNumber, DEVICE_DRIVE_0_SERIAL_CODE_LENGTH) + 1;
}
FORCEINLINE
STATIC
VOID
InitStorageProperties(_Out_ PSTORAGE_PROPERTY_QUERY Query,
_In_ STORAGE_PROPERTY_ID PropertyId,
_In_ STORAGE_QUERY_TYPE QueryType)
{
Query->PropertyId = PropertyId;
Query->QueryType = QueryType;
}
/*
* TODO: Query PhysicalDrive%n to get the serial numbers for all harddrives, can
* use the command "wmic diskdrive" check in console.
@ -1008,7 +1049,7 @@ GetHardDiskDriveSerialNumber(_Inout_ PVOID ConfigDrive0Serial,
HANDLE handle = NULL;
OBJECT_ATTRIBUTES attributes = {0};
IO_STATUS_BLOCK status_block = {0};
STORAGE_PROPERTY_QUERY storage_property = {0};
STORAGE_PROPERTY_QUERY query = {0};
STORAGE_DESCRIPTOR_HEADER storage_descriptor_header = {0};
PSTORAGE_DEVICE_DESCRIPTOR device_descriptor = NULL;
UNICODE_STRING physical_drive_path = {0};
@ -1037,8 +1078,7 @@ GetHardDiskDriveSerialNumber(_Inout_ PVOID ConfigDrive0Serial,
goto end;
}
storage_property.PropertyId = StorageDeviceProperty;
storage_property.QueryType = PropertyStandardQuery;
InitStorageProperties(&query, StorageDeviceProperty, PropertyStandardQuery);
status = ImpZwDeviceIoControlFile(handle,
NULL,
@ -1046,7 +1086,7 @@ GetHardDiskDriveSerialNumber(_Inout_ PVOID ConfigDrive0Serial,
NULL,
&status_block,
IOCTL_STORAGE_QUERY_PROPERTY,
&storage_property,
&query,
sizeof(STORAGE_PROPERTY_QUERY),
&storage_descriptor_header,
sizeof(STORAGE_DESCRIPTOR_HEADER));
@ -1072,7 +1112,7 @@ GetHardDiskDriveSerialNumber(_Inout_ PVOID ConfigDrive0Serial,
NULL,
&status_block,
IOCTL_STORAGE_QUERY_PROPERTY,
&storage_property,
&query,
sizeof(STORAGE_PROPERTY_QUERY),
device_descriptor,
storage_descriptor_header.Size);
@ -1086,10 +1126,8 @@ GetHardDiskDriveSerialNumber(_Inout_ PVOID ConfigDrive0Serial,
if (!device_descriptor->SerialNumberOffset)
goto end;
serial_number = (PCHAR)((UINT64)device_descriptor +
device_descriptor->SerialNumberOffset);
serial_length =
strnlen_s(serial_number, DEVICE_DRIVE_0_SERIAL_CODE_LENGTH) + 1;
serial_number = GetStorageDescriptorSerialNumber(device_descriptor);
serial_length = GetStorageDescriptorSerialLength(serial_number);
if (serial_length > ConfigDrive0MaxSize) {
status = STATUS_BUFFER_TOO_SMALL;
@ -1097,6 +1135,7 @@ GetHardDiskDriveSerialNumber(_Inout_ PVOID ConfigDrive0Serial,
}
RtlCopyMemory(ConfigDrive0Serial, serial_number, serial_length);
end:
if (handle)
@ -1350,7 +1389,6 @@ DetectEptHooksInKeyFunctions()
return status;
}
STATIC
VOID
FindWinLogonProcess(_In_ PPROCESS_LIST_ENTRY Entry, _In_opt_ PVOID Context)
{
@ -1643,16 +1681,73 @@ end:
return status;
}
FORCEINLINE
STATIC
VOID
IncrementActiveThreadCount(_Inout_ PSYS_MODULE_VAL_CONTEXT Context)
{
InterlockedIncrement(&Context->active_thread_count);
}
FORCEINLINE
STATIC
VOID
DecrementActiveThreadCount(_Inout_ PSYS_MODULE_VAL_CONTEXT Context)
{
InterlockedDecrement(&Context->active_thread_count);
}
FORCEINLINE
STATIC
VOID
SetVerificationBlockAsComplete(_In_ PSYS_MODULE_VAL_CONTEXT Context)
{
InterlockedExchange(&Context->complete, TRUE);
}
FORCEINLINE
STATIC
UINT32
GetCurrentVerificationIndex(_In_ PSYS_MODULE_VAL_CONTEXT Context)
{
return InterlockedExchange(&Context->current_count, Context->current_count);
}
FORCEINLINE
STATIC
UINT32
GetCurrentVerificationMaxIndex(_In_ PSYS_MODULE_VAL_CONTEXT Context,
_In_ UINT32 Count)
{
return Count + Context->block_size;
}
FORCEINLINE
STATIC
VOID
UpdateCurrentVerificationIndex(_In_ PSYS_MODULE_VAL_CONTEXT Context,
_In_ UINT32 Count)
{
InterlockedExchange(&Context->current_count, Count);
}
STATIC
VOID
SystemModuleVerificationDispatchFunction(_In_ PDEVICE_OBJECT DeviceObject,
_In_ PSYS_MODULE_VAL_CONTEXT Context)
{
InterlockedIncrement(&Context->active_thread_count);
IncrementActiveThreadCount(Context);
LONG count =
InterlockedExchange(&Context->current_count, Context->current_count);
LONG max = count + Context->block_size;
UINT32 count = GetCurrentVerificationIndex(Context);
/*
* theres a race condition here, where if the max is taken after a thread
* has alredy completed an iteration, meaning the current_count will be +1
* then what the starting thread is expecting, meaning the final iteration
* will be off by one. To fix just need to calculate the block max before
* threads are dispatched. todo!
*/
UINT32 max = GetCurrentVerificationMaxIndex(Context, count);
for (; count < max && count < Context->total_count; count++) {
if (!InterlockedCompareExchange(
@ -1662,14 +1757,33 @@ SystemModuleVerificationDispatchFunction(_In_ PDEVICE_OBJECT DeviceObject,
}
if (count == Context->total_count)
InterlockedExchange(&Context->complete, TRUE);
SetVerificationBlockAsComplete(Context);
InterlockedExchange(&Context->current_count, count);
InterlockedDecrement(&Context->active_thread_count);
UpdateCurrentVerificationIndex(Context, count);
DecrementActiveThreadCount(Context);
}
#define VALIDATION_BLOCK_SIZE 25
FORCEINLINE
STATIC
VOID
InitSysModuleValidationContext(_Out_ PSYS_MODULE_VAL_CONTEXT Context,
_In_ PMODULE_DISPATCHER_HEADER DispatcherArray,
_In_ PSYSTEM_MODULES SystemModules)
{
Context->active_thread_count = 0;
Context->active = TRUE;
Context->complete = FALSE;
Context->dispatcher_info = DispatcherArray;
Context->module_info = SystemModules->address;
Context->total_count = SystemModules->module_count;
Context->block_size = VALIDATION_BLOCK_SIZE;
/* skip hal.dll and ntosrnl.exe */
Context->current_count = 2;
}
/*
* Multithreaded delayed priority work items improve 1% lows by 25% and reduces
* average PC latency by 10% compared to traditional multithreading. This is
@ -1685,9 +1799,9 @@ STATIC
NTSTATUS
InitialiseSystemModuleVerificationContext(PSYS_MODULE_VAL_CONTEXT Context)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
SYSTEM_MODULES modules = {0};
PMODULE_DISPATCHER_HEADER dispatcher_array = NULL;
NTSTATUS status = STATUS_UNSUCCESSFUL;
SYSTEM_MODULES modules = {0};
PMODULE_DISPATCHER_HEADER dispatcher = NULL;
status = GetSystemModuleInformation(&modules);
@ -1698,27 +1812,17 @@ InitialiseSystemModuleVerificationContext(PSYS_MODULE_VAL_CONTEXT Context)
DEBUG_VERBOSE("driver count: %lx", modules.module_count);
dispatcher_array = ImpExAllocatePool2(POOL_FLAG_NON_PAGED,
modules.module_count *
sizeof(MODULE_DISPATCHER_HEADER),
POOL_TAG_INTEGRITY);
dispatcher = ImpExAllocatePool2(POOL_FLAG_NON_PAGED,
modules.module_count *
sizeof(MODULE_DISPATCHER_HEADER),
POOL_TAG_INTEGRITY);
if (!dispatcher_array) {
if (!dispatcher) {
ImpExFreePoolWithTag(modules.address, SYSTEM_MODULES_POOL);
return STATUS_MEMORY_NOT_ALLOCATED;
}
Context->active_thread_count = 0;
Context->active = TRUE;
Context->complete = FALSE;
Context->dispatcher_info = dispatcher_array;
Context->module_info = modules.address;
Context->total_count = modules.module_count;
Context->block_size = VALIDATION_BLOCK_SIZE;
/* skip hal.dll and ntosrnl.exe */
Context->current_count = 2;
InitSysModuleValidationContext(Context, dispatcher, &modules);
return status;
}
@ -1762,6 +1866,24 @@ CleanupValidationContextOnUnload(_In_ PSYS_MODULE_VAL_CONTEXT Context)
FreeModuleVerificationItems(Context);
}
STATIC
VOID
DispatchVerificationWorkerThreads(_In_ PSYS_MODULE_VAL_CONTEXT Context)
{
for (INT index = 0; index < VERIFICATION_THREAD_COUNT; index++) {
Context->work_items[index] =
ImpIoAllocateWorkItem(GetDriverDeviceObject());
if (!Context->work_items[index])
continue;
ImpIoQueueWorkItem(Context->work_items[index],
SystemModuleVerificationDispatchFunction,
DelayedWorkQueue,
Context);
}
}
NTSTATUS
SystemModuleVerificationDispatcher()
{
@ -1795,19 +1917,7 @@ SystemModuleVerificationDispatcher()
FreeWorkItems(context);
}
for (INT index = 0; index < VERIFICATION_THREAD_COUNT; index++) {
work_item = ImpIoAllocateWorkItem(GetDriverDeviceObject());
if (!work_item)
continue;
ImpIoQueueWorkItem(work_item,
SystemModuleVerificationDispatchFunction,
DelayedWorkQueue,
context);
context->work_items[index] = work_item;
}
DispatchVerificationWorkerThreads(context);
DEBUG_VERBOSE(
"All worker threads dispatched for system module validation.");
@ -1892,3 +2002,203 @@ ValidateOurDriversDispatchRoutines()
return TRUE;
}
STATIC
VOID
FreeHeartbeatObjects(_Inout_ PHEARTBEAT_CONFIGURATION Configuration)
{
if (Configuration->dpc) {
ImpExFreePoolWithTag(Configuration->dpc, POOL_TAG_HEARTBEAT);
Configuration->dpc = NULL;
}
if (Configuration->timer) {
ImpExFreePoolWithTag(Configuration->timer, POOL_TAG_HEARTBEAT);
Configuration->timer = NULL;
}
}
STATIC
NTSTATUS
AllocateHeartbeatObjects(_Inout_ PHEARTBEAT_CONFIGURATION Configuration)
{
Configuration->dpc = ImpExAllocatePool2(
POOL_FLAG_NON_PAGED, sizeof(KDPC), POOL_TAG_HEARTBEAT);
if (!Configuration->dpc)
return STATUS_INSUFFICIENT_RESOURCES;
Configuration->timer = ImpExAllocatePool2(
POOL_FLAG_NON_PAGED, sizeof(KTIMER), POOL_TAG_HEARTBEAT);
if (!Configuration->timer) {
ImpExFreePoolWithTag(Configuration->dpc, POOL_TAG_HEARTBEAT);
return STATUS_INSUFFICIENT_RESOURCES;
}
return STATUS_SUCCESS;
}
#define HEARTBEAT_NANOSECONDS_LOW \
(60ULL * 10000000ULL) // 1 min in 100-nanosecond intervals
#define HEARTBEAT_NANOSECONDS_HIGH \
(240ULL * 10000000ULL) // 4 mins in 100-nanosecond intervals
#define TICKS_TO_100_NS_INTERVALS(tick_count) ((tick_count) * 100000)
/* Generate a random due time between 1 and 4 minutes in 100-nanosecond
* intervals. */
STATIC
LARGE_INTEGER
GenerateHeartbeatDueTime(_In_ PHEARTBEAT_CONFIGURATION Configuration)
{
LARGE_INTEGER ticks = {0};
KeQueryTickCount(&ticks);
UINT64 interval =
HEARTBEAT_NANOSECONDS_LOW +
(TICKS_TO_100_NS_INTERVALS(ticks.QuadPart) %
(HEARTBEAT_NANOSECONDS_HIGH - HEARTBEAT_NANOSECONDS_LOW));
LARGE_INTEGER due_time = {.QuadPart = -interval};
return due_time;
}
FORCEINLINE
STATIC
VOID
InitialiseHeartbeatObjects(_Inout_ PHEARTBEAT_CONFIGURATION Configuration)
{
KeInitializeDpc(Configuration->dpc, HeartbeatDpcRoutine, Configuration);
KeInitializeTimer(Configuration->timer);
KeSetTimer(Configuration->timer,
GenerateHeartbeatDueTime(Configuration),
Configuration->dpc);
}
FORCEINLINE
STATIC
VOID
SetHeartbeatActive(_Inout_ PHEARTBEAT_CONFIGURATION Configuration)
{
InterlockedIncrement(&Configuration->active);
}
FORCEINLINE
STATIC
VOID
SetheartbeatInactive(_Inout_ PHEARTBEAT_CONFIGURATION Configuration)
{
InterlockedDecrement(&Configuration->active);
}
/* Blocks until heartbeat execution is complete */
FORCEINLINE
STATIC
VOID
WaitForHeartbeatCompletion(_In_ PHEARTBEAT_CONFIGURATION Configuration)
{
while (Configuration->active)
;
}
STATIC
VOID
HeartbeatWorkItem(_In_ PDEVICE_OBJECT DeviceObject, _In_opt_ PVOID Context)
{
UNREFERENCED_PARAMETER(DeviceObject);
if (!ARGUMENT_PRESENT(Context))
return;
NTSTATUS status = STATUS_UNSUCCESSFUL;
PHEARTBEAT_CONFIGURATION config = (PHEARTBEAT_CONFIGURATION)Context;
DEBUG_INFO("heartbeat work routine called");
/* Ensure we wait until our heartbeats DPC has terminated. */
KeFlushQueuedDpcs();
FreeHeartbeatObjects(config);
status = AllocateHeartbeatObjects(config);
if (!NT_SUCCESS(status)) {
DEBUG_ERROR("AllocateHeartbeatObjects %x", status);
return;
}
InitialiseHeartbeatObjects(config);
SetheartbeatInactive(config);
}
STATIC
VOID
HeartbeatDpcRoutine(_In_ PKDPC Dpc,
_In_opt_ PVOID DeferredContext,
_In_opt_ PVOID SystemArgument1,
_In_opt_ PVOID SystemArgument2)
{
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(SystemArgument1);
UNREFERENCED_PARAMETER(SystemArgument2);
if (!ARGUMENT_PRESENT(DeferredContext))
return;
PHEARTBEAT_CONFIGURATION config = (PHEARTBEAT_CONFIGURATION)DeferredContext;
SetHeartbeatActive(config);
DEBUG_INFO("heartbeat called!");
config->counter++;
IoQueueWorkItem(
config->work_item, HeartbeatWorkItem, NormalWorkQueue, config);
}
/*
* The premise behind this initial heartbeat monitor is that at a random
* interval a timer will be set. Once this timer is set, a dpc routine will
* run that will insert a heartbeat packet into the io queue which will be
* processed by user mode. Once the heartbeat is inserted, we queue a work
* item which will wait until the dpc routine is finished, free the current
* timer and work item (this is safe as the timer is removed from the timer
* queue when its alerted) and allocate a new timer and dpc object. We will
* then initalise them and insert them with another random value.
*
* The goal of this is to make reverse engineering the heartbeat process as
* hard as possible. And while it is only a start, I think its a start in
* the right direction.
*/
NTSTATUS
InitialiseHeartbeatConfiguration(_Inout_ PHEARTBEAT_CONFIGURATION Configuration)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
Configuration->counter = 0;
Configuration->active = FALSE;
Configuration->seed = GenerateRandSeed();
Configuration->work_item = IoAllocateWorkItem(GetDriverDeviceObject());
if (!Configuration->work_item)
return STATUS_INSUFFICIENT_RESOURCES;
status = AllocateHeartbeatObjects(Configuration);
if (!NT_SUCCESS(status)) {
DEBUG_ERROR("AllocateHeartbeatObjects %x", status);
return status;
}
InitialiseHeartbeatObjects(Configuration);
return status;
}
VOID
FreeHeartbeatConfiguration(_Inout_ PHEARTBEAT_CONFIGURATION Configuration)
{
WaitForHeartbeatCompletion(Configuration);
KeCancelTimer(Configuration->timer);
FreeHeartbeatObjects(Configuration);
IoFreeWorkItem(Configuration->work_item);
}

View file

@ -76,10 +76,10 @@ GetHardDiskDriveSerialNumber(_Inout_ PVOID ConfigDrive0Serial,
_In_ SIZE_T ConfigDrive0MaxSize);
NTSTATUS
ParseSMBIOSTable(_Out_ PVOID Buffer,
_In_ SIZE_T BufferSize,
_In_ ULONG TableIndex,
_In_ ULONG TableSubIndex);
ParseSMBIOSTable(_Out_ PVOID Buffer,
_In_ SIZE_T BufferSize,
_In_ SMBIOS_TABLE_INDEX TableIndex,
_In_ ULONG TableSubIndex);
NTSTATUS
DetectEptHooksInKeyFunctions();
@ -90,9 +90,6 @@ ScanForSignature(_In_ PVOID BaseAddress,
_In_ LPCSTR Signature,
_In_ SIZE_T SignatureLength);
NTSTATUS
ValidateNtoskrnl();
NTSTATUS
GetOsVersionInformation(_Out_ PRTL_OSVERSIONINFOW VersionInfo);
@ -120,4 +117,14 @@ ValidateOurDriversDispatchRoutines();
VOID
DeferredModuleHashingCallback();
VOID
FindWinLogonProcess(_In_ PPROCESS_LIST_ENTRY Entry, _In_opt_ PVOID Context);
NTSTATUS
InitialiseHeartbeatConfiguration(
_Inout_ PHEARTBEAT_CONFIGURATION Configuration);
VOID
FreeHeartbeatConfiguration(_Inout_ PHEARTBEAT_CONFIGURATION Configuration);
#endif

View file

@ -68,6 +68,8 @@ DispatchApcOperation(_In_ PAPC_OPERATION_ID Operation);
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20023, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_VALIDATE_PCI_DEVICES \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20024, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_VALIDATE_WIN32K_TABLES \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20025, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define APC_OPERATION_STACKWALK 0x1
@ -82,18 +84,21 @@ DispatchApcOperation(_In_ PAPC_OPERATION_ID Operation);
* note: maybe we should use a spinlock here? Dont really want competing threads
* sleeping. I think spinlock should be used here.
*/
STATIC
VOID
IrpQueueAcquireLock(_In_ PIO_CSQ Csq, _Out_ PKIRQL Irql)
{
KeAcquireSpinLock(&GetIrpQueueHead()->lock, Irql);
}
STATIC
VOID
IrpQueueReleaseLock(_In_ PIO_CSQ Csq, _In_ KIRQL Irql)
{
KeReleaseSpinLock(&GetIrpQueueHead()->lock, Irql);
}
STATIC
PIRP
IrpQueuePeekNextEntry(_In_ PIO_CSQ Csq, _In_ PIRP Irp, _In_ PVOID Context)
{
@ -106,6 +111,7 @@ IrpQueuePeekNextEntry(_In_ PIO_CSQ Csq, _In_ PIRP Irp, _In_ PVOID Context)
return CONTAINING_RECORD(queue->queue.Flink, IRP, Tail.Overlay.ListEntry);
}
STATIC
VOID
IrpQueueRemove(_In_ PIO_CSQ Csq, _In_ PIRP Irp)
{
@ -114,12 +120,14 @@ IrpQueueRemove(_In_ PIO_CSQ Csq, _In_ PIRP Irp)
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
}
STATIC
BOOLEAN
IrpQueueIsThereDeferredReport(_In_ PIRP_QUEUE_HEAD Queue)
{
return Queue->deferred_reports.count > 0 ? TRUE : FALSE;
}
STATIC
PDEFERRED_REPORT
IrpQueueRemoveDeferredReport(_In_ PIRP_QUEUE_HEAD Queue)
{
@ -134,6 +142,7 @@ IrpQueueFreeDeferredReport(_In_ PDEFERRED_REPORT Report)
ImpExFreePoolWithTag(Report, REPORT_POOL_TAG);
}
STATIC
NTSTATUS
IrpQueueCompleteDeferredReport(_In_ PDEFERRED_REPORT Report, _In_ PIRP Irp)
{
@ -152,6 +161,7 @@ IrpQueueCompleteDeferredReport(_In_ PDEFERRED_REPORT Report, _In_ PIRP Irp)
return STATUS_SUCCESS;
}
STATIC
NTSTATUS
IrpQueueQueryPendingReports(_In_ PIRP Irp)
{
@ -188,6 +198,7 @@ end:
return status;
}
STATIC
VOID
IrpQueueInsert(_In_ PIO_CSQ Csq, _In_ PIRP Irp)
{
@ -196,6 +207,7 @@ IrpQueueInsert(_In_ PIO_CSQ Csq, _In_ PIRP Irp)
queue->count++;
}
STATIC
VOID
IrpQueueCompleteCancelledIrp(_In_ PIO_CSQ Csq, _In_ PIRP Irp)
{
@ -205,6 +217,7 @@ IrpQueueCompleteCancelledIrp(_In_ PIO_CSQ Csq, _In_ PIRP Irp)
ImpIofCompleteRequest(Irp, IO_NO_INCREMENT);
}
STATIC
PDEFERRED_REPORT
IrpQueueAllocateDeferredReport(_In_ PVOID Buffer, _In_ UINT32 BufferSize)
{
@ -221,6 +234,7 @@ IrpQueueAllocateDeferredReport(_In_ PVOID Buffer, _In_ UINT32 BufferSize)
#define MAX_DEFERRED_REPORTS_COUNT 100
STATIC
VOID
IrpQueueDeferReport(_In_ PIRP_QUEUE_HEAD Queue,
_In_ PVOID Buffer,
@ -258,8 +272,7 @@ IrpQueueCompleteIrp(_In_ PVOID Buffer, _In_ ULONG BufferSize)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
PIRP_QUEUE_HEAD queue = GetIrpQueueHead();
PIRP irp = IoCsqRemoveNextIrp(&queue->csq, NULL);
PIRP irp = IoCsqRemoveNextIrp(&queue->csq, NULL);
/*
* If no irps are available in our queue, lets store it in a deferred
@ -461,6 +474,19 @@ SharedMappingWorkRoutine(_In_ PDEVICE_OBJECT DeviceObject,
break;
case ssValidateWin32kDispatchTables:
DEBUG_INFO(
"SHARED_STATE_OPERATION_ID: ValidateWin32kDispatchTables Received");
status = ValidateWin32kDispatchTables();
if (!NT_SUCCESS(status))
DEBUG_ERROR("ValidateWin32kDispatchTables failed with status %x",
status);
break;
default: DEBUG_ERROR("Invalid SHARED_STATE_OPERATION_ID Received");
}
@ -516,7 +542,7 @@ SharedMappingInitialiseTimer(_In_ PSHARED_MAPPING Mapping)
LARGE_INTEGER due_time = {0};
LONG period = 0;
due_time.QuadPart = ABSOLUTE(SECONDS(30));
due_time.QuadPart = -ABSOLUTE(SECONDS(30));
Mapping->work_item = IoAllocateWorkItem(GetDriverDeviceObject());
@ -534,6 +560,21 @@ SharedMappingInitialiseTimer(_In_ PSHARED_MAPPING Mapping)
return STATUS_SUCCESS;
}
STATIC
VOID
InitSharedMappingStructure(_Out_ PSHARED_MAPPING Mapping,
_In_ PVOID KernelBuffer,
_In_ PVOID UserBuffer,
_In_ PMDL Mdl)
{
Mapping->kernel_buffer = (PSHARED_STATE)KernelBuffer;
Mapping->user_buffer = UserBuffer;
Mapping->mdl = Mdl;
Mapping->size = PAGE_SIZE;
Mapping->active = TRUE;
Mapping->work_item_status = FALSE;
}
STATIC
NTSTATUS
SharedMappingInitialise(_In_ PIRP Irp)
@ -594,13 +635,7 @@ SharedMappingInitialise(_In_ PIRP Irp)
return status;
}
mapping->kernel_buffer = (PSHARED_STATE)buffer;
mapping->user_buffer = user_buffer;
mapping->mdl = mdl;
mapping->size = PAGE_SIZE;
mapping->active = TRUE;
mapping->work_item_status = FALSE;
InitSharedMappingStructure(mapping, buffer, user_buffer, mdl);
SharedMappingInitialiseTimer(mapping);
mapping_init = (PSHARED_MAPPING_INIT)Irp->AssociatedIrp.SystemBuffer;
@ -1051,6 +1086,18 @@ DeviceControl(_In_ PDEVICE_OBJECT DeviceObject, _Inout_ PIRP Irp)
break;
case IOCTL_VALIDATE_WIN32K_TABLES:
DEBUG_INFO("IOCTL_VALIDATE_WIN32K_TABLES Received");
status = ValidateWin32kDispatchTables();
if (!NT_SUCCESS(status))
DEBUG_ERROR("ValidateWin32kDispatchTables failed with status %x",
status);
break;
default:
DEBUG_WARNING("Invalid IOCTL passed to driver: %lx",
stack_location->Parameters.DeviceIoControl.IoControlCode);

View file

@ -22,7 +22,7 @@ typedef enum _SHARED_STATE_OPERATION_ID {
ssScanForEptHooks,
ssInitiateDpcStackwalk,
ssValidateSystemModules,
ssValidateWin32kDispatchTables
} SHARED_STATE_OPERATION_ID;
typedef struct _SHARED_STATE {

View file

@ -7,6 +7,7 @@
#include "imports.h"
#include "apc.h"
#include "thread.h"
#include "pe.h"
#define WHITELISTED_MODULE_TAG 'whte'
@ -214,6 +215,17 @@ FindSystemModuleByName(_In_ LPCSTR ModuleName,
return NULL;
}
FORCEINLINE
STATIC
VOID
InitWhitelistedRegionStructure(_Out_ PWHITELISTED_REGIONS Region,
_In_ UINT64 Base,
_In_ UINT64 End)
{
Region->base = Base;
Region->end = End;
}
STATIC
NTSTATUS
PopulateWhitelistedModuleBuffer(_Inout_ PVOID Buffer,
@ -235,8 +247,9 @@ PopulateWhitelistedModuleBuffer(_Inout_ PVOID Buffer,
continue;
WHITELISTED_REGIONS region = {0};
region.base = (UINT64)module->ImageBase;
region.end = region.base + module->ImageSize;
InitWhitelistedRegionStructure(&region,
(UINT64)module->ImageBase,
region.base + module->ImageSize);
UINT64 destination =
(UINT64)Buffer + index * sizeof(WHITELISTED_REGIONS);
@ -246,6 +259,13 @@ PopulateWhitelistedModuleBuffer(_Inout_ PVOID Buffer,
return STATUS_SUCCESS;
}
STATIC
UINT64
GetDriverMajorDispatchFunction(_In_ PDRIVER_OBJECT Driver)
{
return Driver->MajorFunction[IRP_MJ_DEVICE_CONTROL];
}
STATIC
NTSTATUS
ValidateDriverIOCTLDispatchRegion(_In_ PDRIVER_OBJECT Driver,
@ -264,7 +284,7 @@ ValidateDriverIOCTLDispatchRegion(_In_ PDRIVER_OBJECT Driver,
*Flag = TRUE;
dispatch_function = Driver->MajorFunction[IRP_MJ_DEVICE_CONTROL];
dispatch_function = GetDriverMajorDispatchFunction(Driver);
if (dispatch_function == NULL)
return STATUS_SUCCESS;
@ -412,6 +432,17 @@ ValidateDriverObjectHasBackingModule(_In_ PSYSTEM_MODULES ModuleInformation,
return STATUS_SUCCESS;
}
FORCEINLINE
STATIC
VOID
InitSystemModulesStructure(_Out_ PSYSTEM_MODULES Modules,
_In_ PVOID Buffer,
_In_ INT Count)
{
Modules->address = Buffer;
Modules->module_count = Count;
}
// 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)
@ -432,26 +463,26 @@ GetSystemModuleInformation(_Out_ PSYSTEM_MODULES ModuleInformation)
return status;
}
PRTL_MODULE_EXTENDED_INFO driver_information =
PRTL_MODULE_EXTENDED_INFO buffer =
ExAllocatePool2(POOL_FLAG_NON_PAGED, size, SYSTEM_MODULES_POOL);
if (!driver_information) {
if (!buffer) {
DEBUG_ERROR("Failed to allocate pool LOL");
return STATUS_MEMORY_NOT_ALLOCATED;
}
status = RtlQueryModuleInformation(
&size, sizeof(RTL_MODULE_EXTENDED_INFO), driver_information);
&size, sizeof(RTL_MODULE_EXTENDED_INFO), buffer);
if (!NT_SUCCESS(status)) {
DEBUG_ERROR("RtlQueryModuleInformation 2 failed with status %x",
status);
ExFreePoolWithTag(driver_information, SYSTEM_MODULES_POOL);
ExFreePoolWithTag(buffer, SYSTEM_MODULES_POOL);
return STATUS_ABANDONED;
}
ModuleInformation->address = driver_information;
ModuleInformation->module_count = size / sizeof(RTL_MODULE_EXTENDED_INFO);
InitSystemModulesStructure(
ModuleInformation, buffer, size / sizeof(RTL_MODULE_EXTENDED_INFO));
return status;
}
@ -489,7 +520,7 @@ ValidateDriverObjects(_In_ PSYSTEM_MODULES SystemModules,
AddDriverToList(Head, current_driver, REASON_NO_BACKING_MODULE);
if (NT_SUCCESS(status))
Head->count += 1;
Head->count++;
}
status = ValidateDriverIOCTLDispatchRegion(
@ -506,7 +537,7 @@ ValidateDriverObjects(_In_ PSYSTEM_MODULES SystemModules,
status = AddDriverToList(
Head, current_driver, REASON_INVALID_IOCTL_DISPATCH);
if (NT_SUCCESS(status))
Head->count += 1;
Head->count++;
}
sub_entry = sub_entry->ChainLink;
@ -609,16 +640,54 @@ end:
return STATUS_SUCCESS;
}
FORCEINLINE
STATIC
INT
GetModuleCount(_In_ PINVALID_DRIVERS_HEAD Head)
{
return Head->count >= MODULE_VALIDATION_FAILURE_MAX_REPORT_COUNT
? MODULE_VALIDATION_FAILURE_MAX_REPORT_COUNT
: Head->count;
}
STATIC
VOID
ReportInvalidDriverObject(_In_ PINVALID_DRIVERS_HEAD Head)
{
PMODULE_VALIDATION_FAILURE report =
ImpExAllocatePool2(POOL_FLAG_NON_PAGED,
sizeof(MODULE_VALIDATION_FAILURE),
POOL_TAG_INTEGRITY);
if (!report)
return;
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->DriverSize;
ANSI_STRING string = {0};
string.Length = 0;
string.MaximumLength = MODULE_REPORT_DRIVER_NAME_BUFFER_SIZE;
string.Buffer = &report->driver_name;
/* Continue regardless of result */
ImpRtlUnicodeStringToAnsiString(
&string, &Head->first_entry->driver->DriverName, FALSE);
IrpQueueCompleteIrp(report, sizeof(MODULE_VALIDATION_FAILURE));
}
NTSTATUS
HandleValidateDriversIOCTL()
{
PAGED_CODE();
NTSTATUS status = STATUS_UNSUCCESSFUL;
ULONG buffer_size = 0;
SYSTEM_MODULES system_modules = {0};
MODULE_VALIDATION_FAILURE_HEADER header = {0};
PINVALID_DRIVERS_HEAD head = NULL;
NTSTATUS status = STATUS_UNSUCCESSFUL;
ULONG buffer_size = 0;
SYSTEM_MODULES system_modules = {0};
PINVALID_DRIVERS_HEAD head = NULL;
/* Fix annoying visual studio linting error */
RtlZeroMemory(&system_modules, sizeof(SYSTEM_MODULES));
@ -655,18 +724,11 @@ HandleValidateDriversIOCTL()
goto end;
}
header.module_count =
head->count >= MODULE_VALIDATION_FAILURE_MAX_REPORT_COUNT
? MODULE_VALIDATION_FAILURE_MAX_REPORT_COUNT
: head->count;
if (head->count == 0) {
DEBUG_INFO("Found no invalid drivers on the system.");
goto end;
}
DEBUG_VERBOSE("System has an invalid driver count of: %i", head->count);
for (INT index = 0; index < head->count; index++) {
/* make sure we free any non reported modules */
if (index >= MODULE_VALIDATION_FAILURE_MAX_REPORT_COUNT) {
@ -674,42 +736,12 @@ HandleValidateDriversIOCTL()
continue;
}
PMODULE_VALIDATION_FAILURE report =
ImpExAllocatePool2(POOL_FLAG_NON_PAGED,
sizeof(MODULE_VALIDATION_FAILURE),
POOL_TAG_INTEGRITY);
if (!report)
continue;
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->DriverSize;
ANSI_STRING string = {0};
string.Length = 0;
string.MaximumLength = MODULE_REPORT_DRIVER_NAME_BUFFER_SIZE;
string.Buffer = &report->driver_name;
status = ImpRtlUnicodeStringToAnsiString(
&string, &head->first_entry->driver->DriverName, FALSE);
/* still continue if we fail to get the driver name */
if (!NT_SUCCESS(status))
DEBUG_ERROR("RtlUnicodeStringToAnsiString failed with status %x",
status);
status = IrpQueueCompleteIrp(report, sizeof(MODULE_VALIDATION_FAILURE));
if (!NT_SUCCESS(status)) {
DEBUG_ERROR("IrpQueueCompleteIrp failed with status %x", status);
continue;
}
ReportInvalidDriverObject(head);
RemoveInvalidDriverFromList(head);
}
end:
ImpExFreePoolWithTag(head, INVALID_DRIVER_LIST_HEAD_POOL);
ImpExFreePoolWithTag(system_modules.address, SYSTEM_MODULES_POOL);
@ -748,21 +780,17 @@ IsInstructionPointerInInvalidRegion(_In_ UINT64 RIP,
return STATUS_SUCCESS;
}
NTSTATUS
BOOLEAN
IsInstructionPointerInsideModule(_In_ UINT64 Rip,
_In_ PRTL_MODULE_EXTENDED_INFO Module,
_Out_ PBOOLEAN Result)
_In_ PRTL_MODULE_EXTENDED_INFO Module)
{
UINT64 base = (UINT64)Module->ImageBase;
UINT64 end = base + Module->ImageSize;
if (Rip >= base && Rip <= end) {
*Result = TRUE;
return STATUS_SUCCESS;
}
if (Rip >= base && Rip <= end)
return TRUE;
*Result = FALSE;
return STATUS_SUCCESS;
return FALSE;
}
STATIC
@ -890,17 +918,38 @@ AnalyseNmiData(_In_ PNMI_CONTEXT NmiContext, _In_ PSYSTEM_MODULES SystemModules)
continue;
}
if (!flag) {
if (!flag)
ReportInvalidRipFoundDuringNmi(&NmiContext[core]);
return STATUS_SUCCESS;
}
}
return STATUS_SUCCESS;
}
FORCEINLINE
STATIC
TASK_STATE_SEGMENT_64*
GetTaskStateSegment(_In_ UINT64 Kpcr)
{
return *(TASK_STATE_SEGMENT_64**)(Kpcr + KPCR_TSS_BASE_OFFSET);
}
FORCEINLINE
STATIC
PMACHINE_FRAME
GetIsrMachineFrame(_In_ TASK_STATE_SEGMENT_64* TaskStateSegment)
{
return TaskStateSegment->Ist3 - sizeof(MACHINE_FRAME);
}
FORCEINLINE
STATIC
BOOLEAN
IsUserModeAddress(_In_ UINT64 Rip)
{
return Rip <= WINDOWS_USERMODE_MAX_ADDRESS ? TRUE : FALSE;
}
STATIC BOOLEAN
NmiCallback(_Inout_opt_ PVOID Context, _In_ BOOLEAN Handled)
{
UNREFERENCED_PARAMETER(Handled);
@ -924,10 +973,10 @@ NmiCallback(_Inout_opt_ PVOID Context, _In_ BOOLEAN Handled)
* the ISR stack to find the interrupted rip.
*/
kpcr = __readmsr(IA32_GS_BASE);
tss = *(TASK_STATE_SEGMENT_64**)(kpcr + KPCR_TSS_BASE_OFFSET);
machine_frame = tss->Ist3 - sizeof(MACHINE_FRAME);
tss = GetTaskStateSegment(kpcr);
machine_frame = GetIsrMachineFrame(tss);
if (machine_frame->rip <= WINDOWS_USERMODE_MAX_ADDRESS)
if (IsUserModeAddress(machine_frame->rip))
context[core].user_thread = TRUE;
context[core].interrupted_rip = machine_frame->rip;
@ -1257,6 +1306,19 @@ ValidateThreadViaKernelApcCallback(_In_ PTHREAD_LIST_ENTRY ThreadListEntry,
IncrementApcCount(APC_CONTEXT_ID_STACKWALK);
}
FORCEINLINE
STATIC
VOID
SetApcAllocationInProgress(_In_ PAPC_STACKWALK_CONTEXT Context)
{
Context->header.allocation_in_progress = TRUE;
}
UnsetApcAllocationInProgress(_In_ PAPC_STACKWALK_CONTEXT Context)
{
Context->header.allocation_in_progress = FALSE;
}
/*
* Since NMIs are only executed on the thread that is running on each logical
* core, it makes sense to make use of APCs that, while can be masked off,
@ -1306,11 +1368,10 @@ ValidateThreadsViaKernelApc()
InsertApcContext(context);
context->header.allocation_in_progress = TRUE;
SetApcAllocationInProgress(context);
EnumerateThreadListWithCallbackRoutine(ValidateThreadViaKernelApcCallback,
context);
context->header.allocation_in_progress = FALSE;
UnsetApcAllocationInProgress(context);
return status;
}
@ -1866,4 +1927,311 @@ end:
ImpObDereferenceObject(directory);
ImpZwClose(handle);
return STATUS_SUCCESS;
}
PVOID
FindDriverBaseNoApi(_In_ PDRIVER_OBJECT DriverObject, _In_ PWCH Name)
{
PKLDR_DATA_TABLE_ENTRY first =
(PKLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection;
/* first entry contains invalid data, 2nd entry is the kernel */
PKLDR_DATA_TABLE_ENTRY entry =
((PKLDR_DATA_TABLE_ENTRY)DriverObject->DriverSection)
->InLoadOrderLinks.Flink->Flink;
while (entry->InLoadOrderLinks.Flink != first) {
/* todo: write our own unicode string comparison function, since
* the entire point of this is to find exports with no exports.
*/
if (!wcscmp(entry->BaseDllName.Buffer, Name)) {
return entry->DllBase;
}
entry = entry->InLoadOrderLinks.Flink;
}
return NULL;
}
STATIC
VOID
ValidateDispatchTableRoutines(_In_ PVOID* Table, _In_ UINT32 Entries)
{
}
PRTL_MODULE_EXTENDED_INFO
FindModuleByName(_In_ PSYSTEM_MODULES Modules, _In_ PCHAR ModuleName)
{
for (UINT32 index = 0; index < Modules->module_count; index++) {
PRTL_MODULE_EXTENDED_INFO entry =
&((PRTL_MODULE_EXTENDED_INFO)(Modules->address))[index];
if (strstr(entry->FullPathName, ModuleName))
return entry;
}
return NULL;
}
#define KERNEL_LOW_ADDRESS 0xFFFF000000000000
#define KERNEL_HIGH_ADDRESS 0xFFFFFFFFFFFFFFFF
BOOLEAN
IsValidKernelAddress(_In_ UINT64 Address)
{
if (!(Address >= KERNEL_LOW_ADDRESS && Address <= KERNEL_HIGH_ADDRESS))
return FALSE;
if (!MmIsAddressValid(Address))
return FALSE;
return TRUE;
}
/*
* Follows a chain of valid pointers until a pointer is no longer present in the
* chain, and returns the final pointer. Assumes the argument "Start" contains a
* valid pointer at its address.
*
* The try catch here is also useless. We can work on making this more secure
* later.
*/
PVOID
FindChainedPointerEnding(_In_ PVOID* Start)
{
PVOID* current = *Start;
PVOID prev = Start;
while (IsValidKernelAddress(current)) {
__try {
prev = current;
current = *current;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return prev;
}
}
return prev;
}
#define WIN32KBASE_DXGKRNL_INTERFACE_FUNC_COUNT 98
// clang-format off
/*
* ffffa135`fa847828 fffff805`5c7ccf60
* ffffa135`fa847828 fffff805`5c7ccf60 dxgkrnl!DXG_GUEST_COMPOSITIONOBJECTCHANNEL::ChannelStarted
* ffffa135`fa847830 fffff805`5c7ccf60 dxgkrnl!DXG_GUEST_COMPOSITIONOBJECTCHANNEL::ChannelStarted
* ffffa135`fa847838 fffff805`5c7e4ca0 dxgkrnl!DxgkProcessCallout
* ffffa135`fa847840 fffff805`5c7b2580 dxgkrnl!DxgkNotifyProcessFreezeCallout
* ffffa135`fa847848 fffff805`5c7b2430 dxgkrnl!DxgkNotifyProcessThawCallout
* ffffa135`fa847850 fffff805`5c7daf30 dxgkrnl!DxgkOpenAdapter
* ffffa135`fa847858 fffff805`5c7ff6e0 dxgkrnl!DxgkEnumAdapters2Impl
* ffffa135`fa847860 fffff805`5c839f00 dxgkrnl!DxgkGetMaximumAdapterCount
* ffffa135`fa847868 fffff805`5c7e37c0 dxgkrnl!DxgkCloseAdapterImpl
* ffffa135`fa847870 fffff805`5c7b3970 dxgkrnl!DxgkDestroyDevice
* ffffa135`fa847878 fffff805`5c7c8370 dxgkrnl!DxgkEscape
* ffffa135`fa847880 fffff805`5c7c58d0 dxgkrnl!DxgkGetPresentHistoryInternal
* ffffa135`fa847888 fffff805`5c9569a0 dxgkrnl!DxgkReleaseProcessVidPnSourceOwners
* ffffa135`fa847890 fffff805`5c8f4de0 dxgkrnl!DxgkPollDisplayChildrenInternal
* ffffa135`fa847898 fffff805`5c837390 dxgkrnl!DxgkFlushPresentHistory
* ffffa135`fa8478a0 fffff805`5c802e00 dxgkrnl!DxgkGetPathsModality
* ffffa135`fa8478a8 fffff805`5c82e7c0 dxgkrnl!DxgkFunctionalizePathsModality
* ffffa135`fa8478b0 fffff805`5c82e6d0 dxgkrnl!DxgkApplyPathsModality
* ffffa135`fa8478b8 fffff805`5c819740 dxgkrnl!DxgkFinalizePathsModality
* ffffa135`fa8478c0 fffff805`5c7b01c0 dxgkrnl!DxgkPersistPathsModality
* ffffa135`fa8478c8 fffff805`5c839d80 dxgkrnl!DxgkFreePathsModality
* ffffa135`fa8478d0 fffff805`5c816870 dxgkrnl!DxgkAugmentCdsj
* ffffa135`fa8478d8 fffff805`5c821270 dxgkrnl!DxgkGetPresentHistoryReadyEvent
* ffffa135`fa8478e0 fffff805`5c806eb0 dxgkrnl!DxgkGetDisplayConfigBufferSizes
* ffffa135`fa8478e8 fffff805`5c8070e0 dxgkrnl!DxgkQueryDisplayConfig
* ffffa135`fa8478f0 fffff805`5c9677d0 dxgkrnl!DxgkHandleForceProjectionMonitor
* ffffa135`fa8478f8 fffff805`5c838f10 dxgkrnl!DxgkUpdateCddDevmodeExtraData
* ffffa135`fa847900 fffff805`5c967ca0 dxgkrnl!DxgkProcessDisplayCalloutBatch
* ffffa135`fa847908 fffff805`5c7f8880 dxgkrnl!DxgkDisplayConfigDeviceInfo
* ffffa135`fa847910 fffff805`5c7e11f0 dxgkrnl!DxgkGetAdapterDeviceDesc
* ffffa135`fa847918 fffff805`5c7e9200 dxgkrnl!DxgkGetMonitorInternalInfo
* ffffa135`fa847920 fffff805`5c82a4f0 dxgkrnl!DxgkBeginTopologyTransition
* ffffa135`fa847928 fffff805`5c829f50 dxgkrnl!DxgkCompleteTopologyTransition
* ffffa135`fa847930 fffff805`5c8f4130 dxgkrnl!DxgkNeedToEnableCddPrimary
* ffffa135`fa847938 fffff805`5c82a090 dxgkrnl!DxgkInvalidateMonitorConnections
* ffffa135`fa847940 fffff805`5c807340 dxgkrnl!DxgkWriteDiagEntry
* ffffa135`fa847948 fffff805`5c815800 dxgkrnl!DxgkGetAdapterDefaultScaling
* ffffa135`fa847950 fffff805`5c816240 dxgkrnl!DxgkConvertDisplayConfigCScalingToDdiScaling
* ffffa135`fa847958 fffff805`5c8397e0 dxgkrnl!DxgkGetGlobalRawmodeFlag
* ffffa135`fa847960 fffff805`5c967e70 dxgkrnl!DxgkSetGlobalRawmodeFlag
* ffffa135`fa847968 fffff805`5c839530 dxgkrnl!DxgkQueryModeListCacheLuid
* ffffa135`fa847970 fffff805`5c826ff0 dxgkrnl!DxgkThreadCallout
* ffffa135`fa847978 fffff805`5c829c40 dxgkrnl!DxgkSessionConnected
* ffffa135`fa847980 fffff805`5c829a60 dxgkrnl!DxgkPreSessionDisconnected
* ffffa135`fa847988 fffff805`5c829b90 dxgkrnl!DxgkSessionDisconnected
* ffffa135`fa847990 fffff805`5c844420 dxgkrnl!DxgkSessionReconnected
* ffffa135`fa847998 fffff805`5c8440f0 dxgkrnl!DxgkGetAdapter
* ffffa135`fa8479a0 fffff805`5c844290 dxgkrnl!DxgkReleaseAdapter
* ffffa135`fa8479a8 fffff805`5c82c200 dxgkrnl!DxgkDesktopSwitch
* ffffa135`fa8479b0 fffff805`5c811860 dxgkrnl!DxgkStatusChangeNotify
* ffffa135`fa8479b8 fffff805`5c928fd0 dxgkrnl!DxgkEnableUnorderedWaitsForDevice
* ffffa135`fa8479c0 fffff805`5c839670 dxgkrnl!DxgkCddVerifyCddDevMode
* ffffa135`fa8479c8 fffff805`5c93bf30 dxgkrnl!DxgkIsVidPnSourceOwnerDwm
* ffffa135`fa8479d0 fffff805`5c8377a0 dxgkrnl!DxgkIsVidPnSourceOwnerExclusive
* ffffa135`fa8479d8 fffff805`5c7f8720 dxgkrnl!DxgkGetMonitorDeviceObject
* ffffa135`fa8479e0 fffff805`5c831680 dxgkrnl!DxgkRegisterDwmProcess
* ffffa135`fa8479e8 fffff805`5c8fa0a0 dxgkrnl!DxgkGetSharedResourceAdapterLuid
* ffffa135`fa8479f0 fffff805`5c8e7590 dxgkrnl!DxgkNotifyMonitorDimming
* ffffa135`fa8479f8 fffff805`5c820d10 dxgkrnl!DxgkGetSharedAllocationObjectType
* ffffa135`fa847a00 fffff805`5c820d20 dxgkrnl!DxgkGetSharedSyncObjectType
* ffffa135`fa847a08 fffff805`5c83b1b0 dxgkrnl!DxgkGetDisplayManagerObjectType
* ffffa135`fa847a10 fffff805`5c93be10 dxgkrnl!DxgkGetProcessInterferenceCount
* ffffa135`fa847a18 fffff805`5c839cd0 dxgkrnl!DxgkGetGpuUsageStatistics
* ffffa135`fa847a20 fffff805`5c815320 dxgkrnl!DxgkUpdateGdiInfo
* ffffa135`fa847a28 fffff805`5c8393d0 dxgkrnl!DxgkSetPresenterViewMode
* ffffa135`fa847a30 fffff805`5c836930 dxgkrnl!DxgkGetPresenterViewMode
* ffffa135`fa847a38 fffff805`5c827820 dxgkrnl!DxgkSetProcessStatus
* ffffa135`fa847a40 fffff805`5c7fa180 dxgkrnl!DxgkConvertLegacyQDCAdapterAndIdToActual
* ffffa135`fa847a48 fffff805`5c81b510 dxgkrnl!DxgkDisplayOnOff
* ffffa135`fa847a50 fffff805`5c815c30 dxgkrnl!DxgkIsVirtualizationDisabledForTarget
* ffffa135`fa847a58 fffff805`5c8378f0 dxgkrnl!DxgkIsSourceInHardwareClone
* ffffa135`fa847a60 fffff805`5c96d7d0 dxgkrnl!DxgkProcessLockScreen
* ffffa135`fa847a68 fffff805`5c964bd0 dxgkrnl!DxgkCopyPathsModality
* ffffa135`fa847a70 fffff805`5c964b30 dxgkrnl!DxgkApplyCdsjToPathsModality
* ffffa135`fa847a78 fffff805`5c979410 dxgkrnl!DxgkUpdateDpiInfoForNewOverride
* ffffa135`fa847a80 fffff805`5c839a00 dxgkrnl!DxgkInitializeDpi
* ffffa135`fa847a88 fffff805`5c839930 dxgkrnl!DxgkGetDpiOverrideForSource
* ffffa135`fa847a90 fffff805`5c980420 dxgkrnl!DxgkGetLegacyDpiInfo
* ffffa135`fa847a98 fffff805`5c94e0e0 dxgkrnl!DxgkWin32kSetPointerPosition
* ffffa135`fa847aa0 fffff805`5c94e240 dxgkrnl!DxgkWin32kSetPointerShape
* ffffa135`fa847aa8 fffff805`5c844730 dxgkrnl!DxgkGetUseHWGPUInRemoteSession
* ffffa135`fa847ab0 fffff805`5c945520 dxgkrnl!DxgkLPMDisplayControl
* ffffa135`fa847ab8 fffff805`5c945470 dxgkrnl!DxgkEnableHighPrecisionBrightness
* ffffa135`fa847ac0 fffff805`5c945640 dxgkrnl!DxgkSetHighPrecisionBrightness
* ffffa135`fa847ac8 fffff805`5c844670 dxgkrnl!DxgkChangeD3RequestsState
* ffffa135`fa847ad0 fffff805`5c836b90 dxgkrnl!DxgkGetMonitorEdid
* ffffa135`fa847ad8 fffff805`5c967620 dxgkrnl!DxgkConvertPathsModalityToDisplayConfig
* ffffa135`fa847ae0 fffff805`5c815d40 dxgkrnl!DxgkConvertDisplayConfigToDevMode
* ffffa135`fa847ae8 fffff805`5c7febd0 dxgkrnl!DxgkDDisplayEnumInternal
* ffffa135`fa847af0 fffff805`5c9677a0 dxgkrnl!DxgkGetMonitorDisplayId
* ffffa135`fa847af8 fffff805`5c964c60 dxgkrnl!DxgkEnumerateModesForPathsModality
* ffffa135`fa847b00 fffff805`5c8f0e70 dxgkrnl!DxgCreateLiveDumpWithWdLogs
* ffffa135`fa847b08 fffff805`5c9818d0 dxgkrnl!DxgkDispMgrReferenceObjectByHandle
* ffffa135`fa847b10 fffff805`5c9818b0 dxgkrnl!DxgkDispMgrIsTargetOwned
* ffffa135`fa847b18 fffff805`5c98bb20 dxgkrnl!DxgkCheckDisplayState
* ffffa135`fa847b20 fffff805`5c8363c0 dxgkrnl!DxgkSetKernelDisplayPolicy
* ffffa135`fa847b28 fffff805`5c839720 dxgkrnl!DxgkSendDisplayBrokerMessage
* ffffa135`fa847b30 fffff805`5c96fb30 dxgkrnl!DxgkGetWddmRemoteSessionGdiViewRange
*/
// clang-format on
STATIC
VOID
ReportWin32kBase_DxgInterfaceViolation(_In_ UINT32 TableIndex,
_In_ UINT64 Address)
{
PDATA_TABLE_ROUTINE_REPORT report =
ImpExAllocatePool2(POOL_FLAG_NON_PAGED,
sizeof(DATA_TABLE_ROUTINE_REPORT),
REPORT_POOL_TAG);
if (!report)
return;
report->id = REPORT_DATA_TABLE_ROUTINE;
report->address = Address;
report->id = Win32kBase_gDxgInterface;
report->index = TableIndex;
// todo! report->routine = ??
IrpQueueCompleteIrp(report, sizeof(DPC_STACKWALK_REPORT));
}
STATIC
NTSTATUS
ValidateWin32kBase_gDxgInterface()
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
SYSTEM_MODULES modules = {0};
PRTL_MODULE_EXTENDED_INFO win32kbase = NULL;
PRTL_MODULE_EXTENDED_INFO dxgkrnl = NULL;
KAPC_STATE apc = {0};
PKPROCESS winlogon = NULL;
PVOID* dxg_interface = NULL;
status = GetSystemModuleInformation(&modules);
if (!NT_SUCCESS(status)) {
DEBUG_ERROR("GetSystemModuleInformation failed %x", status);
return status;
}
win32kbase = FindModuleByName(&modules, "win32kbase.sys");
if (!win32kbase) {
status = STATUS_UNSUCCESSFUL;
goto end;
}
EnumerateProcessListWithCallbackRoutine(FindWinLogonProcess, &winlogon);
if (!winlogon) {
status = STATUS_UNSUCCESSFUL;
goto end;
}
KeStackAttachProcess(winlogon, &apc);
dxg_interface = PeFindExportByName(win32kbase->ImageBase, "gDxgkInterface");
if (!dxg_interface) {
status = STATUS_UNSUCCESSFUL;
goto detatch;
}
/* The functions in this table reside in dxgkrnl.sys */
dxgkrnl = FindModuleByName(&modules, "dxgkrnl.sys");
if (!dxgkrnl) {
status = STATUS_UNSUCCESSFUL;
goto detatch;
}
/* first 3 qwords are housekeeping. */
for (UINT32 index = 3; index < WIN32KBASE_DXGKRNL_INTERFACE_FUNC_COUNT + 3;
index++) {
if (!dxg_interface[index])
continue;
PVOID entry = FindChainedPointerEnding(dxg_interface[index]);
#if DEBUG
DEBUG_INFO("chain entry test: %p", entry);
DEBUG_INFO("regular entry: %p", dxg_interface[index]);
#endif
if (!IsInstructionPointerInsideModule(entry, dxgkrnl)) {
DEBUG_ERROR("invalid entry!!!");
ReportWin32kBase_DxgInterfaceViolation(index, entry);
}
}
detatch:
KeUnstackDetachProcess(&apc);
end:
if (modules.address)
ExFreePoolWithTag(modules.address, SYSTEM_MODULES_POOL);
return status;
}
/* todo: win32kEngInterface */
NTSTATUS
ValidateWin32kDispatchTables()
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
status = ValidateWin32kBase_gDxgInterface();
if (!NT_SUCCESS(status)) {
DEBUG_ERROR("ValidateWin32kBase_gDxgInterface: %x", status);
return status;
}
return status;
}

View file

@ -67,6 +67,9 @@ FlipKThreadMiscFlagsFlag(_In_ PKTHREAD Thread,
_In_ ULONG FlagIndex,
_In_ BOOLEAN NewValue);
PVOID
FindDriverBaseNoApi(_In_ PDRIVER_OBJECT DriverObject, _In_ PWCH Name);
NTSTATUS
DispatchStackwalkToEachCpuViaDpc();
@ -80,4 +83,7 @@ NTSTATUS
GetDriverObjectByDriverName(_In_ PUNICODE_STRING DriverName,
_Out_ PDRIVER_OBJECT* DriverObject);
NTSTATUS
ValidateWin32kDispatchTables();
#endif

77
driver/pe.c Normal file
View file

@ -0,0 +1,77 @@
#include "pe.h"
PNT_HEADER_64
PeGetNtHeader(_In_ PVOID Image)
{
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)Image;
if (dos->e_magic != IMAGE_DOS_SIGNATURE)
return NULL;
return CONVERT_RELATIVE_ADDRESS(PNT_HEADER_64, Image, dos->e_lfanew);
}
PIMAGE_DATA_DIRECTORY
PeGetExportDataDirectory(_In_ PVOID Image)
{
PNT_HEADER_64 nt = PeGetNtHeader(Image);
if (IMAGE_DIRECTORY_ENTRY_EXPORT >= nt->OptionalHeader.NumberOfRvaAndSizes)
return NULL;
return (PIMAGE_DATA_DIRECTORY)&nt->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
}
PIMAGE_EXPORT_DIRECTORY
PeGetExportDirectory(_In_ PVOID Image,
_In_ PIMAGE_DATA_DIRECTORY ExportDataDirectory)
{
if (!ExportDataDirectory->VirtualAddress || !ExportDataDirectory->Size)
return NULL;
return CONVERT_RELATIVE_ADDRESS(
PIMAGE_EXPORT_DIRECTORY, Image, ExportDataDirectory->VirtualAddress);
}
PVOID
PeFindExportByName(_In_ PVOID Image, _In_ PCHAR Name)
{
ANSI_STRING target = {0};
PNT_HEADER_64 nt = NULL;
PIMAGE_DATA_DIRECTORY data = NULL;
PIMAGE_EXPORT_DIRECTORY export = NULL;
RtlInitAnsiString(&target, Name);
nt = PeGetNtHeader(Image);
if (!nt)
return NULL;
data = PeGetExportDataDirectory(Image);
if (!data)
return NULL;
export = PeGetExportDirectory(Image, data);
if (!export)
return NULL;
PUINT32 functions =
CONVERT_RELATIVE_ADDRESS(PUINT32, Image, export->AddressOfFunctions);
PUINT32 names =
CONVERT_RELATIVE_ADDRESS(PUINT32, Image, export->AddressOfNames);
PUINT16 ordinals =
CONVERT_RELATIVE_ADDRESS(PUINT16, Image, export->AddressOfNameOrdinals);
for (UINT32 index = 0; index < export->NumberOfNames; index++) {
PCHAR export = CONVERT_RELATIVE_ADDRESS(PCHAR, Image, names[index]);
if (!strcmp(Name, export))
return CONVERT_RELATIVE_ADDRESS(
PVOID, Image, functions[ordinals[index]]);
}
return NULL;
}

12
driver/pe.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef PE_H
#define PE_H
#include "common.h"
#define IMAGE_DOS_SIGNATURE 0x5a4d /* MZ */
#define IMAGE_NT_SIGNATURE 0x00004550 /* PE00 */
PVOID
PeFindExportByName(_In_ PVOID Image, _In_ PCHAR Name);
#endif

View file

@ -61,6 +61,13 @@ SessionGetCallbackConfiguration(
ImpKeReleaseGuardedMutex(&GetActiveSession()->lock);
}
STATIC
VOID
SessionTerminateHeartbeat(_In_ PHEARTBEAT_CONFIGURATION Configuration)
{
FreeHeartbeatConfiguration(Configuration);
}
VOID
SessionTerminate()
{
@ -74,6 +81,7 @@ SessionTerminate()
session->um_handle = NULL;
session->process = NULL;
session->is_session_active = FALSE;
SessionTerminateHeartbeat(&session->heartbeat_config);
ImpKeReleaseGuardedMutex(&session->lock);
}
@ -119,6 +127,13 @@ SessionInitialise(_In_ PIRP Irp)
information->session_aes_key,
AES_128_KEY_SIZE);
status = InitialiseHeartbeatConfiguration(&session->heartbeat_config);
if (!NT_SUCCESS(status)) {
DEBUG_ERROR("InitialiseHeartbeatConfiguration %x", status);
goto end;
}
end:
ImpKeReleaseGuardedMutex(&session->lock);
return status;
@ -160,7 +175,7 @@ VOID
SessionIncrementIrpsProcessedCount()
{
ImpKeAcquireGuardedMutex(&GetActiveSession()->lock);
GetActiveSession()->irps_processed++;
GetActiveSession()->irps_received;
ImpKeReleaseGuardedMutex(&GetActiveSession()->lock);
}

View file

@ -17,7 +17,8 @@
typedef enum _TABLE_ID
{
HalDispatch = 0,
HalPrivateDispatch
HalPrivateDispatch,
Win32kBase_gDxgInterface
} TABLE_ID;
typedef struct _HYPERVISOR_DETECTION_REPORT
@ -64,6 +65,7 @@ typedef struct _DATA_TABLE_ROUTINE_REPORT
UINT32 report_code;
TABLE_ID id;
UINT64 address;
UINT32 index;
CHAR routine[DATA_TABLE_ROUTINE_BUF_SIZE];
} DATA_TABLE_ROUTINE_REPORT, *PDATA_TABLE_ROUTINE_REPORT;

15
driver/util.c Normal file
View file

@ -0,0 +1,15 @@
#include "common.h"
LARGE_INTEGER
GenerateRandSeed()
{
LARGE_INTEGER system_time = {0};
LARGE_INTEGER up_time = {0};
LARGE_INTEGER seed = {0};
KeQuerySystemTime(&system_time);
KeQueryTickCount(&up_time);
seed.QuadPart = system_time.QuadPart ^ up_time.QuadPart;
return seed;
}

9
driver/util.h Normal file
View file

@ -0,0 +1,9 @@
#ifndef UTIL_H
#define UTIL_H
#include "common.h"
LARGE_INTEGER
GenerateRandSeed();
#endif

View file

@ -47,15 +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->k_interface.initiate_apc_stackwalk();
// this->issue_kernel_job();
this->k_interface.initiate_apc_stackwalk();
helper::sleep_thread(DISPATCH_LOOP_SLEEP_TIME);
}
}
@ -102,5 +102,9 @@ void dispatcher::dispatcher::issue_kernel_job() {
case 11:
thread_pool.queue_job([this]() { k_interface.validate_pci_devices(); });
break;
case 12:
thread_pool.queue_job(
[this]() { k_interface.validate_win32k_dispatch_tables(); });
break;
}
}

View file

@ -211,45 +211,51 @@ void kernel_interface::kernel_interface::validate_system_modules() {
this->generic_driver_call(ioctl_code::ValidateSystemModules);
}
void kernel_interface::kernel_interface::validate_win32k_dispatch_tables() {
this->generic_driver_call(ioctl_code::ValidateWin32kDispatchTables);
}
void kernel_interface::kernel_interface::
verify_process_module_executable_regions() {
// HANDLE handle = INVALID_HANDLE_VALUE;
// MODULEENTRY32 module_entry = {0};
// BOOLEAN status = FALSE;
// process_module module = {0};
// unsigned long bytes_returned = 0;
// RtlDosPathNameToNtPathName_U pRtlDosPathNameToNtPathName_U = NULL;
// UNICODE_STRING nt_path_name = {0};
// pRtlDosPathNameToNtPathName_U = (RtlDosPathNameToNtPathName_U)GetProcAddress(
// GetModuleHandle("ntdll.dll"), "RtlDosPathNameToNtPathName_U");
// handle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32,
// GetCurrentProcessId());
// if (handle == INVALID_HANDLE_VALUE) {
// LOG_ERROR("CreateToolHelp32Snapshot with TH32CS_SNAPMODULE failed with "
// "status 0x%x",
// GetLastError());
// return;
// }
// module_entry.dwSize = sizeof(MODULEENTRY32);
// if (!Module32First(handle, &module_entry)) {
// LOG_ERROR("Module32First failed with status 0x%x", GetLastError());
// return;
// }
// do {
// module.module_base = module_entry.modBaseAddr;
// module.module_size = module_entry.modBaseSize;
// status = (*pRtlDosPathNameToNtPathName_U)(module_entry.szExePath,
// &nt_path_name, NULL, NULL);
// if (!status) {
// LOG_ERROR("RtlDosPathNameToNtPathName_U failed with no status.");
// continue;
// }
// memcpy(module.module_path, nt_path_name.Buffer, MAX_MODULE_PATH);
// this->generic_driver_call_input(ioctl_code::ValidateProcessLoadedModule,
// &module, sizeof(module), &bytes_returned);
// } while (Module32Next(handle, &module_entry));
//end:
// CloseHandle(handle);
// HANDLE handle = INVALID_HANDLE_VALUE;
// MODULEENTRY32 module_entry = {0};
// BOOLEAN status = FALSE;
// process_module module = {0};
// unsigned long bytes_returned = 0;
// RtlDosPathNameToNtPathName_U pRtlDosPathNameToNtPathName_U = NULL;
// UNICODE_STRING nt_path_name = {0};
// pRtlDosPathNameToNtPathName_U =
// (RtlDosPathNameToNtPathName_U)GetProcAddress(
// GetModuleHandle("ntdll.dll"), "RtlDosPathNameToNtPathName_U");
// handle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32,
// GetCurrentProcessId());
// if (handle == INVALID_HANDLE_VALUE) {
// LOG_ERROR("CreateToolHelp32Snapshot with TH32CS_SNAPMODULE failed with "
// "status 0x%x",
// GetLastError());
// return;
// }
// module_entry.dwSize = sizeof(MODULEENTRY32);
// if (!Module32First(handle, &module_entry)) {
// LOG_ERROR("Module32First failed with status 0x%x", GetLastError());
// return;
// }
// do {
// module.module_base = module_entry.modBaseAddr;
// module.module_size = module_entry.modBaseSize;
// status = (*pRtlDosPathNameToNtPathName_U)(module_entry.szExePath,
// &nt_path_name, NULL, NULL);
// if (!status) {
// LOG_ERROR("RtlDosPathNameToNtPathName_U failed with no status.");
// continue;
// }
// memcpy(module.module_path, nt_path_name.Buffer, MAX_MODULE_PATH);
// this->generic_driver_call_input(ioctl_code::ValidateProcessLoadedModule,
// &module, sizeof(module),
// &bytes_returned);
// } while (Module32Next(handle, &module_entry));
// end:
// CloseHandle(handle);
}
void kernel_interface::kernel_interface::initiate_apc_stackwalk() {

View file

@ -63,6 +63,7 @@ struct data_table_routine_report {
uint32_t report_code;
table_id id;
uint64_t address;
uint32_t index;
char routine[DATA_TABLE_ROUTINE_BUF_SIZE];
};
@ -139,10 +140,11 @@ enum ioctl_code
InsertIrpIntoIrpQueue = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20021, METHOD_BUFFERED, FILE_ANY_ACCESS),
QueryDeferredReports = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20022, METHOD_BUFFERED, FILE_ANY_ACCESS),
InitiateSharedMapping = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20023, METHOD_BUFFERED, FILE_ANY_ACCESS),
ValidatePciDevices = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20024, METHOD_BUFFERED, FILE_ANY_ACCESS)
ValidatePciDevices = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20024, METHOD_BUFFERED, FILE_ANY_ACCESS),
ValidateWin32kDispatchTables = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x20025, METHOD_BUFFERED, FILE_ANY_ACCESS)
};
constexpr int SHARED_STATE_OPERATION_COUNT = 9;
constexpr int SHARED_STATE_OPERATION_COUNT = 10;
enum shared_state_operation_id
{
@ -155,6 +157,7 @@ enum shared_state_operation_id
ssScanForEptHooks,
ssInitiateDpcStackwalk,
ssValidateSystemModules,
ssValidateWin32kDispatchTables
};
// clang-format on
@ -252,5 +255,6 @@ public:
void send_pending_irp();
void write_shared_mapping_operation(shared_state_operation_id operation_id);
void initiate_shared_mapping();
void validate_win32k_dispatch_tables();
};
} // namespace kernel_interface

8
notes.md Normal file
View file

@ -0,0 +1,8 @@
new feature notes:
- random heartbeat timer event callback. These timers should be single shot events, once fired we get a new random time and insert that. This way the timer objects are always fresh and we dont use a global timer object.
- session cookie new value per session
- session statistics need to be updated each time a new irp is inserted into the queue
- same with when we receive an irp
- this information can be used to detect malicious interferrence with the system
- use a reverse irp method, user mode program receives and irp and checks if it contains a special code indicating it must send an irp to tthe driver ?