From dd38277278b851bab88b33d6ef7c081670a56132 Mon Sep 17 00:00:00 2001 From: lhodges1 Date: Fri, 25 Aug 2023 17:38:45 +1000 Subject: [PATCH] make the integrity checks actually GOOD lolz --- driver/integrity.c | 111 +++++++++++++++++++++++++++++++++++++-------- driver/integrity.h | 111 ++++++++++++++++++++++++++++++++++++++++++++- driver/ioctl.c | 5 +- user/km/driver.cpp | 7 ++- user/km/driver.h | 1 - user/main.cpp | 2 +- 6 files changed, 212 insertions(+), 25 deletions(-) diff --git a/driver/integrity.c b/driver/integrity.c index 46e25f0..e8b1087 100644 --- a/driver/integrity.c +++ b/driver/integrity.c @@ -3,6 +3,13 @@ #include "common.h" #include "modules.h" +typedef struct _INTEGRITY_CHECK_HEADER +{ + INT executable_section_count; + LONG total_packet_size; + +}INTEGRITY_CHECK_HEADER, *PINTEGRITY_CHECK_HEADER; + /* * 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 @@ -37,26 +44,24 @@ NTSTATUS GetDriverImageSize( return status; } -/* -* Instead of copying pages with the EDB (execute disable bit) not set, I am simply -* copying the entire image which we can then send to the server which can then can -* analyse the executable sections from there. Until I find a better way to enumerate -* kernel memory without having to walk the pages tables to check the EDB bit this -* is how I will be doing it. c: -* -* TODO: We will hash this based on timestamp sent from the server. -*/ -NTSTATUS CopyDriverExecutableRegions( +NTSTATUS New_CopyDriverExecutableRegions( _In_ PIRP Irp ) { NTSTATUS status; SYSTEM_MODULES modules = { 0 }; PRTL_MODULE_EXTENDED_INFO driver_info; - MM_COPY_ADDRESS address; PVOID mapped_address; PHYSICAL_ADDRESS physical_address; - SIZE_T bytes_returned; + PIMAGE_DOS_HEADER dos_header; + PLOCAL_NT_HEADER nt_header; + PIMAGE_SECTION_HEADER section; + ULONG total_packet_size = 0; + ULONG previous_section_size = 0; + PVOID buffer = NULL; + ULONG num_sections = 0; + ULONG num_executable_sections = 0; + UINT64 buffer_base; status = GetSystemModuleInformation( &modules ); @@ -71,7 +76,16 @@ NTSTATUS CopyDriverExecutableRegions( &modules ); - Irp->IoStatus.Information = driver_info->ImageSize; + /* + * 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. + */ + buffer = ExAllocatePool2( POOL_FLAG_NON_PAGED, driver_info->ImageSize + sizeof( INTEGRITY_CHECK_HEADER ), POOL_TAG_INTEGRITY); + + if ( !buffer ) + goto end; /* * Map the drivers physical memory into our IO space, then copy it into @@ -79,6 +93,9 @@ NTSTATUS CopyDriverExecutableRegions( */ physical_address.QuadPart = MmGetPhysicalAddress( driver_info->ImageBase ).QuadPart; + /* + * Verifier doesn't like it when we map system pages xD (sometimes ?) + */ mapped_address = MmMapIoSpace( physical_address, driver_info->ImageSize, @@ -91,14 +108,67 @@ NTSTATUS CopyDriverExecutableRegions( goto end; } - RtlCopyMemory( - Irp->AssociatedIrp.SystemBuffer, - mapped_address, - driver_info->ImageSize + dos_header = ( PIMAGE_DOS_HEADER )driver_info->ImageBase; + + /* + * The IMAGE_DOS_HEADER.e_lfanew stores the offset of the IMAGE_NT_HEADER from the base + * of the image. + */ + nt_header = ( struct _IMAGE_NT_HEADERS64* )( ( UINT64 )driver_info->ImageBase + dos_header->e_lfanew ); + + num_sections = nt_header->FileHeader.NumberOfSections; + + /* + * 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 ); + + buffer_base = ( UINT64 )buffer + sizeof( INTEGRITY_CHECK_HEADER ); + + for ( ULONG index = 0; index < num_sections; index++ ) + { + if ( section->Characteristics & IMAGE_SCN_MEM_EXECUTE ) + { + DEBUG_LOG( "Found executable section with name: %s", section->Name ); + + RtlCopyMemory( + ( UINT64 )buffer_base + previous_section_size, + section, + sizeof( IMAGE_SECTION_HEADER ) + ); + + RtlCopyMemory( + ( UINT64 )buffer_base + sizeof( IMAGE_SECTION_HEADER ), + ( UINT64 )mapped_address + section->PointerToRawData, + section->SizeOfRawData + ); + + total_packet_size += section->SizeOfRawData + sizeof( IMAGE_SECTION_HEADER ); + num_executable_sections += 1; + previous_section_size = sizeof( IMAGE_SECTION_HEADER ) + section->SizeOfRawData; + } + + section++; + } + + INTEGRITY_CHECK_HEADER header = { 0 }; + header.executable_section_count = num_executable_sections; + header.total_packet_size = total_packet_size + sizeof( INTEGRITY_CHECK_HEADER ); + + RtlCopyMemory( + buffer, + &header, + sizeof( INTEGRITY_CHECK_HEADER ) ); - if ( !NT_SUCCESS( status ) ) - DEBUG_ERROR( "MmCopyMemory failed with status %x", status ); + Irp->IoStatus.Information = total_packet_size; + + RtlCopyMemory( + Irp->AssociatedIrp.SystemBuffer, + buffer, + total_packet_size + ); end: @@ -107,5 +177,8 @@ end: if ( modules.address ) ExFreePoolWithTag( modules.address, SYSTEM_MODULES_POOL ); + if ( buffer ) + ExFreePoolWithTag( buffer, POOL_TAG_INTEGRITY ); + return status; } \ No newline at end of file diff --git a/driver/integrity.h b/driver/integrity.h index d43ba95..cf8c053 100644 --- a/driver/integrity.h +++ b/driver/integrity.h @@ -13,4 +13,113 @@ NTSTATUS GetDriverImageSize( _In_ PIRP Irp ); -#endif \ No newline at end of file +NTSTATUS New_CopyDriverExecutableRegions( + _In_ PIRP Irp +); + +#define IMAGE_SCN_MEM_EXECUTE 0x20000000 + +#define IMAGE_SIZEOF_SHORT_NAME 8 + +typedef struct _IMAGE_SECTION_HEADER { + unsigned char Name[ IMAGE_SIZEOF_SHORT_NAME ]; + union { + unsigned long PhysicalAddress; + unsigned long VirtualSize; + } Misc; + unsigned long VirtualAddress; + unsigned long SizeOfRawData; + unsigned long PointerToRawData; + unsigned long PointerToRelocations; + unsigned long PointerToLinenumbers; + unsigned short NumberOfRelocations; + unsigned short NumberOfLinenumbers; + unsigned long Characteristics; +} IMAGE_SECTION_HEADER, * PIMAGE_SECTION_HEADER; + +typedef struct _IMAGE_FILE_HEADER { + unsigned short Machine; + unsigned short NumberOfSections; + unsigned long TimeDateStamp; + unsigned long PointerToSymbolTable; + unsigned long NumberOfSymbols; + unsigned short SizeOfOptionalHeader; + unsigned short Characteristics; +} IMAGE_FILE_HEADER, * PIMAGE_FILE_HEADER; + +typedef struct _IMAGE_DATA_DIRECTORY { + unsigned long VirtualAddress; + unsigned long Size; +} IMAGE_DATA_DIRECTORY, * PIMAGE_DATA_DIRECTORY; + +#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 + +typedef struct _IMAGE_OPTIONAL_HEADER64 { + unsigned short Magic; + unsigned char MajorLinkerVersion; + unsigned char MinorLinkerVersion; + unsigned long SizeOfCode; + unsigned long SizeOfInitializedData; + unsigned long SizeOfUninitializedData; + unsigned long AddressOfEntryPoint; + unsigned long BaseOfCode; + ULONGLONG ImageBase; + unsigned long SectionAlignment; + unsigned long FileAlignment; + unsigned short MajorOperatingSystemVersion; + unsigned short MinorOperatingSystemVersion; + unsigned short MajorImageVersion; + unsigned short MinorImageVersion; + unsigned short MajorSubsystemVersion; + unsigned short MinorSubsystemVersion; + unsigned long Win32VersionValue; + unsigned long SizeOfImage; + unsigned long SizeOfHeaders; + unsigned long CheckSum; + unsigned short Subsystem; + unsigned short DllCharacteristics; + ULONGLONG SizeOfStackReserve; + ULONGLONG SizeOfStackCommit; + ULONGLONG SizeOfHeapReserve; + ULONGLONG SizeOfHeapCommit; + unsigned long LoaderFlags; + unsigned long NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY DataDirectory[ IMAGE_NUMBEROF_DIRECTORY_ENTRIES ]; +} IMAGE_OPTIONAL_HEADER64, * PIMAGE_OPTIONAL_HEADER64; + +typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header + unsigned short e_magic; // Magic number + unsigned short e_cblp; // Bytes on last page of file + unsigned short e_cp; // Pages in file + unsigned short e_crlc; // Relocations + unsigned short e_cparhdr; // Size of header in paragraphs + unsigned short e_minalloc; // Minimum extra paragraphs needed + unsigned short e_maxalloc; // Maximum extra paragraphs needed + unsigned short e_ss; // Initial (relative) SS value + unsigned short e_sp; // Initial SP value + unsigned short e_csum; // Checksum + unsigned short e_ip; // Initial IP value + unsigned short e_cs; // Initial (relative) CS value + unsigned short e_lfarlc; // File address of relocation table + unsigned short e_ovno; // Overlay number + unsigned short e_res[ 4 ]; // Reserved words + unsigned short e_oemid; // OEM identifier (for e_oeminfo) + unsigned short e_oeminfo; // OEM information; e_oemid specific + unsigned short e_res2[ 10 ]; // Reserved words + LONG e_lfanew; // File address of new exe header +} IMAGE_DOS_HEADER, * PIMAGE_DOS_HEADER; + +typedef struct _LOCAL_NT_HEADER { + unsigned long Signature; + IMAGE_FILE_HEADER FileHeader; + IMAGE_OPTIONAL_HEADER64 OptionalHeader; +} LOCAL_NT_HEADER, * PLOCAL_NT_HEADER; + +#define IMAGE_FIRST_SECTION( ntheader ) ((PIMAGE_SECTION_HEADER) \ + ((ULONG_PTR)(ntheader) + \ + FIELD_OFFSET( LOCAL_NT_HEADER, OptionalHeader ) + \ + ((ntheader))->FileHeader.SizeOfOptionalHeader \ + )) + +#endif + diff --git a/driver/ioctl.c b/driver/ioctl.c index d185d1c..39799c8 100644 --- a/driver/ioctl.c +++ b/driver/ioctl.c @@ -30,7 +30,8 @@ NTSTATUS DeviceControl( */ ReadInitialisedConfigFlag( &security_flag ); - if ( security_flag == FALSE ) + if ( security_flag == FALSE && + stack_location->Parameters.DeviceIoControl.IoControlCode != IOCTL_NOTIFY_DRIVER_ON_PROCESS_LAUNCH ) goto end; switch ( stack_location->Parameters.DeviceIoControl.IoControlCode ) @@ -154,7 +155,7 @@ NTSTATUS DeviceControl( NULL, NULL, NULL, - CopyDriverExecutableRegions, + New_CopyDriverExecutableRegions, Irp ); diff --git a/user/km/driver.cpp b/user/km/driver.cpp index c46191c..bdca3ab 100644 --- a/user/km/driver.cpp +++ b/user/km/driver.cpp @@ -308,6 +308,11 @@ VOID kernelmode::Driver::RequestModuleExecutableRegions() LOG_INFO( "module size: %lx", module_size ); + /* + * allocate a buffer big enough for the entire module not including section headers or + * packet headers, however it should be big enough since executable sections do not + * make up 100% of the image size. Bit hacky but it works. + */ buffer = malloc( module_size ); if ( !buffer ) @@ -332,7 +337,7 @@ VOID kernelmode::Driver::RequestModuleExecutableRegions() LOG_INFO( "bytes returned: %lx", bytes_returned ); - this->report_interface->ServerSend( buffer, module_size, SERVER_SEND_MODULE_INTEGRITY_CHECK ); + this->report_interface->ServerSend( buffer, bytes_returned, SERVER_SEND_MODULE_INTEGRITY_CHECK ); end: free( buffer ); diff --git a/user/km/driver.h b/user/km/driver.h index d86ee39..ffebe7a 100644 --- a/user/km/driver.h +++ b/user/km/driver.h @@ -44,7 +44,6 @@ namespace kernelmode VOID ValidateKPRCBThreads(); VOID CheckHandleTableEntries(); VOID RequestModuleExecutableRegions(); - /* todo: driver integrity check */ }; struct DRIVER_INITIATION_INFORMATION diff --git a/user/main.cpp b/user/main.cpp index 9a00af8..81f3d4f 100644 --- a/user/main.cpp +++ b/user/main.cpp @@ -31,7 +31,7 @@ DWORD WINAPI Init(HINSTANCE hinstDLL) //kmanager.MonitorCallbackReports(); //kmanager.RunNmiCallbacks(); //kmanager.VerifySystemModules(); - //kmanager.RequestModuleExecutableRegionsForIntegrityCheck(); + kmanager.RequestModuleExecutableRegionsForIntegrityCheck(); //kmanager.MonitorCallbackReports(); //umanager.ValidateProcessModules();