2023-08-22 19:32:25 +02:00
|
|
|
|
#include "integrity.h"
|
|
|
|
|
|
|
|
|
|
#include "common.h"
|
2023-09-01 13:46:31 +02:00
|
|
|
|
#include "driver.h"
|
2023-08-22 19:32:25 +02:00
|
|
|
|
#include "modules.h"
|
2023-10-21 12:25:08 +02:00
|
|
|
|
#include "callbacks.h"
|
2023-11-09 08:30:59 +01:00
|
|
|
|
#include "ioctl.h"
|
2024-01-07 05:13:41 +01:00
|
|
|
|
#include "imports.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 <initguid.h>
|
|
|
|
|
#include <devpkey.h>
|
2023-09-01 12:56:27 +02:00
|
|
|
|
|
2023-08-25 09:38:45 +02:00
|
|
|
|
typedef struct _INTEGRITY_CHECK_HEADER
|
|
|
|
|
{
|
2023-12-13 05:06:27 +01:00
|
|
|
|
INT executable_section_count;
|
2023-10-05 08:27:17 +02:00
|
|
|
|
LONG total_packet_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
|
|
|
|
|
2023-09-27 15:10:12 +02:00
|
|
|
|
#define MAX_MODULE_PATH 256
|
|
|
|
|
|
|
|
|
|
typedef struct _PROCESS_MODULE_INFORMATION
|
|
|
|
|
{
|
2023-12-13 05:06:27 +01:00
|
|
|
|
PVOID module_base;
|
2023-10-05 08:27:17 +02:00
|
|
|
|
SIZE_T module_size;
|
2023-12-13 05:06:27 +01: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
|
|
|
|
|
|
|
|
|
typedef struct _PROCESS_MODULE_VALIDATION_RESULT
|
|
|
|
|
{
|
2023-10-05 08:27:17 +02:00
|
|
|
|
INT 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
|
|
|
|
|
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
|
|
|
|
|
GetModuleInformationByName(_Out_ PRTL_MODULE_EXTENDED_INFO ModuleInfo, _In_ LPCSTR ModuleName);
|
|
|
|
|
|
|
|
|
|
STATIC
|
|
|
|
|
NTSTATUS
|
|
|
|
|
StoreModuleExecutableRegionsInBuffer(_Outptr_result_bytebuffer_(*BytesWritten) PVOID* Buffer,
|
|
|
|
|
_In_ PVOID ModuleBase,
|
|
|
|
|
_In_ SIZE_T ModuleSize,
|
|
|
|
|
_Out_ _Deref_out_range_(>, 0) PSIZE_T BytesWritten);
|
|
|
|
|
|
|
|
|
|
STATIC
|
|
|
|
|
NTSTATUS
|
|
|
|
|
MapDiskImageIntoVirtualAddressSpace(_Inout_ PHANDLE SectionHandle,
|
|
|
|
|
_Outptr_result_bytebuffer_(*Size) PVOID* Section,
|
|
|
|
|
_In_ PUNICODE_STRING Path,
|
|
|
|
|
_Out_ _Deref_out_range_(>, 0) PSIZE_T Size);
|
|
|
|
|
|
|
|
|
|
STATIC
|
|
|
|
|
NTSTATUS
|
|
|
|
|
ComputeHashOfBuffer(_In_ PVOID Buffer,
|
|
|
|
|
_In_ ULONG BufferSize,
|
|
|
|
|
_Outptr_result_bytebuffer_(*HashResultSize) PVOID* HashResult,
|
|
|
|
|
_Out_ _Deref_out_range_(>, 0) PULONG HashResultSize);
|
|
|
|
|
|
|
|
|
|
STATIC
|
|
|
|
|
VOID
|
|
|
|
|
GetNextSMBIOSStructureInTable(_Inout_ PSMBIOS_TABLE_HEADER* CurrentStructure);
|
|
|
|
|
|
|
|
|
|
STATIC
|
|
|
|
|
NTSTATUS
|
|
|
|
|
GetStringAtIndexFromSMBIOSTable(_In_ PSMBIOS_TABLE_HEADER Table,
|
|
|
|
|
_In_ INT Index,
|
|
|
|
|
_In_ PVOID Buffer,
|
|
|
|
|
_In_ SIZE_T BufferSize);
|
|
|
|
|
|
|
|
|
|
STATIC
|
|
|
|
|
NTSTATUS
|
|
|
|
|
GetAverageReadTimeAtRoutine(_In_ PVOID RoutineAddress, _Out_ PUINT64 AverageTime);
|
|
|
|
|
|
|
|
|
|
STATIC
|
|
|
|
|
NTSTATUS
|
|
|
|
|
RegistryPathQueryTestSigningCallback(IN PWSTR ValueName,
|
|
|
|
|
IN ULONG ValueType,
|
|
|
|
|
IN PVOID ValueData,
|
|
|
|
|
IN ULONG ValueLength,
|
|
|
|
|
IN PVOID Context,
|
|
|
|
|
IN PVOID EntryContext);
|
2023-10-08 06:24:54 +02:00
|
|
|
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
2023-12-13 05:06:27 +01: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, ComputeHashOfBuffer)
|
2024-01-02 23:29:23 +01:00
|
|
|
|
// # pragma alloc_text(PAGE, VerifyInMemoryImageVsDiskImage)
|
2023-12-13 05:06:27 +01:00
|
|
|
|
# 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)
|
|
|
|
|
# pragma alloc_text(PAGE, RegistryPathQueryTestSigningCallback)
|
|
|
|
|
// #pragma alloc_text(PAGE, DetermineIfTestSigningIsEnabled)
|
2023-10-08 06:24:54 +02:00
|
|
|
|
#endif
|
|
|
|
|
|
2023-08-23 14:14:20 +02:00
|
|
|
|
/*
|
2023-12-13 05:06:27 +01: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-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
|
|
|
|
{
|
2023-10-09 20:19:51 +02:00
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
2023-12-27 04:35:46 +01:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
LPCSTR driver_name = NULL;
|
2023-12-13 05:06:27 +01:00
|
|
|
|
SYSTEM_MODULES modules = {0};
|
|
|
|
|
PRTL_MODULE_EXTENDED_INFO driver_info = NULL;
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-12-27 04:35:46 +01:00
|
|
|
|
GetDriverName(&driver_name);
|
|
|
|
|
|
|
|
|
|
if (!driver_name)
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("GetDriverName failed with no status.");
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
status = GetSystemModuleInformation(&modules);
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("GetSystemModuleInformation failed with status %x", status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-12-27 04:35:46 +01:00
|
|
|
|
driver_info = FindSystemModuleByName(driver_name, &modules);
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-11-09 12:11:02 +01:00
|
|
|
|
if (!driver_info)
|
|
|
|
|
{
|
2023-12-23 19:52:55 +01:00
|
|
|
|
DEBUG_ERROR("FindSystemModuleByName failed with no status code");
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpExFreePoolWithTag(modules.address, SYSTEM_MODULES_POOL);
|
2023-11-09 12:11:02 +01:00
|
|
|
|
return STATUS_NOT_FOUND;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-09 08:30:59 +01:00
|
|
|
|
status = ValidateIrpOutputBuffer(Irp, sizeof(ULONG));
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
2023-12-23 19:52:55 +01:00
|
|
|
|
DEBUG_ERROR("ValidateIrpOutputBuffer failed with status %x", status);
|
2023-11-09 08:30:59 +01:00
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
Irp->IoStatus.Information = sizeof(ULONG);
|
|
|
|
|
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, &driver_info->ImageSize, sizeof(ULONG));
|
2023-10-03 18:03:55 +02:00
|
|
|
|
|
2023-11-09 08:30:59 +01:00
|
|
|
|
end:
|
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
if (modules.address)
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpExFreePoolWithTag(modules.address, SYSTEM_MODULES_POOL);
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +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
|
2023-12-13 05:06:27 +01:00
|
|
|
|
GetModuleInformationByName(_Out_ PRTL_MODULE_EXTENDED_INFO ModuleInfo, _In_ LPCSTR ModuleName)
|
2023-08-22 19:32:25 +02:00
|
|
|
|
{
|
2023-10-09 20:19:51 +02:00
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
2023-12-27 04:35:46 +01:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
LPCSTR driver_name = NULL;
|
2023-12-13 05:06:27 +01:00
|
|
|
|
SYSTEM_MODULES modules = {0};
|
|
|
|
|
PRTL_MODULE_EXTENDED_INFO driver_info = NULL;
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-12-27 04:35:46 +01:00
|
|
|
|
GetDriverName(&driver_name);
|
|
|
|
|
|
|
|
|
|
if (!driver_name)
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("GetDriverName failed with no status.");
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
status = GetSystemModuleInformation(&modules);
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("GetSystemModuleInformation failed with status %x", status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-12-27 04:35:46 +01:00
|
|
|
|
driver_info = FindSystemModuleByName(driver_name, &modules);
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-11-09 12:11:02 +01:00
|
|
|
|
if (!driver_info)
|
|
|
|
|
{
|
2023-12-23 19:52:55 +01:00
|
|
|
|
DEBUG_ERROR("FindSystemModuleByName failed with no status");
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpExFreePoolWithTag(modules.address, SYSTEM_MODULES_POOL);
|
2023-11-09 12:11:02 +01:00
|
|
|
|
return STATUS_NOT_FOUND;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
ModuleInfo->FileNameOffset = driver_info->FileNameOffset;
|
2023-12-13 05:06:27 +01:00
|
|
|
|
ModuleInfo->ImageBase = driver_info->ImageBase;
|
|
|
|
|
ModuleInfo->ImageSize = driver_info->ImageSize;
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
RtlCopyMemory(
|
2023-12-13 05:06:27 +01:00
|
|
|
|
ModuleInfo->FullPathName, driver_info->FullPathName, sizeof(ModuleInfo->FullPathName));
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
if (modules.address)
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpExFreePoolWithTag(modules.address, SYSTEM_MODULES_POOL);
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +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
|
|
|
|
|
|
2023-09-27 06:22:14 +02:00
|
|
|
|
STATIC
|
2023-10-03 18:03:55 +02:00
|
|
|
|
NTSTATUS
|
2023-12-13 05:06:27 +01:00
|
|
|
|
StoreModuleExecutableRegionsInBuffer(_Outptr_result_bytebuffer_(*BytesWritten) PVOID* Buffer,
|
|
|
|
|
_In_ PVOID ModuleBase,
|
|
|
|
|
_In_ SIZE_T ModuleSize,
|
|
|
|
|
_Out_ _Deref_out_range_(>, 0) PSIZE_T BytesWritten)
|
2023-08-31 13:21:49 +02:00
|
|
|
|
{
|
2023-10-09 20:19:51 +02:00
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
2024-01-02 23:29:23 +01:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
2023-12-13 05:06:27 +01:00
|
|
|
|
PIMAGE_DOS_HEADER dos_header = NULL;
|
|
|
|
|
PLOCAL_NT_HEADER nt_header = NULL;
|
|
|
|
|
PIMAGE_SECTION_HEADER section = NULL;
|
|
|
|
|
ULONG total_packet_size = 0;
|
|
|
|
|
ULONG num_sections = 0;
|
|
|
|
|
ULONG num_executable_sections = 0;
|
|
|
|
|
UINT64 buffer_base = 0;
|
|
|
|
|
ULONG bytes_returned = 0;
|
|
|
|
|
MM_COPY_ADDRESS address = {0};
|
|
|
|
|
ULONG buffer_size = 0;
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!ModuleBase || !ModuleSize)
|
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
/*
|
2023-12-13 05:06:27 +01: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.
|
|
|
|
|
*/
|
2023-10-10 15:52:42 +02:00
|
|
|
|
buffer_size = ModuleSize + sizeof(INTEGRITY_CHECK_HEADER);
|
|
|
|
|
|
|
|
|
|
*BytesWritten = 0;
|
2024-01-07 05:13:41 +01:00
|
|
|
|
*Buffer = ImpExAllocatePool2(POOL_FLAG_NON_PAGED, buffer_size, POOL_TAG_INTEGRITY);
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-10-09 20:19:51 +02:00
|
|
|
|
if (*Buffer == NULL)
|
2023-10-05 08:27:17 +02:00
|
|
|
|
return STATUS_MEMORY_NOT_ALLOCATED;
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
/*
|
2023-12-13 05:06:27 +01:00
|
|
|
|
* Note: Verifier doesn't like it when we map the module so rather then mapping it to our
|
|
|
|
|
* address space we will simply use MmCopyMemory on the module to avoid upsetting verifier
|
|
|
|
|
* :)
|
|
|
|
|
*/
|
2023-10-05 08:27:17 +02:00
|
|
|
|
dos_header = (PIMAGE_DOS_HEADER)ModuleBase;
|
|
|
|
|
|
|
|
|
|
/*
|
2023-12-13 05:06:27 +01:00
|
|
|
|
* The IMAGE_DOS_HEADER.e_lfanew stores the offset of the IMAGE_NT_HEADER from the base
|
|
|
|
|
* of the image.
|
|
|
|
|
*/
|
2023-10-05 08:27:17 +02:00
|
|
|
|
nt_header = (struct _IMAGE_NT_HEADERS64*)((UINT64)ModuleBase + dos_header->e_lfanew);
|
2023-10-21 12:25:08 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
num_sections = nt_header->FileHeader.NumberOfSections;
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
/*
|
2023-12-13 05:06:27 +01:00
|
|
|
|
* The IMAGE_FIRST_SECTION macro takes in an IMAGE_NT_HEADER and returns the address of
|
|
|
|
|
* the first section of the PE file.
|
|
|
|
|
*/
|
|
|
|
|
section = IMAGE_FIRST_SECTION(nt_header);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
buffer_base = (UINT64)*Buffer + sizeof(INTEGRITY_CHECK_HEADER);
|
2023-12-13 05:06:27 +01:00
|
|
|
|
|
2024-01-02 23:29:23 +01:00
|
|
|
|
for (ULONG index = 0; index < num_sections - 1; index++)
|
2023-10-05 08:27:17 +02:00
|
|
|
|
{
|
2024-01-02 23:29:23 +01:00
|
|
|
|
/* create a function for this instead, check for writeable sections use !*/
|
2023-10-05 08:27:17 +02:00
|
|
|
|
if (section->Characteristics & IMAGE_SCN_MEM_EXECUTE)
|
|
|
|
|
{
|
|
|
|
|
/*
|
2023-12-13 05:06:27 +01:00
|
|
|
|
* Note: MmCopyMemory will fail on discardable sections.
|
|
|
|
|
*/
|
2023-10-05 08:27:17 +02:00
|
|
|
|
address.VirtualAddress = section;
|
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
status = ImpMmCopyMemory((UINT64)buffer_base + total_packet_size,
|
|
|
|
|
address,
|
|
|
|
|
sizeof(IMAGE_SECTION_HEADER),
|
|
|
|
|
MM_COPY_MEMORY_VIRTUAL,
|
|
|
|
|
&bytes_returned);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("MmCopyMemory failed with status %x", status);
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpExFreePoolWithTag(*Buffer, POOL_TAG_INTEGRITY);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
*Buffer = NULL;
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
address.VirtualAddress = (UINT64)ModuleBase + section->PointerToRawData;
|
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
status = ImpMmCopyMemory((UINT64)buffer_base + total_packet_size +
|
|
|
|
|
sizeof(IMAGE_SECTION_HEADER),
|
|
|
|
|
address,
|
|
|
|
|
section->SizeOfRawData,
|
|
|
|
|
MM_COPY_MEMORY_VIRTUAL,
|
|
|
|
|
&bytes_returned);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("MmCopyMemory failed with status %x", status);
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpExFreePoolWithTag(*Buffer, POOL_TAG_INTEGRITY);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
*Buffer = NULL;
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
total_packet_size += section->SizeOfRawData + sizeof(IMAGE_SECTION_HEADER);
|
|
|
|
|
num_executable_sections += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
section++;
|
|
|
|
|
}
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-12-13 05:06:27 +01:00
|
|
|
|
INTEGRITY_CHECK_HEADER header = {0};
|
2023-10-05 08:27:17 +02:00
|
|
|
|
header.executable_section_count = num_executable_sections;
|
2023-12-13 05:06:27 +01:00
|
|
|
|
header.total_packet_size = total_packet_size + sizeof(INTEGRITY_CHECK_HEADER);
|
|
|
|
|
RtlCopyMemory(*Buffer, &header, sizeof(INTEGRITY_CHECK_HEADER));
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +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
|
2023-12-13 05:06:27 +01:00
|
|
|
|
MapDiskImageIntoVirtualAddressSpace(_Inout_ PHANDLE SectionHandle,
|
|
|
|
|
_Outptr_result_bytebuffer_(*Size) PVOID* Section,
|
|
|
|
|
_In_ PUNICODE_STRING Path,
|
|
|
|
|
_Out_ _Deref_out_range_(>, 0) PSIZE_T Size)
|
2023-08-28 17:00:52 +02:00
|
|
|
|
{
|
2023-10-09 20:19:51 +02:00
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
2023-12-23 19:52:55 +01:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
2023-12-13 05:06:27 +01:00
|
|
|
|
HANDLE file_handle = NULL;
|
|
|
|
|
OBJECT_ATTRIBUTES object_attributes = {0};
|
|
|
|
|
PIO_STATUS_BLOCK pio_block = NULL;
|
|
|
|
|
UNICODE_STRING path = {0};
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2023-10-10 15:52:42 +02:00
|
|
|
|
*Section = NULL;
|
2023-12-13 05:06:27 +01:00
|
|
|
|
*Size = 0;
|
2023-10-10 15:52:42 +02:00
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpRtlInitUnicodeString(&path, Path->Buffer);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
InitializeObjectAttributes(&object_attributes, &path, OBJ_KERNEL_HANDLE, NULL, NULL);
|
2023-12-13 05:06:27 +01:00
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
status =
|
|
|
|
|
ImpZwOpenFile(&file_handle, GENERIC_READ, &object_attributes, &pio_block, NULL, NULL);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
2023-12-23 19:52:55 +01:00
|
|
|
|
DEBUG_ERROR("ZwOpenFile failed with status %x", status);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
object_attributes.ObjectName = NULL;
|
|
|
|
|
|
2023-09-05 19:20:21 +02:00
|
|
|
|
/*
|
2023-12-13 05:06:27 +01:00
|
|
|
|
* Its important that we set the SEC_IMAGE flag with the PAGE_READONLY
|
|
|
|
|
* flag as we are mapping an executable image.
|
|
|
|
|
*/
|
2024-01-07 05:13:41 +01:00
|
|
|
|
status = ImpZwCreateSection(SectionHandle,
|
|
|
|
|
SECTION_ALL_ACCESS,
|
|
|
|
|
&object_attributes,
|
|
|
|
|
NULL,
|
|
|
|
|
PAGE_READONLY,
|
|
|
|
|
SEC_IMAGE,
|
|
|
|
|
file_handle);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("ZwCreateSection failed with status %x", status);
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpZwClose(file_handle);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
*SectionHandle = NULL;
|
|
|
|
|
return status;
|
|
|
|
|
}
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("ObReferenceObjectByHandle failed with status %x", status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
2023-10-05 08:27:17 +02:00
|
|
|
|
/*
|
2023-12-13 05:06:27 +01:00
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
status = ZwMapViewOfSection(*SectionHandle,
|
|
|
|
|
ZwCurrentProcess(),
|
|
|
|
|
Section,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
Size,
|
|
|
|
|
ViewUnmap,
|
|
|
|
|
MEM_TOP_DOWN,
|
|
|
|
|
PAGE_READONLY);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
2024-01-01 17:45:40 +01:00
|
|
|
|
/* caller is responsible for closing handle on success, therefore null it */
|
2023-10-05 08:27:17 +02:00
|
|
|
|
DEBUG_ERROR("ZwMapViewOfSection failed with status %x", status);
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpZwClose(file_handle);
|
|
|
|
|
ImpZwClose(*SectionHandle);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
*SectionHandle = NULL;
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpZwClose(file_handle);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
return status;
|
2023-09-01 12:56:27 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-27 06:22:14 +02:00
|
|
|
|
STATIC
|
2023-10-03 18:03:55 +02:00
|
|
|
|
NTSTATUS
|
2023-12-13 05:06:27 +01:00
|
|
|
|
ComputeHashOfBuffer(_In_ PVOID Buffer,
|
|
|
|
|
_In_ ULONG BufferSize,
|
|
|
|
|
_Outptr_result_bytebuffer_(*HashResultSize) PVOID* HashResult,
|
|
|
|
|
_Out_ _Deref_out_range_(>, 0) PULONG HashResultSize)
|
2023-09-01 12:56:27 +02:00
|
|
|
|
{
|
2023-10-09 20:19:51 +02:00
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
2024-01-01 17:45:40 +01:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
2023-12-13 05:06:27 +01:00
|
|
|
|
BCRYPT_ALG_HANDLE algo_handle = NULL;
|
|
|
|
|
BCRYPT_HASH_HANDLE hash_handle = NULL;
|
|
|
|
|
ULONG bytes_copied = 0;
|
|
|
|
|
ULONG resulting_hash_size = 0;
|
|
|
|
|
ULONG hash_object_size = 0;
|
|
|
|
|
PCHAR hash_object = NULL;
|
|
|
|
|
PCHAR resulting_hash = NULL;
|
|
|
|
|
|
|
|
|
|
*HashResult = NULL;
|
2023-10-10 15:52:42 +02:00
|
|
|
|
*HashResultSize = 0;
|
|
|
|
|
|
2023-12-13 05:06:27 +01:00
|
|
|
|
status = BCryptOpenAlgorithmProvider(&algo_handle, BCRYPT_SHA256_ALGORITHM, NULL, NULL);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("BCryptOpenAlogrithmProvider failed with status %x", status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2023-12-13 05:06:27 +01:00
|
|
|
|
* Request the size of the hash object buffer, this is different then the buffer that
|
|
|
|
|
* will store the resulting hash, instead this will be used to store the hash object
|
|
|
|
|
* used to create the hash.
|
|
|
|
|
*/
|
|
|
|
|
status = BCryptGetProperty(algo_handle,
|
|
|
|
|
BCRYPT_OBJECT_LENGTH,
|
|
|
|
|
(PCHAR)&hash_object_size,
|
|
|
|
|
sizeof(ULONG),
|
|
|
|
|
&bytes_copied,
|
|
|
|
|
NULL);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("BCryptGetProperty failed with status %x", status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
hash_object = ImpExAllocatePool2(POOL_FLAG_NON_PAGED, hash_object_size, POOL_TAG_INTEGRITY);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!hash_object)
|
|
|
|
|
{
|
|
|
|
|
status = STATUS_MEMORY_NOT_ALLOCATED;
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2023-12-13 05:06:27 +01:00
|
|
|
|
* This call gets the size of the resulting hash, which we will use to allocate the
|
|
|
|
|
* resulting hash buffer.
|
|
|
|
|
*/
|
|
|
|
|
status = BCryptGetProperty(algo_handle,
|
|
|
|
|
BCRYPT_HASH_LENGTH,
|
|
|
|
|
(PCHAR)&resulting_hash_size,
|
|
|
|
|
sizeof(ULONG),
|
|
|
|
|
&bytes_copied,
|
|
|
|
|
NULL);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("BCryptGetProperty failed with status %x", status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-13 05:06:27 +01:00
|
|
|
|
resulting_hash =
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpExAllocatePool2(POOL_FLAG_NON_PAGED, resulting_hash_size, POOL_TAG_INTEGRITY);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!resulting_hash)
|
|
|
|
|
{
|
|
|
|
|
status = STATUS_MEMORY_NOT_ALLOCATED;
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2023-12-13 05:06:27 +01:00
|
|
|
|
* Here we create our hash object and store it in the hash_object buffer.
|
|
|
|
|
*/
|
2023-10-05 08:27:17 +02:00
|
|
|
|
status = BCryptCreateHash(
|
2023-12-13 05:06:27 +01:00
|
|
|
|
algo_handle, &hash_handle, hash_object, hash_object_size, NULL, NULL, NULL);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("BCryptCreateHash failed with status %x", status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2023-12-13 05:06:27 +01:00
|
|
|
|
* This function hashes the buffer, but does NOT store it in our resulting buffer yet,
|
|
|
|
|
* we need to call BCryptFinishHash to retrieve the final hash.
|
|
|
|
|
*/
|
|
|
|
|
status = BCryptHashData(hash_handle, Buffer, BufferSize, NULL);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("BCryptHashData failed with status %x", status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2023-12-13 05:06:27 +01:00
|
|
|
|
* As said in the previous comment, this is where we retrieve the final hash and store
|
|
|
|
|
* it in our output buffer.
|
|
|
|
|
*/
|
|
|
|
|
status = BCryptFinishHash(hash_handle, resulting_hash, resulting_hash_size, NULL);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("BCryptFinishHash failed with status %x", status);
|
2023-10-30 12:57:24 +01:00
|
|
|
|
goto end;
|
2023-10-05 08:27:17 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-12-13 05:06:27 +01:00
|
|
|
|
*HashResult = resulting_hash;
|
2023-10-05 08:27:17 +02:00
|
|
|
|
*HashResultSize = resulting_hash_size;
|
2023-09-01 12:56:27 +02:00
|
|
|
|
|
|
|
|
|
end:
|
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
if (algo_handle)
|
|
|
|
|
BCryptCloseAlgorithmProvider(algo_handle, NULL);
|
2023-09-01 12:56:27 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
if (hash_handle)
|
|
|
|
|
BCryptDestroyHash(hash_handle);
|
2023-09-01 12:56:27 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
if (hash_object)
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpExFreePoolWithTag(hash_object, POOL_TAG_INTEGRITY);
|
2023-08-31 17:49:04 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +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
|
|
|
|
{
|
2023-10-10 15:52:42 +02:00
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
2023-12-23 19:52:55 +01:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
2023-12-27 04:35:46 +01:00
|
|
|
|
LPCSTR driver_name = NULL;
|
2023-12-13 05:06:27 +01:00
|
|
|
|
SIZE_T bytes_written = NULL;
|
|
|
|
|
PVOID buffer = NULL;
|
|
|
|
|
RTL_MODULE_EXTENDED_INFO module_info = {0};
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2023-12-27 04:35:46 +01:00
|
|
|
|
GetDriverName(&driver_name);
|
|
|
|
|
|
|
|
|
|
if (!driver_name)
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("GetDriverName failed with no status");
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = GetModuleInformationByName(&module_info, driver_name);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2023-10-09 20:19:51 +02:00
|
|
|
|
if (!NT_SUCCESS(status) || !module_info.ImageBase || !module_info.ImageSize)
|
2023-10-05 08:27:17 +02:00
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("GetModuleInformationByName failed with status %x", status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = StoreModuleExecutableRegionsInBuffer(
|
2023-12-13 05:06:27 +01:00
|
|
|
|
&buffer, module_info.ImageBase, module_info.ImageSize, &bytes_written);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("StoreModuleExecutableRegionsInBuffe failed with status %x", status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-11-09 08:30:59 +01:00
|
|
|
|
status = ValidateIrpOutputBuffer(Irp, bytes_written);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
2023-12-23 19:52:55 +01:00
|
|
|
|
DEBUG_ERROR("ValidateIrpOutputBuffer failed with status %x", status);
|
2023-11-09 08:30:59 +01:00
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
Irp->IoStatus.Information = bytes_written;
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-12-13 05:06:27 +01:00
|
|
|
|
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, buffer, bytes_written);
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-11-09 08:30:59 +01:00
|
|
|
|
end:
|
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
if (buffer)
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpExFreePoolWithTag(buffer, POOL_TAG_INTEGRITY);
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +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:
|
|
|
|
|
*
|
|
|
|
|
* 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.)
|
|
|
|
|
*
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
|
|
|
|
* source: https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_2.7.1.pdf
|
|
|
|
|
*/
|
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
|
|
|
|
{
|
2023-10-09 20:19:51 +02:00
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
2023-12-13 05:06:27 +01:00
|
|
|
|
PCHAR string_section_start =
|
|
|
|
|
(PCHAR)((UINT64)*CurrentStructure + (*CurrentStructure)->Length);
|
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
PCHAR current_char_in_strings = string_section_start;
|
2023-12-13 05:06:27 +01:00
|
|
|
|
PCHAR next_char_in_strings = string_section_start + 1;
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-11-08 16:02:17 +01:00
|
|
|
|
for (;;)
|
2023-09-05 19:20:21 +02:00
|
|
|
|
{
|
2023-12-13 05:06:27 +01:00
|
|
|
|
if (*current_char_in_strings == NULL_TERMINATOR &&
|
|
|
|
|
*next_char_in_strings == NULL_TERMINATOR)
|
2023-10-05 08:27:17 +02:00
|
|
|
|
{
|
2023-12-13 05:06:27 +01:00
|
|
|
|
*CurrentStructure =
|
|
|
|
|
(PSMBIOS_TABLE_HEADER)((UINT64)next_char_in_strings + 1);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
current_char_in_strings++;
|
|
|
|
|
next_char_in_strings++;
|
2023-09-05 19:20:21 +02:00
|
|
|
|
}
|
2023-09-04 15:36:26 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-04 17:00:36 +02:00
|
|
|
|
/*
|
2023-12-13 05:06:27 +01: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.
|
|
|
|
|
*
|
|
|
|
|
* 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-09-27 06:22:14 +02:00
|
|
|
|
STATIC
|
2023-10-03 18:03:55 +02:00
|
|
|
|
NTSTATUS
|
2023-12-13 05:06:27 +01:00
|
|
|
|
GetStringAtIndexFromSMBIOSTable(_In_ PSMBIOS_TABLE_HEADER Table,
|
|
|
|
|
_In_ INT Index,
|
|
|
|
|
_In_ PVOID Buffer,
|
|
|
|
|
_In_ SIZE_T BufferSize)
|
2023-09-04 15:36:26 +02:00
|
|
|
|
{
|
2023-10-09 20:19:51 +02:00
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
2023-12-13 05:06:27 +01:00
|
|
|
|
INT current_string_char_index = 0;
|
|
|
|
|
INT string_count = 0;
|
|
|
|
|
PCHAR current_string_char = (PCHAR)((UINT64)Table + Table->Length);
|
|
|
|
|
PCHAR next_string_char = current_string_char + 1;
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-12-13 05:06:27 +01:00
|
|
|
|
for (;;)
|
2023-10-05 08:27:17 +02:00
|
|
|
|
{
|
|
|
|
|
if (*current_string_char == NULL_TERMINATOR && *next_string_char == NULL_TERMINATOR)
|
|
|
|
|
return STATUS_NOT_FOUND;
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
if (current_string_char_index >= BufferSize)
|
|
|
|
|
return STATUS_BUFFER_TOO_SMALL;
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
if (string_count + 1 == Index)
|
|
|
|
|
{
|
|
|
|
|
if (*current_string_char == NULL_TERMINATOR)
|
|
|
|
|
return STATUS_SUCCESS;
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-12-13 05:06:27 +01:00
|
|
|
|
RtlCopyMemory((UINT64)Buffer + current_string_char_index,
|
|
|
|
|
current_string_char,
|
|
|
|
|
sizeof(CHAR));
|
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
current_string_char_index++;
|
|
|
|
|
goto increment;
|
|
|
|
|
}
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
if (*current_string_char == NULL_TERMINATOR)
|
|
|
|
|
{
|
|
|
|
|
current_string_char_index = 0;
|
|
|
|
|
string_count++;
|
|
|
|
|
}
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
increment:
|
2023-09-05 19:20:21 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
current_string_char++;
|
|
|
|
|
next_string_char++;
|
|
|
|
|
}
|
2023-10-03 18:03:55 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
return STATUS_NOT_FOUND;
|
2023-09-04 15:36:26 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-12-25 16:54:35 +01:00
|
|
|
|
/* for generic intel */
|
2023-12-27 04:35:46 +01:00
|
|
|
|
// #define SMBIOS_SYSTEM_INFORMATION_TYPE_2_TABLE 2
|
|
|
|
|
// #define MOTHERBOARD_SERIAL_CODE_TABLE_INDEX 4
|
2023-12-25 16:54:35 +01:00
|
|
|
|
|
|
|
|
|
/* for testing purposes in vmware */
|
2023-12-27 04:35:46 +01:00
|
|
|
|
// #define VMWARE_SMBIOS_TABLE 1
|
|
|
|
|
// #define VMWARE_SMBIOS_TABLE_INDEX 3
|
2023-12-25 16:54:35 +01:00
|
|
|
|
|
2023-10-03 18:03:55 +02:00
|
|
|
|
NTSTATUS
|
2023-12-27 04:35:46 +01:00
|
|
|
|
ParseSMBIOSTable(_Out_ PVOID Buffer,
|
|
|
|
|
_In_ SIZE_T BufferSize,
|
2023-12-25 16:54:35 +01:00
|
|
|
|
_In_ SMBIOS_TABLE_INDEX TableIndex,
|
2023-12-27 04:35:46 +01:00
|
|
|
|
_In_ ULONG TableSubIndex)
|
2023-09-04 15:36:26 +02:00
|
|
|
|
{
|
2023-10-10 15:52:42 +02:00
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
2023-12-23 19:52:55 +01:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
2023-12-13 05:06:27 +01:00
|
|
|
|
PVOID firmware_table_buffer = NULL;
|
|
|
|
|
ULONG firmware_table_buffer_size = 0;
|
|
|
|
|
ULONG bytes_returned = 0;
|
|
|
|
|
PRAW_SMBIOS_DATA smbios_data = NULL;
|
|
|
|
|
PSMBIOS_TABLE_HEADER smbios_table_header = NULL;
|
2023-10-12 13:27:40 +02:00
|
|
|
|
PRAW_SMBIOS_TABLE_01 smbios_baseboard_information = NULL;
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
status = ImpExGetSystemFirmwareTable(SMBIOS_TABLE, 0, NULL, 0, &firmware_table_buffer_size);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
/*
|
2023-12-13 05:06:27 +01: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.
|
|
|
|
|
*/
|
2023-10-05 08:27:17 +02:00
|
|
|
|
if (firmware_table_buffer_size == NULL)
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("ExGetSystemFirmwareTable call 1 failed to get required buffer size.");
|
|
|
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-13 05:06:27 +01:00
|
|
|
|
firmware_table_buffer =
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpExAllocatePool2(POOL_FLAG_NON_PAGED, firmware_table_buffer_size, POOL_TAG_INTEGRITY);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!firmware_table_buffer)
|
|
|
|
|
return STATUS_MEMORY_NOT_ALLOCATED;
|
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
status = ImpExGetSystemFirmwareTable(
|
2023-12-13 05:06:27 +01:00
|
|
|
|
SMBIOS_TABLE, NULL, firmware_table_buffer, firmware_table_buffer_size, &bytes_returned);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("ExGetSystemFirmwareTable call 2 failed with status %x", status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-13 05:06:27 +01:00
|
|
|
|
smbios_data = (PRAW_SMBIOS_DATA)firmware_table_buffer;
|
2023-10-05 08:27:17 +02:00
|
|
|
|
smbios_table_header = (PSMBIOS_TABLE_HEADER)(&smbios_data->SMBIOSTableData[0]);
|
|
|
|
|
|
|
|
|
|
/*
|
2023-12-13 05:06:27 +01: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
|
|
|
|
|
*/
|
2023-12-25 16:54:35 +01:00
|
|
|
|
while (smbios_table_header->Type != TableIndex)
|
2023-10-05 08:27:17 +02:00
|
|
|
|
GetNextSMBIOSStructureInTable(&smbios_table_header);
|
|
|
|
|
|
2023-12-25 16:54:35 +01:00
|
|
|
|
status =
|
|
|
|
|
GetStringAtIndexFromSMBIOSTable(smbios_table_header, TableSubIndex, Buffer, BufferSize);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("GetStringAtIndexFromSMBIOSTable failed with status %x", status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
2023-09-04 15:36:26 +02:00
|
|
|
|
|
|
|
|
|
end:
|
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
if (firmware_table_buffer)
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpExFreePoolWithTag(firmware_table_buffer, POOL_TAG_INTEGRITY);
|
2023-09-04 15:36:26 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
return status;
|
2023-09-04 15:36:26 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-02 23:29:23 +01:00
|
|
|
|
STATIC
|
|
|
|
|
NTSTATUS
|
|
|
|
|
ComputeHashOfSections(_In_ PIMAGE_SECTION_HEADER DiskSection,
|
|
|
|
|
_In_ PIMAGE_SECTION_HEADER MemorySection,
|
|
|
|
|
_Out_ PVOID* DiskHash,
|
|
|
|
|
_Out_ PULONG DiskHashSize,
|
|
|
|
|
_Out_ PVOID* MemoryHash,
|
|
|
|
|
_Out_ PULONG MemoryHashSize)
|
|
|
|
|
{
|
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
|
|
|
|
|
if (DiskSection->SizeOfRawData != MemorySection->SizeOfRawData)
|
|
|
|
|
{
|
|
|
|
|
DEBUG_WARNING("Executable section sizes differ between images.");
|
|
|
|
|
return STATUS_INVALID_BUFFER_SIZE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = ComputeHashOfBuffer((UINT64)DiskSection + sizeof(IMAGE_SECTION_HEADER),
|
|
|
|
|
DiskSection->SizeOfRawData,
|
|
|
|
|
DiskHash,
|
|
|
|
|
DiskHashSize);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("ComputeHashOfBuffer failed with status %x", status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = ComputeHashOfBuffer((UINT64)MemorySection + sizeof(IMAGE_SECTION_HEADER),
|
|
|
|
|
MemorySection->SizeOfRawData,
|
|
|
|
|
MemoryHash,
|
|
|
|
|
MemoryHashSize);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("ComputeHashOfBuffer 2 failed with status %x", status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
STATIC
|
|
|
|
|
BOOLEAN
|
|
|
|
|
CompareHashes(_In_ PVOID Hash1, _In_ PVOID Hash2, _In_ UINT32 Length)
|
|
|
|
|
{
|
|
|
|
|
if (RtlCompareMemory(Hash1, Hash2, Length) == Length)
|
|
|
|
|
return TRUE;
|
|
|
|
|
else
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typedef struct _VAL_INTEGRITY_HEADER
|
|
|
|
|
{
|
|
|
|
|
INTEGRITY_CHECK_HEADER integrity_check_header;
|
|
|
|
|
IMAGE_SECTION_HEADER section_header;
|
|
|
|
|
CHAR section_base[];
|
|
|
|
|
|
|
|
|
|
} VAL_INTEGRITY_HEADER, *PVAL_INTEGRITY_HEADER;
|
|
|
|
|
|
2023-09-04 17:56:28 +02:00
|
|
|
|
/*
|
2023-12-13 05:06:27 +01: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.
|
|
|
|
|
*
|
|
|
|
|
* 1. Take a snapshot of the modules in the process from our dll
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
|
|
|
|
* 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-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
|
|
|
|
{
|
2023-10-09 20:19:51 +02:00
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
2024-01-02 23:29:23 +01:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
PROCESS_MODULE_VALIDATION_RESULT validation_result = {0};
|
|
|
|
|
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;
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2023-11-09 08:30:59 +01:00
|
|
|
|
status = ValidateIrpInputBuffer(Irp, sizeof(PROCESS_MODULE_INFORMATION));
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
2023-12-23 19:52:55 +01:00
|
|
|
|
DEBUG_ERROR("ValidateIrpInputBuffer failed with status %x", status);
|
2023-11-09 08:30:59 +01:00
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
module_info = (PPROCESS_MODULE_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
|
|
|
|
|
|
GetProtectedProcessEProcess(&process);
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpRtlInitUnicodeString(&module_path, &module_info->module_path);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
/*
|
2023-12-13 05:06:27 +01:00
|
|
|
|
* Attach because the offsets given are from the process' context.
|
|
|
|
|
*/
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpKeStackAttachProcess(process, &apc_state);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
status = StoreModuleExecutableRegionsInBuffer(
|
2024-01-02 23:29:23 +01:00
|
|
|
|
&memory_buffer, module_info->module_base, module_info->module_size, &bytes_written);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpKeUnstackDetachProcess(&apc_state);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("StoreModuleExecutableRegionsInBuffer failed with status %x", status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = MapDiskImageIntoVirtualAddressSpace(
|
2024-01-07 05:13:41 +01:00
|
|
|
|
§ion_handle, §ion, &module_path, §ion_size, 0);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("MapDiskImageIntoVirtualAddressSpace failed with status %x", status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = StoreModuleExecutableRegionsInBuffer(
|
2023-12-13 05:06:27 +01:00
|
|
|
|
&disk_buffer, section, section_size, &bytes_written);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("StoreModuleExecutableRegionsInbuffer 2 failed with status %x", status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-02 23:29:23 +01:00
|
|
|
|
status = ComputeHashOfSections(&memory_buffer->section_header,
|
|
|
|
|
&disk_buffer->section_header,
|
|
|
|
|
&disk_hash,
|
|
|
|
|
&disk_hash_size,
|
|
|
|
|
&memory_hash,
|
|
|
|
|
&memory_hash_size);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
2024-01-02 23:29:23 +01:00
|
|
|
|
DEBUG_ERROR("ComputeHashOfSections failed with status %x", status);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-02 23:29:23 +01:00
|
|
|
|
/*
|
|
|
|
|
* Because each module is passed per IRP we don't need to send any reports
|
|
|
|
|
* to the queue we can simply pass it back to usermode via the same IRP.
|
|
|
|
|
* We also don't need to send any module information since usermode has everything
|
|
|
|
|
* needed to file the report.
|
|
|
|
|
*/
|
2024-01-07 05:13:41 +01:00
|
|
|
|
validation_result.is_module_valid = CompareHashes(disk_hash, memory_hash, memory_hash_size);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2023-11-09 08:30:59 +01:00
|
|
|
|
status = ValidateIrpOutputBuffer(Irp, sizeof(PROCESS_MODULE_VALIDATION_RESULT));
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("Failed to validate IRP output buffer");
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
Irp->IoStatus.Information = sizeof(PROCESS_MODULE_VALIDATION_RESULT);
|
|
|
|
|
|
2023-12-13 05:06:27 +01:00
|
|
|
|
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,
|
|
|
|
|
&validation_result,
|
|
|
|
|
sizeof(PROCESS_MODULE_VALIDATION_RESULT));
|
2023-09-04 17:00:36 +02:00
|
|
|
|
|
|
|
|
|
end:
|
2023-12-13 05:06:27 +01:00
|
|
|
|
|
|
|
|
|
if (section_handle)
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpZwClose(section_handle);
|
2023-09-05 18:04:06 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
if (section)
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpZwUnmapViewOfSection(ZwCurrentProcess(), section);
|
2023-09-04 17:00:36 +02:00
|
|
|
|
|
2024-01-02 23:29:23 +01:00
|
|
|
|
if (memory_buffer)
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpExFreePoolWithTag(memory_buffer, POOL_TAG_INTEGRITY);
|
2023-09-05 11:16:32 +02:00
|
|
|
|
|
2024-01-02 23:29:23 +01:00
|
|
|
|
if (memory_hash)
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpExFreePoolWithTag(memory_hash, POOL_TAG_INTEGRITY);
|
2023-09-04 17:00:36 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
if (disk_buffer)
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpExFreePoolWithTag(disk_buffer, POOL_TAG_INTEGRITY);
|
2023-09-05 18:04:06 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
if (disk_hash)
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpExFreePoolWithTag(disk_hash, POOL_TAG_INTEGRITY);
|
2023-09-05 18:04:06 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
return status;
|
2023-09-06 17:33:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-06 17:44:57 +02:00
|
|
|
|
/*
|
2023-12-13 05:06:27 +01:00
|
|
|
|
* TODO: Query PhysicalDrive%n to get the serial numbers for all harddrives, can use the command
|
|
|
|
|
* "wmic diskdrive" check in console.
|
|
|
|
|
*/
|
2023-10-03 18:03:55 +02:00
|
|
|
|
NTSTATUS
|
2023-12-13 05:06:27 +01:00
|
|
|
|
GetHardDiskDriveSerialNumber(_Inout_ PVOID ConfigDrive0Serial, _In_ SIZE_T ConfigDrive0MaxSize)
|
2023-09-06 17:33:08 +02:00
|
|
|
|
{
|
2023-10-09 20:19:51 +02:00
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
2023-12-23 19:52:55 +01:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
2023-12-13 05:06:27 +01:00
|
|
|
|
HANDLE handle = NULL;
|
|
|
|
|
OBJECT_ATTRIBUTES attributes = {0};
|
|
|
|
|
IO_STATUS_BLOCK status_block = {0};
|
|
|
|
|
STORAGE_PROPERTY_QUERY storage_property = {0};
|
|
|
|
|
STORAGE_DESCRIPTOR_HEADER storage_descriptor_header = {0};
|
|
|
|
|
PSTORAGE_DEVICE_DESCRIPTOR device_descriptor = NULL;
|
|
|
|
|
UNICODE_STRING physical_drive_path = {0};
|
|
|
|
|
PCHAR serial_number = NULL;
|
|
|
|
|
SIZE_T serial_length = 0;
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpRtlInitUnicodeString(&physical_drive_path, L"\\DosDevices\\PhysicalDrive0");
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2023-11-18 11:40:22 +01:00
|
|
|
|
/*
|
2023-12-13 05:06:27 +01:00
|
|
|
|
* No need to use the flag OBJ_FORCE_ACCESS_CHECK since we arent passing a handle given
|
|
|
|
|
* to us from usermode.
|
|
|
|
|
*/
|
|
|
|
|
InitializeObjectAttributes(&attributes,
|
|
|
|
|
&physical_drive_path,
|
|
|
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL);
|
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
status = ImpZwOpenFile(&handle, GENERIC_READ, &attributes, &status_block, NULL, NULL);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
2023-09-06 17:33:08 +02:00
|
|
|
|
{
|
2023-12-23 19:52:55 +01:00
|
|
|
|
DEBUG_ERROR("ZwOpenFile on PhysicalDrive0 failed with status %x", status);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
goto end;
|
2023-09-06 17:33:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
storage_property.PropertyId = StorageDeviceProperty;
|
2023-12-13 05:06:27 +01:00
|
|
|
|
storage_property.QueryType = PropertyStandardQuery;
|
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
status = ImpZwDeviceIoControlFile(handle,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
&status_block,
|
|
|
|
|
IOCTL_STORAGE_QUERY_PROPERTY,
|
|
|
|
|
&storage_property,
|
|
|
|
|
sizeof(STORAGE_PROPERTY_QUERY),
|
|
|
|
|
&storage_descriptor_header,
|
|
|
|
|
sizeof(STORAGE_DESCRIPTOR_HEADER));
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
2023-12-23 19:52:55 +01:00
|
|
|
|
DEBUG_ERROR("ZwDeviceIoControlFile first call failed with status %x", status);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
device_descriptor = ImpExAllocatePool2(
|
2023-12-13 05:06:27 +01:00
|
|
|
|
POOL_FLAG_NON_PAGED, storage_descriptor_header.Size, POOL_TAG_INTEGRITY);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!device_descriptor)
|
|
|
|
|
{
|
|
|
|
|
status = STATUS_MEMORY_NOT_ALLOCATED;
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
status = ImpZwDeviceIoControlFile(handle,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
&status_block,
|
|
|
|
|
IOCTL_STORAGE_QUERY_PROPERTY,
|
|
|
|
|
&storage_property,
|
|
|
|
|
sizeof(STORAGE_PROPERTY_QUERY),
|
|
|
|
|
device_descriptor,
|
|
|
|
|
storage_descriptor_header.Size);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
2023-12-23 19:52:55 +01:00
|
|
|
|
DEBUG_ERROR("ZwDeviceIoControlFile second call failed with status %x", status);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (device_descriptor->SerialNumberOffset > 0)
|
|
|
|
|
{
|
2023-12-13 05:06:27 +01:00
|
|
|
|
serial_number =
|
|
|
|
|
(PCHAR)((UINT64)device_descriptor + device_descriptor->SerialNumberOffset);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
serial_length = strnlen_s(serial_number, DEVICE_DRIVE_0_SERIAL_CODE_LENGTH) + 1;
|
|
|
|
|
|
|
|
|
|
if (serial_length > ConfigDrive0MaxSize)
|
|
|
|
|
{
|
2023-12-23 19:52:55 +01:00
|
|
|
|
DEBUG_ERROR(
|
|
|
|
|
"Serial length is greater then the allocated buffer size for the drives serial number.");
|
2023-10-05 08:27:17 +02:00
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-13 05:06:27 +01:00
|
|
|
|
RtlCopyMemory(ConfigDrive0Serial, serial_number, serial_length);
|
2023-09-06 17:33:08 +02:00
|
|
|
|
|
2024-01-02 23:29:23 +01:00
|
|
|
|
DEBUG_VERBOSE("Successfully retrieved hard disk serial number.");
|
|
|
|
|
}
|
2023-09-06 17:33:08 +02:00
|
|
|
|
end:
|
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
if (handle)
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpZwClose(handle);
|
2023-09-06 17:33:08 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
if (device_descriptor)
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpExFreePoolWithTag(device_descriptor, POOL_TAG_INTEGRITY);
|
2023-09-06 17:33:08 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
return status;
|
2023-09-28 09:15:47 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-12-13 05:06:27 +01:00
|
|
|
|
// VOID
|
|
|
|
|
// EnumeratePciDevices()
|
2023-10-03 17:23:01 +02:00
|
|
|
|
//{
|
2023-12-13 05:06:27 +01:00
|
|
|
|
// NTSTATUS status;
|
|
|
|
|
// PZZWSTR device_interfaces;
|
|
|
|
|
// PWSTR list_base;
|
|
|
|
|
// DEVPROPKEY key = { 0 };
|
|
|
|
|
// UNICODE_STRING symbolic_link = { 0 };
|
|
|
|
|
// WCHAR device_id[ 512 ];
|
|
|
|
|
// PZZWSTR current_string = NULL;
|
|
|
|
|
// SIZE_T string_length = 0;
|
2023-10-03 17:23:01 +02:00
|
|
|
|
//
|
2023-12-13 05:06:27 +01:00
|
|
|
|
// /* PCI guid */
|
|
|
|
|
// CONST GUID guid = { 0x5b45201d, 0xf2f2, 0x4f3b, 0x85, 0xbb, 0x30, 0xff, 0x1f, 0x95, 0x35,
|
|
|
|
|
// 0x99 };
|
2023-10-03 17:23:01 +02:00
|
|
|
|
//
|
2023-12-13 05:06:27 +01:00
|
|
|
|
// status = IoGetDeviceInterfaces(
|
|
|
|
|
// &guid,
|
|
|
|
|
// NULL,
|
|
|
|
|
// NULL,
|
|
|
|
|
// &device_interfaces
|
|
|
|
|
// );
|
2023-10-03 17:23:01 +02:00
|
|
|
|
//
|
2023-12-13 05:06:27 +01:00
|
|
|
|
// if ( !NT_SUCCESS( status ) )
|
|
|
|
|
// {
|
2024-01-07 05:13:41 +01:00
|
|
|
|
// DEBUG_VERBOSE( "IoGetDeviceInterfaces failed with status %x", status );
|
2023-12-13 05:06:27 +01:00
|
|
|
|
// return;
|
|
|
|
|
// }
|
2023-10-03 17:23:01 +02:00
|
|
|
|
//
|
2023-12-13 05:06:27 +01:00
|
|
|
|
// current_string = device_interfaces;
|
2023-10-03 17:23:01 +02:00
|
|
|
|
//
|
2023-12-13 05:06:27 +01:00
|
|
|
|
// while ( *current_string != NULL_TERMINATOR )
|
|
|
|
|
// {
|
|
|
|
|
// string_length = wcslen( current_string );
|
2023-10-03 17:23:01 +02:00
|
|
|
|
//
|
2023-12-13 05:06:27 +01:00
|
|
|
|
// symbolic_link.Buffer = current_string;
|
|
|
|
|
// symbolic_link.Length = string_length;
|
|
|
|
|
// symbolic_link.MaximumLength = string_length;
|
2023-10-03 17:23:01 +02:00
|
|
|
|
//
|
2024-01-07 05:13:41 +01:00
|
|
|
|
// DEBUG_VERBOSE( "Device Interface: %wZ", symbolic_link );
|
2023-10-03 17:23:01 +02:00
|
|
|
|
//
|
2023-12-13 05:06:27 +01:00
|
|
|
|
// current_string += symbolic_link.Length + 1;
|
|
|
|
|
// }
|
2023-10-03 17:23:01 +02:00
|
|
|
|
//
|
2024-01-07 05:13:41 +01:00
|
|
|
|
// ImpExFreePoolWithTag( device_interfaces, NULL );
|
2023-12-13 05:06:27 +01:00
|
|
|
|
// }
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2023-10-03 18:03:55 +02:00
|
|
|
|
PVOID
|
2023-12-13 05:06:27 +01:00
|
|
|
|
ScanForSignature(_In_ PVOID BaseAddress,
|
|
|
|
|
_In_ SIZE_T MaxLength,
|
|
|
|
|
_In_ LPCSTR Signature,
|
|
|
|
|
_In_ SIZE_T SignatureLength)
|
2023-10-02 16:31:30 +02:00
|
|
|
|
{
|
2023-10-10 15:52:42 +02:00
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
2023-12-13 05:06:27 +01:00
|
|
|
|
CHAR current_char = 0;
|
2023-10-05 08:27:17 +02:00
|
|
|
|
CHAR current_sig_char = 0;
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
for (INT index = 0; index < MaxLength; index++)
|
2023-10-02 16:31:30 +02:00
|
|
|
|
{
|
2023-10-05 08:27:17 +02:00
|
|
|
|
for (INT sig_index = 0; sig_index < SignatureLength + 1; sig_index++)
|
|
|
|
|
{
|
2023-12-13 05:06:27 +01:00
|
|
|
|
current_char = *(PCHAR)((UINT64)BaseAddress + index + sig_index);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
current_sig_char = Signature[sig_index];
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
if (sig_index == SignatureLength)
|
|
|
|
|
return (PVOID)((UINT64)BaseAddress + index);
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
if (current_char != current_sig_char)
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-10-02 16:31:30 +02:00
|
|
|
|
}
|
2023-10-03 17:23:01 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +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
|
|
|
|
{
|
2023-10-05 08:27:17 +02:00
|
|
|
|
CONST UINT64 start = __readmsr(IA32_APERF_MSR) << 32;
|
2023-12-13 05:06:27 +01:00
|
|
|
|
CHAR value = *(PCHAR)InstructionAddress;
|
2023-10-05 08:27:17 +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
|
|
|
|
{
|
2023-10-05 08:27:17 +02:00
|
|
|
|
UINT64 read_average = 0;
|
2024-01-01 17:45:40 +01:00
|
|
|
|
UINT64 old_irql = 0;
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
MeasureInstructionRead(Address);
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
old_irql = __readcr8();
|
|
|
|
|
__writecr8(HIGH_LEVEL);
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
_disable();
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
for (ULONG iteration = 0; iteration < Count; iteration++)
|
|
|
|
|
read_average += MeasureInstructionRead(Address);
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
_enable();
|
|
|
|
|
__writecr8(old_irql);
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2023-12-23 19:52:55 +01:00
|
|
|
|
DEBUG_VERBOSE("EPT Detection - Read Average: %llx", read_average);
|
2023-10-06 09:02:10 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +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
|
|
|
|
/*
|
2023-12-13 05:06:27 +01: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.
|
|
|
|
|
*
|
|
|
|
|
* Credits to momo5502 for the idea: https://momo5502.com/blog/?p=255
|
|
|
|
|
*
|
|
|
|
|
* [+] EPT: Read average: 14991c28f5c2
|
|
|
|
|
* [+] no EPT: Read average: 28828f5c28
|
|
|
|
|
*
|
|
|
|
|
* 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-10-03 14:31:30 +02:00
|
|
|
|
STATIC
|
2023-10-02 16:31:30 +02:00
|
|
|
|
NTSTATUS
|
2023-12-13 05:06:27 +01:00
|
|
|
|
GetAverageReadTimeAtRoutine(_In_ PVOID RoutineAddress, _Out_ PUINT64 AverageTime)
|
2023-10-02 16:31:30 +02:00
|
|
|
|
{
|
2023-10-06 09:02:10 +02:00
|
|
|
|
if (!RoutineAddress || !AverageTime)
|
2023-12-23 19:52:55 +01:00
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2023-10-06 09:02:10 +02:00
|
|
|
|
*AverageTime = MeasureReads(RoutineAddress, EPT_CHECK_NUM_ITERATIONS);
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2023-12-23 19:52:55 +01: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
|
|
|
|
|
*
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
|
|
|
|
* The following open source Intel VT-X hv's w/ EPT functionality have been tested and detected
|
|
|
|
|
* in a non vm environment:
|
|
|
|
|
*
|
|
|
|
|
* HyperDbg !epthook (https://github.com/HyperDbg/HyperDbg): detected
|
|
|
|
|
* DdiMon (https://github.com/tandasat/DdiMon): detected
|
|
|
|
|
*/
|
|
|
|
|
WCHAR CONTROL_FUNCTIONS[EPT_CONTROL_FUNCTIONS_COUNT][EPT_MAX_FUNCTION_NAME_LENGTH] = {
|
|
|
|
|
L"RtlAssert",
|
|
|
|
|
L"PsAcquireSiloHardReference",
|
|
|
|
|
L"PsDereferencePrimaryToken",
|
|
|
|
|
L"ZwCommitEnlistment"};
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
/*
|
2023-12-13 05:06:27 +01: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.
|
|
|
|
|
*/
|
|
|
|
|
UINT64 CONTROL_FUNCTION_ADDRESSES[EPT_CONTROL_FUNCTIONS_COUNT] = {0};
|
|
|
|
|
UINT64 PROTECTED_FUNCTION_ADDRESSES[EPT_PROTECTED_FUNCTIONS_COUNT] = {0};
|
2023-10-06 09:02:10 +02:00
|
|
|
|
|
|
|
|
|
STATIC
|
|
|
|
|
NTSTATUS
|
|
|
|
|
InitiateEptFunctionAddressArrays()
|
|
|
|
|
{
|
2023-10-10 15:52:42 +02:00
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
2023-10-06 09:02:10 +02:00
|
|
|
|
UNICODE_STRING current_function;
|
|
|
|
|
|
|
|
|
|
for (INT index = 0; index < EPT_CONTROL_FUNCTIONS_COUNT; index++)
|
|
|
|
|
{
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpRtlInitUnicodeString(¤t_function, CONTROL_FUNCTIONS[index]);
|
|
|
|
|
CONTROL_FUNCTION_ADDRESSES[index] = ImpMmGetSystemRoutineAddress(¤t_function);
|
2023-10-06 09:02:10 +02:00
|
|
|
|
|
|
|
|
|
if (!CONTROL_FUNCTION_ADDRESSES[index])
|
2023-12-23 19:52:55 +01:00
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
2023-10-06 09:02:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (INT index = 0; index < EPT_PROTECTED_FUNCTIONS_COUNT; index++)
|
|
|
|
|
{
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpRtlInitUnicodeString(¤t_function, CONTROL_FUNCTIONS[index]);
|
|
|
|
|
PROTECTED_FUNCTION_ADDRESSES[index] =
|
|
|
|
|
ImpMmGetSystemRoutineAddress(¤t_function);
|
2023-10-06 09:02:10 +02:00
|
|
|
|
|
|
|
|
|
if (!PROTECTED_FUNCTION_ADDRESSES[index])
|
2023-12-23 19:52:55 +01:00
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
2023-10-06 09:02:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-03 18:03:55 +02:00
|
|
|
|
NTSTATUS
|
2023-10-03 14:31:30 +02:00
|
|
|
|
DetectEptHooksInKeyFunctions()
|
|
|
|
|
{
|
2023-10-10 15:52:42 +02:00
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
2023-12-23 19:52:55 +01:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
2023-12-13 05:06:27 +01:00
|
|
|
|
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-01-07 05:13:41 +01:00
|
|
|
|
/* todo: once we call this, we need to set a flag to skip this, otherwise we just return
|
|
|
|
|
* early */
|
2023-10-06 09:02:10 +02:00
|
|
|
|
status = InitiateEptFunctionAddressArrays();
|
2023-10-03 14:31:30 +02:00
|
|
|
|
|
2023-10-06 09:02:10 +02:00
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("InitiateEptFunctionAddressArrays failed with status %x", status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
2023-10-03 14:31:30 +02:00
|
|
|
|
|
2023-10-06 09:02:10 +02:00
|
|
|
|
for (INT index = 0; index < EPT_CONTROL_FUNCTIONS_COUNT; index++)
|
|
|
|
|
{
|
2023-12-13 05:06:27 +01:00
|
|
|
|
status = GetAverageReadTimeAtRoutine(CONTROL_FUNCTION_ADDRESSES[index],
|
|
|
|
|
&instruction_time);
|
2023-10-02 16:31:30 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("DetectEptPresentOnFunction failed with status %x", status);
|
|
|
|
|
control_fails += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2023-10-03 14:31:30 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
control_time_sum += instruction_time;
|
|
|
|
|
}
|
2023-10-03 14:31:30 +02:00
|
|
|
|
|
2023-10-06 09:02:10 +02:00
|
|
|
|
if (control_time_sum == 0)
|
2023-10-05 08:27:17 +02:00
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
2023-10-03 14:31:30 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
control_average = control_time_sum / (EPT_CONTROL_FUNCTIONS_COUNT - control_fails);
|
2023-10-03 14:31:30 +02:00
|
|
|
|
|
2023-10-06 09:02:10 +02:00
|
|
|
|
if (control_average == 0)
|
2024-01-07 05:13:41 +01:00
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
2023-10-03 14:31:30 +02:00
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
for (INT index = 0; index < EPT_PROTECTED_FUNCTIONS_COUNT; index++)
|
2023-10-03 14:31:30 +02:00
|
|
|
|
{
|
2023-12-13 05:06:27 +01:00
|
|
|
|
status = GetAverageReadTimeAtRoutine(PROTECTED_FUNCTION_ADDRESSES[index],
|
|
|
|
|
&instruction_time);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("DetectEptPresentOnFunction failed with status %x", status);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-13 05:06:27 +01:00
|
|
|
|
/* [+] EPT hook detected at function: ExAllocatePoolWithTag with execution time of:
|
|
|
|
|
* 149b7777777 */
|
2023-10-05 08:27:17 +02:00
|
|
|
|
if (control_average * EPT_EXECUTION_TIME_MULTIPLIER < instruction_time)
|
|
|
|
|
{
|
2023-12-23 19:52:55 +01:00
|
|
|
|
DEBUG_WARNING(
|
2023-12-13 05:06:27 +01:00
|
|
|
|
"EPT hook detected at function: %llx with execution time of: %llx",
|
|
|
|
|
PROTECTED_FUNCTION_ADDRESSES[index],
|
|
|
|
|
instruction_time);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
|
/* close game etc. */
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2023-12-23 19:52:55 +01:00
|
|
|
|
DEBUG_INFO("No ept hook detected at function: %llx",
|
|
|
|
|
PROTECTED_FUNCTION_ADDRESSES[index]);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
}
|
2023-10-03 14:31:30 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-10-05 08:27:17 +02:00
|
|
|
|
return status;
|
|
|
|
|
}
|
2023-10-03 14:31:30 +02:00
|
|
|
|
|
2023-10-21 12:25:08 +02:00
|
|
|
|
STATIC
|
|
|
|
|
VOID
|
2023-12-13 05:06:27 +01:00
|
|
|
|
FindWinLogonProcess(_In_ PPROCESS_LIST_ENTRY Entry, _In_opt_ PVOID Context)
|
2023-10-21 12:25:08 +02:00
|
|
|
|
{
|
2023-12-13 05:06:27 +01:00
|
|
|
|
LPCSTR process_name = NULL;
|
|
|
|
|
PEPROCESS* process = (PEPROCESS*)Context;
|
2023-10-21 12:25:08 +02:00
|
|
|
|
|
|
|
|
|
if (!Context)
|
|
|
|
|
return;
|
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
process_name = ImpPsGetProcessImageFileName(Entry->process);
|
2023-10-21 12:25:08 +02:00
|
|
|
|
|
|
|
|
|
if (!strcmp(process_name, "winlogon.exe"))
|
|
|
|
|
{
|
2023-12-23 19:52:55 +01:00
|
|
|
|
DEBUG_VERBOSE("32 bit WinLogon.exe process found at address: %llx",
|
|
|
|
|
(UINT64)Entry->process);
|
2023-10-21 12:25:08 +02:00
|
|
|
|
*process = Entry->process;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
{
|
2023-12-31 15:06:24 +01: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;
|
2024-01-02 23:29:23 +01:00
|
|
|
|
PVAL_INTEGRITY_HEADER memory_buffer = NULL;
|
2023-12-31 15:06:24 +01:00
|
|
|
|
ULONG memory_buffer_size = 0;
|
|
|
|
|
PEPROCESS process = NULL;
|
|
|
|
|
KAPC_STATE apc_state = {0};
|
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpRtlInitAnsiString(&ansi_string, Module->FullPathName);
|
2023-12-31 15:06:24 +01:00
|
|
|
|
|
|
|
|
|
if (!ansi_string.Buffer)
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("RtlInitAnsiString failed with status %x", status);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
status = ImpRtlAnsiStringToUnicodeString(&path, &ansi_string, TRUE);
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2023-10-20 20:30:45 +02:00
|
|
|
|
if (!NT_SUCCESS(status))
|
2023-10-03 14:31:30 +02:00
|
|
|
|
{
|
2023-12-31 15:06:24 +01:00
|
|
|
|
DEBUG_ERROR("RtlAnsiStringToUnicodeString failed with status %x", status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-20 20:30:45 +02:00
|
|
|
|
/*
|
2023-12-31 15:06:24 +01: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.
|
2023-12-13 05:06:27 +01:00
|
|
|
|
*/
|
2024-01-07 05:13:41 +01:00
|
|
|
|
if (!ImpMmIsAddressValid(Module->ImageBase))
|
2023-10-20 20:30:45 +02:00
|
|
|
|
{
|
2024-01-07 05:13:41 +01:00
|
|
|
|
// DEBUG_VERBOSE("Win32k related module found, acquiring 32 bit address space...");
|
2023-10-20 20:30:45 +02:00
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
// EnumerateProcessListWithCallbackRoutine(FindWinLogonProcess, &process);
|
2023-10-20 20:30:45 +02:00
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
// if (!process)
|
|
|
|
|
// goto end;
|
2023-10-03 16:11:27 +02:00
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
// ImpKeStackAttachProcess(process, &apc_state);
|
2023-12-13 05:06:27 +01:00
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
// status = StoreModuleExecutableRegionsInBuffer((PVOID)&memory_buffer,
|
|
|
|
|
// Module->ImageBase,
|
|
|
|
|
// Module->ImageSize,
|
|
|
|
|
// &memory_buffer_size);
|
2023-10-10 15:52:42 +02:00
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
// ImpKeUnstackDetachProcess(&apc_state);
|
|
|
|
|
status = STATUS_INVALID_IMAGE_WIN_32;
|
|
|
|
|
goto end;
|
2023-12-31 15:06:24 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-01-02 23:29:23 +01:00
|
|
|
|
status = StoreModuleExecutableRegionsInBuffer((PVOID)&memory_buffer,
|
|
|
|
|
Module->ImageBase,
|
|
|
|
|
Module->ImageSize,
|
|
|
|
|
&memory_buffer_size);
|
2023-12-31 15:06:24 +01:00
|
|
|
|
}
|
2023-10-05 08:27:17 +02:00
|
|
|
|
|
2023-12-31 15:06:24 +01: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-01-07 05:13:41 +01:00
|
|
|
|
status = ComputeHashOfBuffer(memory_buffer->section_base,
|
|
|
|
|
memory_buffer->section_header.SizeOfRawData,
|
|
|
|
|
&memory_hash,
|
|
|
|
|
&memory_hash_size);
|
2023-10-21 12:25:08 +02:00
|
|
|
|
|
2023-12-31 15:06:24 +01:00
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
2024-01-02 23:29:23 +01:00
|
|
|
|
DEBUG_VERBOSE("ComputeHashOfSections failed with status %x", status);
|
2023-12-31 15:06:24 +01:00
|
|
|
|
goto end;
|
|
|
|
|
}
|
2023-10-21 12:25:08 +02:00
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
RtlCopyMemory(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
|
|
|
|
|
2023-12-31 15:06:24 +01:00
|
|
|
|
if (memory_buffer)
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpExFreePoolWithTag(memory_buffer, POOL_TAG_INTEGRITY);
|
2023-12-31 15:06:24 +01:00
|
|
|
|
|
2024-01-02 23:29:23 +01:00
|
|
|
|
if (memory_hash)
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpExFreePoolWithTag(memory_hash, POOL_TAG_INTEGRITY);
|
2024-01-02 23:29:23 +01:00
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
if (path.Buffer)
|
|
|
|
|
ImpRtlFreeUnicodeString(&path);
|
2023-10-20 20:30:45 +02:00
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
return status;
|
|
|
|
|
}
|
2023-10-20 20:30:45 +02:00
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
VOID
|
|
|
|
|
ValidateSystemModule(_In_ PRTL_MODULE_EXTENDED_INFO Module)
|
|
|
|
|
{
|
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
PDRIVER_LIST_ENTRY entry = NULL;
|
|
|
|
|
PVOID hash = NULL;
|
2023-12-31 15:06:24 +01:00
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
hash = ExAllocatePool2(POOL_FLAG_NON_PAGED, 32, POOL_TAG_INTEGRITY);
|
|
|
|
|
|
|
|
|
|
if (!hash)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
FindDriverEntryByBaseAddress(Module->ImageBase, &entry);
|
|
|
|
|
|
|
|
|
|
if (!entry)
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("FindDriverEntryByBaseAddress failed with no status");
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = HashModule(Module, hash);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("HashModule failed with status %x", status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (CompareHashes(hash, entry->text_hash, sizeof(entry->text_hash)))
|
|
|
|
|
DEBUG_VERBOSE("Module: %s text regions are valid.", Module->FullPathName);
|
|
|
|
|
else
|
|
|
|
|
DEBUG_WARNING("**!!** Module: %s text regions are not valid **!!**", Module->FullPathName);
|
|
|
|
|
|
|
|
|
|
end:
|
|
|
|
|
if (hash)
|
|
|
|
|
ExFreePoolWithTag(hash, POOL_TAG_INTEGRITY);
|
2023-12-31 15:06:24 +01:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-02 23:29:23 +01:00
|
|
|
|
NTSTATUS
|
|
|
|
|
ValidateOurDriverImage()
|
|
|
|
|
{
|
2024-01-07 05:13:41 +01:00
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
LPCSTR driver_name = NULL;
|
|
|
|
|
UNICODE_STRING path = {0};
|
|
|
|
|
SYSTEM_MODULES modules = {0};
|
|
|
|
|
PRTL_MODULE_EXTENDED_INFO module_info = NULL;
|
|
|
|
|
PVOID section = NULL;
|
|
|
|
|
HANDLE section_handle = NULL;
|
|
|
|
|
ULONG section_size = 0;
|
|
|
|
|
PVAL_INTEGRITY_HEADER disk_buffer = NULL;
|
|
|
|
|
ULONG disk_buffer_size = 0;
|
|
|
|
|
PVOID disk_hash = NULL;
|
|
|
|
|
ULONG disk_hash_size = 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;
|
2024-01-02 23:29:23 +01:00
|
|
|
|
|
|
|
|
|
GetDriverPath(&path);
|
|
|
|
|
GetDriverName(&driver_name);
|
|
|
|
|
|
|
|
|
|
if (!driver_name)
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("GetDriverName failed with no status");
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = GetSystemModuleInformation(&modules);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("GetSystemModuleInformation failed with status %x", status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module_info = FindSystemModuleByName(driver_name, &modules);
|
|
|
|
|
|
|
|
|
|
if (!module_info)
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("FindSystemModuleByName failed with no status.");
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
/* here we need to map our disk image, like the previous integ checks */
|
|
|
|
|
status =
|
|
|
|
|
MapDiskImageIntoVirtualAddressSpace(§ion_handle, §ion, &path, §ion_size);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("MapDiskImageIntoVirtualAddressSpace failed with status %x", status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = StoreModuleExecutableRegionsInBuffer(
|
|
|
|
|
(PVOID)&disk_buffer, section, section_size, &disk_buffer_size);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("StoreModuleExecutableRegionsInBuffer failed with status %x", status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = StoreModuleExecutableRegionsInBuffer((PVOID)&memory_buffer,
|
|
|
|
|
module_info->ImageBase,
|
|
|
|
|
module_info->ImageSize,
|
|
|
|
|
&memory_buffer_size);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("StoreModuleExecutableRegionsInBuffer 2 failed with status %x", status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = ComputeHashOfSections(&memory_buffer->section_header,
|
|
|
|
|
&disk_buffer->section_header,
|
|
|
|
|
&disk_hash,
|
|
|
|
|
&disk_hash_size,
|
|
|
|
|
&memory_hash,
|
|
|
|
|
&memory_hash_size);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_VERBOSE("ComputeHashOfSections failed with status %x", status);
|
|
|
|
|
goto end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
|
|
|
|
if (CompareHashes(disk_hash, memory_hash, memory_hash_size))
|
|
|
|
|
DEBUG_VERBOSE("Driver image is valid. Integrity check complete");
|
|
|
|
|
else
|
|
|
|
|
DEBUG_WARNING("Drive image is NOT valid. !!!");
|
2024-01-02 23:29:23 +01:00
|
|
|
|
|
|
|
|
|
end:
|
2024-01-07 05:13:41 +01:00
|
|
|
|
if (memory_buffer)
|
|
|
|
|
ExFreePoolWithTag(memory_buffer, POOL_TAG_INTEGRITY);
|
|
|
|
|
|
|
|
|
|
if (disk_buffer)
|
|
|
|
|
ExFreePoolWithTag(disk_buffer, POOL_TAG_INTEGRITY);
|
|
|
|
|
|
|
|
|
|
if (memory_hash)
|
|
|
|
|
ExFreePoolWithTag(memory_hash, POOL_TAG_INTEGRITY);
|
|
|
|
|
|
|
|
|
|
if (disk_hash)
|
|
|
|
|
ExFreePoolWithTag(disk_hash, POOL_TAG_INTEGRITY);
|
|
|
|
|
|
|
|
|
|
if (section_handle)
|
|
|
|
|
ZwClose(section_handle);
|
|
|
|
|
|
|
|
|
|
if (section)
|
|
|
|
|
ZwUnmapViewOfSection(ZwCurrentProcess(), section);
|
|
|
|
|
|
2024-01-02 23:29:23 +01:00
|
|
|
|
if (modules.address)
|
|
|
|
|
ExFreePoolWithTag(modules.address, SYSTEM_MODULES_POOL);
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-31 15:06:24 +01:00
|
|
|
|
STATIC
|
|
|
|
|
VOID
|
|
|
|
|
SystemModuleVerificationDispatchFunction(_In_ PDEVICE_OBJECT DeviceObject,
|
|
|
|
|
_In_ PSYS_MODULE_VAL_CONTEXT Context)
|
|
|
|
|
{
|
|
|
|
|
InterlockedIncrement(&Context->active_thread_count);
|
|
|
|
|
|
|
|
|
|
LONG count = InterlockedExchange(&Context->current_count, Context->current_count);
|
|
|
|
|
LONG max = count + Context->block_size;
|
|
|
|
|
|
|
|
|
|
for (; count < max && count < Context->total_count; count++)
|
|
|
|
|
{
|
|
|
|
|
if (!InterlockedCompareExchange(
|
|
|
|
|
&Context->dispatcher_info[count].validated, TRUE, FALSE))
|
2023-10-20 20:30:45 +02:00
|
|
|
|
{
|
2023-12-31 15:06:24 +01:00
|
|
|
|
ValidateSystemModule(&Context->module_info[count]);
|
2023-10-20 20:30:45 +02:00
|
|
|
|
}
|
2023-12-31 15:06:24 +01:00
|
|
|
|
}
|
2023-10-20 20:30:45 +02:00
|
|
|
|
|
2023-12-31 15:06:24 +01:00
|
|
|
|
if (count == Context->total_count)
|
|
|
|
|
InterlockedExchange(&Context->complete, TRUE);
|
2023-12-23 19:52:55 +01:00
|
|
|
|
|
2023-12-31 15:06:24 +01:00
|
|
|
|
InterlockedExchange(&Context->current_count, count);
|
|
|
|
|
InterlockedDecrement(&Context->active_thread_count);
|
|
|
|
|
}
|
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
|
|
|
|
|
2023-12-31 15:06:24 +01: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
|
2024-01-01 17:45:40 +01:00
|
|
|
|
* 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
|
|
|
|
*
|
|
|
|
|
* A potential idea for further improvement is finding the cores with the least cpu usages and
|
|
|
|
|
* setting the worker threads affinity accordingly.
|
|
|
|
|
*/
|
|
|
|
|
STATIC
|
|
|
|
|
NTSTATUS
|
|
|
|
|
InitialiseSystemModuleVerificationContext(PSYS_MODULE_VAL_CONTEXT Context)
|
|
|
|
|
{
|
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
SYSTEM_MODULES modules = {0};
|
|
|
|
|
PMODULE_DISPATCHER_HEADER dispatcher_array = NULL;
|
|
|
|
|
|
|
|
|
|
status = GetSystemModuleInformation(&modules);
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("GetSystemModuleInformation failed with status %x", status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
2023-10-20 20:30:45 +02:00
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
DEBUG_VERBOSE("driver count: %lx", modules.module_count);
|
|
|
|
|
|
|
|
|
|
dispatcher_array =
|
|
|
|
|
ImpExAllocatePool2(POOL_FLAG_NON_PAGED,
|
|
|
|
|
modules.module_count * sizeof(MODULE_DISPATCHER_HEADER),
|
|
|
|
|
POOL_TAG_INTEGRITY);
|
2023-12-31 15:06:24 +01:00
|
|
|
|
if (!dispatcher_array)
|
|
|
|
|
{
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpExFreePoolWithTag(modules.address, SYSTEM_MODULES_POOL);
|
2023-12-31 15:06:24 +01:00
|
|
|
|
return STATUS_MEMORY_NOT_ALLOCATED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Context->active_thread_count = 0;
|
|
|
|
|
Context->active = TRUE;
|
|
|
|
|
Context->complete = FALSE;
|
|
|
|
|
Context->dispatcher_info = dispatcher_array;
|
|
|
|
|
Context->module_info = modules.address;
|
|
|
|
|
Context->total_count = modules.module_count;
|
|
|
|
|
Context->block_size = VALIDATION_BLOCK_SIZE;
|
|
|
|
|
|
2024-01-01 17:45:40 +01:00
|
|
|
|
/* skip hal.dll and ntosrnl.exe */
|
|
|
|
|
Context->current_count = 2;
|
|
|
|
|
|
2023-12-31 15:06:24 +01:00
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
FreeWorkItems(_In_ PSYS_MODULE_VAL_CONTEXT Context)
|
|
|
|
|
{
|
|
|
|
|
for (INT index = 0; index < VERIFICATION_THREAD_COUNT; index++)
|
|
|
|
|
{
|
|
|
|
|
if (Context->work_items[index])
|
|
|
|
|
{
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpIoFreeWorkItem(Context->work_items[index]);
|
|
|
|
|
Context->work_items[index] = NULL;
|
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-01-02 23:29:23 +01: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)
|
2024-01-07 05:13:41 +01:00
|
|
|
|
{
|
|
|
|
|
ImpExFreePoolWithTag(Context->module_info, SYSTEM_MODULES_POOL);
|
|
|
|
|
Context->module_info = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-02 23:29:23 +01:00
|
|
|
|
if (Context->dispatcher_info)
|
2024-01-07 05:13:41 +01:00
|
|
|
|
{
|
|
|
|
|
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-01-07 05:13:41 +01:00
|
|
|
|
Context->active = FALSE;
|
2024-01-02 23:29:23 +01:00
|
|
|
|
Context->complete = TRUE;
|
|
|
|
|
FreeWorkItems(Context);
|
|
|
|
|
FreeModuleVerificationItems(Context);
|
2023-12-31 15:06:24 +01:00
|
|
|
|
}
|
2023-12-23 19:52:55 +01:00
|
|
|
|
|
2023-12-31 15:06:24 +01:00
|
|
|
|
NTSTATUS
|
|
|
|
|
SystemModuleVerificationDispatcher()
|
|
|
|
|
{
|
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
|
|
|
PSYS_MODULE_VAL_CONTEXT context = NULL;
|
|
|
|
|
PIO_WORKITEM work_item = NULL;
|
2023-12-23 19:52:55 +01:00
|
|
|
|
|
2023-12-31 15:06:24 +01:00
|
|
|
|
GetSystemModuleValidationContext(&context);
|
2023-12-23 19:52:55 +01:00
|
|
|
|
|
2023-12-31 15:06:24 +01:00
|
|
|
|
if (context->complete)
|
|
|
|
|
{
|
|
|
|
|
DEBUG_VERBOSE("System modules integrity check complete. Freeing items.");
|
2024-01-02 23:29:23 +01:00
|
|
|
|
context->active = FALSE;
|
2024-01-01 18:58:18 +01:00
|
|
|
|
context->complete = FALSE;
|
2023-12-31 15:06:24 +01:00
|
|
|
|
FreeModuleVerificationItems(context);
|
|
|
|
|
FreeWorkItems(context);
|
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
|
}
|
2023-12-23 19:52:55 +01:00
|
|
|
|
|
2023-12-31 15:06:24 +01:00
|
|
|
|
if (!context->active)
|
|
|
|
|
{
|
|
|
|
|
DEBUG_VERBOSE("Context not active, generating new one");
|
2023-12-23 19:52:55 +01:00
|
|
|
|
|
2023-12-31 15:06:24 +01:00
|
|
|
|
status = InitialiseSystemModuleVerificationContext(context);
|
2023-12-23 19:52:55 +01:00
|
|
|
|
|
2023-12-31 15:06:24 +01:00
|
|
|
|
if (!NT_SUCCESS(status))
|
2023-10-20 20:30:45 +02:00
|
|
|
|
{
|
2023-12-31 15:06:24 +01:00
|
|
|
|
DEBUG_ERROR(
|
|
|
|
|
"InitialiseSystemModuleVerificationContext failed with status %x",
|
|
|
|
|
status);
|
|
|
|
|
return status;
|
2023-10-20 20:30:45 +02:00
|
|
|
|
}
|
2023-12-31 15:06:24 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
FreeWorkItems(context);
|
|
|
|
|
}
|
2023-10-20 20:30:45 +02:00
|
|
|
|
|
2023-12-31 15:06:24 +01:00
|
|
|
|
for (INT index = 0; index < VERIFICATION_THREAD_COUNT; index++)
|
|
|
|
|
{
|
2024-01-07 05:13:41 +01:00
|
|
|
|
work_item = ImpIoAllocateWorkItem(GetDriverDeviceObject());
|
2023-10-20 20:30:45 +02:00
|
|
|
|
|
2023-12-31 15:06:24 +01:00
|
|
|
|
if (!work_item)
|
|
|
|
|
continue;
|
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
ImpIoQueueWorkItem(
|
2023-12-31 15:06:24 +01:00
|
|
|
|
work_item, SystemModuleVerificationDispatchFunction, DelayedWorkQueue, context);
|
|
|
|
|
|
|
|
|
|
context->work_items[index] = work_item;
|
2023-10-20 20:30:45 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-01-01 17:45:40 +01:00
|
|
|
|
DEBUG_VERBOSE("All worker threads dispatched for system module validation.");
|
2023-10-21 15:11:59 +02:00
|
|
|
|
|
2023-12-31 15:06:24 +01: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)
|
|
|
|
|
{
|
|
|
|
|
NTSTATUS status = STATUS_ABANDONED;
|
|
|
|
|
RTL_OSVERSIONINFOW info = {0};
|
|
|
|
|
|
|
|
|
|
if (!VersionInfo)
|
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
|
|
2024-01-07 05:13:41 +01:00
|
|
|
|
status = ImpRtlGetVersion(&info);
|
2023-12-23 19:52:55 +01:00
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ERROR("RtlGetVersion failed with status %x", status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VersionInfo->dwBuildNumber = info.dwBuildNumber;
|
|
|
|
|
VersionInfo->dwMajorVersion = info.dwMajorVersion;
|
|
|
|
|
VersionInfo->dwMinorVersion = info.dwMinorVersion;
|
|
|
|
|
VersionInfo->dwOSVersionInfoSize = info.dwOSVersionInfoSize;
|
|
|
|
|
VersionInfo->dwPlatformId = info.dwPlatformId;
|
|
|
|
|
|
|
|
|
|
RtlCopyMemory(
|
|
|
|
|
VersionInfo->szCSDVersion, info.szCSDVersion, sizeof(VersionInfo->szCSDVersion));
|
|
|
|
|
|
2023-10-20 20:30:45 +02:00
|
|
|
|
return status;
|
2024-01-07 05:13:41 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define KPCR_KPRCB_OFFSET 0x180
|
|
|
|
|
#define KPCRB_IDLE_THREAD_OFFSET 0x018
|
|
|
|
|
#define KTHREAD_IDLE_TIME_OFFSET 0x28c
|
|
|
|
|
#define KPCRB_KERNEL_TIME_OFFSET 0x7e84
|
|
|
|
|
#define KPCRB_USER_TIME_OFFSET 0x7e88
|
|
|
|
|
|
|
|
|
|
UINT32
|
|
|
|
|
CalculateCpuCoreUsage(_In_ UINT32 Core)
|
|
|
|
|
{
|
|
|
|
|
PVOID kpcr = NULL;
|
|
|
|
|
PVOID kpcrb = NULL;
|
|
|
|
|
PVOID idle_thread = NULL;
|
|
|
|
|
UINT32 idle_time = 0;
|
|
|
|
|
UINT32 kernel_time = 0;
|
|
|
|
|
UINT32 user_time = 0;
|
|
|
|
|
|
|
|
|
|
KeSetSystemAffinityThread(1ull << Core);
|
|
|
|
|
|
|
|
|
|
while (Core != KeGetCurrentProcessorNumber())
|
|
|
|
|
YieldProcessor();
|
|
|
|
|
|
|
|
|
|
kpcr = __readmsr(IA32_GS_BASE);
|
|
|
|
|
kpcrb = (UINT64)kpcr + KPCR_KPRCB_OFFSET;
|
|
|
|
|
idle_thread = *(UINT64*)((UINT64)kpcrb + KPCRB_IDLE_THREAD_OFFSET);
|
|
|
|
|
|
|
|
|
|
idle_time = *(UINT32*)((UINT64)idle_thread + KTHREAD_IDLE_TIME_OFFSET);
|
|
|
|
|
kernel_time = *(UINT32*)((UINT64)kpcrb + KPCRB_KERNEL_TIME_OFFSET);
|
|
|
|
|
user_time = *(UINT32*)((UINT64)kpcrb + KPCRB_USER_TIME_OFFSET);
|
|
|
|
|
|
|
|
|
|
return (100 - (UINT32)(UInt32x32To64(idle_time, 100) / (UINT64)(kernel_time + user_time)));
|
2023-09-04 17:00:36 +02:00
|
|
|
|
}
|