#include "driver_util.h" namespace driver_util { void* get_driver_base(const char* module_name) { ULONG bytes{}; NTSTATUS status = ZwQuerySystemInformation( SystemModuleInformation, NULL, bytes, &bytes ); if (!bytes) return NULL; PRTL_PROCESS_MODULES modules = (PRTL_PROCESS_MODULES)ExAllocatePool(NonPagedPool, bytes); if (modules) { status = ZwQuerySystemInformation( SystemModuleInformation, modules, bytes, &bytes ); if (!NT_SUCCESS(status)) { ExFreePool(modules); return NULL; } PRTL_PROCESS_MODULE_INFORMATION module = modules->Modules; PVOID module_base{}, module_size{}; for (ULONG i = 0; i < modules->NumberOfModules; i++) { if (strcmp(reinterpret_cast(module[i].FullPathName + module[i].OffsetToFileName), module_name) == 0) { module_base = module[i].ImageBase; module_size = (PVOID)module[i].ImageSize; break; } } ExFreePool(modules); return module_base; } return NULL; } void* get_kmode_export(const char* mod_name, const char* proc_name) { if (!mod_name || !proc_name) return NULL; void* result = get_driver_base(mod_name); if (!result) return NULL; return RtlFindExportedRoutineByName(result, proc_name); } PIMAGE_FILE_HEADER get_file_header(void* base_addr) { if (!base_addr || *(short*)base_addr != 0x5A4D) return NULL; PIMAGE_DOS_HEADER dos_headers = reinterpret_cast(base_addr); PIMAGE_NT_HEADERS nt_headers = reinterpret_cast( reinterpret_cast(base_addr) + dos_headers->e_lfanew); return &nt_headers->FileHeader; } void* iat_hook(void* base_addr, const char* import, void* func_addr) { if (!base_addr || *(short*)base_addr != 0x5A4D || !import || !func_addr) return NULL; PIMAGE_DOS_HEADER dos_headers = reinterpret_cast(base_addr); PIMAGE_NT_HEADERS nt_headers = reinterpret_cast( reinterpret_cast(base_addr) + dos_headers->e_lfanew); IMAGE_DATA_DIRECTORY import_dir = nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; PIMAGE_IMPORT_DESCRIPTOR import_des = reinterpret_cast(import_dir.VirtualAddress + (DWORD_PTR)base_addr); LPCSTR lib_name = NULL; PVOID result = NULL; PIMAGE_IMPORT_BY_NAME func_name = NULL; if (!import_des) return NULL; while (import_des->Name != NULL) { lib_name = (LPCSTR)import_des->Name + (DWORD_PTR)base_addr; if (get_driver_base(lib_name)) { PIMAGE_THUNK_DATA org_first_thunk = NULL, first_thunk = NULL; org_first_thunk = (PIMAGE_THUNK_DATA)((DWORD_PTR)base_addr + import_des->OriginalFirstThunk); first_thunk = (PIMAGE_THUNK_DATA)((DWORD_PTR)base_addr + import_des->FirstThunk); while (org_first_thunk->u1.AddressOfData != NULL) { func_name = (PIMAGE_IMPORT_BY_NAME)((DWORD_PTR)base_addr + org_first_thunk->u1.AddressOfData); if (strcmp(func_name->Name, import) == 0) { // save old function pointer result = reinterpret_cast(first_thunk->u1.Function); // // although disabling wp bit can cause crashes, im disabling it for nano seconds. only to write 8 bytes... // in reality this is 1 mov instruction. // { // // disable write protection // _disable(); auto cr0 = __readcr0(); cr0 &= 0xfffffffffffeffff; __writecr0(cr0); } // swap address first_thunk->u1.Function = reinterpret_cast(func_addr); { // // enable write protection // auto cr0 = __readcr0(); cr0 |= 0x10000; __writecr0(cr0); _enable(); } return result; } ++org_first_thunk; ++first_thunk; } } ++import_des; } return NULL; } PDRIVER_OBJECT get_drv_obj(PUNICODE_STRING driver_name) { HANDLE handle{}; OBJECT_ATTRIBUTES attributes{}; UNICODE_STRING directory_name{}; PVOID directory{}; BOOLEAN success = FALSE; RtlInitUnicodeString(&directory_name, L"\\Driver"); InitializeObjectAttributes( &attributes, &directory_name, OBJ_CASE_INSENSITIVE, NULL, NULL ); // open OBJECT_DIRECTORY for \\Driver auto status = ZwOpenDirectoryObject( &handle, DIRECTORY_ALL_ACCESS, &attributes ); if (!NT_SUCCESS(status)) { DBG_PRINT("ZwOpenDirectoryObject Failed"); return NULL; } // Get OBJECT_DIRECTORY pointer from HANDLE status = ObReferenceObjectByHandle( handle, DIRECTORY_ALL_ACCESS, nullptr, KernelMode, &directory, nullptr ); if (!NT_SUCCESS(status)) { DBG_PRINT("ObReferenceObjectByHandle Failed"); ZwClose(handle); return NULL; } const auto directory_object = POBJECT_DIRECTORY(directory); if (!directory_object) return NULL; ExAcquirePushLockExclusiveEx(&directory_object->Lock, 0); // traverse hash table with 37 entries // when a new object is created, the object manager computes a hash value in the range zero to 36 from the object name and creates an OBJECT_DIRECTORY_ENTRY. // http://www.informit.com/articles/article.aspx?p=22443&seqNum=7 for (auto entry : directory_object->HashBuckets) { if (!entry) continue; while (entry && entry->Object) { auto driver = PDRIVER_OBJECT(entry->Object); if (!driver) continue; if (wcscmp(driver->DriverExtension->ServiceKeyName.Buffer, driver_name->Buffer) == 0) return driver; entry = entry->ChainLink; } } ExReleasePushLockExclusiveEx(&directory_object->Lock, 0); // Release the acquired resources back to the OS ObDereferenceObject(directory); ZwClose(handle); //TODO remove return NULL; } void copy_driver(PUNICODE_STRING image_path) { HANDLE h_file; OBJECT_ATTRIBUTES attr; IO_STATUS_BLOCK status_block; LARGE_INTEGER offset; UNICODE_STRING name; FILE_STANDARD_INFORMATION standard_info; RtlZeroMemory(&standard_info, sizeof(standard_info)); InitializeObjectAttributes( &attr, image_path, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL ); NTSTATUS status = ZwCreateFile( &h_file, GENERIC_READ, &attr, &status_block, NULL, FILE_ATTRIBUTE_NORMAL, NULL, FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, NULL ); ZwQueryInformationFile( h_file, &status_block, &standard_info, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation ); void* drv_buffer = ExAllocatePool( NonPagedPool, standard_info.AllocationSize.QuadPart ); status = ZwReadFile( h_file, NULL, NULL, NULL, &status_block, drv_buffer, standard_info.AllocationSize.QuadPart, &offset, NULL ); RtlInitUnicodeString(&name, L"\\DosDevices\\C:\\last_load_drv.sys"); InitializeObjectAttributes(&attr, &name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL ); ZwCreateFile( &h_file, GENERIC_WRITE, &attr, &status_block, NULL, FILE_ATTRIBUTE_NORMAL, NULL, FILE_OVERWRITE_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, NULL ); ZwWriteFile( h_file, NULL, NULL, NULL, &status_block, drv_buffer, standard_info.AllocationSize.QuadPart, &offset, NULL ); ZwClose(h_file); ExFreePool(drv_buffer); } void mem_dump(void* base_addr, unsigned len) { if (!base_addr || !len) return; HANDLE h_file; UNICODE_STRING name; OBJECT_ATTRIBUTES attr; IO_STATUS_BLOCK status_block; LARGE_INTEGER offset{ NULL }; RtlInitUnicodeString(&name, L"\\DosDevices\\C:\\dump.bin"); InitializeObjectAttributes(&attr, &name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL ); auto status = ZwCreateFile( &h_file, GENERIC_WRITE, &attr, &status_block, NULL, FILE_ATTRIBUTE_NORMAL, NULL, FILE_OVERWRITE_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, NULL ); status = ZwWriteFile( h_file, NULL, NULL, NULL, &status_block, base_addr, len, &offset, NULL ); ZwClose(h_file); } }