mirror of
https://github.com/momo5502/hypervisor.git
synced 2025-07-03 01:31:51 +00:00
Compare commits
70 Commits
feature/li
...
aab19ccb34
Author | SHA1 | Date | |
---|---|---|---|
aab19ccb34 | |||
4051223045 | |||
dbc7b5422e | |||
de99750e53 | |||
5a796c7aae | |||
af3d08e791 | |||
8da8fa7f8e | |||
111b9c9a01 | |||
243ddeebdb | |||
a6e484d9df | |||
b3dec2e80c | |||
4d68b0bb78 | |||
79fd28ad7e | |||
7073f7169a | |||
b74f712975 | |||
e9d4b3345c | |||
55234c3504 | |||
69f66d11e4 | |||
2d8de2835c | |||
b9c4d85bb0 | |||
30873e4ebb | |||
53c24b8325 | |||
0896133821 | |||
d5bf81d99b | |||
761490c808 | |||
c1d0a354c2 | |||
08727330e1 | |||
7a7f757f09 | |||
8d2b581adf | |||
046df34929 | |||
35f18600b8 | |||
bc4ea8c9a2 | |||
0e2450f47e | |||
083e67e1d7 | |||
8b4c277f11 | |||
a3f67b20b8 | |||
4cde82aae1 | |||
90889e7d32 | |||
938d929de6 | |||
28dd94f2ef | |||
f8f636a829 | |||
4cbbaed72f | |||
95120b73ab | |||
65417e3e7a | |||
a6e0d7de47 | |||
c2587af857 | |||
05a677a19a | |||
06db3371ad | |||
6f7f0f74c4 | |||
e379103e0f | |||
531305e104 | |||
1d23c10734 | |||
33b44f1dc1 | |||
dcab775bb9 | |||
129380419d | |||
a67e2ae833 | |||
d1ad347e84 | |||
d778a3190a | |||
4cd7e711f7 | |||
952e89adae | |||
10828cff46 | |||
9bf0b94e29 | |||
cf013601b8 | |||
2fad5d0684 | |||
620de17a01 | |||
6253a44356 | |||
842de71a69 | |||
c95f3ce9ce | |||
325118892d | |||
1519181150 |
15
.github/dependabot.yml
vendored
15
.github/dependabot.yml
vendored
@ -1,7 +1,12 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: gitsubmodule
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 10
|
||||
- package-ecosystem: gitsubmodule
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: monthly
|
||||
open-pull-requests-limit: 10
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: monthly
|
||||
|
14
.github/workflows/build.yml
vendored
14
.github/workflows/build.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
||||
- release
|
||||
steps:
|
||||
- name: Check out files
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
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=2166289
|
||||
curl -L --output wdksetup.exe https://go.microsoft.com/fwlink/?linkid=2196230
|
||||
cmd /c start /wait wdksetup.exe /ceip off /quiet /features +
|
||||
|
||||
- name: Setup CMake
|
||||
@ -38,13 +38,10 @@ jobs:
|
||||
uses: ammaraskar/msvc-problem-matcher@master
|
||||
|
||||
- name: Setup DevCmd
|
||||
uses: ilammy/msvc-dev-cmd@v1.10.0
|
||||
uses: ilammy/msvc-dev-cmd@v1.13.0
|
||||
with:
|
||||
arch: x64
|
||||
|
||||
- name: Setup Ninja
|
||||
uses: ashutoshvarma/setup-ninja@master
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake --preset=${{matrix.configuration}}
|
||||
|
||||
@ -52,10 +49,11 @@ jobs:
|
||||
run: cmake --build --preset=${{matrix.configuration}}
|
||||
|
||||
- name: Upload ${{matrix.configuration}} binaries
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
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
|
||||
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_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
|
10
README.md
10
README.md
@ -1,11 +1,15 @@
|
||||

|
||||
[](https://github.com/momo5502/hypervisor/actions)
|
||||
[](https://paypal.me/momo5502)
|
||||
|
||||
# Hypervisor
|
||||
|
||||
Hypervisor experiments.
|
||||
Nothing serious. Yet.
|
||||
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.
|
||||
|
||||
## Credits
|
||||
|
||||
<a href="https://github.com/ionescu007/SimpleVisor">SimpleVisor</a>
|
||||
<a href="https://github.com/Gbps/gbhv/tree/master/gbhv">gbhv</a>
|
||||
|
||||
<a href="https://www.flaticon.com/free-icon/cyber-security_2092663?related_id=2092663&origin=tag" title="cyber security icons">Icon</a>
|
||||
<a href="https://www.flaticon.com/free-icon/cyber-security_2092663?related_id=2092663&origin=tag" title="cyber security icons">Icon</a>
|
||||
|
2
external/FindWDK
vendored
2
external/FindWDK
vendored
Submodule external/FindWDK updated: 43fd504e1d...04b4151f6d
2
external/ia32-doc
vendored
2
external/ia32-doc
vendored
Submodule external/ia32-doc updated: 77e021b690...2bc5284e04
@ -1,3 +1,4 @@
|
||||
add_subdirectory(shared)
|
||||
add_subdirectory(driver)
|
||||
add_subdirectory(runner)
|
||||
add_subdirectory(library)
|
||||
add_subdirectory(runner)
|
||||
|
@ -2,9 +2,9 @@ enable_language(ASM_MASM)
|
||||
|
||||
string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
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,13 +29,29 @@ 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"
|
||||
CONTENT "#define DRIVER_FILE \"${DRIVER_FILE}\"\n#define DRIVER_NAME \"${DRIVER_NAME}\"\n"
|
||||
)
|
||||
|
||||
add_library(driver_file INTERFACE)
|
||||
|
38
src/driver/allocator.hpp
Normal file
38
src/driver/allocator.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
#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,9 +4,12 @@
|
||||
#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\\HelloDev"
|
||||
#define DEV_NAME L"\\Device\\HelloDev"
|
||||
#define DOS_DEV_NAME L"\\DosDevices\\HyperHook"
|
||||
#define DEV_NAME L"\\Device\\HyperHook"
|
||||
|
||||
class global_driver
|
||||
{
|
||||
@ -15,8 +18,13 @@ public:
|
||||
: sleep_callback_([this](const sleep_callback::type type)
|
||||
{
|
||||
this->sleep_notification(type);
|
||||
})
|
||||
, irp_(driver_object, DEV_NAME, DOS_DEV_NAME)
|
||||
}),
|
||||
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)
|
||||
{
|
||||
debug_log("Driver started\n");
|
||||
}
|
||||
@ -24,7 +32,6 @@ public:
|
||||
~global_driver()
|
||||
{
|
||||
debug_log("Unloading driver\n");
|
||||
this->hypervisor_.disable_all_ept_hooks();
|
||||
}
|
||||
|
||||
global_driver(global_driver&&) noexcept = delete;
|
||||
@ -41,6 +48,7 @@ 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)
|
||||
@ -58,6 +66,21 @@ 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};
|
||||
@ -70,7 +93,10 @@ _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)
|
||||
{
|
||||
@ -87,6 +113,7 @@ extern "C" NTSTATUS DriverEntry(const PDRIVER_OBJECT driver_object, PUNICODE_STR
|
||||
try
|
||||
{
|
||||
driver_object->DriverUnload = unload;
|
||||
globals::run_constructors();
|
||||
global_driver_instance = new global_driver(driver_object);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
|
@ -7,16 +7,6 @@
|
||||
#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
|
||||
@ -95,18 +85,16 @@ namespace vmx
|
||||
}
|
||||
}
|
||||
|
||||
void reset_all_watch_point_pages(ept_code_watch_point* watch_point)
|
||||
void reset_all_watch_point_pages(utils::list<ept_code_watch_point>& watch_points)
|
||||
{
|
||||
while (watch_point)
|
||||
for (const auto& watch_point : watch_points)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -123,6 +111,8 @@ 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);
|
||||
@ -142,35 +132,16 @@ namespace vmx
|
||||
|
||||
ept::~ept()
|
||||
{
|
||||
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);
|
||||
}
|
||||
this->disable_all_hooks();
|
||||
}
|
||||
|
||||
void ept::install_page_hook(void* destination, const void* source, const size_t length,
|
||||
ept_translation_hint* translation_hint)
|
||||
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->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);
|
||||
@ -194,7 +165,8 @@ namespace vmx
|
||||
}
|
||||
|
||||
void ept::install_hook(const void* destination, const void* source, const size_t length,
|
||||
ept_translation_hint* translation_hint)
|
||||
const process_id source_pid, const process_id target_pid,
|
||||
const utils::list<ept_translation_hint>& hints)
|
||||
{
|
||||
auto current_destination = reinterpret_cast<uint64_t>(destination);
|
||||
auto current_source = reinterpret_cast<uint64_t>(source);
|
||||
@ -207,21 +179,20 @@ namespace vmx
|
||||
const auto page_remaining = PAGE_SIZE - page_offset;
|
||||
const auto data_to_write = min(page_remaining, current_length);
|
||||
|
||||
ept_translation_hint* relevant_hint = nullptr;
|
||||
ept_translation_hint* current_hint = translation_hint;
|
||||
while (current_hint)
|
||||
|
||||
const ept_translation_hint* relevant_hint = nullptr;
|
||||
for (const auto& hint : hints)
|
||||
{
|
||||
if (current_hint->virtual_base_address == aligned_destination)
|
||||
if (hint.virtual_base_address == aligned_destination)
|
||||
{
|
||||
relevant_hint = current_hint;
|
||||
relevant_hint = &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, relevant_hint);
|
||||
reinterpret_cast<const void*>(current_source), data_to_write, source_pid,
|
||||
target_pid, relevant_hint);
|
||||
|
||||
current_length -= data_to_write;
|
||||
current_destination += data_to_write;
|
||||
@ -229,14 +200,9 @@ namespace vmx
|
||||
}
|
||||
}
|
||||
|
||||
void ept::disable_all_hooks() const
|
||||
void ept::disable_all_hooks()
|
||||
{
|
||||
auto* hook = this->ept_hooks;
|
||||
while (hook)
|
||||
{
|
||||
hook->target_page->flags = hook->original_entry.flags;
|
||||
hook = hook->next_hook;
|
||||
}
|
||||
this->ept_hooks.clear();
|
||||
}
|
||||
|
||||
void ept::handle_violation(guest_context& guest_context)
|
||||
@ -325,7 +291,7 @@ namespace vmx
|
||||
|
||||
// --------------------------
|
||||
|
||||
epdpte temp_epdpte;
|
||||
pml1 temp_epdpte{};
|
||||
temp_epdpte.flags = 0;
|
||||
temp_epdpte.read_access = 1;
|
||||
temp_epdpte.write_access = 1;
|
||||
@ -340,7 +306,7 @@ namespace vmx
|
||||
|
||||
// --------------------------
|
||||
|
||||
epde_2mb temp_epde{};
|
||||
pml2 temp_epde{};
|
||||
temp_epde.flags = 0;
|
||||
temp_epde.read_access = 1;
|
||||
temp_epde.write_access = 1;
|
||||
@ -360,7 +326,8 @@ namespace vmx
|
||||
}
|
||||
}
|
||||
|
||||
void ept::install_code_watch_point(const uint64_t physical_page)
|
||||
void ept::install_code_watch_point(const uint64_t physical_page, const process_id source_pid,
|
||||
const process_id target_pid)
|
||||
{
|
||||
const auto physical_base_address = reinterpret_cast<uint64_t>(PAGE_ALIGN(physical_page));
|
||||
|
||||
@ -369,23 +336,21 @@ namespace vmx
|
||||
return;
|
||||
}
|
||||
|
||||
auto* watch_point = this->allocate_ept_code_watch_point();
|
||||
if (!watch_point)
|
||||
{
|
||||
throw std::runtime_error("Failed to allocate watch point");
|
||||
}
|
||||
auto& watch_point = this->allocate_ept_code_watch_point();
|
||||
watch_point.source_pid = source_pid;
|
||||
watch_point.target_pid = target_pid;
|
||||
|
||||
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
|
||||
@ -435,111 +400,75 @@ namespace vmx
|
||||
}
|
||||
|
||||
const auto* pml2 = reinterpret_cast<pml2_ptr*>(pml2_entry);
|
||||
auto* pml1 = this->find_pml1_table(pml2->page_frame_number * PAGE_SIZE);
|
||||
if (!pml1)
|
||||
auto* pml1_table = this->find_pml1_table(pml2->page_frame_number * PAGE_SIZE);
|
||||
if (!pml1_table)
|
||||
{
|
||||
pml1 = static_cast<epte*>(memory::get_virtual_address(pml2->page_frame_number * PAGE_SIZE));
|
||||
pml1_table = static_cast<pml1*>(memory::get_virtual_address(pml2->page_frame_number * PAGE_SIZE));
|
||||
}
|
||||
|
||||
if (!pml1)
|
||||
if (!pml1_table)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &pml1[ADDRMASK_EPT_PML1_INDEX(physical_address)];
|
||||
return &pml1_table[ADDRMASK_EPT_PML1_INDEX(physical_address)];
|
||||
}
|
||||
|
||||
pml1* ept::find_pml1_table(const uint64_t physical_address) const
|
||||
pml1* ept::find_pml1_table(const uint64_t physical_address)
|
||||
{
|
||||
auto* split = this->ept_splits;
|
||||
while (split)
|
||||
for (auto& split : this->ept_splits)
|
||||
{
|
||||
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()
|
||||
{
|
||||
auto* split = memory::allocate_aligned_object<ept_split>();
|
||||
if (!split)
|
||||
{
|
||||
throw std::runtime_error("Failed to allocate ept split object");
|
||||
}
|
||||
|
||||
split->next_split = this->ept_splits;
|
||||
this->ept_splits = split;
|
||||
|
||||
return split;
|
||||
return this->ept_splits.emplace_back();
|
||||
}
|
||||
|
||||
ept_hook* ept::allocate_ept_hook(const uint64_t physical_address)
|
||||
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;
|
||||
return this->ept_hooks.emplace_back(physical_address);
|
||||
}
|
||||
|
||||
ept_hook* ept::find_ept_hook(const uint64_t physical_address) const
|
||||
ept_hook* ept::find_ept_hook(const uint64_t physical_address)
|
||||
{
|
||||
auto* hook = this->ept_hooks;
|
||||
while (hook)
|
||||
for (auto& hook : this->ept_hooks)
|
||||
{
|
||||
if (hook->physical_base_address == physical_address)
|
||||
if (hook.physical_base_address == physical_address)
|
||||
{
|
||||
return hook;
|
||||
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()
|
||||
{
|
||||
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;
|
||||
return this->ept_code_watch_points.emplace_back();
|
||||
}
|
||||
|
||||
ept_code_watch_point* ept::find_ept_code_watch_point(const uint64_t physical_address) const
|
||||
ept_code_watch_point* ept::find_ept_code_watch_point(const uint64_t physical_address)
|
||||
{
|
||||
auto* watch_point = this->ept_code_watch_points;
|
||||
while (watch_point)
|
||||
for (auto& watch_point : this->ept_code_watch_points)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
ept_hook* ept::get_or_create_ept_hook(void* destination, ept_translation_hint* translation_hint)
|
||||
ept_hook* ept::get_or_create_ept_hook(void* destination, const ept_translation_hint* translation_hint)
|
||||
{
|
||||
const auto virtual_target = PAGE_ALIGN(destination);
|
||||
|
||||
@ -574,12 +503,7 @@ namespace vmx
|
||||
return hook;
|
||||
}
|
||||
|
||||
hook = this->allocate_ept_hook(physical_base_address);
|
||||
|
||||
if (!hook)
|
||||
{
|
||||
throw std::runtime_error("Failed to allocate hook");
|
||||
}
|
||||
hook = &this->allocate_ept_hook(physical_base_address);
|
||||
|
||||
this->split_large_page(physical_address);
|
||||
|
||||
@ -625,9 +549,9 @@ namespace vmx
|
||||
return;
|
||||
}
|
||||
|
||||
auto* split = this->allocate_ept_split();
|
||||
auto& split = this->allocate_ept_split();
|
||||
|
||||
epte pml1_template{};
|
||||
pml1 pml1_template{};
|
||||
pml1_template.flags = 0;
|
||||
pml1_template.read_access = 1;
|
||||
pml1_template.write_access = 1;
|
||||
@ -636,11 +560,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{};
|
||||
@ -649,23 +573,18 @@ 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;
|
||||
}
|
||||
|
||||
ept_translation_hint* ept::generate_translation_hints(const void* destination, const size_t length)
|
||||
utils::list<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([¤t_hints]()
|
||||
{
|
||||
ept::free_translation_hints(current_hints);
|
||||
});
|
||||
|
||||
while (current_length != 0)
|
||||
{
|
||||
const auto aligned_destination = PAGE_ALIGN(current_destination);
|
||||
@ -673,42 +592,24 @@ namespace vmx
|
||||
const auto page_remaining = PAGE_SIZE - page_offset;
|
||||
const auto data_to_write = min(page_remaining, current_length);
|
||||
|
||||
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)
|
||||
const auto physical_base_address = memory::get_physical_address(aligned_destination);
|
||||
if (!physical_base_address)
|
||||
{
|
||||
throw std::runtime_error("Failed to resolve physical address");
|
||||
}
|
||||
|
||||
memcpy(¤t_hints->page[0], aligned_destination, PAGE_SIZE);
|
||||
auto& current_hint = hints.emplace_back();
|
||||
|
||||
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_destination += data_to_write;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
return hints;
|
||||
}
|
||||
|
||||
uint64_t* ept::get_access_records(size_t* count)
|
||||
@ -723,4 +624,37 @@ 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;
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,32 @@
|
||||
#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
|
||||
{
|
||||
using pml4 = ept_pml4;
|
||||
using pml3 = epdpte;
|
||||
using pml2 = epde_2mb;
|
||||
using pml2_ptr = epde;
|
||||
using pml1 = epte;
|
||||
using pml4 = ept_pml4e;
|
||||
using pml3 = ept_pdpte;
|
||||
using pml2 = ept_pde_2mb;
|
||||
using pml2_ptr = ept_pde;
|
||||
using pml1 = ept_pte;
|
||||
|
||||
using pml4_entry = pml4e_64;
|
||||
using pml3_entry = pdpte_64;
|
||||
using pml2_entry = pde_64;
|
||||
using pml1_entry = pte_64;
|
||||
|
||||
struct ept_split
|
||||
{
|
||||
@ -19,20 +37,19 @@ 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{};
|
||||
ept_code_watch_point* next_watch_point{nullptr};
|
||||
process_id source_pid{0};
|
||||
process_id target_pid{0};
|
||||
};
|
||||
|
||||
struct ept_hook
|
||||
{
|
||||
ept_hook(const uint64_t physical_base);
|
||||
ept_hook(uint64_t physical_base);
|
||||
~ept_hook();
|
||||
|
||||
DECLSPEC_PAGE_ALIGN uint8_t fake_page[PAGE_SIZE]{};
|
||||
@ -46,7 +63,8 @@ namespace vmx
|
||||
pml1 execute_entry{};
|
||||
pml1 readwrite_entry{};
|
||||
|
||||
ept_hook* next_hook{nullptr};
|
||||
process_id source_pid{0};
|
||||
process_id target_pid{0};
|
||||
};
|
||||
|
||||
struct ept_translation_hint
|
||||
@ -55,8 +73,6 @@ namespace vmx
|
||||
|
||||
uint64_t physical_base_address{};
|
||||
const void* virtual_base_address{};
|
||||
|
||||
ept_translation_hint* next_hint{nullptr};
|
||||
};
|
||||
|
||||
struct guest_context;
|
||||
@ -74,11 +90,12 @@ namespace vmx
|
||||
|
||||
void initialize();
|
||||
|
||||
void install_code_watch_point(uint64_t physical_page);
|
||||
void install_code_watch_point(uint64_t physical_page, process_id source_pid, process_id target_pid);
|
||||
|
||||
void install_hook(const void* destination, const void* source, size_t length,
|
||||
ept_translation_hint* translation_hint = nullptr);
|
||||
void disable_all_hooks() const;
|
||||
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 handle_violation(guest_context& guest_context);
|
||||
void handle_misconfiguration(guest_context& guest_context) const;
|
||||
@ -86,11 +103,12 @@ namespace vmx
|
||||
ept_pointer get_ept_pointer() const;
|
||||
void invalidate() const;
|
||||
|
||||
static ept_translation_hint* generate_translation_hints(const void* destination, size_t length);
|
||||
static void free_translation_hints(ept_translation_hint* hints);
|
||||
static utils::list<ept_translation_hint> generate_translation_hints(const void* destination, size_t length);
|
||||
|
||||
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];
|
||||
@ -98,27 +116,27 @@ namespace vmx
|
||||
|
||||
uint64_t access_records[1024];
|
||||
|
||||
ept_split* ept_splits{nullptr};
|
||||
ept_hook* ept_hooks{nullptr};
|
||||
ept_code_watch_point* ept_code_watch_points{nullptr};
|
||||
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{};
|
||||
|
||||
pml2* get_pml2_entry(uint64_t physical_address);
|
||||
pml1* get_pml1_entry(uint64_t physical_address);
|
||||
pml1* find_pml1_table(uint64_t physical_address) const;
|
||||
pml1* find_pml1_table(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_split& allocate_ept_split();
|
||||
ept_hook& allocate_ept_hook(uint64_t physical_address);
|
||||
ept_hook* find_ept_hook(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_code_watch_point& allocate_ept_code_watch_point();
|
||||
ept_code_watch_point* find_ept_code_watch_point(uint64_t physical_address);
|
||||
|
||||
ept_hook* get_or_create_ept_hook(void* destination, ept_translation_hint* translation_hint = nullptr);
|
||||
ept_hook* get_or_create_ept_hook(void* destination, const 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,
|
||||
ept_translation_hint* translation_hint = nullptr);
|
||||
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 record_access(uint64_t rip);
|
||||
};
|
||||
|
@ -17,9 +17,14 @@ 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;
|
||||
};
|
||||
|
126
src/driver/globals.cpp
Normal file
126
src/driver/globals.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
#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;
|
||||
}
|
7
src/driver/globals.hpp
Normal file
7
src/driver/globals.hpp
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace globals
|
||||
{
|
||||
void run_constructors();
|
||||
void run_destructors();
|
||||
}
|
@ -7,11 +7,20 @@
|
||||
#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};
|
||||
@ -30,11 +39,6 @@ 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{};
|
||||
@ -49,6 +53,12 @@ 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();
|
||||
@ -129,11 +139,16 @@ hypervisor::hypervisor()
|
||||
|
||||
instance = this;
|
||||
|
||||
if (!is_virtualization_supported())
|
||||
if (!is_vmx_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();
|
||||
@ -142,6 +157,7 @@ hypervisor::hypervisor()
|
||||
|
||||
hypervisor::~hypervisor()
|
||||
{
|
||||
this->disable_all_ept_hooks();
|
||||
this->disable();
|
||||
this->free_vm_states();
|
||||
instance = nullptr;
|
||||
@ -163,11 +179,12 @@ bool hypervisor::is_enabled() const
|
||||
}
|
||||
|
||||
bool hypervisor::install_ept_hook(const void* destination, const void* source, const size_t length,
|
||||
vmx::ept_translation_hint* translation_hint)
|
||||
const process_id source_pid, const process_id target_pid,
|
||||
const utils::list<vmx::ept_translation_hint>& hints)
|
||||
{
|
||||
try
|
||||
{
|
||||
this->ept_->install_hook(destination, source, length, translation_hint);
|
||||
this->ept_->install_hook(destination, source, length, source_pid, target_pid, hints);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
@ -180,23 +197,16 @@ bool hypervisor::install_ept_hook(const void* destination, const void* source, c
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
this->invalidate_cores();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hypervisor::install_ept_code_watch_point(const uint64_t physical_page, bool invalidate) const
|
||||
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
|
||||
{
|
||||
try
|
||||
{
|
||||
this->ept_->install_code_watch_point(physical_page);
|
||||
this->ept_->install_code_watch_point(physical_page, source_pid, target_pid);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
@ -220,12 +230,13 @@ bool hypervisor::install_ept_code_watch_point(const uint64_t physical_page, bool
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hypervisor::install_ept_code_watch_points(const uint64_t* physical_pages, const size_t count) const
|
||||
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 success = true;
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
success &= this->install_ept_code_watch_point(physical_pages[i], false);
|
||||
success &= this->install_ept_code_watch_point(physical_pages[i], source_pid, target_pid, false);
|
||||
}
|
||||
|
||||
thread::dispatch_on_all_cores([&]
|
||||
@ -242,7 +253,7 @@ void hypervisor::disable_all_ept_hooks() const
|
||||
|
||||
thread::dispatch_on_all_cores([&]
|
||||
{
|
||||
auto* vm_state = this->get_current_vm_state();
|
||||
const auto* vm_state = this->get_current_vm_state();
|
||||
if (!vm_state)
|
||||
{
|
||||
return;
|
||||
@ -265,6 +276,17 @@ 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();
|
||||
@ -429,7 +451,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;
|
||||
}
|
||||
@ -447,13 +469,378 @@ 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_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)
|
||||
{
|
||||
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 &&
|
||||
(read_vmx(VMCS_GUEST_CS_SELECTOR) & SEGMENT_ACCESS_RIGHTS_DESCRIPTOR_PRIVILEGE_LEVEL_MASK) == DPL_SYSTEM)
|
||||
is_system())
|
||||
{
|
||||
guest_context.exit_vm = true;
|
||||
return;
|
||||
@ -520,6 +907,9 @@ 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:
|
||||
@ -606,7 +996,7 @@ void setup_vmcs_for_cpu(vmx::state& vm_state)
|
||||
|
||||
ia32_vmx_exit_ctls_register exit_ctls_register{};
|
||||
exit_ctls_register.host_address_space_size = 1;
|
||||
__vmx_vmwrite(VMCS_CTRL_VMEXIT_CONTROLS,
|
||||
__vmx_vmwrite(VMCS_CTRL_PRIMARY_VMEXIT_CONTROLS,
|
||||
adjust_msr(launch_context->msr_data[15],
|
||||
exit_ctls_register.flags));
|
||||
|
||||
@ -697,7 +1087,8 @@ 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));
|
||||
@ -735,11 +1126,16 @@ 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_virtualization_supported())
|
||||
if (!is_vmx_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;
|
||||
|
||||
@ -755,6 +1151,8 @@ 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()
|
||||
@ -825,45 +1223,16 @@ void hypervisor::free_vm_states()
|
||||
}
|
||||
}
|
||||
|
||||
bool hypervisor::try_install_ept_hook_on_core(const void* destination, const void* source, const size_t length,
|
||||
vmx::ept_translation_hint* translation_hint)
|
||||
void hypervisor::invalidate_cores() const
|
||||
{
|
||||
try
|
||||
thread::dispatch_on_all_cores([&]
|
||||
{
|
||||
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();
|
||||
}
|
||||
const auto* vm_state = this->get_current_vm_state();
|
||||
if (vm_state && this->is_enabled())
|
||||
{
|
||||
vm_state->ept->invalidate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
vmx::state* hypervisor::get_current_vm_state() const
|
||||
|
@ -19,11 +19,13 @@ public:
|
||||
|
||||
bool is_enabled() const;
|
||||
|
||||
bool install_ept_hook(const void* destination, const void* source, size_t length,
|
||||
vmx::ept_translation_hint* translation_hint = nullptr);
|
||||
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_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;
|
||||
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;
|
||||
|
||||
void disable_all_ept_hooks() const;
|
||||
|
||||
@ -31,6 +33,8 @@ public:
|
||||
|
||||
static hypervisor* get_instance();
|
||||
|
||||
bool cleanup_process(process_id process);
|
||||
|
||||
private:
|
||||
uint32_t vm_state_count_{0};
|
||||
vmx::state** vm_states_{nullptr};
|
||||
@ -43,10 +47,7 @@ private:
|
||||
void allocate_vm_states();
|
||||
void free_vm_states();
|
||||
|
||||
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);
|
||||
void invalidate_cores() const;
|
||||
|
||||
vmx::state* get_current_vm_state() const;
|
||||
};
|
||||
|
@ -37,11 +37,12 @@ namespace
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
vmx::ept_translation_hint* generate_translation_hints(uint32_t process_id, const void* target_address, size_t size)
|
||||
utils::list<vmx::ept_translation_hint> generate_translation_hints(uint32_t process_id, const void* target_address,
|
||||
size_t size)
|
||||
{
|
||||
vmx::ept_translation_hint* translation_hints{nullptr};
|
||||
utils::list<vmx::ept_translation_hint> translation_hints{};
|
||||
|
||||
thread::kernel_thread t([&translation_hints, process_id, target_address, size]
|
||||
thread::kernel_thread([&translation_hints, process_id, target_address, size]
|
||||
{
|
||||
debug_log("Looking up process: %d\n", process_id);
|
||||
|
||||
@ -52,8 +53,7 @@ namespace
|
||||
return;
|
||||
}
|
||||
|
||||
const auto name = process_handle.get_image_filename();
|
||||
if (name)
|
||||
if (const auto name = process_handle.get_image_filename())
|
||||
{
|
||||
debug_log("Attaching to %s\n", name);
|
||||
}
|
||||
@ -62,9 +62,7 @@ namespace
|
||||
|
||||
debug_log("Generating translation hints for address: %p\n", target_address);
|
||||
translation_hints = vmx::ept::generate_translation_hints(target_address, size);
|
||||
});
|
||||
|
||||
t.join();
|
||||
}).join();
|
||||
|
||||
return translation_hints;
|
||||
}
|
||||
@ -83,24 +81,18 @@ 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);
|
||||
translation_hints = generate_translation_hints(request.process_id, request.target_address,
|
||||
request.source_data_size);
|
||||
const auto translation_hints = generate_translation_hints(request.process_id, request.target_address,
|
||||
request.source_data_size);
|
||||
|
||||
if (!translation_hints)
|
||||
if (translation_hints.empty())
|
||||
{
|
||||
debug_log("Failed to generate tranlsation hints\n");
|
||||
return;
|
||||
}
|
||||
|
||||
hypervisor->install_ept_hook(request.target_address, buffer.get(), request.source_data_size,
|
||||
translation_hints);
|
||||
process::get_current_process_id(), request.process_id, translation_hints);
|
||||
}
|
||||
|
||||
void unhook()
|
||||
@ -131,7 +123,7 @@ namespace
|
||||
|
||||
void watch_regions(const watch_request& watch_request)
|
||||
{
|
||||
auto* hypervisor = hypervisor::get_instance();
|
||||
const auto* hypervisor = hypervisor::get_instance();
|
||||
if (!hypervisor)
|
||||
{
|
||||
throw std::runtime_error("Hypervisor not installed");
|
||||
@ -168,7 +160,7 @@ namespace
|
||||
throw std::runtime_error("Failed to copy buffer");
|
||||
}
|
||||
|
||||
thread::kernel_thread t([watch_request_copy, hypervisor, &index, &page_buffer]
|
||||
thread::kernel_thread t([watch_request_copy, &index, &page_buffer]
|
||||
{
|
||||
debug_log("Looking up process: %d\n", watch_request_copy.process_id);
|
||||
|
||||
@ -218,7 +210,8 @@ namespace
|
||||
t.join();
|
||||
|
||||
debug_log("Installing watch points...\n");
|
||||
(void)hypervisor->install_ept_code_watch_points(page_buffer.get(), index);
|
||||
(void)hypervisor->install_ept_code_watch_points(page_buffer.get(), index, process::get_current_process_id(),
|
||||
watch_request_copy.process_id);
|
||||
debug_log("Watch points installed\n");
|
||||
}
|
||||
|
||||
@ -240,7 +233,7 @@ namespace
|
||||
|
||||
void get_records(const PIRP irp, const PIO_STACK_LOCATION irp_sp)
|
||||
{
|
||||
auto* hypervisor = hypervisor::get_instance();
|
||||
const auto* hypervisor = hypervisor::get_instance();
|
||||
if (!hypervisor)
|
||||
{
|
||||
throw std::runtime_error("Hypervisor not installed");
|
||||
@ -259,7 +252,6 @@ namespace
|
||||
irp->IoStatus.Status = STATUS_SUCCESS;
|
||||
|
||||
const auto irp_sp = IoGetCurrentIrpStackLocation(irp);
|
||||
|
||||
if (irp_sp)
|
||||
{
|
||||
const auto ioctr_code = irp_sp->Parameters.DeviceIoControl.IoControlCode;
|
||||
|
433
src/driver/list.hpp
Normal file
433
src/driver/list.hpp
Normal file
@ -0,0 +1,433 @@
|
||||
#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__
|
||||
#define debug_log(...)
|
||||
#else
|
||||
#define debug_log(msg, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[MOMO] " msg, __VA_ARGS__)
|
||||
#define debug_log(...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[MOMO] " __VA_ARGS__)
|
||||
#endif
|
||||
|
@ -68,6 +68,18 @@ 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);
|
||||
@ -102,7 +114,10 @@ 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);
|
||||
@ -121,7 +136,7 @@ namespace memory
|
||||
}
|
||||
}
|
||||
|
||||
bool prope_for_read(const void* address, const size_t length, const uint64_t alignment)
|
||||
bool probe_for_read(const void* address, const size_t length, const uint64_t alignment)
|
||||
{
|
||||
__try
|
||||
{
|
||||
@ -136,13 +151,13 @@ namespace memory
|
||||
|
||||
void assert_readability(const void* address, const size_t length, const uint64_t alignment)
|
||||
{
|
||||
if (!prope_for_read(address, length, alignment))
|
||||
if (!probe_for_read(address, length, alignment))
|
||||
{
|
||||
throw std::runtime_error("Access violation");
|
||||
}
|
||||
}
|
||||
|
||||
bool prope_for_write(const void* address, const size_t length, const uint64_t alignment)
|
||||
bool probe_for_write(const void* address, const size_t length, const uint64_t alignment)
|
||||
{
|
||||
__try
|
||||
{
|
||||
@ -157,7 +172,7 @@ namespace memory
|
||||
|
||||
void assert_writability(const void* address, const size_t length, const uint64_t alignment)
|
||||
{
|
||||
if (!prope_for_write(address, length, alignment))
|
||||
if (!probe_for_write(address, length, alignment))
|
||||
{
|
||||
throw std::runtime_error("Access violation");
|
||||
}
|
||||
|
@ -10,6 +10,9 @@ 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);
|
||||
|
||||
@ -27,10 +30,10 @@ namespace memory
|
||||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||||
void free_non_paged_memory(void* memory);
|
||||
|
||||
bool prope_for_read(const void* address, size_t length, uint64_t alignment = 1);
|
||||
bool probe_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 prope_for_write(const void* address, size_t length, uint64_t alignment = 1);
|
||||
bool probe_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>
|
||||
|
@ -56,6 +56,29 @@ 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
|
||||
|
@ -37,7 +37,7 @@ namespace process
|
||||
|
||||
process_handle::process_handle(const process_handle& obj)
|
||||
{
|
||||
this->operator=(std::move(obj));
|
||||
this->operator=(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;
|
||||
}
|
||||
|
||||
uint32_t process_handle::get_id() const
|
||||
process_id process_handle::get_id() const
|
||||
{
|
||||
if (!this->handle_)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return uint32_t(uint64_t(PsGetProcessId(this->handle_)));
|
||||
return process_id_from_handle(PsGetProcessId(this->handle_));
|
||||
}
|
||||
|
||||
const char* process_handle::get_image_filename() const
|
||||
@ -111,16 +111,25 @@ namespace process
|
||||
this->own_ = false;
|
||||
}
|
||||
|
||||
process_handle find_process_by_id(const uint32_t process_id)
|
||||
process_id process_id_from_handle(HANDLE handle)
|
||||
{
|
||||
PEPROCESS process{};
|
||||
const uint64_t process_id_long = process_id;
|
||||
if (PsLookupProcessByProcessId(HANDLE(process_id_long), &process) != STATUS_SUCCESS)
|
||||
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)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return process_handle{process, true};
|
||||
return process_handle{process_obj, true};
|
||||
}
|
||||
|
||||
process_handle get_current_process()
|
||||
@ -128,6 +137,11 @@ 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())
|
||||
|
@ -19,7 +19,7 @@ namespace process
|
||||
operator PEPROCESS() const;
|
||||
|
||||
bool is_alive() const;
|
||||
uint32_t get_id() const;
|
||||
process_id get_id() const;
|
||||
|
||||
const char* get_image_filename() const;
|
||||
|
||||
@ -30,9 +30,14 @@ namespace process
|
||||
void release();
|
||||
};
|
||||
|
||||
process_handle find_process_by_id(uint32_t process_id);
|
||||
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 get_current_process();
|
||||
|
||||
process_id get_current_process_id();
|
||||
|
||||
class scoped_process_attacher
|
||||
{
|
||||
public:
|
||||
|
67
src/driver/process_callback.cpp
Normal file
67
src/driver/process_callback.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
#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));
|
||||
}
|
||||
}
|
||||
}
|
42
src/driver/process_callback.hpp
Normal file
42
src/driver/process_callback.hpp
Normal file
@ -0,0 +1,42 @@
|
||||
#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
src/driver/resource.hpp
Normal file
1
src/driver/resource.hpp
Normal file
@ -0,0 +1 @@
|
||||
#pragma once
|
95
src/driver/resource.rc
Normal file
95
src/driver/resource.rc
Normal file
@ -0,0 +1,95 @@
|
||||
// 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,3 +16,6 @@
|
||||
#include "nt_ext.hpp"
|
||||
#include "new.hpp"
|
||||
#include "exception.hpp"
|
||||
|
||||
// Not sure if this is good, but fuck it.
|
||||
using process_id = uint32_t;
|
||||
|
@ -17,4 +17,22 @@ 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;
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ namespace std
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
typename remove_reference<T>::type&& move(T&& arg)
|
||||
typename remove_reference<T>::type&& move(T&& arg) noexcept
|
||||
{
|
||||
return static_cast<typename remove_reference<T>::type&&>(arg);
|
||||
}
|
||||
|
284
src/driver/vector.hpp
Normal file
284
src/driver/vector.hpp
Normal file
@ -0,0 +1,284 @@
|
||||
#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);
|
||||
}
|
||||
};
|
||||
}
|
18
src/include/hyperhook.h
Normal file
18
src/include/hyperhook.h
Normal file
@ -0,0 +1,18 @@
|
||||
#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);
|
27
src/library/CMakeLists.txt
Normal file
27
src/library/CMakeLists.txt
Normal file
@ -0,0 +1,27 @@
|
||||
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")
|
@ -31,7 +31,10 @@ driver::driver(const std::filesystem::path& driver_file, const std::string& serv
|
||||
throw std::runtime_error("Unable to create service");
|
||||
}
|
||||
|
||||
StartServiceA(this->service_, 0, nullptr);
|
||||
if(!StartServiceA(this->service_, 0, nullptr))
|
||||
{
|
||||
printf("Failed to start service: %d\n", GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
driver::~driver()
|
@ -4,15 +4,21 @@
|
||||
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_{};
|
@ -4,6 +4,7 @@
|
||||
class driver_device
|
||||
{
|
||||
public:
|
||||
driver_device() = default;
|
||||
driver_device(const std::string& driver_device);
|
||||
~driver_device() = default;
|
||||
|
||||
@ -13,6 +14,11 @@ 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;
|
119
src/library/main.cpp
Normal file
119
src/library/main.cpp
Normal file
@ -0,0 +1,119 @@
|
||||
#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;
|
||||
}
|
@ -69,7 +69,7 @@ namespace process
|
||||
std::string buffer{};
|
||||
buffer.resize(1024);
|
||||
|
||||
const auto length = GetModuleFileNameExA(process, module, &buffer[0], static_cast<DWORD>(buffer.size()));
|
||||
const auto length = GetModuleFileNameExA(process, module, buffer.data(), static_cast<DWORD>(buffer.size()));
|
||||
if (length > 0)
|
||||
{
|
||||
buffer.resize(length);
|
94
src/library/resource.rc
Normal file
94
src/library/resource.rc
Normal file
@ -0,0 +1,94 @@
|
||||
// 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
|
@ -5,10 +5,13 @@
|
||||
#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")
|
@ -1,20 +1,15 @@
|
||||
file(GLOB_RECURSE runner_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
|
||||
file(GLOB_RECURSE runner_headers ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp)
|
||||
file(GLOB_RECURSE runner_sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
|
||||
file(GLOB_RECURSE runner_headers CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp)
|
||||
|
||||
add_executable(runner #WIN32
|
||||
${runner_sources}
|
||||
${runner_headers}
|
||||
)
|
||||
|
||||
target_precompile_headers(runner
|
||||
PRIVATE std_include.hpp
|
||||
)
|
||||
|
||||
set_property(TARGET runner APPEND_STRING PROPERTY LINK_FLAGS " /MANIFESTUAC:\"level='requireAdministrator'\"")
|
||||
|
||||
target_link_libraries(runner
|
||||
shared
|
||||
driver_file
|
||||
target_link_libraries(runner PRIVATE
|
||||
library
|
||||
)
|
||||
|
||||
set_source_files_properties(resource.rc PROPERTIES LANGUAGE RC)
|
||||
|
@ -1,344 +1,133 @@
|
||||
#include "std_include.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
#include <conio.h>
|
||||
#include <fstream>
|
||||
#include <set>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "driver.hpp"
|
||||
#include "driver_device.hpp"
|
||||
#include "process.hpp"
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
|
||||
#include <irp_data.hpp>
|
||||
#include <hyperhook.h>
|
||||
|
||||
#include "resource.hpp"
|
||||
#include "utils/io.hpp"
|
||||
#include "utils/nt.hpp"
|
||||
|
||||
#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,
|
||||
bool patch_data(const uint32_t process_id, const uint64_t address, const void* buffer,
|
||||
const size_t length)
|
||||
{
|
||||
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);
|
||||
return hyperhook_write(process_id, address, buffer, length) != 0;
|
||||
}
|
||||
|
||||
void insert_nop(const driver_device& driver_device, const uint32_t pid, const uint64_t addr, const size_t length)
|
||||
bool insert_nop(const uint32_t process_id, const uint64_t address, const size_t length)
|
||||
{
|
||||
std::vector<uint8_t> buffer{};
|
||||
buffer.resize(length);
|
||||
memset(buffer.data(), 0x90, buffer.size());
|
||||
|
||||
patch_data(driver_device, pid, addr, buffer.data(), buffer.size());
|
||||
return patch_data(process_id, address, buffer.data(), buffer.size());
|
||||
}
|
||||
|
||||
void remove_hooks(const driver_device& driver_device)
|
||||
std::optional<uint32_t> get_process_id_from_window(const char* class_name, const char* window_name)
|
||||
{
|
||||
(void)driver_device.send(UNHOOK_DRV_IOCTL, driver_device::data{});
|
||||
}
|
||||
|
||||
std::vector<uint8_t> load_resource(const int id)
|
||||
{
|
||||
auto* const res = FindResource(GetModuleHandleA(nullptr), MAKEINTRESOURCE(id), RT_RCDATA);
|
||||
if (!res) return {};
|
||||
|
||||
auto* const handle = LoadResource(nullptr, res);
|
||||
if (!handle) return {};
|
||||
|
||||
const auto* data_ptr = static_cast<uint8_t*>(LockResource(handle));
|
||||
const auto data_size = SizeofResource(nullptr, 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))
|
||||
const auto window = FindWindowA(class_name, window_name);
|
||||
if (!window)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
const utils::nt::library library(reinterpret_cast<HMODULE>(data.data()));
|
||||
if (!library.is_valid())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto section_headers = library.get_section_headers();
|
||||
|
||||
std::vector<std::pair<size_t, size_t>> regions{};
|
||||
for (const auto& section_header : section_headers)
|
||||
{
|
||||
if (section_header->Characteristics & IMAGE_SCN_MEM_EXECUTE)
|
||||
{
|
||||
regions.emplace_back(section_header->VirtualAddress, section_header->Misc.VirtualSize);
|
||||
}
|
||||
}
|
||||
|
||||
return regions;
|
||||
DWORD process_id{};
|
||||
GetWindowThreadProcessId(window, &process_id);
|
||||
return static_cast<uint32_t>(process_id);
|
||||
}
|
||||
|
||||
uint32_t get_process_id()
|
||||
void patch_iw5(const uint32_t pid)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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 {};
|
||||
}
|
||||
|
||||
//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;
|
||||
}
|
||||
}
|
||||
|
||||
result.resize(i);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
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(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(driver_device, pid, 0x47F0D0, data1, sizeof(data1));
|
||||
patch_data(pid, 0x47F0D0, data1, sizeof(data1));
|
||||
|
||||
// Compass show enemies
|
||||
constexpr uint8_t data2[] = {0xEB, 0x13};
|
||||
patch_data(driver_device, pid, 0x4437A8, data2, sizeof(data2));
|
||||
patch_data(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));
|
||||
*/
|
||||
patch_data(pid, 0x443A2A, data3, sizeof(data3));
|
||||
patch_data(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);
|
||||
void try_patch_iw5()
|
||||
{
|
||||
const auto pid = get_process_id_from_window("IW5", nullptr);
|
||||
if (pid)
|
||||
{
|
||||
printf("Patching IW5...\n");
|
||||
patch_iw5(*pid);
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
*/
|
||||
void patch_t6(const uint32_t pid)
|
||||
{
|
||||
// Force calling SatellitePingEnemyPlayer
|
||||
insert_nop(pid, 0x7993B1, 2);
|
||||
insert_nop(pid, 0x7993C1, 2);
|
||||
|
||||
/*printf("Press any key to disable all hooks!\n");
|
||||
(void)_getch();
|
||||
// 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
|
||||
|
||||
remove_hooks(driver_device);
|
||||
// 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
|
||||
}
|
||||
|
||||
printf("Press any key to exit!\n");
|
||||
(void)_getch();*/
|
||||
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)
|
||||
{
|
||||
printf("Patching T6...\n");
|
||||
patch_t6(*pid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int safe_main(const int /*argc*/, char* /*argv*/[])
|
||||
{
|
||||
if (hyperhook_initialize() == 0)
|
||||
{
|
||||
throw std::runtime_error("Failed to initialize HyperHook");
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
try_patch_iw5();
|
||||
try_patch_t6();
|
||||
|
||||
printf("Press any key to exit!\n");
|
||||
if (_getch() != 'r')
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(const int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
unsafe_main(argc, argv);
|
||||
return 0;
|
||||
return safe_main(argc, argv);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
|
@ -1,3 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#define DRIVER_BINARY 300
|
@ -8,8 +8,6 @@
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "windows.h"
|
||||
#include "resource.hpp"
|
||||
#include <driver_file.h>
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
@ -63,12 +61,12 @@ BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "momo5502"
|
||||
VALUE "FileDescription", "Open-IW5"
|
||||
VALUE "FileDescription", "HyperHook Runner"
|
||||
VALUE "FileVersion", "1.0.0.0"
|
||||
VALUE "InternalName", "Open-IW5"
|
||||
VALUE "InternalName", "HyperHook Runner"
|
||||
VALUE "LegalCopyright", "All rights reserved."
|
||||
VALUE "OriginalFilename", "open-iw5.exe"
|
||||
VALUE "ProductName", "open-iw5"
|
||||
VALUE "OriginalFilename", "runner.exe"
|
||||
VALUE "ProductName", "runner"
|
||||
VALUE "ProductVersion", "1.0.0.0"
|
||||
END
|
||||
END
|
||||
@ -84,7 +82,6 @@ END
|
||||
//
|
||||
|
||||
102 ICON "resources/icon.ico"
|
||||
DRIVER_BINARY RCDATA DRIVER_FILE
|
||||
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
file(GLOB shared_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
|
||||
file(GLOB shared_headers ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp)
|
||||
file(GLOB shared_sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
|
||||
file(GLOB shared_headers CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp)
|
||||
|
||||
add_library(shared INTERFACE
|
||||
${shared_headers}
|
||||
|
Reference in New Issue
Block a user