1
0
mirror of https://github.com/momo5502/hypervisor.git synced 2025-07-04 18:21:55 +00:00

Compare commits

..

3 Commits

Author SHA1 Message Date
89f1825b8c More library fixes 2022-06-07 20:39:33 +02:00
b266388c55 Correct resource extraction 2022-06-07 20:33:00 +02:00
6730723d36 Library support 2022-06-07 20:20:21 +02:00
56 changed files with 686 additions and 2262 deletions

View File

@ -20,7 +20,7 @@ jobs:
- release
steps:
- name: Check out files
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
submodules: true
fetch-depth: 0
@ -28,7 +28,7 @@ jobs:
- name: Install WDK
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 +
- name: Setup CMake
@ -38,7 +38,7 @@ jobs:
uses: ammaraskar/msvc-problem-matcher@master
- name: Setup DevCmd
uses: ilammy/msvc-dev-cmd@v1.12.0
uses: ilammy/msvc-dev-cmd@v1.10.0
with:
arch: x64
@ -52,11 +52,10 @@ jobs:
run: cmake --build --preset=${{matrix.configuration}}
- name: Upload ${{matrix.configuration}} binaries
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v2
with:
name: ${{matrix.configuration}} binaries
path: |
build/${{matrix.configuration}}/artifacts/*.exe
build/${{matrix.configuration}}/artifacts/*.dll
build/${{matrix.configuration}}/artifacts/*.pdb
build/${{matrix.configuration}}/artifacts/*.sys

View File

@ -9,7 +9,7 @@ project(hypervisor LANGUAGES C CXX)
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_property(GLOBAL PROPERTY USE_FOLDERS ON)

View File

@ -1,11 +1,7 @@
![license](https://img.shields.io/github/license/momo5502/hypervisor.svg)
[![build](https://github.com/momo5502/hypervisor/workflows/Build/badge.svg)](https://github.com/momo5502/hypervisor/actions)
[![paypal](https://img.shields.io/badge/PayPal-support-blue.svg?logo=paypal)](https://paypal.me/momo5502)
# Hypervisor
Experimental VT-X type 2 hypervisor with EPT hooking/analysis support.
Basically just a tool I use for reverse engineering and stuff. Nothing too serious.
Hypervisor experiments.
Nothing serious. Yet.
## Credits

View File

@ -1,4 +1,3 @@
add_subdirectory(shared)
add_subdirectory(driver)
add_subdirectory(library)
add_subdirectory(runner)

View File

@ -2,9 +2,9 @@ enable_language(ASM_MASM)
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_headers CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp)
file(GLOB driver_asm_sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.asm)
file(GLOB driver_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
file(GLOB driver_headers ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp)
file(GLOB driver_asm_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.asm)
wdk_add_driver(driver
${driver_sources}
@ -17,11 +17,11 @@ target_precompile_headers(driver
cmake_path(NATIVE_PATH PROJECT_SOURCE_DIR NORMALIZE WINDOWS_PROJECT_DIR)
#add_custom_command(TARGET driver
# 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>"
# COMMENT "Signing using Nvidia certificate (Revoked with KB5013942)"
#)
add_custom_command(TARGET driver
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>"
COMMENT "Signing using Nvidia certificate (Revoked with KB5013942)"
)
target_link_libraries(driver
vcrtl_driver
@ -29,29 +29,13 @@ target_link_libraries(driver
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_NAME "$<TARGET_FILE_NAME:driver>")
file (GENERATE
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)

View File

@ -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);
}
};
}

View File

@ -4,12 +4,9 @@
#include "irp.hpp"
#include "exception.hpp"
#include "hypervisor.hpp"
#include "globals.hpp"
#include "process.hpp"
#include "process_callback.hpp"
#define DOS_DEV_NAME L"\\DosDevices\\HyperHook"
#define DEV_NAME L"\\Device\\HyperHook"
#define DOS_DEV_NAME L"\\DosDevices\\HelloDev"
#define DEV_NAME L"\\Device\\HelloDev"
class global_driver
{
@ -18,13 +15,8 @@ public:
: sleep_callback_([this](const sleep_callback::type type)
{
this->sleep_notification(type);
}),
process_callback_(
[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)
})
, irp_(driver_object, DEV_NAME, DOS_DEV_NAME)
{
debug_log("Driver started\n");
}
@ -32,6 +24,7 @@ public:
~global_driver()
{
debug_log("Unloading driver\n");
this->hypervisor_.disable_all_ept_hooks();
}
global_driver(global_driver&&) noexcept = delete;
@ -48,7 +41,6 @@ private:
bool hypervisor_was_enabled_{false};
hypervisor hypervisor_{};
sleep_callback sleep_callback_{};
process_callback::scoped_process_callback process_callback_{};
irp irp_{};
void sleep_notification(const sleep_callback::type type)
@ -66,21 +58,6 @@ private:
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};
@ -93,10 +70,7 @@ _Function_class_(DRIVER_UNLOAD) void unload(const PDRIVER_OBJECT driver_object)
{
global_driver_instance->pre_destroy(driver_object);
delete global_driver_instance;
global_driver_instance = nullptr;
}
globals::run_destructors();
}
catch (std::exception& e)
{
@ -112,8 +86,8 @@ extern "C" NTSTATUS DriverEntry(const PDRIVER_OBJECT driver_object, PUNICODE_STR
{
try
{
debug_log("Starting driver...");
driver_object->DriverUnload = unload;
globals::run_constructors();
global_driver_instance = new global_driver(driver_object);
}
catch (std::exception& e)

View File

@ -7,6 +7,16 @@
#include "memory.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
@ -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->read_access = 0;
watch_point.target_page->execute_access = 1;
watch_point->target_page->write_access = 0;
watch_point->target_page->read_access = 0;
watch_point->target_page->execute_access = 1;
}
watch_point = watch_point->next_watch_point;
}
}
}
@ -111,8 +123,6 @@ namespace vmx
ept_hook::~ept_hook()
{
this->target_page->flags = this->original_entry.flags;
if (mapped_virtual_address)
{
memory::unmap_physical_memory(mapped_virtual_address, PAGE_SIZE);
@ -132,16 +142,35 @@ namespace vmx
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);
}
void ept::install_page_hook(void* destination, const void* source, const size_t length, const process_id source_pid,
const process_id target_pid,
const ept_translation_hint* translation_hint)
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,
ept_translation_hint* 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));
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,
const process_id source_pid, const process_id target_pid,
const utils::list<ept_translation_hint>& hints)
ept_translation_hint* translation_hint)
{
auto current_destination = reinterpret_cast<uint64_t>(destination);
auto current_source = reinterpret_cast<uint64_t>(source);
@ -179,20 +207,21 @@ namespace vmx
const auto page_remaining = PAGE_SIZE - page_offset;
const auto data_to_write = min(page_remaining, current_length);
const ept_translation_hint* relevant_hint = nullptr;
for (const auto& hint : hints)
ept_translation_hint* relevant_hint = nullptr;
ept_translation_hint* current_hint = translation_hint;
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;
}
current_hint = current_hint->next_hint;
}
this->install_page_hook(reinterpret_cast<void*>(current_destination),
reinterpret_cast<const void*>(current_source), data_to_write, source_pid,
target_pid, relevant_hint);
reinterpret_cast<const void*>(current_source), data_to_write, relevant_hint);
current_length -= 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)
@ -291,7 +325,7 @@ namespace vmx
// --------------------------
epdpte temp_epdpte{};
epdpte temp_epdpte;
temp_epdpte.flags = 0;
temp_epdpte.read_access = 1;
temp_epdpte.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,
const process_id target_pid)
void ept::install_code_watch_point(const uint64_t physical_page)
{
const auto physical_base_address = reinterpret_cast<uint64_t>(PAGE_ALIGN(physical_page));
@ -336,21 +369,23 @@ namespace vmx
return;
}
auto& watch_point = this->allocate_ept_code_watch_point();
watch_point.source_pid = source_pid;
watch_point.target_pid = target_pid;
auto* watch_point = this->allocate_ept_code_watch_point();
if (!watch_point)
{
throw std::runtime_error("Failed to allocate watch point");
}
this->split_large_page(physical_base_address);
watch_point.physical_base_address = physical_base_address;
watch_point.target_page = this->get_pml1_entry(physical_base_address);
if (!watch_point.target_page)
watch_point->physical_base_address = physical_base_address;
watch_point->target_page = this->get_pml1_entry(physical_base_address);
if (!watch_point->target_page)
{
throw std::runtime_error("Failed to get PML1 entry for target address");
}
watch_point.target_page->write_access = 0;
watch_point.target_page->read_access = 0;
watch_point->target_page->write_access = 0;
watch_point->target_page->read_access = 0;
}
ept_pointer ept::get_ept_pointer() const
@ -414,61 +449,97 @@ namespace vmx
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;
}
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)
{
throw std::runtime_error("Failed to allocate ept split object");
}
ept_hook& ept::allocate_ept_hook(const uint64_t physical_address)
{
return this->ept_hooks.emplace_back(physical_address);
split->next_split = this->ept_splits;
this->ept_splits = split;
return split;
}
ept_hook* ept::find_ept_hook(const uint64_t physical_address)
ept_hook* ept::allocate_ept_hook(const uint64_t physical_address)
{
for (auto& hook : this->ept_hooks)
auto* hook = memory::allocate_aligned_object<ept_hook>(physical_address);
if (!hook)
{
if (hook.physical_base_address == physical_address)
{
return &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;
}
hook = hook->next_hook;
}
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");
}
ept_code_watch_point* ept::find_ept_code_watch_point(const uint64_t physical_address)
{
for (auto& watch_point : this->ept_code_watch_points)
{
if (watch_point.physical_base_address == physical_address)
{
return &watch_point;
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) const
{
auto* watch_point = this->ept_code_watch_points;
while (watch_point)
{
if (watch_point->physical_base_address == physical_address)
{
return watch_point;
}
watch_point = watch_point->next_watch_point;
}
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);
@ -503,7 +574,12 @@ namespace vmx
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);
@ -549,7 +625,7 @@ namespace vmx
return;
}
auto& split = this->allocate_ept_split();
auto* split = this->allocate_ept_split();
epte pml1_template{};
pml1_template.flags = 0;
@ -560,11 +636,11 @@ namespace vmx
pml1_template.ignore_pat = target_entry->ignore_pat;
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)
{
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{};
@ -573,18 +649,23 @@ namespace vmx
new_pointer.write_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;
}
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_length = length;
ept_translation_hint* current_hints = nullptr;
auto destructor = utils::finally([&current_hints]()
{
ept::free_translation_hints(current_hints);
});
while (current_length != 0)
{
const auto aligned_destination = PAGE_ALIGN(current_destination);
@ -592,24 +673,42 @@ namespace vmx
const auto page_remaining = PAGE_SIZE - page_offset;
const auto data_to_write = min(page_remaining, current_length);
const auto physical_base_address = memory::get_physical_address(aligned_destination);
if (!physical_base_address)
auto* new_hint = memory::allocate_non_paged_object<ept_translation_hint>();
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");
}
auto& current_hint = hints.emplace_back();
current_hint.virtual_base_address = aligned_destination;
current_hint.physical_base_address = physical_base_address;
memcpy(&current_hint.page[0], aligned_destination, PAGE_SIZE);
memcpy(&current_hints->page[0], aligned_destination, PAGE_SIZE);
current_length -= 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)
@ -624,37 +723,4 @@ namespace vmx
*count = i;
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;
}
}

View File

@ -1,19 +1,6 @@
#pragma once
#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
{
@ -23,11 +10,6 @@ namespace vmx
using pml2_ptr = epde;
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
{
DECLSPEC_PAGE_ALIGN pml1 pml1[EPT_PTE_ENTRY_COUNT]{};
@ -37,19 +19,20 @@ namespace vmx
pml2 entry{};
pml2_ptr pointer;
};
ept_split* next_split{nullptr};
};
struct ept_code_watch_point
{
uint64_t physical_base_address{};
pml1* target_page{};
process_id source_pid{0};
process_id target_pid{0};
ept_code_watch_point* next_watch_point{nullptr};
};
struct ept_hook
{
ept_hook(uint64_t physical_base);
ept_hook(const uint64_t physical_base);
~ept_hook();
DECLSPEC_PAGE_ALIGN uint8_t fake_page[PAGE_SIZE]{};
@ -63,8 +46,7 @@ namespace vmx
pml1 execute_entry{};
pml1 readwrite_entry{};
process_id source_pid{0};
process_id target_pid{0};
ept_hook* next_hook{nullptr};
};
struct ept_translation_hint
@ -73,6 +55,8 @@ namespace vmx
uint64_t physical_base_address{};
const void* virtual_base_address{};
ept_translation_hint* next_hint{nullptr};
};
struct guest_context;
@ -90,12 +74,11 @@ namespace vmx
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,
process_id target_pid,
const utils::list<ept_translation_hint>& hints = {});
void disable_all_hooks();
void install_hook(const void* destination, const void* source, size_t length,
ept_translation_hint* translation_hint = nullptr);
void disable_all_hooks() const;
void handle_violation(guest_context& guest_context);
void handle_misconfiguration(guest_context& guest_context) const;
@ -103,12 +86,11 @@ namespace vmx
ept_pointer get_ept_pointer() 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);
bool cleanup_process(process_id process);
private:
DECLSPEC_PAGE_ALIGN pml4 epml4[EPT_PML4E_ENTRY_COUNT];
DECLSPEC_PAGE_ALIGN pml3 epdpt[EPT_PDPTE_ENTRY_COUNT];
@ -116,27 +98,27 @@ namespace vmx
uint64_t access_records[1024];
utils::list<ept_split, utils::AlignedAllocator> ept_splits{};
utils::list<ept_hook, utils::AlignedAllocator> ept_hooks{};
utils::list<ept_code_watch_point> ept_code_watch_points{};
ept_split* ept_splits{nullptr};
ept_hook* ept_hooks{nullptr};
ept_code_watch_point* ept_code_watch_points{nullptr};
pml2* get_pml2_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_hook& allocate_ept_hook(uint64_t physical_address);
ept_hook* find_ept_hook(uint64_t physical_address);
ept_split* allocate_ept_split();
ept_hook* allocate_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* find_ept_code_watch_point(uint64_t physical_address);
ept_code_watch_point* allocate_ept_code_watch_point();
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 install_page_hook(void* destination, const void* source, size_t length, process_id source_pid,
process_id target_pid, const ept_translation_hint* translation_hint = nullptr);
void install_page_hook(void* destination, const void* source, size_t length,
ept_translation_hint* translation_hint = nullptr);
void record_access(uint64_t rip);
};

View File

@ -17,14 +17,9 @@ namespace std
class exception
{
public:
exception() = default;
exception& operator=(const 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 const char* what() const noexcept = 0;
};

View File

@ -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;
}

View File

@ -1,7 +0,0 @@
#pragma once
namespace globals
{
void run_constructors();
void run_destructors();
}

View File

@ -7,20 +7,11 @@
#include "memory.hpp"
#include "thread.hpp"
#include "assembly.hpp"
#include "process.hpp"
#include "string.hpp"
#define DPL_USER 3
#define DPL_SYSTEM 0
typedef struct _EPROCESS
{
DISPATCHER_HEADER Header;
LIST_ENTRY ProfileListHead;
ULONG_PTR DirectoryTableBase;
UCHAR Data[1];
} EPROCESS, *PEPROCESS;
namespace
{
hypervisor* instance{nullptr};
@ -39,6 +30,11 @@ namespace
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()
{
cpuid_eax_01 data{};
@ -53,12 +49,6 @@ namespace
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)
{
special_registers.cr0 = __readcr0();
@ -139,16 +129,11 @@ hypervisor::hypervisor()
instance = this;
if (!is_vmx_supported())
if (!is_virtualization_supported())
{
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");
this->allocate_vm_states();
this->enable();
@ -157,7 +142,6 @@ hypervisor::hypervisor()
hypervisor::~hypervisor()
{
this->disable_all_ept_hooks();
this->disable();
this->free_vm_states();
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,
const process_id source_pid, const process_id target_pid,
const utils::list<vmx::ept_translation_hint>& hints)
vmx::ept_translation_hint* translation_hint)
{
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)
{
@ -197,16 +180,23 @@ bool hypervisor::install_ept_hook(const void* destination, const void* source, c
return false;
}
this->invalidate_cores();
return true;
volatile long failures = 0;
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,
const process_id target_pid, const bool invalidate) const
bool hypervisor::install_ept_code_watch_point(const uint64_t physical_page, bool invalidate) const
{
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)
{
@ -230,13 +220,12 @@ bool hypervisor::install_ept_code_watch_point(const uint64_t physical_page, cons
return true;
}
bool hypervisor::install_ept_code_watch_points(const uint64_t* physical_pages, const size_t count,
const process_id source_pid, const process_id target_pid) const
bool hypervisor::install_ept_code_watch_points(const uint64_t* physical_pages, const size_t count) const
{
bool success = true;
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([&]
@ -253,7 +242,7 @@ void hypervisor::disable_all_ept_hooks() const
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)
{
return;
@ -276,17 +265,6 @@ hypervisor* hypervisor::get_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()
{
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.reserved1 = 0;
result.access_rights.unusable = ~gdt_entry->present;
result.access_rights.unusable = !gdt_entry->present;
return result;
}
@ -469,378 +447,13 @@ void vmx_handle_invd()
__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_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_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)
{
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];
if (guest_context.vp_regs->Rax == 0x41414141 &&
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;
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:
vm_state.ept->handle_misconfiguration(guest_context);
break;
case VMX_EXIT_REASON_EXCEPTION_OR_NMI:
vmx_handle_exception(guest_context);
break;
//case VMX_EXIT_REASON_EXECUTE_RDTSC:
// break;
default:
@ -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_DR7, state->kernel_dr7);
const auto stack_pointer = reinterpret_cast<uintptr_t>(vm_state.stack_buffer) + KERNEL_STACK_SIZE - sizeof(
CONTEXT);
const auto stack_pointer = reinterpret_cast<uintptr_t>(vm_state.stack_buffer) + KERNEL_STACK_SIZE - sizeof(CONTEXT);
__vmx_vmwrite(VMCS_GUEST_RSP, stack_pointer);
__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());
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");
}
if (!is_vmx_available())
{
throw std::runtime_error("VMX not available on this core");
}
vm_state->launch_context.launched = false;
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");
}
enable_syscall_hooking();
}
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();
if (vm_state && this->is_enabled())
this->install_ept_hook_on_core(destination, source, length, translation_hint);
return true;
}
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

View File

@ -19,13 +19,11 @@ public:
bool is_enabled() const;
bool install_ept_hook(const void* destination, const void* source, size_t length, process_id source_pid,
process_id target_pid, const utils::list<vmx::ept_translation_hint>& hints = {});
bool install_ept_hook(const void* destination, const void* source, size_t length,
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 invalidate = true) const;
bool install_ept_code_watch_points(const uint64_t* physical_pages, size_t count, process_id source_pid,
process_id target_pid) const;
bool install_ept_code_watch_point(uint64_t physical_page, bool invalidate = true) const;
bool install_ept_code_watch_points(const uint64_t* physical_pages, size_t count) const;
void disable_all_ept_hooks() const;
@ -33,8 +31,6 @@ public:
static hypervisor* get_instance();
bool cleanup_process(process_id process);
private:
uint32_t vm_state_count_{0};
vmx::state** vm_states_{nullptr};
@ -47,7 +43,10 @@ private:
void allocate_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;
};

View File

@ -37,12 +37,11 @@ namespace
return STATUS_SUCCESS;
}
utils::list<vmx::ept_translation_hint> generate_translation_hints(uint32_t process_id, const void* target_address,
size_t size)
vmx::ept_translation_hint* generate_translation_hints(uint32_t process_id, const void* target_address, 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);
@ -53,7 +52,8 @@ namespace
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);
}
@ -62,7 +62,9 @@ namespace
debug_log("Generating translation hints for address: %p\n", target_address);
translation_hints = vmx::ept::generate_translation_hints(target_address, size);
}).join();
});
t.join();
return translation_hints;
}
@ -81,18 +83,24 @@ namespace
throw std::runtime_error("Failed to copy buffer");
}
vmx::ept_translation_hint* translation_hints = nullptr;
auto destructor = utils::finally([&translation_hints]()
{
vmx::ept::free_translation_hints(translation_hints);
});
memcpy(buffer.get(), request.source_data, request.source_data_size);
const auto translation_hints = generate_translation_hints(request.process_id, request.target_address,
translation_hints = generate_translation_hints(request.process_id, request.target_address,
request.source_data_size);
if (translation_hints.empty())
if (!translation_hints)
{
debug_log("Failed to generate tranlsation hints\n");
return;
}
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()
@ -123,7 +131,7 @@ namespace
void watch_regions(const watch_request& watch_request)
{
const auto* hypervisor = hypervisor::get_instance();
auto* hypervisor = hypervisor::get_instance();
if (!hypervisor)
{
throw std::runtime_error("Hypervisor not installed");
@ -160,7 +168,7 @@ namespace
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);
@ -210,8 +218,7 @@ namespace
t.join();
debug_log("Installing watch points...\n");
(void)hypervisor->install_ept_code_watch_points(page_buffer.get(), index, process::get_current_process_id(),
watch_request_copy.process_id);
(void)hypervisor->install_ept_code_watch_points(page_buffer.get(), index);
debug_log("Watch points installed\n");
}
@ -233,7 +240,7 @@ namespace
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)
{
throw std::runtime_error("Hypervisor not installed");
@ -252,6 +259,7 @@ namespace
irp->IoStatus.Status = STATUS_SUCCESS;
const auto irp_sp = IoGetCurrentIrpStackLocation(irp);
if (irp_sp)
{
const auto ioctr_code = irp_sp->Parameters.DeviceIoControl.IoControlCode;

View File

@ -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;
}
};
}

View File

@ -3,5 +3,5 @@
#ifdef NDEBUG__
#define debug_log(...)
#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

View File

@ -68,18 +68,6 @@ namespace 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)
{
return static_cast<uint64_t>(MmGetPhysicalAddress(address).QuadPart);
@ -114,10 +102,7 @@ namespace memory
void* allocate_non_paged_memory(const size_t size)
{
#pragma warning(push)
#pragma warning(disable: 4996)
void* memory = ExAllocatePoolWithTag(NonPagedPool, size, 'MOMO');
#pragma warning(pop)
if (memory)
{
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
{
@ -151,13 +136,13 @@ namespace memory
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");
}
}
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
{
@ -172,7 +157,7 @@ namespace memory
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");
}

View File

@ -10,9 +10,6 @@ namespace memory
_IRQL_requires_max_(DISPATCH_LEVEL)
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);
void* get_virtual_address(uint64_t address);
@ -30,10 +27,10 @@ namespace memory
_IRQL_requires_max_(DISPATCH_LEVEL)
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);
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);
template <typename T, typename... Args>

View File

@ -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
VOID
NTAPI

View File

@ -37,7 +37,7 @@ namespace process
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)
@ -80,14 +80,14 @@ namespace process
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_)
{
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
@ -111,25 +111,16 @@ namespace process
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));
}
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)
PEPROCESS process{};
const uint64_t process_id_long = process_id;
if (PsLookupProcessByProcessId(HANDLE(process_id_long), &process) != STATUS_SUCCESS)
{
return {};
}
return process_handle{process_obj, true};
return process_handle{process, true};
}
process_handle get_current_process()
@ -137,11 +128,6 @@ namespace process
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)
{
if (!process || !process.is_alive())

View File

@ -19,7 +19,7 @@ namespace process
operator PEPROCESS() const;
bool is_alive() const;
process_id get_id() const;
uint32_t get_id() const;
const char* get_image_filename() const;
@ -30,14 +30,9 @@ namespace process
void release();
};
process_id process_id_from_handle(HANDLE handle);
HANDLE handle_from_process_id(process_id process);
process_handle find_process_by_id(process_id process);
process_handle find_process_by_id(uint32_t process_id);
process_handle get_current_process();
process_id get_current_process_id();
class scoped_process_attacher
{
public:

View File

@ -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));
}
}
}

View File

@ -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_{};
};
}

View File

@ -1 +0,0 @@
#pragma once

View File

@ -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

View File

@ -16,6 +16,3 @@
#include "nt_ext.hpp"
#include "new.hpp"
#include "exception.hpp"
// Not sure if this is good, but fuck it.
using process_id = uint32_t;

View File

@ -17,22 +17,4 @@ namespace string
RtlStringCchPrintfA(buffer, VA_BUFFER_SIZE, message, std::forward<Args>(args)...);
return buffer;
}
inline bool equal(const char* s1, const char* s2)
{
if (!s1)
{
return !s2;
}
while (*s1)
{
if (*(s1++) != *(s2++))
{
return false;
}
}
return !*s2;
}
}

View File

@ -27,7 +27,7 @@ namespace std
};
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);
}

View File

@ -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);
}
};
}

View File

@ -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);

View File

@ -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")

View File

@ -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;
}

View File

@ -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

View File

@ -1,18 +1,23 @@
file(GLOB_RECURSE runner_sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
file(GLOB_RECURSE runner_headers CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp)
file(GLOB_RECURSE runner_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
file(GLOB_RECURSE runner_headers ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp)
add_executable(runner #WIN32
add_library(hyperhook SHARED #WIN32
${runner_sources}
${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
library
set_property(TARGET hyperhook APPEND_STRING PROPERTY LINK_FLAGS " /MANIFESTUAC:\"level='requireAdministrator'\"")
target_link_libraries(hyperhook
shared
driver_file
)
set_source_files_properties(resource.rc PROPERTIES LANGUAGE RC)
target_sources(runner PRIVATE
target_sources(hyperhook PRIVATE
resource.rc
)

View File

@ -31,10 +31,7 @@ driver::driver(const std::filesystem::path& driver_file, const std::string& serv
throw std::runtime_error("Unable to create service");
}
if(!StartServiceA(this->service_, 0, nullptr))
{
printf("Failed to start service: %d\n", GetLastError());
}
StartServiceA(this->service_, 0, nullptr);
}
driver::~driver()

View File

@ -4,21 +4,15 @@
class driver
{
public:
driver() = default;
driver(const std::filesystem::path& driver_file, const std::string& service_name);
~driver();
driver(const driver&) = delete;
driver& operator=(const driver&) = delete;
driver(driver&& obj) noexcept = default;
driver(driver&& obj) noexcept = default;;
driver& operator=(driver&& obj) noexcept = default;
operator bool() const
{
return this->service_;
}
private:
service_handle manager_{};
service_handle service_{};

View File

@ -4,7 +4,6 @@
class driver_device
{
public:
driver_device() = default;
driver_device(const std::string& driver_device);
~driver_device() = default;
@ -14,11 +13,6 @@ public:
driver_device(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>;
bool send(DWORD ioctl_code, const data& input) const;
bool send(DWORD ioctl_code, const data& input, data& output) const;

View File

@ -1,133 +1,348 @@
#include <vector>
#include "std_include.hpp"
#include <iostream>
#include <filesystem>
#include <conio.h>
#include <optional>
#include <stdexcept>
#include <fstream>
#include <set>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include "driver.hpp"
#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)
{
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{};
buffer.resize(length);
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);
if (!window)
(void)driver_device.send(UNHOOK_DRV_IOCTL, driver_device::data{});
}
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 {};
}
DWORD process_id{};
GetWindowThreadProcessId(window, &process_id);
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)
const utils::nt::library library(reinterpret_cast<HMODULE>(data.data()));
if (!library.is_valid())
{
printf("Patching IW5...\n");
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);
return {};
}
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");
patch_t6(*pid);
if (section_header->Characteristics & IMAGE_SCN_MEM_EXECUTE)
{
regions.emplace_back(section_header->VirtualAddress, section_header->Misc.VirtualSize);
}
}
return regions;
}
int safe_main(const int /*argc*/, char* /*argv*/[])
uint32_t get_process_id()
{
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)
{
try_patch_iw5();
try_patch_t6();
char in[1];
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 (_getch() != 'r')
//if (out_len <= initial_len)
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;
}
}
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
{
return safe_main(argc, argv);
unsafe_main(argc, argv);
return 0;
}
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();
AttachConsole(GetCurrentProcessId());
@ -153,5 +368,20 @@ int __stdcall WinMain(HINSTANCE, HINSTANCE, char*, int)
freopen_s(&fp, "conout$", "w", stdout);
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);
}

View File

@ -69,7 +69,7 @@ namespace process
std::string buffer{};
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)
{
buffer.resize(length);

3
src/runner/resource.hpp Normal file
View File

@ -0,0 +1,3 @@
#pragma once
#define DRIVER_BINARY 300

View File

@ -8,6 +8,8 @@
// Generated from the TEXTINCLUDE 2 resource.
//
#include "windows.h"
#include "resource.hpp"
#include <driver_file.h>
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
@ -61,12 +63,12 @@ BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "momo5502"
VALUE "FileDescription", "HyperHook Runner"
VALUE "FileDescription", "Open-IW5"
VALUE "FileVersion", "1.0.0.0"
VALUE "InternalName", "HyperHook Runner"
VALUE "InternalName", "Open-IW5"
VALUE "LegalCopyright", "All rights reserved."
VALUE "OriginalFilename", "runner.exe"
VALUE "ProductName", "runner"
VALUE "OriginalFilename", "open-iw5.exe"
VALUE "ProductName", "open-iw5"
VALUE "ProductVersion", "1.0.0.0"
END
END
@ -82,6 +84,7 @@ END
//
102 ICON "resources/icon.ico"
DRIVER_BINARY RCDATA DRIVER_FILE

View File

@ -5,13 +5,10 @@
#include <mutex>
#include <filesystem>
#include <functional>
#include <iostream>
#include <set>
#include <Windows.h>
#include <Shlwapi.h>
#include <ShlObj.h>
#include <Psapi.h>
#include <conio.h>
#pragma comment(lib, "Shlwapi.lib")

View File

@ -1,5 +1,5 @@
file(GLOB shared_sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
file(GLOB shared_headers CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp)
file(GLOB shared_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
file(GLOB shared_headers ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp)
add_library(shared INTERFACE
${shared_headers}