mirror of
https://github.com/momo5502/hypervisor.git
synced 2025-07-04 02:01:58 +00:00
Compare commits
3 Commits
main
...
feature/li
Author | SHA1 | Date | |
---|---|---|---|
89f1825b8c | |||
b266388c55 | |||
6730723d36 |
15
.github/dependabot.yml
vendored
15
.github/dependabot.yml
vendored
@ -1,12 +1,7 @@
|
|||||||
version: 2
|
version: 2
|
||||||
updates:
|
updates:
|
||||||
- package-ecosystem: gitsubmodule
|
- package-ecosystem: gitsubmodule
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: monthly
|
interval: daily
|
||||||
open-pull-requests-limit: 10
|
open-pull-requests-limit: 10
|
||||||
|
|
||||||
- package-ecosystem: "github-actions"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: monthly
|
|
||||||
|
12
.github/workflows/build.yml
vendored
12
.github/workflows/build.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
|||||||
- release
|
- release
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files
|
- name: Check out files
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
@ -28,7 +28,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Install WDK
|
- name: Install WDK
|
||||||
run: |
|
run: |
|
||||||
curl -L --output wdksetup.exe https://go.microsoft.com/fwlink/?linkid=2196230
|
curl -L --output wdksetup.exe https://go.microsoft.com/fwlink/?linkid=2166289
|
||||||
cmd /c start /wait wdksetup.exe /ceip off /quiet /features +
|
cmd /c start /wait wdksetup.exe /ceip off /quiet /features +
|
||||||
|
|
||||||
- name: Setup CMake
|
- name: Setup CMake
|
||||||
@ -38,10 +38,13 @@ jobs:
|
|||||||
uses: ammaraskar/msvc-problem-matcher@master
|
uses: ammaraskar/msvc-problem-matcher@master
|
||||||
|
|
||||||
- name: Setup DevCmd
|
- name: Setup DevCmd
|
||||||
uses: ilammy/msvc-dev-cmd@v1.13.0
|
uses: ilammy/msvc-dev-cmd@v1.10.0
|
||||||
with:
|
with:
|
||||||
arch: x64
|
arch: x64
|
||||||
|
|
||||||
|
- name: Setup Ninja
|
||||||
|
uses: ashutoshvarma/setup-ninja@master
|
||||||
|
|
||||||
- name: Configure CMake
|
- name: Configure CMake
|
||||||
run: cmake --preset=${{matrix.configuration}}
|
run: cmake --preset=${{matrix.configuration}}
|
||||||
|
|
||||||
@ -49,11 +52,10 @@ jobs:
|
|||||||
run: cmake --build --preset=${{matrix.configuration}}
|
run: cmake --build --preset=${{matrix.configuration}}
|
||||||
|
|
||||||
- name: Upload ${{matrix.configuration}} binaries
|
- name: Upload ${{matrix.configuration}} binaries
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: ${{matrix.configuration}} binaries
|
name: ${{matrix.configuration}} binaries
|
||||||
path: |
|
path: |
|
||||||
build/${{matrix.configuration}}/artifacts/*.exe
|
build/${{matrix.configuration}}/artifacts/*.exe
|
||||||
build/${{matrix.configuration}}/artifacts/*.dll
|
|
||||||
build/${{matrix.configuration}}/artifacts/*.pdb
|
build/${{matrix.configuration}}/artifacts/*.pdb
|
||||||
build/${{matrix.configuration}}/artifacts/*.sys
|
build/${{matrix.configuration}}/artifacts/*.sys
|
@ -9,7 +9,7 @@ project(hypervisor LANGUAGES C CXX)
|
|||||||
|
|
||||||
set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
|
set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||

|
|
||||||
[](https://github.com/momo5502/hypervisor/actions)
|
|
||||||
[](https://paypal.me/momo5502)
|
|
||||||
|
|
||||||
# Hypervisor
|
# Hypervisor
|
||||||
|
|
||||||
Experimental VT-X type 2 hypervisor with EPT hooking/analysis support.
|
Hypervisor experiments.
|
||||||
Basically just a tool I use for reverse engineering and stuff. Nothing too serious.
|
Nothing serious. Yet.
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
|
2
external/FindWDK
vendored
2
external/FindWDK
vendored
Submodule external/FindWDK updated: 04b4151f6d...43fd504e1d
2
external/ia32-doc
vendored
2
external/ia32-doc
vendored
Submodule external/ia32-doc updated: 2bc5284e04...77e021b690
@ -1,4 +1,3 @@
|
|||||||
add_subdirectory(shared)
|
add_subdirectory(shared)
|
||||||
add_subdirectory(driver)
|
add_subdirectory(driver)
|
||||||
add_subdirectory(library)
|
|
||||||
add_subdirectory(runner)
|
add_subdirectory(runner)
|
@ -2,9 +2,9 @@ enable_language(ASM_MASM)
|
|||||||
|
|
||||||
string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
|
string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
|
||||||
|
|
||||||
file(GLOB driver_sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
|
file(GLOB driver_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
|
||||||
file(GLOB driver_headers CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp)
|
file(GLOB driver_headers ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp)
|
||||||
file(GLOB driver_asm_sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.asm)
|
file(GLOB driver_asm_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.asm)
|
||||||
|
|
||||||
wdk_add_driver(driver
|
wdk_add_driver(driver
|
||||||
${driver_sources}
|
${driver_sources}
|
||||||
@ -17,11 +17,11 @@ target_precompile_headers(driver
|
|||||||
|
|
||||||
cmake_path(NATIVE_PATH PROJECT_SOURCE_DIR NORMALIZE WINDOWS_PROJECT_DIR)
|
cmake_path(NATIVE_PATH PROJECT_SOURCE_DIR NORMALIZE WINDOWS_PROJECT_DIR)
|
||||||
|
|
||||||
#add_custom_command(TARGET driver
|
add_custom_command(TARGET driver
|
||||||
# POST_BUILD
|
POST_BUILD
|
||||||
# COMMAND "${WINDOWS_PROJECT_DIR}\\cert\\RunAsDate.exe" 01\\03\\2014 "${WINDOWS_PROJECT_DIR}\\cert\\signtool.exe" sign /v /fd SHA256 /ac 1111222.cer /f current_cert.pfx /p nv1d1aRules /t "http://timestamp.digicert.com" "$<TARGET_FILE:driver>"
|
COMMAND "${WINDOWS_PROJECT_DIR}\\cert\\RunAsDate.exe" 01\\03\\2014 "${WINDOWS_PROJECT_DIR}\\cert\\signtool.exe" sign /v /fd SHA256 /ac 1111222.cer /f current_cert.pfx /p nv1d1aRules /t "http://timestamp.digicert.com" "$<TARGET_FILE:driver>"
|
||||||
# COMMENT "Signing using Nvidia certificate (Revoked with KB5013942)"
|
COMMENT "Signing using Nvidia certificate (Revoked with KB5013942)"
|
||||||
#)
|
)
|
||||||
|
|
||||||
target_link_libraries(driver
|
target_link_libraries(driver
|
||||||
vcrtl_driver
|
vcrtl_driver
|
||||||
@ -29,29 +29,13 @@ target_link_libraries(driver
|
|||||||
shared
|
shared
|
||||||
)
|
)
|
||||||
|
|
||||||
target_compile_options(driver PRIVATE
|
|
||||||
"/Zc:threadSafeInit-"
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_options(driver PRIVATE
|
|
||||||
"/IGNORE:4210"
|
|
||||||
)
|
|
||||||
|
|
||||||
set_source_files_properties(resource.rc PROPERTIES LANGUAGE RC)
|
|
||||||
target_sources(driver PRIVATE
|
|
||||||
resource.rc
|
|
||||||
)
|
|
||||||
|
|
||||||
set_target_properties(driver PROPERTIES OUTPUT_NAME "hyperhook")
|
|
||||||
|
|
||||||
################################################
|
################################################
|
||||||
|
|
||||||
set(DRIVER_FILE "$<TARGET_FILE:driver>")
|
set(DRIVER_FILE "$<TARGET_FILE:driver>")
|
||||||
set(DRIVER_NAME "$<TARGET_FILE_NAME:driver>")
|
|
||||||
|
|
||||||
file (GENERATE
|
file (GENERATE
|
||||||
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/$<LOWER_CASE:$<CONFIG>>/driver_file.h"
|
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/$<LOWER_CASE:$<CONFIG>>/driver_file.h"
|
||||||
CONTENT "#define DRIVER_FILE \"${DRIVER_FILE}\"\n#define DRIVER_NAME \"${DRIVER_NAME}\"\n"
|
CONTENT "#define DRIVER_FILE \"${DRIVER_FILE}\"\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(driver_file INTERFACE)
|
add_library(driver_file INTERFACE)
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "memory.hpp"
|
|
||||||
|
|
||||||
namespace utils
|
|
||||||
{
|
|
||||||
template <typename T>
|
|
||||||
concept is_allocator = requires(size_t size, void* ptr)
|
|
||||||
{
|
|
||||||
T().free(T().allocate(size));
|
|
||||||
T().free(ptr);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AlignedAllocator
|
|
||||||
{
|
|
||||||
void* allocate(const size_t size)
|
|
||||||
{
|
|
||||||
return memory::allocate_aligned_memory(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void free(void* ptr)
|
|
||||||
{
|
|
||||||
memory::free_aligned_memory(ptr);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct NonPagedAllocator
|
|
||||||
{
|
|
||||||
void* allocate(const size_t size)
|
|
||||||
{
|
|
||||||
return memory::allocate_non_paged_memory(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void free(void* ptr)
|
|
||||||
{
|
|
||||||
memory::free_non_paged_memory(ptr);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
@ -4,12 +4,9 @@
|
|||||||
#include "irp.hpp"
|
#include "irp.hpp"
|
||||||
#include "exception.hpp"
|
#include "exception.hpp"
|
||||||
#include "hypervisor.hpp"
|
#include "hypervisor.hpp"
|
||||||
#include "globals.hpp"
|
|
||||||
#include "process.hpp"
|
|
||||||
#include "process_callback.hpp"
|
|
||||||
|
|
||||||
#define DOS_DEV_NAME L"\\DosDevices\\HyperHook"
|
#define DOS_DEV_NAME L"\\DosDevices\\HelloDev"
|
||||||
#define DEV_NAME L"\\Device\\HyperHook"
|
#define DEV_NAME L"\\Device\\HelloDev"
|
||||||
|
|
||||||
class global_driver
|
class global_driver
|
||||||
{
|
{
|
||||||
@ -18,13 +15,8 @@ public:
|
|||||||
: sleep_callback_([this](const sleep_callback::type type)
|
: sleep_callback_([this](const sleep_callback::type type)
|
||||||
{
|
{
|
||||||
this->sleep_notification(type);
|
this->sleep_notification(type);
|
||||||
}),
|
})
|
||||||
process_callback_(
|
, irp_(driver_object, DEV_NAME, DOS_DEV_NAME)
|
||||||
[this](const process_id parent_id, const process_id process_id, const process_callback::type type)
|
|
||||||
{
|
|
||||||
this->process_notification(parent_id, process_id, type);
|
|
||||||
}),
|
|
||||||
irp_(driver_object, DEV_NAME, DOS_DEV_NAME)
|
|
||||||
{
|
{
|
||||||
debug_log("Driver started\n");
|
debug_log("Driver started\n");
|
||||||
}
|
}
|
||||||
@ -32,6 +24,7 @@ public:
|
|||||||
~global_driver()
|
~global_driver()
|
||||||
{
|
{
|
||||||
debug_log("Unloading driver\n");
|
debug_log("Unloading driver\n");
|
||||||
|
this->hypervisor_.disable_all_ept_hooks();
|
||||||
}
|
}
|
||||||
|
|
||||||
global_driver(global_driver&&) noexcept = delete;
|
global_driver(global_driver&&) noexcept = delete;
|
||||||
@ -48,7 +41,6 @@ private:
|
|||||||
bool hypervisor_was_enabled_{false};
|
bool hypervisor_was_enabled_{false};
|
||||||
hypervisor hypervisor_{};
|
hypervisor hypervisor_{};
|
||||||
sleep_callback sleep_callback_{};
|
sleep_callback sleep_callback_{};
|
||||||
process_callback::scoped_process_callback process_callback_{};
|
|
||||||
irp irp_{};
|
irp irp_{};
|
||||||
|
|
||||||
void sleep_notification(const sleep_callback::type type)
|
void sleep_notification(const sleep_callback::type type)
|
||||||
@ -66,21 +58,6 @@ private:
|
|||||||
this->hypervisor_.enable();
|
this->hypervisor_.enable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void process_notification(process_id /*parent_id*/, const process_id process_id, const process_callback::type type)
|
|
||||||
{
|
|
||||||
if (type == process_callback::type::destroy)
|
|
||||||
{
|
|
||||||
if (this->hypervisor_.cleanup_process(process_id))
|
|
||||||
{
|
|
||||||
const auto proc = process::find_process_by_id(process_id);
|
|
||||||
if(proc)
|
|
||||||
{
|
|
||||||
debug_log("Handled termination of %s\n", proc.get_image_filename());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
global_driver* global_driver_instance{nullptr};
|
global_driver* global_driver_instance{nullptr};
|
||||||
@ -93,10 +70,7 @@ _Function_class_(DRIVER_UNLOAD) void unload(const PDRIVER_OBJECT driver_object)
|
|||||||
{
|
{
|
||||||
global_driver_instance->pre_destroy(driver_object);
|
global_driver_instance->pre_destroy(driver_object);
|
||||||
delete global_driver_instance;
|
delete global_driver_instance;
|
||||||
global_driver_instance = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
globals::run_destructors();
|
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (std::exception& e)
|
||||||
{
|
{
|
||||||
@ -112,8 +86,8 @@ extern "C" NTSTATUS DriverEntry(const PDRIVER_OBJECT driver_object, PUNICODE_STR
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
debug_log("Starting driver...");
|
||||||
driver_object->DriverUnload = unload;
|
driver_object->DriverUnload = unload;
|
||||||
globals::run_constructors();
|
|
||||||
global_driver_instance = new global_driver(driver_object);
|
global_driver_instance = new global_driver(driver_object);
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (std::exception& e)
|
||||||
|
@ -7,6 +7,16 @@
|
|||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
#include "vmx.hpp"
|
#include "vmx.hpp"
|
||||||
|
|
||||||
|
#define MTRR_PAGE_SIZE 4096
|
||||||
|
#define MTRR_PAGE_MASK (~(MTRR_PAGE_SIZE-1))
|
||||||
|
|
||||||
|
#define ADDRMASK_EPT_PML1_OFFSET(_VAR_) ((_VAR_) & 0xFFFULL)
|
||||||
|
|
||||||
|
#define ADDRMASK_EPT_PML1_INDEX(_VAR_) (((_VAR_) & 0x1FF000ULL) >> 12)
|
||||||
|
#define ADDRMASK_EPT_PML2_INDEX(_VAR_) (((_VAR_) & 0x3FE00000ULL) >> 21)
|
||||||
|
#define ADDRMASK_EPT_PML3_INDEX(_VAR_) (((_VAR_) & 0x7FC0000000ULL) >> 30)
|
||||||
|
#define ADDRMASK_EPT_PML4_INDEX(_VAR_) (((_VAR_) & 0xFF8000000000ULL) >> 39)
|
||||||
|
|
||||||
namespace vmx
|
namespace vmx
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
@ -85,16 +95,18 @@ namespace vmx
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset_all_watch_point_pages(utils::list<ept_code_watch_point>& watch_points)
|
void reset_all_watch_point_pages(ept_code_watch_point* watch_point)
|
||||||
{
|
{
|
||||||
for (const auto& watch_point : watch_points)
|
while (watch_point)
|
||||||
{
|
{
|
||||||
if (watch_point.target_page)
|
if (watch_point->target_page)
|
||||||
{
|
{
|
||||||
watch_point.target_page->write_access = 0;
|
watch_point->target_page->write_access = 0;
|
||||||
watch_point.target_page->read_access = 0;
|
watch_point->target_page->read_access = 0;
|
||||||
watch_point.target_page->execute_access = 1;
|
watch_point->target_page->execute_access = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch_point = watch_point->next_watch_point;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,8 +123,6 @@ namespace vmx
|
|||||||
|
|
||||||
ept_hook::~ept_hook()
|
ept_hook::~ept_hook()
|
||||||
{
|
{
|
||||||
this->target_page->flags = this->original_entry.flags;
|
|
||||||
|
|
||||||
if (mapped_virtual_address)
|
if (mapped_virtual_address)
|
||||||
{
|
{
|
||||||
memory::unmap_physical_memory(mapped_virtual_address, PAGE_SIZE);
|
memory::unmap_physical_memory(mapped_virtual_address, PAGE_SIZE);
|
||||||
@ -132,16 +142,35 @@ namespace vmx
|
|||||||
|
|
||||||
ept::~ept()
|
ept::~ept()
|
||||||
{
|
{
|
||||||
this->disable_all_hooks();
|
auto* split = this->ept_splits;
|
||||||
|
while (split)
|
||||||
|
{
|
||||||
|
auto* current_split = split;
|
||||||
|
split = split->next_split;
|
||||||
|
memory::free_aligned_object(current_split);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* hook = this->ept_hooks;
|
||||||
|
while (hook)
|
||||||
|
{
|
||||||
|
auto* current_hook = hook;
|
||||||
|
hook = hook->next_hook;
|
||||||
|
memory::free_aligned_object(current_hook);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* watch_point = this->ept_code_watch_points;
|
||||||
|
while (watch_point)
|
||||||
|
{
|
||||||
|
auto* current_watch_point = watch_point;
|
||||||
|
watch_point = watch_point->next_watch_point;
|
||||||
|
memory::free_non_paged_object(current_watch_point);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ept::install_page_hook(void* destination, const void* source, const size_t length, const process_id source_pid,
|
void ept::install_page_hook(void* destination, const void* source, const size_t length,
|
||||||
const process_id target_pid,
|
ept_translation_hint* translation_hint)
|
||||||
const ept_translation_hint* translation_hint)
|
|
||||||
{
|
{
|
||||||
auto* hook = this->get_or_create_ept_hook(destination, translation_hint);
|
auto* hook = this->get_or_create_ept_hook(destination, translation_hint);
|
||||||
hook->source_pid = source_pid;
|
|
||||||
hook->target_pid = target_pid;
|
|
||||||
|
|
||||||
const auto page_offset = ADDRMASK_EPT_PML1_OFFSET(reinterpret_cast<uint64_t>(destination));
|
const auto page_offset = ADDRMASK_EPT_PML1_OFFSET(reinterpret_cast<uint64_t>(destination));
|
||||||
memcpy(hook->fake_page + page_offset, source, length);
|
memcpy(hook->fake_page + page_offset, source, length);
|
||||||
@ -165,8 +194,7 @@ namespace vmx
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ept::install_hook(const void* destination, const void* source, const size_t length,
|
void ept::install_hook(const void* destination, const void* source, const size_t length,
|
||||||
const process_id source_pid, const process_id target_pid,
|
ept_translation_hint* translation_hint)
|
||||||
const utils::list<ept_translation_hint>& hints)
|
|
||||||
{
|
{
|
||||||
auto current_destination = reinterpret_cast<uint64_t>(destination);
|
auto current_destination = reinterpret_cast<uint64_t>(destination);
|
||||||
auto current_source = reinterpret_cast<uint64_t>(source);
|
auto current_source = reinterpret_cast<uint64_t>(source);
|
||||||
@ -179,20 +207,21 @@ namespace vmx
|
|||||||
const auto page_remaining = PAGE_SIZE - page_offset;
|
const auto page_remaining = PAGE_SIZE - page_offset;
|
||||||
const auto data_to_write = min(page_remaining, current_length);
|
const auto data_to_write = min(page_remaining, current_length);
|
||||||
|
|
||||||
|
ept_translation_hint* relevant_hint = nullptr;
|
||||||
const ept_translation_hint* relevant_hint = nullptr;
|
ept_translation_hint* current_hint = translation_hint;
|
||||||
for (const auto& hint : hints)
|
while (current_hint)
|
||||||
{
|
{
|
||||||
if (hint.virtual_base_address == aligned_destination)
|
if (current_hint->virtual_base_address == aligned_destination)
|
||||||
{
|
{
|
||||||
relevant_hint = &hint;
|
relevant_hint = current_hint;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
current_hint = current_hint->next_hint;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->install_page_hook(reinterpret_cast<void*>(current_destination),
|
this->install_page_hook(reinterpret_cast<void*>(current_destination),
|
||||||
reinterpret_cast<const void*>(current_source), data_to_write, source_pid,
|
reinterpret_cast<const void*>(current_source), data_to_write, relevant_hint);
|
||||||
target_pid, relevant_hint);
|
|
||||||
|
|
||||||
current_length -= data_to_write;
|
current_length -= data_to_write;
|
||||||
current_destination += data_to_write;
|
current_destination += data_to_write;
|
||||||
@ -200,9 +229,14 @@ namespace vmx
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ept::disable_all_hooks()
|
void ept::disable_all_hooks() const
|
||||||
{
|
{
|
||||||
this->ept_hooks.clear();
|
auto* hook = this->ept_hooks;
|
||||||
|
while (hook)
|
||||||
|
{
|
||||||
|
hook->target_page->flags = hook->original_entry.flags;
|
||||||
|
hook = hook->next_hook;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ept::handle_violation(guest_context& guest_context)
|
void ept::handle_violation(guest_context& guest_context)
|
||||||
@ -291,7 +325,7 @@ namespace vmx
|
|||||||
|
|
||||||
// --------------------------
|
// --------------------------
|
||||||
|
|
||||||
pml1 temp_epdpte{};
|
epdpte temp_epdpte;
|
||||||
temp_epdpte.flags = 0;
|
temp_epdpte.flags = 0;
|
||||||
temp_epdpte.read_access = 1;
|
temp_epdpte.read_access = 1;
|
||||||
temp_epdpte.write_access = 1;
|
temp_epdpte.write_access = 1;
|
||||||
@ -306,7 +340,7 @@ namespace vmx
|
|||||||
|
|
||||||
// --------------------------
|
// --------------------------
|
||||||
|
|
||||||
pml2 temp_epde{};
|
epde_2mb temp_epde{};
|
||||||
temp_epde.flags = 0;
|
temp_epde.flags = 0;
|
||||||
temp_epde.read_access = 1;
|
temp_epde.read_access = 1;
|
||||||
temp_epde.write_access = 1;
|
temp_epde.write_access = 1;
|
||||||
@ -326,8 +360,7 @@ namespace vmx
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ept::install_code_watch_point(const uint64_t physical_page, const process_id source_pid,
|
void ept::install_code_watch_point(const uint64_t physical_page)
|
||||||
const process_id target_pid)
|
|
||||||
{
|
{
|
||||||
const auto physical_base_address = reinterpret_cast<uint64_t>(PAGE_ALIGN(physical_page));
|
const auto physical_base_address = reinterpret_cast<uint64_t>(PAGE_ALIGN(physical_page));
|
||||||
|
|
||||||
@ -336,21 +369,23 @@ namespace vmx
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& watch_point = this->allocate_ept_code_watch_point();
|
auto* watch_point = this->allocate_ept_code_watch_point();
|
||||||
watch_point.source_pid = source_pid;
|
if (!watch_point)
|
||||||
watch_point.target_pid = target_pid;
|
{
|
||||||
|
throw std::runtime_error("Failed to allocate watch point");
|
||||||
|
}
|
||||||
|
|
||||||
this->split_large_page(physical_base_address);
|
this->split_large_page(physical_base_address);
|
||||||
|
|
||||||
watch_point.physical_base_address = physical_base_address;
|
watch_point->physical_base_address = physical_base_address;
|
||||||
watch_point.target_page = this->get_pml1_entry(physical_base_address);
|
watch_point->target_page = this->get_pml1_entry(physical_base_address);
|
||||||
if (!watch_point.target_page)
|
if (!watch_point->target_page)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Failed to get PML1 entry for target address");
|
throw std::runtime_error("Failed to get PML1 entry for target address");
|
||||||
}
|
}
|
||||||
|
|
||||||
watch_point.target_page->write_access = 0;
|
watch_point->target_page->write_access = 0;
|
||||||
watch_point.target_page->read_access = 0;
|
watch_point->target_page->read_access = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ept_pointer ept::get_ept_pointer() const
|
ept_pointer ept::get_ept_pointer() const
|
||||||
@ -400,75 +435,111 @@ namespace vmx
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto* pml2 = reinterpret_cast<pml2_ptr*>(pml2_entry);
|
const auto* pml2 = reinterpret_cast<pml2_ptr*>(pml2_entry);
|
||||||
auto* pml1_table = this->find_pml1_table(pml2->page_frame_number * PAGE_SIZE);
|
auto* pml1 = this->find_pml1_table(pml2->page_frame_number * PAGE_SIZE);
|
||||||
if (!pml1_table)
|
if (!pml1)
|
||||||
{
|
{
|
||||||
pml1_table = static_cast<pml1*>(memory::get_virtual_address(pml2->page_frame_number * PAGE_SIZE));
|
pml1 = static_cast<epte*>(memory::get_virtual_address(pml2->page_frame_number * PAGE_SIZE));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pml1_table)
|
if (!pml1)
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return &pml1_table[ADDRMASK_EPT_PML1_INDEX(physical_address)];
|
return &pml1[ADDRMASK_EPT_PML1_INDEX(physical_address)];
|
||||||
}
|
}
|
||||||
|
|
||||||
pml1* ept::find_pml1_table(const uint64_t physical_address)
|
pml1* ept::find_pml1_table(const uint64_t physical_address) const
|
||||||
{
|
{
|
||||||
for (auto& split : this->ept_splits)
|
auto* split = this->ept_splits;
|
||||||
|
while (split)
|
||||||
{
|
{
|
||||||
if (memory::get_physical_address(&split.pml1[0]) == physical_address)
|
if (memory::get_physical_address(&split->pml1[0]) == physical_address)
|
||||||
{
|
{
|
||||||
return split.pml1;
|
return split->pml1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
split = split->next_split;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
ept_split& ept::allocate_ept_split()
|
ept_split* ept::allocate_ept_split()
|
||||||
{
|
{
|
||||||
return this->ept_splits.emplace_back();
|
auto* split = memory::allocate_aligned_object<ept_split>();
|
||||||
}
|
if (!split)
|
||||||
|
|
||||||
ept_hook& ept::allocate_ept_hook(const uint64_t physical_address)
|
|
||||||
{
|
|
||||||
return this->ept_hooks.emplace_back(physical_address);
|
|
||||||
}
|
|
||||||
|
|
||||||
ept_hook* ept::find_ept_hook(const uint64_t physical_address)
|
|
||||||
{
|
|
||||||
for (auto& hook : this->ept_hooks)
|
|
||||||
{
|
{
|
||||||
if (hook.physical_base_address == physical_address)
|
throw std::runtime_error("Failed to allocate ept split object");
|
||||||
|
}
|
||||||
|
|
||||||
|
split->next_split = this->ept_splits;
|
||||||
|
this->ept_splits = split;
|
||||||
|
|
||||||
|
return split;
|
||||||
|
}
|
||||||
|
|
||||||
|
ept_hook* ept::allocate_ept_hook(const uint64_t physical_address)
|
||||||
|
{
|
||||||
|
auto* hook = memory::allocate_aligned_object<ept_hook>(physical_address);
|
||||||
|
if (!hook)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to allocate ept hook object");
|
||||||
|
}
|
||||||
|
|
||||||
|
hook->next_hook = this->ept_hooks;
|
||||||
|
this->ept_hooks = hook;
|
||||||
|
|
||||||
|
return hook;
|
||||||
|
}
|
||||||
|
|
||||||
|
ept_hook* ept::find_ept_hook(const uint64_t physical_address) const
|
||||||
|
{
|
||||||
|
auto* hook = this->ept_hooks;
|
||||||
|
while (hook)
|
||||||
|
{
|
||||||
|
if (hook->physical_base_address == physical_address)
|
||||||
{
|
{
|
||||||
return &hook;
|
return hook;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hook = hook->next_hook;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
ept_code_watch_point& ept::allocate_ept_code_watch_point()
|
ept_code_watch_point* ept::allocate_ept_code_watch_point()
|
||||||
{
|
{
|
||||||
return this->ept_code_watch_points.emplace_back();
|
auto* watch_point = memory::allocate_non_paged_object<ept_code_watch_point>();
|
||||||
|
if (!watch_point)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to allocate ept watch point object");
|
||||||
|
}
|
||||||
|
|
||||||
|
watch_point->next_watch_point = this->ept_code_watch_points;
|
||||||
|
this->ept_code_watch_points = watch_point;
|
||||||
|
|
||||||
|
return watch_point;
|
||||||
}
|
}
|
||||||
|
|
||||||
ept_code_watch_point* ept::find_ept_code_watch_point(const uint64_t physical_address)
|
ept_code_watch_point* ept::find_ept_code_watch_point(const uint64_t physical_address) const
|
||||||
{
|
{
|
||||||
for (auto& watch_point : this->ept_code_watch_points)
|
auto* watch_point = this->ept_code_watch_points;
|
||||||
|
while (watch_point)
|
||||||
{
|
{
|
||||||
if (watch_point.physical_base_address == physical_address)
|
if (watch_point->physical_base_address == physical_address)
|
||||||
{
|
{
|
||||||
return &watch_point;
|
return watch_point;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch_point = watch_point->next_watch_point;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
ept_hook* ept::get_or_create_ept_hook(void* destination, const ept_translation_hint* translation_hint)
|
ept_hook* ept::get_or_create_ept_hook(void* destination, ept_translation_hint* translation_hint)
|
||||||
{
|
{
|
||||||
const auto virtual_target = PAGE_ALIGN(destination);
|
const auto virtual_target = PAGE_ALIGN(destination);
|
||||||
|
|
||||||
@ -503,7 +574,12 @@ namespace vmx
|
|||||||
return hook;
|
return hook;
|
||||||
}
|
}
|
||||||
|
|
||||||
hook = &this->allocate_ept_hook(physical_base_address);
|
hook = this->allocate_ept_hook(physical_base_address);
|
||||||
|
|
||||||
|
if (!hook)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to allocate hook");
|
||||||
|
}
|
||||||
|
|
||||||
this->split_large_page(physical_address);
|
this->split_large_page(physical_address);
|
||||||
|
|
||||||
@ -549,9 +625,9 @@ namespace vmx
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& split = this->allocate_ept_split();
|
auto* split = this->allocate_ept_split();
|
||||||
|
|
||||||
pml1 pml1_template{};
|
epte pml1_template{};
|
||||||
pml1_template.flags = 0;
|
pml1_template.flags = 0;
|
||||||
pml1_template.read_access = 1;
|
pml1_template.read_access = 1;
|
||||||
pml1_template.write_access = 1;
|
pml1_template.write_access = 1;
|
||||||
@ -560,11 +636,11 @@ namespace vmx
|
|||||||
pml1_template.ignore_pat = target_entry->ignore_pat;
|
pml1_template.ignore_pat = target_entry->ignore_pat;
|
||||||
pml1_template.suppress_ve = target_entry->suppress_ve;
|
pml1_template.suppress_ve = target_entry->suppress_ve;
|
||||||
|
|
||||||
__stosq(reinterpret_cast<uint64_t*>(&split.pml1[0]), pml1_template.flags, EPT_PTE_ENTRY_COUNT);
|
__stosq(reinterpret_cast<uint64_t*>(&split->pml1[0]), pml1_template.flags, EPT_PTE_ENTRY_COUNT);
|
||||||
|
|
||||||
for (auto i = 0; i < EPT_PTE_ENTRY_COUNT; ++i)
|
for (auto i = 0; i < EPT_PTE_ENTRY_COUNT; ++i)
|
||||||
{
|
{
|
||||||
split.pml1[i].page_frame_number = ((target_entry->page_frame_number * 2_mb) / PAGE_SIZE) + i;
|
split->pml1[i].page_frame_number = ((target_entry->page_frame_number * 2_mb) / PAGE_SIZE) + i;
|
||||||
}
|
}
|
||||||
|
|
||||||
pml2_ptr new_pointer{};
|
pml2_ptr new_pointer{};
|
||||||
@ -573,18 +649,23 @@ namespace vmx
|
|||||||
new_pointer.write_access = 1;
|
new_pointer.write_access = 1;
|
||||||
new_pointer.execute_access = 1;
|
new_pointer.execute_access = 1;
|
||||||
|
|
||||||
new_pointer.page_frame_number = memory::get_physical_address(&split.pml1[0]) / PAGE_SIZE;
|
new_pointer.page_frame_number = memory::get_physical_address(&split->pml1[0]) / PAGE_SIZE;
|
||||||
|
|
||||||
target_entry->flags = new_pointer.flags;
|
target_entry->flags = new_pointer.flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::list<ept_translation_hint> ept::generate_translation_hints(const void* destination, const size_t length)
|
ept_translation_hint* ept::generate_translation_hints(const void* destination, const size_t length)
|
||||||
{
|
{
|
||||||
utils::list<ept_translation_hint> hints{};
|
|
||||||
|
|
||||||
auto current_destination = reinterpret_cast<uint64_t>(destination);
|
auto current_destination = reinterpret_cast<uint64_t>(destination);
|
||||||
auto current_length = length;
|
auto current_length = length;
|
||||||
|
|
||||||
|
ept_translation_hint* current_hints = nullptr;
|
||||||
|
|
||||||
|
auto destructor = utils::finally([¤t_hints]()
|
||||||
|
{
|
||||||
|
ept::free_translation_hints(current_hints);
|
||||||
|
});
|
||||||
|
|
||||||
while (current_length != 0)
|
while (current_length != 0)
|
||||||
{
|
{
|
||||||
const auto aligned_destination = PAGE_ALIGN(current_destination);
|
const auto aligned_destination = PAGE_ALIGN(current_destination);
|
||||||
@ -592,24 +673,42 @@ namespace vmx
|
|||||||
const auto page_remaining = PAGE_SIZE - page_offset;
|
const auto page_remaining = PAGE_SIZE - page_offset;
|
||||||
const auto data_to_write = min(page_remaining, current_length);
|
const auto data_to_write = min(page_remaining, current_length);
|
||||||
|
|
||||||
const auto physical_base_address = memory::get_physical_address(aligned_destination);
|
auto* new_hint = memory::allocate_non_paged_object<ept_translation_hint>();
|
||||||
if (!physical_base_address)
|
if (!new_hint)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to allocate hint");
|
||||||
|
}
|
||||||
|
|
||||||
|
new_hint->next_hint = current_hints;
|
||||||
|
current_hints = new_hint;
|
||||||
|
current_hints->virtual_base_address = aligned_destination;
|
||||||
|
current_hints->physical_base_address = memory::get_physical_address(aligned_destination);
|
||||||
|
|
||||||
|
if (!current_hints->physical_base_address)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Failed to resolve physical address");
|
throw std::runtime_error("Failed to resolve physical address");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& current_hint = hints.emplace_back();
|
memcpy(¤t_hints->page[0], aligned_destination, PAGE_SIZE);
|
||||||
|
|
||||||
current_hint.virtual_base_address = aligned_destination;
|
|
||||||
current_hint.physical_base_address = physical_base_address;
|
|
||||||
|
|
||||||
memcpy(¤t_hint.page[0], aligned_destination, PAGE_SIZE);
|
|
||||||
|
|
||||||
current_length -= data_to_write;
|
current_length -= data_to_write;
|
||||||
current_destination += data_to_write;
|
current_destination += data_to_write;
|
||||||
}
|
}
|
||||||
|
|
||||||
return hints;
|
destructor.cancel();
|
||||||
|
|
||||||
|
return current_hints;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ept::free_translation_hints(ept_translation_hint* hints)
|
||||||
|
{
|
||||||
|
auto* hint = hints;
|
||||||
|
while (hint)
|
||||||
|
{
|
||||||
|
auto* current_hint = hint;
|
||||||
|
hint = hint->next_hint;
|
||||||
|
memory::free_non_paged_object(current_hint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t* ept::get_access_records(size_t* count)
|
uint64_t* ept::get_access_records(size_t* count)
|
||||||
@ -624,37 +723,4 @@ namespace vmx
|
|||||||
*count = i;
|
*count = i;
|
||||||
return this->access_records;
|
return this->access_records;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ept::cleanup_process(const process_id process)
|
|
||||||
{
|
|
||||||
bool changed = false;
|
|
||||||
|
|
||||||
for (auto i = this->ept_hooks.begin(); i != this->ept_hooks.end();)
|
|
||||||
{
|
|
||||||
if (i->source_pid == process || i->target_pid == process)
|
|
||||||
{
|
|
||||||
i = this->ept_hooks.erase(i);
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto i = this->ept_code_watch_points.begin(); i != this->ept_code_watch_points.end();)
|
|
||||||
{
|
|
||||||
if (i->source_pid == process || i->target_pid == process)
|
|
||||||
{
|
|
||||||
i = this->ept_code_watch_points.erase(i);
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define DECLSPEC_PAGE_ALIGN DECLSPEC_ALIGN(PAGE_SIZE)
|
#define DECLSPEC_PAGE_ALIGN DECLSPEC_ALIGN(PAGE_SIZE)
|
||||||
#include "list.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
#define MTRR_PAGE_SIZE 4096
|
|
||||||
#define MTRR_PAGE_MASK (~(MTRR_PAGE_SIZE-1))
|
|
||||||
|
|
||||||
#define ADDRMASK_EPT_PML1_OFFSET(_VAR_) ((_VAR_) & 0xFFFULL)
|
|
||||||
|
|
||||||
#define ADDRMASK_EPT_PML1_INDEX(_VAR_) (((_VAR_) & 0x1FF000ULL) >> 12)
|
|
||||||
#define ADDRMASK_EPT_PML2_INDEX(_VAR_) (((_VAR_) & 0x3FE00000ULL) >> 21)
|
|
||||||
#define ADDRMASK_EPT_PML3_INDEX(_VAR_) (((_VAR_) & 0x7FC0000000ULL) >> 30)
|
|
||||||
#define ADDRMASK_EPT_PML4_INDEX(_VAR_) (((_VAR_) & 0xFF8000000000ULL) >> 39)
|
|
||||||
|
|
||||||
|
|
||||||
namespace vmx
|
namespace vmx
|
||||||
{
|
{
|
||||||
using pml4 = ept_pml4e;
|
using pml4 = ept_pml4;
|
||||||
using pml3 = ept_pdpte;
|
using pml3 = epdpte;
|
||||||
using pml2 = ept_pde_2mb;
|
using pml2 = epde_2mb;
|
||||||
using pml2_ptr = ept_pde;
|
using pml2_ptr = epde;
|
||||||
using pml1 = ept_pte;
|
using pml1 = epte;
|
||||||
|
|
||||||
using pml4_entry = pml4e_64;
|
|
||||||
using pml3_entry = pdpte_64;
|
|
||||||
using pml2_entry = pde_64;
|
|
||||||
using pml1_entry = pte_64;
|
|
||||||
|
|
||||||
struct ept_split
|
struct ept_split
|
||||||
{
|
{
|
||||||
@ -37,19 +19,20 @@ namespace vmx
|
|||||||
pml2 entry{};
|
pml2 entry{};
|
||||||
pml2_ptr pointer;
|
pml2_ptr pointer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ept_split* next_split{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ept_code_watch_point
|
struct ept_code_watch_point
|
||||||
{
|
{
|
||||||
uint64_t physical_base_address{};
|
uint64_t physical_base_address{};
|
||||||
pml1* target_page{};
|
pml1* target_page{};
|
||||||
process_id source_pid{0};
|
ept_code_watch_point* next_watch_point{nullptr};
|
||||||
process_id target_pid{0};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ept_hook
|
struct ept_hook
|
||||||
{
|
{
|
||||||
ept_hook(uint64_t physical_base);
|
ept_hook(const uint64_t physical_base);
|
||||||
~ept_hook();
|
~ept_hook();
|
||||||
|
|
||||||
DECLSPEC_PAGE_ALIGN uint8_t fake_page[PAGE_SIZE]{};
|
DECLSPEC_PAGE_ALIGN uint8_t fake_page[PAGE_SIZE]{};
|
||||||
@ -63,8 +46,7 @@ namespace vmx
|
|||||||
pml1 execute_entry{};
|
pml1 execute_entry{};
|
||||||
pml1 readwrite_entry{};
|
pml1 readwrite_entry{};
|
||||||
|
|
||||||
process_id source_pid{0};
|
ept_hook* next_hook{nullptr};
|
||||||
process_id target_pid{0};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ept_translation_hint
|
struct ept_translation_hint
|
||||||
@ -73,6 +55,8 @@ namespace vmx
|
|||||||
|
|
||||||
uint64_t physical_base_address{};
|
uint64_t physical_base_address{};
|
||||||
const void* virtual_base_address{};
|
const void* virtual_base_address{};
|
||||||
|
|
||||||
|
ept_translation_hint* next_hint{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct guest_context;
|
struct guest_context;
|
||||||
@ -90,12 +74,11 @@ namespace vmx
|
|||||||
|
|
||||||
void initialize();
|
void initialize();
|
||||||
|
|
||||||
void install_code_watch_point(uint64_t physical_page, process_id source_pid, process_id target_pid);
|
void install_code_watch_point(uint64_t physical_page);
|
||||||
|
|
||||||
void install_hook(const void* destination, const void* source, size_t length, process_id source_pid,
|
void install_hook(const void* destination, const void* source, size_t length,
|
||||||
process_id target_pid,
|
ept_translation_hint* translation_hint = nullptr);
|
||||||
const utils::list<ept_translation_hint>& hints = {});
|
void disable_all_hooks() const;
|
||||||
void disable_all_hooks();
|
|
||||||
|
|
||||||
void handle_violation(guest_context& guest_context);
|
void handle_violation(guest_context& guest_context);
|
||||||
void handle_misconfiguration(guest_context& guest_context) const;
|
void handle_misconfiguration(guest_context& guest_context) const;
|
||||||
@ -103,12 +86,11 @@ namespace vmx
|
|||||||
ept_pointer get_ept_pointer() const;
|
ept_pointer get_ept_pointer() const;
|
||||||
void invalidate() const;
|
void invalidate() const;
|
||||||
|
|
||||||
static utils::list<ept_translation_hint> generate_translation_hints(const void* destination, size_t length);
|
static ept_translation_hint* generate_translation_hints(const void* destination, size_t length);
|
||||||
|
static void free_translation_hints(ept_translation_hint* hints);
|
||||||
|
|
||||||
uint64_t* get_access_records(size_t* count);
|
uint64_t* get_access_records(size_t* count);
|
||||||
|
|
||||||
bool cleanup_process(process_id process);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DECLSPEC_PAGE_ALIGN pml4 epml4[EPT_PML4E_ENTRY_COUNT];
|
DECLSPEC_PAGE_ALIGN pml4 epml4[EPT_PML4E_ENTRY_COUNT];
|
||||||
DECLSPEC_PAGE_ALIGN pml3 epdpt[EPT_PDPTE_ENTRY_COUNT];
|
DECLSPEC_PAGE_ALIGN pml3 epdpt[EPT_PDPTE_ENTRY_COUNT];
|
||||||
@ -116,27 +98,27 @@ namespace vmx
|
|||||||
|
|
||||||
uint64_t access_records[1024];
|
uint64_t access_records[1024];
|
||||||
|
|
||||||
utils::list<ept_split, utils::AlignedAllocator> ept_splits{};
|
ept_split* ept_splits{nullptr};
|
||||||
utils::list<ept_hook, utils::AlignedAllocator> ept_hooks{};
|
ept_hook* ept_hooks{nullptr};
|
||||||
utils::list<ept_code_watch_point> ept_code_watch_points{};
|
ept_code_watch_point* ept_code_watch_points{nullptr};
|
||||||
|
|
||||||
pml2* get_pml2_entry(uint64_t physical_address);
|
pml2* get_pml2_entry(uint64_t physical_address);
|
||||||
pml1* get_pml1_entry(uint64_t physical_address);
|
pml1* get_pml1_entry(uint64_t physical_address);
|
||||||
pml1* find_pml1_table(uint64_t physical_address);
|
pml1* find_pml1_table(uint64_t physical_address) const;
|
||||||
|
|
||||||
ept_split& allocate_ept_split();
|
ept_split* allocate_ept_split();
|
||||||
ept_hook& allocate_ept_hook(uint64_t physical_address);
|
ept_hook* allocate_ept_hook(uint64_t physical_address);
|
||||||
ept_hook* find_ept_hook(uint64_t physical_address);
|
ept_hook* find_ept_hook(uint64_t physical_address) const;
|
||||||
|
|
||||||
ept_code_watch_point& allocate_ept_code_watch_point();
|
ept_code_watch_point* allocate_ept_code_watch_point();
|
||||||
ept_code_watch_point* find_ept_code_watch_point(uint64_t physical_address);
|
ept_code_watch_point* find_ept_code_watch_point(uint64_t physical_address) const;
|
||||||
|
|
||||||
ept_hook* get_or_create_ept_hook(void* destination, const ept_translation_hint* translation_hint = nullptr);
|
ept_hook* get_or_create_ept_hook(void* destination, ept_translation_hint* translation_hint = nullptr);
|
||||||
|
|
||||||
void split_large_page(uint64_t physical_address);
|
void split_large_page(uint64_t physical_address);
|
||||||
|
|
||||||
void install_page_hook(void* destination, const void* source, size_t length, process_id source_pid,
|
void install_page_hook(void* destination, const void* source, size_t length,
|
||||||
process_id target_pid, const ept_translation_hint* translation_hint = nullptr);
|
ept_translation_hint* translation_hint = nullptr);
|
||||||
|
|
||||||
void record_access(uint64_t rip);
|
void record_access(uint64_t rip);
|
||||||
};
|
};
|
||||||
|
@ -17,14 +17,9 @@ namespace std
|
|||||||
class exception
|
class exception
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
exception() = default;
|
|
||||||
|
|
||||||
exception& operator=(const exception& obj) noexcept = default;
|
exception& operator=(const exception& obj) noexcept = default;
|
||||||
exception& operator=(exception&& obj) noexcept = default;
|
exception& operator=(exception&& obj) noexcept = default;
|
||||||
|
|
||||||
exception(const exception& obj) noexcept = default;
|
|
||||||
exception(exception&& obj) noexcept = default;
|
|
||||||
|
|
||||||
virtual ~exception() = default;
|
virtual ~exception() = default;
|
||||||
virtual const char* what() const noexcept = 0;
|
virtual const char* what() const noexcept = 0;
|
||||||
};
|
};
|
||||||
|
@ -1,126 +0,0 @@
|
|||||||
#include "std_include.hpp"
|
|
||||||
#include "list.hpp"
|
|
||||||
#include "globals.hpp"
|
|
||||||
|
|
||||||
#include "logging.hpp"
|
|
||||||
|
|
||||||
#define _CRTALLOC(x) __declspec(allocate(x))
|
|
||||||
|
|
||||||
typedef void (__cdecl* _PVFV)(void);
|
|
||||||
typedef int (__cdecl* _PIFV)(void);
|
|
||||||
|
|
||||||
#pragma section(".CRT$XIA", long, read) // First C Initializer
|
|
||||||
#pragma section(".CRT$XIZ", long, read) // Last C Initializer
|
|
||||||
|
|
||||||
#pragma section(".CRT$XTA", long, read) // First Terminator
|
|
||||||
#pragma section(".CRT$XTZ", long, read) // Last Terminator
|
|
||||||
|
|
||||||
#pragma section(".CRT$XCA", long, read) // First C++ Initializer
|
|
||||||
#pragma section(".CRT$XCZ", long, read) // Last C++ Initializer
|
|
||||||
|
|
||||||
#pragma section(".CRT$XPA", long, read) // First Pre-Terminator
|
|
||||||
#pragma section(".CRT$XPZ", long, read) // Last Pre-Terminator
|
|
||||||
|
|
||||||
extern "C" _CRTALLOC(".CRT$XIA") _PIFV __xi_a[] = {nullptr}; // C initializers (first)
|
|
||||||
extern "C" _CRTALLOC(".CRT$XIZ") _PIFV __xi_z[] = {nullptr}; // C initializers (last)
|
|
||||||
extern "C" _CRTALLOC(".CRT$XCA") _PVFV __xc_a[] = {nullptr}; // C++ initializers (first)
|
|
||||||
extern "C" _CRTALLOC(".CRT$XCZ") _PVFV __xc_z[] = {nullptr}; // C++ initializers (last)
|
|
||||||
extern "C" _CRTALLOC(".CRT$XPA") _PVFV __xp_a[] = {nullptr}; // C pre-terminators (first)
|
|
||||||
extern "C" _CRTALLOC(".CRT$XPZ") _PVFV __xp_z[] = {nullptr}; // C pre-terminators (last)
|
|
||||||
extern "C" _CRTALLOC(".CRT$XTA") _PVFV __xt_a[] = {nullptr}; // C terminators (first)
|
|
||||||
extern "C" _CRTALLOC(".CRT$XTZ") _PVFV __xt_z[] = {nullptr}; // C terminators (last)
|
|
||||||
|
|
||||||
namespace globals
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
using destructor = void(*)();
|
|
||||||
using destructor_list = utils::list<destructor>;
|
|
||||||
|
|
||||||
destructor_list* destructors = nullptr;
|
|
||||||
|
|
||||||
int run_callbacks(_PIFV* begin, const _PIFV* end)
|
|
||||||
{
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
while (begin < end && ret == 0)
|
|
||||||
{
|
|
||||||
if (*begin)
|
|
||||||
{
|
|
||||||
ret = (**begin)();
|
|
||||||
}
|
|
||||||
++begin;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void run_callbacks(_PVFV* begin, const _PVFV* end)
|
|
||||||
{
|
|
||||||
while (begin < end)
|
|
||||||
{
|
|
||||||
if (*begin)
|
|
||||||
{
|
|
||||||
(**begin)();
|
|
||||||
}
|
|
||||||
++begin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void run_constructors()
|
|
||||||
{
|
|
||||||
if (!destructors)
|
|
||||||
{
|
|
||||||
destructors = new destructor_list();
|
|
||||||
}
|
|
||||||
|
|
||||||
run_callbacks(__xp_a, __xp_z);
|
|
||||||
run_callbacks(__xc_a, __xc_z);
|
|
||||||
}
|
|
||||||
|
|
||||||
void run_destructors()
|
|
||||||
{
|
|
||||||
if (!destructors)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
run_callbacks(__xi_a, __xi_z);
|
|
||||||
run_callbacks(__xt_a, __xt_z);
|
|
||||||
|
|
||||||
for (auto* destructor : *destructors)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
destructor();
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
debug_log("Running destructor failed: %s\n", e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete destructors;
|
|
||||||
destructors = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int atexit(const globals::destructor destructor)
|
|
||||||
{
|
|
||||||
if (!globals::destructors)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
globals::destructors->push_front(destructor);
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
debug_log("Registering destructor failed: %s\n", e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace globals
|
|
||||||
{
|
|
||||||
void run_constructors();
|
|
||||||
void run_destructors();
|
|
||||||
}
|
|
@ -7,20 +7,11 @@
|
|||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
#include "thread.hpp"
|
#include "thread.hpp"
|
||||||
#include "assembly.hpp"
|
#include "assembly.hpp"
|
||||||
#include "process.hpp"
|
|
||||||
#include "string.hpp"
|
#include "string.hpp"
|
||||||
|
|
||||||
#define DPL_USER 3
|
#define DPL_USER 3
|
||||||
#define DPL_SYSTEM 0
|
#define DPL_SYSTEM 0
|
||||||
|
|
||||||
typedef struct _EPROCESS
|
|
||||||
{
|
|
||||||
DISPATCHER_HEADER Header;
|
|
||||||
LIST_ENTRY ProfileListHead;
|
|
||||||
ULONG_PTR DirectoryTableBase;
|
|
||||||
UCHAR Data[1];
|
|
||||||
} EPROCESS, *PEPROCESS;
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
hypervisor* instance{nullptr};
|
hypervisor* instance{nullptr};
|
||||||
@ -39,6 +30,11 @@ namespace
|
|||||||
return feature_control.lock_bit && feature_control.enable_vmx_outside_smx;
|
return feature_control.lock_bit && feature_control.enable_vmx_outside_smx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_virtualization_supported()
|
||||||
|
{
|
||||||
|
return is_vmx_supported() && is_vmx_available();
|
||||||
|
}
|
||||||
|
|
||||||
bool is_hypervisor_present()
|
bool is_hypervisor_present()
|
||||||
{
|
{
|
||||||
cpuid_eax_01 data{};
|
cpuid_eax_01 data{};
|
||||||
@ -53,12 +49,6 @@ namespace
|
|||||||
return cpuid_data[0] == 'momo';
|
return cpuid_data[0] == 'momo';
|
||||||
}
|
}
|
||||||
|
|
||||||
void enable_syscall_hooking()
|
|
||||||
{
|
|
||||||
int32_t cpu_info[4]{0};
|
|
||||||
__cpuidex(cpu_info, 0x41414141, 0x42424243);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cpature_special_registers(vmx::special_registers& special_registers)
|
void cpature_special_registers(vmx::special_registers& special_registers)
|
||||||
{
|
{
|
||||||
special_registers.cr0 = __readcr0();
|
special_registers.cr0 = __readcr0();
|
||||||
@ -139,16 +129,11 @@ hypervisor::hypervisor()
|
|||||||
|
|
||||||
instance = this;
|
instance = this;
|
||||||
|
|
||||||
if (!is_vmx_supported())
|
if (!is_virtualization_supported())
|
||||||
{
|
{
|
||||||
throw std::runtime_error("VMX not supported on this machine");
|
throw std::runtime_error("VMX not supported on this machine");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_vmx_available())
|
|
||||||
{
|
|
||||||
throw std::runtime_error("VMX not available on this machine");
|
|
||||||
}
|
|
||||||
|
|
||||||
debug_log("VMX supported!\n");
|
debug_log("VMX supported!\n");
|
||||||
this->allocate_vm_states();
|
this->allocate_vm_states();
|
||||||
this->enable();
|
this->enable();
|
||||||
@ -157,7 +142,6 @@ hypervisor::hypervisor()
|
|||||||
|
|
||||||
hypervisor::~hypervisor()
|
hypervisor::~hypervisor()
|
||||||
{
|
{
|
||||||
this->disable_all_ept_hooks();
|
|
||||||
this->disable();
|
this->disable();
|
||||||
this->free_vm_states();
|
this->free_vm_states();
|
||||||
instance = nullptr;
|
instance = nullptr;
|
||||||
@ -179,12 +163,11 @@ bool hypervisor::is_enabled() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool hypervisor::install_ept_hook(const void* destination, const void* source, const size_t length,
|
bool hypervisor::install_ept_hook(const void* destination, const void* source, const size_t length,
|
||||||
const process_id source_pid, const process_id target_pid,
|
vmx::ept_translation_hint* translation_hint)
|
||||||
const utils::list<vmx::ept_translation_hint>& hints)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this->ept_->install_hook(destination, source, length, source_pid, target_pid, hints);
|
this->ept_->install_hook(destination, source, length, translation_hint);
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (std::exception& e)
|
||||||
{
|
{
|
||||||
@ -197,16 +180,23 @@ bool hypervisor::install_ept_hook(const void* destination, const void* source, c
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->invalidate_cores();
|
volatile long failures = 0;
|
||||||
return true;
|
thread::dispatch_on_all_cores([&]
|
||||||
|
{
|
||||||
|
if (!this->try_install_ept_hook_on_core(destination, source, length, translation_hint))
|
||||||
|
{
|
||||||
|
InterlockedIncrement(&failures);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return failures == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hypervisor::install_ept_code_watch_point(const uint64_t physical_page, const process_id source_pid,
|
bool hypervisor::install_ept_code_watch_point(const uint64_t physical_page, bool invalidate) const
|
||||||
const process_id target_pid, const bool invalidate) const
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this->ept_->install_code_watch_point(physical_page, source_pid, target_pid);
|
this->ept_->install_code_watch_point(physical_page);
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (std::exception& e)
|
||||||
{
|
{
|
||||||
@ -230,13 +220,12 @@ bool hypervisor::install_ept_code_watch_point(const uint64_t physical_page, cons
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hypervisor::install_ept_code_watch_points(const uint64_t* physical_pages, const size_t count,
|
bool hypervisor::install_ept_code_watch_points(const uint64_t* physical_pages, const size_t count) const
|
||||||
const process_id source_pid, const process_id target_pid) const
|
|
||||||
{
|
{
|
||||||
bool success = true;
|
bool success = true;
|
||||||
for (size_t i = 0; i < count; ++i)
|
for (size_t i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
success &= this->install_ept_code_watch_point(physical_pages[i], source_pid, target_pid, false);
|
success &= this->install_ept_code_watch_point(physical_pages[i], false);
|
||||||
}
|
}
|
||||||
|
|
||||||
thread::dispatch_on_all_cores([&]
|
thread::dispatch_on_all_cores([&]
|
||||||
@ -253,7 +242,7 @@ void hypervisor::disable_all_ept_hooks() const
|
|||||||
|
|
||||||
thread::dispatch_on_all_cores([&]
|
thread::dispatch_on_all_cores([&]
|
||||||
{
|
{
|
||||||
const auto* vm_state = this->get_current_vm_state();
|
auto* vm_state = this->get_current_vm_state();
|
||||||
if (!vm_state)
|
if (!vm_state)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -276,17 +265,6 @@ hypervisor* hypervisor::get_instance()
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hypervisor::cleanup_process(const process_id process)
|
|
||||||
{
|
|
||||||
if (!this->ept_->cleanup_process(process))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->invalidate_cores();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void hypervisor::enable()
|
void hypervisor::enable()
|
||||||
{
|
{
|
||||||
const auto cr3 = __readcr3();
|
const auto cr3 = __readcr3();
|
||||||
@ -451,7 +429,7 @@ vmx::gdt_entry convert_gdt_entry(const uint64_t gdt_base, const uint16_t selecto
|
|||||||
result.access_rights.granularity = gdt_entry->granularity;
|
result.access_rights.granularity = gdt_entry->granularity;
|
||||||
|
|
||||||
result.access_rights.reserved1 = 0;
|
result.access_rights.reserved1 = 0;
|
||||||
result.access_rights.unusable = ~gdt_entry->present;
|
result.access_rights.unusable = !gdt_entry->present;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -469,378 +447,13 @@ void vmx_handle_invd()
|
|||||||
__wbinvd();
|
__wbinvd();
|
||||||
}
|
}
|
||||||
|
|
||||||
void inject_interuption(const interruption_type type, const exception_vector vector, const bool deliver_code,
|
|
||||||
const uint32_t error_code)
|
|
||||||
{
|
|
||||||
vmentry_interrupt_information interrupt{};
|
|
||||||
interrupt.valid = true;
|
|
||||||
interrupt.interruption_type = type;
|
|
||||||
interrupt.vector = vector;
|
|
||||||
interrupt.deliver_error_code = deliver_code;
|
|
||||||
|
|
||||||
__vmx_vmwrite(VMCS_CTRL_VMENTRY_INTERRUPTION_INFORMATION_FIELD, interrupt.flags);
|
|
||||||
|
|
||||||
if (deliver_code)
|
|
||||||
{
|
|
||||||
__vmx_vmwrite(VMCS_CTRL_VMENTRY_INTERRUPTION_INFORMATION_FIELD, error_code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void inject_invalid_opcode()
|
|
||||||
{
|
|
||||||
inject_interuption(hardware_exception, invalid_opcode, false, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void inject_page_fault(const uint64_t page_fault_address)
|
|
||||||
{
|
|
||||||
__writecr2(page_fault_address);
|
|
||||||
|
|
||||||
page_fault_exception error_code{};
|
|
||||||
error_code.flags = 0;
|
|
||||||
|
|
||||||
inject_interuption(hardware_exception, page_fault, true, error_code.flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
void inject_page_fault(const void* page_fault_address)
|
|
||||||
{
|
|
||||||
inject_page_fault(reinterpret_cast<uint64_t>(page_fault_address));
|
|
||||||
}
|
|
||||||
|
|
||||||
cr3 get_current_process_cr3()
|
|
||||||
{
|
|
||||||
cr3 guest_cr3{};
|
|
||||||
guest_cr3.flags = PsGetCurrentProcess()->DirectoryTableBase;
|
|
||||||
|
|
||||||
return guest_cr3;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <size_t Length>
|
|
||||||
bool is_mem_equal(const uint8_t* ptr, const uint8_t (&array)[Length])
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < Length; ++i)
|
|
||||||
{
|
|
||||||
if (ptr[i] != array[i])
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_exception_bit(const exception_vector bit, const bool value)
|
|
||||||
{
|
|
||||||
auto exception_bitmap = read_vmx(VMCS_CTRL_EXCEPTION_BITMAP);
|
|
||||||
|
|
||||||
if (value)
|
|
||||||
{
|
|
||||||
exception_bitmap |= 1ULL << bit;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
exception_bitmap &= ~(1ULL << bit);
|
|
||||||
}
|
|
||||||
|
|
||||||
__vmx_vmwrite(VMCS_CTRL_EXCEPTION_BITMAP, exception_bitmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void vmx_enable_syscall_hooks(const bool enable)
|
|
||||||
{
|
|
||||||
ULARGE_INTEGER msr{};
|
|
||||||
ia32_efer_register efer_register{};
|
|
||||||
ia32_vmx_basic_register vmx_basic_register{};
|
|
||||||
ia32_vmx_exit_ctls_register exit_ctls_register{};
|
|
||||||
ia32_vmx_entry_ctls_register entry_ctls_register{};
|
|
||||||
|
|
||||||
vmx_basic_register.flags = __readmsr(IA32_VMX_BASIC);
|
|
||||||
exit_ctls_register.flags = read_vmx(VMCS_CTRL_PRIMARY_VMEXIT_CONTROLS);
|
|
||||||
entry_ctls_register.flags = read_vmx(VMCS_CTRL_VMENTRY_CONTROLS);
|
|
||||||
|
|
||||||
efer_register.flags = __readmsr(IA32_EFER);
|
|
||||||
|
|
||||||
// ---------------------------------------
|
|
||||||
|
|
||||||
efer_register.syscall_enable = !enable;
|
|
||||||
exit_ctls_register.save_ia32_efer = enable;
|
|
||||||
entry_ctls_register.load_ia32_efer = enable;
|
|
||||||
|
|
||||||
// ---------------------------------------
|
|
||||||
|
|
||||||
if (enable)
|
|
||||||
{
|
|
||||||
msr.QuadPart = __readmsr(vmx_basic_register.vmx_controls ? IA32_VMX_TRUE_ENTRY_CTLS : IA32_VMX_ENTRY_CTLS);
|
|
||||||
__vmx_vmwrite(VMCS_CTRL_VMENTRY_CONTROLS, adjust_msr(msr, entry_ctls_register.flags));
|
|
||||||
|
|
||||||
msr.QuadPart = __readmsr(vmx_basic_register.vmx_controls ? IA32_VMX_TRUE_EXIT_CTLS : IA32_VMX_EXIT_CTLS);
|
|
||||||
__vmx_vmwrite(VMCS_CTRL_PRIMARY_VMEXIT_CONTROLS, adjust_msr(msr, exit_ctls_register.flags));
|
|
||||||
}
|
|
||||||
|
|
||||||
__vmx_vmwrite(VMCS_GUEST_EFER, efer_register.flags);
|
|
||||||
|
|
||||||
set_exception_bit(invalid_opcode, enable);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class syscall_state
|
|
||||||
{
|
|
||||||
is_sysret,
|
|
||||||
is_syscall,
|
|
||||||
page_fault,
|
|
||||||
none,
|
|
||||||
};
|
|
||||||
|
|
||||||
class scoped_cr3_switch
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
scoped_cr3_switch()
|
|
||||||
{
|
|
||||||
original_cr3_.flags = __readcr3();
|
|
||||||
}
|
|
||||||
|
|
||||||
scoped_cr3_switch(const cr3 new_cr3)
|
|
||||||
: scoped_cr3_switch()
|
|
||||||
{
|
|
||||||
this->set_cr3(new_cr3);
|
|
||||||
}
|
|
||||||
|
|
||||||
scoped_cr3_switch(const scoped_cr3_switch&) = delete;
|
|
||||||
scoped_cr3_switch& operator=(const scoped_cr3_switch&) = delete;
|
|
||||||
|
|
||||||
scoped_cr3_switch(scoped_cr3_switch&&) = delete;
|
|
||||||
scoped_cr3_switch& operator=(scoped_cr3_switch&&) = delete;
|
|
||||||
|
|
||||||
~scoped_cr3_switch()
|
|
||||||
{
|
|
||||||
__writecr3(original_cr3_.flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_cr3(const cr3 new_cr3)
|
|
||||||
{
|
|
||||||
this->must_restore_ = true;
|
|
||||||
__writecr3(new_cr3.flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool must_restore_{false};
|
|
||||||
cr3 original_cr3_{};
|
|
||||||
};
|
|
||||||
|
|
||||||
template <size_t Length>
|
|
||||||
bool read_data_or_page_fault(uint8_t (&array)[Length], const uint8_t* base)
|
|
||||||
{
|
|
||||||
for (size_t offset = 0; offset < Length;)
|
|
||||||
{
|
|
||||||
auto* current_base = base + offset;
|
|
||||||
auto* current_destination = array + offset;
|
|
||||||
auto read_length = Length - offset;
|
|
||||||
|
|
||||||
const auto* page_start = static_cast<uint8_t*>(PAGE_ALIGN(current_base));
|
|
||||||
const auto* next_page = page_start + PAGE_SIZE;
|
|
||||||
|
|
||||||
if (current_base + read_length > next_page)
|
|
||||||
{
|
|
||||||
read_length = next_page - current_base;
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += read_length;
|
|
||||||
|
|
||||||
const auto physical_base = memory::get_physical_address(const_cast<uint8_t*>(current_base));
|
|
||||||
|
|
||||||
if (!physical_base)
|
|
||||||
{
|
|
||||||
inject_page_fault(current_base);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!memory::read_physical_memory(current_destination, physical_base, read_length))
|
|
||||||
{
|
|
||||||
// Not sure if we can recover from that :(
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
syscall_state get_syscall_state(const vmx::guest_context& guest_context)
|
|
||||||
{
|
|
||||||
scoped_cr3_switch cr3_switch{};
|
|
||||||
|
|
||||||
constexpr auto PCID_NONE = 0x000;
|
|
||||||
constexpr auto PCID_MASK = 0x003;
|
|
||||||
|
|
||||||
cr3 guest_cr3{};
|
|
||||||
guest_cr3.flags = read_vmx(VMCS_GUEST_CR3);
|
|
||||||
|
|
||||||
if ((guest_cr3.flags & PCID_MASK) != PCID_NONE)
|
|
||||||
{
|
|
||||||
cr3_switch.set_cr3(get_current_process_cr3());
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto* rip = reinterpret_cast<uint8_t*>(guest_context.guest_rip);
|
|
||||||
|
|
||||||
constexpr uint8_t syscall_bytes[] = {0x0F, 0x05};
|
|
||||||
constexpr uint8_t sysret_bytes[] = {0x48, 0x0F, 0x07};
|
|
||||||
|
|
||||||
constexpr auto max_byte_length = max(sizeof(sysret_bytes), sizeof(syscall_bytes));
|
|
||||||
|
|
||||||
uint8_t data[max_byte_length];
|
|
||||||
|
|
||||||
if (!read_data_or_page_fault(data, rip))
|
|
||||||
{
|
|
||||||
return syscall_state::page_fault;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_mem_equal(data, syscall_bytes))
|
|
||||||
{
|
|
||||||
return syscall_state::is_syscall;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_mem_equal(data, sysret_bytes))
|
|
||||||
{
|
|
||||||
return syscall_state::is_sysret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return syscall_state::none;
|
|
||||||
}
|
|
||||||
|
|
||||||
void vmx_handle_exception(vmx::guest_context& guest_context)
|
|
||||||
{
|
|
||||||
vmexit_interrupt_information interrupt{};
|
|
||||||
interrupt.flags = static_cast<uint32_t>(read_vmx(VMCS_VMEXIT_INTERRUPTION_INFORMATION));
|
|
||||||
|
|
||||||
if (interrupt.interruption_type == non_maskable_interrupt
|
|
||||||
&& interrupt.vector == nmi)
|
|
||||||
{
|
|
||||||
// TODO ?
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (interrupt.vector == invalid_opcode)
|
|
||||||
{
|
|
||||||
guest_context.increment_rip = false;
|
|
||||||
|
|
||||||
const auto state = get_syscall_state(guest_context);
|
|
||||||
|
|
||||||
if (state == syscall_state::page_fault)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto proc = process::get_current_process();
|
|
||||||
|
|
||||||
const auto filename = proc.get_image_filename();
|
|
||||||
if (string::equal(filename, "explorer.exe"))
|
|
||||||
{
|
|
||||||
debug_log("Explorer SYSCALL: %d\n", static_cast<uint32_t>(guest_context.vp_regs->Rax));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state == syscall_state::is_syscall)
|
|
||||||
{
|
|
||||||
const auto instruction_length = read_vmx(VMCS_VMEXIT_INSTRUCTION_LENGTH);
|
|
||||||
|
|
||||||
const auto star = __readmsr(IA32_STAR);
|
|
||||||
const auto lstar = __readmsr(IA32_LSTAR);
|
|
||||||
const auto fmask = __readmsr(IA32_FMASK);
|
|
||||||
|
|
||||||
guest_context.vp_regs->Rcx = guest_context.guest_rip + instruction_length;
|
|
||||||
guest_context.guest_rip = lstar;
|
|
||||||
__vmx_vmwrite(VMCS_GUEST_RIP, guest_context.guest_rip);
|
|
||||||
|
|
||||||
|
|
||||||
guest_context.vp_regs->R11 = guest_context.guest_e_flags;
|
|
||||||
guest_context.guest_e_flags &= ~(fmask | RFLAGS_RESUME_FLAG_FLAG);
|
|
||||||
__vmx_vmwrite(VMCS_GUEST_RFLAGS, guest_context.guest_e_flags);
|
|
||||||
|
|
||||||
|
|
||||||
vmx::gdt_entry gdt_entry{};
|
|
||||||
gdt_entry.selector.flags = static_cast<uint16_t>((star >> 32) & ~3);
|
|
||||||
gdt_entry.base = 0;
|
|
||||||
gdt_entry.limit = 0xFFFFF;
|
|
||||||
gdt_entry.access_rights.flags = 0xA09B;
|
|
||||||
|
|
||||||
__vmx_vmwrite(VMCS_GUEST_CS_SELECTOR, gdt_entry.selector.flags);
|
|
||||||
__vmx_vmwrite(VMCS_GUEST_CS_LIMIT, gdt_entry.limit);
|
|
||||||
__vmx_vmwrite(VMCS_GUEST_CS_ACCESS_RIGHTS, gdt_entry.access_rights.flags);
|
|
||||||
__vmx_vmwrite(VMCS_GUEST_CS_BASE, gdt_entry.base);
|
|
||||||
|
|
||||||
gdt_entry = {};
|
|
||||||
gdt_entry.selector.flags = static_cast<uint16_t>(((star >> 32) & ~3) + 8);
|
|
||||||
gdt_entry.base = 0;
|
|
||||||
gdt_entry.limit = 0xFFFFF;
|
|
||||||
gdt_entry.access_rights.flags = 0xC093;
|
|
||||||
|
|
||||||
__vmx_vmwrite(VMCS_GUEST_SS_SELECTOR, gdt_entry.selector.flags);
|
|
||||||
__vmx_vmwrite(VMCS_GUEST_SS_LIMIT, gdt_entry.limit);
|
|
||||||
__vmx_vmwrite(VMCS_GUEST_SS_ACCESS_RIGHTS, gdt_entry.access_rights.flags);
|
|
||||||
__vmx_vmwrite(VMCS_GUEST_SS_BASE, gdt_entry.base);
|
|
||||||
}
|
|
||||||
else if (state == syscall_state::is_sysret)
|
|
||||||
{
|
|
||||||
const auto star = __readmsr(IA32_STAR);
|
|
||||||
|
|
||||||
guest_context.vp_regs->Rip = guest_context.vp_regs->Rcx;
|
|
||||||
__vmx_vmwrite(VMCS_GUEST_RIP, guest_context.vp_regs->Rip);
|
|
||||||
|
|
||||||
guest_context.guest_e_flags = (guest_context.vp_regs->R11 & 0x3C7FD7) | 2;
|
|
||||||
__vmx_vmwrite(VMCS_GUEST_RFLAGS, guest_context.guest_e_flags);
|
|
||||||
|
|
||||||
vmx::gdt_entry gdt_entry{};
|
|
||||||
gdt_entry.selector.flags = static_cast<uint16_t>(((star >> 48) + 16) | 3);
|
|
||||||
gdt_entry.base = 0;
|
|
||||||
gdt_entry.limit = 0xFFFFF;
|
|
||||||
gdt_entry.access_rights.flags = 0xA0FB;
|
|
||||||
|
|
||||||
__vmx_vmwrite(VMCS_GUEST_CS_SELECTOR, gdt_entry.selector.flags);
|
|
||||||
__vmx_vmwrite(VMCS_GUEST_CS_LIMIT, gdt_entry.limit);
|
|
||||||
__vmx_vmwrite(VMCS_GUEST_CS_ACCESS_RIGHTS, gdt_entry.access_rights.flags);
|
|
||||||
__vmx_vmwrite(VMCS_GUEST_CS_BASE, gdt_entry.base);
|
|
||||||
|
|
||||||
gdt_entry = {};
|
|
||||||
gdt_entry.selector.flags = static_cast<uint16_t>(((star >> 48) + 8) | 3);
|
|
||||||
gdt_entry.base = 0;
|
|
||||||
gdt_entry.limit = 0xFFFFF;
|
|
||||||
gdt_entry.access_rights.flags = 0xC0F3;
|
|
||||||
|
|
||||||
__vmx_vmwrite(VMCS_GUEST_SS_SELECTOR, gdt_entry.selector.flags);
|
|
||||||
__vmx_vmwrite(VMCS_GUEST_SS_LIMIT, gdt_entry.limit);
|
|
||||||
__vmx_vmwrite(VMCS_GUEST_SS_ACCESS_RIGHTS, gdt_entry.access_rights.flags);
|
|
||||||
__vmx_vmwrite(VMCS_GUEST_SS_BASE, gdt_entry.base);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
inject_invalid_opcode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
__vmx_vmwrite(VMCS_CTRL_VMENTRY_INTERRUPTION_INFORMATION_FIELD, interrupt.flags);
|
|
||||||
if (interrupt.error_code_valid)
|
|
||||||
{
|
|
||||||
__vmx_vmwrite(VMCS_CTRL_VMENTRY_EXCEPTION_ERROR_CODE, read_vmx(VMCS_VMEXIT_INTERRUPTION_ERROR_CODE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_system()
|
|
||||||
{
|
|
||||||
return (read_vmx(VMCS_GUEST_CS_SELECTOR) & SEGMENT_ACCESS_RIGHTS_DESCRIPTOR_PRIVILEGE_LEVEL_MASK) == DPL_SYSTEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
void vmx_handle_cpuid(vmx::guest_context& guest_context)
|
void vmx_handle_cpuid(vmx::guest_context& guest_context)
|
||||||
{
|
{
|
||||||
if (guest_context.vp_regs->Rax == 0x41414141 &&
|
|
||||||
guest_context.vp_regs->Rcx == 0x42424243 &&
|
|
||||||
is_system())
|
|
||||||
{
|
|
||||||
vmx_enable_syscall_hooks(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
INT32 cpu_info[4];
|
INT32 cpu_info[4];
|
||||||
|
|
||||||
if (guest_context.vp_regs->Rax == 0x41414141 &&
|
if (guest_context.vp_regs->Rax == 0x41414141 &&
|
||||||
guest_context.vp_regs->Rcx == 0x42424242 &&
|
guest_context.vp_regs->Rcx == 0x42424242 &&
|
||||||
is_system())
|
(read_vmx(VMCS_GUEST_CS_SELECTOR) & SEGMENT_ACCESS_RIGHTS_DESCRIPTOR_PRIVILEGE_LEVEL_MASK) == DPL_SYSTEM)
|
||||||
{
|
{
|
||||||
guest_context.exit_vm = true;
|
guest_context.exit_vm = true;
|
||||||
return;
|
return;
|
||||||
@ -907,9 +520,6 @@ void vmx_dispatch_vm_exit(vmx::guest_context& guest_context, const vmx::state& v
|
|||||||
case VMX_EXIT_REASON_EPT_MISCONFIGURATION:
|
case VMX_EXIT_REASON_EPT_MISCONFIGURATION:
|
||||||
vm_state.ept->handle_misconfiguration(guest_context);
|
vm_state.ept->handle_misconfiguration(guest_context);
|
||||||
break;
|
break;
|
||||||
case VMX_EXIT_REASON_EXCEPTION_OR_NMI:
|
|
||||||
vmx_handle_exception(guest_context);
|
|
||||||
break;
|
|
||||||
//case VMX_EXIT_REASON_EXECUTE_RDTSC:
|
//case VMX_EXIT_REASON_EXECUTE_RDTSC:
|
||||||
// break;
|
// break;
|
||||||
default:
|
default:
|
||||||
@ -996,7 +606,7 @@ void setup_vmcs_for_cpu(vmx::state& vm_state)
|
|||||||
|
|
||||||
ia32_vmx_exit_ctls_register exit_ctls_register{};
|
ia32_vmx_exit_ctls_register exit_ctls_register{};
|
||||||
exit_ctls_register.host_address_space_size = 1;
|
exit_ctls_register.host_address_space_size = 1;
|
||||||
__vmx_vmwrite(VMCS_CTRL_PRIMARY_VMEXIT_CONTROLS,
|
__vmx_vmwrite(VMCS_CTRL_VMEXIT_CONTROLS,
|
||||||
adjust_msr(launch_context->msr_data[15],
|
adjust_msr(launch_context->msr_data[15],
|
||||||
exit_ctls_register.flags));
|
exit_ctls_register.flags));
|
||||||
|
|
||||||
@ -1087,8 +697,7 @@ void setup_vmcs_for_cpu(vmx::state& vm_state)
|
|||||||
__vmx_vmwrite(VMCS_GUEST_DEBUGCTL, state->debug_control);
|
__vmx_vmwrite(VMCS_GUEST_DEBUGCTL, state->debug_control);
|
||||||
__vmx_vmwrite(VMCS_GUEST_DR7, state->kernel_dr7);
|
__vmx_vmwrite(VMCS_GUEST_DR7, state->kernel_dr7);
|
||||||
|
|
||||||
const auto stack_pointer = reinterpret_cast<uintptr_t>(vm_state.stack_buffer) + KERNEL_STACK_SIZE - sizeof(
|
const auto stack_pointer = reinterpret_cast<uintptr_t>(vm_state.stack_buffer) + KERNEL_STACK_SIZE - sizeof(CONTEXT);
|
||||||
CONTEXT);
|
|
||||||
|
|
||||||
__vmx_vmwrite(VMCS_GUEST_RSP, stack_pointer);
|
__vmx_vmwrite(VMCS_GUEST_RSP, stack_pointer);
|
||||||
__vmx_vmwrite(VMCS_GUEST_RIP, reinterpret_cast<uintptr_t>(vm_launch));
|
__vmx_vmwrite(VMCS_GUEST_RIP, reinterpret_cast<uintptr_t>(vm_launch));
|
||||||
@ -1126,16 +735,11 @@ void hypervisor::enable_core(const uint64_t system_directory_table_base)
|
|||||||
debug_log("Enabling hypervisor on core %d\n", thread::get_processor_index());
|
debug_log("Enabling hypervisor on core %d\n", thread::get_processor_index());
|
||||||
auto* vm_state = this->get_current_vm_state();
|
auto* vm_state = this->get_current_vm_state();
|
||||||
|
|
||||||
if (!is_vmx_supported())
|
if (!is_virtualization_supported())
|
||||||
{
|
{
|
||||||
throw std::runtime_error("VMX not supported on this core");
|
throw std::runtime_error("VMX not supported on this core");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_vmx_available())
|
|
||||||
{
|
|
||||||
throw std::runtime_error("VMX not available on this core");
|
|
||||||
}
|
|
||||||
|
|
||||||
vm_state->launch_context.launched = false;
|
vm_state->launch_context.launched = false;
|
||||||
vm_state->launch_context.system_directory_table_base = system_directory_table_base;
|
vm_state->launch_context.system_directory_table_base = system_directory_table_base;
|
||||||
|
|
||||||
@ -1151,8 +755,6 @@ void hypervisor::enable_core(const uint64_t system_directory_table_base)
|
|||||||
{
|
{
|
||||||
throw std::runtime_error("Hypervisor is not present");
|
throw std::runtime_error("Hypervisor is not present");
|
||||||
}
|
}
|
||||||
|
|
||||||
enable_syscall_hooking();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void hypervisor::disable_core()
|
void hypervisor::disable_core()
|
||||||
@ -1223,16 +825,45 @@ void hypervisor::free_vm_states()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void hypervisor::invalidate_cores() const
|
bool hypervisor::try_install_ept_hook_on_core(const void* destination, const void* source, const size_t length,
|
||||||
|
vmx::ept_translation_hint* translation_hint)
|
||||||
{
|
{
|
||||||
thread::dispatch_on_all_cores([&]
|
try
|
||||||
{
|
{
|
||||||
const auto* vm_state = this->get_current_vm_state();
|
this->install_ept_hook_on_core(destination, source, length, translation_hint);
|
||||||
if (vm_state && this->is_enabled())
|
return true;
|
||||||
{
|
}
|
||||||
vm_state->ept->invalidate();
|
catch (std::exception& e)
|
||||||
}
|
{
|
||||||
});
|
debug_log("Failed to install ept hook on core %d: %s\n", thread::get_processor_index(), e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
debug_log("Failed to install ept hook on core %d.\n", thread::get_processor_index());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hypervisor::install_ept_hook_on_core(const void* destination, const void* source, const size_t length,
|
||||||
|
vmx::ept_translation_hint* translation_hint)
|
||||||
|
{
|
||||||
|
auto* vm_state = this->get_current_vm_state();
|
||||||
|
if (!vm_state)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("No vm state available");
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)destination;
|
||||||
|
(void)source;
|
||||||
|
(void)length;
|
||||||
|
(void)translation_hint;
|
||||||
|
//vm_state->ept->install_hook(destination, source, length, translation_hint);
|
||||||
|
|
||||||
|
if (this->is_enabled())
|
||||||
|
{
|
||||||
|
vm_state->ept->invalidate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vmx::state* hypervisor::get_current_vm_state() const
|
vmx::state* hypervisor::get_current_vm_state() const
|
||||||
|
@ -19,13 +19,11 @@ public:
|
|||||||
|
|
||||||
bool is_enabled() const;
|
bool is_enabled() const;
|
||||||
|
|
||||||
bool install_ept_hook(const void* destination, const void* source, size_t length, process_id source_pid,
|
bool install_ept_hook(const void* destination, const void* source, size_t length,
|
||||||
process_id target_pid, const utils::list<vmx::ept_translation_hint>& hints = {});
|
vmx::ept_translation_hint* translation_hint = nullptr);
|
||||||
|
|
||||||
bool install_ept_code_watch_point(uint64_t physical_page, process_id source_pid, process_id target_pid,
|
bool install_ept_code_watch_point(uint64_t physical_page, bool invalidate = true) const;
|
||||||
bool invalidate = true) const;
|
bool install_ept_code_watch_points(const uint64_t* physical_pages, size_t count) const;
|
||||||
bool install_ept_code_watch_points(const uint64_t* physical_pages, size_t count, process_id source_pid,
|
|
||||||
process_id target_pid) const;
|
|
||||||
|
|
||||||
void disable_all_ept_hooks() const;
|
void disable_all_ept_hooks() const;
|
||||||
|
|
||||||
@ -33,8 +31,6 @@ public:
|
|||||||
|
|
||||||
static hypervisor* get_instance();
|
static hypervisor* get_instance();
|
||||||
|
|
||||||
bool cleanup_process(process_id process);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t vm_state_count_{0};
|
uint32_t vm_state_count_{0};
|
||||||
vmx::state** vm_states_{nullptr};
|
vmx::state** vm_states_{nullptr};
|
||||||
@ -47,7 +43,10 @@ private:
|
|||||||
void allocate_vm_states();
|
void allocate_vm_states();
|
||||||
void free_vm_states();
|
void free_vm_states();
|
||||||
|
|
||||||
void invalidate_cores() const;
|
bool try_install_ept_hook_on_core(const void* destination, const void* source, size_t length,
|
||||||
|
vmx::ept_translation_hint* translation_hint = nullptr);
|
||||||
|
void install_ept_hook_on_core(const void* destination, const void* source, size_t length,
|
||||||
|
vmx::ept_translation_hint* translation_hint = nullptr);
|
||||||
|
|
||||||
vmx::state* get_current_vm_state() const;
|
vmx::state* get_current_vm_state() const;
|
||||||
};
|
};
|
||||||
|
@ -37,12 +37,11 @@ namespace
|
|||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::list<vmx::ept_translation_hint> generate_translation_hints(uint32_t process_id, const void* target_address,
|
vmx::ept_translation_hint* generate_translation_hints(uint32_t process_id, const void* target_address, size_t size)
|
||||||
size_t size)
|
|
||||||
{
|
{
|
||||||
utils::list<vmx::ept_translation_hint> translation_hints{};
|
vmx::ept_translation_hint* translation_hints{nullptr};
|
||||||
|
|
||||||
thread::kernel_thread([&translation_hints, process_id, target_address, size]
|
thread::kernel_thread t([&translation_hints, process_id, target_address, size]
|
||||||
{
|
{
|
||||||
debug_log("Looking up process: %d\n", process_id);
|
debug_log("Looking up process: %d\n", process_id);
|
||||||
|
|
||||||
@ -53,7 +52,8 @@ namespace
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const auto name = process_handle.get_image_filename())
|
const auto name = process_handle.get_image_filename();
|
||||||
|
if (name)
|
||||||
{
|
{
|
||||||
debug_log("Attaching to %s\n", name);
|
debug_log("Attaching to %s\n", name);
|
||||||
}
|
}
|
||||||
@ -62,7 +62,9 @@ namespace
|
|||||||
|
|
||||||
debug_log("Generating translation hints for address: %p\n", target_address);
|
debug_log("Generating translation hints for address: %p\n", target_address);
|
||||||
translation_hints = vmx::ept::generate_translation_hints(target_address, size);
|
translation_hints = vmx::ept::generate_translation_hints(target_address, size);
|
||||||
}).join();
|
});
|
||||||
|
|
||||||
|
t.join();
|
||||||
|
|
||||||
return translation_hints;
|
return translation_hints;
|
||||||
}
|
}
|
||||||
@ -81,18 +83,24 @@ namespace
|
|||||||
throw std::runtime_error("Failed to copy buffer");
|
throw std::runtime_error("Failed to copy buffer");
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(buffer.get(), request.source_data, request.source_data_size);
|
vmx::ept_translation_hint* translation_hints = nullptr;
|
||||||
const auto translation_hints = generate_translation_hints(request.process_id, request.target_address,
|
auto destructor = utils::finally([&translation_hints]()
|
||||||
request.source_data_size);
|
{
|
||||||
|
vmx::ept::free_translation_hints(translation_hints);
|
||||||
|
});
|
||||||
|
|
||||||
if (translation_hints.empty())
|
memcpy(buffer.get(), request.source_data, request.source_data_size);
|
||||||
|
translation_hints = generate_translation_hints(request.process_id, request.target_address,
|
||||||
|
request.source_data_size);
|
||||||
|
|
||||||
|
if (!translation_hints)
|
||||||
{
|
{
|
||||||
debug_log("Failed to generate tranlsation hints\n");
|
debug_log("Failed to generate tranlsation hints\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
hypervisor->install_ept_hook(request.target_address, buffer.get(), request.source_data_size,
|
hypervisor->install_ept_hook(request.target_address, buffer.get(), request.source_data_size,
|
||||||
process::get_current_process_id(), request.process_id, translation_hints);
|
translation_hints);
|
||||||
}
|
}
|
||||||
|
|
||||||
void unhook()
|
void unhook()
|
||||||
@ -123,7 +131,7 @@ namespace
|
|||||||
|
|
||||||
void watch_regions(const watch_request& watch_request)
|
void watch_regions(const watch_request& watch_request)
|
||||||
{
|
{
|
||||||
const auto* hypervisor = hypervisor::get_instance();
|
auto* hypervisor = hypervisor::get_instance();
|
||||||
if (!hypervisor)
|
if (!hypervisor)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Hypervisor not installed");
|
throw std::runtime_error("Hypervisor not installed");
|
||||||
@ -160,7 +168,7 @@ namespace
|
|||||||
throw std::runtime_error("Failed to copy buffer");
|
throw std::runtime_error("Failed to copy buffer");
|
||||||
}
|
}
|
||||||
|
|
||||||
thread::kernel_thread t([watch_request_copy, &index, &page_buffer]
|
thread::kernel_thread t([watch_request_copy, hypervisor, &index, &page_buffer]
|
||||||
{
|
{
|
||||||
debug_log("Looking up process: %d\n", watch_request_copy.process_id);
|
debug_log("Looking up process: %d\n", watch_request_copy.process_id);
|
||||||
|
|
||||||
@ -210,8 +218,7 @@ namespace
|
|||||||
t.join();
|
t.join();
|
||||||
|
|
||||||
debug_log("Installing watch points...\n");
|
debug_log("Installing watch points...\n");
|
||||||
(void)hypervisor->install_ept_code_watch_points(page_buffer.get(), index, process::get_current_process_id(),
|
(void)hypervisor->install_ept_code_watch_points(page_buffer.get(), index);
|
||||||
watch_request_copy.process_id);
|
|
||||||
debug_log("Watch points installed\n");
|
debug_log("Watch points installed\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,7 +240,7 @@ namespace
|
|||||||
|
|
||||||
void get_records(const PIRP irp, const PIO_STACK_LOCATION irp_sp)
|
void get_records(const PIRP irp, const PIO_STACK_LOCATION irp_sp)
|
||||||
{
|
{
|
||||||
const auto* hypervisor = hypervisor::get_instance();
|
auto* hypervisor = hypervisor::get_instance();
|
||||||
if (!hypervisor)
|
if (!hypervisor)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Hypervisor not installed");
|
throw std::runtime_error("Hypervisor not installed");
|
||||||
@ -252,6 +259,7 @@ namespace
|
|||||||
irp->IoStatus.Status = STATUS_SUCCESS;
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
||||||
|
|
||||||
const auto irp_sp = IoGetCurrentIrpStackLocation(irp);
|
const auto irp_sp = IoGetCurrentIrpStackLocation(irp);
|
||||||
|
|
||||||
if (irp_sp)
|
if (irp_sp)
|
||||||
{
|
{
|
||||||
const auto ioctr_code = irp_sp->Parameters.DeviceIoControl.IoControlCode;
|
const auto ioctr_code = irp_sp->Parameters.DeviceIoControl.IoControlCode;
|
||||||
|
@ -1,433 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "allocator.hpp"
|
|
||||||
#include "exception.hpp"
|
|
||||||
#include "finally.hpp"
|
|
||||||
|
|
||||||
namespace utils
|
|
||||||
{
|
|
||||||
template <typename T, typename ObjectAllocator = NonPagedAllocator, typename ListAllocator = NonPagedAllocator>
|
|
||||||
requires is_allocator<ObjectAllocator> && is_allocator<ListAllocator>
|
|
||||||
class list
|
|
||||||
{
|
|
||||||
struct list_entry
|
|
||||||
{
|
|
||||||
T* entry{nullptr};
|
|
||||||
list_entry* next{nullptr};
|
|
||||||
|
|
||||||
void* this_base{nullptr};
|
|
||||||
void* entry_base{nullptr};
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
using type = T;
|
|
||||||
|
|
||||||
class iterator
|
|
||||||
{
|
|
||||||
friend list;
|
|
||||||
|
|
||||||
public:
|
|
||||||
iterator(list_entry* entry = nullptr)
|
|
||||||
: entry_(entry)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
T& operator*() const
|
|
||||||
{
|
|
||||||
return *this->entry_->entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
T* operator->() const
|
|
||||||
{
|
|
||||||
return this->entry_->entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const iterator& i) const
|
|
||||||
{
|
|
||||||
return this->entry_ == i.entry_;
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator operator++()
|
|
||||||
{
|
|
||||||
this->entry_ = this->entry_->next;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator operator+(const size_t num) const
|
|
||||||
{
|
|
||||||
auto entry = this->entry_;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < num; ++i)
|
|
||||||
{
|
|
||||||
if (!entry)
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
entry = entry->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return {entry};
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
list_entry* entry_{nullptr};
|
|
||||||
|
|
||||||
list_entry* get_entry() const
|
|
||||||
{
|
|
||||||
return entry_;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class const_iterator
|
|
||||||
{
|
|
||||||
friend list;
|
|
||||||
|
|
||||||
public:
|
|
||||||
const_iterator(list_entry* entry = nullptr)
|
|
||||||
: entry_(entry)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
const T& operator*() const
|
|
||||||
{
|
|
||||||
return *this->entry_->entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
const T* operator->() const
|
|
||||||
{
|
|
||||||
return this->entry_->entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const const_iterator& i) const
|
|
||||||
{
|
|
||||||
return this->entry_ == i.entry_;
|
|
||||||
}
|
|
||||||
|
|
||||||
const_iterator operator++()
|
|
||||||
{
|
|
||||||
this->entry_ = this->entry_->next;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
list_entry* entry_{nullptr};
|
|
||||||
|
|
||||||
list_entry* get_entry() const
|
|
||||||
{
|
|
||||||
return entry_;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
list() = default;
|
|
||||||
|
|
||||||
~list()
|
|
||||||
{
|
|
||||||
this->clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
list(const list& obj)
|
|
||||||
: list()
|
|
||||||
{
|
|
||||||
this->operator=(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
list(list&& obj) noexcept
|
|
||||||
: list()
|
|
||||||
{
|
|
||||||
this->operator=(std::move(obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
list& operator=(const list& obj)
|
|
||||||
{
|
|
||||||
if (this != &obj)
|
|
||||||
{
|
|
||||||
this->clear();
|
|
||||||
|
|
||||||
for (const auto& i : obj)
|
|
||||||
{
|
|
||||||
this->push_back(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
list& operator=(list&& obj) noexcept
|
|
||||||
{
|
|
||||||
if (this != &obj)
|
|
||||||
{
|
|
||||||
this->clear();
|
|
||||||
|
|
||||||
this->entries_ = obj.entries_;
|
|
||||||
obj.entries_ = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
T& push_back(const T& obj)
|
|
||||||
{
|
|
||||||
auto& entry = this->add_uninitialized_entry_back();
|
|
||||||
new(&entry) T(obj);
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
T& push_back(T&& obj)
|
|
||||||
{
|
|
||||||
auto& entry = this->add_uninitialized_entry_back();
|
|
||||||
new(&entry) T(std::move(obj));
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename... Args>
|
|
||||||
T& emplace_back(Args&&... args)
|
|
||||||
{
|
|
||||||
auto& entry = this->add_uninitialized_entry_back();
|
|
||||||
new(&entry) T(std::forward<Args>(args)...);
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
T& push_front(const T& obj)
|
|
||||||
{
|
|
||||||
auto& entry = this->add_uninitialized_entry_front();
|
|
||||||
new(&entry) T(obj);
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
T& push_front(T&& obj)
|
|
||||||
{
|
|
||||||
auto& entry = this->add_uninitialized_entry_front();
|
|
||||||
new(&entry) T(std::move(obj));
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename... Args>
|
|
||||||
T& emplace_front(Args&&... args)
|
|
||||||
{
|
|
||||||
auto& entry = this->add_uninitialized_entry_front();
|
|
||||||
new(&entry) T(std::forward<Args>(args)...);
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
T& operator[](const size_t index)
|
|
||||||
{
|
|
||||||
return this->at(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
const T& operator[](const size_t index) const
|
|
||||||
{
|
|
||||||
return this->at(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
T& at(const size_t index)
|
|
||||||
{
|
|
||||||
size_t i = 0;
|
|
||||||
for (auto& obj : *this)
|
|
||||||
{
|
|
||||||
if (++i == index)
|
|
||||||
{
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw std::runtime_error("Out of bounds access");
|
|
||||||
}
|
|
||||||
|
|
||||||
const T& at(const size_t index) const
|
|
||||||
{
|
|
||||||
size_t i = 0;
|
|
||||||
for (const auto& obj : *this)
|
|
||||||
{
|
|
||||||
if (++i == index)
|
|
||||||
{
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw std::runtime_error("Out of bounds access");
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear()
|
|
||||||
{
|
|
||||||
while (!this->empty())
|
|
||||||
{
|
|
||||||
this->erase(this->begin());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] size_t size() const
|
|
||||||
{
|
|
||||||
size_t i = 0;
|
|
||||||
for (const auto& obj : *this)
|
|
||||||
{
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator begin()
|
|
||||||
{
|
|
||||||
return {this->entries_};
|
|
||||||
}
|
|
||||||
|
|
||||||
const_iterator begin() const
|
|
||||||
{
|
|
||||||
return {this->entries_};
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator end()
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const_iterator end() const
|
|
||||||
{
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void erase(T& entry)
|
|
||||||
{
|
|
||||||
auto** insertion_point = &this->entries_;
|
|
||||||
while (*insertion_point)
|
|
||||||
{
|
|
||||||
if ((*insertion_point)->entry != &entry)
|
|
||||||
{
|
|
||||||
insertion_point = &(*insertion_point)->next;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* list_entry = *insertion_point;
|
|
||||||
*insertion_point = list_entry->next;
|
|
||||||
|
|
||||||
list_entry->entry->~T();
|
|
||||||
this->object_allocator_.free(list_entry->entry_base);
|
|
||||||
this->list_allocator_.free(list_entry->this_base);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw std::runtime_error("Bad entry");
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator erase(iterator iterator)
|
|
||||||
{
|
|
||||||
auto* list_entry = iterator.get_entry();
|
|
||||||
auto** insertion_point = &this->entries_;
|
|
||||||
while (*insertion_point && list_entry)
|
|
||||||
{
|
|
||||||
if (*insertion_point != list_entry)
|
|
||||||
{
|
|
||||||
insertion_point = &(*insertion_point)->next;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
*insertion_point = list_entry->next;
|
|
||||||
|
|
||||||
list_entry->entry->~T();
|
|
||||||
this->object_allocator_.free(list_entry->entry_base);
|
|
||||||
this->list_allocator_.free(list_entry->this_base);
|
|
||||||
|
|
||||||
return {*insertion_point};
|
|
||||||
}
|
|
||||||
|
|
||||||
throw std::runtime_error("Bad iterator");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool empty() const
|
|
||||||
{
|
|
||||||
return this->entries_ == nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend iterator;
|
|
||||||
|
|
||||||
ObjectAllocator object_allocator_{};
|
|
||||||
ListAllocator list_allocator_{};
|
|
||||||
list_entry* entries_{nullptr};
|
|
||||||
|
|
||||||
template <typename U, typename V>
|
|
||||||
static U* align_pointer(V* pointer)
|
|
||||||
{
|
|
||||||
const auto align_bits = alignof(U) - 1;
|
|
||||||
auto ptr = reinterpret_cast<intptr_t>(pointer);
|
|
||||||
ptr = (ptr + align_bits) & (~align_bits);
|
|
||||||
|
|
||||||
return reinterpret_cast<U*>(ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void allocate_entry(void*& list_base, void*& entry_base)
|
|
||||||
{
|
|
||||||
list_base = nullptr;
|
|
||||||
entry_base = nullptr;
|
|
||||||
|
|
||||||
auto destructor = utils::finally([&]
|
|
||||||
{
|
|
||||||
if (list_base)
|
|
||||||
{
|
|
||||||
this->list_allocator_.free(list_base);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry_base)
|
|
||||||
{
|
|
||||||
this->object_allocator_.free(entry_base);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
list_base = this->list_allocator_.allocate(sizeof(list_entry) + alignof(list_entry));
|
|
||||||
if (!list_base)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Memory allocation failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
entry_base = this->object_allocator_.allocate(sizeof(T) + alignof(T));
|
|
||||||
if (!entry_base)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Memory allocation failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
destructor.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
list_entry& create_uninitialized_list_entry()
|
|
||||||
{
|
|
||||||
void* list_base = {};
|
|
||||||
void* entry_base = {};
|
|
||||||
this->allocate_entry(list_base, entry_base);
|
|
||||||
|
|
||||||
auto* obj = align_pointer<T>(entry_base);
|
|
||||||
auto* entry = align_pointer<list_entry>(list_base);
|
|
||||||
|
|
||||||
entry->this_base = list_base;
|
|
||||||
entry->entry_base = entry_base;
|
|
||||||
entry->next = nullptr;
|
|
||||||
entry->entry = obj;
|
|
||||||
|
|
||||||
return *entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
T& add_uninitialized_entry_back()
|
|
||||||
{
|
|
||||||
auto** insertion_point = &this->entries_;
|
|
||||||
while (*insertion_point)
|
|
||||||
{
|
|
||||||
insertion_point = &(*insertion_point)->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& entry = this->create_uninitialized_list_entry();
|
|
||||||
*insertion_point = &entry;
|
|
||||||
|
|
||||||
return *entry.entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
T& add_uninitialized_entry_front()
|
|
||||||
{
|
|
||||||
auto& entry = this->create_uninitialized_list_entry();
|
|
||||||
entry.next = this->entries_;
|
|
||||||
this->entries_ = &entry;
|
|
||||||
|
|
||||||
return *entry.entry;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
@ -3,5 +3,5 @@
|
|||||||
#ifdef NDEBUG__
|
#ifdef NDEBUG__
|
||||||
#define debug_log(...)
|
#define debug_log(...)
|
||||||
#else
|
#else
|
||||||
#define debug_log(...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[MOMO] " __VA_ARGS__)
|
#define debug_log(msg, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[MOMO] " msg, __VA_ARGS__)
|
||||||
#endif
|
#endif
|
||||||
|
@ -68,18 +68,6 @@ namespace memory
|
|||||||
return memory;
|
return memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
_IRQL_requires_max_(APC_LEVEL)
|
|
||||||
|
|
||||||
bool read_physical_memory(void* destination, uint64_t physical_address, const size_t size)
|
|
||||||
{
|
|
||||||
size_t bytes_read{};
|
|
||||||
MM_COPY_ADDRESS source{};
|
|
||||||
source.PhysicalAddress.QuadPart = static_cast<int64_t>(physical_address);
|
|
||||||
|
|
||||||
return MmCopyMemory(destination, source, size, MM_COPY_MEMORY_PHYSICAL, &bytes_read) == STATUS_SUCCESS &&
|
|
||||||
bytes_read == size;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t get_physical_address(void* address)
|
uint64_t get_physical_address(void* address)
|
||||||
{
|
{
|
||||||
return static_cast<uint64_t>(MmGetPhysicalAddress(address).QuadPart);
|
return static_cast<uint64_t>(MmGetPhysicalAddress(address).QuadPart);
|
||||||
@ -114,10 +102,7 @@ namespace memory
|
|||||||
|
|
||||||
void* allocate_non_paged_memory(const size_t size)
|
void* allocate_non_paged_memory(const size_t size)
|
||||||
{
|
{
|
||||||
#pragma warning(push)
|
|
||||||
#pragma warning(disable: 4996)
|
|
||||||
void* memory = ExAllocatePoolWithTag(NonPagedPool, size, 'MOMO');
|
void* memory = ExAllocatePoolWithTag(NonPagedPool, size, 'MOMO');
|
||||||
#pragma warning(pop)
|
|
||||||
if (memory)
|
if (memory)
|
||||||
{
|
{
|
||||||
RtlSecureZeroMemory(memory, size);
|
RtlSecureZeroMemory(memory, size);
|
||||||
@ -136,7 +121,7 @@ namespace memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool probe_for_read(const void* address, const size_t length, const uint64_t alignment)
|
bool prope_for_read(const void* address, const size_t length, const uint64_t alignment)
|
||||||
{
|
{
|
||||||
__try
|
__try
|
||||||
{
|
{
|
||||||
@ -151,13 +136,13 @@ namespace memory
|
|||||||
|
|
||||||
void assert_readability(const void* address, const size_t length, const uint64_t alignment)
|
void assert_readability(const void* address, const size_t length, const uint64_t alignment)
|
||||||
{
|
{
|
||||||
if (!probe_for_read(address, length, alignment))
|
if (!prope_for_read(address, length, alignment))
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Access violation");
|
throw std::runtime_error("Access violation");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool probe_for_write(const void* address, const size_t length, const uint64_t alignment)
|
bool prope_for_write(const void* address, const size_t length, const uint64_t alignment)
|
||||||
{
|
{
|
||||||
__try
|
__try
|
||||||
{
|
{
|
||||||
@ -172,7 +157,7 @@ namespace memory
|
|||||||
|
|
||||||
void assert_writability(const void* address, const size_t length, const uint64_t alignment)
|
void assert_writability(const void* address, const size_t length, const uint64_t alignment)
|
||||||
{
|
{
|
||||||
if (!probe_for_write(address, length, alignment))
|
if (!prope_for_write(address, length, alignment))
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Access violation");
|
throw std::runtime_error("Access violation");
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,6 @@ namespace memory
|
|||||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||||
void* allocate_aligned_memory(size_t size);
|
void* allocate_aligned_memory(size_t size);
|
||||||
|
|
||||||
_IRQL_requires_max_(APC_LEVEL)
|
|
||||||
bool read_physical_memory(void* destination, uint64_t physical_address, size_t size);
|
|
||||||
|
|
||||||
uint64_t get_physical_address(void* address);
|
uint64_t get_physical_address(void* address);
|
||||||
void* get_virtual_address(uint64_t address);
|
void* get_virtual_address(uint64_t address);
|
||||||
|
|
||||||
@ -30,10 +27,10 @@ namespace memory
|
|||||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||||
void free_non_paged_memory(void* memory);
|
void free_non_paged_memory(void* memory);
|
||||||
|
|
||||||
bool probe_for_read(const void* address, size_t length, uint64_t alignment = 1);
|
bool prope_for_read(const void* address, size_t length, uint64_t alignment = 1);
|
||||||
void assert_readability(const void* address, size_t length, uint64_t alignment = 1);
|
void assert_readability(const void* address, size_t length, uint64_t alignment = 1);
|
||||||
|
|
||||||
bool probe_for_write(const void* address, size_t length, uint64_t alignment = 1);
|
bool prope_for_write(const void* address, size_t length, uint64_t alignment = 1);
|
||||||
void assert_writability(const void* address, size_t length, uint64_t alignment = 1);
|
void assert_writability(const void* address, size_t length, uint64_t alignment = 1);
|
||||||
|
|
||||||
template <typename T, typename... Args>
|
template <typename T, typename... Args>
|
||||||
|
@ -56,29 +56,6 @@ MmAllocateContiguousNodeMemory(
|
|||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
typedef struct _MM_COPY_ADDRESS {
|
|
||||||
union {
|
|
||||||
PVOID VirtualAddress;
|
|
||||||
PHYSICAL_ADDRESS PhysicalAddress;
|
|
||||||
};
|
|
||||||
} MM_COPY_ADDRESS, * PMMCOPY_ADDRESS;
|
|
||||||
|
|
||||||
#define MM_COPY_MEMORY_PHYSICAL 0x1
|
|
||||||
#define MM_COPY_MEMORY_VIRTUAL 0x2
|
|
||||||
|
|
||||||
_IRQL_requires_max_(APC_LEVEL)
|
|
||||||
NTKERNELAPI
|
|
||||||
NTSTATUS
|
|
||||||
MmCopyMemory(
|
|
||||||
_In_ PVOID TargetAddress,
|
|
||||||
_In_ MM_COPY_ADDRESS SourceAddress,
|
|
||||||
_In_ SIZE_T NumberOfBytes,
|
|
||||||
_In_ ULONG Flags,
|
|
||||||
_Out_ PSIZE_T NumberOfBytesTransferred
|
|
||||||
);
|
|
||||||
|
|
||||||
// ----------------------------------------
|
|
||||||
|
|
||||||
NTSYSAPI
|
NTSYSAPI
|
||||||
VOID
|
VOID
|
||||||
NTAPI
|
NTAPI
|
||||||
|
@ -37,7 +37,7 @@ namespace process
|
|||||||
|
|
||||||
process_handle::process_handle(const process_handle& obj)
|
process_handle::process_handle(const process_handle& obj)
|
||||||
{
|
{
|
||||||
this->operator=(obj);
|
this->operator=(std::move(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
process_handle& process_handle::operator=(const process_handle& obj)
|
process_handle& process_handle::operator=(const process_handle& obj)
|
||||||
@ -80,14 +80,14 @@ namespace process
|
|||||||
return KeWaitForSingleObject(this->handle_, Executive, KernelMode, FALSE, &zero_time) != STATUS_WAIT_0;
|
return KeWaitForSingleObject(this->handle_, Executive, KernelMode, FALSE, &zero_time) != STATUS_WAIT_0;
|
||||||
}
|
}
|
||||||
|
|
||||||
process_id process_handle::get_id() const
|
uint32_t process_handle::get_id() const
|
||||||
{
|
{
|
||||||
if (!this->handle_)
|
if (!this->handle_)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return process_id_from_handle(PsGetProcessId(this->handle_));
|
return uint32_t(uint64_t(PsGetProcessId(this->handle_)));
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* process_handle::get_image_filename() const
|
const char* process_handle::get_image_filename() const
|
||||||
@ -111,25 +111,16 @@ namespace process
|
|||||||
this->own_ = false;
|
this->own_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
process_id process_id_from_handle(HANDLE handle)
|
process_handle find_process_by_id(const uint32_t process_id)
|
||||||
{
|
{
|
||||||
return process_id(size_t(handle));
|
PEPROCESS process{};
|
||||||
}
|
const uint64_t process_id_long = process_id;
|
||||||
|
if (PsLookupProcessByProcessId(HANDLE(process_id_long), &process) != STATUS_SUCCESS)
|
||||||
HANDLE handle_from_process_id(const process_id process)
|
|
||||||
{
|
|
||||||
return HANDLE(size_t(process));
|
|
||||||
}
|
|
||||||
|
|
||||||
process_handle find_process_by_id(const process_id process)
|
|
||||||
{
|
|
||||||
PEPROCESS process_obj{};
|
|
||||||
if (PsLookupProcessByProcessId(handle_from_process_id(process), &process_obj) != STATUS_SUCCESS)
|
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return process_handle{process_obj, true};
|
return process_handle{process, true};
|
||||||
}
|
}
|
||||||
|
|
||||||
process_handle get_current_process()
|
process_handle get_current_process()
|
||||||
@ -137,11 +128,6 @@ namespace process
|
|||||||
return process_handle{PsGetCurrentProcess(), false};
|
return process_handle{PsGetCurrentProcess(), false};
|
||||||
}
|
}
|
||||||
|
|
||||||
process_id get_current_process_id()
|
|
||||||
{
|
|
||||||
return get_current_process().get_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
scoped_process_attacher::scoped_process_attacher(const process_handle& process)
|
scoped_process_attacher::scoped_process_attacher(const process_handle& process)
|
||||||
{
|
{
|
||||||
if (!process || !process.is_alive())
|
if (!process || !process.is_alive())
|
||||||
|
@ -19,7 +19,7 @@ namespace process
|
|||||||
operator PEPROCESS() const;
|
operator PEPROCESS() const;
|
||||||
|
|
||||||
bool is_alive() const;
|
bool is_alive() const;
|
||||||
process_id get_id() const;
|
uint32_t get_id() const;
|
||||||
|
|
||||||
const char* get_image_filename() const;
|
const char* get_image_filename() const;
|
||||||
|
|
||||||
@ -30,14 +30,9 @@ namespace process
|
|||||||
void release();
|
void release();
|
||||||
};
|
};
|
||||||
|
|
||||||
process_id process_id_from_handle(HANDLE handle);
|
process_handle find_process_by_id(uint32_t process_id);
|
||||||
HANDLE handle_from_process_id(process_id process);
|
|
||||||
|
|
||||||
process_handle find_process_by_id(process_id process);
|
|
||||||
process_handle get_current_process();
|
process_handle get_current_process();
|
||||||
|
|
||||||
process_id get_current_process_id();
|
|
||||||
|
|
||||||
class scoped_process_attacher
|
class scoped_process_attacher
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
#include "std_include.hpp"
|
|
||||||
#include "process_callback.hpp"
|
|
||||||
#include "process.hpp"
|
|
||||||
#include "list.hpp"
|
|
||||||
#include "logging.hpp"
|
|
||||||
|
|
||||||
namespace process_callback
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
utils::list<callback_function>& get_callback_list()
|
|
||||||
{
|
|
||||||
static utils::list<callback_function> list{};
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
void process_notification_callback(const HANDLE parent, const HANDLE process, const BOOLEAN create)
|
|
||||||
{
|
|
||||||
const auto& list = get_callback_list();
|
|
||||||
for (const auto& callback : list)
|
|
||||||
{
|
|
||||||
callback(process::process_id_from_handle(parent), process::process_id_from_handle(process),
|
|
||||||
create == FALSE ? type::destroy : type::create);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class process_notifier
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
process_notifier()
|
|
||||||
: added_(PsSetCreateProcessNotifyRoutine(process_notification_callback, FALSE) == STATUS_SUCCESS)
|
|
||||||
{
|
|
||||||
get_callback_list();
|
|
||||||
|
|
||||||
if (!added_)
|
|
||||||
{
|
|
||||||
debug_log("Failed to register process notification callback\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~process_notifier()
|
|
||||||
{
|
|
||||||
if (this->added_)
|
|
||||||
{
|
|
||||||
PsSetCreateProcessNotifyRoutine(process_notification_callback, TRUE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool added_{};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void* add(callback_function callback)
|
|
||||||
{
|
|
||||||
static process_notifier _;
|
|
||||||
return &get_callback_list().push_back(std::move(callback));
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove(void* handle)
|
|
||||||
{
|
|
||||||
if (handle)
|
|
||||||
{
|
|
||||||
get_callback_list().erase(*static_cast<callback_function*>(handle));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "functional.hpp"
|
|
||||||
|
|
||||||
namespace process_callback
|
|
||||||
{
|
|
||||||
enum class type
|
|
||||||
{
|
|
||||||
create,
|
|
||||||
destroy,
|
|
||||||
};
|
|
||||||
|
|
||||||
using callback = void(process_id parent_id, process_id process_id, type type);
|
|
||||||
using callback_function = std::function<callback>;
|
|
||||||
|
|
||||||
void* add(callback_function callback);
|
|
||||||
void remove(void* handle);
|
|
||||||
|
|
||||||
class scoped_process_callback
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
scoped_process_callback() = default;
|
|
||||||
|
|
||||||
scoped_process_callback(callback_function function)
|
|
||||||
: handle_(add(std::move(function)))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~scoped_process_callback()
|
|
||||||
{
|
|
||||||
remove(this->handle_);
|
|
||||||
}
|
|
||||||
|
|
||||||
scoped_process_callback(scoped_process_callback&& obj) noexcept = delete;
|
|
||||||
scoped_process_callback& operator=(scoped_process_callback&& obj) noexcept = delete;
|
|
||||||
|
|
||||||
scoped_process_callback(const scoped_process_callback& obj) = delete;
|
|
||||||
scoped_process_callback& operator=(const scoped_process_callback& obj) = delete;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void* handle_{};
|
|
||||||
};
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
#pragma once
|
|
@ -1,95 +0,0 @@
|
|||||||
// Microsoft Visual C++ generated resource script.
|
|
||||||
//
|
|
||||||
#pragma code_page(65001)
|
|
||||||
|
|
||||||
#define APSTUDIO_READONLY_SYMBOLS
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Generated from the TEXTINCLUDE 2 resource.
|
|
||||||
//
|
|
||||||
#include "windows.h"
|
|
||||||
#include "resource.hpp"
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
#undef APSTUDIO_READONLY_SYMBOLS
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
// English (United States) resources
|
|
||||||
|
|
||||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
|
||||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
|
||||||
|
|
||||||
#ifdef APSTUDIO_INVOKED
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// TEXTINCLUDE
|
|
||||||
//
|
|
||||||
|
|
||||||
1 TEXTINCLUDE
|
|
||||||
BEGIN
|
|
||||||
"#include ""windows.h""\r\n"
|
|
||||||
"\0"
|
|
||||||
END
|
|
||||||
|
|
||||||
2 TEXTINCLUDE
|
|
||||||
BEGIN
|
|
||||||
"\r\n"
|
|
||||||
"\0"
|
|
||||||
END
|
|
||||||
|
|
||||||
#endif // APSTUDIO_INVOKED
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Version
|
|
||||||
//
|
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
|
||||||
FILEVERSION 1,0,0,0
|
|
||||||
PRODUCTVERSION 1,0,0,0
|
|
||||||
FILEFLAGSMASK 0x3fL
|
|
||||||
#ifdef _DEBUG
|
|
||||||
FILEFLAGS 0x1L
|
|
||||||
#else
|
|
||||||
FILEFLAGS 0x0L
|
|
||||||
#endif
|
|
||||||
FILEOS 0x40004L
|
|
||||||
FILETYPE VFT_DLL
|
|
||||||
FILESUBTYPE 0x0L
|
|
||||||
BEGIN
|
|
||||||
BLOCK "StringFileInfo"
|
|
||||||
BEGIN
|
|
||||||
BLOCK "040904b0"
|
|
||||||
BEGIN
|
|
||||||
VALUE "CompanyName", "momo5502"
|
|
||||||
VALUE "FileDescription", "HyperHook Driver"
|
|
||||||
VALUE "FileVersion", "1.0.0.0"
|
|
||||||
VALUE "InternalName", "HyperHook Driver"
|
|
||||||
VALUE "LegalCopyright", "All rights reserved."
|
|
||||||
VALUE "OriginalFilename", "hyperhook.sys"
|
|
||||||
VALUE "ProductName", "hyperhook"
|
|
||||||
VALUE "ProductVersion", "1.0.0.0"
|
|
||||||
END
|
|
||||||
END
|
|
||||||
BLOCK "VarFileInfo"
|
|
||||||
BEGIN
|
|
||||||
VALUE "Translation", 0x409, 1200
|
|
||||||
END
|
|
||||||
END
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif // English (United States) resources
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef APSTUDIO_INVOKED
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Generated from the TEXTINCLUDE 3 resource.
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
#endif // not APSTUDIO_INVOKED
|
|
@ -16,6 +16,3 @@
|
|||||||
#include "nt_ext.hpp"
|
#include "nt_ext.hpp"
|
||||||
#include "new.hpp"
|
#include "new.hpp"
|
||||||
#include "exception.hpp"
|
#include "exception.hpp"
|
||||||
|
|
||||||
// Not sure if this is good, but fuck it.
|
|
||||||
using process_id = uint32_t;
|
|
||||||
|
@ -17,22 +17,4 @@ namespace string
|
|||||||
RtlStringCchPrintfA(buffer, VA_BUFFER_SIZE, message, std::forward<Args>(args)...);
|
RtlStringCchPrintfA(buffer, VA_BUFFER_SIZE, message, std::forward<Args>(args)...);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool equal(const char* s1, const char* s2)
|
|
||||||
{
|
|
||||||
if (!s1)
|
|
||||||
{
|
|
||||||
return !s2;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (*s1)
|
|
||||||
{
|
|
||||||
if (*(s1++) != *(s2++))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return !*s2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ namespace std
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
typename remove_reference<T>::type&& move(T&& arg) noexcept
|
typename remove_reference<T>::type&& move(T&& arg)
|
||||||
{
|
{
|
||||||
return static_cast<typename remove_reference<T>::type&&>(arg);
|
return static_cast<typename remove_reference<T>::type&&>(arg);
|
||||||
}
|
}
|
||||||
|
@ -1,284 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "allocator.hpp"
|
|
||||||
#include "exception.hpp"
|
|
||||||
#include "finally.hpp"
|
|
||||||
|
|
||||||
namespace utils
|
|
||||||
{
|
|
||||||
template <typename T, typename Allocator = NonPagedAllocator>
|
|
||||||
requires is_allocator<Allocator>
|
|
||||||
class vector
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using type = T;
|
|
||||||
|
|
||||||
vector() = default;
|
|
||||||
|
|
||||||
~vector()
|
|
||||||
{
|
|
||||||
this->clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
vector(const vector& obj)
|
|
||||||
: vector()
|
|
||||||
{
|
|
||||||
this->operator=(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
vector(vector&& obj) noexcept
|
|
||||||
: vector()
|
|
||||||
{
|
|
||||||
this->operator=(std::move(obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
vector& operator=(const vector& obj)
|
|
||||||
{
|
|
||||||
if (this != &obj)
|
|
||||||
{
|
|
||||||
this->clear();
|
|
||||||
this->reserve(obj.size_);
|
|
||||||
|
|
||||||
for (const auto& i : obj)
|
|
||||||
{
|
|
||||||
this->push_back(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
vector& operator=(vector&& obj) noexcept
|
|
||||||
{
|
|
||||||
if (this != &obj)
|
|
||||||
{
|
|
||||||
this->clear();
|
|
||||||
|
|
||||||
this->storage_ = obj.storage_;
|
|
||||||
this->capacity_ = obj.capacity_;
|
|
||||||
this->size_ = obj.size_;
|
|
||||||
|
|
||||||
obj.storage_ = nullptr;
|
|
||||||
obj.capacity_ = 0;
|
|
||||||
obj.size_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reserve(size_t capacity)
|
|
||||||
{
|
|
||||||
if (this->capacity_ >= capacity)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* old_mem = this->storage_;
|
|
||||||
auto* old_data = this->data();
|
|
||||||
|
|
||||||
this->storage_ = this->allocate_memory_for_capacity(capacity);
|
|
||||||
this->capacity_ = capacity;
|
|
||||||
|
|
||||||
auto _ = utils::finally([&old_mem, this]
|
|
||||||
{
|
|
||||||
this->free_memory(old_mem);
|
|
||||||
});
|
|
||||||
|
|
||||||
auto* data = this->data();
|
|
||||||
for (size_t i = 0; i < this->size_; ++i)
|
|
||||||
{
|
|
||||||
new(data + i) T(std::move(old_data[i]));
|
|
||||||
old_data[i].~T();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
T& push_back(const T& obj)
|
|
||||||
{
|
|
||||||
auto& entry = this->add_uninitialized_entry();
|
|
||||||
|
|
||||||
new(&entry) T(obj);
|
|
||||||
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
T& push_back(T&& obj)
|
|
||||||
{
|
|
||||||
auto& entry = this->add_uninitialized_entry();
|
|
||||||
|
|
||||||
new(&entry) T(std::move(obj));
|
|
||||||
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename... Args>
|
|
||||||
T& emplace_back(Args&&... args)
|
|
||||||
{
|
|
||||||
auto& entry = this->add_uninitialized_entry();
|
|
||||||
|
|
||||||
new(&entry) T(std::forward<Args>(args)...);
|
|
||||||
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
T& operator[](const size_t index)
|
|
||||||
{
|
|
||||||
return this->at(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
const T& operator[](const size_t index) const
|
|
||||||
{
|
|
||||||
return this->at(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
T& at(const size_t index)
|
|
||||||
{
|
|
||||||
if (index >= this->size_)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Out of bounds access");
|
|
||||||
}
|
|
||||||
|
|
||||||
return this->data()[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
const T& at(const size_t index) const
|
|
||||||
{
|
|
||||||
if (index >= this->size_)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Out of bounds access");
|
|
||||||
}
|
|
||||||
|
|
||||||
return this->data()[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear()
|
|
||||||
{
|
|
||||||
auto* data = this->data();
|
|
||||||
for (size_t i = 0; i < this->size_; ++i)
|
|
||||||
{
|
|
||||||
data[i].~T();
|
|
||||||
}
|
|
||||||
|
|
||||||
free_memory(this->storage_);
|
|
||||||
this->storage_ = nullptr;
|
|
||||||
this->capacity_ = 0;
|
|
||||||
this->size_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] size_t capacity() const
|
|
||||||
{
|
|
||||||
return this->capacity_;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] size_t size() const
|
|
||||||
{
|
|
||||||
return this->size_;
|
|
||||||
}
|
|
||||||
|
|
||||||
T* data()
|
|
||||||
{
|
|
||||||
if (!this->storage_)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return static_cast<T*>(align_pointer(this->storage_));
|
|
||||||
}
|
|
||||||
|
|
||||||
const T* data() const
|
|
||||||
{
|
|
||||||
if (!this->storage_)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return static_cast<const T*>(align_pointer(this->storage_));
|
|
||||||
}
|
|
||||||
|
|
||||||
T* begin()
|
|
||||||
{
|
|
||||||
return this->data();
|
|
||||||
}
|
|
||||||
|
|
||||||
const T* begin() const
|
|
||||||
{
|
|
||||||
return this->data();
|
|
||||||
}
|
|
||||||
|
|
||||||
T* end()
|
|
||||||
{
|
|
||||||
return this->data() + this->size_;
|
|
||||||
}
|
|
||||||
|
|
||||||
const T* end() const
|
|
||||||
{
|
|
||||||
return this->data() + this->size_;
|
|
||||||
}
|
|
||||||
|
|
||||||
T* erase(T* iterator)
|
|
||||||
{
|
|
||||||
auto index = iterator - this->begin();
|
|
||||||
if (index < 0 || static_cast<size_t>(index) > this->size_)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Bad iterator");
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto data = this->data();
|
|
||||||
for (size_t i = index + 1; i < this->size_; ++i)
|
|
||||||
{
|
|
||||||
data[i - 1] = std::move(data[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
data[this->size_--].~T();
|
|
||||||
|
|
||||||
return iterator;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool empty() const
|
|
||||||
{
|
|
||||||
return this->size_ == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Allocator allocator_{};
|
|
||||||
void* storage_{nullptr};
|
|
||||||
size_t capacity_{0};
|
|
||||||
size_t size_{0};
|
|
||||||
|
|
||||||
T& add_uninitialized_entry()
|
|
||||||
{
|
|
||||||
if (this->size_ + 1 > this->capacity_)
|
|
||||||
{
|
|
||||||
this->reserve(max(this->capacity_, 5) * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* data = this->data() + this->size_++;
|
|
||||||
return *data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* allocate_memory_for_capacity(const size_t capacity)
|
|
||||||
{
|
|
||||||
constexpr auto alignment = alignof(T);
|
|
||||||
auto* memory = this->allocator_.allocate(capacity * sizeof(T) + alignment);
|
|
||||||
if (!memory)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Failed to allocate memory");
|
|
||||||
}
|
|
||||||
|
|
||||||
return memory;
|
|
||||||
}
|
|
||||||
|
|
||||||
void free_memory(void* memory)
|
|
||||||
{
|
|
||||||
this->allocator_.free(memory);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename U>
|
|
||||||
static U* align_pointer(U* pointer)
|
|
||||||
{
|
|
||||||
const auto align_bits = alignof(T) - 1;
|
|
||||||
auto ptr = reinterpret_cast<intptr_t>(pointer);
|
|
||||||
ptr = (ptr + align_bits) & (~align_bits);
|
|
||||||
|
|
||||||
return reinterpret_cast<U*>(ptr);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
#ifndef EXTERN_C
|
|
||||||
#ifdef __cplusplus
|
|
||||||
#define EXTERN_C extern "C"
|
|
||||||
#else
|
|
||||||
#define EXTERN_C
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef DLL_IMPORT
|
|
||||||
#define DLL_IMPORT __declspec(dllimport)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
EXTERN_C DLL_IMPORT
|
|
||||||
int hyperhook_initialize();
|
|
||||||
|
|
||||||
EXTERN_C DLL_IMPORT
|
|
||||||
int hyperhook_write(unsigned int process_id, unsigned long long address, const void* data,
|
|
||||||
unsigned long long size);
|
|
@ -1,27 +0,0 @@
|
|||||||
file(GLOB_RECURSE library_sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
|
|
||||||
file(GLOB_RECURSE library_headers CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp)
|
|
||||||
|
|
||||||
add_library(library SHARED
|
|
||||||
${library_sources}
|
|
||||||
${library_headers}
|
|
||||||
)
|
|
||||||
|
|
||||||
target_precompile_headers(library PRIVATE
|
|
||||||
std_include.hpp
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(library PRIVATE
|
|
||||||
shared
|
|
||||||
driver_file
|
|
||||||
)
|
|
||||||
|
|
||||||
target_include_directories(library PUBLIC
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../include
|
|
||||||
)
|
|
||||||
|
|
||||||
set_source_files_properties(resource.rc PROPERTIES LANGUAGE RC)
|
|
||||||
target_sources(library PRIVATE
|
|
||||||
resource.rc
|
|
||||||
)
|
|
||||||
|
|
||||||
set_target_properties(library PROPERTIES OUTPUT_NAME "hyperhook")
|
|
@ -1,119 +0,0 @@
|
|||||||
#include "std_include.hpp"
|
|
||||||
|
|
||||||
#include "driver.hpp"
|
|
||||||
#include "driver_device.hpp"
|
|
||||||
#include <driver_file.h>
|
|
||||||
#include <irp_data.hpp>
|
|
||||||
|
|
||||||
#include "utils/io.hpp"
|
|
||||||
|
|
||||||
#define DLL_IMPORT __declspec(dllexport)
|
|
||||||
#include <hyperhook.h>
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
void patch_data(const driver_device& driver_device, const uint32_t pid, const uint64_t address,
|
|
||||||
const uint8_t* buffer,
|
|
||||||
const size_t length)
|
|
||||||
{
|
|
||||||
hook_request hook_request{};
|
|
||||||
hook_request.process_id = pid;
|
|
||||||
hook_request.target_address = reinterpret_cast<void*>(address);
|
|
||||||
|
|
||||||
hook_request.source_data = buffer;
|
|
||||||
hook_request.source_data_size = length;
|
|
||||||
|
|
||||||
driver_device::data input{};
|
|
||||||
input.assign(reinterpret_cast<uint8_t*>(&hook_request),
|
|
||||||
reinterpret_cast<uint8_t*>(&hook_request) + sizeof(hook_request));
|
|
||||||
|
|
||||||
(void)driver_device.send(HOOK_DRV_IOCTL, input);
|
|
||||||
}
|
|
||||||
|
|
||||||
driver_device create_driver_device()
|
|
||||||
{
|
|
||||||
return driver_device{R"(\\.\HyperHook)"};
|
|
||||||
}
|
|
||||||
|
|
||||||
driver create_driver()
|
|
||||||
{
|
|
||||||
return driver{std::filesystem::absolute(DRIVER_NAME), "HyperHookDriver"};
|
|
||||||
}
|
|
||||||
|
|
||||||
driver_device& get_driver_device()
|
|
||||||
{
|
|
||||||
static driver hypervisor{};
|
|
||||||
static driver_device device{};
|
|
||||||
|
|
||||||
if (!device)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
device = create_driver_device();
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (device)
|
|
||||||
{
|
|
||||||
return device;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hypervisor)
|
|
||||||
{
|
|
||||||
hypervisor = create_driver();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!device)
|
|
||||||
{
|
|
||||||
device = create_driver_device();
|
|
||||||
}
|
|
||||||
|
|
||||||
return device;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int hyperhook_initialize()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
const auto& device = get_driver_device();
|
|
||||||
if (device)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
printf("%s\n", e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int hyperhook_write(const unsigned int process_id, const unsigned long long address, const void* data,
|
|
||||||
const unsigned long long size)
|
|
||||||
{
|
|
||||||
if (hyperhook_initialize() == 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
const auto& device = get_driver_device();
|
|
||||||
if (device)
|
|
||||||
{
|
|
||||||
patch_data(device, process_id, address, static_cast<const uint8_t*>(data), size);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
printf("%s\n", e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,94 +0,0 @@
|
|||||||
// Microsoft Visual C++ generated resource script.
|
|
||||||
//
|
|
||||||
#pragma code_page(65001)
|
|
||||||
|
|
||||||
#define APSTUDIO_READONLY_SYMBOLS
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Generated from the TEXTINCLUDE 2 resource.
|
|
||||||
//
|
|
||||||
#include "windows.h"
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
#undef APSTUDIO_READONLY_SYMBOLS
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
// English (United States) resources
|
|
||||||
|
|
||||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
|
||||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
|
||||||
|
|
||||||
#ifdef APSTUDIO_INVOKED
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// TEXTINCLUDE
|
|
||||||
//
|
|
||||||
|
|
||||||
1 TEXTINCLUDE
|
|
||||||
BEGIN
|
|
||||||
"#include ""windows.h""\r\n"
|
|
||||||
"\0"
|
|
||||||
END
|
|
||||||
|
|
||||||
2 TEXTINCLUDE
|
|
||||||
BEGIN
|
|
||||||
"\r\n"
|
|
||||||
"\0"
|
|
||||||
END
|
|
||||||
|
|
||||||
#endif // APSTUDIO_INVOKED
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Version
|
|
||||||
//
|
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
|
||||||
FILEVERSION 1,0,0,0
|
|
||||||
PRODUCTVERSION 1,0,0,0
|
|
||||||
FILEFLAGSMASK 0x3fL
|
|
||||||
#ifdef _DEBUG
|
|
||||||
FILEFLAGS 0x1L
|
|
||||||
#else
|
|
||||||
FILEFLAGS 0x0L
|
|
||||||
#endif
|
|
||||||
FILEOS 0x40004L
|
|
||||||
FILETYPE VFT_DLL
|
|
||||||
FILESUBTYPE 0x0L
|
|
||||||
BEGIN
|
|
||||||
BLOCK "StringFileInfo"
|
|
||||||
BEGIN
|
|
||||||
BLOCK "040904b0"
|
|
||||||
BEGIN
|
|
||||||
VALUE "CompanyName", "momo5502"
|
|
||||||
VALUE "FileDescription", "HyperHook"
|
|
||||||
VALUE "FileVersion", "1.0.0.0"
|
|
||||||
VALUE "InternalName", "HyperHook"
|
|
||||||
VALUE "LegalCopyright", "All rights reserved."
|
|
||||||
VALUE "OriginalFilename", "hyperhook.dll"
|
|
||||||
VALUE "ProductName", "hyperhook"
|
|
||||||
VALUE "ProductVersion", "1.0.0.0"
|
|
||||||
END
|
|
||||||
END
|
|
||||||
BLOCK "VarFileInfo"
|
|
||||||
BEGIN
|
|
||||||
VALUE "Translation", 0x409, 1200
|
|
||||||
END
|
|
||||||
END
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif // English (United States) resources
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef APSTUDIO_INVOKED
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Generated from the TEXTINCLUDE 3 resource.
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
|
||||||
#endif // not APSTUDIO_INVOKED
|
|
@ -1,18 +1,23 @@
|
|||||||
file(GLOB_RECURSE runner_sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
|
file(GLOB_RECURSE runner_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
|
||||||
file(GLOB_RECURSE runner_headers CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp)
|
file(GLOB_RECURSE runner_headers ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp)
|
||||||
|
|
||||||
add_executable(runner #WIN32
|
add_library(hyperhook SHARED #WIN32
|
||||||
${runner_sources}
|
${runner_sources}
|
||||||
${runner_headers}
|
${runner_headers}
|
||||||
)
|
)
|
||||||
|
|
||||||
set_property(TARGET runner APPEND_STRING PROPERTY LINK_FLAGS " /MANIFESTUAC:\"level='requireAdministrator'\"")
|
target_precompile_headers(hyperhook
|
||||||
|
PRIVATE std_include.hpp
|
||||||
|
)
|
||||||
|
|
||||||
target_link_libraries(runner PRIVATE
|
set_property(TARGET hyperhook APPEND_STRING PROPERTY LINK_FLAGS " /MANIFESTUAC:\"level='requireAdministrator'\"")
|
||||||
library
|
|
||||||
|
target_link_libraries(hyperhook
|
||||||
|
shared
|
||||||
|
driver_file
|
||||||
)
|
)
|
||||||
|
|
||||||
set_source_files_properties(resource.rc PROPERTIES LANGUAGE RC)
|
set_source_files_properties(resource.rc PROPERTIES LANGUAGE RC)
|
||||||
target_sources(runner PRIVATE
|
target_sources(hyperhook PRIVATE
|
||||||
resource.rc
|
resource.rc
|
||||||
)
|
)
|
||||||
|
@ -31,10 +31,7 @@ driver::driver(const std::filesystem::path& driver_file, const std::string& serv
|
|||||||
throw std::runtime_error("Unable to create service");
|
throw std::runtime_error("Unable to create service");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!StartServiceA(this->service_, 0, nullptr))
|
StartServiceA(this->service_, 0, nullptr);
|
||||||
{
|
|
||||||
printf("Failed to start service: %d\n", GetLastError());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
driver::~driver()
|
driver::~driver()
|
@ -4,21 +4,15 @@
|
|||||||
class driver
|
class driver
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
driver() = default;
|
|
||||||
driver(const std::filesystem::path& driver_file, const std::string& service_name);
|
driver(const std::filesystem::path& driver_file, const std::string& service_name);
|
||||||
~driver();
|
~driver();
|
||||||
|
|
||||||
driver(const driver&) = delete;
|
driver(const driver&) = delete;
|
||||||
driver& operator=(const driver&) = delete;
|
driver& operator=(const driver&) = delete;
|
||||||
|
|
||||||
driver(driver&& obj) noexcept = default;
|
driver(driver&& obj) noexcept = default;;
|
||||||
driver& operator=(driver&& obj) noexcept = default;
|
driver& operator=(driver&& obj) noexcept = default;
|
||||||
|
|
||||||
operator bool() const
|
|
||||||
{
|
|
||||||
return this->service_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
service_handle manager_{};
|
service_handle manager_{};
|
||||||
service_handle service_{};
|
service_handle service_{};
|
@ -4,7 +4,6 @@
|
|||||||
class driver_device
|
class driver_device
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
driver_device() = default;
|
|
||||||
driver_device(const std::string& driver_device);
|
driver_device(const std::string& driver_device);
|
||||||
~driver_device() = default;
|
~driver_device() = default;
|
||||||
|
|
||||||
@ -14,11 +13,6 @@ public:
|
|||||||
driver_device(driver_device&& obj) noexcept = default;
|
driver_device(driver_device&& obj) noexcept = default;
|
||||||
driver_device& operator=(driver_device&& obj) noexcept = default;
|
driver_device& operator=(driver_device&& obj) noexcept = default;
|
||||||
|
|
||||||
operator bool() const
|
|
||||||
{
|
|
||||||
return this->device_;
|
|
||||||
}
|
|
||||||
|
|
||||||
using data = std::vector<uint8_t>;
|
using data = std::vector<uint8_t>;
|
||||||
bool send(DWORD ioctl_code, const data& input) const;
|
bool send(DWORD ioctl_code, const data& input) const;
|
||||||
bool send(DWORD ioctl_code, const data& input, data& output) const;
|
bool send(DWORD ioctl_code, const data& input, data& output) const;
|
@ -1,133 +1,348 @@
|
|||||||
#include <vector>
|
#include "std_include.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <filesystem>
|
||||||
#include <conio.h>
|
#include <conio.h>
|
||||||
#include <optional>
|
#include <fstream>
|
||||||
#include <stdexcept>
|
#include <set>
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#include "driver.hpp"
|
||||||
#include <Windows.h>
|
#include "driver_device.hpp"
|
||||||
|
#include "process.hpp"
|
||||||
|
|
||||||
#include <hyperhook.h>
|
#include <irp_data.hpp>
|
||||||
|
|
||||||
|
#include "resource.hpp"
|
||||||
|
#include "utils/io.hpp"
|
||||||
|
#include "utils/nt.hpp"
|
||||||
|
|
||||||
bool patch_data(const uint32_t process_id, const uint64_t address, const void* buffer,
|
#pragma comment(lib, "Shlwapi.lib")
|
||||||
|
|
||||||
|
void patch_data(const driver_device& driver_device, const uint32_t pid, const uint64_t addr, const uint8_t* buffer,
|
||||||
const size_t length)
|
const size_t length)
|
||||||
{
|
{
|
||||||
return hyperhook_write(process_id, address, buffer, length) != 0;
|
hook_request hook_request{};
|
||||||
|
hook_request.process_id = pid;
|
||||||
|
hook_request.target_address = reinterpret_cast<void*>(addr);
|
||||||
|
|
||||||
|
hook_request.source_data = buffer;
|
||||||
|
hook_request.source_data_size = length;
|
||||||
|
|
||||||
|
driver_device::data input{};
|
||||||
|
input.assign(reinterpret_cast<uint8_t*>(&hook_request),
|
||||||
|
reinterpret_cast<uint8_t*>(&hook_request) + sizeof(hook_request));
|
||||||
|
|
||||||
|
(void)driver_device.send(HOOK_DRV_IOCTL, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool insert_nop(const uint32_t process_id, const uint64_t address, const size_t length)
|
void insert_nop(const driver_device& driver_device, const uint32_t pid, const uint64_t addr, const size_t length)
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> buffer{};
|
std::vector<uint8_t> buffer{};
|
||||||
buffer.resize(length);
|
buffer.resize(length);
|
||||||
memset(buffer.data(), 0x90, buffer.size());
|
memset(buffer.data(), 0x90, buffer.size());
|
||||||
|
|
||||||
return patch_data(process_id, address, buffer.data(), buffer.size());
|
patch_data(driver_device, pid, addr, buffer.data(), buffer.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<uint32_t> get_process_id_from_window(const char* class_name, const char* window_name)
|
void remove_hooks(const driver_device& driver_device)
|
||||||
{
|
{
|
||||||
const auto window = FindWindowA(class_name, window_name);
|
(void)driver_device.send(UNHOOK_DRV_IOCTL, driver_device::data{});
|
||||||
if (!window)
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> load_resource(const int id)
|
||||||
|
{
|
||||||
|
HMODULE modhandle = nullptr;
|
||||||
|
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
||||||
|
reinterpret_cast<LPCSTR>(&load_resource), &modhandle);
|
||||||
|
|
||||||
|
auto* const res = FindResource(modhandle, MAKEINTRESOURCE(id), RT_RCDATA);
|
||||||
|
if (!res) return {};
|
||||||
|
|
||||||
|
auto* const handle = LoadResource(modhandle, res);
|
||||||
|
if (!handle) return {};
|
||||||
|
|
||||||
|
const auto* data_ptr = static_cast<uint8_t*>(LockResource(handle));
|
||||||
|
const auto data_size = SizeofResource(modhandle, res);
|
||||||
|
|
||||||
|
std::vector<uint8_t> data{};
|
||||||
|
data.assign(data_ptr, data_ptr + data_size);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::filesystem::path extract_driver()
|
||||||
|
{
|
||||||
|
const auto data = load_resource(DRIVER_BINARY);
|
||||||
|
|
||||||
|
auto driver_file = std::filesystem::temp_directory_path() / "driver.sys";
|
||||||
|
|
||||||
|
std::ofstream out_file{};
|
||||||
|
out_file.open(driver_file.generic_string(), std::ios::out | std::ios::binary);
|
||||||
|
out_file.write(reinterpret_cast<const char*>(data.data()), static_cast<std::streamsize>(data.size()));
|
||||||
|
out_file.close();
|
||||||
|
|
||||||
|
return driver_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<size_t, size_t>> find_executable_regions(const std::string& pe_file)
|
||||||
|
{
|
||||||
|
std::string data{};
|
||||||
|
if (!utils::io::read_file(pe_file, &data))
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD process_id{};
|
const utils::nt::library library(reinterpret_cast<HMODULE>(data.data()));
|
||||||
GetWindowThreadProcessId(window, &process_id);
|
if (!library.is_valid())
|
||||||
return static_cast<uint32_t>(process_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void patch_iw5(const uint32_t pid)
|
|
||||||
{
|
|
||||||
insert_nop(pid, 0x4488A8, 2); // Force calling CG_DrawFriendOrFoeTargetBoxes
|
|
||||||
insert_nop(pid, 0x47F6C7, 2); // Ignore blind-eye perks
|
|
||||||
//insert_nop(driver_device, pid, 0x44894C, 2); // Miniconsole
|
|
||||||
|
|
||||||
// Always full alpha
|
|
||||||
constexpr uint8_t data1[] = {0xD9, 0xE8, 0xC3};
|
|
||||||
patch_data(pid, 0x47F0D0, data1, sizeof(data1));
|
|
||||||
|
|
||||||
// Compass show enemies
|
|
||||||
constexpr uint8_t data2[] = {0xEB, 0x13};
|
|
||||||
patch_data(pid, 0x4437A8, data2, sizeof(data2));
|
|
||||||
|
|
||||||
// Enemy arrows
|
|
||||||
constexpr uint8_t data3[] = {0xEB};
|
|
||||||
patch_data(pid, 0x443A2A, data3, sizeof(data3));
|
|
||||||
patch_data(pid, 0x443978, data3, sizeof(data3));
|
|
||||||
}
|
|
||||||
|
|
||||||
void try_patch_iw5()
|
|
||||||
{
|
|
||||||
const auto pid = get_process_id_from_window("IW5", nullptr);
|
|
||||||
if (pid)
|
|
||||||
{
|
{
|
||||||
printf("Patching IW5...\n");
|
return {};
|
||||||
patch_iw5(*pid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void patch_t6(const uint32_t pid)
|
|
||||||
{
|
|
||||||
// Force calling SatellitePingEnemyPlayer
|
|
||||||
insert_nop(pid, 0x7993B1, 2);
|
|
||||||
insert_nop(pid, 0x7993C1, 2);
|
|
||||||
|
|
||||||
// Better vsat updates
|
|
||||||
insert_nop(pid, 0x41D06C, 2); // No time check
|
|
||||||
insert_nop(pid, 0x41D092, 2); // No perk check
|
|
||||||
insert_nop(pid, 0x41D0BB, 2); // No fadeout
|
|
||||||
|
|
||||||
// Enable chopper boxes
|
|
||||||
insert_nop(pid, 0x7B539C, 6); // ShouldDrawPlayerTargetHighlights
|
|
||||||
insert_nop(pid, 0x7B53AE, 6); // Enable chopper boxes
|
|
||||||
insert_nop(pid, 0x7B5461, 6); // Ignore player not visible
|
|
||||||
insert_nop(pid, 0x7B5471, 6); // Ignore blind-eye perks
|
|
||||||
}
|
|
||||||
|
|
||||||
void try_patch_t6()
|
|
||||||
{
|
|
||||||
auto pid = get_process_id_from_window(nullptr, "Call of Duty" "\xAE" ": Black Ops II - Multiplayer");
|
|
||||||
if (!pid)
|
|
||||||
{
|
|
||||||
pid = get_process_id_from_window("CoDBlackOps", nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pid)
|
const auto section_headers = library.get_section_headers();
|
||||||
|
|
||||||
|
std::vector<std::pair<size_t, size_t>> regions{};
|
||||||
|
for (const auto& section_header : section_headers)
|
||||||
{
|
{
|
||||||
printf("Patching T6...\n");
|
if (section_header->Characteristics & IMAGE_SCN_MEM_EXECUTE)
|
||||||
patch_t6(*pid);
|
{
|
||||||
|
regions.emplace_back(section_header->VirtualAddress, section_header->Misc.VirtualSize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return regions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t get_process_id()
|
||||||
int safe_main(const int /*argc*/, char* /*argv*/[])
|
|
||||||
{
|
{
|
||||||
if (hyperhook_initialize() == 0)
|
std::string pid_str{};
|
||||||
|
printf("Please enter the pid: ");
|
||||||
|
std::getline(std::cin, pid_str);
|
||||||
|
|
||||||
|
return atoi(pid_str.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
void watch_regions(const driver_device& driver_device, const uint32_t pid, const HMODULE module,
|
||||||
|
const std::vector<std::pair<size_t, size_t>>& regions)
|
||||||
|
{
|
||||||
|
std::vector<watch_region> watch_regions{};
|
||||||
|
watch_regions.reserve(regions.size());
|
||||||
|
|
||||||
|
for (const auto& region : regions)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Failed to initialize HyperHook");
|
watch_region watch_region{};
|
||||||
|
watch_region.virtual_address = reinterpret_cast<uint8_t*>(module) + region.first;
|
||||||
|
watch_region.length = region.second;
|
||||||
|
watch_regions.push_back(watch_region);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch_request request{};
|
||||||
|
request.process_id = pid;
|
||||||
|
request.watch_regions = watch_regions.data();
|
||||||
|
request.watch_region_count = watch_regions.size();
|
||||||
|
|
||||||
|
driver_device::data out{};
|
||||||
|
size_t out_len = 0;
|
||||||
|
driver_device.send(WATCH_DRV_IOCTL, &request, sizeof(request), out.data(), &out_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint64_t> query_records(const driver_device& driver_device, const size_t current_size = 0)
|
||||||
|
{
|
||||||
|
std::vector<uint64_t> result{};
|
||||||
|
result.resize(std::max(size_t(1024), current_size * 2));
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
try_patch_iw5();
|
char in[1];
|
||||||
try_patch_t6();
|
constexpr auto element_len = sizeof(decltype(result)::value_type);
|
||||||
|
const size_t initial_len = result.size() * element_len;
|
||||||
|
size_t out_len = initial_len;
|
||||||
|
if (!driver_device.send(GET_RECORDS_DRV_IOCTL, in, 0, result.data(), &out_len))
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
printf("Press any key to exit!\n");
|
//if (out_len <= initial_len)
|
||||||
if (_getch() != 'r')
|
if (result.back() == 0)
|
||||||
|
{
|
||||||
|
//result.resize(out_len / element_len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//result.resize((out_len / element_len) + 10);
|
||||||
|
const auto new_size = result.size() * 2;
|
||||||
|
|
||||||
|
result = {};
|
||||||
|
result.resize(result.size() * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shrink
|
||||||
|
size_t i;
|
||||||
|
for (i = result.size(); i > 0; --i)
|
||||||
|
{
|
||||||
|
if (result[i - 1] != 0)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
result.resize(i);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(const int argc, char* argv[])
|
void report_records(const std::atomic_bool& flag, const driver_device& driver_device, const uint32_t pid,
|
||||||
|
const HMODULE target_module, const std::vector<std::pair<size_t, size_t>>& regions)
|
||||||
|
{
|
||||||
|
std::set<uint64_t> access_addresses{};
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
while (!flag)
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
const auto new_records = query_records(driver_device, access_addresses.size());
|
||||||
|
|
||||||
|
for (const auto& new_record : new_records)
|
||||||
|
{
|
||||||
|
if (access_addresses.emplace(new_record).second)
|
||||||
|
{
|
||||||
|
printf("%p\n", reinterpret_cast<void*>(new_record));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((++i) % 5 == 0)
|
||||||
|
{
|
||||||
|
watch_regions(driver_device, pid, target_module, regions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void unsafe_main(const int /*argc*/, char* /*argv*/[])
|
||||||
|
{
|
||||||
|
{
|
||||||
|
const auto driver_file = extract_driver();
|
||||||
|
|
||||||
|
driver driver{driver_file, "MomoLul"};
|
||||||
|
const driver_device driver_device{R"(\\.\HelloDev)"};
|
||||||
|
|
||||||
|
const auto pid = get_process_id();
|
||||||
|
|
||||||
|
printf("Opening process...\n");
|
||||||
|
auto proc = process::open(pid, PROCESS_QUERY_INFORMATION | PROCESS_VM_READ);
|
||||||
|
if (!proc)
|
||||||
|
{
|
||||||
|
printf("Failed to open process...\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Reading modules...\n");
|
||||||
|
const auto modules = process::get_modules(proc);
|
||||||
|
printf("Found %zu modules:\n", modules.size());
|
||||||
|
|
||||||
|
std::vector<std::string> module_files{};
|
||||||
|
module_files.reserve(modules.size());
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (const auto& module : modules)
|
||||||
|
{
|
||||||
|
auto name = process::get_module_filename(proc, module);
|
||||||
|
printf("(%i)\t%p: %s\n", i++, static_cast<void*>(module), name.data());
|
||||||
|
module_files.emplace_back(std::move(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't need this anymore
|
||||||
|
proc = {};
|
||||||
|
|
||||||
|
std::string module_str{};
|
||||||
|
printf("\nPlease enter the module number: ");
|
||||||
|
std::getline(std::cin, module_str);
|
||||||
|
|
||||||
|
const auto module_num = atoi(module_str.data());
|
||||||
|
|
||||||
|
if (module_num < 0 || static_cast<size_t>(module_num) >= modules.size())
|
||||||
|
{
|
||||||
|
printf("Invalid module num\n");
|
||||||
|
_getch();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto target_module = modules[module_num];
|
||||||
|
const auto module_base = reinterpret_cast<uint8_t*>(target_module);
|
||||||
|
const auto& file = module_files[module_num];
|
||||||
|
printf("Analyzing %s...\n", file.data());
|
||||||
|
const auto regions = find_executable_regions(file);
|
||||||
|
|
||||||
|
printf("Executable regions:\n");
|
||||||
|
for (const auto& region : regions)
|
||||||
|
{
|
||||||
|
printf("%p - %zu\n", module_base + region.first, region.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
watch_regions(driver_device, pid, target_module, regions);
|
||||||
|
|
||||||
|
std::atomic_bool terminate{false};
|
||||||
|
std::thread t([&]()
|
||||||
|
{
|
||||||
|
printf("\nWatching access:\n");
|
||||||
|
report_records(terminate, driver_device, pid, target_module, regions);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
_getch();
|
||||||
|
|
||||||
|
terminate = true;
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\nWatching stopped.\n");
|
||||||
|
_getch();
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
// IW5
|
||||||
|
insert_nop(driver_device, pid, 0x4488A8, 2); // Force calling CG_DrawFriendOrFoeTargetBoxes
|
||||||
|
insert_nop(driver_device, pid, 0x47F6C7, 2); // Ignore blind-eye perks
|
||||||
|
//insert_nop(driver_device, pid, 0x44894C, 2); // Miniconsole
|
||||||
|
|
||||||
|
// Always full alpha
|
||||||
|
constexpr uint8_t data1[] = {0xD9, 0xE8, 0xC3};
|
||||||
|
patch_data(driver_device, pid, 0x47F0D0, data1, sizeof(data1));
|
||||||
|
|
||||||
|
// Compass show enemies
|
||||||
|
constexpr uint8_t data2[] = {0xEB, 0x13};
|
||||||
|
patch_data(driver_device, pid, 0x4437A8, data2, sizeof(data2));
|
||||||
|
|
||||||
|
// Enemy arrows
|
||||||
|
constexpr uint8_t data3[] = {0xEB};
|
||||||
|
patch_data(driver_device, pid, 0x443A2A, data3, sizeof(data3));
|
||||||
|
patch_data(driver_device, pid, 0x443978, data3, sizeof(data3));
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
insert_nop(driver_device, pid, 0x441D5A, 6);
|
||||||
|
insert_nop(driver_device, pid, 0x525104, 2);
|
||||||
|
insert_nop(driver_device, pid, 0x525121, 2);
|
||||||
|
|
||||||
|
constexpr uint8_t data3[] = {0xEB};
|
||||||
|
patch_data(driver_device, pid, 0x525087, data3, sizeof(data3));
|
||||||
|
patch_data(driver_device, pid, 0x524E7F, data3, sizeof(data3));
|
||||||
|
patch_data(driver_device, pid, 0x52512C, data3, sizeof(data3));
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*printf("Press any key to disable all hooks!\n");
|
||||||
|
(void)_getch();
|
||||||
|
|
||||||
|
remove_hooks(driver_device);
|
||||||
|
|
||||||
|
printf("Press any key to exit!\n");
|
||||||
|
(void)_getch();*/
|
||||||
|
}
|
||||||
|
|
||||||
|
int _main(const int argc, char* argv[])
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return safe_main(argc, argv);
|
unsafe_main(argc, argv);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (std::exception& e)
|
||||||
{
|
{
|
||||||
@ -143,7 +358,7 @@ int main(const int argc, char* argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int __stdcall WinMain(HINSTANCE, HINSTANCE, char*, int)
|
int __stdcall _WinMain(HINSTANCE, HINSTANCE, char*, int)
|
||||||
{
|
{
|
||||||
AllocConsole();
|
AllocConsole();
|
||||||
AttachConsole(GetCurrentProcessId());
|
AttachConsole(GetCurrentProcessId());
|
||||||
@ -153,5 +368,20 @@ int __stdcall WinMain(HINSTANCE, HINSTANCE, char*, int)
|
|||||||
freopen_s(&fp, "conout$", "w", stdout);
|
freopen_s(&fp, "conout$", "w", stdout);
|
||||||
freopen_s(&fp, "conout$", "w", stderr);
|
freopen_s(&fp, "conout$", "w", stderr);
|
||||||
|
|
||||||
return main(__argc, __argv);
|
return _main(__argc, __argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
const driver_device& get_driver_device()
|
||||||
|
{
|
||||||
|
static const auto driver_file = extract_driver();
|
||||||
|
|
||||||
|
static driver driver{driver_file, "MomoLul"};
|
||||||
|
static const driver_device driver_device{R"(\\.\HelloDev)"};
|
||||||
|
return driver_device;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" __declspec(dllexport) void hyperhook_patch_data(const uint32_t pid, const uint64_t address, const void* data,
|
||||||
|
const size_t length)
|
||||||
|
{
|
||||||
|
patch_data(get_driver_device(), pid, address, static_cast<const uint8_t*>(data), length);
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ namespace process
|
|||||||
std::string buffer{};
|
std::string buffer{};
|
||||||
buffer.resize(1024);
|
buffer.resize(1024);
|
||||||
|
|
||||||
const auto length = GetModuleFileNameExA(process, module, buffer.data(), static_cast<DWORD>(buffer.size()));
|
const auto length = GetModuleFileNameExA(process, module, &buffer[0], static_cast<DWORD>(buffer.size()));
|
||||||
if (length > 0)
|
if (length > 0)
|
||||||
{
|
{
|
||||||
buffer.resize(length);
|
buffer.resize(length);
|
3
src/runner/resource.hpp
Normal file
3
src/runner/resource.hpp
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define DRIVER_BINARY 300
|
@ -8,6 +8,8 @@
|
|||||||
// Generated from the TEXTINCLUDE 2 resource.
|
// Generated from the TEXTINCLUDE 2 resource.
|
||||||
//
|
//
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
|
#include "resource.hpp"
|
||||||
|
#include <driver_file.h>
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
#undef APSTUDIO_READONLY_SYMBOLS
|
#undef APSTUDIO_READONLY_SYMBOLS
|
||||||
@ -61,12 +63,12 @@ BEGIN
|
|||||||
BLOCK "040904b0"
|
BLOCK "040904b0"
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "momo5502"
|
VALUE "CompanyName", "momo5502"
|
||||||
VALUE "FileDescription", "HyperHook Runner"
|
VALUE "FileDescription", "Open-IW5"
|
||||||
VALUE "FileVersion", "1.0.0.0"
|
VALUE "FileVersion", "1.0.0.0"
|
||||||
VALUE "InternalName", "HyperHook Runner"
|
VALUE "InternalName", "Open-IW5"
|
||||||
VALUE "LegalCopyright", "All rights reserved."
|
VALUE "LegalCopyright", "All rights reserved."
|
||||||
VALUE "OriginalFilename", "runner.exe"
|
VALUE "OriginalFilename", "open-iw5.exe"
|
||||||
VALUE "ProductName", "runner"
|
VALUE "ProductName", "open-iw5"
|
||||||
VALUE "ProductVersion", "1.0.0.0"
|
VALUE "ProductVersion", "1.0.0.0"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
@ -82,6 +84,7 @@ END
|
|||||||
//
|
//
|
||||||
|
|
||||||
102 ICON "resources/icon.ico"
|
102 ICON "resources/icon.ico"
|
||||||
|
DRIVER_BINARY RCDATA DRIVER_FILE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,13 +5,10 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <iostream>
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <Shlwapi.h>
|
#include <Shlwapi.h>
|
||||||
#include <ShlObj.h>
|
#include <ShlObj.h>
|
||||||
#include <Psapi.h>
|
#include <Psapi.h>
|
||||||
#include <conio.h>
|
|
||||||
|
|
||||||
#pragma comment(lib, "Shlwapi.lib")
|
#pragma comment(lib, "Shlwapi.lib")
|
@ -1,5 +1,5 @@
|
|||||||
file(GLOB shared_sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
|
file(GLOB shared_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
|
||||||
file(GLOB shared_headers CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp)
|
file(GLOB shared_headers ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp)
|
||||||
|
|
||||||
add_library(shared INTERFACE
|
add_library(shared INTERFACE
|
||||||
${shared_headers}
|
${shared_headers}
|
||||||
|
Reference in New Issue
Block a user