#include "driver.h" #include #include "../common.h" #include typedef BOOLEAN(NTAPI* RtlDosPathNameToNtPathName_U)(PCWSTR DosPathName, PUNICODE_STRING NtPathName, PCWSTR* NtFileNamePart, PVOID DirectoryInfo); using namespace global::report_structures; kernelmode::Driver::Driver(LPCWSTR DriverName, std::shared_ptr ReportInterface) { this->driver_name = DriverName; this->report_interface = ReportInterface; this->driver_handle = CreateFileW(DriverName, GENERIC_WRITE | GENERIC_READ | GENERIC_EXECUTE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0); if (this->driver_handle == INVALID_HANDLE_VALUE) { LOG_ERROR("Failed to open handle to driver with status 0x%x", GetLastError()); return; } this->NotifyDriverOnProcessLaunch(); } kernelmode::Driver::~Driver() { this->NotifyDriverOnProcessTermination(); } VOID kernelmode::Driver::RunNmiCallbacks() { BOOLEAN status = FALSE; DWORD bytes_returned = 0; NMI_CALLBACK_FAILURE report = {0}; status = DeviceIoControl(this->driver_handle, IOCTL_RUN_NMI_CALLBACKS, NULL, NULL, &report, sizeof(NMI_CALLBACK_FAILURE), &bytes_returned, (LPOVERLAPPED)NULL); if (status == NULL) { LOG_ERROR("DeviceIoControl failed with status code 0x%x", GetLastError()); return; } if (bytes_returned == NULL) { LOG_INFO("All threads valid, nmis fine."); return; } /* else, report */ this->report_interface->ReportViolation(&report); } /* * 1. Checks that every device object has a system module to back it * 2. Checks the IOCTL dispatch routines to ensure they lie within the module */ VOID kernelmode::Driver::VerifySystemModuleDriverObjects() { BOOLEAN status = FALSE; DWORD bytes_returned = 0; PVOID buffer = NULL; SIZE_T buffer_size = 0; SIZE_T header_size = 0; /* * allocate enough to report 5 invalid driver objects + header. The reason we use a raw * pointer here is so we can pass the address to DeviceIoControl. You are not able (atleast * as far as im concerned) to pass a shared ptr to DeviceIoControl. */ header_size = sizeof(MODULE_VALIDATION_FAILURE_HEADER); buffer_size = sizeof(MODULE_VALIDATION_FAILURE) * MODULE_VALIDATION_FAILURE_MAX_REPORT_COUNT + header_size; buffer = malloc(buffer_size); if (!buffer) return; status = DeviceIoControl(this->driver_handle, IOCTL_VALIDATE_DRIVER_OBJECTS, NULL, NULL, buffer, buffer_size, &bytes_returned, NULL); if (status == NULL) { LOG_ERROR("DeviceIoControl failed with status code 0x%x", GetLastError()); free(buffer); return; } if (bytes_returned == NULL) { LOG_INFO("All modules valid :)"); free(buffer); return; } /* * We are splitting up each packet here and passing them on one by one since * if I am being honest it is just easier in c++ and that way the process * is streamlined just like all other report packets. */ MODULE_VALIDATION_FAILURE_HEADER* header = (MODULE_VALIDATION_FAILURE_HEADER*)buffer; LOG_INFO("Module count: %lx", header->module_count); for (int i = 0; i < header->module_count; i++) { MODULE_VALIDATION_FAILURE* report = (MODULE_VALIDATION_FAILURE*)((UINT64)buffer + sizeof(MODULE_VALIDATION_FAILURE_HEADER) + i * sizeof(MODULE_VALIDATION_FAILURE)); this->report_interface->ReportViolation(report); } free(buffer); } /* * HOW THIS WILL WORK: * * 1. On driver initiation, ObRegisterCallbacks will be registered * 2. Each time a process that is not whitelisted tries to open a handle * to our game we will store the report in an a report queue * 3. the user mode app will then periodically query the driver asking * how many pending reports there are * 4. once the number is received, the app will allocate a buffer large enough * for all the reports and once again call CompleteQueuedCallbackReports * 5. This will then retrieve the reports into the buffer and from there * we can iteratively report them the same way as we do with the system * modules. */ struct REPORT_ID { INT report_id; }; VOID kernelmode::Driver::QueryReportQueue() { BOOLEAN status = FALSE; DWORD bytes_returned = 0; PVOID buffer = NULL; LONG buffer_size = 0; REPORT_ID* report_header = NULL; SIZE_T total_size = NULL; OPEN_HANDLE_FAILURE_REPORT handle_report = {0}; ATTACH_PROCESS_REPORT attach_report = {0}; INVALID_PROCESS_ALLOCATION_REPORT allocation_report = {0}; APC_STACKWALK_REPORT apc_report = {0}; HIDDEN_SYSTEM_THREAD_REPORT hidden_report = {0}; /* allocate enough for the largest report buffer * max reports */ buffer_size = sizeof(APC_STACKWALK_REPORT) * MAX_REPORTS_PER_IRP + sizeof(REPORT_QUEUE_HEADER); /* this isnt very c++ of us... */ buffer = malloc(buffer_size); status = DeviceIoControl(this->driver_handle, IOCTL_HANDLE_REPORTS_IN_CALLBACK_QUEUE, NULL, NULL, buffer, buffer_size, &bytes_returned, NULL); if (status == NULL) { LOG_ERROR("DeviceIoControl failed with status code 0x%x", GetLastError()); free(buffer); return; } REPORT_QUEUE_HEADER* header = (REPORT_QUEUE_HEADER*)buffer; if (!header || !header->count) goto end; for (INT index = 0; index < header->count; index++) { report_header = (REPORT_ID*)((UINT64)buffer + sizeof(REPORT_QUEUE_HEADER) + total_size); LOG_INFO("Report id: %d", report_header->report_id); switch (report_header->report_id) { case REPORT_ILLEGAL_ATTACH_PROCESS: ReportTypeFromReportQueue( buffer, &total_size, &attach_report); break; case REPORT_ILLEGAL_HANDLE_OPERATION: ReportTypeFromReportQueue( buffer, &total_size, &handle_report); break; case REPORT_INVALID_PROCESS_ALLOCATION: ReportTypeFromReportQueue( buffer, &total_size, &allocation_report); break; case REPORT_APC_STACKWALK: ReportTypeFromReportQueue( buffer, &total_size, &apc_report); break; case REPORT_HIDDEN_SYSTEM_THREAD: ReportTypeFromReportQueue( buffer, &total_size, &hidden_report); break; case REPORT_DPC_STACKWALK: ReportTypeFromReportQueue( buffer, &total_size, &hidden_report); break; case REPORT_DATA_TABLE_ROUTINE: ReportTypeFromReportQueue( buffer, &total_size, &hidden_report); break; default: break; } } end: free(buffer); } VOID kernelmode::Driver::RunCallbackReportQueue() { /*TODO have some volatile flag instead */ this->QueryReportQueue(); } VOID kernelmode::Driver::NotifyDriverOnProcessLaunch() { BOOLEAN status = FALSE; kernelmode::DRIVER_INITIATION_INFORMATION information = {0}; information.protected_process_id = GetCurrentProcessId(); status = DeviceIoControl(this->driver_handle, IOCTL_NOTIFY_DRIVER_ON_PROCESS_LAUNCH, &information, sizeof(kernelmode::DRIVER_INITIATION_INFORMATION), NULL, NULL, NULL, NULL); if (status == NULL) LOG_ERROR("Failed to notify driver on process launch 0x%x", GetLastError()); } VOID kernelmode::Driver::DetectSystemVirtualization() { BOOLEAN status = FALSE; HYPERVISOR_DETECTION_REPORT report = {0}; DWORD bytes_returned = 0; status = DeviceIoControl(this->driver_handle, IOCTL_PERFORM_VIRTUALIZATION_CHECK, NULL, NULL, &report, sizeof(HYPERVISOR_DETECTION_REPORT), &bytes_returned, NULL); if (status == NULL) { LOG_ERROR("DeviceIoControl failed virtualization detect with status %x", GetLastError()); return; } if (report.aperf_msr_timing_check == TRUE || report.invd_emulation_check == TRUE) LOG_INFO("HYPERVISOR DETECTED!!!"); /* shutdown the application or smth lmao */ } VOID kernelmode::Driver::CheckHandleTableEntries() { BOOLEAN status = FALSE; DWORD bytes_returned = {0}; /* * Only pass the IOCTL code and nothing else since the reports are bundled * with the handle ObRegisterCallbacks report queue hence the QueryReportQueue * function will handle these reports. */ status = DeviceIoControl(this->driver_handle, IOCTL_ENUMERATE_HANDLE_TABLES, NULL, NULL, NULL, NULL, &bytes_returned, NULL); if (status == NULL) LOG_ERROR("CheckHandleTableEntries failed with status %x", status); } VOID kernelmode::Driver::RequestModuleExecutableRegions() { BOOLEAN status = FALSE; DWORD bytes_returned = 0; ULONG module_size = 0; PVOID buffer = NULL; module_size = this->RequestTotalModuleSize(); if (module_size == NULL) { LOG_ERROR("RequestTotalModuleSize failed lolz"); return; } 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) return; status = DeviceIoControl(this->driver_handle, IOCTL_RETRIEVE_MODULE_EXECUTABLE_REGIONS, NULL, NULL, buffer, module_size, &bytes_returned, NULL); if (status == NULL) { LOG_ERROR("failed to retrieve module executable regions lozl %x", GetLastError()); goto end; } LOG_INFO("bytes returned: %lx", bytes_returned); this->report_interface->ServerSend( buffer, bytes_returned, CLIENT_REQUEST_MODULE_INTEGRITY_CHECK); end: free(buffer); } VOID kernelmode::Driver::ScanForUnlinkedProcess() { BOOLEAN status = FALSE; DWORD bytes_returned = 0; INVALID_PROCESS_ALLOCATION_REPORT report = {0}; status = DeviceIoControl(this->driver_handle, IOCTL_SCAN_FOR_UNLINKED_PROCESS, NULL, NULL, &report, sizeof(report), &bytes_returned, NULL); if (status == NULL) { LOG_ERROR("failed to scan for unlinked processes %x", GetLastError()); return; } } VOID kernelmode::Driver::PerformIntegrityCheck() { BOOLEAN status = FALSE; status = DeviceIoControl( this->driver_handle, IOCTL_PERFORM_INTEGRITY_CHECK, NULL, NULL, NULL, NULL, NULL, NULL); if (status == NULL) LOG_ERROR("Failed to perform integrity check with status %x", status); } ULONG kernelmode::Driver::RequestTotalModuleSize() { BOOLEAN status = FALSE; DWORD bytes_returned = 0; ULONG module_size = 0; status = DeviceIoControl(this->driver_handle, IOCTL_REQUEST_TOTAL_MODULE_SIZE, NULL, NULL, &module_size, sizeof(ULONG), &bytes_returned, NULL); if (status == NULL) LOG_ERROR("CheckHandleTableEntries failed with status %x", status); return module_size; } VOID kernelmode::Driver::NotifyDriverOnProcessTermination() { BOOLEAN status = FALSE; status = DeviceIoControl(this->driver_handle, IOCTL_NOTIFY_DRIVER_ON_PROCESS_TERMINATION, NULL, NULL, NULL, NULL, NULL, NULL); if (status == NULL) LOG_ERROR("NotifyDriverOnProcessTermination failed with status %x", status); } VOID kernelmode::Driver::CheckForAttachedThreads() { BOOLEAN status = FALSE; status = DeviceIoControl( this->driver_handle, IOCTL_DETECT_ATTACHED_THREADS, NULL, NULL, NULL, NULL, NULL, NULL); if (status == NULL) LOG_ERROR("failed to check for attached threads %x", GetLastError()); } VOID kernelmode::Driver::CheckForEptHooks() { BOOLEAN status = FALSE; status = DeviceIoControl( this->driver_handle, IOCTL_CHECK_FOR_EPT_HOOK, NULL, NULL, NULL, NULL, NULL, NULL); if (status == NULL) LOG_ERROR("failed to check for ept hooks %x", GetLastError()); } VOID kernelmode::Driver::StackwalkThreadsViaDpc() { BOOLEAN status = FALSE; status = DeviceIoControl( this->driver_handle, IOCTL_LAUNCH_DPC_STACKWALK, NULL, NULL, NULL, NULL, NULL, NULL); if (status == NULL) LOG_ERROR("failed to stackwalk threads via dpc %x", GetLastError()); } VOID kernelmode::Driver::ValidateSystemModules() { BOOLEAN status = FALSE; status = DeviceIoControl( this->driver_handle, IOCTL_VALIDATE_SYSTEM_MODULES, NULL, NULL, NULL, NULL, NULL, NULL); if (status == NULL) LOG_ERROR("failed to validate system modules %x", GetLastError()); } VOID kernelmode::Driver::CheckDriverHeartbeat() { } VOID kernelmode::Driver::VerifyProcessLoadedModuleExecutableRegions() { HANDLE process_modules_handle = INVALID_HANDLE_VALUE; MODULEENTRY32 module_entry = {0}; BOOLEAN status = FALSE; PROCESS_MODULE_INFORMATION module_information = {0}; PROCESS_MODULE_VALIDATION_RESULT validation_result = {0}; DWORD bytes_returned = 0; RtlDosPathNameToNtPathName_U pRtlDosPathNameToNtPathName_U = NULL; UNICODE_STRING nt_path_name = {0}; pRtlDosPathNameToNtPathName_U = (RtlDosPathNameToNtPathName_U)GetProcAddress( GetModuleHandle(L"ntdll.dll"), "RtlDosPathNameToNtPathName_U"); process_modules_handle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, GetCurrentProcessId()); if (process_modules_handle == INVALID_HANDLE_VALUE) { LOG_ERROR("CreateToolHelp32Snapshot with TH32CS_SNAPMODULE failed with status 0x%x", GetLastError()); return; } module_entry.dwSize = sizeof(MODULEENTRY32); if (!Module32First(process_modules_handle, &module_entry)) { LOG_ERROR("Module32First failed with status 0x%x", GetLastError()); return; } do { module_information.module_base = module_entry.modBaseAddr; module_information.module_size = module_entry.modBaseSize; status = (*pRtlDosPathNameToNtPathName_U)( module_entry.szExePath, &nt_path_name, NULL, NULL); if (!status) { LOG_ERROR("RtlDosPathNameToNtPathName_U failed with no status."); continue; } memcpy(module_information.module_path, nt_path_name.Buffer, MAX_MODULE_PATH); status = DeviceIoControl(this->driver_handle, IOCTL_VALIDATE_PROCESS_LOADED_MODULE, &module_information, sizeof(module_information), &validation_result, sizeof(validation_result), &bytes_returned, NULL); if (status == NULL || bytes_returned == NULL) { LOG_ERROR("failed to validate process module with status %x", GetLastError()); continue; } if (validation_result.is_module_valid == FALSE) { /*TODO: copy module aswell from an anomaly offset */ PROCESS_MODULES_INTEGRITY_CHECK_FAILURE report; report.report_code = REPORT_CODE_PROCESS_MODULE_VERIFICATION; report.module_base_address = (UINT64)module_entry.modBaseAddr; report.module_size = module_entry.modBaseSize; std::wstring wstr(module_entry.szModule); std::string module_name_string = std::string(wstr.begin(), wstr.end()); memcpy( &report.module_name, &module_name_string, module_name_string.length()); this->report_interface->ReportViolation(&report); } else { LOG_INFO("Module %S is valid", module_entry.szModule); } } while (Module32Next(process_modules_handle, &module_entry)); end: CloseHandle(process_modules_handle); } VOID kernelmode::Driver::SendClientHardwareInformation() { BOOLEAN status = FALSE; global::headers::SYSTEM_INFORMATION system_information = {0}; DWORD bytes_returned = 0; status = DeviceIoControl(this->driver_handle, IOCTL_REQUEST_HARDWARE_INFORMATION, NULL, NULL, &system_information, sizeof(global::headers::SYSTEM_INFORMATION), &bytes_returned, NULL); if (status == NULL || bytes_returned == NULL) { LOG_ERROR("DeviceIoControl failed with status %x", GetLastError()); return; } this->report_interface->ServerSend(&system_information, sizeof(global::headers::SYSTEM_INFORMATION), CLIENT_SEND_SYSTEM_INFORMATION); } BOOLEAN kernelmode::Driver::InitiateApcOperation(INT OperationId) { BOOLEAN status = FALSE; APC_OPERATION_INFORMATION operation = {0}; operation.operation_id = OperationId; status = DeviceIoControl(this->driver_handle, IOCTL_INITIATE_APC_OPERATION, &operation, sizeof(APC_OPERATION_INFORMATION), NULL, NULL, NULL, NULL); if (status == NULL) { LOG_ERROR("DeviceIoControl failed with status %x", GetLastError()); return status; } } VOID kernelmode::Driver::SendIrpForDriverToStore() { BOOLEAN status = FALSE; status = DeviceIoControl( this->driver_handle, IOCTL_INSERT_IRP_INTO_QUEUE, NULL, NULL, NULL, NULL, NULL, NULL); if (status == NULL) LOG_ERROR("failed to insert irp into irp queue %x", GetLastError()); } VOID GetKernelStructureOffsets() { }