/* WARNING: utils.hpp must be the first file included... this is because i use getenv and that requires _CRT_SECURE_NO_WARNINGS... */ #pragma once #define _CRT_SECURE_NO_WARNINGS #pragma comment(lib, "ntdll.lib") #include #include #include #include #include #include #include #include #include #include #include "ia32.hpp" inline bool dbg_print = false; #define DBG_PRINT(format, ...) \ std::printf(format, __VA_ARGS__ ) typedef struct _RTL_PROCESS_MODULE_INFORMATION { HANDLE Section; PVOID MappedBase; PVOID ImageBase; ULONG ImageSize; ULONG Flags; USHORT LoadOrderIndex; USHORT InitOrderIndex; USHORT LoadCount; USHORT OffsetToFileName; UCHAR FullPathName[256]; } RTL_PROCESS_MODULE_INFORMATION, * PRTL_PROCESS_MODULE_INFORMATION; typedef struct _RTL_PROCESS_MODULES { ULONG NumberOfModules; RTL_PROCESS_MODULE_INFORMATION Modules[1]; } RTL_PROCESS_MODULES, * PRTL_PROCESS_MODULES; #define SystemKernelVaShadowInformation (SYSTEM_INFORMATION_CLASS) 196 typedef struct _SYSTEM_KERNEL_VA_SHADOW_INFORMATION { struct { ULONG KvaShadowEnabled : 1; ULONG KvaShadowUserGlobal : 1; ULONG KvaShadowPcid : 1; ULONG KvaShadowInvpcid : 1; ULONG KvaShadowRequired : 1; ULONG KvaShadowRequiredAvailable : 1; ULONG InvalidPteBit : 6; ULONG L1DataCacheFlushSupported : 1; ULONG L1TerminalFaultMitigationPresent : 1; ULONG Reserved : 18; } KvaShadowFlags; } SYSTEM_KERNEL_VA_SHADOW_INFORMATION, * PSYSTEM_KERNEL_VA_SHADOW_INFORMATION; namespace utils { inline std::uintptr_t scan(std::uintptr_t base, std::uint32_t size, const char* pattern, const char* mask) { static const auto check_mask = [&](const char* base, const char* pattern, const char* mask) -> bool { for (; *mask; ++base, ++pattern, ++mask) if (*mask == 'x' && *base != *pattern) return false; return true; }; size -= strlen(mask); for (auto i = 0; i <= size; ++i) { void* addr = (void*)&(((char*)base)[i]); if (check_mask((char*)addr, pattern, mask)) return reinterpret_cast(addr); } return NULL; } inline void open_binary_file(const std::string& file, std::vector& data) { std::ifstream fstr(file, std::ios::binary); fstr.unsetf(std::ios::skipws); fstr.seekg(0, std::ios::end); const auto file_size = fstr.tellg(); fstr.seekg(NULL, std::ios::beg); data.reserve(static_cast(file_size)); data.insert(data.begin(), std::istream_iterator(fstr), std::istream_iterator()); } inline std::uint32_t get_pid(const wchar_t* proc_name) { PROCESSENTRY32 proc_info; proc_info.dwSize = sizeof(proc_info); HANDLE proc_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); if (proc_snapshot == INVALID_HANDLE_VALUE) return NULL; Process32First(proc_snapshot, &proc_info); if (!wcscmp(proc_info.szExeFile, proc_name)) { CloseHandle(proc_snapshot); return proc_info.th32ProcessID; } while (Process32Next(proc_snapshot, &proc_info)) { if (!wcscmp(proc_info.szExeFile, proc_name)) { CloseHandle(proc_snapshot); return proc_info.th32ProcessID; } } CloseHandle(proc_snapshot); return NULL; } namespace kmodule { using kmodule_callback_t = std::function; inline void each_module(kmodule_callback_t callback) { void* buffer = nullptr; DWORD buffer_size = NULL; auto status = NtQuerySystemInformation( static_cast(0xB), buffer, buffer_size, &buffer_size); while (status == STATUS_INFO_LENGTH_MISMATCH) { VirtualFree(buffer, NULL, MEM_RELEASE); buffer = VirtualAlloc(nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); status = NtQuerySystemInformation( static_cast(0xB), buffer, buffer_size, &buffer_size); } if (!NT_SUCCESS(status)) { VirtualFree(buffer, NULL, MEM_RELEASE); return; } const auto modules = static_cast(buffer); for (auto idx = 0u; idx < modules->NumberOfModules; ++idx) { auto full_path = std::string( reinterpret_cast( modules->Modules[idx].FullPathName)); if (full_path.find("\\SystemRoot\\") != std::string::npos) full_path.replace(full_path.find("\\SystemRoot\\"), sizeof("\\SystemRoot\\") - 1, std::string(getenv("SYSTEMROOT")).append("\\")); else if (full_path.find("\\??\\") != std::string::npos) full_path.replace(full_path.find("\\??\\"), sizeof("\\??\\") - 1, ""); if (!callback(&modules->Modules[idx], full_path.c_str())) { VirtualFree(buffer, NULL, MEM_RELEASE); return; } } VirtualFree(buffer, NULL, MEM_RELEASE); return; } inline std::uintptr_t get_base(const char* module_name) { void* buffer = nullptr; DWORD buffer_size = NULL; auto status = NtQuerySystemInformation( static_cast(0xB), buffer, buffer_size, &buffer_size); while (status == STATUS_INFO_LENGTH_MISMATCH) { VirtualFree(buffer, NULL, MEM_RELEASE); buffer = VirtualAlloc(nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); status = NtQuerySystemInformation( static_cast(0xB), buffer, buffer_size, &buffer_size); } if (!NT_SUCCESS(status)) { VirtualFree(buffer, NULL, MEM_RELEASE); return NULL; } const auto modules = static_cast(buffer); for (auto idx = 0u; idx < modules->NumberOfModules; ++idx) { const auto current_module_name = std::string(reinterpret_cast( modules->Modules[idx].FullPathName) + modules->Modules[idx].OffsetToFileName); if (!_stricmp(current_module_name.c_str(), module_name)) { const auto result = reinterpret_cast( modules->Modules[idx].ImageBase); VirtualFree(buffer, NULL, MEM_RELEASE); return result; } } VirtualFree(buffer, NULL, MEM_RELEASE); return NULL; } inline std::uintptr_t get_export(const char* module_name, const char* export_name) { void* buffer = nullptr; DWORD buffer_size = NULL; NTSTATUS status = NtQuerySystemInformation( static_cast(0xB), buffer, buffer_size, &buffer_size ); while (status == STATUS_INFO_LENGTH_MISMATCH) { VirtualFree(buffer, 0, MEM_RELEASE); buffer = VirtualAlloc(nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); status = NtQuerySystemInformation( static_cast(0xB), buffer, buffer_size, &buffer_size ); } if (!NT_SUCCESS(status)) { VirtualFree(buffer, 0, MEM_RELEASE); return NULL; } const auto modules = static_cast(buffer); for (auto idx = 0u; idx < modules->NumberOfModules; ++idx) { // find module and then load library it const std::string current_module_name = std::string(reinterpret_cast( modules->Modules[idx].FullPathName) + modules->Modules[idx].OffsetToFileName ); if (!_stricmp(current_module_name.c_str(), module_name)) { auto full_path = std::string( reinterpret_cast( modules->Modules[idx].FullPathName)); if (full_path.find("\\SystemRoot\\") != std::string::npos) full_path.replace(full_path.find("\\SystemRoot\\"), sizeof("\\SystemRoot\\") - 1, std::string(getenv("SYSTEMROOT")).append("\\")); else if (full_path.find("\\??\\") != std::string::npos) full_path.replace(full_path.find("\\??\\"), sizeof("\\??\\") - 1, ""); const auto module_base = LoadLibraryExA( full_path.c_str(), NULL, DONT_RESOLVE_DLL_REFERENCES ); if (!module_base) { // free the RTL_PROCESS_MODULES buffer... VirtualFree(buffer, NULL, MEM_RELEASE); return NULL; } const auto image_base = reinterpret_cast( modules->Modules[idx].ImageBase); // free the RTL_PROCESS_MODULES buffer... VirtualFree(buffer, NULL, MEM_RELEASE); const auto export_um_addr = reinterpret_cast( GetProcAddress(module_base, export_name)); if (!export_um_addr) return NULL; return (export_um_addr - reinterpret_cast(module_base)) + image_base; } } VirtualFree(buffer, NULL, MEM_RELEASE); return NULL; } } namespace pe { using section_callback_t = std::function; // returns an std::vector containing all of the bytes of the section // and also the RVA from the image base to the beginning of the section... inline std::pair, std::uint32_t> get_section(std::uintptr_t module_base, const char* section_name) { const auto nt_headers = reinterpret_cast( reinterpret_cast(module_base)->e_lfanew + module_base); const auto section_header = reinterpret_cast( reinterpret_cast(nt_headers) + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) + nt_headers->FileHeader.SizeOfOptionalHeader); for (auto idx = 0u; idx < nt_headers->FileHeader.NumberOfSections; ++idx) { const auto _section_name = reinterpret_cast( section_header[idx].Name); // sometimes section names are not null terminated... if (!strncmp(_section_name, section_name, strlen(section_name) - 1)) { const auto section_base = reinterpret_cast( module_base + section_header[idx].VirtualAddress); const auto section_end = reinterpret_cast( section_base + section_header[idx].Misc.VirtualSize); std::vector section_bin(section_base, section_end); return { section_bin, section_header[idx].VirtualAddress }; } } return { {}, {} }; } inline void each_section(section_callback_t callback, std::uintptr_t module_base) { if (!module_base) return; const auto nt_headers = reinterpret_cast( reinterpret_cast(module_base)->e_lfanew + module_base); const auto section_header = reinterpret_cast( reinterpret_cast(nt_headers) + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) + nt_headers->FileHeader.SizeOfOptionalHeader); for (auto idx = 0u; idx < nt_headers->FileHeader.NumberOfSections; ++idx) { const auto _section_name = reinterpret_cast( section_header[idx].Name); // keep looping until the callback returns false... if (!callback(§ion_header[idx], module_base)) return; } } } namespace rop { // https://j00ru.vexillium.org/2011/06/smep-what-is-it-and-how-to-beat-it-on-windows/ // http://blog.ptsecurity.com/2012/09/bypassing-intel-smep-on-windows-8-x64.html?m=1 // just implimented the rop information from these posts... inline std::uintptr_t find_kgadget(const char* sig, const char* mask) { std::uintptr_t result = 0u; kmodule::each_module( [&](auto kernel_image, const char* image_name) -> bool { utils::pe::each_section( [&](auto section_header, std::uintptr_t image_base) -> bool { if (section_header->Characteristics & IMAGE_SCN_CNT_CODE && !(section_header->Characteristics & IMAGE_SCN_MEM_DISCARDABLE)) { const auto rop_gadget = utils::scan(image_base + section_header->VirtualAddress, section_header->Misc.VirtualSize, sig, mask); if(rop_gadget) result = (rop_gadget - image_base) + reinterpret_cast(kernel_image->ImageBase); return !rop_gadget; } return true; }, reinterpret_cast( LoadLibraryExA(image_name, NULL, DONT_RESOLVE_DLL_REFERENCES)) ); return !result; } ); return result; } } }