mirror of
https://github.com/donnaskiez/ac.git
synced 2024-11-21 22:24:08 +01:00
integrate some new stuff [wip] (#9)
This commit is contained in:
parent
23444671ea
commit
07904949a6
22 changed files with 1176 additions and 268 deletions
17
README.md
17
README.md
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
|
|
67
driver/io.c
67
driver/io.c
|
@ -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);
|
||||
|
|
|
@ -22,7 +22,7 @@ typedef enum _SHARED_STATE_OPERATION_ID {
|
|||
ssScanForEptHooks,
|
||||
ssInitiateDpcStackwalk,
|
||||
ssValidateSystemModules,
|
||||
|
||||
ssValidateWin32kDispatchTables
|
||||
} SHARED_STATE_OPERATION_ID;
|
||||
|
||||
typedef struct _SHARED_STATE {
|
||||
|
|
516
driver/modules.c
516
driver/modules.c
|
@ -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(®ion,
|
||||
(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;
|
||||
}
|
|
@ -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
77
driver/pe.c
Normal 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
12
driver/pe.h
Normal 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
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
15
driver/util.c
Normal 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
9
driver/util.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
LARGE_INTEGER
|
||||
GenerateRandSeed();
|
||||
|
||||
#endif
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -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
8
notes.md
Normal 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 ?
|
Loading…
Reference in a new issue