2023-08-22 19:32:25 +02:00
|
|
|
|
#include "integrity.h"
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
#include "callbacks.h"
|
2023-08-22 19:32:25 +02:00
|
|
|
|
#include "common.h"
|
2024-08-01 06:21:53 +02:00
|
|
|
|
#include "crypt.h"
|
2023-09-01 13:46:31 +02:00
|
|
|
|
#include "driver.h"
|
2024-01-07 05:13:41 +01:00
|
|
|
|
#include "imports.h"
|
2024-08-01 06:21:53 +02:00
|
|
|
|
#include "io.h"
|
2024-08-04 08:30:31 +02:00
|
|
|
|
#include "lib/stdlib.h"
|
2024-08-01 06:21:53 +02:00
|
|
|
|
#include "modules.h"
|
|
|
|
|
#include "pe.h"
|
2024-01-31 08:32:13 +01:00
|
|
|
|
#include "session.h"
|
2024-05-04 17:43:01 +02:00
|
|
|
|
#include "util.h"
|
2023-08-22 19:32:25 +02:00
|
|
|
|
|
2023-09-01 12:56:27 +02:00
|
|
|
|
#include <bcrypt.h>
|
2023-09-28 09:15:47 +02:00
|
|
|
|
#include <devpkey.h>
|
2024-08-01 06:21:53 +02:00
|
|
|
|
#include <initguid.h>
|
2023-09-01 12:56:27 +02:00
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
// clang-format off
|
|
|
|
|
|
2024-04-13 06:40:51 +02:00
|
|
|
|
typedef struct _INTEGRITY_CHECK_HEADER {
|
2024-08-04 08:30:31 +02:00
|
|
|
|
|
|
|
|
|
/* Count of total sections contained within the buffer */
|
2024-07-19 16:27:50 +02:00
|
|
|
|
UINT32 section_count;
|
2024-08-04 08:30:31 +02:00
|
|
|
|
|
|
|
|
|
/* Total size of the buffer */
|
2024-07-19 16:27:50 +02:00
|
|
|
|
UINT32 total_size;
|
2023-08-25 09:38:45 +02:00
|
|
|
|
|
2023-12-13 05:06:27 +01:00
|
|
|
|
} INTEGRITY_CHECK_HEADER, *PINTEGRITY_CHECK_HEADER;
|
2023-08-25 09:38:45 +02:00
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
|
2024-04-13 06:40:51 +02:00
|
|
|
|
typedef struct _PROCESS_MODULE_INFORMATION {
|
2024-08-04 08:30:31 +02:00
|
|
|
|
/* Pointer to the base of the module*/
|
2024-08-01 06:21:53 +02:00
|
|
|
|
PVOID module_base;
|
2024-08-04 08:30:31 +02:00
|
|
|
|
|
|
|
|
|
/* Total size of the module */
|
2024-04-13 10:23:14 +02:00
|
|
|
|
SIZE_T module_size;
|
2024-08-04 08:30:31 +02:00
|
|
|
|
|
|
|
|
|
/* Path to the modules executable image*/
|
2024-08-01 06:21:53 +02:00
|
|
|
|
WCHAR module_path[MAX_MODULE_PATH];
|
2023-09-27 15:10:12 +02:00
|
|
|
|
|
2023-12-13 05:06:27 +01:00
|
|
|
|
} PROCESS_MODULE_INFORMATION, *PPROCESS_MODULE_INFORMATION;
|
2023-09-27 15:10:12 +02:00
|
|
|
|
|
2024-04-13 06:40:51 +02:00
|
|
|
|
typedef struct _PROCESS_MODULE_VALIDATION_RESULT {
|
2024-08-04 08:30:31 +02:00
|
|
|
|
|
|
|
|
|
/* Boolean value of whether or not the module image is valid */
|
2024-07-19 16:27:50 +02:00
|
|
|
|
UINT32 is_module_valid;
|
2023-09-27 15:10:12 +02:00
|
|
|
|
|
2023-12-13 05:06:27 +01:00
|
|
|
|
} PROCESS_MODULE_VALIDATION_RESULT, *PPROCESS_MODULE_VALIDATION_RESULT;
|
2023-09-27 15:10:12 +02:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
typedef struct _VAL_INTEGRITY_HEADER {
|
2024-08-04 08:30:31 +02:00
|
|
|
|
|
|
|
|
|
/* Header containing information pertaining to the buffer */
|
2024-07-19 16:27:50 +02:00
|
|
|
|
INTEGRITY_CHECK_HEADER integrity_check_header;
|
2024-08-04 08:30:31 +02:00
|
|
|
|
|
|
|
|
|
/* Section header */
|
2024-08-01 06:21:53 +02:00
|
|
|
|
IMAGE_SECTION_HEADER section_header;
|
2024-08-04 08:30:31 +02:00
|
|
|
|
|
|
|
|
|
/* Pointer to the start of the sections image */
|
2024-08-01 06:21:53 +02:00
|
|
|
|
CHAR section_base[];
|
2024-07-19 16:27:50 +02:00
|
|
|
|
|
|
|
|
|
} VAL_INTEGRITY_HEADER, *PVAL_INTEGRITY_HEADER;
|
|
|
|
|
|
2023-12-13 05:06:27 +01:00
|
|
|
|
STATIC
|
|
|
|
|
NTSTATUS
|
2023-10-08 06:24:54 +02:00
|
|
|
|
InitiateEptFunctionAddressArrays();
|
|
|
|
|
|
2023-12-13 05:06:27 +01:00
|
|
|
|
STATIC
|
|
|
|
|
NTSTATUS
|
2024-07-19 16:27:50 +02:00
|
|
|
|
GetModuleInformationByName(
|
|
|
|
|
_Out_ PRTL_MODULE_EXTENDED_INFO ModuleInfo,
|
|
|
|
|
_In_ LPCSTR ModuleName);
|
2023-12-13 05:06:27 +01:00
|
|
|
|
|
|
|
|
|
STATIC
|
|
|
|
|
NTSTATUS
|
2024-07-19 16:27:50 +02:00
|
|
|
|
StoreModuleExecutableRegionsInBuffer(
|
|
|
|
|
_Out_ PVOID* Buffer,
|
|
|
|
|
_In_ PVOID ModuleBase,
|
|
|
|
|
_In_ SIZE_T ModuleSize,
|
|
|
|
|
_Out_ PSIZE_T BytesWritten,
|
|
|
|
|
_In_ BOOLEAN IsModulex86
|
|
|
|
|
);
|
2023-12-13 05:06:27 +01:00
|
|
|
|
|
|
|
|
|
STATIC
|
|
|
|
|
NTSTATUS
|
2024-07-19 16:27:50 +02:00
|
|
|
|
MapDiskImageIntoVirtualAddressSpace(
|
|
|
|
|
_Inout_ PHANDLE SectionHandle,
|
|
|
|
|
_Out_ PVOID* Section,
|
|
|
|
|
_In_ PUNICODE_STRING Path,
|
|
|
|
|
_Out_ PSIZE_T Size
|
|
|
|
|
);
|
2023-12-13 05:06:27 +01:00
|
|
|
|
|
|
|
|
|
STATIC
|
|
|
|
|
VOID
|
2024-07-19 16:27:50 +02:00
|
|
|
|
GetNextSMBIOSStructureInTable(
|
|
|
|
|
_Inout_ PSMBIOS_TABLE_HEADER* CurrentStructure
|
|
|
|
|
);
|
2023-12-13 05:06:27 +01:00
|
|
|
|
|
|
|
|
|
STATIC
|
|
|
|
|
NTSTATUS
|
2024-07-19 16:27:50 +02:00
|
|
|
|
GetStringAtIndexFromSMBIOSTable(
|
|
|
|
|
_In_ PSMBIOS_TABLE_HEADER Table,
|
|
|
|
|
_In_ UINT32 Index,
|
|
|
|
|
_In_ PVOID Buffer,
|
|
|
|
|
_In_ SIZE_T BufferSize
|
|
|
|
|
);
|
2023-12-13 05:06:27 +01:00
|
|
|
|
|
|
|
|
|
STATIC
|
|
|
|
|
NTSTATUS
|
2024-07-19 16:27:50 +02:00
|
|
|
|
GetAverageReadTimeAtRoutine(
|
|
|
|
|
_In_ PVOID RoutineAddress,
|
|
|
|
|
_Out_ PUINT64 AverageTime
|
|
|
|
|
);
|
2023-12-13 05:06:27 +01:00
|
|
|
|
|
2024-05-04 17:43:01 +02:00
|
|
|
|
STATIC
|
|
|
|
|
VOID
|
2024-07-19 16:27:50 +02:00
|
|
|
|
HeartbeatDpcRoutine(
|
|
|
|
|
_In_ PKDPC Dpc,
|
|
|
|
|
_In_opt_ PVOID DeferredContext,
|
|
|
|
|
_In_opt_ PVOID SystemArgument1,
|
|
|
|
|
_In_opt_ PVOID SystemArgument2
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// clang-format on
|
2024-05-04 17:43:01 +02:00
|
|
|
|
|
2023-10-08 06:24:54 +02:00
|
|
|
|
#ifdef ALLOC_PRAGMA
|
2024-04-13 10:23:14 +02:00
|
|
|
|
# pragma alloc_text(PAGE, GetDriverImageSize)
|
|
|
|
|
# pragma alloc_text(PAGE, GetModuleInformationByName)
|
|
|
|
|
# pragma alloc_text(PAGE, StoreModuleExecutableRegionsInBuffer)
|
|
|
|
|
# pragma alloc_text(PAGE, MapDiskImageIntoVirtualAddressSpace)
|
|
|
|
|
# pragma alloc_text(PAGE, RetrieveInMemoryModuleExecutableSections)
|
|
|
|
|
# pragma alloc_text(PAGE, GetNextSMBIOSStructureInTable)
|
|
|
|
|
# pragma alloc_text(PAGE, GetStringAtIndexFromSMBIOSTable)
|
|
|
|
|
# pragma alloc_text(PAGE, ParseSMBIOSTable)
|
|
|
|
|
# pragma alloc_text(PAGE, ValidateProcessLoadedModule)
|
|
|
|
|
# pragma alloc_text(PAGE, GetHardDiskDriveSerialNumber)
|
|
|
|
|
# pragma alloc_text(PAGE, ScanForSignature)
|
|
|
|
|
# pragma alloc_text(PAGE, InitiateEptFunctionAddressArrays)
|
|
|
|
|
# pragma alloc_text(PAGE, DetectEptHooksInKeyFunctions)
|
2023-10-08 06:24:54 +02:00
|
|
|
|
#endif
|
|
|
|
|
|
2023-08-23 14:14:20 +02:00
|
|
|
|
/*
|
2024-04-13 06:40:51 +02:00
|
|
|
|
* note: this can be put into its own function wihtout an IRP as argument then
|
|
|
|
|
* it can be used in both the get driver image ioctl handler and the
|
|
|
|
|
* CopyDriverExecvutableRegions func
|
2023-12-13 05:06:27 +01:00
|
|
|
|
*/
|
2023-10-03 18:03:55 +02:00
|
|
|
|
NTSTATUS
|
2023-12-13 05:06:27 +01:00
|
|
|
|
GetDriverImageSize(_Inout_ PIRP Irp)
|
2023-08-23 14:14:20 +02:00
|
|
|
|
{
|
2024-04-13 10:23:14 +02:00
|
|
|
|
PAGED_CODE();
|
2023-10-09 20:19:51 +02:00
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(Irp != NULL);
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
2024-08-04 08:30:31 +02:00
|
|
|
|
LPCSTR name = GetDriverName();
|
2024-08-01 06:21:53 +02:00
|
|
|
|
SYSTEM_MODULES modules = {0};
|
2024-08-04 08:30:31 +02:00
|
|
|
|
PRTL_MODULE_EXTENDED_INFO driver = NULL;
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
status = GetSystemModuleInformation(&modules);
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR("GetSystemModuleInformation failed with status %x", status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
driver = FindSystemModuleByName(name, &modules);
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
if (!driver) {
|
2024-04-13 10:23:14 +02:00
|
|
|
|
DEBUG_ERROR("FindSystemModuleByName failed with no status code");
|
|
|
|
|
ImpExFreePoolWithTag(modules.address, SYSTEM_MODULES_POOL);
|
|
|
|
|
return STATUS_NOT_FOUND;
|
|
|
|
|
}
|
2023-11-09 12:11:02 +01:00
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
status = ValidateIrpOutputBuffer(Irp, sizeof(UINT32));
|
2023-11-09 08:30:59 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR("ValidateIrpOutputBuffer failed with status %x", status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
2023-11-09 08:30:59 +01:00
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
Irp->IoStatus.Information = sizeof(UINT32);
|
2024-07-19 16:27:50 +02:00
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
IntCopyMemory(
|
|
|
|
|
Irp->AssociatedIrp.SystemBuffer,
|
2024-08-04 08:30:31 +02:00
|
|
|
|
&driver->ImageSize,
|
|
|
|
|
sizeof(UINT32));
|
2023-10-03 18:03:55 +02:00
|
|
|
|
|
2023-11-09 08:30:59 +01:00
|
|
|
|
end:
|
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (modules.address)
|
|
|
|
|
ImpExFreePoolWithTag(modules.address, SYSTEM_MODULES_POOL);
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
return status;
|
2023-08-23 14:14:20 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-27 06:22:14 +02:00
|
|
|
|
STATIC
|
2023-10-03 18:03:55 +02:00
|
|
|
|
NTSTATUS
|
2024-08-01 06:21:53 +02:00
|
|
|
|
GetModuleInformationByName(
|
|
|
|
|
_Out_ PRTL_MODULE_EXTENDED_INFO ModuleInfo, _In_ LPCSTR ModuleName)
|
2023-08-22 19:32:25 +02:00
|
|
|
|
{
|
2024-04-13 10:23:14 +02:00
|
|
|
|
PAGED_CODE();
|
2023-10-09 20:19:51 +02:00
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(ModuleName != NULL);
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
LPCSTR driver_name = GetDriverName();
|
|
|
|
|
SYSTEM_MODULES modules = {0};
|
2024-04-13 10:23:14 +02:00
|
|
|
|
PRTL_MODULE_EXTENDED_INFO driver_info = NULL;
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
status = GetSystemModuleInformation(&modules);
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR("GetSystemModuleInformation failed with status %x", status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2024-05-11 14:54:58 +02:00
|
|
|
|
/* TODO: think this remains from testing, we only use this to find our
|
|
|
|
|
* driver anyway but should be fixed. */
|
2024-04-13 10:23:14 +02:00
|
|
|
|
driver_info = FindSystemModuleByName(driver_name, &modules);
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!driver_info) {
|
|
|
|
|
DEBUG_ERROR("FindSystemModuleByName failed with no status");
|
|
|
|
|
ImpExFreePoolWithTag(modules.address, SYSTEM_MODULES_POOL);
|
|
|
|
|
return STATUS_NOT_FOUND;
|
|
|
|
|
}
|
2023-11-09 12:11:02 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
ModuleInfo->FileNameOffset = driver_info->FileNameOffset;
|
2024-08-01 06:21:53 +02:00
|
|
|
|
ModuleInfo->ImageBase = driver_info->ImageBase;
|
|
|
|
|
ModuleInfo->ImageSize = driver_info->ImageSize;
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
IntCopyMemory(
|
|
|
|
|
ModuleInfo->FullPathName,
|
|
|
|
|
driver_info->FullPathName,
|
|
|
|
|
sizeof(ModuleInfo->FullPathName));
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (modules.address)
|
|
|
|
|
ImpExFreePoolWithTag(modules.address, SYSTEM_MODULES_POOL);
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
return status;
|
2023-08-28 17:00:52 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-20 20:30:45 +02:00
|
|
|
|
#define PE_TYPE_32_BIT 0x10b
|
|
|
|
|
|
2024-05-04 17:43:01 +02:00
|
|
|
|
FORCEINLINE
|
|
|
|
|
STATIC
|
|
|
|
|
BOOLEAN
|
|
|
|
|
IsSectionExecutable(_In_ PIMAGE_SECTION_HEADER Section)
|
|
|
|
|
{
|
|
|
|
|
return Section->Characteristics & IMAGE_SCN_MEM_EXECUTE ? TRUE : FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-05 15:20:35 +02:00
|
|
|
|
FORCEINLINE
|
|
|
|
|
STATIC
|
|
|
|
|
BOOLEAN
|
|
|
|
|
IsModuleAddressSafe(_In_ PVOID Base, _In_ BOOLEAN x86)
|
|
|
|
|
{
|
|
|
|
|
return !MmIsAddressValid(Base) && !x86 ? FALSE : TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FORCEINLINE
|
|
|
|
|
STATIC
|
|
|
|
|
UINT32
|
|
|
|
|
GetSectionTotalPacketSize(_In_ PIMAGE_SECTION_HEADER Section)
|
|
|
|
|
{
|
|
|
|
|
return Section->SizeOfRawData + sizeof(IMAGE_SECTION_HEADER);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FORCEINLINE
|
|
|
|
|
STATIC
|
|
|
|
|
VOID
|
2024-08-01 06:21:53 +02:00
|
|
|
|
InitIntegrityCheckHeader(
|
|
|
|
|
_Out_ PINTEGRITY_CHECK_HEADER Header,
|
|
|
|
|
_In_ UINT32 SectionCount,
|
|
|
|
|
_In_ UINT32 TotalSize)
|
2024-05-05 15:20:35 +02:00
|
|
|
|
{
|
2024-07-19 16:27:50 +02:00
|
|
|
|
Header->section_count = SectionCount;
|
2024-08-01 06:21:53 +02:00
|
|
|
|
Header->total_size = TotalSize + sizeof(INTEGRITY_CHECK_HEADER);
|
2024-05-05 15:20:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-27 06:22:14 +02:00
|
|
|
|
STATIC
|
2023-10-03 18:03:55 +02:00
|
|
|
|
NTSTATUS
|
2024-08-01 06:21:53 +02:00
|
|
|
|
StoreModuleExecutableRegionsInBuffer(
|
|
|
|
|
_Out_ PVOID* Buffer,
|
|
|
|
|
_In_ PVOID ModuleBase,
|
|
|
|
|
_In_ SIZE_T ModuleSize,
|
|
|
|
|
_Out_ PSIZE_T BytesWritten,
|
|
|
|
|
_In_ BOOLEAN IsModulex86)
|
2023-08-31 13:21:49 +02:00
|
|
|
|
{
|
2024-04-13 10:23:14 +02:00
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(Buffer != NULL);
|
|
|
|
|
NT_ASSERT(ModuleBase != NULL);
|
|
|
|
|
NT_ASSERT(BytesWritten != NULL);
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
UINT32 total_packet_size = 0;
|
|
|
|
|
UINT32 num_sections = 0;
|
|
|
|
|
UINT32 num_executable_sections = 0;
|
|
|
|
|
UINT64 buffer_base = 0;
|
|
|
|
|
UINT32 bytes_returned = 0;
|
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
PNT_HEADER_64 nt_header = NULL;
|
|
|
|
|
PIMAGE_SECTION_HEADER section = NULL;
|
|
|
|
|
MM_COPY_ADDRESS address = {0};
|
|
|
|
|
INTEGRITY_CHECK_HEADER header = {0};
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
|
|
|
|
if (!ModuleBase || !ModuleSize)
|
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
|
|
2024-05-05 15:20:35 +02:00
|
|
|
|
if (!IsModuleAddressSafe(ModuleBase, IsModulex86))
|
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
/*
|
|
|
|
|
* The reason we allocate a buffer to temporarily hold the section data
|
|
|
|
|
* is that we don't know the total size until after we iterate over the
|
|
|
|
|
* sections meaning we cant set Irp->IoStatus.Information to the size of
|
|
|
|
|
* our reponse until we enumerate and count all executable sections for
|
|
|
|
|
* the file.
|
|
|
|
|
*/
|
|
|
|
|
*BytesWritten = 0;
|
2024-08-01 06:21:53 +02:00
|
|
|
|
*Buffer = ImpExAllocatePool2(
|
|
|
|
|
POOL_FLAG_NON_PAGED,
|
|
|
|
|
ModuleSize + sizeof(INTEGRITY_CHECK_HEADER),
|
|
|
|
|
POOL_TAG_INTEGRITY);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
|
|
|
|
if (*Buffer == NULL)
|
|
|
|
|
return STATUS_MEMORY_NOT_ALLOCATED;
|
|
|
|
|
|
2024-06-09 09:22:22 +02:00
|
|
|
|
/* For context, when we are hashing x86 modules, MmIsAddressValid will
|
|
|
|
|
* return FALSE. Yet we still need protection for when an invalid address is
|
|
|
|
|
* passed for a non-x86 based image.*/
|
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
/*
|
|
|
|
|
* The IMAGE_DOS_HEADER.e_lfanew stores the offset of the
|
|
|
|
|
* IMAGE_NT_HEADER from the base of the image.
|
|
|
|
|
*/
|
2024-08-01 06:21:53 +02:00
|
|
|
|
nt_header = PeGetNtHeader(ModuleBase);
|
2024-05-05 15:20:35 +02:00
|
|
|
|
num_sections = GetSectionCount(nt_header);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The IMAGE_FIRST_SECTION macro takes in an IMAGE_NT_HEADER and returns
|
|
|
|
|
* the address of the first section of the PE file.
|
|
|
|
|
*/
|
2024-08-01 06:21:53 +02:00
|
|
|
|
section = IMAGE_FIRST_SECTION(nt_header);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
buffer_base = (UINT64)*Buffer + sizeof(INTEGRITY_CHECK_HEADER);
|
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
for (UINT32 index = 0; index < num_sections - 1; index++) {
|
2024-05-04 17:43:01 +02:00
|
|
|
|
if (!IsSectionExecutable(section)) {
|
2024-04-13 10:23:14 +02:00
|
|
|
|
section++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
address.VirtualAddress = section;
|
2024-08-01 06:21:53 +02:00
|
|
|
|
status = ImpMmCopyMemory(
|
|
|
|
|
(UINT64)buffer_base + total_packet_size,
|
|
|
|
|
address,
|
|
|
|
|
sizeof(IMAGE_SECTION_HEADER),
|
|
|
|
|
MM_COPY_MEMORY_VIRTUAL,
|
|
|
|
|
&bytes_returned);
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
ImpExFreePoolWithTag(*Buffer, POOL_TAG_INTEGRITY);
|
|
|
|
|
*Buffer = NULL;
|
|
|
|
|
return status;
|
2024-01-25 12:09:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
address.VirtualAddress = (UINT64)ModuleBase + section->PointerToRawData;
|
2024-08-01 06:21:53 +02:00
|
|
|
|
status = ImpMmCopyMemory(
|
|
|
|
|
(UINT64)buffer_base + total_packet_size +
|
|
|
|
|
sizeof(IMAGE_SECTION_HEADER),
|
|
|
|
|
address,
|
|
|
|
|
section->SizeOfRawData,
|
|
|
|
|
MM_COPY_MEMORY_VIRTUAL,
|
|
|
|
|
&bytes_returned);
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
ImpExFreePoolWithTag(*Buffer, POOL_TAG_INTEGRITY);
|
|
|
|
|
*Buffer = NULL;
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-05 15:20:35 +02:00
|
|
|
|
total_packet_size += GetSectionTotalPacketSize(section);
|
2024-05-04 17:43:01 +02:00
|
|
|
|
num_executable_sections++;
|
2024-04-13 10:23:14 +02:00
|
|
|
|
section++;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
InitIntegrityCheckHeader(
|
|
|
|
|
&header,
|
|
|
|
|
num_executable_sections,
|
|
|
|
|
total_packet_size);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
2024-07-22 12:43:09 +02:00
|
|
|
|
IntCopyMemory(*Buffer, &header, sizeof(INTEGRITY_CHECK_HEADER));
|
2024-04-13 10:23:14 +02:00
|
|
|
|
*BytesWritten = total_packet_size + sizeof(INTEGRITY_CHECK_HEADER);
|
|
|
|
|
return status;
|
2023-08-31 17:49:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-27 06:22:14 +02:00
|
|
|
|
STATIC
|
2023-10-03 18:03:55 +02:00
|
|
|
|
NTSTATUS
|
2024-08-01 06:21:53 +02:00
|
|
|
|
MapDiskImageIntoVirtualAddressSpace(
|
|
|
|
|
_Inout_ PHANDLE SectionHandle,
|
|
|
|
|
_Out_ PVOID* Section,
|
|
|
|
|
_In_ PUNICODE_STRING Path,
|
|
|
|
|
_Out_ PSIZE_T Size)
|
2023-08-28 17:00:52 +02:00
|
|
|
|
{
|
2024-04-13 10:23:14 +02:00
|
|
|
|
PAGED_CODE();
|
2023-10-09 20:19:51 +02:00
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(SectionHandle != NULL);
|
|
|
|
|
NT_ASSERT(Section != NULL);
|
|
|
|
|
NT_ASSERT(Path != NULL);
|
|
|
|
|
NT_ASSERT(Size != NULL);
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
HANDLE handle = NULL;
|
|
|
|
|
OBJECT_ATTRIBUTES oa = {0};
|
|
|
|
|
PIO_STATUS_BLOCK io = NULL;
|
|
|
|
|
UNICODE_STRING path = {0};
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
*Section = NULL;
|
2024-08-01 06:21:53 +02:00
|
|
|
|
*Size = 0;
|
2023-10-10 15:52:42 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
ImpRtlInitUnicodeString(&path, Path->Buffer);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
InitializeObjectAttributes(&oa, &path, OBJ_KERNEL_HANDLE, NULL, NULL);
|
2023-12-13 05:06:27 +01:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
status = ImpZwOpenFile(&handle, GENERIC_READ, &oa, &io, NULL, NULL);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR("ZwOpenFile failed with status %x", status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
oa.ObjectName = NULL;
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Its important that we set the SEC_IMAGE flag with the PAGE_READONLY
|
|
|
|
|
* flag as we are mapping an executable image.
|
|
|
|
|
*/
|
2024-08-01 06:21:53 +02:00
|
|
|
|
status = ImpZwCreateSection(
|
|
|
|
|
SectionHandle,
|
|
|
|
|
SECTION_ALL_ACCESS,
|
|
|
|
|
&oa,
|
|
|
|
|
NULL,
|
|
|
|
|
PAGE_READONLY,
|
|
|
|
|
SEC_IMAGE,
|
|
|
|
|
handle);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR("ZwCreateSection failed with status %x", status);
|
2024-07-19 16:27:50 +02:00
|
|
|
|
ImpZwClose(handle);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
*SectionHandle = NULL;
|
|
|
|
|
return status;
|
|
|
|
|
}
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR("ObReferenceObjectByHandle failed with status %x", status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* Mapping a section with the flag SEC_IMAGE (see function above) tells
|
|
|
|
|
* the os we are mapping an executable image. This then allows the OS to
|
|
|
|
|
* take care of parsing the PE header and dealing with all relocations
|
|
|
|
|
* for us, meaning the mapped image will be identical to the in memory
|
|
|
|
|
* image.
|
|
|
|
|
*/
|
2024-08-01 06:21:53 +02:00
|
|
|
|
status = ImpZwMapViewOfSection(
|
|
|
|
|
*SectionHandle,
|
|
|
|
|
ZwCurrentProcess(),
|
|
|
|
|
Section,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
Size,
|
|
|
|
|
ViewUnmap,
|
|
|
|
|
MEM_TOP_DOWN,
|
|
|
|
|
PAGE_READONLY);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR("ZwMapViewOfSection failed with status %x", status);
|
2024-07-19 16:27:50 +02:00
|
|
|
|
ImpZwClose(handle);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
ImpZwClose(*SectionHandle);
|
|
|
|
|
*SectionHandle = NULL;
|
2023-10-05 08:27:17 +02:00
|
|
|
|
return status;
|
2024-04-13 10:23:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
ImpZwClose(handle);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
return status;
|
2023-08-31 13:21:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-03 18:03:55 +02:00
|
|
|
|
NTSTATUS
|
2023-12-13 05:06:27 +01:00
|
|
|
|
RetrieveInMemoryModuleExecutableSections(_Inout_ PIRP Irp)
|
2023-08-31 18:42:38 +02:00
|
|
|
|
{
|
2024-04-13 10:23:14 +02:00
|
|
|
|
PAGED_CODE();
|
2023-10-10 15:52:42 +02:00
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(Irp != NULL);
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
SIZE_T bytes_written = NULL;
|
|
|
|
|
PVOID buffer = NULL;
|
|
|
|
|
RTL_MODULE_EXTENDED_INFO module_info = {0};
|
|
|
|
|
LPCSTR driver_name = GetDriverName();
|
2023-12-27 04:35:46 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
status = GetModuleInformationByName(&module_info, driver_name);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR("GetModuleInformationByName failed with status %x", status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
status = StoreModuleExecutableRegionsInBuffer(
|
|
|
|
|
&buffer,
|
|
|
|
|
module_info.ImageBase,
|
|
|
|
|
module_info.ImageSize,
|
|
|
|
|
&bytes_written,
|
|
|
|
|
FALSE);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
2024-08-01 06:21:53 +02:00
|
|
|
|
DEBUG_ERROR(
|
|
|
|
|
"StoreModuleExecutableRegionsInBuffe failed with status %x",
|
|
|
|
|
status);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
return status;
|
|
|
|
|
}
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
status = ValidateIrpOutputBuffer(Irp, bytes_written);
|
2023-11-09 08:30:59 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR("ValidateIrpOutputBuffer failed with status %x", status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
2023-11-09 08:30:59 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
Irp->IoStatus.Information = bytes_written;
|
2024-07-22 12:43:09 +02:00
|
|
|
|
IntCopyMemory(Irp->AssociatedIrp.SystemBuffer, buffer, bytes_written);
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-11-09 08:30:59 +01:00
|
|
|
|
end:
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (buffer)
|
|
|
|
|
ImpExFreePoolWithTag(buffer, POOL_TAG_INTEGRITY);
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
return status;
|
2023-09-03 19:33:27 +02:00
|
|
|
|
}
|
2023-12-25 16:54:35 +01:00
|
|
|
|
#define SMBIOS_TABLE 'RSMB'
|
|
|
|
|
#define NULL_TERMINATOR '\0'
|
2023-09-04 15:36:26 +02:00
|
|
|
|
/*
|
2023-12-13 05:06:27 +01:00
|
|
|
|
* From line 727 in the SMBIOS Specification:
|
|
|
|
|
*
|
2024-04-13 06:40:51 +02:00
|
|
|
|
* 727 <EFBFBD> Each structure shall be terminated by a double-null (0000h), either
|
|
|
|
|
* directly following the 728 formatted area (if no strings are present) or
|
|
|
|
|
* directly following the last string. This includes 729 system- and
|
|
|
|
|
* OEM-specific structures and allows upper-level software to easily traverse
|
|
|
|
|
* the 730 structure table. (See structure-termination examples later in this
|
|
|
|
|
* clause.)
|
2023-12-13 05:06:27 +01:00
|
|
|
|
*
|
2024-04-13 06:40:51 +02:00
|
|
|
|
* TLDR is that if the first two characters proceeding the structure are null
|
|
|
|
|
* terminators, then there are no strings, otherwise to find the end of the
|
|
|
|
|
* string section simply iterate until there is a double null terminator.
|
2023-12-13 05:06:27 +01:00
|
|
|
|
*
|
2024-04-13 06:40:51 +02:00
|
|
|
|
* source:
|
|
|
|
|
* https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf
|
2023-12-13 05:06:27 +01:00
|
|
|
|
*/
|
2023-09-27 06:22:14 +02:00
|
|
|
|
STATIC
|
2023-10-03 18:03:55 +02:00
|
|
|
|
VOID
|
2023-12-13 05:06:27 +01:00
|
|
|
|
GetNextSMBIOSStructureInTable(_Inout_ PSMBIOS_TABLE_HEADER* CurrentStructure)
|
2023-09-04 15:36:26 +02:00
|
|
|
|
{
|
2024-04-13 10:23:14 +02:00
|
|
|
|
PAGED_CODE();
|
2023-12-13 05:06:27 +01:00
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(CurrentStructure != NULL);
|
|
|
|
|
|
|
|
|
|
PCHAR string_section_start = NULL;
|
|
|
|
|
PCHAR current_char_in_strings = NULL;
|
|
|
|
|
PCHAR next_char_in_strings = NULL;
|
|
|
|
|
|
|
|
|
|
string_section_start =
|
2024-04-13 10:23:14 +02:00
|
|
|
|
(PCHAR)((UINT64)*CurrentStructure + (*CurrentStructure)->Length);
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
current_char_in_strings = string_section_start;
|
|
|
|
|
next_char_in_strings = string_section_start + 1;
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
for (;;) {
|
|
|
|
|
if (*current_char_in_strings == NULL_TERMINATOR &&
|
|
|
|
|
*next_char_in_strings == NULL_TERMINATOR) {
|
|
|
|
|
*CurrentStructure =
|
|
|
|
|
(PSMBIOS_TABLE_HEADER)(next_char_in_strings + 1);
|
|
|
|
|
return;
|
2023-09-05 19:20:21 +02:00
|
|
|
|
}
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
|
|
|
|
current_char_in_strings++;
|
|
|
|
|
next_char_in_strings++;
|
|
|
|
|
}
|
2023-09-04 15:36:26 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-04 17:00:36 +02:00
|
|
|
|
/*
|
2024-04-13 06:40:51 +02:00
|
|
|
|
* Remember that the string index does not start from the beginning of the
|
|
|
|
|
* struct. For example, lets take RAW_SMBIOS_TABLE_02: the first string is NOT
|
|
|
|
|
* "Type" at index 0, the first string is Manufacturer. So if we want to find
|
|
|
|
|
* the SerialNumber, the string index would be 4, as the previous 3 values
|
|
|
|
|
* (after the header) are all strings. So remember, the index is into the number
|
|
|
|
|
* of strings that exist for the given table, NOT the size of the structure or a
|
|
|
|
|
* values index into the struct.
|
2023-12-13 05:06:27 +01:00
|
|
|
|
*
|
2024-04-13 06:40:51 +02:00
|
|
|
|
* Here we count the number of strings by incrementing the string_count each
|
|
|
|
|
* time we pass a null terminator so we know when we're at the beginning of the
|
|
|
|
|
* target string.
|
2023-12-13 05:06:27 +01:00
|
|
|
|
*/
|
2023-09-27 06:22:14 +02:00
|
|
|
|
STATIC
|
2023-10-03 18:03:55 +02:00
|
|
|
|
NTSTATUS
|
2024-08-01 06:21:53 +02:00
|
|
|
|
GetStringAtIndexFromSMBIOSTable(
|
|
|
|
|
_In_ PSMBIOS_TABLE_HEADER Table,
|
|
|
|
|
_In_ UINT32 Index,
|
|
|
|
|
_In_ PVOID Buffer,
|
|
|
|
|
_In_ SIZE_T BufferSize)
|
2023-09-04 15:36:26 +02:00
|
|
|
|
{
|
2024-04-13 10:23:14 +02:00
|
|
|
|
PAGED_CODE();
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(Table != NULL);
|
|
|
|
|
NT_ASSERT(Buffer != NULL);
|
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
UINT32 current_string_char_index = 0;
|
2024-08-01 06:21:53 +02:00
|
|
|
|
UINT32 string_count = 0;
|
|
|
|
|
PCHAR current_string_char = (PCHAR)((UINT64)Table + Table->Length);
|
|
|
|
|
PCHAR next_string_char = current_string_char + 1;
|
2024-08-04 08:30:31 +02:00
|
|
|
|
UINT64 dest = 0;
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
for (;;) {
|
|
|
|
|
if (*current_string_char == NULL_TERMINATOR &&
|
|
|
|
|
*next_string_char == NULL_TERMINATOR)
|
|
|
|
|
return STATUS_NOT_FOUND;
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (current_string_char_index >= BufferSize)
|
|
|
|
|
return STATUS_BUFFER_TOO_SMALL;
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (string_count + 1 == Index) {
|
|
|
|
|
if (*current_string_char == NULL_TERMINATOR)
|
|
|
|
|
return STATUS_SUCCESS;
|
2023-12-13 05:06:27 +01:00
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
dest = (UINT64)Buffer + current_string_char_index;
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2024-07-22 12:43:09 +02:00
|
|
|
|
IntCopyMemory(dest, current_string_char, sizeof(CHAR));
|
2024-04-13 10:23:14 +02:00
|
|
|
|
current_string_char_index++;
|
|
|
|
|
goto increment;
|
|
|
|
|
}
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (*current_string_char == NULL_TERMINATOR) {
|
|
|
|
|
current_string_char_index = 0;
|
|
|
|
|
string_count++;
|
2023-10-05 08:27:17 +02:00
|
|
|
|
}
|
2023-10-03 18:03:55 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
increment:
|
|
|
|
|
current_string_char++;
|
|
|
|
|
next_string_char++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return STATUS_NOT_FOUND;
|
2023-09-04 15:36:26 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-04 17:43:01 +02:00
|
|
|
|
FORCEINLINE
|
|
|
|
|
STATIC
|
|
|
|
|
PRAW_SMBIOS_DATA
|
|
|
|
|
GetRawSmbiosData(_In_ PVOID FirmwareTable)
|
|
|
|
|
{
|
|
|
|
|
return (PRAW_SMBIOS_DATA)FirmwareTable;
|
|
|
|
|
}
|
2023-12-25 16:54:35 +01:00
|
|
|
|
|
2024-05-04 17:43:01 +02:00
|
|
|
|
FORCEINLINE
|
|
|
|
|
STATIC
|
|
|
|
|
PSMBIOS_TABLE_HEADER
|
|
|
|
|
GetSmbiosTableHeader(_In_ PRAW_SMBIOS_DATA Data)
|
|
|
|
|
{
|
|
|
|
|
return (PSMBIOS_TABLE_HEADER)(&Data->SMBIOSTableData[0]);
|
|
|
|
|
}
|
2023-12-25 16:54:35 +01:00
|
|
|
|
|
2023-10-03 18:03:55 +02:00
|
|
|
|
NTSTATUS
|
2024-08-01 06:21:53 +02:00
|
|
|
|
ParseSMBIOSTable(
|
|
|
|
|
_Out_ PVOID Buffer,
|
|
|
|
|
_In_ SIZE_T BufferSize,
|
|
|
|
|
_In_ SMBIOS_TABLE_INDEX TableIndex,
|
|
|
|
|
_In_ ULONG TableSubIndex)
|
2023-09-04 15:36:26 +02:00
|
|
|
|
{
|
2024-04-13 10:23:14 +02:00
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(Buffer != NULL);
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
PVOID buffer = NULL;
|
|
|
|
|
ULONG buffer_size = 0;
|
|
|
|
|
ULONG bytes_copied = 0;
|
|
|
|
|
PRAW_SMBIOS_DATA smbios_data = NULL;
|
|
|
|
|
PSMBIOS_TABLE_HEADER header = NULL;
|
|
|
|
|
PRAW_SMBIOS_TABLE_01 baseboard = NULL;
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
status =
|
|
|
|
|
ImpExGetSystemFirmwareTable(SMBIOS_TABLE, 0, NULL, 0, &buffer_size);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Because we pass a null buffer here, the NTSTATUS result will be a
|
|
|
|
|
* BUFFER_TOO_SMALL error, so to validate this function call we check
|
|
|
|
|
* the return bytes returned (which indicate required buffer size) is
|
|
|
|
|
* above 0.
|
|
|
|
|
*/
|
2024-07-19 16:27:50 +02:00
|
|
|
|
if (buffer_size == NULL) {
|
2024-04-13 10:23:14 +02:00
|
|
|
|
DEBUG_ERROR(
|
|
|
|
|
"ExGetSystemFirmwareTable call 1 failed to get required buffer size.");
|
|
|
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
buffer = ImpExAllocatePool2(
|
|
|
|
|
POOL_FLAG_NON_PAGED,
|
|
|
|
|
buffer_size,
|
|
|
|
|
POOL_TAG_INTEGRITY);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
if (!buffer)
|
2024-04-13 10:23:14 +02:00
|
|
|
|
return STATUS_MEMORY_NOT_ALLOCATED;
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
status = ImpExGetSystemFirmwareTable(
|
|
|
|
|
SMBIOS_TABLE,
|
|
|
|
|
NULL,
|
|
|
|
|
buffer,
|
|
|
|
|
buffer_size,
|
|
|
|
|
&bytes_copied);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
2024-08-01 06:21:53 +02:00
|
|
|
|
DEBUG_ERROR(
|
|
|
|
|
"ExGetSystemFirmwareTable call 2 failed with status %x",
|
|
|
|
|
status);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
smbios_data = GetRawSmbiosData(buffer);
|
2024-08-01 06:21:53 +02:00
|
|
|
|
header = GetSmbiosTableHeader(smbios_data);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The System Information table is equal to Type == 2 and contains the
|
|
|
|
|
* serial number of the motherboard in the computer among various other
|
|
|
|
|
* things.
|
|
|
|
|
*
|
|
|
|
|
* source:
|
|
|
|
|
* https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf
|
|
|
|
|
* line 823
|
|
|
|
|
*/
|
2024-07-19 16:27:50 +02:00
|
|
|
|
while (header->Type != TableIndex)
|
|
|
|
|
GetNextSMBIOSStructureInTable(&header);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
status = GetStringAtIndexFromSMBIOSTable(
|
|
|
|
|
header,
|
|
|
|
|
TableSubIndex,
|
|
|
|
|
Buffer,
|
|
|
|
|
BufferSize);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
2024-08-01 06:21:53 +02:00
|
|
|
|
DEBUG_ERROR(
|
|
|
|
|
"GetStringAtIndexFromSMBIOSTable failed with status %x",
|
|
|
|
|
status);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
goto end;
|
|
|
|
|
}
|
2023-09-04 15:36:26 +02:00
|
|
|
|
|
|
|
|
|
end:
|
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
if (buffer)
|
|
|
|
|
ImpExFreePoolWithTag(buffer, POOL_TAG_INTEGRITY);
|
2023-09-04 15:36:26 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
return status;
|
2023-09-04 15:36:26 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-02 23:29:23 +01:00
|
|
|
|
STATIC
|
|
|
|
|
NTSTATUS
|
2024-08-01 06:21:53 +02:00
|
|
|
|
ComputeHashOfSections(
|
|
|
|
|
_In_ PIMAGE_SECTION_HEADER DiskSection,
|
|
|
|
|
_In_ PIMAGE_SECTION_HEADER MemorySection,
|
|
|
|
|
_Out_ PVOID* DiskHash,
|
|
|
|
|
_Out_ PULONG DiskHashSize,
|
|
|
|
|
_Out_ PVOID* MemoryHash,
|
|
|
|
|
_Out_ PULONG MemoryHashSize)
|
2024-01-02 23:29:23 +01:00
|
|
|
|
{
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(DiskSection != NULL);
|
|
|
|
|
NT_ASSERT(MemorySection != NULL);
|
|
|
|
|
NT_ASSERT(DiskHash != NULL);
|
|
|
|
|
NT_ASSERT(DiskHashSize != NULL);
|
|
|
|
|
NT_ASSERT(MemoryHash != NULL);
|
|
|
|
|
NT_ASSERT(MemoryHashSize != NULL);
|
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
2024-01-02 23:29:23 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (DiskSection->SizeOfRawData != MemorySection->SizeOfRawData) {
|
|
|
|
|
DEBUG_WARNING("Executable section sizes differ between images.");
|
|
|
|
|
return STATUS_INVALID_BUFFER_SIZE;
|
|
|
|
|
}
|
2024-01-02 23:29:23 +01:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
status = CryptHashBuffer_sha256(
|
|
|
|
|
(UINT64)DiskSection + sizeof(IMAGE_SECTION_HEADER),
|
|
|
|
|
DiskSection->SizeOfRawData,
|
|
|
|
|
DiskHash,
|
|
|
|
|
DiskHashSize);
|
2024-01-02 23:29:23 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
2024-07-19 16:27:50 +02:00
|
|
|
|
DEBUG_ERROR("CryptHashBuffer_sha256 failed with status %x", status);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
return status;
|
|
|
|
|
}
|
2024-01-02 23:29:23 +01:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
status = CryptHashBuffer_sha256(
|
|
|
|
|
(UINT64)MemorySection + sizeof(IMAGE_SECTION_HEADER),
|
|
|
|
|
MemorySection->SizeOfRawData,
|
|
|
|
|
MemoryHash,
|
|
|
|
|
MemoryHashSize);
|
2024-01-02 23:29:23 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
2024-07-19 16:27:50 +02:00
|
|
|
|
DEBUG_ERROR("CryptHashBuffer_sha256 2 failed with status %x", status);
|
2024-01-02 23:29:23 +01:00
|
|
|
|
return status;
|
2024-04-13 10:23:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return status;
|
2024-01-02 23:29:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-04 17:43:01 +02:00
|
|
|
|
FORCEINLINE
|
2024-01-02 23:29:23 +01:00
|
|
|
|
STATIC
|
|
|
|
|
BOOLEAN
|
|
|
|
|
CompareHashes(_In_ PVOID Hash1, _In_ PVOID Hash2, _In_ UINT32 Length)
|
|
|
|
|
{
|
2024-07-22 12:43:09 +02:00
|
|
|
|
return IntCompareMemory(Hash1, Hash2, Length) == Length ? TRUE : FALSE;
|
2024-01-02 23:29:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
2024-04-13 10:06:59 +02:00
|
|
|
|
STATIC
|
|
|
|
|
VOID
|
|
|
|
|
ReportInvalidProcessModule(_In_ PPROCESS_MODULE_INFORMATION Module)
|
|
|
|
|
{
|
2024-08-01 06:21:53 +02:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
UINT32 len = 0;
|
2024-07-19 16:27:50 +02:00
|
|
|
|
PPROCESS_MODULE_VALIDATION_REPORT report = NULL;
|
|
|
|
|
|
|
|
|
|
len = CryptRequestRequiredBufferLength(
|
2024-05-11 14:54:58 +02:00
|
|
|
|
sizeof(PROCESS_MODULE_VALIDATION_REPORT));
|
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
report = ImpExAllocatePool2(POOL_FLAG_NON_PAGED, len, REPORT_POOL_TAG);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
|
|
|
|
if (!report)
|
|
|
|
|
return;
|
|
|
|
|
|
2024-05-11 14:54:58 +02:00
|
|
|
|
INIT_REPORT_PACKET(report, REPORT_INVALID_PROCESS_MODULE, 0);
|
2024-05-05 12:42:22 +02:00
|
|
|
|
|
|
|
|
|
report->image_base = Module->module_base;
|
|
|
|
|
report->image_size = Module->module_size;
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
IntCopyMemory(
|
|
|
|
|
report->module_path,
|
|
|
|
|
Module->module_path,
|
|
|
|
|
sizeof(report->module_path));
|
2024-07-19 16:27:50 +02:00
|
|
|
|
|
|
|
|
|
status = CryptEncryptBuffer(report, len);
|
2024-05-11 14:54:58 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR("CryptEncryptBuffer: %lx", status);
|
2024-07-19 16:27:50 +02:00
|
|
|
|
ImpExFreePoolWithTag(report, len);
|
2024-05-11 14:54:58 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
IrpQueueSchedulePacket(report, len);
|
2024-04-13 10:06:59 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-04 17:56:28 +02:00
|
|
|
|
/*
|
2024-04-13 06:40:51 +02:00
|
|
|
|
* Because the infrastructure has already been setup to validate modules in the
|
|
|
|
|
* driver, that is how I will validate the usermode modules as well. Another
|
|
|
|
|
* reason is that the win32 api makes it very easy to take a snapshot of the
|
|
|
|
|
* modules and enumerate them with easy to use functions and macros.
|
2023-12-13 05:06:27 +01:00
|
|
|
|
*
|
|
|
|
|
* 1. Take a snapshot of the modules in the process from our dll
|
2024-04-13 06:40:51 +02:00
|
|
|
|
* 2. pass the image base, image size and the image path to our driver via an
|
|
|
|
|
* IRP
|
|
|
|
|
* 3. from our driver, to first verify the in memory module, attach to our
|
|
|
|
|
* protected process and using the base + size simply use
|
|
|
|
|
* StoreModuleExecutableRegionsInBuffer()
|
|
|
|
|
* 4. Next we use the path to map the image on disk into memory, and pass the
|
|
|
|
|
* image to StoreModuleExecutableRegionsInBuffer() just as we did before.
|
|
|
|
|
* 5. With the 2 buffers that contain both images executable regions, we hash
|
|
|
|
|
* them and compare for anomalies.
|
2023-12-13 05:06:27 +01:00
|
|
|
|
*
|
2024-04-13 06:40:51 +02:00
|
|
|
|
* note: Its important to realise that since these are user mode modules, they
|
|
|
|
|
* are often hooked by various legitimate programs - such as discord, nvidia
|
|
|
|
|
* etc. So this needs to be rethinked.
|
2023-12-13 05:06:27 +01:00
|
|
|
|
*/
|
2023-10-03 18:03:55 +02:00
|
|
|
|
NTSTATUS
|
2023-12-13 05:06:27 +01:00
|
|
|
|
ValidateProcessLoadedModule(_Inout_ PIRP Irp)
|
2023-09-04 17:00:36 +02:00
|
|
|
|
{
|
2024-04-13 10:23:14 +02:00
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(Irp != NULL);
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
2024-04-13 10:23:14 +02:00
|
|
|
|
PROCESS_MODULE_VALIDATION_RESULT validation_result = {0};
|
2024-08-01 06:21:53 +02:00
|
|
|
|
PPROCESS_MODULE_INFORMATION module_info = NULL;
|
|
|
|
|
PKPROCESS process = NULL;
|
|
|
|
|
KAPC_STATE apc_state = {0};
|
|
|
|
|
PVAL_INTEGRITY_HEADER memory_buffer = NULL;
|
|
|
|
|
PVAL_INTEGRITY_HEADER disk_buffer = NULL;
|
|
|
|
|
PVOID memory_hash = NULL;
|
|
|
|
|
PVOID disk_hash = NULL;
|
|
|
|
|
ULONG memory_hash_size = 0;
|
|
|
|
|
ULONG disk_hash_size = 0;
|
|
|
|
|
SIZE_T bytes_written = 0;
|
|
|
|
|
UNICODE_STRING module_path = {0};
|
|
|
|
|
HANDLE section_handle = NULL;
|
|
|
|
|
PVOID section = NULL;
|
|
|
|
|
ULONG section_size = 0;
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
|
|
|
|
status = ValidateIrpInputBuffer(Irp, sizeof(PROCESS_MODULE_INFORMATION));
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR("ValidateIrpInputBuffer failed with status %x", status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
module_info = (PPROCESS_MODULE_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
SessionGetProcess(&process);
|
|
|
|
|
ImpRtlInitUnicodeString(&module_path, &module_info->module_path);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
/*
|
|
|
|
|
* Attach because the offsets given are from the process' context.
|
|
|
|
|
*/
|
|
|
|
|
ImpKeStackAttachProcess(process, &apc_state);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
status = StoreModuleExecutableRegionsInBuffer(
|
|
|
|
|
&memory_buffer,
|
|
|
|
|
module_info->module_base,
|
|
|
|
|
module_info->module_size,
|
|
|
|
|
&bytes_written,
|
|
|
|
|
FALSE);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
ImpKeUnstackDetachProcess(&apc_state);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR(
|
|
|
|
|
"StoreModuleExecutableRegionsInBuffer failed with status %x",
|
|
|
|
|
status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
status = MapDiskImageIntoVirtualAddressSpace(
|
|
|
|
|
§ion_handle,
|
|
|
|
|
§ion,
|
|
|
|
|
&module_path,
|
|
|
|
|
§ion_size);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
2024-08-01 06:21:53 +02:00
|
|
|
|
DEBUG_ERROR(
|
|
|
|
|
"MapDiskImageIntoVirtualAddressSpace failed with status %x",
|
|
|
|
|
status);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
status = StoreModuleExecutableRegionsInBuffer(
|
|
|
|
|
&disk_buffer,
|
|
|
|
|
section,
|
|
|
|
|
section_size,
|
|
|
|
|
&bytes_written,
|
|
|
|
|
FALSE);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR(
|
|
|
|
|
"StoreModuleExecutableRegionsInbuffer 2 failed with status %x",
|
|
|
|
|
status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
status = ComputeHashOfSections(
|
|
|
|
|
&memory_buffer->section_header,
|
|
|
|
|
&disk_buffer->section_header,
|
|
|
|
|
&disk_hash,
|
|
|
|
|
&disk_hash_size,
|
|
|
|
|
&memory_hash,
|
|
|
|
|
&memory_hash_size);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR("ComputeHashOfSections failed with status %x", status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!CompareHashes(disk_hash, memory_hash, memory_hash_size))
|
|
|
|
|
ReportInvalidProcessModule(module_info);
|
2023-09-04 17:00:36 +02:00
|
|
|
|
|
|
|
|
|
end:
|
2023-12-13 05:06:27 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (section_handle)
|
|
|
|
|
ImpZwClose(section_handle);
|
2023-09-05 18:04:06 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (section)
|
|
|
|
|
ImpZwUnmapViewOfSection(ZwCurrentProcess(), section);
|
2023-09-04 17:00:36 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (memory_buffer)
|
|
|
|
|
ImpExFreePoolWithTag(memory_buffer, POOL_TAG_INTEGRITY);
|
2023-09-05 11:16:32 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (memory_hash)
|
|
|
|
|
ImpExFreePoolWithTag(memory_hash, POOL_TAG_INTEGRITY);
|
2023-09-04 17:00:36 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (disk_buffer)
|
|
|
|
|
ImpExFreePoolWithTag(disk_buffer, POOL_TAG_INTEGRITY);
|
2023-09-05 18:04:06 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (disk_hash)
|
|
|
|
|
ImpExFreePoolWithTag(disk_hash, POOL_TAG_INTEGRITY);
|
2023-09-05 18:04:06 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
return status;
|
2023-09-06 17:33:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-09 09:22:22 +02:00
|
|
|
|
NTSTATUS
|
2024-08-01 06:21:53 +02:00
|
|
|
|
HashUserModule(
|
|
|
|
|
_In_ PPROCESS_MAP_MODULE_ENTRY Entry,
|
|
|
|
|
_Out_ PVOID OutBuffer,
|
|
|
|
|
_In_ UINT32 OutBufferSize)
|
2024-06-09 09:22:22 +02:00
|
|
|
|
{
|
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(Entry != NULL);
|
|
|
|
|
NT_ASSERT(OutBuffer != NULL);
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
KAPC_STATE apc_state = {0};
|
|
|
|
|
PVAL_INTEGRITY_HEADER memory_buffer = NULL;
|
|
|
|
|
PVOID memory_hash = NULL;
|
|
|
|
|
ULONG memory_hash_size = 0;
|
|
|
|
|
SIZE_T bytes_written = 0;
|
|
|
|
|
PACTIVE_SESSION session = GetActiveSession();
|
2024-06-09 09:22:22 +02:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Attach because the offsets given are from the process' context.
|
|
|
|
|
*/
|
|
|
|
|
ImpKeStackAttachProcess(session->process, &apc_state);
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
status = StoreModuleExecutableRegionsInBuffer(
|
|
|
|
|
&memory_buffer,
|
|
|
|
|
Entry->base,
|
|
|
|
|
Entry->size,
|
|
|
|
|
&bytes_written,
|
|
|
|
|
FALSE);
|
2024-06-09 09:22:22 +02:00
|
|
|
|
|
|
|
|
|
ImpKeUnstackDetachProcess(&apc_state);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR(
|
|
|
|
|
"StoreModuleExecutableRegionsInBuffer failed with status %x",
|
|
|
|
|
status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
status = CryptHashBuffer_sha256(
|
|
|
|
|
memory_buffer->section_base,
|
|
|
|
|
memory_buffer->section_header.SizeOfRawData,
|
|
|
|
|
&memory_hash,
|
|
|
|
|
&memory_hash_size);
|
2024-06-09 09:22:22 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
2024-07-19 16:27:50 +02:00
|
|
|
|
DEBUG_ERROR("CryptHashBuffer_sha256 failed with status %x", status);
|
2024-06-09 09:22:22 +02:00
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (OutBufferSize > memory_hash_size) {
|
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-22 12:43:09 +02:00
|
|
|
|
IntCopyMemory(OutBuffer, memory_hash, memory_hash_size);
|
2024-06-09 09:22:22 +02:00
|
|
|
|
|
|
|
|
|
end:
|
|
|
|
|
|
|
|
|
|
if (memory_buffer)
|
|
|
|
|
ImpExFreePoolWithTag(memory_buffer, POOL_TAG_INTEGRITY);
|
|
|
|
|
|
|
|
|
|
if (memory_hash)
|
|
|
|
|
ImpExFreePoolWithTag(memory_hash, POOL_TAG_INTEGRITY);
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-04 17:43:01 +02:00
|
|
|
|
FORCEINLINE
|
|
|
|
|
STATIC
|
|
|
|
|
PCHAR
|
|
|
|
|
GetStorageDescriptorSerialNumber(_In_ PSTORAGE_DEVICE_DESCRIPTOR Descriptor)
|
|
|
|
|
{
|
|
|
|
|
return (PCHAR)((UINT64)Descriptor + Descriptor->SerialNumberOffset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FORCEINLINE
|
|
|
|
|
STATIC
|
|
|
|
|
SIZE_T
|
|
|
|
|
GetStorageDescriptorSerialLength(_In_ PCHAR SerialNumber)
|
|
|
|
|
{
|
2024-07-22 12:43:09 +02:00
|
|
|
|
return IntStringLength(SerialNumber, DEVICE_DRIVE_0_SERIAL_CODE_LENGTH) + 1;
|
2024-05-04 17:43:01 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FORCEINLINE
|
|
|
|
|
STATIC
|
|
|
|
|
VOID
|
2024-08-01 06:21:53 +02:00
|
|
|
|
InitStorageProperties(
|
|
|
|
|
_Out_ PSTORAGE_PROPERTY_QUERY Query,
|
|
|
|
|
_In_ STORAGE_PROPERTY_ID PropertyId,
|
|
|
|
|
_In_ STORAGE_QUERY_TYPE QueryType)
|
2024-05-04 17:43:01 +02:00
|
|
|
|
{
|
|
|
|
|
Query->PropertyId = PropertyId;
|
2024-08-01 06:21:53 +02:00
|
|
|
|
Query->QueryType = QueryType;
|
2024-05-04 17:43:01 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-06 17:44:57 +02:00
|
|
|
|
/*
|
2024-04-13 06:40:51 +02:00
|
|
|
|
* TODO: Query PhysicalDrive%n to get the serial numbers for all harddrives, can
|
|
|
|
|
* use the command "wmic diskdrive" check in console.
|
2023-12-13 05:06:27 +01:00
|
|
|
|
*/
|
2023-10-03 18:03:55 +02:00
|
|
|
|
NTSTATUS
|
2024-08-01 06:21:53 +02:00
|
|
|
|
GetHardDiskDriveSerialNumber(
|
|
|
|
|
_Inout_ PVOID ConfigDrive0Serial, _In_ SIZE_T ConfigDrive0MaxSize)
|
2023-09-06 17:33:08 +02:00
|
|
|
|
{
|
2024-04-13 10:23:14 +02:00
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(ConfigDrive0Serial != NULL);
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
HANDLE handle = NULL;
|
|
|
|
|
OBJECT_ATTRIBUTES attributes = {0};
|
|
|
|
|
IO_STATUS_BLOCK status_block = {0};
|
|
|
|
|
STORAGE_PROPERTY_QUERY query = {0};
|
|
|
|
|
STORAGE_DESCRIPTOR_HEADER header = {0};
|
|
|
|
|
PSTORAGE_DEVICE_DESCRIPTOR descriptor = NULL;
|
|
|
|
|
UNICODE_STRING path = {0};
|
|
|
|
|
PCHAR serial_number = NULL;
|
|
|
|
|
SIZE_T serial_length = 0;
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
ImpRtlInitUnicodeString(&path, L"\\DosDevices\\PhysicalDrive0");
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* No need to use the flag OBJ_FORCE_ACCESS_CHECK since we arent passing
|
|
|
|
|
* a handle given to us from usermode.
|
|
|
|
|
*/
|
2024-08-01 06:21:53 +02:00
|
|
|
|
InitializeObjectAttributes(
|
|
|
|
|
&attributes,
|
|
|
|
|
&path,
|
|
|
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
status = ImpZwOpenFile(
|
|
|
|
|
&handle,
|
|
|
|
|
GENERIC_READ,
|
|
|
|
|
&attributes,
|
|
|
|
|
&status_block,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
2024-08-01 06:21:53 +02:00
|
|
|
|
DEBUG_ERROR(
|
|
|
|
|
"ZwOpenFile on PhysicalDrive0 failed with status %x",
|
|
|
|
|
status);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-04 17:43:01 +02:00
|
|
|
|
InitStorageProperties(&query, StorageDeviceProperty, PropertyStandardQuery);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
status = ImpZwDeviceIoControlFile(
|
|
|
|
|
handle,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
&status_block,
|
|
|
|
|
IOCTL_STORAGE_QUERY_PROPERTY,
|
|
|
|
|
&query,
|
|
|
|
|
sizeof(STORAGE_PROPERTY_QUERY),
|
|
|
|
|
&header,
|
|
|
|
|
sizeof(STORAGE_DESCRIPTOR_HEADER));
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
2024-08-01 06:21:53 +02:00
|
|
|
|
DEBUG_ERROR(
|
|
|
|
|
"ZwDeviceIoControlFile first call failed with status %x",
|
|
|
|
|
status);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
descriptor = ImpExAllocatePool2(
|
|
|
|
|
POOL_FLAG_NON_PAGED,
|
|
|
|
|
header.Size,
|
|
|
|
|
POOL_TAG_INTEGRITY);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
if (!descriptor) {
|
2024-04-13 10:23:14 +02:00
|
|
|
|
status = STATUS_MEMORY_NOT_ALLOCATED;
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
status = ImpZwDeviceIoControlFile(
|
|
|
|
|
handle,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
&status_block,
|
|
|
|
|
IOCTL_STORAGE_QUERY_PROPERTY,
|
|
|
|
|
&query,
|
|
|
|
|
sizeof(STORAGE_PROPERTY_QUERY),
|
|
|
|
|
descriptor,
|
|
|
|
|
header.Size);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
2024-08-01 06:21:53 +02:00
|
|
|
|
DEBUG_ERROR(
|
|
|
|
|
"ZwDeviceIoControlFile second call failed with status %x",
|
|
|
|
|
status);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
goto end;
|
|
|
|
|
}
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
if (!descriptor->SerialNumberOffset)
|
2024-04-13 10:23:14 +02:00
|
|
|
|
goto end;
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
serial_number = GetStorageDescriptorSerialNumber(descriptor);
|
2024-05-04 17:43:01 +02:00
|
|
|
|
serial_length = GetStorageDescriptorSerialLength(serial_number);
|
2023-09-06 17:33:08 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (serial_length > ConfigDrive0MaxSize) {
|
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
2024-04-13 10:06:59 +02:00
|
|
|
|
|
2024-07-22 12:43:09 +02:00
|
|
|
|
IntCopyMemory(ConfigDrive0Serial, serial_number, serial_length);
|
2024-05-04 17:43:01 +02:00
|
|
|
|
|
2023-09-06 17:33:08 +02:00
|
|
|
|
end:
|
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (handle)
|
|
|
|
|
ImpZwClose(handle);
|
2023-09-06 17:33:08 +02:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
if (descriptor)
|
|
|
|
|
ImpExFreePoolWithTag(descriptor, POOL_TAG_INTEGRITY);
|
2023-09-06 17:33:08 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
return status;
|
2023-09-28 09:15:47 +02:00
|
|
|
|
}
|
2023-10-03 18:03:55 +02:00
|
|
|
|
PVOID
|
2024-08-01 06:21:53 +02:00
|
|
|
|
ScanForSignature(
|
|
|
|
|
_In_ PVOID BaseAddress,
|
|
|
|
|
_In_ SIZE_T MaxLength,
|
|
|
|
|
_In_ LPCSTR Signature,
|
|
|
|
|
_In_ SIZE_T SignatureLength)
|
2023-10-02 16:31:30 +02:00
|
|
|
|
{
|
2024-04-13 10:23:14 +02:00
|
|
|
|
PAGED_CODE();
|
2023-10-10 15:52:42 +02:00
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(BaseAddress != NULL);
|
|
|
|
|
NT_ASSERT(Signature != NULL);
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
CHAR current_char = 0;
|
2024-04-13 10:23:14 +02:00
|
|
|
|
CHAR current_sig_char = 0;
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
for (UINT32 index = 0; index < MaxLength; index++) {
|
|
|
|
|
for (UINT32 sig = 0; sig < SignatureLength + 1; sig++) {
|
2024-08-01 06:21:53 +02:00
|
|
|
|
current_char = *(PCHAR)((UINT64)BaseAddress + index + sig);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
current_sig_char = Signature[sig];
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (sig == SignatureLength)
|
|
|
|
|
return (PVOID)((UINT64)BaseAddress + index);
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (current_char != current_sig_char)
|
|
|
|
|
break;
|
2023-10-02 16:31:30 +02:00
|
|
|
|
}
|
2024-04-13 10:23:14 +02:00
|
|
|
|
}
|
2023-10-03 17:23:01 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
return NULL;
|
2023-10-02 16:31:30 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-02 18:44:59 +02:00
|
|
|
|
/*
|
2023-12-13 05:06:27 +01:00
|
|
|
|
* Lets ensure to the compiler doens't optimise out our useless instructions...
|
|
|
|
|
*/
|
2023-10-02 16:31:30 +02:00
|
|
|
|
#pragma optimize("", off)
|
|
|
|
|
|
|
|
|
|
STATIC
|
|
|
|
|
UINT64
|
2023-12-13 05:06:27 +01:00
|
|
|
|
MeasureInstructionRead(_In_ PVOID InstructionAddress)
|
2023-10-02 16:31:30 +02:00
|
|
|
|
{
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(InstructionAddress != NULL);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
CONST UINT64 start = __readmsr(IA32_APERF_MSR) << 32;
|
2024-08-01 06:21:53 +02:00
|
|
|
|
CHAR value = *(PCHAR)InstructionAddress;
|
2024-04-13 10:23:14 +02:00
|
|
|
|
return (__readmsr(IA32_APERF_MSR) << 32) - start;
|
2023-10-02 16:31:30 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma optimize("", on)
|
|
|
|
|
|
2023-10-03 14:31:30 +02:00
|
|
|
|
STATIC
|
2023-10-03 18:03:55 +02:00
|
|
|
|
UINT64
|
2023-12-13 05:06:27 +01:00
|
|
|
|
MeasureReads(_In_ PVOID Address, _In_ ULONG Count)
|
2023-10-02 16:31:30 +02:00
|
|
|
|
{
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(Address != NULL);
|
|
|
|
|
NT_ASSERT(Count > 0);
|
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
UINT64 read_average = 0;
|
2024-08-01 06:21:53 +02:00
|
|
|
|
KIRQL irql = {0};
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
MeasureInstructionRead(Address);
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2024-05-05 15:20:35 +02:00
|
|
|
|
KeRaiseIrql(HIGH_LEVEL, &irql);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
_disable();
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
for (UINT32 iteration = 0; iteration < Count; iteration++)
|
2024-04-13 10:23:14 +02:00
|
|
|
|
read_average += MeasureInstructionRead(Address);
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
_enable();
|
2024-05-05 15:20:35 +02:00
|
|
|
|
KeLowerIrql(irql);
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
DEBUG_VERBOSE("EPT Detection - Read Average: %llx", read_average);
|
2023-10-06 09:02:10 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
return read_average / Count;
|
2023-10-02 16:31:30 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-12-13 05:06:27 +01:00
|
|
|
|
#define EPT_CHECK_NUM_ITERATIONS 30
|
|
|
|
|
#define EPT_CONTROL_FUNCTIONS_COUNT 4
|
2023-10-03 16:11:27 +02:00
|
|
|
|
#define EPT_PROTECTED_FUNCTIONS_COUNT 2
|
2023-12-13 05:06:27 +01:00
|
|
|
|
#define EPT_MAX_FUNCTION_NAME_LENGTH 128
|
2023-10-03 16:11:27 +02:00
|
|
|
|
#define EPT_EXECUTION_TIME_MULTIPLIER 10
|
|
|
|
|
|
2023-10-02 16:31:30 +02:00
|
|
|
|
/*
|
2024-04-13 06:40:51 +02:00
|
|
|
|
* Even though we test for the presence of a hypervisor, we should still test
|
|
|
|
|
* for the presence of EPT hooks on key functions as this is a primary method
|
|
|
|
|
* for reversing AC's.
|
2023-12-13 05:06:27 +01:00
|
|
|
|
*
|
|
|
|
|
* Credits to momo5502 for the idea: https://momo5502.com/blog/?p=255
|
|
|
|
|
*
|
|
|
|
|
* [+] EPT: Read average: 14991c28f5c2
|
|
|
|
|
* [+] no EPT: Read average: 28828f5c28
|
|
|
|
|
*
|
2024-04-13 06:40:51 +02:00
|
|
|
|
* On average a read when HyperDbg's !epthook is active is around ~125x longer.
|
|
|
|
|
* Will need to continue testing with other HV's, however it is a good start.
|
2023-12-13 05:06:27 +01:00
|
|
|
|
*/
|
2023-10-03 14:31:30 +02:00
|
|
|
|
STATIC
|
2023-10-02 16:31:30 +02:00
|
|
|
|
NTSTATUS
|
2024-08-01 06:21:53 +02:00
|
|
|
|
GetAverageReadTimeAtRoutine(
|
|
|
|
|
_In_ PVOID RoutineAddress, _Out_ PUINT64 AverageTime)
|
2023-10-02 16:31:30 +02:00
|
|
|
|
{
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(RoutineAddress != NULL);
|
|
|
|
|
NT_ASSERT(AverageTime != NULL);
|
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!RoutineAddress || !AverageTime)
|
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2024-06-09 09:22:22 +02:00
|
|
|
|
if (!MmIsAddressValid(RoutineAddress))
|
|
|
|
|
return STATUS_INVALID_ADDRESS;
|
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
*AverageTime = MeasureReads(RoutineAddress, EPT_CHECK_NUM_ITERATIONS);
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
return *AverageTime == 0 ? STATUS_UNSUCCESSFUL : STATUS_SUCCESS;
|
2023-10-03 14:31:30 +02:00
|
|
|
|
}
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2023-10-03 14:31:30 +02:00
|
|
|
|
/*
|
2023-12-13 05:06:27 +01:00
|
|
|
|
* todo: encrypt both arrays
|
|
|
|
|
*
|
2024-04-13 06:40:51 +02:00
|
|
|
|
* The goal with the control functions is to find a reference time for an
|
|
|
|
|
* average read on a function that is not EPT hooked. To accomplish this I've
|
|
|
|
|
* selected some arbitrary, rarely used functions that shouldn't really ever
|
|
|
|
|
* have an EPT hook active on them. This will give us a baseline that we can
|
|
|
|
|
* then average out to find a relatively accurate average read time.
|
2023-12-13 05:06:27 +01:00
|
|
|
|
*
|
2024-04-13 06:40:51 +02:00
|
|
|
|
* From here, we have an array of protected functions which are commonly hooked
|
|
|
|
|
* via EPT to reverse anti cheats. We then check the read times of these
|
|
|
|
|
* functions and compare them to the average of the read times for the control
|
|
|
|
|
* functions. If the read threshold exceeds a multiple of 10, we can be fairly
|
|
|
|
|
* certain an EPT hook is active.
|
2023-12-13 05:06:27 +01:00
|
|
|
|
*
|
2024-04-13 06:40:51 +02:00
|
|
|
|
* Each time we measure the read we perform 30 iterations to ensure we get a
|
|
|
|
|
* consistent result aswell as disabling interrupts + raising IRQL to ensure the
|
|
|
|
|
* test is as accurate as possible.
|
2023-12-13 05:06:27 +01:00
|
|
|
|
*
|
2024-04-13 06:40:51 +02:00
|
|
|
|
* The following open source Intel VT-X hv's w/ EPT functionality have been
|
|
|
|
|
* tested and detected in a non vm environment:
|
2023-12-13 05:06:27 +01:00
|
|
|
|
*
|
|
|
|
|
* HyperDbg !epthook (https://github.com/HyperDbg/HyperDbg): detected
|
|
|
|
|
* DdiMon (https://github.com/tandasat/DdiMon): detected
|
|
|
|
|
*/
|
2024-04-13 06:40:51 +02:00
|
|
|
|
WCHAR CONTROL_FUNCTIONS[EPT_CONTROL_FUNCTIONS_COUNT]
|
|
|
|
|
[EPT_MAX_FUNCTION_NAME_LENGTH] = {
|
|
|
|
|
L"RtlAssert",
|
|
|
|
|
L"PsAcquireSiloHardReference",
|
|
|
|
|
L"PsDereferencePrimaryToken",
|
|
|
|
|
L"ZwCommitEnlistment"};
|
2023-12-13 05:06:27 +01:00
|
|
|
|
|
2024-04-13 06:40:51 +02:00
|
|
|
|
WCHAR PROTECTED_FUNCTIONS[EPT_PROTECTED_FUNCTIONS_COUNT]
|
|
|
|
|
[EPT_MAX_FUNCTION_NAME_LENGTH] = {
|
|
|
|
|
L"ExAllocatePoolWithTag", L"MmCopyMemory"};
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2023-10-06 09:02:10 +02:00
|
|
|
|
/*
|
2024-04-13 06:40:51 +02:00
|
|
|
|
* For whatever reason MmGetSystemRoutineAddress only works once, then every
|
|
|
|
|
* call thereafter fails. So will be storing the routine addresses in arrays
|
|
|
|
|
* since they dont change once the kernel is loaded.
|
2023-12-13 05:06:27 +01:00
|
|
|
|
*/
|
2024-06-09 09:22:22 +02:00
|
|
|
|
#pragma section("NonPagedPool", read, write)
|
|
|
|
|
__declspec(allocate("NonPagedPool")) UINT64
|
|
|
|
|
CONTROL_FUNCTION_ADDRESSES[EPT_CONTROL_FUNCTIONS_COUNT] = {0};
|
|
|
|
|
__declspec(allocate("NonPagedPool")) UINT64
|
|
|
|
|
PROTECTED_FUNCTION_ADDRESSES[EPT_PROTECTED_FUNCTIONS_COUNT] = {0};
|
2023-10-06 09:02:10 +02:00
|
|
|
|
|
|
|
|
|
STATIC
|
|
|
|
|
NTSTATUS
|
|
|
|
|
InitiateEptFunctionAddressArrays()
|
|
|
|
|
{
|
2024-04-13 10:23:14 +02:00
|
|
|
|
PAGED_CODE();
|
2023-10-10 15:52:42 +02:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
UNICODE_STRING current_function = {0};
|
2023-10-06 09:02:10 +02:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
for (UINT32 index = 0; index < EPT_CONTROL_FUNCTIONS_COUNT; index++) {
|
2024-04-13 10:23:14 +02:00
|
|
|
|
ImpRtlInitUnicodeString(¤t_function, CONTROL_FUNCTIONS[index]);
|
|
|
|
|
CONTROL_FUNCTION_ADDRESSES[index] =
|
|
|
|
|
ImpMmGetSystemRoutineAddress(¤t_function);
|
2023-10-06 09:02:10 +02:00
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(CONTROL_FUNCTION_ADDRESSES[index] != NULL);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!CONTROL_FUNCTION_ADDRESSES[index])
|
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
|
|
|
}
|
2023-10-06 09:02:10 +02:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
for (UINT32 index = 0; index < EPT_PROTECTED_FUNCTIONS_COUNT; index++) {
|
2024-04-23 12:23:47 +02:00
|
|
|
|
ImpRtlInitUnicodeString(¤t_function, PROTECTED_FUNCTIONS[index]);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
PROTECTED_FUNCTION_ADDRESSES[index] =
|
|
|
|
|
ImpMmGetSystemRoutineAddress(¤t_function);
|
2023-10-06 09:02:10 +02:00
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(PROTECTED_FUNCTION_ADDRESSES[index] != NULL);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!PROTECTED_FUNCTION_ADDRESSES[index])
|
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
|
|
|
}
|
2023-10-06 09:02:10 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
return STATUS_SUCCESS;
|
2023-10-06 09:02:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-22 12:43:09 +02:00
|
|
|
|
STATIC
|
|
|
|
|
VOID
|
2024-08-01 06:21:53 +02:00
|
|
|
|
ReportEptHook(
|
|
|
|
|
_In_ UINT64 ControlAverage,
|
|
|
|
|
_In_ UINT64 ReadAverage,
|
|
|
|
|
_In_ WCHAR FunctionName)
|
2024-07-22 12:43:09 +02:00
|
|
|
|
{
|
2024-08-01 06:21:53 +02:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
UINT32 len = 0;
|
2024-07-22 12:43:09 +02:00
|
|
|
|
PEPT_HOOK_REPORT report = NULL;
|
2024-08-01 06:21:53 +02:00
|
|
|
|
UNICODE_STRING string = {0};
|
2024-07-22 12:43:09 +02:00
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
len = CryptRequestRequiredBufferLength(sizeof(EPT_HOOK_REPORT));
|
2024-07-22 12:43:09 +02:00
|
|
|
|
report = ImpExAllocatePool2(POOL_FLAG_NON_PAGED, len, REPORT_POOL_TAG);
|
|
|
|
|
|
|
|
|
|
if (!report)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
INIT_REPORT_PACKET(report, REPORT_EPT_HOOK, 0);
|
|
|
|
|
|
|
|
|
|
report->control_average = ControlAverage;
|
2024-08-01 06:21:53 +02:00
|
|
|
|
report->read_average = ReadAverage;
|
2024-07-22 12:43:09 +02:00
|
|
|
|
|
|
|
|
|
RtlInitUnicodeString(&string, FunctionName);
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
status = UnicodeToCharBufString(
|
|
|
|
|
&string,
|
|
|
|
|
report->function_name,
|
|
|
|
|
sizeof(report->function_name));
|
2024-07-22 12:43:09 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
DEBUG_ERROR("UnicodeToCharBufString: %x", status);
|
|
|
|
|
|
|
|
|
|
status = CryptEncryptBuffer(report, len);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR("CryptEncryptBuffer: %lx", status);
|
|
|
|
|
ImpExFreePoolWithTag(report, len);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IrpQueueSchedulePacket(report, len);
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-03 18:03:55 +02:00
|
|
|
|
NTSTATUS
|
2023-10-03 14:31:30 +02:00
|
|
|
|
DetectEptHooksInKeyFunctions()
|
|
|
|
|
{
|
2024-04-13 10:23:14 +02:00
|
|
|
|
PAGED_CODE();
|
2023-10-10 15:52:42 +02:00
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
UINT32 control_fails = 0;
|
|
|
|
|
UINT64 instruction_time = 0;
|
|
|
|
|
UINT64 control_time_sum = 0;
|
|
|
|
|
UINT64 control_average = 0;
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
/* todo: once we call this, we need to set a flag to skip this,
|
|
|
|
|
* otherwise we just return early */
|
|
|
|
|
status = InitiateEptFunctionAddressArrays();
|
2023-10-03 14:31:30 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
2024-08-01 06:21:53 +02:00
|
|
|
|
DEBUG_ERROR(
|
|
|
|
|
"InitiateEptFunctionAddressArrays failed with status %x",
|
|
|
|
|
status);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
for (UINT32 index = 0; index < EPT_CONTROL_FUNCTIONS_COUNT; index++) {
|
2024-08-01 06:21:53 +02:00
|
|
|
|
status = GetAverageReadTimeAtRoutine(
|
|
|
|
|
CONTROL_FUNCTION_ADDRESSES[index],
|
|
|
|
|
&instruction_time);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
2024-08-01 06:21:53 +02:00
|
|
|
|
DEBUG_ERROR(
|
|
|
|
|
"DetectEptPresentOnFunction failed with status %x",
|
|
|
|
|
status);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
control_fails += 1;
|
|
|
|
|
continue;
|
2023-10-06 09:02:10 +02:00
|
|
|
|
}
|
2023-10-03 14:31:30 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
control_time_sum += instruction_time;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (control_time_sum == 0)
|
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
|
|
|
|
|
|
|
|
control_average =
|
|
|
|
|
control_time_sum / (EPT_CONTROL_FUNCTIONS_COUNT - control_fails);
|
|
|
|
|
|
|
|
|
|
if (control_average == 0)
|
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
for (UINT32 index = 0; index < EPT_PROTECTED_FUNCTIONS_COUNT; index++) {
|
2024-08-01 06:21:53 +02:00
|
|
|
|
status = GetAverageReadTimeAtRoutine(
|
|
|
|
|
PROTECTED_FUNCTION_ADDRESSES[index],
|
|
|
|
|
&instruction_time);
|
2023-10-03 14:31:30 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
2024-08-01 06:21:53 +02:00
|
|
|
|
DEBUG_ERROR(
|
|
|
|
|
"DetectEptPresentOnFunction failed with status %x",
|
|
|
|
|
status);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
continue;
|
2023-10-05 08:27:17 +02:00
|
|
|
|
}
|
2023-10-03 14:31:30 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
/* [+] EPT hook detected at function: ExAllocatePoolWithTag with
|
|
|
|
|
* execution time of: 149b7777777 */
|
|
|
|
|
if (control_average * EPT_EXECUTION_TIME_MULTIPLIER <
|
|
|
|
|
instruction_time) {
|
|
|
|
|
DEBUG_WARNING(
|
|
|
|
|
"EPT hook detected at function: %llx with execution time of: %llx",
|
|
|
|
|
PROTECTED_FUNCTION_ADDRESSES[index],
|
|
|
|
|
instruction_time);
|
2024-08-01 06:21:53 +02:00
|
|
|
|
ReportEptHook(
|
|
|
|
|
control_average,
|
|
|
|
|
instruction_time,
|
|
|
|
|
PROTECTED_FUNCTION_ADDRESSES[index]);
|
2023-10-03 14:31:30 +02:00
|
|
|
|
}
|
2024-04-13 10:23:14 +02:00
|
|
|
|
}
|
2023-10-03 14:31:30 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
return status;
|
2023-10-05 08:27:17 +02:00
|
|
|
|
}
|
2023-10-03 14:31:30 +02:00
|
|
|
|
|
2023-10-21 12:25:08 +02:00
|
|
|
|
VOID
|
2024-06-09 09:22:22 +02:00
|
|
|
|
FindWinLogonProcess(_In_ PPROCESS_LIST_ENTRY Node, _In_opt_ PVOID Context)
|
2023-10-21 12:25:08 +02:00
|
|
|
|
{
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(Node != NULL);
|
|
|
|
|
NT_ASSERT(Context != NULL);
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
LPCSTR process_name = NULL;
|
|
|
|
|
PEPROCESS* process = (PEPROCESS*)Context;
|
2023-10-21 12:25:08 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!Context)
|
|
|
|
|
return;
|
2023-10-21 12:25:08 +02:00
|
|
|
|
|
2024-06-09 09:22:22 +02:00
|
|
|
|
process_name = ImpPsGetProcessImageFileName(Node->process);
|
2023-10-21 12:25:08 +02:00
|
|
|
|
|
2024-07-22 12:43:09 +02:00
|
|
|
|
if (!IntCompareString(process_name, "winlogon.exe"))
|
2024-06-09 09:22:22 +02:00
|
|
|
|
*process = Node->process;
|
2024-02-11 15:34:28 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
STATIC
|
|
|
|
|
NTSTATUS
|
2024-08-01 06:21:53 +02:00
|
|
|
|
StoreModuleExecutableRegionsx86(
|
|
|
|
|
_In_ PRTL_MODULE_EXTENDED_INFO Module,
|
|
|
|
|
_In_ PVOID* Buffer,
|
|
|
|
|
_In_ PULONG BufferSize)
|
2024-02-11 15:34:28 +01:00
|
|
|
|
{
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(Module != NULL);
|
|
|
|
|
NT_ASSERT(Buffer != NULL);
|
|
|
|
|
NT_ASSERT(BufferSize != NULL);
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
PEPROCESS process = NULL;
|
2024-04-13 10:23:14 +02:00
|
|
|
|
KAPC_STATE apc_state = {0};
|
2024-02-11 15:34:28 +01:00
|
|
|
|
|
2024-06-11 13:41:55 +02:00
|
|
|
|
RtlHashmapEnumerate(GetProcessHashmap(), FindWinLogonProcess, &process);
|
2024-02-11 15:34:28 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!process)
|
|
|
|
|
return STATUS_NOT_FOUND;
|
2024-02-11 15:34:28 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
ImpKeStackAttachProcess(process, &apc_state);
|
2024-02-11 15:34:28 +01:00
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
status = StoreModuleExecutableRegionsInBuffer(
|
|
|
|
|
Buffer,
|
|
|
|
|
Module->ImageBase,
|
|
|
|
|
Module->ImageSize,
|
|
|
|
|
BufferSize,
|
|
|
|
|
TRUE);
|
2024-02-11 15:34:28 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
ImpKeUnstackDetachProcess(&apc_state);
|
2024-02-11 15:34:28 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
DEBUG_ERROR(
|
|
|
|
|
"StoreModuleExecutableRegionsInBuffer-x86 failed with status %x",
|
|
|
|
|
status);
|
2024-02-11 15:34:28 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
return status;
|
2024-02-11 15:34:28 +01:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-06 10:40:55 +02:00
|
|
|
|
FORCEINLINE
|
|
|
|
|
STATIC
|
|
|
|
|
VOID
|
|
|
|
|
Enablex86Hashing(_In_ PDRIVER_LIST_HEAD Head)
|
|
|
|
|
{
|
|
|
|
|
Head->can_hash_x86 = TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-11 15:34:28 +01:00
|
|
|
|
VOID
|
2024-08-01 06:21:53 +02:00
|
|
|
|
DeferredModuleHashingCallback(
|
|
|
|
|
_In_ PDEVICE_OBJECT DeviceObject, _In_opt_ PVOID Context)
|
2024-02-11 15:34:28 +01:00
|
|
|
|
{
|
2024-05-11 14:54:58 +02:00
|
|
|
|
UNREFERENCED_PARAMETER(Context);
|
|
|
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
2024-07-19 16:27:50 +02:00
|
|
|
|
RTL_MODULE_EXTENDED_INFO module = {0};
|
2024-08-01 06:21:53 +02:00
|
|
|
|
PDRIVER_LIST_HEAD list = GetDriverList();
|
|
|
|
|
PLIST_ENTRY head = &GetDriverList()->deferred_list;
|
|
|
|
|
PLIST_ENTRY entry = NULL;
|
|
|
|
|
PDRIVER_LIST_ENTRY driver = NULL;
|
2024-02-11 15:34:28 +01:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
Enablex86Hashing(list);
|
2024-05-06 10:40:55 +02:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
entry = RemoveHeadList(head);
|
2024-02-11 15:34:28 +01:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
if (entry == head)
|
2024-04-13 10:23:14 +02:00
|
|
|
|
goto end;
|
2024-02-11 15:34:28 +01:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
while (entry != head) {
|
|
|
|
|
driver = CONTAINING_RECORD(entry, DRIVER_LIST_ENTRY, deferred_entry);
|
2024-02-11 15:34:28 +01:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
DriverListEntryToExtendedModuleInfo(driver, &module);
|
2024-03-04 05:12:44 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
DEBUG_VERBOSE("Hashing Deferred Module: %s", module.FullPathName);
|
2024-02-11 15:34:28 +01:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
status = HashModule(&module, &driver->text_hash);
|
2024-02-11 15:34:28 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR("HashModule-x86 failed with status %x", status);
|
2024-07-19 16:27:50 +02:00
|
|
|
|
driver->hashed = FALSE;
|
2024-08-01 06:21:53 +02:00
|
|
|
|
entry = RemoveHeadList(head);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
continue;
|
2023-10-21 12:25:08 +02:00
|
|
|
|
}
|
2024-02-11 15:34:28 +01:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
driver->hashed = TRUE;
|
2024-08-01 06:21:53 +02:00
|
|
|
|
entry = RemoveHeadList(head);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-11 15:34:28 +01:00
|
|
|
|
end:
|
2024-05-11 14:54:58 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
DEBUG_VERBOSE("All deferred modules hashed.");
|
2024-07-19 16:27:50 +02:00
|
|
|
|
ImpIoFreeWorkItem(list->work_item);
|
|
|
|
|
list->work_item = NULL;
|
2023-10-21 12:25:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
NTSTATUS
|
|
|
|
|
HashModule(_In_ PRTL_MODULE_EXTENDED_INFO Module, _Out_ PVOID Hash)
|
2023-10-05 08:27:17 +02:00
|
|
|
|
{
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(Module != NULL);
|
|
|
|
|
NT_ASSERT(Hash != NULL);
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
ANSI_STRING ansi_string = {0};
|
|
|
|
|
UNICODE_STRING path = {0};
|
|
|
|
|
ULONG memory_text_size = 0;
|
|
|
|
|
PVOID memory_hash = NULL;
|
|
|
|
|
ULONG memory_hash_size = 0;
|
|
|
|
|
PVAL_INTEGRITY_HEADER memory_buffer = NULL;
|
|
|
|
|
ULONG memory_buffer_size = 0;
|
|
|
|
|
PDRIVER_LIST_HEAD list = GetDriverList();
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
|
|
|
|
ImpRtlInitAnsiString(&ansi_string, Module->FullPathName);
|
|
|
|
|
|
|
|
|
|
if (!ansi_string.Buffer) {
|
|
|
|
|
DEBUG_ERROR("RtlInitAnsiString failed with status %x", status);
|
2024-05-11 14:54:58 +02:00
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
2024-04-13 10:23:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = ImpRtlAnsiStringToUnicodeString(&path, &ansi_string, TRUE);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
2024-08-01 06:21:53 +02:00
|
|
|
|
DEBUG_ERROR(
|
|
|
|
|
"RtlAnsiStringToUnicodeString failed with status %x",
|
|
|
|
|
status);
|
2024-05-11 14:54:58 +02:00
|
|
|
|
return status;
|
2024-04-13 10:23:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* For win32k and related modules, because they are 32bit for us to read
|
|
|
|
|
* the memory we need to attach to a 32 bit process. A simple check is
|
|
|
|
|
* that the 32 bit image base wont be a valid address, while this is
|
|
|
|
|
* hacky it works. Then we simply attach to a 32 bit address space, in
|
|
|
|
|
* our case winlogon, which will allow us to perform the copy.
|
|
|
|
|
*
|
|
|
|
|
* Since the driver loads at system startup, our driver is loaded before
|
|
|
|
|
* the WinLogon process has started, so to combat this return return
|
|
|
|
|
* early with a status code. This will mark the module as not hashed and
|
|
|
|
|
* x86. We will then queue a work item to hash these modules later once
|
|
|
|
|
* WinLogon has started.
|
|
|
|
|
*/
|
|
|
|
|
if (!ImpMmIsAddressValid(Module->ImageBase) && !list->can_hash_x86) {
|
|
|
|
|
status = STATUS_INVALID_IMAGE_WIN_32;
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
else if (!ImpMmIsAddressValid(Module->ImageBase) && list->can_hash_x86) {
|
2023-10-20 20:30:45 +02:00
|
|
|
|
/*
|
2024-04-13 10:23:14 +02:00
|
|
|
|
* Once the WinLogon process has started, we can then hash new
|
|
|
|
|
* x86 modules.
|
2023-12-13 05:06:27 +01:00
|
|
|
|
*/
|
2024-08-01 06:21:53 +02:00
|
|
|
|
status = StoreModuleExecutableRegionsx86(
|
|
|
|
|
Module,
|
|
|
|
|
(PVOID)&memory_buffer,
|
|
|
|
|
&memory_buffer_size);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
2024-08-01 06:21:53 +02:00
|
|
|
|
status = StoreModuleExecutableRegionsInBuffer(
|
|
|
|
|
(PVOID)&memory_buffer,
|
|
|
|
|
Module->ImageBase,
|
|
|
|
|
Module->ImageSize,
|
|
|
|
|
&memory_buffer_size,
|
|
|
|
|
FALSE);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
}
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR(
|
|
|
|
|
"StoreModuleExecutableRegionsInbuffer 2 failed with status %x",
|
|
|
|
|
status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
status = CryptHashBuffer_sha256(
|
|
|
|
|
memory_buffer->section_base,
|
|
|
|
|
memory_buffer->section_header.SizeOfRawData,
|
|
|
|
|
&memory_hash,
|
|
|
|
|
&memory_hash_size);
|
2024-04-13 06:40:51 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_VERBOSE("ComputeHashOfSections failed with status %x", status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
2023-10-21 12:25:08 +02:00
|
|
|
|
|
2024-07-22 12:43:09 +02:00
|
|
|
|
IntCopyMemory(Hash, memory_hash, memory_hash_size);
|
2023-10-20 20:30:45 +02:00
|
|
|
|
|
2023-12-31 15:06:24 +01:00
|
|
|
|
end:
|
2023-10-20 20:30:45 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (memory_buffer)
|
|
|
|
|
ImpExFreePoolWithTag(memory_buffer, POOL_TAG_INTEGRITY);
|
2023-12-31 15:06:24 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (memory_hash)
|
|
|
|
|
ImpExFreePoolWithTag(memory_hash, POOL_TAG_INTEGRITY);
|
2024-01-02 23:29:23 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (path.Buffer)
|
|
|
|
|
ImpRtlFreeUnicodeString(&path);
|
2023-10-20 20:30:45 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
return status;
|
2024-01-07 05:13:41 +01:00
|
|
|
|
}
|
2023-10-20 20:30:45 +02:00
|
|
|
|
|
2024-05-11 18:06:34 +02:00
|
|
|
|
/*
|
|
|
|
|
* As said in the comment below, in the future we want to be able to copy a
|
|
|
|
|
* small part of the spot where the image has changed, say the next 50 bytes.
|
|
|
|
|
* This would be useful for scanning for any jmp x etc. For this thisl do.
|
|
|
|
|
*/
|
|
|
|
|
STATIC
|
|
|
|
|
VOID
|
2024-05-15 11:48:09 +02:00
|
|
|
|
ReportModifiedSystemImage(_In_ PRTL_MODULE_EXTENDED_INFO Module)
|
2024-05-11 18:06:34 +02:00
|
|
|
|
{
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(Module != NULL);
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
UINT32 len = 0;
|
2024-07-19 16:27:50 +02:00
|
|
|
|
PSYSTEM_MODULE_INTEGRITY_CHECK_REPORT report = NULL;
|
|
|
|
|
|
|
|
|
|
len = CryptRequestRequiredBufferLength(
|
2024-05-11 18:06:34 +02:00
|
|
|
|
sizeof(SYSTEM_MODULE_INTEGRITY_CHECK_REPORT));
|
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
report = ImpExAllocatePool2(POOL_FLAG_NON_PAGED, len, REPORT_POOL_TAG);
|
2024-05-11 18:06:34 +02:00
|
|
|
|
|
|
|
|
|
if (!report)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
INIT_REPORT_PACKET(report, REPORT_PATCHED_SYSTEM_MODULE, 0);
|
|
|
|
|
|
|
|
|
|
report->image_base = Module->ImageBase;
|
|
|
|
|
report->image_size = Module->ImageSize;
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
IntCopyMemory(
|
|
|
|
|
report->path_name,
|
|
|
|
|
Module->FullPathName,
|
|
|
|
|
sizeof(report->path_name));
|
2024-05-11 18:06:34 +02:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
status = CryptEncryptBuffer(report, len);
|
2024-05-11 18:06:34 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR("CryptEncryptBuffer: %lx", status);
|
2024-07-19 16:27:50 +02:00
|
|
|
|
ImpExFreePoolWithTag(report, len);
|
2024-05-11 18:06:34 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
IrpQueueSchedulePacket(report, len);
|
2024-05-11 18:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
VOID
|
|
|
|
|
ValidateSystemModule(_In_ PRTL_MODULE_EXTENDED_INFO Module)
|
|
|
|
|
{
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(Module != NULL);
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
PDRIVER_LIST_ENTRY entry = NULL;
|
|
|
|
|
PVOID hash = NULL;
|
2023-12-31 15:06:24 +01:00
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
hash = ExAllocatePool2(
|
|
|
|
|
POOL_FLAG_NON_PAGED,
|
|
|
|
|
SHA_256_HASH_LENGTH,
|
|
|
|
|
POOL_TAG_INTEGRITY);
|
2024-01-07 05:13:41 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!hash)
|
|
|
|
|
return;
|
2024-01-07 05:13:41 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
FindDriverEntryByBaseAddress(Module->ImageBase, &entry);
|
2024-01-07 05:13:41 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!entry) {
|
|
|
|
|
DEBUG_ERROR("FindDriverEntryByBaseAddress failed with no status");
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
2024-01-07 05:13:41 +01:00
|
|
|
|
|
2024-06-09 09:38:22 +02:00
|
|
|
|
/* For now, there is some issue that sometimes occurs when validing x86
|
|
|
|
|
* modules, for now lets skip them.*/
|
|
|
|
|
if (entry->x86)
|
|
|
|
|
goto end;
|
|
|
|
|
|
2024-05-11 18:06:34 +02:00
|
|
|
|
/*
|
|
|
|
|
* Ideally, we would like to have access to the offset into the module that
|
|
|
|
|
* doesnt match, allowing us to copy the next 50 bytes for example. Since we
|
|
|
|
|
* only store the hash, we can only check whether something has changed, but
|
|
|
|
|
* we dont really have access to any information regarding what changed. In
|
|
|
|
|
* the future it might be nice (though requires a fair amount of memory) to
|
|
|
|
|
* store a copy of images on load in the list alongside the hash. That way
|
|
|
|
|
* if there is a change in the hash, we can access the old buffer, perform a
|
|
|
|
|
* memory comparison, and find the point where the change exists.
|
|
|
|
|
*/
|
2024-04-13 10:23:14 +02:00
|
|
|
|
status = HashModule(Module, hash);
|
2024-01-07 05:13:41 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR("HashModule failed with status %x", status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
2024-01-07 05:13:41 +01:00
|
|
|
|
|
2024-05-11 18:06:34 +02:00
|
|
|
|
if (CompareHashes(hash, entry->text_hash, SHA_256_HASH_LENGTH)) {
|
2024-08-01 06:21:53 +02:00
|
|
|
|
DEBUG_VERBOSE(
|
|
|
|
|
"Module: %s text regions are valid.",
|
|
|
|
|
Module->FullPathName);
|
2024-05-11 18:06:34 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
2024-08-01 06:21:53 +02:00
|
|
|
|
DEBUG_WARNING(
|
|
|
|
|
"**!!** Module: %s text regions are NOT valid **!!**",
|
|
|
|
|
Module->FullPathName);
|
2024-05-15 11:48:09 +02:00
|
|
|
|
ReportModifiedSystemImage(Module);
|
2024-05-11 18:06:34 +02:00
|
|
|
|
}
|
2024-01-07 05:13:41 +01:00
|
|
|
|
|
|
|
|
|
end:
|
2024-05-11 14:54:58 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (hash)
|
|
|
|
|
ExFreePoolWithTag(hash, POOL_TAG_INTEGRITY);
|
2023-12-31 15:06:24 +01:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-15 11:48:09 +02:00
|
|
|
|
STATIC
|
|
|
|
|
VOID
|
|
|
|
|
ReportModifiedSelfDriverImage(_In_ PRTL_MODULE_EXTENDED_INFO Module)
|
|
|
|
|
{
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(Module != NULL);
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
UINT32 len = 0;
|
2024-07-19 16:27:50 +02:00
|
|
|
|
PDRIVER_SELF_INTEGRITY_CHECK_REPORT packet = NULL;
|
|
|
|
|
|
|
|
|
|
len = CryptRequestRequiredBufferLength(
|
2024-05-15 11:48:09 +02:00
|
|
|
|
sizeof(DRIVER_SELF_INTEGRITY_CHECK_REPORT));
|
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
packet = ImpExAllocatePool2(POOL_FLAG_NON_PAGED, len, REPORT_POOL_TAG);
|
2024-05-15 11:48:09 +02:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
if (!packet)
|
2024-05-15 11:48:09 +02:00
|
|
|
|
return;
|
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
INIT_REPORT_PACKET(packet, REPORT_SELF_DRIVER_PATCHED, 0);
|
2024-05-15 11:48:09 +02:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
packet->image_base = Module->ImageBase;
|
|
|
|
|
packet->image_size = Module->ImageSize;
|
2024-05-15 11:48:09 +02:00
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
IntCopyMemory(
|
|
|
|
|
packet->path_name,
|
|
|
|
|
Module->FullPathName,
|
|
|
|
|
sizeof(packet->path_name));
|
2024-05-15 11:48:09 +02:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
status = CryptEncryptBuffer(packet, len);
|
2024-05-15 11:48:09 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR("CryptEncryptBuffer: %lx", status);
|
2024-07-19 16:27:50 +02:00
|
|
|
|
ImpExFreePoolWithTag(packet, len);
|
2024-05-15 11:48:09 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
IrpQueueSchedulePacket(packet, len);
|
2024-05-15 11:48:09 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-02 23:29:23 +01:00
|
|
|
|
NTSTATUS
|
|
|
|
|
ValidateOurDriverImage()
|
|
|
|
|
{
|
2024-08-01 06:21:53 +02:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
SYSTEM_MODULES modules = {0};
|
|
|
|
|
PRTL_MODULE_EXTENDED_INFO module_info = NULL;
|
|
|
|
|
PVOID memory_hash = NULL;
|
|
|
|
|
ULONG memory_hash_size = 0;
|
|
|
|
|
PDRIVER_LIST_ENTRY entry = NULL;
|
|
|
|
|
LPCSTR driver_name = GetDriverName();
|
|
|
|
|
PUNICODE_STRING path = GetDriverPath();
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
|
|
|
|
status = GetSystemModuleInformation(&modules);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR("GetSystemModuleInformation failed with status %x", status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
2024-01-02 23:29:23 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
module_info = FindSystemModuleByName(driver_name, &modules);
|
2024-01-02 23:29:23 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!module_info) {
|
|
|
|
|
DEBUG_ERROR("FindSystemModuleByName failed with no status.");
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
2024-01-02 23:29:23 +01:00
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
memory_hash = ExAllocatePool2(
|
|
|
|
|
POOL_FLAG_NON_PAGED,
|
|
|
|
|
SHA_256_HASH_LENGTH,
|
|
|
|
|
POOL_TAG_INTEGRITY);
|
2024-01-07 05:13:41 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!memory_hash)
|
|
|
|
|
goto end;
|
2024-01-07 05:13:41 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
FindDriverEntryByBaseAddress(module_info->ImageBase, &entry);
|
2024-01-07 05:13:41 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!entry) {
|
|
|
|
|
DEBUG_ERROR("FindDriverEntryByBaseAddress failed with no status.");
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
2024-01-07 05:13:41 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (entry->hashed == FALSE) {
|
|
|
|
|
DEBUG_WARNING("Our module has not been hashed, returning.");
|
|
|
|
|
status = STATUS_HASH_NOT_PRESENT;
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
2024-01-14 05:31:19 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
status = HashModule(module_info, memory_hash);
|
2024-01-07 05:13:41 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR("HashModule failed with status %x", status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
2024-01-07 05:13:41 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
/*
|
|
|
|
|
* Since we don't pass a return value, I think we would raise an invalid
|
|
|
|
|
* module error and stop the users game session ? since module .text
|
|
|
|
|
* section error would be a large red flag
|
|
|
|
|
*/
|
2024-05-15 11:48:09 +02:00
|
|
|
|
if (CompareHashes(memory_hash, entry->text_hash, SHA_256_HASH_LENGTH)) {
|
2024-04-13 10:23:14 +02:00
|
|
|
|
DEBUG_VERBOSE("Driver image is valid. Integrity check complete");
|
2024-05-15 11:48:09 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
2024-04-13 10:23:14 +02:00
|
|
|
|
DEBUG_WARNING("**!!** Driver image is NOT valid. **!!**");
|
2024-05-15 11:48:09 +02:00
|
|
|
|
ReportModifiedSelfDriverImage(module_info);
|
|
|
|
|
}
|
2024-01-02 23:29:23 +01:00
|
|
|
|
|
|
|
|
|
end:
|
2024-01-07 05:13:41 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (memory_hash)
|
|
|
|
|
ExFreePoolWithTag(memory_hash, POOL_TAG_INTEGRITY);
|
2024-01-07 05:13:41 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (modules.address)
|
|
|
|
|
ExFreePoolWithTag(modules.address, SYSTEM_MODULES_POOL);
|
2024-01-02 23:29:23 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
return status;
|
2024-01-02 23:29:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-04 17:43:01 +02:00
|
|
|
|
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
|
2024-08-01 06:21:53 +02:00
|
|
|
|
GetCurrentVerificationMaxIndex(
|
|
|
|
|
_In_ PSYS_MODULE_VAL_CONTEXT Context, _In_ UINT32 Count)
|
2024-05-04 17:43:01 +02:00
|
|
|
|
{
|
|
|
|
|
return Count + Context->block_size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FORCEINLINE
|
|
|
|
|
STATIC
|
|
|
|
|
VOID
|
2024-08-01 06:21:53 +02:00
|
|
|
|
UpdateCurrentVerificationIndex(
|
|
|
|
|
_In_ PSYS_MODULE_VAL_CONTEXT Context, _In_ UINT32 Count)
|
2024-05-04 17:43:01 +02:00
|
|
|
|
{
|
|
|
|
|
InterlockedExchange(&Context->current_count, Count);
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-31 15:06:24 +01:00
|
|
|
|
STATIC
|
|
|
|
|
VOID
|
2024-08-01 06:21:53 +02:00
|
|
|
|
SystemModuleVerificationDispatchFunction(
|
|
|
|
|
_In_ PDEVICE_OBJECT DeviceObject, _In_ PSYS_MODULE_VAL_CONTEXT Context)
|
2023-12-31 15:06:24 +01:00
|
|
|
|
{
|
2024-05-11 14:54:58 +02:00
|
|
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(Context != NULL);
|
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
UINT32 count = 0;
|
2024-08-01 06:21:53 +02:00
|
|
|
|
UINT32 max = 0;
|
2024-07-19 16:27:50 +02:00
|
|
|
|
|
2024-05-04 17:43:01 +02:00
|
|
|
|
IncrementActiveThreadCount(Context);
|
2024-07-19 16:27:50 +02:00
|
|
|
|
count = GetCurrentVerificationIndex(Context);
|
2024-05-04 17:43:01 +02:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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!
|
|
|
|
|
*/
|
2024-07-19 16:27:50 +02:00
|
|
|
|
max = GetCurrentVerificationMaxIndex(Context, count);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
|
|
|
|
|
for (; count < max && count < Context->total_count; count++) {
|
2024-08-04 08:30:31 +02:00
|
|
|
|
DEBUG_VERBOSE(
|
|
|
|
|
"ThrId: %lx, Count: %lx, Max: %lx, Total Count: %lx",
|
|
|
|
|
PsGetCurrentThreadId(),
|
|
|
|
|
count,
|
|
|
|
|
max,
|
|
|
|
|
Context->total_count);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!InterlockedCompareExchange(
|
2024-07-19 16:27:50 +02:00
|
|
|
|
&Context->dispatcher_info[count].validated,
|
|
|
|
|
TRUE,
|
|
|
|
|
FALSE)) {
|
2024-04-13 10:23:14 +02:00
|
|
|
|
ValidateSystemModule(&Context->module_info[count]);
|
2023-12-31 15:06:24 +01:00
|
|
|
|
}
|
2024-04-13 10:23:14 +02:00
|
|
|
|
}
|
2023-10-20 20:30:45 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (count == Context->total_count)
|
2024-05-04 17:43:01 +02:00
|
|
|
|
SetVerificationBlockAsComplete(Context);
|
2023-12-23 19:52:55 +01:00
|
|
|
|
|
2024-05-04 17:43:01 +02:00
|
|
|
|
UpdateCurrentVerificationIndex(Context, count);
|
|
|
|
|
DecrementActiveThreadCount(Context);
|
2023-12-31 15:06:24 +01:00
|
|
|
|
}
|
2023-10-20 20:30:45 +02:00
|
|
|
|
|
2023-12-31 15:06:24 +01:00
|
|
|
|
#define VALIDATION_BLOCK_SIZE 25
|
2023-10-20 20:30:45 +02:00
|
|
|
|
|
2024-05-04 17:43:01 +02:00
|
|
|
|
FORCEINLINE
|
|
|
|
|
STATIC
|
|
|
|
|
VOID
|
2024-08-01 06:21:53 +02:00
|
|
|
|
InitSysModuleValidationContext(
|
|
|
|
|
_Out_ PSYS_MODULE_VAL_CONTEXT Context,
|
|
|
|
|
_In_ PMODULE_DISPATCHER_HEADER DispatcherArray,
|
|
|
|
|
_In_ PSYSTEM_MODULES SystemModules)
|
2024-05-04 17:43:01 +02:00
|
|
|
|
{
|
|
|
|
|
Context->active_thread_count = 0;
|
2024-08-01 06:21:53 +02:00
|
|
|
|
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;
|
2024-05-04 17:43:01 +02:00
|
|
|
|
|
|
|
|
|
/* skip hal.dll and ntosrnl.exe */
|
|
|
|
|
Context->current_count = 2;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-31 15:06:24 +01:00
|
|
|
|
/*
|
2024-04-13 06:40:51 +02:00
|
|
|
|
* Multithreaded delayed priority work items improve 1% lows by 25% and reduces
|
|
|
|
|
* average PC latency by 10% compared to traditional multithreading. This is
|
|
|
|
|
* important as having high average fps but low 1% lows just leads to stuttery
|
|
|
|
|
* gameplay which in competitive multiplayer games is simply not alright.
|
|
|
|
|
* Overall still room for improvement but from a statistical and feel standpoint
|
|
|
|
|
* which the gameplay is much smoother (tested in cs2).
|
2023-12-31 15:06:24 +01:00
|
|
|
|
*
|
2024-04-13 06:40:51 +02:00
|
|
|
|
* A potential idea for further improvement is finding the cores with the least
|
|
|
|
|
* cpu usages and setting the worker threads affinity accordingly.
|
2023-12-31 15:06:24 +01:00
|
|
|
|
*/
|
|
|
|
|
STATIC
|
|
|
|
|
NTSTATUS
|
|
|
|
|
InitialiseSystemModuleVerificationContext(PSYS_MODULE_VAL_CONTEXT Context)
|
|
|
|
|
{
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(Context != NULL);
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
SYSTEM_MODULES modules = {0};
|
2024-05-04 17:43:01 +02:00
|
|
|
|
PMODULE_DISPATCHER_HEADER dispatcher = NULL;
|
2024-08-01 06:21:53 +02:00
|
|
|
|
UINT32 count = 0;
|
2023-12-31 15:06:24 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
status = GetSystemModuleInformation(&modules);
|
2023-12-31 15:06:24 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR("GetSystemModuleInformation failed with status %x", status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
2023-10-20 20:30:45 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
DEBUG_VERBOSE("driver count: %lx", modules.module_count);
|
2024-01-07 05:13:41 +01:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
count = modules.module_count * sizeof(MODULE_DISPATCHER_HEADER);
|
|
|
|
|
|
|
|
|
|
dispatcher =
|
|
|
|
|
ImpExAllocatePool2(POOL_FLAG_NON_PAGED, count, POOL_TAG_INTEGRITY);
|
2024-04-13 10:06:59 +02:00
|
|
|
|
|
2024-05-04 17:43:01 +02:00
|
|
|
|
if (!dispatcher) {
|
2024-04-13 10:23:14 +02:00
|
|
|
|
ImpExFreePoolWithTag(modules.address, SYSTEM_MODULES_POOL);
|
|
|
|
|
return STATUS_MEMORY_NOT_ALLOCATED;
|
|
|
|
|
}
|
2023-12-31 15:06:24 +01:00
|
|
|
|
|
2024-05-04 17:43:01 +02:00
|
|
|
|
InitSysModuleValidationContext(Context, dispatcher, &modules);
|
2024-04-13 10:23:14 +02:00
|
|
|
|
return status;
|
2023-12-31 15:06:24 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FreeWorkItems(_In_ PSYS_MODULE_VAL_CONTEXT Context)
|
|
|
|
|
{
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(Context != NULL);
|
|
|
|
|
|
|
|
|
|
for (UINT32 index = 0; index < VERIFICATION_THREAD_COUNT; index++) {
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (Context->work_items[index]) {
|
|
|
|
|
ImpIoFreeWorkItem(Context->work_items[index]);
|
|
|
|
|
Context->work_items[index] = NULL;
|
2023-12-31 15:06:24 +01:00
|
|
|
|
}
|
2024-04-13 10:23:14 +02:00
|
|
|
|
}
|
2023-12-31 15:06:24 +01:00
|
|
|
|
}
|
2023-12-23 19:52:55 +01:00
|
|
|
|
|
2023-12-31 15:06:24 +01:00
|
|
|
|
STATIC
|
|
|
|
|
VOID
|
|
|
|
|
FreeModuleVerificationItems(_In_ PSYS_MODULE_VAL_CONTEXT Context)
|
|
|
|
|
{
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(Context != NULL);
|
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
/* if a thread hasnt completed by this point, something catastrophic has
|
|
|
|
|
* gone wrong and maybe its better not to yield..*/
|
|
|
|
|
while (Context->active_thread_count)
|
|
|
|
|
YieldProcessor();
|
|
|
|
|
|
|
|
|
|
if (Context->module_info) {
|
|
|
|
|
ImpExFreePoolWithTag(Context->module_info, SYSTEM_MODULES_POOL);
|
|
|
|
|
Context->module_info = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Context->dispatcher_info) {
|
|
|
|
|
ImpExFreePoolWithTag(Context->dispatcher_info, POOL_TAG_INTEGRITY);
|
|
|
|
|
Context->dispatcher_info = NULL;
|
|
|
|
|
}
|
2024-01-02 23:29:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
CleanupValidationContextOnUnload(_In_ PSYS_MODULE_VAL_CONTEXT Context)
|
|
|
|
|
{
|
2024-08-01 06:21:53 +02:00
|
|
|
|
Context->active = FALSE;
|
2024-04-13 10:23:14 +02:00
|
|
|
|
Context->complete = TRUE;
|
|
|
|
|
FreeWorkItems(Context);
|
|
|
|
|
FreeModuleVerificationItems(Context);
|
2023-12-31 15:06:24 +01:00
|
|
|
|
}
|
2023-12-23 19:52:55 +01:00
|
|
|
|
|
2024-05-04 17:43:01 +02:00
|
|
|
|
STATIC
|
|
|
|
|
VOID
|
|
|
|
|
DispatchVerificationWorkerThreads(_In_ PSYS_MODULE_VAL_CONTEXT Context)
|
|
|
|
|
{
|
2024-07-19 16:27:50 +02:00
|
|
|
|
for (UINT32 index = 0; index < VERIFICATION_THREAD_COUNT; index++) {
|
2024-05-04 17:43:01 +02:00
|
|
|
|
Context->work_items[index] =
|
|
|
|
|
ImpIoAllocateWorkItem(GetDriverDeviceObject());
|
|
|
|
|
|
|
|
|
|
if (!Context->work_items[index])
|
|
|
|
|
continue;
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
ImpIoQueueWorkItem(
|
|
|
|
|
Context->work_items[index],
|
|
|
|
|
SystemModuleVerificationDispatchFunction,
|
|
|
|
|
DelayedWorkQueue,
|
|
|
|
|
Context);
|
2024-05-04 17:43:01 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-31 15:06:24 +01:00
|
|
|
|
NTSTATUS
|
|
|
|
|
SystemModuleVerificationDispatcher()
|
|
|
|
|
{
|
2024-08-01 06:21:53 +02:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
PIO_WORKITEM work_item = NULL;
|
|
|
|
|
PSYS_MODULE_VAL_CONTEXT context = GetSystemModuleValidationContext();
|
2023-12-23 19:52:55 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (context->complete) {
|
|
|
|
|
DEBUG_VERBOSE(
|
|
|
|
|
"System modules integrity check complete. Freeing items.");
|
2024-07-19 16:27:50 +02:00
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
context->active = FALSE;
|
2024-04-13 10:23:14 +02:00
|
|
|
|
context->complete = FALSE;
|
2024-07-19 16:27:50 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
FreeModuleVerificationItems(context);
|
|
|
|
|
FreeWorkItems(context);
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
|
}
|
2023-12-23 19:52:55 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!context->active) {
|
|
|
|
|
DEBUG_VERBOSE("Context not active, generating new one");
|
2023-12-23 19:52:55 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
status = InitialiseSystemModuleVerificationContext(context);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR(
|
|
|
|
|
"InitialiseSystemModuleVerificationContext failed with status %x",
|
|
|
|
|
status);
|
|
|
|
|
return status;
|
2023-12-31 15:06:24 +01:00
|
|
|
|
}
|
2024-04-13 10:23:14 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
FreeWorkItems(context);
|
|
|
|
|
}
|
2023-10-20 20:30:45 +02:00
|
|
|
|
|
2024-05-04 17:43:01 +02:00
|
|
|
|
DispatchVerificationWorkerThreads(context);
|
2023-10-20 20:30:45 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
DEBUG_VERBOSE(
|
|
|
|
|
"All worker threads dispatched for system module validation.");
|
2023-10-21 15:11:59 +02:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
return STATUS_SUCCESS;
|
2023-10-21 15:02:35 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-12-23 19:52:55 +01:00
|
|
|
|
NTSTATUS
|
|
|
|
|
GetOsVersionInformation(_Out_ PRTL_OSVERSIONINFOW VersionInfo)
|
|
|
|
|
{
|
2024-08-01 06:21:53 +02:00
|
|
|
|
NTSTATUS status = STATUS_ABANDONED;
|
|
|
|
|
RTL_OSVERSIONINFOW info = {0};
|
2023-12-23 19:52:55 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!VersionInfo)
|
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
2023-12-23 19:52:55 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
status = ImpRtlGetVersion(&info);
|
2023-12-23 19:52:55 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR("RtlGetVersion failed with status %x", status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
2023-12-23 19:52:55 +01:00
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
VersionInfo->dwBuildNumber = info.dwBuildNumber;
|
|
|
|
|
VersionInfo->dwMajorVersion = info.dwMajorVersion;
|
|
|
|
|
VersionInfo->dwMinorVersion = info.dwMinorVersion;
|
2024-04-13 10:23:14 +02:00
|
|
|
|
VersionInfo->dwOSVersionInfoSize = info.dwOSVersionInfoSize;
|
2024-08-01 06:21:53 +02:00
|
|
|
|
VersionInfo->dwPlatformId = info.dwPlatformId;
|
2023-12-23 19:52:55 +01:00
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
IntCopyMemory(
|
|
|
|
|
VersionInfo->szCSDVersion,
|
|
|
|
|
info.szCSDVersion,
|
|
|
|
|
sizeof(VersionInfo->szCSDVersion));
|
2023-12-23 19:52:55 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
return status;
|
2024-01-07 05:13:41 +01:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-12 06:40:33 +01:00
|
|
|
|
BOOLEAN
|
|
|
|
|
ValidateOurDriversDispatchRoutines()
|
|
|
|
|
{
|
2024-04-13 10:23:14 +02:00
|
|
|
|
PDRIVER_OBJECT driver = GetDriverObject();
|
2024-01-12 06:40:33 +01:00
|
|
|
|
|
2024-04-13 10:23:14 +02:00
|
|
|
|
if (driver->MajorFunction[IRP_MJ_CREATE] != DeviceCreate ||
|
|
|
|
|
driver->MajorFunction[IRP_MJ_CLOSE] != DeviceClose ||
|
|
|
|
|
driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] != DeviceControl) {
|
|
|
|
|
DEBUG_WARNING(
|
|
|
|
|
"**!!** Drivers dispatch routine has been tampered with. **!!**");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
2024-01-28 08:34:09 +01:00
|
|
|
|
}
|
2024-05-04 17:43:01 +02:00
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2024-08-01 06:21:53 +02:00
|
|
|
|
Configuration->dpc = ImpExAllocatePool2(
|
|
|
|
|
POOL_FLAG_NON_PAGED,
|
|
|
|
|
sizeof(KDPC),
|
|
|
|
|
POOL_TAG_HEARTBEAT);
|
2024-05-04 17:43:01 +02:00
|
|
|
|
|
|
|
|
|
if (!Configuration->dpc)
|
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
Configuration->timer = ImpExAllocatePool2(
|
|
|
|
|
POOL_FLAG_NON_PAGED,
|
|
|
|
|
sizeof(KTIMER),
|
|
|
|
|
POOL_TAG_HEARTBEAT);
|
2024-05-04 17:43:01 +02:00
|
|
|
|
|
|
|
|
|
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
|
2024-05-11 14:54:58 +02:00
|
|
|
|
GenerateHeartbeatDueTime()
|
2024-05-04 17:43:01 +02:00
|
|
|
|
{
|
2024-08-01 06:21:53 +02:00
|
|
|
|
UINT64 interval = 0;
|
|
|
|
|
LARGE_INTEGER ticks = {0};
|
2024-07-19 16:27:50 +02:00
|
|
|
|
LARGE_INTEGER due_time = {0};
|
|
|
|
|
|
2024-05-04 17:43:01 +02:00
|
|
|
|
KeQueryTickCount(&ticks);
|
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
interval = HEARTBEAT_NANOSECONDS_LOW +
|
|
|
|
|
(TICKS_TO_100_NS_INTERVALS(ticks.QuadPart) %
|
|
|
|
|
(HEARTBEAT_NANOSECONDS_HIGH - HEARTBEAT_NANOSECONDS_LOW));
|
2024-05-04 17:43:01 +02:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
due_time.QuadPart = -interval;
|
2024-05-04 17:43:01 +02:00
|
|
|
|
return due_time;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FORCEINLINE
|
|
|
|
|
STATIC
|
|
|
|
|
VOID
|
2024-07-19 16:27:50 +02:00
|
|
|
|
InitialiseHeartbeatObjects(_Inout_ PHEARTBEAT_CONFIGURATION Config)
|
2024-05-04 17:43:01 +02:00
|
|
|
|
{
|
2024-07-19 16:27:50 +02:00
|
|
|
|
KeInitializeDpc(Config->dpc, HeartbeatDpcRoutine, Config);
|
|
|
|
|
KeInitializeTimer(Config->timer);
|
|
|
|
|
KeSetTimer(Config->timer, GenerateHeartbeatDueTime(), Config->dpc);
|
2024-05-04 17:43:01 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FORCEINLINE
|
|
|
|
|
STATIC
|
|
|
|
|
VOID
|
|
|
|
|
SetHeartbeatActive(_Inout_ PHEARTBEAT_CONFIGURATION Configuration)
|
|
|
|
|
{
|
|
|
|
|
InterlockedIncrement(&Configuration->active);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FORCEINLINE
|
|
|
|
|
STATIC
|
|
|
|
|
VOID
|
2024-05-06 09:08:39 +02:00
|
|
|
|
SetHeartbeatInactive(_Inout_ PHEARTBEAT_CONFIGURATION Configuration)
|
2024-05-04 17:43:01 +02:00
|
|
|
|
{
|
|
|
|
|
InterlockedDecrement(&Configuration->active);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Blocks until heartbeat execution is complete */
|
|
|
|
|
FORCEINLINE
|
|
|
|
|
STATIC
|
|
|
|
|
VOID
|
|
|
|
|
WaitForHeartbeatCompletion(_In_ PHEARTBEAT_CONFIGURATION Configuration)
|
|
|
|
|
{
|
|
|
|
|
while (Configuration->active)
|
2024-05-05 12:42:22 +02:00
|
|
|
|
YieldProcessor();
|
2024-05-04 17:43:01 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-05 12:42:22 +02:00
|
|
|
|
FORCEINLINE
|
|
|
|
|
STATIC
|
|
|
|
|
VOID
|
|
|
|
|
IncrementHeartbeatCounter(_In_ PHEARTBEAT_CONFIGURATION Configuration)
|
|
|
|
|
{
|
|
|
|
|
InterlockedIncrement(&Configuration->counter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FORCEINLINE
|
|
|
|
|
STATIC
|
|
|
|
|
PHEARTBEAT_PACKET
|
2024-07-19 16:27:50 +02:00
|
|
|
|
BuildHeartbeatPacket(_In_ UINT32 Size)
|
2024-05-05 12:42:22 +02:00
|
|
|
|
{
|
2024-08-01 06:21:53 +02:00
|
|
|
|
PIRP_QUEUE_HEAD queue = GetIrpQueueHead();
|
2024-07-19 16:27:50 +02:00
|
|
|
|
PHEARTBEAT_PACKET packet = NULL;
|
2024-05-11 14:54:58 +02:00
|
|
|
|
|
2024-07-19 16:27:50 +02:00
|
|
|
|
packet = ImpExAllocatePool2(POOL_FLAG_NON_PAGED, Size, POOL_TAG_HEARTBEAT);
|
2024-05-05 12:42:22 +02:00
|
|
|
|
|
|
|
|
|
if (!packet)
|
|
|
|
|
return NULL;
|
2024-05-05 13:07:05 +02:00
|
|
|
|
|
2024-05-11 14:54:58 +02:00
|
|
|
|
INIT_HEARTBEAT_PACKET(packet);
|
2024-05-05 13:07:05 +02:00
|
|
|
|
|
2024-05-12 09:32:10 +02:00
|
|
|
|
KeAcquireGuardedMutex(&queue->lock);
|
2024-05-05 14:36:25 +02:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Its important to remember that since we query the packet metrics before
|
|
|
|
|
* the metrics are incremented for the current packets they will always be 1
|
|
|
|
|
* less then whats noted.
|
|
|
|
|
*/
|
2024-05-05 13:07:05 +02:00
|
|
|
|
packet->total_heartbeats_completed = queue->total_heartbeats_completed;
|
2024-08-01 06:21:53 +02:00
|
|
|
|
packet->total_irps_completed = queue->total_irps_completed;
|
|
|
|
|
packet->total_reports_completed = queue->total_reports_completed;
|
2024-07-19 16:27:50 +02:00
|
|
|
|
|
2024-05-12 09:32:10 +02:00
|
|
|
|
KeReleaseGuardedMutex(&queue->lock);
|
2024-05-05 13:07:05 +02:00
|
|
|
|
|
|
|
|
|
return packet;
|
2024-05-05 12:42:22 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-04 17:43:01 +02:00
|
|
|
|
STATIC
|
|
|
|
|
VOID
|
2024-06-09 09:22:22 +02:00
|
|
|
|
HeartbeatWorkItem(_In_ PDEVICE_OBJECT DeviceObject, _In_opt_ PVOID Context)
|
2024-05-04 17:43:01 +02:00
|
|
|
|
{
|
2024-06-09 09:22:22 +02:00
|
|
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
2024-05-04 17:43:01 +02:00
|
|
|
|
|
2024-08-04 08:30:31 +02:00
|
|
|
|
NT_ASSERT(Context != NULL);
|
|
|
|
|
|
2024-06-09 09:22:22 +02:00
|
|
|
|
if (!ARGUMENT_PRESENT(Context))
|
2024-05-04 17:43:01 +02:00
|
|
|
|
return;
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
UINT32 packet_size = 0;
|
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
PHEARTBEAT_PACKET packet = NULL;
|
|
|
|
|
PHEARTBEAT_CONFIGURATION config = (PHEARTBEAT_CONFIGURATION)Context;
|
2024-05-05 13:07:05 +02:00
|
|
|
|
|
|
|
|
|
DEBUG_VERBOSE("Heartbeat timer alerted. Generating heartbeat packet.");
|
2024-05-04 17:43:01 +02:00
|
|
|
|
|
|
|
|
|
SetHeartbeatActive(config);
|
|
|
|
|
|
2024-05-11 14:54:58 +02:00
|
|
|
|
packet_size = CryptRequestRequiredBufferLength(sizeof(HEARTBEAT_PACKET));
|
2024-08-01 06:21:53 +02:00
|
|
|
|
packet = BuildHeartbeatPacket(packet_size);
|
2024-05-05 12:42:22 +02:00
|
|
|
|
|
2024-05-05 13:07:05 +02:00
|
|
|
|
if (packet) {
|
2024-05-11 14:54:58 +02:00
|
|
|
|
status = CryptEncryptBuffer(packet, packet_size);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
|
DEBUG_ERROR("CryptEncryptBuffer: %lx", status);
|
|
|
|
|
ImpExFreePoolWithTag(packet, POOL_TAG_HEARTBEAT);
|
2024-06-09 09:22:22 +02:00
|
|
|
|
goto queue_next;
|
2024-05-11 14:54:58 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-05-11 17:27:18 +02:00
|
|
|
|
IrpQueueSchedulePacket(packet, packet_size);
|
2024-05-05 13:07:05 +02:00
|
|
|
|
IncrementHeartbeatCounter(config);
|
|
|
|
|
}
|
2024-05-05 12:42:22 +02:00
|
|
|
|
|
2024-06-09 09:22:22 +02:00
|
|
|
|
queue_next:
|
|
|
|
|
/* 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
|
2024-08-01 06:21:53 +02:00
|
|
|
|
HeartbeatDpcRoutine(
|
|
|
|
|
_In_ PKDPC Dpc,
|
|
|
|
|
_In_opt_ PVOID DeferredContext,
|
|
|
|
|
_In_opt_ PVOID SystemArgument1,
|
|
|
|
|
_In_opt_ PVOID SystemArgument2)
|
2024-06-09 09:22:22 +02:00
|
|
|
|
{
|
|
|
|
|
UNREFERENCED_PARAMETER(Dpc);
|
|
|
|
|
UNREFERENCED_PARAMETER(SystemArgument1);
|
|
|
|
|
UNREFERENCED_PARAMETER(SystemArgument2);
|
|
|
|
|
|
|
|
|
|
if (!ARGUMENT_PRESENT(DeferredContext))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
PHEARTBEAT_CONFIGURATION config = (PHEARTBEAT_CONFIGURATION)DeferredContext;
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
IoQueueWorkItem(
|
|
|
|
|
config->work_item,
|
|
|
|
|
HeartbeatWorkItem,
|
|
|
|
|
NormalWorkQueue,
|
|
|
|
|
config);
|
2024-05-04 17:43:01 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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
|
2024-05-15 11:48:09 +02:00
|
|
|
|
InitialiseHeartbeatConfiguration(_Out_ PHEARTBEAT_CONFIGURATION Configuration)
|
2024-05-04 17:43:01 +02:00
|
|
|
|
{
|
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
|
2024-08-01 06:21:53 +02:00
|
|
|
|
Configuration->counter = 0;
|
|
|
|
|
Configuration->active = FALSE;
|
|
|
|
|
Configuration->seed = GenerateRandSeed();
|
2024-05-04 17:43:01 +02:00
|
|
|
|
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);
|
|
|
|
|
}
|