IST code is still broken and demo is broken...

merge-requests/1/head
_xeroxz 4 years ago
parent 51f47aa52e
commit 71005f896f

@ -23,9 +23,8 @@ Global
{881CCCA6-80EB-486F-8923-4F4B7DD41F9A}.Release|x64.Build.0 = Release|x64
{881CCCA6-80EB-486F-8923-4F4B7DD41F9A}.Release|x64.Deploy.0 = Release|x64
{881CCCA6-80EB-486F-8923-4F4B7DD41F9A}.Release|x86.ActiveCfg = Release|x64
{A3D028D7-1650-4036-9FB0-4B8CC16268FE}.Debug|x64.ActiveCfg = Debug|x64
{A3D028D7-1650-4036-9FB0-4B8CC16268FE}.Debug|x64.Build.0 = Debug|x64
{A3D028D7-1650-4036-9FB0-4B8CC16268FE}.Debug|x86.ActiveCfg = Debug|x64
{A3D028D7-1650-4036-9FB0-4B8CC16268FE}.Debug|x64.ActiveCfg = Release|x64
{A3D028D7-1650-4036-9FB0-4B8CC16268FE}.Debug|x86.ActiveCfg = Release|x64
{A3D028D7-1650-4036-9FB0-4B8CC16268FE}.Release|x64.ActiveCfg = Release|x64
{A3D028D7-1650-4036-9FB0-4B8CC16268FE}.Release|x64.Build.0 = Release|x64
{A3D028D7-1650-4036-9FB0-4B8CC16268FE}.Release|x86.ActiveCfg = Release|x64

@ -0,0 +1,78 @@
#include "bluepill.h"
namespace bluepill
{
auto get_dirbase() -> u64
{
vmcall_command_t command{};
memset(&command, NULL, sizeof command);
command.present = true;
command.option = vmcall_option::dirbase;
hypercall(VMCALL_KEY, &command);
return command.dirbase;
}
auto translate(void* dirbase, void* virt_addr) -> u64
{
vmcall_command_t command{};
memset(&command, NULL, sizeof command);
command.present = true;
command.option = vmcall_option::translate;
command.translate.dirbase = reinterpret_cast<u64>(dirbase);
command.translate.virt_addr = reinterpret_cast<u64>(virt_addr);
hypercall(VMCALL_KEY, &command);
return command.translate.phys_addr;
}
auto read_phys(void* dest, void* phys_src, u64 size) -> bool
{
vmcall_command_t command{};
memset(&command, NULL, sizeof command);
command.present = true;
command.option = vmcall_option::read_phys;
command.read_phys.virt_dest = reinterpret_cast<u64>(dest);
command.read_phys.phys_src = reinterpret_cast<u64>(phys_src);
command.read_phys.dirbase_dest = get_dirbase();
command.read_phys.size = size;
hypercall(VMCALL_KEY, &command);
return command.result;
}
auto write_phys(void* phys_dest, void* src, u64 size) -> bool
{
vmcall_command_t command{};
memset(&command, NULL, sizeof command);
command.present = true;
command.option = vmcall_option::write_phys;
command.write_phys.virt_src = reinterpret_cast<u64>(src);
command.write_phys.phys_dest = reinterpret_cast<u64>(phys_dest);
command.write_phys.dirbase_src = get_dirbase();
command.write_phys.size = size;
hypercall(VMCALL_KEY, &command);
return command.result;
}
auto copy_virt(void* dirbase_src, void* virt_src, void* dirbase_dest, void* virt_dest, u64 size) -> bool
{
vmcall_command_t command{};
memset(&command, NULL, sizeof command);
command.present = true;
command.copy_virt.dirbase_dest = reinterpret_cast<u64>(dirbase_dest);
command.copy_virt.virt_dest = reinterpret_cast<u64>(virt_dest);
command.copy_virt.dirbase_src = reinterpret_cast<u64>(dirbase_src);
command.copy_virt.virt_src = reinterpret_cast<u64>(virt_src);
command.copy_virt.size = size;
hypercall(VMCALL_KEY, &command);
return command.result;
}
}

@ -0,0 +1,88 @@
#pragma once
#include <Windows.h>
#include <intrin.h>
#define VMCALL_KEY 0xC0FFEE
using u8 = unsigned char;
using u16 = unsigned short;
using u32 = unsigned int;
using u64 = unsigned long long;
using u128 = __m128;
using s8 = char;
using s16 = short;
using s32 = int;
using s64 = long long;
namespace bluepill
{
enum class vmcall_option
{
translate,
copy_virt,
write_phys,
read_phys,
dirbase
};
typedef struct _vmcall_command_t
{
bool present;
bool result;
vmcall_option option;
union
{
struct
{
u64 dirbase;
u64 virt_addr;
u64 phys_addr;
} translate;
struct
{
u64 virt_src;
u64 dirbase_src;
u64 virt_dest;
u64 dirbase_dest;
u64 size;
} copy_virt;
struct
{
u64 virt_src;
u64 dirbase_src;
u64 phys_dest;
u64 size;
} write_phys;
struct
{
u64 phys_src;
u64 dirbase_dest;
u64 virt_dest;
u64 size;
} read_phys;
u64 dirbase;
};
} vmcall_command_t, * pvmcall_command_t;
// vmcall into the hypervisor...
extern "C" u64 hypercall(u64 key, pvmcall_command_t command);
// get vmexiting logical processors pml4...
auto get_dirbase() -> u64;
// read/write physical memory...
auto read_phys(void* dest, void* phys_src, u64 size) -> bool;
auto write_phys(void* phys_dest, void* src, u64 size) -> bool;
// translate virtual to physical...
auto translate(void* dirbase, void* virt_addr)->u64;
// copy virtual memory between two address spaces... page protections are ignored...
auto copy_virt(void* dirbase_src, void* virt_src, void* dirbase_dest, void* virt_dest, u64 size) -> bool;
}

@ -1,10 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
@ -18,18 +14,12 @@
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<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>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
@ -37,39 +27,22 @@
</ImportGroup>
<ImportGroup Label="Shared">
</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|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<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>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@ -79,7 +52,9 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="bluepill.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="vdm_ctx\vdm_ctx.cpp" />
</ItemGroup>
<ItemGroup>
<MASM Include="hypercall.asm">
@ -87,7 +62,10 @@
</MASM>
</ItemGroup>
<ItemGroup>
<ClInclude Include="hypercall.h" />
<ClInclude Include="bluepill.h" />
<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">

@ -9,19 +9,37 @@
<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>{02abfc13-9b80-4455-8b2b-282417e04ab4}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="bluepill.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="vdm_ctx\vdm_ctx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="hypercall.asm">
<MASM Include="hypercall.asm">
<Filter>Source Files</Filter>
</None>
</MASM>
</ItemGroup>
<ItemGroup>
<ClInclude Include="hypercall.h">
<ClInclude Include="bluepill.h">
<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>

@ -1,6 +1,6 @@
.code
hypercall proc
cpuid
vmcall
ret
hypercall endp
end

@ -1,20 +0,0 @@
#pragma once
#include <Windows.h>
#include <intrin.h>
using u8 = unsigned char;
using u16 = unsigned short;
using u32 = unsigned int;
using u64 = unsigned long long;
using u128 = __m128;
using s8 = char;
using s16 = short;
using s32 = int;
using s64 = long long;
namespace bluepill
{
constexpr auto key = 0xC0FFEE;
extern "C" u64 hypercall(u64 key);
}

@ -1,9 +1,57 @@
#include <iostream>
#include <intrin.h>
#include "hypercall.h"
#include "bluepill.h"
#include "vdm_ctx/vdm_ctx.hpp"
int main()
auto __cdecl main(int argc, char** argv) -> void
{
std::printf("hypercall result: 0x%x\n", bluepill::hypercall(bluepill::key));
vdm::read_phys_t _read_phys =
[&](void* addr, void* buffer, std::size_t size) -> bool
{
return bluepill::read_phys(buffer, addr, size);
};
vdm::write_phys_t _write_phys =
[&](void* addr, void* buffer, std::size_t size) -> bool
{
return bluepill::write_phys(addr, buffer, size);
};
const auto dirbase =
reinterpret_cast<void*>(
bluepill::get_dirbase());
std::printf("current dirbase -> 0x%p\n", dirbase);
std::getchar();
const auto nt_shutdown_phys =
bluepill::translate(dirbase,
util::get_kmodule_export("ntoskrnl.exe",
vdm::syscall_hook.second));
std::printf("NtShutdownSystem translated (phys) -> 0x%p\n", nt_shutdown_phys);
std::getchar();
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("[+] kernel MZ -> 0x%x\n", mz_bytes);
std::getchar();
}

@ -0,0 +1,35 @@
#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
);

@ -0,0 +1,234 @@
#pragma once
#include <Windows.h>
#include <ntstatus.h>
#include <cstdint>
#include <string_view>
#include <algorithm>
#include <string_view>
#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;
}
}

@ -0,0 +1,122 @@
#include "vdm_ctx.hpp"
namespace vdm
{
vdm_ctx::vdm_ctx(read_phys_t& read_func, write_phys_t& write_func)
:
read_phys(read_func),
write_phys(write_func)
{
// already found the syscall's physical page...
if (vdm::syscall_address.load())
return;
vdm::ntoskrnl = reinterpret_cast<std::uint8_t*>(
LoadLibraryExA("ntoskrnl.exe", NULL,
DONT_RESOLVE_DLL_REFERENCES));
nt_rva = reinterpret_cast<std::uint32_t>(
util::get_kmodule_export(
"ntoskrnl.exe",
syscall_hook.first,
true
));
vdm::nt_page_offset = nt_rva % PAGE_4KB;
// for each physical memory range, make a thread to search it
std::vector<std::thread> search_threads;
for (auto ranges : util::pmem_ranges)
search_threads.emplace_back(std::thread(
&vdm_ctx::locate_syscall,
this,
ranges.first,
ranges.second
));
for (std::thread& search_thread : search_threads)
search_thread.join();
}
void vdm_ctx::set_read(read_phys_t& read_func)
{
this->read_phys = read_func;
}
void vdm_ctx::set_write(write_phys_t& write_func)
{
this->write_phys = write_func;
}
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
));
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,103 @@
#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* addr, void* buffer, std::size_t size)>;
using write_phys_t = std::function<bool(void* addr, void* buffer, std::size_t size)>;
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;
}
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;
};
}

@ -12,18 +12,18 @@ auto drv_entry(PDRIVER_OBJECT driver_object, PUNICODE_STRING registry_path) -> N
cr3 cr3_value;
cr3_value.flags = __readcr3();
cr3_value.address_of_page_directory =
cr3_value.pml4_pfn =
(MmGetPhysicalAddress(mm::pml4).QuadPart >> 12);
memset(mm::pml4, NULL, sizeof mm::pml4);
mm::pml4[PML4_SELF_REF].pfn = cr3_value.address_of_page_directory;
mm::pml4[PML4_SELF_REF].pfn = cr3_value.pml4_pfn;
mm::pml4[PML4_SELF_REF].present = true;
mm::pml4[PML4_SELF_REF].rw = true;
mm::pml4[PML4_SELF_REF].user_supervisor = false;
PHYSICAL_ADDRESS current_pml4;
current_pml4.QuadPart =
(cr3{ __readcr3() }.address_of_page_directory << 12);
(cr3{ __readcr3() }.pml4_pfn << 12);
const auto kernel_pml4 =
reinterpret_cast<mm::ppml4e>(
@ -33,7 +33,7 @@ auto drv_entry(PDRIVER_OBJECT driver_object, PUNICODE_STRING registry_path) -> N
memcpy(&mm::pml4[256], &kernel_pml4[256], sizeof(mm::pml4e) * 256);
// setup mapping ptes to be present, writeable, executable, and user supervisor false...
for (auto idx = 0u; idx < 254; ++idx)
for (auto idx = 0u; idx < 255; ++idx)
{
reinterpret_cast<mm::ppte>(mm::pml4)[idx].present = true;
reinterpret_cast<mm::ppte>(mm::pml4)[idx].rw = true;

@ -1,5 +1,27 @@
#include "vmxexit_handler.h"
auto get_command(u64 dirbase, u64 command_ptr) -> vmcall_command_t
{
const auto virt_map =
mm::map_virt(dirbase, command_ptr);
if (!virt_map)
return {};
return *reinterpret_cast<pvmcall_command_t>(virt_map);
}
auto set_command(u64 dirbase, u64 command_ptr, const vmcall_command_t& vmcall_command) -> void
{
const auto virt_map =
mm::map_virt(dirbase, command_ptr);
if (!virt_map)
return;
*reinterpret_cast<pvmcall_command_t>(virt_map) = vmcall_command;
}
auto exit_handler(hv::pguest_registers regs) -> void
{
u64 exit_reason;
@ -8,20 +30,6 @@ auto exit_handler(hv::pguest_registers regs) -> void
switch (exit_reason)
{
case VMX_EXIT_REASON_EXECUTE_CPUID:
{
if (regs->rcx == 0xC0FFEE)
{
__try
{
*(u8*)0x0 = 0xDE;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
regs->rax = 0xC0FFEE;
break;
}
}
else
{
int result[4];
__cpuid(result, regs->rax);
@ -29,7 +37,6 @@ auto exit_handler(hv::pguest_registers regs) -> void
regs->rbx = result[1];
regs->rcx = result[2];
regs->rdx = result[3];
}
break;
}
case VMX_EXIT_REASON_EXECUTE_XSETBV:
@ -126,6 +133,97 @@ auto exit_handler(hv::pguest_registers regs) -> void
__invd();
break;
}
case VMX_EXIT_REASON_EXECUTE_VMCALL:
{
if (regs->rcx == VMCALL_KEY)
{
// test SEH... IST is currently boonk...
__try
{
*reinterpret_cast<int*>(0x0) = 0xDE;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
__debugbreak();
}
cr3 dirbase;
__vmx_vmread(VMCS_GUEST_CR3, &dirbase.flags);
auto command = get_command(
dirbase.pml4_pfn << 12, regs->rdx);
if (command.present)
{
switch (command.option)
{
case vmcall_option::copy_virt:
{
command.result =
mm::copy_virt(
command.copy_virt.dirbase_src,
command.copy_virt.virt_src,
command.copy_virt.dirbase_dest,
command.copy_virt.virt_dest,
command.copy_virt.size);
break;
}
case vmcall_option::translate:
{
command.translate.phys_addr =
mm::translate(mm::virt_addr_t{
command.translate.virt_addr },
command.translate.dirbase);
// true if address is not null...
command.result = command.translate.phys_addr;
break;
}
case vmcall_option::read_phys:
{
command.result =
mm::read_phys(
command.read_phys.dirbase_dest,
command.read_phys.phys_src,
command.read_phys.virt_dest,
command.read_phys.size);
break;
}
case vmcall_option::write_phys:
{
command.result =
mm::write_phys(
command.write_phys.dirbase_src,
command.write_phys.phys_dest,
command.write_phys.virt_src,
command.write_phys.size);
break;
}
case vmcall_option::dirbase:
{
command.result = true;
command.dirbase = dirbase.pml4_pfn << 12;
break;
}
default:
// check to see why the option was invalid...
__debugbreak();
break;
}
set_command(dirbase.pml4_pfn << 12, regs->rdx, command);
}
}
else
{
vmentry_interrupt_information interrupt{};
interrupt.flags = interruption_type::hardware_exception;
interrupt.vector = EXCEPTION_INVALID_OPCODE;
interrupt.valid = true;
__vmx_vmwrite(VMCS_CTRL_VMENTRY_INTERRUPTION_INFORMATION_FIELD, interrupt.flags);
}
break;
}
case VMX_EXIT_REASON_EXECUTE_VMWRITE:
case VMX_EXIT_REASON_EXECUTE_VMREAD:
case VMX_EXIT_REASON_EXECUTE_VMPTRST:
@ -133,7 +231,6 @@ auto exit_handler(hv::pguest_registers regs) -> void
case VMX_EXIT_REASON_EXECUTE_VMCLEAR:
case VMX_EXIT_REASON_EXECUTE_VMXOFF:
case VMX_EXIT_REASON_EXECUTE_VMXON:
case VMX_EXIT_REASON_EXECUTE_VMCALL:
{
vmentry_interrupt_information interrupt{};
interrupt.flags = interruption_type::hardware_exception;

@ -29,6 +29,7 @@ extern "C" void _sgdt(void*);
#define HOST_STACK_PAGES 6
#define HOST_STACK_SIZE PAGE_SIZE * HOST_STACK_PAGES
#define VMCALL_KEY 0xC0FFEE
// Export Directory
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0

@ -294,7 +294,7 @@ typedef union
* @see Vol3A[4.3(32-BIT PAGING)]
* @see Vol3A[4.5(4-LEVEL PAGING)]
*/
uint64_t address_of_page_directory : 36;
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

@ -52,7 +52,7 @@ namespace idt
result.segment_selector = readcs();
result.gate_type = SEGMENT_DESCRIPTOR_TYPE_INTERRUPT_GATE;
result.present = true;
//result.ist_index = ist_index;
result.ist_index = ist_index;
result.offset_high = idt_handler.offset_high;
result.offset_middle = idt_handler.offset_middle;

105
mm.cpp

@ -4,7 +4,7 @@ namespace mm
{
auto translate(virt_addr_t virt_addr) -> u64
{
virt_addr_t cursor{ vmxroot_pml4 };
virt_addr_t cursor{ (u64) vmxroot_pml4 };
if (!reinterpret_cast<ppml4e>(cursor.value)[virt_addr.pml4_index].present)
return {};
@ -82,7 +82,7 @@ namespace mm
auto map_page(u64 phys_addr, map_type type) -> u64
{
cpuid_eax_01 cpuid_value;
virt_addr_t result{ vmxroot_pml4 };
virt_addr_t result{ (u64) vmxroot_pml4 };
__cpuid((int*)&cpuid_value, 1);
result.pt_index = (cpuid_value
@ -93,16 +93,16 @@ namespace mm
reinterpret_cast<ppte>(vmxroot_pml4)
[result.pt_index].pfn = phys_addr >> 12;
__invlpg(result.value);
result.offset_4kb = virt_addr_t{ (void*)phys_addr }.offset_4kb;
return reinterpret_cast<u64>(result.value);
__invlpg((void*)result.value);
result.offset_4kb = phys_addr_t{ phys_addr }.offset_4kb;
return result.value;
}
auto map_virt(u64 dirbase, u64 virt_addr, map_type map_type) -> u64
{
const auto phys_addr =
translate(virt_addr_t{ (void*)
virt_addr }, dirbase, map_type);
translate(virt_addr_t{ virt_addr },
dirbase, map_type);
if (!phys_addr)
return {};
@ -110,15 +110,102 @@ namespace mm
return map_page(phys_addr, map_type);
}
auto read_phys(u64 dirbase, u64 guest_phys, u64 guest_virt, u64 size) -> bool
{
// handle reading over page boundaries
// of both src and dest...
while (size)
{
auto dest_current_size = PAGE_SIZE -
virt_addr_t{ guest_virt }.offset_4kb;
if (size < dest_current_size)
dest_current_size = size;
auto src_current_size = PAGE_SIZE -
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_virt(dirbase, guest_virt, map_type::dest));
if (!mapped_dest)
return false;
const auto mapped_src =
reinterpret_cast<void*>(
map_page(guest_phys, map_type::src));
if (!mapped_src)
return false;
memcpy(mapped_dest, mapped_src, current_size);
guest_phys += current_size;
guest_virt += current_size;
size -= current_size;
}
return true;
}
auto write_phys(u64 dirbase, u64 guest_phys, u64 guest_virt, u64 size) -> bool
{
// handle reading over page boundaries
// of both src and dest...
while (size)
{
auto dest_current_size = PAGE_SIZE -
virt_addr_t{ guest_virt }.offset_4kb;
if (size < dest_current_size)
dest_current_size = size;
auto src_current_size = PAGE_SIZE -
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_virt(dirbase, guest_virt, map_type::src));
if (!mapped_src)
return false;
const auto mapped_dest =
reinterpret_cast<void*>(
map_page(guest_phys, map_type::dest));
if (!mapped_src)
return false;
memcpy(mapped_dest, mapped_src, current_size);
guest_phys += current_size;
guest_virt += current_size;
size -= current_size;
}
return true;
}
auto copy_virt(u64 dirbase_src, u64 virt_src, u64 dirbase_dest, u64 virt_dest, u64 size) -> bool
{
while (size)
{
auto dest_size = PAGE_SIZE - virt_addr_t{ (void*)virt_dest }.offset_4kb;
auto dest_size = PAGE_SIZE - virt_addr_t{ virt_dest }.offset_4kb;
if (size < dest_size)
dest_size = size;
auto src_size = PAGE_SIZE - virt_addr_t{ (void*)virt_src }.offset_4kb;
auto src_size = PAGE_SIZE - virt_addr_t{ virt_src }.offset_4kb;
if (size < src_size)
src_size = size;

@ -8,7 +8,7 @@ namespace mm
{
typedef union _virt_addr_t
{
void* value;
u64 value;
struct
{
u64 offset_4kb : 12;
@ -37,6 +37,7 @@ namespace mm
};
} virt_addr_t, * pvirt_addr_t;
using phys_addr_t = virt_addr_t;
typedef union _pml4e
{
@ -138,12 +139,15 @@ namespace mm
auto translate(virt_addr_t virt_addr, u64 pml4_phys, map_type type = map_type::src) -> u64;
// map a page into vmxroot address space...
auto map_page(u64 phys_addr, map_type type) -> u64;
auto map_page(u64 phys_addr, map_type type = map_type::src) -> u64;
// map a page (4kb) from another address into vmxroot...
auto map_virt(u64 dirbase, u64 virt_addr, map_type map_type)->u64;
auto map_virt(u64 dirbase, u64 virt_addr, map_type map_type = map_type::src)->u64;
// copy virtual memory without changing cr3... this maps the physical memory into vmxroot
// address space and copies the memory directly between the physical pages... the memory must be paged in...
auto copy_virt(u64 dirbase_src, u64 virt_src, u64 dirbase_dest, u64 virt_dest, u64 size) -> bool;
auto read_phys(u64 dirbase, u64 guest_phys, u64 guest_virt, u64 size) -> bool;
auto write_phys(u64 dirbase, u64 guest_phys, u64 guest_virt, u64 size) -> bool;
}

@ -2,6 +2,64 @@
#include "hv_types.hpp"
#include "debug.hpp"
#include "invd.hpp"
#include "mm.hpp"
enum class vmcall_option
{
translate,
copy_virt,
write_phys,
read_phys,
dirbase
};
typedef struct _vmcall_command_t
{
bool present;
bool result;
vmcall_option option;
union
{
struct
{
u64 dirbase;
u64 virt_addr;
u64 phys_addr;
} translate;
struct
{
u64 virt_src;
u64 dirbase_src;
u64 virt_dest;
u64 dirbase_dest;
u64 size;
} copy_virt;
struct
{
u64 virt_src;
u64 dirbase_src;
u64 phys_dest;
u64 size;
} write_phys;
struct
{
u64 phys_src;
u64 dirbase_dest;
u64 virt_dest;
u64 size;
} read_phys;
u64 dirbase;
};
} vmcall_command_t, * pvmcall_command_t;
extern "C" auto vmxexit_handler() -> void;
extern "C" auto exit_handler(hv::pguest_registers regs) -> void;
auto get_command(u64 dirbase, u64 command_ptr) -> vmcall_command_t;
auto set_command(u64 dirbase, u64 command_ptr, const vmcall_command_t& vmcall_command) -> void;
Loading…
Cancel
Save