parent
ce7d94c49a
commit
34cacd54a9
@ -1,158 +0,0 @@
|
|||||||
<?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>{09b41831-3164-48ad-8660-23457d82b73b}</ProjectGuid>
|
|
||||||
<RootNamespace>DeepSpaceNetwork</RootNamespace>
|
|
||||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
|
||||||
<ProjectName>Example</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>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v142</PlatformToolset>
|
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
|
||||||
<ImportGroup Label="ExtensionSettings">
|
|
||||||
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
|
|
||||||
</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)</PreprocessorDefinitions>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
</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>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClCompile Include="main.cpp" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<MASM Include="com.asm">
|
|
||||||
<FileType>Document</FileType>
|
|
||||||
</MASM>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClInclude Include="com.h" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
|
||||||
<ImportGroup Label="ExtensionTargets">
|
|
||||||
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
|
|
||||||
</ImportGroup>
|
|
||||||
</Project>
|
|
@ -1,9 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#define VMEXIT_KEY 0xDEADBEEFDEADBEEF
|
|
||||||
enum class vmexit_command_t
|
|
||||||
{
|
|
||||||
init_paging_tables = 0x111
|
|
||||||
// add your commands here...
|
|
||||||
};
|
|
||||||
|
|
||||||
extern "C" size_t hypercall(size_t key, vmexit_command_t command);
|
|
@ -1,9 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include "com.h"
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
auto result = hypercall(VMEXIT_KEY, vmexit_command_t::init_paging_tables);
|
|
||||||
std::printf("[+] hyper-v (CPUID) init page table result -> %d\n", result);
|
|
||||||
std::getchar();
|
|
||||||
}
|
|
@ -0,0 +1,69 @@
|
|||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
auto dbg::debug_print_decimal(long long number) -> void
|
||||||
|
{
|
||||||
|
if (number < 0)
|
||||||
|
{
|
||||||
|
__outbyte(PORT_NUM, '-');
|
||||||
|
number = -number;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto d = 1000000000000000000; d != 0; d /= 10)
|
||||||
|
if ((number / d) != 0)
|
||||||
|
__outbyte(PORT_NUM, alphabet[(number / d) % 10]);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dbg::debug_print_hex(u64 number, const bool show_zeros) -> void
|
||||||
|
{
|
||||||
|
for (auto d = 0x1000000000000000; d != 0; d /= 0x10)
|
||||||
|
if (show_zeros || (number / d) != 0)
|
||||||
|
__outbyte(PORT_NUM, alphabet[(number / d) % 0x10]);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dbg::print(const char* format, ...) -> void
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
|
||||||
|
while (format[0])
|
||||||
|
{
|
||||||
|
if (format[0] == '%')
|
||||||
|
{
|
||||||
|
switch (format[1])
|
||||||
|
{
|
||||||
|
case 'd':
|
||||||
|
debug_print_decimal(va_arg(args, int));
|
||||||
|
format += 2;
|
||||||
|
continue;
|
||||||
|
case 'x':
|
||||||
|
debug_print_hex(va_arg(args, u32), false);
|
||||||
|
format += 2;
|
||||||
|
continue;
|
||||||
|
case 'l':
|
||||||
|
if (format[2] == 'l')
|
||||||
|
{
|
||||||
|
switch (format[3])
|
||||||
|
{
|
||||||
|
case 'd':
|
||||||
|
debug_print_decimal(va_arg(args, u64));
|
||||||
|
format += 4;
|
||||||
|
continue;
|
||||||
|
case 'x':
|
||||||
|
debug_print_hex(va_arg(args, u64), false);
|
||||||
|
format += 4;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
debug_print_hex(va_arg(args, u64), true);
|
||||||
|
format += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__outbyte(PORT_NUM, format[0]);
|
||||||
|
++format;
|
||||||
|
}
|
||||||
|
va_end(args);
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#define PORT_NUM 0x2F8
|
||||||
|
namespace dbg
|
||||||
|
{
|
||||||
|
constexpr char alphabet[] = "0123456789ABCDEF";
|
||||||
|
auto debug_print_decimal(long long number) -> void;
|
||||||
|
auto debug_print_hex(u64 number, const bool show_zeros) -> void;
|
||||||
|
auto print(const char* format, ...) -> void;
|
||||||
|
}
|
@ -0,0 +1,363 @@
|
|||||||
|
#include "mm.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
auto mm::map_guest_phys(guest_phys_t phys_addr, map_type_t map_type) -> u64
|
||||||
|
{
|
||||||
|
//const auto host_phys =
|
||||||
|
//translate_guest_physical(
|
||||||
|
//phys_addr, map_type);
|
||||||
|
|
||||||
|
//if (!host_phys)
|
||||||
|
//return {};
|
||||||
|
|
||||||
|
return map_page(phys_addr, map_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mm::map_guest_virt(guest_phys_t dirbase, guest_virt_t virt_addr, map_type_t map_type) -> u64
|
||||||
|
{
|
||||||
|
const auto guest_phys =
|
||||||
|
translate_guest_virtual(
|
||||||
|
dirbase, virt_addr, map_type);
|
||||||
|
|
||||||
|
if (!guest_phys)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return map_guest_phys(guest_phys, map_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mm::map_page(host_phys_t phys_addr, map_type_t map_type) -> u64
|
||||||
|
{
|
||||||
|
cpuid_eax_01 cpuid_value;
|
||||||
|
__cpuid((int*)&cpuid_value, 1);
|
||||||
|
|
||||||
|
mm::pt[(cpuid_value
|
||||||
|
.cpuid_additional_information
|
||||||
|
.initial_apic_id * 2)
|
||||||
|
+ (unsigned)map_type].pfn = phys_addr >> 12;
|
||||||
|
|
||||||
|
__invlpg(reinterpret_cast<void*>(
|
||||||
|
get_map_virt(virt_addr_t{ phys_addr }.offset_4kb, map_type)));
|
||||||
|
|
||||||
|
return get_map_virt(virt_addr_t{ phys_addr }.offset_4kb, map_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mm::get_map_virt(u16 offset, map_type_t map_type) -> u64
|
||||||
|
{
|
||||||
|
cpuid_eax_01 cpuid_value;
|
||||||
|
__cpuid((int*)&cpuid_value, 1);
|
||||||
|
virt_addr_t virt_addr{ MAPPING_ADDRESS_BASE };
|
||||||
|
|
||||||
|
virt_addr.pt_index = (cpuid_value
|
||||||
|
.cpuid_additional_information
|
||||||
|
.initial_apic_id * 2)
|
||||||
|
+ (unsigned)map_type;
|
||||||
|
|
||||||
|
return virt_addr.value + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mm::translate(host_virt_t host_virt) -> u64
|
||||||
|
{
|
||||||
|
virt_addr_t virt_addr{ host_virt };
|
||||||
|
virt_addr_t cursor{ (u64)hyperv_pml4 };
|
||||||
|
|
||||||
|
if (!reinterpret_cast<ppml4e>(cursor.value)[virt_addr.pml4_index].present)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
cursor.pt_index = virt_addr.pml4_index;
|
||||||
|
if (!reinterpret_cast<ppdpte>(cursor.value)[virt_addr.pdpt_index].present)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// handle 1gb large page...
|
||||||
|
if (reinterpret_cast<ppdpte>(cursor.value)[virt_addr.pdpt_index].large_page)
|
||||||
|
return (reinterpret_cast<ppdpte>(cursor.value)
|
||||||
|
[virt_addr.pdpt_index].pfn << 30) + virt_addr.offset_1gb;
|
||||||
|
|
||||||
|
cursor.pd_index = virt_addr.pml4_index;
|
||||||
|
cursor.pt_index = virt_addr.pdpt_index;
|
||||||
|
if (!reinterpret_cast<ppde>(cursor.value)[virt_addr.pd_index].present)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// handle 2mb large page...
|
||||||
|
if (reinterpret_cast<ppde>(cursor.value)[virt_addr.pd_index].large_page)
|
||||||
|
return (reinterpret_cast<ppde>(cursor.value)
|
||||||
|
[virt_addr.pd_index].pfn << 21) + virt_addr.offset_2mb;
|
||||||
|
|
||||||
|
cursor.pdpt_index = virt_addr.pml4_index;
|
||||||
|
cursor.pd_index = virt_addr.pdpt_index;
|
||||||
|
cursor.pt_index = virt_addr.pd_index;
|
||||||
|
if (!reinterpret_cast<ppte>(cursor.value)[virt_addr.pt_index].present)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return (reinterpret_cast<ppte>(cursor.value)
|
||||||
|
[virt_addr.pt_index].pfn << 12) + virt_addr.offset_4kb;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mm::translate_guest_virtual(guest_phys_t dirbase, guest_virt_t guest_virt, map_type_t map_type) -> u64
|
||||||
|
{
|
||||||
|
virt_addr_t virt_addr{ guest_virt };
|
||||||
|
|
||||||
|
const auto pml4 =
|
||||||
|
reinterpret_cast<pml4e*>(
|
||||||
|
map_guest_phys(dirbase, map_type));
|
||||||
|
|
||||||
|
if (!pml4 || !pml4[virt_addr.pml4_index].present)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const auto pdpt =
|
||||||
|
reinterpret_cast<pdpte*>(map_guest_phys(
|
||||||
|
pml4[virt_addr.pml4_index].pfn << 12, map_type));
|
||||||
|
|
||||||
|
if (!pdpt || !pdpt[virt_addr.pdpt_index].present)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// handle 1gb pages...
|
||||||
|
if (pdpt[virt_addr.pdpt_index].large_page)
|
||||||
|
return (pdpt[virt_addr.pdpt_index].pfn << 12) + virt_addr.offset_1gb;
|
||||||
|
|
||||||
|
const auto pd =
|
||||||
|
reinterpret_cast<pde*>(map_guest_phys(
|
||||||
|
pdpt[virt_addr.pdpt_index].pfn << 12, map_type));
|
||||||
|
|
||||||
|
if (!pd || !pd[virt_addr.pd_index].present)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// handle 2mb pages...
|
||||||
|
if (pd[virt_addr.pd_index].large_page)
|
||||||
|
return (pd[virt_addr.pd_index].pfn << 12) + virt_addr.offset_2mb;
|
||||||
|
|
||||||
|
const auto pt =
|
||||||
|
reinterpret_cast<pte*>(map_guest_phys(
|
||||||
|
pd[virt_addr.pd_index].pfn << 12, map_type));
|
||||||
|
|
||||||
|
if (!pt || !pt[virt_addr.pt_index].present)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return (pt[virt_addr.pt_index].pfn << 12) + virt_addr.offset_4kb;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mm::translate_guest_physical(guest_phys_t phys_addr, map_type_t map_type) -> u64
|
||||||
|
{
|
||||||
|
phys_addr_t guest_phys{ phys_addr };
|
||||||
|
const auto vmcb = svm::get_vmcb();
|
||||||
|
|
||||||
|
const auto npt_pml4 =
|
||||||
|
reinterpret_cast<pnpt_pml4e>(
|
||||||
|
map_page(vmcb->ncr3, map_type));
|
||||||
|
|
||||||
|
if (!npt_pml4[guest_phys.pml4_index].present)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const auto npt_pdpt =
|
||||||
|
reinterpret_cast<pnpt_pdpte>(
|
||||||
|
map_page(npt_pml4[guest_phys.pml4_index].pfn << 12, map_type));
|
||||||
|
|
||||||
|
if (!npt_pdpt[guest_phys.pdpt_index].present)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const auto npt_pd =
|
||||||
|
reinterpret_cast<pnpt_pde>(
|
||||||
|
map_page(npt_pdpt[guest_phys.pdpt_index].pfn << 12, map_type));
|
||||||
|
|
||||||
|
if (!npt_pd[guest_phys.pd_index].present)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// handle 2mb pages...
|
||||||
|
if (reinterpret_cast<pnpt_pde_2mb>(npt_pd)[guest_phys.pd_index].large_page)
|
||||||
|
return (reinterpret_cast<pnpt_pde_2mb>(npt_pd)
|
||||||
|
[guest_phys.pd_index].pfn << 21) + guest_phys.offset_2mb;
|
||||||
|
|
||||||
|
const auto npt_pt =
|
||||||
|
reinterpret_cast<pnpt_pte>(
|
||||||
|
map_page(npt_pd[guest_phys.pd_index].pfn << 12, map_type));
|
||||||
|
|
||||||
|
if (!npt_pt[guest_phys.pt_index].present)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return (npt_pt[guest_phys.pt_index].pfn << 12) + guest_phys.offset_4kb;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mm::init() -> svm::vmxroot_error_t
|
||||||
|
{
|
||||||
|
const auto pdpt_phys =
|
||||||
|
translate(reinterpret_cast<u64>(pdpt));
|
||||||
|
|
||||||
|
const auto pd_phys =
|
||||||
|
translate(reinterpret_cast<u64>(pd));
|
||||||
|
|
||||||
|
const auto pt_phys =
|
||||||
|
translate(reinterpret_cast<u64>(pt));
|
||||||
|
|
||||||
|
if (!pdpt_phys || !pd_phys || !pt_phys)
|
||||||
|
return svm::vmxroot_error_t::invalid_host_virtual;
|
||||||
|
|
||||||
|
// setup mapping page table entries...
|
||||||
|
{
|
||||||
|
hyperv_pml4[MAPPING_PML4_IDX].present = true;
|
||||||
|
hyperv_pml4[MAPPING_PML4_IDX].pfn = pdpt_phys >> 12;
|
||||||
|
hyperv_pml4[MAPPING_PML4_IDX].user_supervisor = false;
|
||||||
|
hyperv_pml4[MAPPING_PML4_IDX].writeable = true;
|
||||||
|
|
||||||
|
pdpt[511].present = true;
|
||||||
|
pdpt[511].pfn = pd_phys >> 12;
|
||||||
|
pdpt[511].user_supervisor = false;
|
||||||
|
pdpt[511].rw = true;
|
||||||
|
|
||||||
|
pd[511].present = true;
|
||||||
|
pd[511].pfn = pt_phys >> 12;
|
||||||
|
pd[511].user_supervisor = false;
|
||||||
|
pd[511].rw = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// each core will have its own page it can use to map
|
||||||
|
// physical memory into virtual memory :^)
|
||||||
|
for (auto idx = 0u; idx < 512; ++idx)
|
||||||
|
{
|
||||||
|
pt[idx].present = true;
|
||||||
|
pt[idx].user_supervisor = false;
|
||||||
|
pt[idx].rw = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto mapped_pml4 =
|
||||||
|
reinterpret_cast<ppml4e>(
|
||||||
|
mm::map_page(__readcr3()));
|
||||||
|
|
||||||
|
// check to make sure translate works...
|
||||||
|
if (translate((u64)mapped_pml4) != __readcr3())
|
||||||
|
return svm::vmxroot_error_t::vmxroot_translate_failure;
|
||||||
|
|
||||||
|
// check to make sure the self ref pml4e is valid...
|
||||||
|
if (mapped_pml4[SELF_REF_PML4_IDX].pfn != __readcr3() >> 12)
|
||||||
|
return svm::vmxroot_error_t::invalid_self_ref_pml4e;
|
||||||
|
|
||||||
|
// check to make sure the mapping pml4e is valid...
|
||||||
|
if (mapped_pml4[MAPPING_PML4_IDX].pfn != pdpt_phys >> 12)
|
||||||
|
return svm::vmxroot_error_t::invalid_mapping_pml4e;
|
||||||
|
|
||||||
|
return svm::vmxroot_error_t::error_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mm::read_guest_phys(guest_phys_t dirbase, guest_phys_t guest_phys,
|
||||||
|
guest_virt_t guest_virt, u64 size) -> svm::vmxroot_error_t
|
||||||
|
{
|
||||||
|
// handle reading over page boundaries of both src and dest...
|
||||||
|
while (size)
|
||||||
|
{
|
||||||
|
auto dest_current_size = PAGE_4KB -
|
||||||
|
virt_addr_t{ guest_virt }.offset_4kb;
|
||||||
|
|
||||||
|
if (size < dest_current_size)
|
||||||
|
dest_current_size = size;
|
||||||
|
|
||||||
|
auto src_current_size = PAGE_4KB -
|
||||||
|
phys_addr_t{ guest_phys }.offset_4kb;
|
||||||
|
|
||||||
|
if (size < src_current_size)
|
||||||
|
src_current_size = size;
|
||||||
|
|
||||||
|
auto current_size =
|
||||||
|
min(dest_current_size, src_current_size);
|
||||||
|
|
||||||
|
const auto mapped_dest =
|
||||||
|
reinterpret_cast<void*>(
|
||||||
|
map_guest_virt(dirbase, guest_virt, map_type_t::map_dest));
|
||||||
|
|
||||||
|
if (!mapped_dest)
|
||||||
|
return svm::vmxroot_error_t::invalid_guest_virtual;
|
||||||
|
|
||||||
|
const auto mapped_src =
|
||||||
|
reinterpret_cast<void*>(
|
||||||
|
map_guest_phys(guest_phys, map_type_t::map_src));
|
||||||
|
|
||||||
|
if (!mapped_src)
|
||||||
|
return svm::vmxroot_error_t::invalid_guest_physical;
|
||||||
|
|
||||||
|
memcpy(mapped_dest, mapped_src, current_size);
|
||||||
|
guest_phys += current_size;
|
||||||
|
guest_virt += current_size;
|
||||||
|
size -= current_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return svm::vmxroot_error_t::error_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mm::write_guest_phys(guest_phys_t dirbase,
|
||||||
|
guest_phys_t guest_phys, guest_virt_t guest_virt, u64 size) -> svm::vmxroot_error_t
|
||||||
|
{
|
||||||
|
// handle reading over page boundaries of both src and dest...
|
||||||
|
while (size)
|
||||||
|
{
|
||||||
|
auto dest_current_size = PAGE_4KB -
|
||||||
|
virt_addr_t{ guest_virt }.offset_4kb;
|
||||||
|
|
||||||
|
if (size < dest_current_size)
|
||||||
|
dest_current_size = size;
|
||||||
|
|
||||||
|
auto src_current_size = PAGE_4KB -
|
||||||
|
phys_addr_t{ guest_phys }.offset_4kb;
|
||||||
|
|
||||||
|
if (size < src_current_size)
|
||||||
|
src_current_size = size;
|
||||||
|
|
||||||
|
auto current_size =
|
||||||
|
min(dest_current_size, src_current_size);
|
||||||
|
|
||||||
|
const auto mapped_src =
|
||||||
|
reinterpret_cast<void*>(
|
||||||
|
map_guest_virt(dirbase, guest_virt, map_type_t::map_src));
|
||||||
|
|
||||||
|
if (!mapped_src)
|
||||||
|
return svm::vmxroot_error_t::invalid_guest_virtual;
|
||||||
|
|
||||||
|
const auto mapped_dest =
|
||||||
|
reinterpret_cast<void*>(
|
||||||
|
map_guest_phys(guest_phys, map_type_t::map_dest));
|
||||||
|
|
||||||
|
if (!mapped_src)
|
||||||
|
return svm::vmxroot_error_t::invalid_guest_physical;
|
||||||
|
|
||||||
|
memcpy(mapped_dest, mapped_src, current_size);
|
||||||
|
guest_phys += current_size;
|
||||||
|
guest_virt += current_size;
|
||||||
|
size -= current_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return svm::vmxroot_error_t::error_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mm::copy_guest_virt(guest_phys_t dirbase_src, guest_virt_t virt_src,
|
||||||
|
guest_virt_t dirbase_dest, guest_virt_t virt_dest, u64 size) -> svm::vmxroot_error_t
|
||||||
|
{
|
||||||
|
while (size)
|
||||||
|
{
|
||||||
|
auto dest_size = PAGE_4KB - virt_addr_t{ virt_dest }.offset_4kb;
|
||||||
|
if (size < dest_size)
|
||||||
|
dest_size = size;
|
||||||
|
|
||||||
|
auto src_size = PAGE_4KB - virt_addr_t{ virt_src }.offset_4kb;
|
||||||
|
if (size < src_size)
|
||||||
|
src_size = size;
|
||||||
|
|
||||||
|
const auto mapped_src =
|
||||||
|
reinterpret_cast<void*>(
|
||||||
|
map_guest_virt(dirbase_src, virt_src, map_type_t::map_src));
|
||||||
|
|
||||||
|
if (!mapped_src)
|
||||||
|
return svm::vmxroot_error_t::invalid_guest_virtual;
|
||||||
|
|
||||||
|
const auto mapped_dest =
|
||||||
|
reinterpret_cast<void*>(
|
||||||
|
map_guest_virt(dirbase_dest, virt_dest, map_type_t::map_dest));
|
||||||
|
|
||||||
|
if (!mapped_dest)
|
||||||
|
return svm::vmxroot_error_t::invalid_guest_virtual;
|
||||||
|
|
||||||
|
auto current_size = min(dest_size, src_size);
|
||||||
|
memcpy(mapped_dest, mapped_src, current_size);
|
||||||
|
|
||||||
|
virt_src += current_size;
|
||||||
|
virt_dest += current_size;
|
||||||
|
size -= current_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return svm::vmxroot_error_t::error_success;
|
||||||
|
}
|
@ -0,0 +1,237 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#define SELF_REF_PML4_IDX 510
|
||||||
|
#define MAPPING_PML4_IDX 100
|
||||||
|
|
||||||
|
#define MAPPING_ADDRESS_BASE 0x0000327FFFE00000
|
||||||
|
#define SELF_REF_PML4 0xFFFFFF7FBFDFE000
|
||||||
|
|
||||||
|
#define EPT_LARGE_PDPTE_OFFSET(_) (((u64)(_)) & ((0x1000 * 0x200 * 0x200) - 1))
|
||||||
|
#define EPT_LARGE_PDE_OFFSET(_) (((u64)(_)) & ((0x1000 * 0x200) - 1))
|
||||||
|
|
||||||
|
#pragma section(".pdpt", read, write)
|
||||||
|
#pragma section(".pd", read, write)
|
||||||
|
#pragma section(".pt", read, write)
|
||||||
|
|
||||||
|
namespace mm
|
||||||
|
{
|
||||||
|
enum class map_type_t
|
||||||
|
{
|
||||||
|
map_src,
|
||||||
|
map_dest
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef union _virt_addr_t
|
||||||
|
{
|
||||||
|
u64 value;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u64 offset_4kb : 12;
|
||||||
|
u64 pt_index : 9;
|
||||||
|
u64 pd_index : 9;
|
||||||
|
u64 pdpt_index : 9;
|
||||||
|
u64 pml4_index : 9;
|
||||||
|
u64 reserved : 16;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u64 offset_2mb : 21;
|
||||||
|
u64 pd_index : 9;
|
||||||
|
u64 pdpt_index : 9;
|
||||||
|
u64 pml4_index : 9;
|
||||||
|
u64 reserved : 16;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u64 offset_1gb : 30;
|
||||||
|
u64 pdpt_index : 9;
|
||||||
|
u64 pml4_index : 9;
|
||||||
|
u64 reserved : 16;
|
||||||
|
};
|
||||||
|
|
||||||
|
} virt_addr_t, * pvirt_addr_t;
|
||||||
|
using phys_addr_t = virt_addr_t;
|
||||||
|
|
||||||
|
typedef union _pml4e
|
||||||
|
{
|
||||||
|
u64 value;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u64 present : 1;
|
||||||
|
u64 writeable : 1;
|
||||||
|
u64 user_supervisor : 1;
|
||||||
|
u64 page_write_through : 1;
|
||||||
|
u64 page_cache : 1;
|
||||||
|
u64 accessed : 1;
|
||||||
|
u64 ignore_1 : 1;
|
||||||
|
u64 page_size : 1;
|
||||||
|
u64 ignore_2 : 4;
|
||||||
|
u64 pfn : 36;
|
||||||
|
u64 reserved : 4;
|
||||||
|
u64 ignore_3 : 11;
|
||||||
|
u64 nx : 1;
|
||||||
|
};
|
||||||
|
} pml4e, * ppml4e;
|
||||||
|
|
||||||
|
typedef union _pdpte
|
||||||
|
{
|
||||||
|
u64 value;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u64 present : 1;
|
||||||
|
u64 rw : 1;
|
||||||
|
u64 user_supervisor : 1;
|
||||||
|
u64 page_write_through : 1;
|
||||||
|
u64 page_cache : 1;
|
||||||
|
u64 accessed : 1;
|
||||||
|
u64 ignore_1 : 1;
|
||||||
|
u64 large_page : 1;
|
||||||
|
u64 ignore_2 : 4;
|
||||||
|
u64 pfn : 36;
|
||||||
|
u64 reserved : 4;
|
||||||
|
u64 ignore_3 : 11;
|
||||||
|
u64 nx : 1;
|
||||||
|
};
|
||||||
|
} pdpte, * ppdpte;
|
||||||
|
|
||||||
|
typedef union _pde
|
||||||
|
{
|
||||||
|
u64 value;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u64 present : 1;
|
||||||
|
u64 rw : 1;
|
||||||
|
u64 user_supervisor : 1;
|
||||||
|
u64 page_write_through : 1;
|
||||||
|
u64 page_cache : 1;
|
||||||
|
u64 accessed : 1;
|
||||||
|
u64 ignore_1 : 1;
|
||||||
|
u64 large_page : 1;
|
||||||
|
u64 ignore_2 : 4;
|
||||||
|
u64 pfn : 36;
|
||||||
|
u64 reserved : 4;
|
||||||
|
u64 ignore_3 : 11;
|
||||||
|
u64 nx : 1;
|
||||||
|
};
|
||||||
|
} pde, * ppde;
|
||||||
|
|
||||||
|
typedef union _pte
|
||||||
|
{
|
||||||
|
u64 value;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u64 present : 1;
|
||||||
|
u64 rw : 1;
|
||||||
|
u64 user_supervisor : 1;
|
||||||
|
u64 page_write_through : 1;
|
||||||
|
u64 page_cache : 1;
|
||||||
|
u64 accessed : 1;
|
||||||
|
u64 dirty : 1;
|
||||||
|
u64 access_type : 1;
|
||||||
|
u64 global : 1;
|
||||||
|
u64 ignore_2 : 3;
|
||||||
|
u64 pfn : 36;
|
||||||
|
u64 reserved : 4;
|
||||||
|
u64 ignore_3 : 7;
|
||||||
|
u64 pk : 4;
|
||||||
|
u64 nx : 1;
|
||||||
|
};
|
||||||
|
} pte, * ppte;
|
||||||
|
|
||||||
|
typedef struct _npt_pml4e
|
||||||
|
{
|
||||||
|
union
|
||||||
|
{
|
||||||
|
u64 value;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u64 present : 1;
|
||||||
|
u64 writeable : 1;
|
||||||
|
u64 user : 1;
|
||||||
|
u64 write_through : 1;
|
||||||
|
u64 cache_disable : 1;
|
||||||
|
u64 accessed : 1;
|
||||||
|
u64 reserved1 : 3;
|
||||||
|
u64 avl : 3;
|
||||||
|
u64 pfn : 40;
|
||||||
|
u64 reserved2 : 11;
|
||||||
|
u64 nx : 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
} npt_pml4e, *pnpt_pml4e, npt_pdpte,
|
||||||
|
*pnpt_pdpte, npt_pde, *pnpt_pde;
|
||||||
|
|
||||||
|
typedef struct _npt_pte
|
||||||
|
{
|
||||||
|
union
|
||||||
|
{
|
||||||
|
u64 value;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u64 present : 1;
|
||||||
|
u64 writeable : 1;
|
||||||
|
u64 user : 1;
|
||||||
|
u64 write_through : 1;
|
||||||
|
u64 cache_disable : 1;
|
||||||
|
u64 accessed : 1;
|
||||||
|
u64 dirty : 1;
|
||||||
|
u64 pat : 1;
|
||||||
|
u64 global : 1;
|
||||||
|
u64 avl : 3;
|
||||||
|
u64 pfn : 40;
|
||||||
|
u64 reserved : 11;
|
||||||
|
u64 nx : 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
} npt_pte, *pnpt_pte;
|
||||||
|
|
||||||
|
typedef struct _npt_pde_2mb
|
||||||
|
{
|
||||||
|
union
|
||||||
|
{
|
||||||
|
u64 value;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u64 present : 1;
|
||||||
|
u64 writeable : 1;
|
||||||
|
u64 user : 1;
|
||||||
|
u64 write_through : 1;
|
||||||
|
u64 cache_disable : 1;
|
||||||
|
u64 accessed : 1;
|
||||||
|
u64 dirty : 1;
|
||||||
|
u64 large_page : 1;
|
||||||
|
u64 global : 1;
|
||||||
|
u64 avl : 3;
|
||||||
|
u64 pat : 1;
|
||||||
|
u64 reserved1 : 8;
|
||||||
|
u64 pfn : 31;
|
||||||
|
u64 reserved2 : 11;
|
||||||
|
u64 nx : 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
} npt_pde_2mb, * pnpt_pde_2mb;
|
||||||
|
|
||||||
|
__declspec(allocate(".pdpt")) inline pdpte pdpt[512];
|
||||||
|
__declspec(allocate(".pd")) inline pde pd[512];
|
||||||
|
__declspec(allocate(".pt")) inline pte pt[512];
|
||||||
|
|
||||||
|
inline const ppml4e hyperv_pml4{ reinterpret_cast<ppml4e>(SELF_REF_PML4) };
|
||||||
|
|
||||||
|
auto init() -> svm::vmxroot_error_t;
|
||||||
|
auto map_guest_phys(guest_phys_t phys_addr, map_type_t map_type = map_type_t::map_src) -> u64;
|
||||||
|
auto map_guest_virt(guest_phys_t dirbase, guest_virt_t virt_addr, map_type_t map_type = map_type_t::map_src) -> u64;
|
||||||
|
|
||||||
|
auto map_page(host_phys_t phys_addr, map_type_t map_type = map_type_t::map_src) -> u64;
|
||||||
|
auto get_map_virt(u16 offset = 0u, map_type_t map_type = map_type_t::map_src) -> u64;
|
||||||
|
|
||||||
|
auto translate(host_virt_t host_virt) -> u64;
|
||||||
|
auto translate_guest_physical(guest_phys_t guest_phys, map_type_t map_type = map_type_t::map_src) -> u64;
|
||||||
|
auto translate_guest_virtual(guest_phys_t dirbase, guest_virt_t guest_virt, map_type_t map_type = map_type_t::map_src) -> u64;
|
||||||
|
|
||||||
|
auto read_guest_phys(guest_phys_t dirbase, guest_phys_t guest_phys, guest_virt_t guest_virt, u64 size) -> svm::vmxroot_error_t;
|
||||||
|
auto write_guest_phys(guest_phys_t dirbase, guest_phys_t guest_phys, guest_virt_t guest_virt, u64 size) -> svm::vmxroot_error_t;
|
||||||
|
auto copy_guest_virt(guest_phys_t dirbase_src, guest_virt_t virt_src, guest_virt_t dirbase_dest, guest_virt_t virt_dest, u64 size) ->svm::vmxroot_error_t;
|
||||||
|
}
|
@ -1,138 +0,0 @@
|
|||||||
#include "pg_table.h"
|
|
||||||
|
|
||||||
namespace pg_table
|
|
||||||
{
|
|
||||||
void* translate(void* virtual_address, const ptable_entries entries)
|
|
||||||
{
|
|
||||||
virt_addr_t virt_addr{ virtual_address };
|
|
||||||
virt_addr_t cursor{ hyperv_pml4 };
|
|
||||||
|
|
||||||
if (entries) entries->pml4e = reinterpret_cast<ppml4e>(cursor.value)[virt_addr.pml4_index];
|
|
||||||
if (!reinterpret_cast<ppml4e>(cursor.value)[virt_addr.pml4_index].present)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
// set the cursor to self reference so that when we read
|
|
||||||
// the addresses pointed to by cursor its going to be a pdpt...
|
|
||||||
cursor.pdpt_index = virt_addr_t{ hyperv_pml4 }.pml4_index;
|
|
||||||
cursor.pd_index = virt_addr_t{ hyperv_pml4 }.pml4_index;
|
|
||||||
cursor.pt_index = virt_addr.pml4_index;
|
|
||||||
if (entries) entries->pdpte = reinterpret_cast<ppdpte>(cursor.value)[virt_addr.pdpt_index];
|
|
||||||
|
|
||||||
if (!reinterpret_cast<ppdpte>(cursor.value)[virt_addr.pdpt_index].present)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
// set the cursor to self reference so that when we read
|
|
||||||
// the addresses pointed to by cursor its going to be a pd...
|
|
||||||
cursor.pdpt_index = virt_addr_t{ hyperv_pml4 }.pml4_index;
|
|
||||||
cursor.pd_index = virt_addr.pml4_index;
|
|
||||||
cursor.pt_index = virt_addr.pdpt_index;
|
|
||||||
if (entries) entries->pde = reinterpret_cast<ppde>(cursor.value)[virt_addr.pd_index];
|
|
||||||
|
|
||||||
if (!reinterpret_cast<ppde>(cursor.value)[virt_addr.pd_index].present)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
// set the cursor to self reference so that when we read
|
|
||||||
// the addresses pointed to by cursor its going to be a pt...
|
|
||||||
cursor.pdpt_index = virt_addr.pml4_index;
|
|
||||||
cursor.pd_index = virt_addr.pdpt_index;
|
|
||||||
cursor.pt_index = virt_addr.pd_index;
|
|
||||||
if (entries) entries->pte = reinterpret_cast<ppte>(cursor.value)[virt_addr.pt_index];
|
|
||||||
|
|
||||||
if (!reinterpret_cast<ppte>(cursor.value)[virt_addr.pt_index].present)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
return reinterpret_cast<void*>(
|
|
||||||
reinterpret_cast<ppte>(cursor.value)[virt_addr.pt_index].pfn << 12);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* translate(void* virtual_address, u32 pml4_pfn, const ptable_entries entries)
|
|
||||||
{
|
|
||||||
virt_addr_t virt_addr{ virtual_address };
|
|
||||||
const auto cursor = get_cursor_page();
|
|
||||||
|
|
||||||
set_cursor_page(pml4_pfn);
|
|
||||||
if (!reinterpret_cast<ppml4e>(cursor)[virt_addr.pml4_index].present)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (entries) entries->pml4e = reinterpret_cast<ppml4e>(cursor)[virt_addr.pml4_index];
|
|
||||||
set_cursor_page(reinterpret_cast<ppml4e>(cursor)[virt_addr.pml4_index].pfn);
|
|
||||||
if (!reinterpret_cast<ppdpte>(cursor)[virt_addr.pdpt_index].present)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (entries) entries->pdpte = reinterpret_cast<ppdpte>(cursor)[virt_addr.pdpt_index];
|
|
||||||
set_cursor_page(reinterpret_cast<ppdpte>(cursor)[virt_addr.pdpt_index].pfn);
|
|
||||||
if (!reinterpret_cast<ppde>(cursor)[virt_addr.pd_index].present)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (entries) entries->pde = reinterpret_cast<ppde>(cursor)[virt_addr.pd_index];
|
|
||||||
set_cursor_page(reinterpret_cast<ppde>(cursor)[virt_addr.pd_index].pfn);
|
|
||||||
if (!reinterpret_cast<ppte>(cursor)[virt_addr.pt_index].present)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (entries) entries->pte = reinterpret_cast<ppte>(cursor)[virt_addr.pt_index];
|
|
||||||
return reinterpret_cast<void*>(
|
|
||||||
reinterpret_cast<ppte>(cursor)[virt_addr.pt_index].pfn << 12);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_cursor_page(u32 phys_pfn)
|
|
||||||
{
|
|
||||||
cpuid_eax_01 cpuid_value;
|
|
||||||
__cpuid((int*)&cpuid_value, 1);
|
|
||||||
pg_table::pt[cpuid_value
|
|
||||||
.cpuid_additional_information
|
|
||||||
.initial_apic_id].pfn = phys_pfn;
|
|
||||||
|
|
||||||
// flush tlb for this page and then ensure the instruction stream
|
|
||||||
// is seralized as to not execute instructions out of order and access the page
|
|
||||||
// before the TLB is flushed...
|
|
||||||
__invlpg(get_cursor_page());
|
|
||||||
_mm_lfence();
|
|
||||||
}
|
|
||||||
|
|
||||||
void* get_cursor_page()
|
|
||||||
{
|
|
||||||
cpuid_eax_01 cpuid_value;
|
|
||||||
__cpuid((int*)&cpuid_value, 1);
|
|
||||||
constexpr auto cursor_page = 0x00007F7FFFE00000;
|
|
||||||
|
|
||||||
virt_addr_t virt_addr{ reinterpret_cast<void*>(cursor_page) };
|
|
||||||
virt_addr.pt_index = cpuid_value
|
|
||||||
.cpuid_additional_information
|
|
||||||
.initial_apic_id;
|
|
||||||
|
|
||||||
return virt_addr.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool init_pg_tables()
|
|
||||||
{
|
|
||||||
auto pdpt_phys = reinterpret_cast<u64>(translate(pdpt));
|
|
||||||
auto pd_phys = reinterpret_cast<u64>(translate(pd));
|
|
||||||
auto pt_phys = reinterpret_cast<u64>(translate(pt));
|
|
||||||
|
|
||||||
if (!pdpt_phys || !pd_phys || !pt_phys)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
hyperv_pml4[254].present = true;
|
|
||||||
hyperv_pml4[254].pfn = pdpt_phys >> 12;
|
|
||||||
hyperv_pml4[254].user_supervisor = false;
|
|
||||||
hyperv_pml4[254].rw = true;
|
|
||||||
|
|
||||||
pdpt[511].present = true;
|
|
||||||
pdpt[511].pfn = pd_phys >> 12;
|
|
||||||
pdpt[511].user_supervisor = false;
|
|
||||||
pdpt[511].rw = true;
|
|
||||||
|
|
||||||
pd[511].present = true;
|
|
||||||
pd[511].pfn = pt_phys >> 12;
|
|
||||||
pd[511].user_supervisor = false;
|
|
||||||
pd[511].rw = true;
|
|
||||||
|
|
||||||
for (auto idx = 0u; idx < 512; ++idx)
|
|
||||||
{
|
|
||||||
pt[idx].present = true;
|
|
||||||
pt[idx].user_supervisor = false;
|
|
||||||
pt[idx].rw = true;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,167 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "types.h"
|
|
||||||
#include "ia32.hpp"
|
|
||||||
|
|
||||||
#pragma section(".pdpt", read, write)
|
|
||||||
#pragma section(".pd", read, write)
|
|
||||||
#pragma section(".pt", read, write)
|
|
||||||
|
|
||||||
namespace pg_table
|
|
||||||
{
|
|
||||||
typedef union _virt_addr_t
|
|
||||||
{
|
|
||||||
void* value;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
u64 offset : 12;
|
|
||||||
u64 pt_index : 9;
|
|
||||||
u64 pd_index : 9;
|
|
||||||
u64 pdpt_index : 9;
|
|
||||||
u64 pml4_index : 9;
|
|
||||||
u64 reserved : 16;
|
|
||||||
};
|
|
||||||
} virt_addr_t, * pvirt_addr_t;
|
|
||||||
|
|
||||||
typedef union _pml4e
|
|
||||||
{
|
|
||||||
u64 value;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
u64 present : 1; // Must be 1, region invalid if 0.
|
|
||||||
u64 rw : 1; // If 0, writes not allowed.
|
|
||||||
u64 user_supervisor : 1; // If 0, user-mode accesses not allowed.
|
|
||||||
u64 PageWriteThrough : 1; // Determines the memory type used to access PDPT.
|
|
||||||
u64 page_cache : 1; // Determines the memory type used to access PDPT.
|
|
||||||
u64 accessed : 1; // If 0, this entry has not been used for translation.
|
|
||||||
u64 Ignored1 : 1;
|
|
||||||
u64 page_size : 1; // Must be 0 for PML4E.
|
|
||||||
u64 Ignored2 : 4;
|
|
||||||
u64 pfn : 36; // The page frame number of the PDPT of this PML4E.
|
|
||||||
u64 Reserved : 4;
|
|
||||||
u64 Ignored3 : 11;
|
|
||||||
u64 nx : 1; // If 1, instruction fetches not allowed.
|
|
||||||
};
|
|
||||||
} pml4e, * ppml4e;
|
|
||||||
|
|
||||||
typedef union _pdpte
|
|
||||||
{
|
|
||||||
u64 value;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
u64 present : 1; // Must be 1, region invalid if 0.
|
|
||||||
u64 rw : 1; // If 0, writes not allowed.
|
|
||||||
u64 user_supervisor : 1; // If 0, user-mode accesses not allowed.
|
|
||||||
u64 PageWriteThrough : 1; // Determines the memory type used to access PD.
|
|
||||||
u64 page_cache : 1; // Determines the memory type used to access PD.
|
|
||||||
u64 accessed : 1; // If 0, this entry has not been used for translation.
|
|
||||||
u64 Ignored1 : 1;
|
|
||||||
u64 page_size : 1; // If 1, this entry maps a 1GB page.
|
|
||||||
u64 Ignored2 : 4;
|
|
||||||
u64 pfn : 36; // The page frame number of the PD of this PDPTE.
|
|
||||||
u64 Reserved : 4;
|
|
||||||
u64 Ignored3 : 11;
|
|
||||||
u64 nx : 1; // If 1, instruction fetches not allowed.
|
|
||||||
};
|
|
||||||
} pdpte, * ppdpte;
|
|
||||||
|
|
||||||
typedef union _pde
|
|
||||||
{
|
|
||||||
u64 value;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
u64 present : 1; // Must be 1, region invalid if 0.
|
|
||||||
u64 rw : 1; // If 0, writes not allowed.
|
|
||||||
u64 user_supervisor : 1; // If 0, user-mode accesses not allowed.
|
|
||||||
u64 PageWriteThrough : 1; // Determines the memory type used to access PT.
|
|
||||||
u64 page_cache : 1; // Determines the memory type used to access PT.
|
|
||||||
u64 accessed : 1; // If 0, this entry has not been used for translation.
|
|
||||||
u64 Ignored1 : 1;
|
|
||||||
u64 page_size : 1; // If 1, this entry maps a 2MB page.
|
|
||||||
u64 Ignored2 : 4;
|
|
||||||
u64 pfn : 36; // The page frame number of the PT of this PDE.
|
|
||||||
u64 Reserved : 4;
|
|
||||||
u64 Ignored3 : 11;
|
|
||||||
u64 nx : 1; // If 1, instruction fetches not allowed.
|
|
||||||
};
|
|
||||||
} pde, * ppde;
|
|
||||||
|
|
||||||
typedef union _pte
|
|
||||||
{
|
|
||||||
u64 value;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
u64 present : 1; // Must be 1, region invalid if 0.
|
|
||||||
u64 rw : 1; // If 0, writes not allowed.
|
|
||||||
u64 user_supervisor : 1; // If 0, user-mode accesses not allowed.
|
|
||||||
u64 PageWriteThrough : 1; // Determines the memory type used to access the memory.
|
|
||||||
u64 page_cache : 1; // Determines the memory type used to access the memory.
|
|
||||||
u64 accessed : 1; // If 0, this entry has not been used for translation.
|
|
||||||
u64 Dirty : 1; // If 0, the memory backing this page has not been written to.
|
|
||||||
u64 PageAccessType : 1; // Determines the memory type used to access the memory.
|
|
||||||
u64 Global : 1; // If 1 and the PGE bit of CR4 is set, translations are global.
|
|
||||||
u64 Ignored2 : 3;
|
|
||||||
u64 pfn : 36; // The page frame number of the backing physical page.
|
|
||||||
u64 reserved : 4;
|
|
||||||
u64 Ignored3 : 7;
|
|
||||||
u64 ProtectionKey : 4; // If the PKE bit of CR4 is set, determines the protection key.
|
|
||||||
u64 nx : 1; // If 1, instruction fetches not allowed.
|
|
||||||
};
|
|
||||||
} pte, * ppte;
|
|
||||||
|
|
||||||
typedef struct _table_entries
|
|
||||||
{
|
|
||||||
pg_table::pml4e pml4e;
|
|
||||||
pg_table::pdpte pdpte;
|
|
||||||
pg_table::pde pde;
|
|
||||||
pg_table::pte pte;
|
|
||||||
} table_entries, *ptable_entries;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// payload paging tables...
|
|
||||||
/// </summary>
|
|
||||||
__declspec(allocate(".pdpt")) inline pdpte pdpt[512];
|
|
||||||
__declspec(allocate(".pd")) inline pde pd[512];
|
|
||||||
__declspec(allocate(".pt")) inline pte pt[512];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// self referencing pml4e is at 255...
|
|
||||||
/// </summary>
|
|
||||||
inline const ppml4e hyperv_pml4{ reinterpret_cast<ppml4e>(0x00007FBFDFEFF000) };
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// only does address translation for hyper-v's context
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="virtual_address">virtual address to be translated...</param>
|
|
||||||
/// <param name="entries">optional </param>
|
|
||||||
/// <returns>returns a physical address...</returns>
|
|
||||||
void* translate(void* virt_addr, const ptable_entries entries = nullptr);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// translates linear virtual addresses to linear physical addresses...
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="virtual_address">virtual address to translate...</param>
|
|
||||||
/// <param name="pml4_pfn">page map level four page frame number...</param>
|
|
||||||
/// <param name="entries">(optional) pointer to a table_entries structure...</param>
|
|
||||||
/// <returns>linear physical address...</returns>
|
|
||||||
void* translate(void* virtual_address, u32 pml4_pfn, const ptable_entries entries = nullptr);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
// changes the cursor address to the specified physical address...
|
|
||||||
// after doing so, the TLB entry for that address is going to be flushed...
|
|
||||||
// a memory fence is applied to prevent out of order execution...
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="phys_pfn">pfn of the physical page to change the cursor too...</param>
|
|
||||||
void set_cursor_page(u32 phys_pfn);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// get the cursor page... each core has its own cursor page...
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>cursor page for the current core...</returns>
|
|
||||||
void* get_cursor_page();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// initalizes paging tables (connects pdpt->pd->pt)
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>was the setup successful?</returns>
|
|
||||||
bool init_pg_tables();
|
|
||||||
}
|
|
@ -0,0 +1,27 @@
|
|||||||
|
#include "vmexit.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
namespace vmexit
|
||||||
|
{
|
||||||
|
auto get_command(guest_virt_t command_ptr) -> svm::command_t
|
||||||
|
{
|
||||||
|
const auto vmcb = svm::get_vmcb();
|
||||||
|
const auto guest_dirbase = cr3{ vmcb->cr3 }.pml4_pfn << 12;
|
||||||
|
|
||||||
|
const auto command_page =
|
||||||
|
mm::map_guest_virt(guest_dirbase, command_ptr);
|
||||||
|
|
||||||
|
return *reinterpret_cast<svm::command_t*>(command_page);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto set_command(guest_virt_t command_ptr, svm::command_t& command_data) -> void
|
||||||
|
{
|
||||||
|
const auto vmcb = svm::get_vmcb();
|
||||||
|
const auto guest_dirbase = cr3{ vmcb->cr3 }.pml4_pfn << 12;
|
||||||
|
|
||||||
|
const auto command_page =
|
||||||
|
mm::map_guest_virt(guest_dirbase, command_ptr);
|
||||||
|
|
||||||
|
*reinterpret_cast<svm::command_t*>(command_page) = command_data;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "mm.h"
|
||||||
|
|
||||||
|
namespace vmexit
|
||||||
|
{
|
||||||
|
auto get_command(guest_virt_t command_ptr) -> svm::command_t;
|
||||||
|
auto set_command(guest_virt_t command_ptr, svm::command_t& command_data) -> void;
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
auto dbg::debug_print_decimal(long long number) -> void
|
||||||
|
{
|
||||||
|
if (number < 0)
|
||||||
|
{
|
||||||
|
__outbyte(PORT_NUM, '-');
|
||||||
|
number = -number;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto d = 1000000000000000000; d != 0; d /= 10)
|
||||||
|
if ((number / d) != 0)
|
||||||
|
__outbyte(PORT_NUM, alphabet[(number / d) % 10]);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dbg::debug_print_hex(u64 number, const bool show_zeros) -> void
|
||||||
|
{
|
||||||
|
for (auto d = 0x1000000000000000; d != 0; d /= 0x10)
|
||||||
|
if (show_zeros || (number / d) != 0)
|
||||||
|
__outbyte(PORT_NUM, alphabet[(number / d) % 0x10]);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dbg::print(const char* format, ...) -> void
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
|
||||||
|
while (format[0])
|
||||||
|
{
|
||||||
|
if (format[0] == '%')
|
||||||
|
{
|
||||||
|
switch (format[1])
|
||||||
|
{
|
||||||
|
case 'd':
|
||||||
|
debug_print_decimal(va_arg(args, int));
|
||||||
|
format += 2;
|
||||||
|
continue;
|
||||||
|
case 'x':
|
||||||
|
debug_print_hex(va_arg(args, u32), false);
|
||||||
|
format += 2;
|
||||||
|
continue;
|
||||||
|
case 'l':
|
||||||
|
if (format[2] == 'l')
|
||||||
|
{
|
||||||
|
switch (format[3])
|
||||||
|
{
|
||||||
|
case 'd':
|
||||||
|
debug_print_decimal(va_arg(args, u64));
|
||||||
|
format += 4;
|
||||||
|
continue;
|
||||||
|
case 'x':
|
||||||
|
debug_print_hex(va_arg(args, u64), false);
|
||||||
|
format += 4;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
debug_print_hex(va_arg(args, u64), true);
|
||||||
|
format += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__outbyte(PORT_NUM, format[0]);
|
||||||
|
++format;
|
||||||
|
}
|
||||||
|
va_end(args);
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#define PORT_NUM 0x2F8
|
||||||
|
namespace dbg
|
||||||
|
{
|
||||||
|
constexpr char alphabet[] = "0123456789ABCDEF";
|
||||||
|
auto debug_print_decimal(long long number) -> void;
|
||||||
|
auto debug_print_hex(u64 number, const bool show_zeros) -> void;
|
||||||
|
auto print(const char* format, ...) -> void;
|
||||||
|
}
|
@ -0,0 +1,360 @@
|
|||||||
|
#include "mm.h"
|
||||||
|
|
||||||
|
auto mm::map_guest_phys(guest_phys_t phys_addr, map_type_t map_type) -> u64
|
||||||
|
{
|
||||||
|
const auto host_phys =
|
||||||
|
translate_guest_physical(
|
||||||
|
phys_addr, map_type);
|
||||||
|
|
||||||
|
if (!host_phys)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return map_page(host_phys, map_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mm::map_guest_virt(guest_phys_t dirbase, guest_virt_t virt_addr, map_type_t map_type) -> u64
|
||||||
|
{
|
||||||
|
const auto guest_phys =
|
||||||
|
translate_guest_virtual(
|
||||||
|
dirbase, virt_addr, map_type);
|
||||||
|
|
||||||
|
if (!guest_phys)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return map_guest_phys(guest_phys, map_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mm::map_page(host_phys_t phys_addr, map_type_t map_type) -> u64
|
||||||
|
{
|
||||||
|
cpuid_eax_01 cpuid_value;
|
||||||
|
__cpuid((int*)&cpuid_value, 1);
|
||||||
|
|
||||||
|
mm::pt[(cpuid_value
|
||||||
|
.cpuid_additional_information
|
||||||
|
.initial_apic_id * 2)
|
||||||
|
+ (unsigned)map_type].pfn = phys_addr >> 12;
|
||||||
|
|
||||||
|
__invlpg(reinterpret_cast<void*>(
|
||||||
|
get_map_virt(virt_addr_t{ phys_addr }.offset_4kb, map_type)));
|
||||||
|
|
||||||
|
return get_map_virt(virt_addr_t{ phys_addr }.offset_4kb, map_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mm::get_map_virt(u16 offset, map_type_t map_type) -> u64
|
||||||
|
{
|
||||||
|
cpuid_eax_01 cpuid_value;
|
||||||
|
__cpuid((int*)&cpuid_value, 1);
|
||||||
|
virt_addr_t virt_addr{ MAPPING_ADDRESS_BASE };
|
||||||
|
|
||||||
|
virt_addr.pt_index = (cpuid_value
|
||||||
|
.cpuid_additional_information
|
||||||
|
.initial_apic_id * 2)
|
||||||
|
+ (unsigned)map_type;
|
||||||
|
|
||||||
|
return virt_addr.value + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mm::translate(host_virt_t host_virt) -> u64
|
||||||
|
{
|
||||||
|
virt_addr_t virt_addr{ host_virt };
|
||||||
|
virt_addr_t cursor{ (u64)hyperv_pml4 };
|
||||||
|
|
||||||
|
if (!reinterpret_cast<ppml4e>(cursor.value)[virt_addr.pml4_index].present)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
cursor.pt_index = virt_addr.pml4_index;
|
||||||
|
if (!reinterpret_cast<ppdpte>(cursor.value)[virt_addr.pdpt_index].present)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// handle 1gb large page...
|
||||||
|
if (reinterpret_cast<ppdpte>(cursor.value)[virt_addr.pdpt_index].large_page)
|
||||||
|
return (reinterpret_cast<ppdpte>(cursor.value)
|
||||||
|
[virt_addr.pdpt_index].pfn << 12) + virt_addr.offset_1gb;
|
||||||
|
|
||||||
|
cursor.pd_index = virt_addr.pml4_index;
|
||||||
|
cursor.pt_index = virt_addr.pdpt_index;
|
||||||
|
if (!reinterpret_cast<ppde>(cursor.value)[virt_addr.pd_index].present)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// handle 2mb large page...
|
||||||
|
if (reinterpret_cast<ppde>(cursor.value)[virt_addr.pd_index].large_page)
|
||||||
|
return (reinterpret_cast<ppde>(cursor.value)
|
||||||
|
[virt_addr.pd_index].pfn << 12) + virt_addr.offset_2mb;
|
||||||
|
|
||||||
|
cursor.pdpt_index = virt_addr.pml4_index;
|
||||||
|
cursor.pd_index = virt_addr.pdpt_index;
|
||||||
|
cursor.pt_index = virt_addr.pd_index;
|
||||||
|
if (!reinterpret_cast<ppte>(cursor.value)[virt_addr.pt_index].present)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return (reinterpret_cast<ppte>(cursor.value)
|
||||||
|
[virt_addr.pt_index].pfn << 12) + virt_addr.offset_4kb;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mm::translate_guest_virtual(guest_phys_t dirbase, guest_virt_t guest_virt, map_type_t map_type) -> u64
|
||||||
|
{
|
||||||
|
virt_addr_t virt_addr{ guest_virt };
|
||||||
|
const auto pml4 =
|
||||||
|
reinterpret_cast<pml4e*>(map_guest_phys(dirbase, map_type));
|
||||||
|
|
||||||
|
if (!pml4[virt_addr.pml4_index].present)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const auto pdpt =
|
||||||
|
reinterpret_cast<pdpte*>(map_guest_phys(
|
||||||
|
pml4[virt_addr.pml4_index].pfn << 12, map_type));
|
||||||
|
|
||||||
|
if (!pdpt[virt_addr.pdpt_index].present)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// handle 1gb pages...
|
||||||
|
if (pdpt[virt_addr.pdpt_index].large_page)
|
||||||
|
return (pdpt[virt_addr.pdpt_index].pfn << 12) + virt_addr.offset_1gb;
|
||||||
|
|
||||||
|
const auto pd =
|
||||||
|
reinterpret_cast<pde*>(map_guest_phys(
|
||||||
|
pdpt[virt_addr.pdpt_index].pfn << 12, map_type));
|
||||||
|
|
||||||
|
if (!pd[virt_addr.pd_index].present)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// handle 2mb pages...
|
||||||
|
if (pd[virt_addr.pd_index].large_page)
|
||||||
|
return (pd[virt_addr.pd_index].pfn << 12) + virt_addr.offset_2mb;
|
||||||
|
|
||||||
|
const auto pt =
|
||||||
|
reinterpret_cast<pte*>(map_guest_phys(
|
||||||
|
pd[virt_addr.pd_index].pfn << 12, map_type));
|
||||||
|
|
||||||
|
if (!pt[virt_addr.pt_index].present)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return (pt[virt_addr.pt_index].pfn << 12) + virt_addr.offset_4kb;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mm::translate_guest_physical(guest_phys_t phys_addr, map_type_t map_type) -> u64
|
||||||
|
{
|
||||||
|
ept_pointer eptp;
|
||||||
|
phys_addr_t guest_phys{ phys_addr };
|
||||||
|
__vmx_vmread(VMCS_CTRL_EPT_POINTER, (size_t*)&eptp);
|
||||||
|
|
||||||
|
const auto epml4 = reinterpret_cast<ept_pml4e*>(
|
||||||
|
map_page(eptp.page_frame_number << 12, map_type));
|
||||||
|
|
||||||
|
const auto epdpt_large =
|
||||||
|
reinterpret_cast<ept_pdpte_1gb*>(map_page(
|
||||||
|
epml4[guest_phys.pml4_index].page_frame_number << 12, map_type));
|
||||||
|
|
||||||
|
// handle 1gb page...
|
||||||
|
if (epdpt_large[guest_phys.pdpt_index].large_page)
|
||||||
|
return (epdpt_large[guest_phys.pdpt_index].page_frame_number
|
||||||
|
* 0x1000 * 0x200 * 0x200) + EPT_LARGE_PDPTE_OFFSET(phys_addr);
|
||||||
|
|
||||||
|
const auto epdpt =
|
||||||
|
reinterpret_cast<ept_pdpte*>(epdpt_large);
|
||||||
|
|
||||||
|
const auto epd_large =
|
||||||
|
reinterpret_cast<epde_2mb*>(map_page(
|
||||||
|
epdpt[guest_phys.pdpt_index].page_frame_number << 12, map_type));
|
||||||
|
|
||||||
|
// handle 2mb page...
|
||||||
|
if (epd_large[guest_phys.pd_index].large_page)
|
||||||
|
return (epd_large[guest_phys.pd_index].page_frame_number
|
||||||
|
* 0x1000 * 0x200) + EPT_LARGE_PDE_OFFSET(phys_addr);
|
||||||
|
|
||||||
|
const auto epd =
|
||||||
|
reinterpret_cast<ept_pde*>(epd_large);
|
||||||
|
|
||||||
|
const auto ept =
|
||||||
|
reinterpret_cast<ept_pte*>(map_page(
|
||||||
|
epd[guest_phys.pd_index].page_frame_number << 12, map_type));
|
||||||
|
|
||||||
|
auto result = ept[guest_phys.pt_index].page_frame_number << 12;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mm::init() -> vmxroot_error_t
|
||||||
|
{
|
||||||
|
const auto pdpt_phys =
|
||||||
|
translate(reinterpret_cast<u64>(pdpt));
|
||||||
|
|
||||||
|
const auto pd_phys =
|
||||||
|
translate(reinterpret_cast<u64>(pd));
|
||||||
|
|
||||||
|
const auto pt_phys =
|
||||||
|
translate(reinterpret_cast<u64>(pt));
|
||||||
|
|
||||||
|
if (!pdpt_phys || !pd_phys || !pt_phys)
|
||||||
|
return vmxroot_error_t::invalid_host_virtual;
|
||||||
|
|
||||||
|
// setup mapping page table entries...
|
||||||
|
{
|
||||||
|
hyperv_pml4[MAPPING_PML4_IDX].present = true;
|
||||||
|
hyperv_pml4[MAPPING_PML4_IDX].pfn = pdpt_phys >> 12;
|
||||||
|
hyperv_pml4[MAPPING_PML4_IDX].user_supervisor = false;
|
||||||
|
hyperv_pml4[MAPPING_PML4_IDX].writeable = true;
|
||||||
|
|
||||||
|
pdpt[511].present = true;
|
||||||
|
pdpt[511].pfn = pd_phys >> 12;
|
||||||
|
pdpt[511].user_supervisor = false;
|
||||||
|
pdpt[511].rw = true;
|
||||||
|
|
||||||
|
pd[511].present = true;
|
||||||
|
pd[511].pfn = pt_phys >> 12;
|
||||||
|
pd[511].user_supervisor = false;
|
||||||
|
pd[511].rw = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// each core will have its own page it can use to map
|
||||||
|
// physical memory into virtual memory :^)
|
||||||
|
for (auto idx = 0u; idx < 512; ++idx)
|
||||||
|
{
|
||||||
|
pt[idx].present = true;
|
||||||
|
pt[idx].user_supervisor = false;
|
||||||
|
pt[idx].rw = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto mapped_pml4 =
|
||||||
|
reinterpret_cast<ppml4e>(
|
||||||
|
mm::map_page(__readcr3()));
|
||||||
|
|
||||||
|
// check to make sure translate works...
|
||||||
|
if (translate((u64)mapped_pml4) != __readcr3())
|
||||||
|
return vmxroot_error_t::vmxroot_translate_failure;
|
||||||
|
|
||||||
|
// check to make sure the self ref pml4e is valid...
|
||||||
|
if (mapped_pml4[SELF_REF_PML4_IDX].pfn != __readcr3() >> 12)
|
||||||
|
return vmxroot_error_t::invalid_self_ref_pml4e;
|
||||||
|
|
||||||
|
// check to make sure the mapping pml4e is valid...
|
||||||
|
if (mapped_pml4[MAPPING_PML4_IDX].pfn != pdpt_phys >> 12)
|
||||||
|
return vmxroot_error_t::invalid_mapping_pml4e;
|
||||||
|
|
||||||
|
return vmxroot_error_t::error_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mm::read_guest_phys(guest_phys_t dirbase, guest_phys_t guest_phys,
|
||||||
|
guest_virt_t guest_virt, u64 size) -> vmxroot_error_t
|
||||||
|
{
|
||||||
|
// handle reading over page boundaries of both src and dest...
|
||||||
|
while (size)
|
||||||
|
{
|
||||||
|
auto dest_current_size = PAGE_4KB -
|
||||||
|
virt_addr_t{ guest_virt }.offset_4kb;
|
||||||
|
|
||||||
|
if (size < dest_current_size)
|
||||||
|
dest_current_size = size;
|
||||||
|
|
||||||
|
auto src_current_size = PAGE_4KB -
|
||||||
|
phys_addr_t{ guest_phys }.offset_4kb;
|
||||||
|
|
||||||
|
if (size < src_current_size)
|
||||||
|
src_current_size = size;
|
||||||
|
|
||||||
|
auto current_size =
|
||||||
|
min(dest_current_size, src_current_size);
|
||||||
|
|
||||||
|
const auto mapped_dest =
|
||||||
|
reinterpret_cast<void*>(
|
||||||
|
map_guest_virt(dirbase, guest_virt, map_type_t::map_dest));
|
||||||
|
|
||||||
|
if (!mapped_dest)
|
||||||
|
return vmxroot_error_t::invalid_guest_virtual;
|
||||||
|
|
||||||
|
const auto mapped_src =
|
||||||
|
reinterpret_cast<void*>(
|
||||||
|
map_guest_phys(guest_phys, map_type_t::map_src));
|
||||||
|
|
||||||
|
if (!mapped_src)
|
||||||
|
return vmxroot_error_t::invalid_guest_physical;
|
||||||
|
|
||||||
|
memcpy(mapped_dest, mapped_src, current_size);
|
||||||
|
guest_phys += current_size;
|
||||||
|
guest_virt += current_size;
|
||||||
|
size -= current_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vmxroot_error_t::error_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mm::write_guest_phys(guest_phys_t dirbase,
|
||||||
|
guest_phys_t guest_phys, guest_virt_t guest_virt, u64 size) -> vmxroot_error_t
|
||||||
|
{
|
||||||
|
// handle reading over page boundaries of both src and dest...
|
||||||
|
while (size)
|
||||||
|
{
|
||||||
|
auto dest_current_size = PAGE_4KB -
|
||||||
|
virt_addr_t{ guest_virt }.offset_4kb;
|
||||||
|
|
||||||
|
if (size < dest_current_size)
|
||||||
|
dest_current_size = size;
|
||||||
|
|
||||||
|
auto src_current_size = PAGE_4KB -
|
||||||
|
phys_addr_t{ guest_phys }.offset_4kb;
|
||||||
|
|
||||||
|
if (size < src_current_size)
|
||||||
|
src_current_size = size;
|
||||||
|
|
||||||
|
auto current_size =
|
||||||
|
min(dest_current_size, src_current_size);
|
||||||
|
|
||||||
|
const auto mapped_src =
|
||||||
|
reinterpret_cast<void*>(
|
||||||
|
map_guest_virt(dirbase, guest_virt, map_type_t::map_src));
|
||||||
|
|
||||||
|
if (!mapped_src)
|
||||||
|
return vmxroot_error_t::invalid_guest_virtual;
|
||||||
|
|
||||||
|
const auto mapped_dest =
|
||||||
|
reinterpret_cast<void*>(
|
||||||
|
map_guest_phys(guest_phys, map_type_t::map_dest));
|
||||||
|
|
||||||
|
if (!mapped_src)
|
||||||
|
return vmxroot_error_t::invalid_guest_physical;
|
||||||
|
|
||||||
|
memcpy(mapped_dest, mapped_src, current_size);
|
||||||
|
guest_phys += current_size;
|
||||||
|
guest_virt += current_size;
|
||||||
|
size -= current_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vmxroot_error_t::error_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mm::copy_guest_virt(guest_phys_t dirbase_src, guest_virt_t virt_src,
|
||||||
|
guest_virt_t dirbase_dest, guest_virt_t virt_dest, u64 size) -> vmxroot_error_t
|
||||||
|
{
|
||||||
|
while (size)
|
||||||
|
{
|
||||||
|
auto dest_size = PAGE_4KB - virt_addr_t{ virt_dest }.offset_4kb;
|
||||||
|
if (size < dest_size)
|
||||||
|
dest_size = size;
|
||||||
|
|
||||||
|
auto src_size = PAGE_4KB - virt_addr_t{ virt_src }.offset_4kb;
|
||||||
|
if (size < src_size)
|
||||||
|
src_size = size;
|
||||||
|
|
||||||
|
const auto mapped_src =
|
||||||
|
reinterpret_cast<void*>(
|
||||||
|
map_guest_virt(dirbase_src, virt_src, map_type_t::map_src));
|
||||||
|
|
||||||
|
if (!mapped_src)
|
||||||
|
return vmxroot_error_t::invalid_guest_virtual;
|
||||||
|
|
||||||
|
const auto mapped_dest =
|
||||||
|
reinterpret_cast<void*>(
|
||||||
|
map_guest_virt(dirbase_dest, virt_dest, map_type_t::map_dest));
|
||||||
|
|
||||||
|
if (!mapped_dest)
|
||||||
|
return vmxroot_error_t::invalid_guest_virtual;
|
||||||
|
|
||||||
|
auto current_size = min(dest_size, src_size);
|
||||||
|
memcpy(mapped_dest, mapped_src, current_size);
|
||||||
|
|
||||||
|
virt_src += current_size;
|
||||||
|
virt_dest += current_size;
|
||||||
|
size -= current_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vmxroot_error_t::error_success;
|
||||||
|
}
|
@ -0,0 +1,165 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "types.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
#define SELF_REF_PML4_IDX 510
|
||||||
|
#define MAPPING_PML4_IDX 100
|
||||||
|
|
||||||
|
#define MAPPING_ADDRESS_BASE 0x0000327FFFE00000
|
||||||
|
#define SELF_REF_PML4 0xFFFFFF7FBFDFE000
|
||||||
|
|
||||||
|
#define EPT_LARGE_PDPTE_OFFSET(_) (((u64)(_)) & ((0x1000 * 0x200 * 0x200) - 1))
|
||||||
|
#define EPT_LARGE_PDE_OFFSET(_) (((u64)(_)) & ((0x1000 * 0x200) - 1))
|
||||||
|
|
||||||
|
#pragma section(".pdpt", read, write)
|
||||||
|
#pragma section(".pd", read, write)
|
||||||
|
#pragma section(".pt", read, write)
|
||||||
|
|
||||||
|
namespace mm
|
||||||
|
{
|
||||||
|
enum class map_type_t
|
||||||
|
{
|
||||||
|
map_src,
|
||||||
|
map_dest
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef union _virt_addr_t
|
||||||
|
{
|
||||||
|
u64 value;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u64 offset_4kb : 12;
|
||||||
|
u64 pt_index : 9;
|
||||||
|
u64 pd_index : 9;
|
||||||
|
u64 pdpt_index : 9;
|
||||||
|
u64 pml4_index : 9;
|
||||||
|
u64 reserved : 16;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u64 offset_2mb : 21;
|
||||||
|
u64 pd_index : 9;
|
||||||
|
u64 pdpt_index : 9;
|
||||||
|
u64 pml4_index : 9;
|
||||||
|
u64 reserved : 16;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u64 offset_1gb : 30;
|
||||||
|
u64 pdpt_index : 9;
|
||||||
|
u64 pml4_index : 9;
|
||||||
|
u64 reserved : 16;
|
||||||
|
};
|
||||||
|
|
||||||
|
} virt_addr_t, * pvirt_addr_t;
|
||||||
|
using phys_addr_t = virt_addr_t;
|
||||||
|
|
||||||
|
typedef union _pml4e
|
||||||
|
{
|
||||||
|
u64 value;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u64 present : 1;
|
||||||
|
u64 writeable : 1;
|
||||||
|
u64 user_supervisor : 1;
|
||||||
|
u64 page_write_through : 1;
|
||||||
|
u64 page_cache : 1;
|
||||||
|
u64 accessed : 1;
|
||||||
|
u64 ignore_1 : 1;
|
||||||
|
u64 page_size : 1;
|
||||||
|
u64 ignore_2 : 4;
|
||||||
|
u64 pfn : 36;
|
||||||
|
u64 reserved : 4;
|
||||||
|
u64 ignore_3 : 11;
|
||||||
|
u64 nx : 1;
|
||||||
|
};
|
||||||
|
} pml4e, * ppml4e;
|
||||||
|
|
||||||
|
typedef union _pdpte
|
||||||
|
{
|
||||||
|
u64 value;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u64 present : 1;
|
||||||
|
u64 rw : 1;
|
||||||
|
u64 user_supervisor : 1;
|
||||||
|
u64 page_write_through : 1;
|
||||||
|
u64 page_cache : 1;
|
||||||
|
u64 accessed : 1;
|
||||||
|
u64 ignore_1 : 1;
|
||||||
|
u64 large_page : 1;
|
||||||
|
u64 ignore_2 : 4;
|
||||||
|
u64 pfn : 36;
|
||||||
|
u64 reserved : 4;
|
||||||
|
u64 ignore_3 : 11;
|
||||||
|
u64 nx : 1;
|
||||||
|
};
|
||||||
|
} pdpte, * ppdpte;
|
||||||
|
|
||||||
|
typedef union _pde
|
||||||
|
{
|
||||||
|
u64 value;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u64 present : 1;
|
||||||
|
u64 rw : 1;
|
||||||
|
u64 user_supervisor : 1;
|
||||||
|
u64 page_write_through : 1;
|
||||||
|
u64 page_cache : 1;
|
||||||
|
u64 accessed : 1;
|
||||||
|
u64 ignore_1 : 1;
|
||||||
|
u64 large_page : 1;
|
||||||
|
u64 ignore_2 : 4;
|
||||||
|
u64 pfn : 36;
|
||||||
|
u64 reserved : 4;
|
||||||
|
u64 ignore_3 : 11;
|
||||||
|
u64 nx : 1;
|
||||||
|
};
|
||||||
|
} pde, * ppde;
|
||||||
|
|
||||||
|
typedef union _pte
|
||||||
|
{
|
||||||
|
u64 value;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u64 present : 1;
|
||||||
|
u64 rw : 1;
|
||||||
|
u64 user_supervisor : 1;
|
||||||
|
u64 page_write_through : 1;
|
||||||
|
u64 page_cache : 1;
|
||||||
|
u64 accessed : 1;
|
||||||
|
u64 dirty : 1;
|
||||||
|
u64 access_type : 1;
|
||||||
|
u64 global : 1;
|
||||||
|
u64 ignore_2 : 3;
|
||||||
|
u64 pfn : 36;
|
||||||
|
u64 reserved : 4;
|
||||||
|
u64 ignore_3 : 7;
|
||||||
|
u64 pk : 4;
|
||||||
|
u64 nx : 1;
|
||||||
|
};
|
||||||
|
} pte, * ppte;
|
||||||
|
|
||||||
|
__declspec(allocate(".pdpt")) inline pdpte pdpt[512];
|
||||||
|
__declspec(allocate(".pd")) inline pde pd[512];
|
||||||
|
__declspec(allocate(".pt")) inline pte pt[512];
|
||||||
|
|
||||||
|
inline const ppml4e hyperv_pml4{ reinterpret_cast<ppml4e>(SELF_REF_PML4) };
|
||||||
|
|
||||||
|
auto init() -> vmxroot_error_t;
|
||||||
|
auto map_guest_phys(guest_phys_t phys_addr, map_type_t map_type = map_type_t::map_src) -> u64;
|
||||||
|
auto map_guest_virt(guest_phys_t dirbase, guest_virt_t virt_addr, map_type_t map_type = map_type_t::map_src) -> u64;
|
||||||
|
|
||||||
|
auto map_page(host_phys_t phys_addr, map_type_t map_type = map_type_t::map_src) -> u64;
|
||||||
|
auto get_map_virt(u16 offset = 0u, map_type_t map_type = map_type_t::map_src) -> u64;
|
||||||
|
|
||||||
|
auto translate(host_virt_t host_virt) -> u64;
|
||||||
|
auto translate_guest_physical(guest_phys_t guest_phys, map_type_t map_type = map_type_t::map_src) -> u64;
|
||||||
|
auto translate_guest_virtual(guest_phys_t dirbase, guest_virt_t guest_virt, map_type_t map_type = map_type_t::map_src) -> u64;
|
||||||
|
|
||||||
|
auto read_guest_phys(guest_phys_t dirbase, guest_phys_t guest_phys, guest_virt_t guest_virt, u64 size) -> vmxroot_error_t;
|
||||||
|
auto write_guest_phys(guest_phys_t dirbase, guest_phys_t guest_phys, guest_virt_t guest_virt, u64 size) -> vmxroot_error_t;
|
||||||
|
auto copy_guest_virt(guest_phys_t dirbase_src, guest_virt_t virt_src, guest_virt_t dirbase_dest, guest_virt_t virt_dest, u64 size) -> vmxroot_error_t;
|
||||||
|
}
|
@ -1,138 +0,0 @@
|
|||||||
#include "pg_table.h"
|
|
||||||
|
|
||||||
namespace pg_table
|
|
||||||
{
|
|
||||||
void* translate(void* virtual_address, const ptable_entries entries)
|
|
||||||
{
|
|
||||||
virt_addr_t virt_addr{ virtual_address };
|
|
||||||
virt_addr_t cursor{ hyperv_pml4 };
|
|
||||||
|
|
||||||
if (entries) entries->pml4e = reinterpret_cast<ppml4e>(cursor.value)[virt_addr.pml4_index];
|
|
||||||
if (!reinterpret_cast<ppml4e>(cursor.value)[virt_addr.pml4_index].present)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
// set the cursor to self reference so that when we read
|
|
||||||
// the addresses pointed to by cursor its going to be a pdpt...
|
|
||||||
cursor.pdpt_index = virt_addr_t{ hyperv_pml4 }.pml4_index;
|
|
||||||
cursor.pd_index = virt_addr_t{ hyperv_pml4 }.pml4_index;
|
|
||||||
cursor.pt_index = virt_addr.pml4_index;
|
|
||||||
if (entries) entries->pdpte = reinterpret_cast<ppdpte>(cursor.value)[virt_addr.pdpt_index];
|
|
||||||
|
|
||||||
if (!reinterpret_cast<ppdpte>(cursor.value)[virt_addr.pdpt_index].present)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
// set the cursor to self reference so that when we read
|
|
||||||
// the addresses pointed to by cursor its going to be a pd...
|
|
||||||
cursor.pdpt_index = virt_addr_t{ hyperv_pml4 }.pml4_index;
|
|
||||||
cursor.pd_index = virt_addr.pml4_index;
|
|
||||||
cursor.pt_index = virt_addr.pdpt_index;
|
|
||||||
if (entries) entries->pde = reinterpret_cast<ppde>(cursor.value)[virt_addr.pd_index];
|
|
||||||
|
|
||||||
if (!reinterpret_cast<ppde>(cursor.value)[virt_addr.pd_index].present)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
// set the cursor to self reference so that when we read
|
|
||||||
// the addresses pointed to by cursor its going to be a pt...
|
|
||||||
cursor.pdpt_index = virt_addr.pml4_index;
|
|
||||||
cursor.pd_index = virt_addr.pdpt_index;
|
|
||||||
cursor.pt_index = virt_addr.pd_index;
|
|
||||||
if (entries) entries->pte = reinterpret_cast<ppte>(cursor.value)[virt_addr.pt_index];
|
|
||||||
|
|
||||||
if (!reinterpret_cast<ppte>(cursor.value)[virt_addr.pt_index].present)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
return reinterpret_cast<void*>(
|
|
||||||
reinterpret_cast<ppte>(cursor.value)[virt_addr.pt_index].pfn << 12);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* translate(void* virtual_address, u32 pml4_pfn, const ptable_entries entries)
|
|
||||||
{
|
|
||||||
virt_addr_t virt_addr{ virtual_address };
|
|
||||||
const auto cursor = get_cursor_page();
|
|
||||||
|
|
||||||
set_cursor_page(pml4_pfn);
|
|
||||||
if (!reinterpret_cast<ppml4e>(cursor)[virt_addr.pml4_index].present)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (entries) entries->pml4e = reinterpret_cast<ppml4e>(cursor)[virt_addr.pml4_index];
|
|
||||||
set_cursor_page(reinterpret_cast<ppml4e>(cursor)[virt_addr.pml4_index].pfn);
|
|
||||||
if (!reinterpret_cast<ppdpte>(cursor)[virt_addr.pdpt_index].present)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (entries) entries->pdpte = reinterpret_cast<ppdpte>(cursor)[virt_addr.pdpt_index];
|
|
||||||
set_cursor_page(reinterpret_cast<ppdpte>(cursor)[virt_addr.pdpt_index].pfn);
|
|
||||||
if (!reinterpret_cast<ppde>(cursor)[virt_addr.pd_index].present)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (entries) entries->pde = reinterpret_cast<ppde>(cursor)[virt_addr.pd_index];
|
|
||||||
set_cursor_page(reinterpret_cast<ppde>(cursor)[virt_addr.pd_index].pfn);
|
|
||||||
if (!reinterpret_cast<ppte>(cursor)[virt_addr.pt_index].present)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (entries) entries->pte = reinterpret_cast<ppte>(cursor)[virt_addr.pt_index];
|
|
||||||
return reinterpret_cast<void*>(
|
|
||||||
reinterpret_cast<ppte>(cursor)[virt_addr.pt_index].pfn << 12);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_cursor_page(u32 phys_pfn)
|
|
||||||
{
|
|
||||||
cpuid_eax_01 cpuid_value;
|
|
||||||
__cpuid((int*)&cpuid_value, 1);
|
|
||||||
pg_table::pt[cpuid_value
|
|
||||||
.cpuid_additional_information
|
|
||||||
.initial_apic_id].pfn = phys_pfn;
|
|
||||||
|
|
||||||
// flush tlb for this page and then ensure the instruction stream
|
|
||||||
// is seralized as to not execute instructions out of order and access the page
|
|
||||||
// before the TLB is flushed...
|
|
||||||
__invlpg(get_cursor_page());
|
|
||||||
_mm_lfence();
|
|
||||||
}
|
|
||||||
|
|
||||||
void* get_cursor_page()
|
|
||||||
{
|
|
||||||
cpuid_eax_01 cpuid_value;
|
|
||||||
__cpuid((int*)&cpuid_value, 1);
|
|
||||||
constexpr auto cursor_page = 0x00007F7FFFE00000;
|
|
||||||
|
|
||||||
virt_addr_t virt_addr{ reinterpret_cast<void*>(cursor_page) };
|
|
||||||
virt_addr.pt_index = cpuid_value
|
|
||||||
.cpuid_additional_information
|
|
||||||
.initial_apic_id;
|
|
||||||
|
|
||||||
return virt_addr.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool init_pg_tables()
|
|
||||||
{
|
|
||||||
auto pdpt_phys = reinterpret_cast<u64>(translate(pdpt));
|
|
||||||
auto pd_phys = reinterpret_cast<u64>(translate(pd));
|
|
||||||
auto pt_phys = reinterpret_cast<u64>(translate(pt));
|
|
||||||
|
|
||||||
if (!pdpt_phys || !pd_phys || !pt_phys)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
hyperv_pml4[254].present = true;
|
|
||||||
hyperv_pml4[254].pfn = pdpt_phys >> 12;
|
|
||||||
hyperv_pml4[254].user_supervisor = false;
|
|
||||||
hyperv_pml4[254].rw = true;
|
|
||||||
|
|
||||||
pdpt[511].present = true;
|
|
||||||
pdpt[511].pfn = pd_phys >> 12;
|
|
||||||
pdpt[511].user_supervisor = false;
|
|
||||||
pdpt[511].rw = true;
|
|
||||||
|
|
||||||
pd[511].present = true;
|
|
||||||
pd[511].pfn = pt_phys >> 12;
|
|
||||||
pd[511].user_supervisor = false;
|
|
||||||
pd[511].rw = true;
|
|
||||||
|
|
||||||
for (auto idx = 0u; idx < 512; ++idx)
|
|
||||||
{
|
|
||||||
pt[idx].present = true;
|
|
||||||
pt[idx].user_supervisor = false;
|
|
||||||
pt[idx].rw = true;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,167 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "types.h"
|
|
||||||
#include "ia32.hpp"
|
|
||||||
|
|
||||||
#pragma section(".pdpt", read, write)
|
|
||||||
#pragma section(".pd", read, write)
|
|
||||||
#pragma section(".pt", read, write)
|
|
||||||
|
|
||||||
namespace pg_table
|
|
||||||
{
|
|
||||||
typedef union _virt_addr_t
|
|
||||||
{
|
|
||||||
void* value;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
u64 offset : 12;
|
|
||||||
u64 pt_index : 9;
|
|
||||||
u64 pd_index : 9;
|
|
||||||
u64 pdpt_index : 9;
|
|
||||||
u64 pml4_index : 9;
|
|
||||||
u64 reserved : 16;
|
|
||||||
};
|
|
||||||
} virt_addr_t, * pvirt_addr_t;
|
|
||||||
|
|
||||||
typedef union _pml4e
|
|
||||||
{
|
|
||||||
u64 value;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
u64 present : 1; // Must be 1, region invalid if 0.
|
|
||||||
u64 rw : 1; // If 0, writes not allowed.
|
|
||||||
u64 user_supervisor : 1; // If 0, user-mode accesses not allowed.
|
|
||||||
u64 PageWriteThrough : 1; // Determines the memory type used to access PDPT.
|
|
||||||
u64 page_cache : 1; // Determines the memory type used to access PDPT.
|
|
||||||
u64 accessed : 1; // If 0, this entry has not been used for translation.
|
|
||||||
u64 Ignored1 : 1;
|
|
||||||
u64 page_size : 1; // Must be 0 for PML4E.
|
|
||||||
u64 Ignored2 : 4;
|
|
||||||
u64 pfn : 36; // The page frame number of the PDPT of this PML4E.
|
|
||||||
u64 Reserved : 4;
|
|
||||||
u64 Ignored3 : 11;
|
|
||||||
u64 nx : 1; // If 1, instruction fetches not allowed.
|
|
||||||
};
|
|
||||||
} pml4e, * ppml4e;
|
|
||||||
|
|
||||||
typedef union _pdpte
|
|
||||||
{
|
|
||||||
u64 value;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
u64 present : 1; // Must be 1, region invalid if 0.
|
|
||||||
u64 rw : 1; // If 0, writes not allowed.
|
|
||||||
u64 user_supervisor : 1; // If 0, user-mode accesses not allowed.
|
|
||||||
u64 PageWriteThrough : 1; // Determines the memory type used to access PD.
|
|
||||||
u64 page_cache : 1; // Determines the memory type used to access PD.
|
|
||||||
u64 accessed : 1; // If 0, this entry has not been used for translation.
|
|
||||||
u64 Ignored1 : 1;
|
|
||||||
u64 page_size : 1; // If 1, this entry maps a 1GB page.
|
|
||||||
u64 Ignored2 : 4;
|
|
||||||
u64 pfn : 36; // The page frame number of the PD of this PDPTE.
|
|
||||||
u64 Reserved : 4;
|
|
||||||
u64 Ignored3 : 11;
|
|
||||||
u64 nx : 1; // If 1, instruction fetches not allowed.
|
|
||||||
};
|
|
||||||
} pdpte, * ppdpte;
|
|
||||||
|
|
||||||
typedef union _pde
|
|
||||||
{
|
|
||||||
u64 value;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
u64 present : 1; // Must be 1, region invalid if 0.
|
|
||||||
u64 rw : 1; // If 0, writes not allowed.
|
|
||||||
u64 user_supervisor : 1; // If 0, user-mode accesses not allowed.
|
|
||||||
u64 PageWriteThrough : 1; // Determines the memory type used to access PT.
|
|
||||||
u64 page_cache : 1; // Determines the memory type used to access PT.
|
|
||||||
u64 accessed : 1; // If 0, this entry has not been used for translation.
|
|
||||||
u64 Ignored1 : 1;
|
|
||||||
u64 page_size : 1; // If 1, this entry maps a 2MB page.
|
|
||||||
u64 Ignored2 : 4;
|
|
||||||
u64 pfn : 36; // The page frame number of the PT of this PDE.
|
|
||||||
u64 Reserved : 4;
|
|
||||||
u64 Ignored3 : 11;
|
|
||||||
u64 nx : 1; // If 1, instruction fetches not allowed.
|
|
||||||
};
|
|
||||||
} pde, * ppde;
|
|
||||||
|
|
||||||
typedef union _pte
|
|
||||||
{
|
|
||||||
u64 value;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
u64 present : 1; // Must be 1, region invalid if 0.
|
|
||||||
u64 rw : 1; // If 0, writes not allowed.
|
|
||||||
u64 user_supervisor : 1; // If 0, user-mode accesses not allowed.
|
|
||||||
u64 PageWriteThrough : 1; // Determines the memory type used to access the memory.
|
|
||||||
u64 page_cache : 1; // Determines the memory type used to access the memory.
|
|
||||||
u64 accessed : 1; // If 0, this entry has not been used for translation.
|
|
||||||
u64 Dirty : 1; // If 0, the memory backing this page has not been written to.
|
|
||||||
u64 PageAccessType : 1; // Determines the memory type used to access the memory.
|
|
||||||
u64 Global : 1; // If 1 and the PGE bit of CR4 is set, translations are global.
|
|
||||||
u64 Ignored2 : 3;
|
|
||||||
u64 pfn : 36; // The page frame number of the backing physical page.
|
|
||||||
u64 reserved : 4;
|
|
||||||
u64 Ignored3 : 7;
|
|
||||||
u64 ProtectionKey : 4; // If the PKE bit of CR4 is set, determines the protection key.
|
|
||||||
u64 nx : 1; // If 1, instruction fetches not allowed.
|
|
||||||
};
|
|
||||||
} pte, * ppte;
|
|
||||||
|
|
||||||
typedef struct _table_entries
|
|
||||||
{
|
|
||||||
pg_table::pml4e pml4e;
|
|
||||||
pg_table::pdpte pdpte;
|
|
||||||
pg_table::pde pde;
|
|
||||||
pg_table::pte pte;
|
|
||||||
} table_entries, *ptable_entries;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// payload paging tables...
|
|
||||||
/// </summary>
|
|
||||||
__declspec(allocate(".pdpt")) inline pdpte pdpt[512];
|
|
||||||
__declspec(allocate(".pd")) inline pde pd[512];
|
|
||||||
__declspec(allocate(".pt")) inline pte pt[512];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// self referencing pml4e is at 255...
|
|
||||||
/// </summary>
|
|
||||||
inline const ppml4e hyperv_pml4{ reinterpret_cast<ppml4e>(0x00007FBFDFEFF000) };
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// only does address translation for hyper-v's context
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="virtual_address">virtual address to be translated...</param>
|
|
||||||
/// <param name="entries">optional </param>
|
|
||||||
/// <returns>returns a physical address...</returns>
|
|
||||||
void* translate(void* virt_addr, const ptable_entries entries = nullptr);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// translates linear virtual addresses to linear physical addresses...
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="virtual_address">virtual address to translate...</param>
|
|
||||||
/// <param name="pml4_pfn">page map level four page frame number...</param>
|
|
||||||
/// <param name="entries">(optional) pointer to a table_entries structure...</param>
|
|
||||||
/// <returns>linear physical address...</returns>
|
|
||||||
void* translate(void* virtual_address, u32 pml4_pfn, const ptable_entries entries = nullptr);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
// changes the cursor address to the specified physical address...
|
|
||||||
// after doing so, the TLB entry for that address is going to be flushed...
|
|
||||||
// a memory fence is applied to prevent out of order execution...
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="phys_pfn">pfn of the physical page to change the cursor too...</param>
|
|
||||||
void set_cursor_page(u32 phys_pfn);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// get the cursor page... each core has its own cursor page...
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>cursor page for the current core...</returns>
|
|
||||||
void* get_cursor_page();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// initalizes paging tables (connects pdpt->pd->pt)
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>was the setup successful?</returns>
|
|
||||||
bool init_pg_tables();
|
|
||||||
}
|
|
@ -0,0 +1,34 @@
|
|||||||
|
#include "vmexit.h"
|
||||||
|
|
||||||
|
namespace vmexit
|
||||||
|
{
|
||||||
|
auto get_command(guest_virt_t command_ptr) -> command_t
|
||||||
|
{
|
||||||
|
u64 guest_dirbase;
|
||||||
|
__vmx_vmread(VMCS_GUEST_CR3, &guest_dirbase);
|
||||||
|
|
||||||
|
// cr3 can contain other high bits so just to be safe
|
||||||
|
// get the pfn and bitshift it...
|
||||||
|
guest_dirbase = cr3{ guest_dirbase }.pml4_pfn << 12;
|
||||||
|
|
||||||
|
const auto command_page =
|
||||||
|
mm::map_guest_virt(guest_dirbase, command_ptr);
|
||||||
|
|
||||||
|
return *reinterpret_cast<command_t*>(command_page);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto set_command(guest_virt_t command_ptr, command_t& command_data) -> void
|
||||||
|
{
|
||||||
|
u64 guest_dirbase;
|
||||||
|
__vmx_vmread(VMCS_GUEST_CR3, &guest_dirbase);
|
||||||
|
|
||||||
|
// cr3 can contain other high bits so just to be safe
|
||||||
|
// get the pfn and bitshift it...
|
||||||
|
guest_dirbase = cr3{ guest_dirbase }.pml4_pfn << 12;
|
||||||
|
|
||||||
|
const auto command_page =
|
||||||
|
mm::map_guest_virt(guest_dirbase, command_ptr);
|
||||||
|
|
||||||
|
*reinterpret_cast<command_t*>(command_page) = command_data;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "mm.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
namespace vmexit
|
||||||
|
{
|
||||||
|
auto get_command(guest_virt_t command_ptr) -> command_t;
|
||||||
|
auto set_command(guest_virt_t command_ptr, command_t& command_data) -> void;
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
typedef struct _INLINE_HOOK
|
||||||
|
{
|
||||||
|
unsigned char Code[14];
|
||||||
|
unsigned char JmpCode[14];
|
||||||
|
|
||||||
|
void* Address;
|
||||||
|
void* HookAddress;
|
||||||
|
} INLINE_HOOK, *PINLINE_HOOK_T;
|
||||||
|
|
||||||
|
VOID MakeInlineHook(PINLINE_HOOK_T Hook, VOID* HookFrom, VOID* HookTo, BOOLEAN Install);
|
||||||
|
VOID EnableInlineHook(PINLINE_HOOK_T Hook);
|
||||||
|
VOID DisableInlineHook(PINLINE_HOOK_T Hook);
|
@ -1,15 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "Utils.h"
|
|
||||||
|
|
||||||
typedef struct _SHITHOOK
|
|
||||||
{
|
|
||||||
unsigned char Code[14];
|
|
||||||
unsigned char JmpCode[14];
|
|
||||||
|
|
||||||
void* Address;
|
|
||||||
void* HookAddress;
|
|
||||||
} SHITHOOK, *PSHITHOOK;
|
|
||||||
|
|
||||||
VOID MakeShitHook(PSHITHOOK Hook, VOID* HookFrom, VOID* HookTo, BOOLEAN Install);
|
|
||||||
VOID EnableShitHook(PSHITHOOK Hook);
|
|
||||||
VOID DisableShitHook(PSHITHOOK Hook);
|
|
@ -0,0 +1,67 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<VCProjectVersion>16.0</VCProjectVersion>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<ProjectGuid>{1770c4fb-345f-40dd-b4a2-2a5dc8a50866}</ProjectGuid>
|
||||||
|
<RootNamespace>example</RootNamespace>
|
||||||
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<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)'=='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)'=='Release|x64'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<PreprocessorDefinitions>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>
|
||||||
|
<AdditionalDependencies>libvoyager.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="main.cpp" />
|
||||||
|
<ClCompile Include="vdm_ctx\vdm_ctx.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="libvoyager.hpp" />
|
||||||
|
<ClInclude Include="util\nt.hpp" />
|
||||||
|
<ClInclude Include="util\util.hpp" />
|
||||||
|
<ClInclude Include="vdm_ctx\vdm_ctx.hpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
@ -0,0 +1,38 @@
|
|||||||
|
<?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++;cppm;ixx;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>{a575c9e1-1d0c-410a-8456-2743e2f93f86}</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>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="libvoyager.hpp">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<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_ctx\vdm_ctx.hpp">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
@ -0,0 +1,151 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <intrin.h>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#define VMEXIT_KEY 0xDEADBEEFDEADBEEF
|
||||||
|
#define PAGE_4KB 0x1000
|
||||||
|
#define PAGE_2MB PAGE_4KB * 512
|
||||||
|
#define PAGE_1GB PAGE_2MB * 512
|
||||||
|
|
||||||
|
using u8 = unsigned char;
|
||||||
|
using u16 = unsigned short;
|
||||||
|
using u32 = unsigned int;
|
||||||
|
using u64 = unsigned long long;
|
||||||
|
|
||||||
|
namespace voyager
|
||||||
|
{
|
||||||
|
// code comments itself...
|
||||||
|
using guest_virt_t = u64;
|
||||||
|
using guest_phys_t = u64;
|
||||||
|
using host_virt_t = u64;
|
||||||
|
using host_phys_t = u64;
|
||||||
|
|
||||||
|
enum class vmexit_command_t
|
||||||
|
{
|
||||||
|
init_page_tables,
|
||||||
|
read_guest_phys,
|
||||||
|
write_guest_phys,
|
||||||
|
copy_guest_virt,
|
||||||
|
get_dirbase,
|
||||||
|
translate
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class vmxroot_error_t
|
||||||
|
{
|
||||||
|
error_success,
|
||||||
|
pml4e_not_present,
|
||||||
|
pdpte_not_present,
|
||||||
|
pde_not_present,
|
||||||
|
pte_not_present,
|
||||||
|
vmxroot_translate_failure,
|
||||||
|
invalid_self_ref_pml4e,
|
||||||
|
invalid_mapping_pml4e,
|
||||||
|
invalid_host_virtual,
|
||||||
|
invalid_guest_physical,
|
||||||
|
invalid_guest_virtual,
|
||||||
|
page_table_init_failed
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef union _command_t
|
||||||
|
{
|
||||||
|
struct _copy_phys
|
||||||
|
{
|
||||||
|
host_phys_t phys_addr;
|
||||||
|
guest_virt_t buffer;
|
||||||
|
u64 size;
|
||||||
|
} copy_phys;
|
||||||
|
|
||||||
|
struct _copy_virt
|
||||||
|
{
|
||||||
|
guest_phys_t dirbase_src;
|
||||||
|
guest_virt_t virt_src;
|
||||||
|
guest_phys_t dirbase_dest;
|
||||||
|
guest_virt_t virt_dest;
|
||||||
|
u64 size;
|
||||||
|
} copy_virt;
|
||||||
|
|
||||||
|
struct _translate_virt
|
||||||
|
{
|
||||||
|
guest_virt_t virt_src;
|
||||||
|
guest_phys_t phys_addr;
|
||||||
|
} translate_virt;
|
||||||
|
|
||||||
|
guest_phys_t dirbase;
|
||||||
|
|
||||||
|
} command_t, * pcommand_t;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// this function is used to cause a vmexit as though its calling a function...
|
||||||
|
/// </summary>
|
||||||
|
extern "C" auto hypercall(u64 key, vmexit_command_t, pcommand_t command)->vmxroot_error_t;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// gets the current cores CR3 value (current address space pml4)...
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>returns the guest cr3 value...</returns>
|
||||||
|
auto current_dirbase()->guest_phys_t;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// initalizies page tables for all cores...
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>status of the initalization...</returns>
|
||||||
|
auto init()->vmxroot_error_t;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// translate a linear virtual address of the current address space
|
||||||
|
/// to a linear physical address...
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="virt_addr">virtual address in the vmexiting core's address space...</param>
|
||||||
|
/// <returns>guest physical address...</returns>
|
||||||
|
auto translate(guest_virt_t virt_addr)->guest_phys_t;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// reads guest physical memory...
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="phys_addr">physical address to read...</param>
|
||||||
|
/// <param name="buffer">buffer (guest virtual address) to read into...</param>
|
||||||
|
/// <param name="size">number of bytes to read (can only be 0x1000 or less)...</param>
|
||||||
|
/// <returns>STATUS_SUCCESS if the read was successful...</returns>
|
||||||
|
auto read_phys(guest_phys_t phys_addr, guest_virt_t buffer, u64 size)->vmxroot_error_t;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// write guest physical memory...
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="phys_addr">physical address to read</param>
|
||||||
|
/// <param name="buffer">guest virtual address to write from...</param>
|
||||||
|
/// <param name="size">number of bytes to write</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
auto write_phys(guest_phys_t phys_addr, guest_virt_t buffer, u64 size)->vmxroot_error_t;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// copy guest virtual memory between virtual address spaces...
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dirbase_src">dirbase of the source address space</param>
|
||||||
|
/// <param name="virt_src">virtual address in the source address space</param>
|
||||||
|
/// <param name="dirbase_dest">dirbase of the destination address space</param>
|
||||||
|
/// <param name="virt_dest">virtual address of the destination address</param>
|
||||||
|
/// <param name="size">size to copy between address spaces</param>
|
||||||
|
/// <returns>returns error_success on successful copy and invalid_guest_virt when an address is invalid...</returns>
|
||||||
|
auto copy_virt(guest_phys_t dirbase_src, guest_virt_t virt_src, guest_phys_t dirbase_dest,
|
||||||
|
guest_virt_t virt_dest, u64 size)->vmxroot_error_t;
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
auto rpm(guest_phys_t dirbase, guest_virt_t virt_addr) -> T
|
||||||
|
{
|
||||||
|
T buffer;
|
||||||
|
auto result = copy_virt(dirbase, virt_addr,
|
||||||
|
current_dirbase(), (guest_virt_t)&buffer, sizeof T);
|
||||||
|
|
||||||
|
if (result != vmxroot_error_t::error_success)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
auto wpm(guest_phys_t dirbase, guest_virt_t virt_addr, const T& data) -> void
|
||||||
|
{
|
||||||
|
copy_virt(dirbase, (guest_virt_t)&data,
|
||||||
|
dirbase, virt_addr, sizeof T);
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
@ -0,0 +1,81 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include "libvoyager.hpp"
|
||||||
|
#include "util/util.hpp"
|
||||||
|
#include "vdm_ctx/vdm_ctx.hpp"
|
||||||
|
|
||||||
|
int __cdecl main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
vdm::read_phys_t _read_phys =
|
||||||
|
[&](void* addr, void* buffer, std::size_t size) -> bool
|
||||||
|
{
|
||||||
|
const auto read_result =
|
||||||
|
voyager::read_phys((u64)addr, (u64)buffer, size);
|
||||||
|
|
||||||
|
return read_result ==
|
||||||
|
voyager::vmxroot_error_t::error_success;
|
||||||
|
};
|
||||||
|
|
||||||
|
vdm::write_phys_t _write_phys =
|
||||||
|
[&](void* addr, void* buffer, std::size_t size) -> bool
|
||||||
|
{
|
||||||
|
const auto write_result =
|
||||||
|
voyager::write_phys((u64)addr, (u64)buffer, size);
|
||||||
|
|
||||||
|
return write_result ==
|
||||||
|
voyager::vmxroot_error_t::error_success;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::printf("[+] voyager init result -> 0x%x (0x0 == success)\n", voyager::init());
|
||||||
|
std::printf("[+] current dirbase -> 0x%p\n", voyager::current_dirbase());
|
||||||
|
std::printf("[+] please wait... this may take up to 30 seconds...\n");
|
||||||
|
|
||||||
|
const auto nt_shutdown_system =
|
||||||
|
util::get_kmodule_export(
|
||||||
|
"ntoskrnl.exe", vdm::syscall_hook.first);
|
||||||
|
|
||||||
|
const auto nt_shutdown_phys =
|
||||||
|
voyager::translate(reinterpret_cast<
|
||||||
|
voyager::guest_virt_t>(nt_shutdown_system));
|
||||||
|
|
||||||
|
std::printf("NtShutdownSystem -> 0x%p\n", nt_shutdown_system);
|
||||||
|
std::printf("NtShutdownSystem (phys) -> 0x%p\n", nt_shutdown_phys);
|
||||||
|
vdm::syscall_address.store(reinterpret_cast<void*>(nt_shutdown_phys));
|
||||||
|
|
||||||
|
vdm::vdm_ctx vdm(_read_phys, _write_phys);
|
||||||
|
const auto ntoskrnl_base =
|
||||||
|
reinterpret_cast<void*>(
|
||||||
|
util::get_kmodule_base("ntoskrnl.exe"));
|
||||||
|
|
||||||
|
const auto ntoskrnl_memcpy =
|
||||||
|
util::get_kmodule_export("ntoskrnl.exe", "memcpy");
|
||||||
|
|
||||||
|
std::printf("[+] %s physical address -> 0x%p\n", vdm::syscall_hook.first, vdm::syscall_address.load());
|
||||||
|
std::printf("[+] %s page offset -> 0x%x\n", vdm::syscall_hook.first, vdm::nt_page_offset);
|
||||||
|
std::printf("[+] ntoskrnl base address -> 0x%p\n", ntoskrnl_base);
|
||||||
|
std::printf("[+] ntoskrnl memcpy address -> 0x%p\n", ntoskrnl_memcpy);
|
||||||
|
|
||||||
|
short mz_bytes = 0;
|
||||||
|
vdm.syscall<decltype(&memcpy)>(
|
||||||
|
ntoskrnl_memcpy,
|
||||||
|
&mz_bytes,
|
||||||
|
ntoskrnl_base,
|
||||||
|
sizeof mz_bytes
|
||||||
|
);
|
||||||
|
|
||||||
|
std::printf("[+] ntoskrnl MZ -> 0x%x\n", mz_bytes);
|
||||||
|
const auto explorer_dirbase =
|
||||||
|
vdm.get_dirbase(util::get_pid("explorer.exe"));
|
||||||
|
|
||||||
|
const auto ntdll_base =
|
||||||
|
reinterpret_cast<std::uintptr_t>(
|
||||||
|
GetModuleHandleA("ntdll.dll"));
|
||||||
|
|
||||||
|
std::printf("explorer.exe dirbase -> 0x%p\n", explorer_dirbase);
|
||||||
|
std::printf("current process dirbase -> %p\n", voyager::current_dirbase());
|
||||||
|
std::printf("ntdll.dll base -> 0x%p\n", ntdll_base);
|
||||||
|
std::printf("ntdll.dll MZ in explorer.exe -> 0x%x\n",
|
||||||
|
voyager::rpm<short>(explorer_dirbase, ntdll_base));
|
||||||
|
|
||||||
|
std::printf("[+] press any key to close...\n");
|
||||||
|
std::getchar();
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <winternl.h>
|
||||||
|
|
||||||
|
#pragma comment(lib, "ntdll.lib")
|
||||||
|
#define PAGE_4KB 0x1000
|
||||||
|
|
||||||
|
constexpr auto SystemModuleInformation = 11;
|
||||||
|
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;
|
||||||
|
|
||||||
|
using PEPROCESS = PVOID;
|
||||||
|
using PsLookupProcessByProcessId = NTSTATUS(__fastcall*)(
|
||||||
|
HANDLE ProcessId,
|
||||||
|
PEPROCESS* Process
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef union
|
||||||
|
{
|
||||||
|
std::uint64_t flags;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
std::uint64_t reserved1 : 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Page-level Write-Through
|
||||||
|
*
|
||||||
|
* [Bit 3] Controls the memory type used to access the first paging structure of the current paging-structure hierarchy.
|
||||||
|
* This bit is not used if paging is disabled, with PAE paging, or with 4-level paging if CR4.PCIDE=1.
|
||||||
|
*
|
||||||
|
* @see Vol3A[4.9(PAGING AND MEMORY TYPING)]
|
||||||
|
*/
|
||||||
|
std::uint64_t page_level_write_through : 1;
|
||||||
|
#define CR3_PAGE_LEVEL_WRITE_THROUGH_BIT 3
|
||||||
|
#define CR3_PAGE_LEVEL_WRITE_THROUGH_FLAG 0x08
|
||||||
|
#define CR3_PAGE_LEVEL_WRITE_THROUGH_MASK 0x01
|
||||||
|
#define CR3_PAGE_LEVEL_WRITE_THROUGH(_) (((_) >> 3) & 0x01)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Page-level Cache Disable
|
||||||
|
*
|
||||||
|
* [Bit 4] Controls the memory type used to access the first paging structure of the current paging-structure hierarchy.
|
||||||
|
* This bit is not used if paging is disabled, with PAE paging, or with 4-level paging2 if CR4.PCIDE=1.
|
||||||
|
*
|
||||||
|
* @see Vol3A[4.9(PAGING AND MEMORY TYPING)]
|
||||||
|
*/
|
||||||
|
std::uint64_t page_level_cache_disable : 1;
|
||||||
|
#define CR3_PAGE_LEVEL_CACHE_DISABLE_BIT 4
|
||||||
|
#define CR3_PAGE_LEVEL_CACHE_DISABLE_FLAG 0x10
|
||||||
|
#define CR3_PAGE_LEVEL_CACHE_DISABLE_MASK 0x01
|
||||||
|
#define CR3_PAGE_LEVEL_CACHE_DISABLE(_) (((_) >> 4) & 0x01)
|
||||||
|
std::uint64_t reserved2 : 7;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Address of page directory
|
||||||
|
*
|
||||||
|
* [Bits 47:12] Physical address of the 4-KByte aligned page directory (32-bit paging) or PML4 table (64-bit paging) used
|
||||||
|
* for linear-address translation.
|
||||||
|
*
|
||||||
|
* @see Vol3A[4.3(32-BIT PAGING)]
|
||||||
|
* @see Vol3A[4.5(4-LEVEL PAGING)]
|
||||||
|
*/
|
||||||
|
std::uint64_t pml4_pfn : 36;
|
||||||
|
#define CR3_ADDRESS_OF_PAGE_DIRECTORY_BIT 12
|
||||||
|
#define CR3_ADDRESS_OF_PAGE_DIRECTORY_FLAG 0xFFFFFFFFF000
|
||||||
|
#define CR3_ADDRESS_OF_PAGE_DIRECTORY_MASK 0xFFFFFFFFF
|
||||||
|
#define CR3_ADDRESS_OF_PAGE_DIRECTORY(_) (((_) >> 12) & 0xFFFFFFFFF)
|
||||||
|
std::uint64_t reserved3 : 16;
|
||||||
|
};
|
||||||
|
} cr3;
|
@ -0,0 +1,264 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <ntstatus.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string_view>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <string_view>
|
||||||
|
#include <tlhelp32.h>
|
||||||
|
#include <map>
|
||||||
|
#include "nt.hpp"
|
||||||
|
|
||||||
|
namespace util
|
||||||
|
{
|
||||||
|
inline std::map<std::uintptr_t, std::size_t> pmem_ranges{};
|
||||||
|
__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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma pack (push, 1)
|
||||||
|
struct PhysicalMemoryPage//CM_PARTIAL_RESOURCE_DESCRIPTOR
|
||||||
|
{
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t shareDisposition;
|
||||||
|
uint16_t flags;
|
||||||
|
uint64_t pBegin;
|
||||||
|
uint32_t sizeButNotExactly;
|
||||||
|
uint32_t pad;
|
||||||
|
|
||||||
|
static constexpr uint16_t cm_resource_memory_large_40{ 0x200 };
|
||||||
|
static constexpr uint16_t cm_resource_memory_large_48{ 0x400 };
|
||||||
|
static constexpr uint16_t cm_resource_memory_large_64{ 0x800 };
|
||||||
|
|
||||||
|
uint64_t size()const noexcept
|
||||||
|
{
|
||||||
|
if (flags & cm_resource_memory_large_40)
|
||||||
|
return uint64_t{ sizeButNotExactly } << 8;
|
||||||
|
else if (flags & cm_resource_memory_large_48)
|
||||||
|
return uint64_t{ sizeButNotExactly } << 16;
|
||||||
|
else if (flags & cm_resource_memory_large_64)
|
||||||
|
return uint64_t{ sizeButNotExactly } << 32;
|
||||||
|
else
|
||||||
|
return uint64_t{ sizeButNotExactly };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static_assert(sizeof(PhysicalMemoryPage) == 20);
|
||||||
|
#pragma pack (pop)
|
||||||
|
|
||||||
|
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++)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
pmem_ranges.emplace(*(uint64_t*)(pmi + 0), *(uint64_t*)(pmi + 8));
|
||||||
|
#else
|
||||||
|
const PhysicalMemoryPage& page{ *(PhysicalMemoryPage*)(pmi - 4) };
|
||||||
|
pmem_ranges.emplace(page.pBegin, page.size());
|
||||||
|
#endif
|
||||||
|
pmi += 20;
|
||||||
|
}
|
||||||
|
delete[] data;
|
||||||
|
RegCloseKey(h_key);
|
||||||
|
return true;
|
||||||
|
})();
|
||||||
|
|
||||||
|
__forceinline auto get_file_header(void* base_addr) -> PIMAGE_FILE_HEADER
|
||||||
|
{
|
||||||
|
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_kmodule_base(const char* module_name) -> std::uintptr_t
|
||||||
|
{
|
||||||
|
void* buffer = nullptr;
|
||||||
|
DWORD buffer_size = NULL;
|
||||||
|
|
||||||
|
auto 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 = NULL;
|
||||||
|
|
||||||
|
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 nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
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("\\"));
|
||||||
|
|
||||||
|
const auto module_base =
|
||||||
|
LoadLibraryEx(
|
||||||
|
full_path.c_str(),
|
||||||
|
NULL,
|
||||||
|
DONT_RESOLVE_DLL_REFERENCES
|
||||||
|
);
|
||||||
|
|
||||||
|
PIMAGE_DOS_HEADER p_idh;
|
||||||
|
PIMAGE_NT_HEADERS p_inh;
|
||||||
|
PIMAGE_EXPORT_DIRECTORY p_ied;
|
||||||
|
|
||||||
|
PDWORD addr, name;
|
||||||
|
PWORD ordinal;
|
||||||
|
|
||||||
|
p_idh = (PIMAGE_DOS_HEADER)module_base;
|
||||||
|
if (p_idh->e_magic != IMAGE_DOS_SIGNATURE)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
p_inh = (PIMAGE_NT_HEADERS)((LPBYTE)module_base + p_idh->e_lfanew);
|
||||||
|
if (p_inh->Signature != IMAGE_NT_SIGNATURE)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (p_inh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
p_ied = (PIMAGE_EXPORT_DIRECTORY)((LPBYTE)module_base +
|
||||||
|
p_inh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
|
||||||
|
|
||||||
|
addr = (PDWORD)((LPBYTE)module_base + p_ied->AddressOfFunctions);
|
||||||
|
name = (PDWORD)((LPBYTE)module_base + p_ied->AddressOfNames);
|
||||||
|
ordinal = (PWORD)((LPBYTE)module_base + p_ied->AddressOfNameOrdinals);
|
||||||
|
|
||||||
|
// find exported function
|
||||||
|
for (auto i = 0; i < p_ied->AddressOfFunctions; i++)
|
||||||
|
{
|
||||||
|
if (!strcmp(export_name, (char*)module_base + name[i]))
|
||||||
|
{
|
||||||
|
if (!rva)
|
||||||
|
{
|
||||||
|
auto result = (void*)((std::uintptr_t)modules->Modules[idx].ImageBase + addr[ordinal[i]]);
|
||||||
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto result = (void*)addr[ordinal[i]];
|
||||||
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
__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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,124 @@
|
|||||||
|
#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;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vdm_ctx::rkm(void* dst, void* src, std::size_t size)
|
||||||
|
{
|
||||||
|
static const auto ntoskrnl_memcpy =
|
||||||
|
util::get_kmodule_export("ntoskrnl.exe", "memcpy");
|
||||||
|
|
||||||
|
this->syscall<decltype(&memcpy)>(
|
||||||
|
ntoskrnl_memcpy, dst, src, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vdm_ctx::wkm(void* dst, void* src, std::size_t size)
|
||||||
|
{
|
||||||
|
static const auto ntoskrnl_memcpy =
|
||||||
|
util::get_kmodule_export("ntoskrnl.exe", "memcpy");
|
||||||
|
|
||||||
|
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
|
||||||
|
));
|
||||||
|
|
||||||
|
// accesses the page in order to make PTE...
|
||||||
|
memset(page_data, NULL, PAGE_4KB);
|
||||||
|
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,125 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <windows.h>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
#include <thread>
|
||||||
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
#include <functional>
|
||||||
|
#include "../util/util.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<bool(void*, void*, std::size_t)>;
|
||||||
|
using write_phys_t = std::function<bool(void*, void*, std::size_t)>;
|
||||||
|
|
||||||
|
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);
|
||||||
|
void rkm(void* dst, void* src, std::size_t size);
|
||||||
|
void 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;
|
||||||
|
rkm((void*)&buffer, (void*)addr, sizeof T);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
__forceinline void wkm(std::uintptr_t addr, const T& value)
|
||||||
|
{
|
||||||
|
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_dirbase(std::uint32_t pid) -> std::uintptr_t
|
||||||
|
{
|
||||||
|
const auto peproc = get_peprocess(pid);
|
||||||
|
|
||||||
|
if (!peproc)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return rkm<cr3>(
|
||||||
|
reinterpret_cast<std::uintptr_t>(peproc) + 0x28).pml4_pfn << 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
__forceinline auto get_peb(std::uint32_t pid) -> PPEB
|
||||||
|
{
|
||||||
|
static const auto get_peb =
|
||||||
|
util::get_kmodule_export(
|
||||||
|
"ntoskrnl.exe", "PsGetProcessPeb");
|
||||||
|
|
||||||
|
return this->syscall<PPEB(*)(PEPROCESS)>(
|
||||||
|
get_peb, get_peprocess(pid));
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
#include "libvoyager.hpp"
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
// taken from hvpp (wbenny). this code runs specific code on each logical processor...
|
||||||
|
// this is required since hyper-v has its own PML4 for each core...
|
||||||
|
// https://github.com/wbenny/hvpp/blob/master/src/hvppctrl/lib/mp.cpp#L4
|
||||||
|
auto voyager::init() -> vmxroot_error_t
|
||||||
|
{
|
||||||
|
GROUP_AFFINITY orig_group_affinity;
|
||||||
|
GetThreadGroupAffinity(GetCurrentThread(), &orig_group_affinity);
|
||||||
|
const auto group_count = GetActiveProcessorGroupCount();
|
||||||
|
|
||||||
|
// each core we are going to vmexit on and ask the payload
|
||||||
|
// to setup the mapping pml4e. for some reason each core on
|
||||||
|
// hyper-v has its own pml4... Not sure why? just is...
|
||||||
|
for (auto group_number = 0u; group_number < group_count; ++group_number)
|
||||||
|
{
|
||||||
|
const auto processor_count = GetActiveProcessorCount(group_number);
|
||||||
|
for (auto processor_number = 0u; processor_number < processor_count; ++processor_number)
|
||||||
|
{
|
||||||
|
GROUP_AFFINITY group_affinity = { 0 };
|
||||||
|
group_affinity.Mask = (KAFFINITY)(1) << processor_number;
|
||||||
|
group_affinity.Group = group_number;
|
||||||
|
SetThreadGroupAffinity(GetCurrentThread(), &group_affinity, NULL);
|
||||||
|
|
||||||
|
auto result = hypercall(VMEXIT_KEY, vmexit_command_t::init_page_tables, nullptr);
|
||||||
|
if (result != vmxroot_error_t::error_success)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SetThreadGroupAffinity(GetCurrentThread(), &orig_group_affinity, NULL);
|
||||||
|
return vmxroot_error_t::error_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto voyager::current_dirbase()->guest_phys_t
|
||||||
|
{
|
||||||
|
command_t command;
|
||||||
|
auto result = hypercall(VMEXIT_KEY, vmexit_command_t::get_dirbase, &command);
|
||||||
|
|
||||||
|
if (result != vmxroot_error_t::error_success)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return command.dirbase;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto voyager::translate(guest_virt_t virt_addr) -> guest_phys_t
|
||||||
|
{
|
||||||
|
command_t command;
|
||||||
|
command.translate_virt.virt_src = virt_addr;
|
||||||
|
|
||||||
|
const auto result = hypercall(VMEXIT_KEY, vmexit_command_t::translate, &command);
|
||||||
|
|
||||||
|
if (result != vmxroot_error_t::error_success)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return command.translate_virt.phys_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto voyager::read_phys(guest_phys_t phys_addr, guest_virt_t buffer, u64 size) -> vmxroot_error_t
|
||||||
|
{
|
||||||
|
command_t command;
|
||||||
|
command.copy_phys = { phys_addr, buffer, size };
|
||||||
|
return hypercall(VMEXIT_KEY, vmexit_command_t::read_guest_phys, &command);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto voyager::write_phys(guest_phys_t phys_addr, guest_virt_t buffer, u64 size) -> vmxroot_error_t
|
||||||
|
{
|
||||||
|
command_t command;
|
||||||
|
command.copy_phys = { phys_addr, buffer, size };
|
||||||
|
return hypercall(VMEXIT_KEY, vmexit_command_t::write_guest_phys, &command);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto voyager::copy_virt(guest_phys_t dirbase_src, guest_virt_t virt_src, guest_phys_t dirbase_dest,
|
||||||
|
guest_virt_t virt_dest, u64 size) -> vmxroot_error_t
|
||||||
|
{
|
||||||
|
command_t command;
|
||||||
|
command.copy_virt = { dirbase_src, virt_src, dirbase_dest, virt_dest, size };
|
||||||
|
return hypercall(VMEXIT_KEY, vmexit_command_t::copy_guest_virt, &command);
|
||||||
|
}
|
@ -0,0 +1,151 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <intrin.h>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#define VMEXIT_KEY 0xDEADBEEFDEADBEEF
|
||||||
|
#define PAGE_4KB 0x1000
|
||||||
|
#define PAGE_2MB PAGE_4KB * 512
|
||||||
|
#define PAGE_1GB PAGE_2MB * 512
|
||||||
|
|
||||||
|
using u8 = unsigned char;
|
||||||
|
using u16 = unsigned short;
|
||||||
|
using u32 = unsigned int;
|
||||||
|
using u64 = unsigned long long;
|
||||||
|
|
||||||
|
namespace voyager
|
||||||
|
{
|
||||||
|
// code comments itself...
|
||||||
|
using guest_virt_t = u64;
|
||||||
|
using guest_phys_t = u64;
|
||||||
|
using host_virt_t = u64;
|
||||||
|
using host_phys_t = u64;
|
||||||
|
|
||||||
|
enum class vmexit_command_t
|
||||||
|
{
|
||||||
|
init_page_tables,
|
||||||
|
read_guest_phys,
|
||||||
|
write_guest_phys,
|
||||||
|
copy_guest_virt,
|
||||||
|
get_dirbase,
|
||||||
|
translate
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class vmxroot_error_t
|
||||||
|
{
|
||||||
|
error_success,
|
||||||
|
pml4e_not_present,
|
||||||
|
pdpte_not_present,
|
||||||
|
pde_not_present,
|
||||||
|
pte_not_present,
|
||||||
|
vmxroot_translate_failure,
|
||||||
|
invalid_self_ref_pml4e,
|
||||||
|
invalid_mapping_pml4e,
|
||||||
|
invalid_host_virtual,
|
||||||
|
invalid_guest_physical,
|
||||||
|
invalid_guest_virtual,
|
||||||
|
page_table_init_failed
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef union _command_t
|
||||||
|
{
|
||||||
|
struct _copy_phys
|
||||||
|
{
|
||||||
|
host_phys_t phys_addr;
|
||||||
|
guest_virt_t buffer;
|
||||||
|
u64 size;
|
||||||
|
} copy_phys;
|
||||||
|
|
||||||
|
struct _copy_virt
|
||||||
|
{
|
||||||
|
guest_phys_t dirbase_src;
|
||||||
|
guest_virt_t virt_src;
|
||||||
|
guest_phys_t dirbase_dest;
|
||||||
|
guest_virt_t virt_dest;
|
||||||
|
u64 size;
|
||||||
|
} copy_virt;
|
||||||
|
|
||||||
|
struct _translate_virt
|
||||||
|
{
|
||||||
|
guest_virt_t virt_src;
|
||||||
|
guest_phys_t phys_addr;
|
||||||
|
} translate_virt;
|
||||||
|
|
||||||
|
guest_phys_t dirbase;
|
||||||
|
|
||||||
|
} command_t, * pcommand_t;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// this function is used to cause a vmexit as though its calling a function...
|
||||||
|
/// </summary>
|
||||||
|
extern "C" auto hypercall(u64 key, vmexit_command_t, pcommand_t command)->vmxroot_error_t;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// gets the current cores CR3 value (current address space pml4)...
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>returns the guest cr3 value...</returns>
|
||||||
|
auto current_dirbase()->guest_phys_t;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// initalizies page tables for all cores...
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>status of the initalization...</returns>
|
||||||
|
auto init()->vmxroot_error_t;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// translate a linear virtual address of the current address space
|
||||||
|
/// to a linear physical address...
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="virt_addr">virtual address in the vmexiting core's address space...</param>
|
||||||
|
/// <returns>guest physical address...</returns>
|
||||||
|
auto translate(guest_virt_t virt_addr)->guest_phys_t;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// reads guest physical memory...
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="phys_addr">physical address to read...</param>
|
||||||
|
/// <param name="buffer">buffer (guest virtual address) to read into...</param>
|
||||||
|
/// <param name="size">number of bytes to read (can only be 0x1000 or less)...</param>
|
||||||
|
/// <returns>STATUS_SUCCESS if the read was successful...</returns>
|
||||||
|
auto read_phys(guest_phys_t phys_addr, guest_virt_t buffer, u64 size)->vmxroot_error_t;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// write guest physical memory...
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="phys_addr">physical address to read</param>
|
||||||
|
/// <param name="buffer">guest virtual address to write from...</param>
|
||||||
|
/// <param name="size">number of bytes to write</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
auto write_phys(guest_phys_t phys_addr, guest_virt_t buffer, u64 size)->vmxroot_error_t;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// copy guest virtual memory between virtual address spaces...
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dirbase_src">dirbase of the source address space</param>
|
||||||
|
/// <param name="virt_src">virtual address in the source address space</param>
|
||||||
|
/// <param name="dirbase_dest">dirbase of the destination address space</param>
|
||||||
|
/// <param name="virt_dest">virtual address of the destination address</param>
|
||||||
|
/// <param name="size">size to copy between address spaces</param>
|
||||||
|
/// <returns>returns error_success on successful copy and invalid_guest_virt when an address is invalid...</returns>
|
||||||
|
auto copy_virt(guest_phys_t dirbase_src, guest_virt_t virt_src, guest_phys_t dirbase_dest,
|
||||||
|
guest_virt_t virt_dest, u64 size)->vmxroot_error_t;
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
auto rpm(guest_phys_t dirbase, guest_virt_t virt_addr) -> T
|
||||||
|
{
|
||||||
|
T buffer;
|
||||||
|
auto result = copy_virt(dirbase, virt_addr,
|
||||||
|
current_dirbase(), (guest_virt_t)&buffer, sizeof T);
|
||||||
|
|
||||||
|
if (result != vmxroot_error_t::error_success)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
auto wpm(guest_phys_t dirbase, guest_virt_t virt_addr, const T& data) -> void
|
||||||
|
{
|
||||||
|
copy_virt(dirbase, (guest_virt_t)&data,
|
||||||
|
dirbase, virt_addr, sizeof T);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="libvoyager.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="libvoyager.hpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<MASM Include="com.asm">
|
||||||
|
<FileType>Document</FileType>
|
||||||
|
</MASM>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<VCProjectVersion>16.0</VCProjectVersion>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<ProjectGuid>{f468e79e-cc93-4a7f-844e-984117b5b08b}</ProjectGuid>
|
||||||
|
<RootNamespace>libvoyager</RootNamespace>
|
||||||
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
</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)'=='Release|x64'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
|
||||||
|
</ImportGroup>
|
||||||
|
</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>
|
After Width: | Height: | Size: 44 KiB |
Loading…
Reference in new issue