1
0
mirror of https://github.com/momo5502/hypervisor.git synced 2025-07-03 01:31:51 +00:00

Compare commits

...

70 Commits

Author SHA1 Message Date
aab19ccb34 Merge pull request #13 from momo5502/dependabot/submodules/external/FindWDK-04b4151
Bump external/FindWDK from `6aaaaf4` to `04b4151`
2025-07-01 13:38:26 +02:00
4051223045 Bump external/FindWDK from 6aaaaf4 to 04b4151
Bumps [external/FindWDK](https://github.com/SergiusTheBest/FindWDK) from `6aaaaf4` to `04b4151`.
- [Commits](6aaaaf423a...04b4151f6d)

---
updated-dependencies:
- dependency-name: external/FindWDK
  dependency-version: 04b4151f6d6257554f2467edc0ff7f0c77074c7e
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-01 10:24:21 +00:00
dbc7b5422e Update ia32-doc submodules
Some checks failed
Build / Build binaries (debug) (push) Has been cancelled
Build / Build binaries (release) (push) Has been cancelled
2025-05-03 09:44:47 +02:00
de99750e53 Update build.yml
Some checks failed
Build / Build binaries (debug) (push) Has been cancelled
Build / Build binaries (release) (push) Has been cancelled
2024-11-24 15:23:54 +01:00
5a796c7aae Merge pull request #10 from momo5502/dependabot/github_actions/actions/upload-artifact-4
Bump actions/upload-artifact from 3 to 4
2024-11-24 15:22:47 +01:00
af3d08e791 Merge pull request #8 from momo5502/dependabot/github_actions/ilammy/msvc-dev-cmd-1.13.0
Bump ilammy/msvc-dev-cmd from 1.12.0 to 1.13.0
2024-11-24 15:22:29 +01:00
8da8fa7f8e Merge pull request #9 from momo5502/dependabot/github_actions/actions/checkout-4
Bump actions/checkout from 3 to 4
2024-11-24 15:22:18 +01:00
111b9c9a01 Bump actions/upload-artifact from 3 to 4
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-24 14:18:12 +00:00
243ddeebdb Bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-24 14:18:10 +00:00
a6e484d9df Bump ilammy/msvc-dev-cmd from 1.12.0 to 1.13.0
Bumps [ilammy/msvc-dev-cmd](https://github.com/ilammy/msvc-dev-cmd) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/ilammy/msvc-dev-cmd/releases)
- [Commits](https://github.com/ilammy/msvc-dev-cmd/compare/v1.12.0...v1.13.0)

---
updated-dependencies:
- dependency-name: ilammy/msvc-dev-cmd
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-24 14:18:08 +00:00
b3dec2e80c Update dependabot.yml 2024-11-24 15:17:31 +01:00
4d68b0bb78 Merge pull request #7 from momo5502/dependabot/submodules/external/FindWDK-6aaaaf4
Some checks failed
Build / Build binaries (debug) (push) Has been cancelled
Build / Build binaries (release) (push) Has been cancelled
Bump external/FindWDK from `79a45e7` to `6aaaaf4`
2024-09-13 10:14:45 +02:00
79fd28ad7e Bump external/FindWDK from 79a45e7 to 6aaaaf4
Bumps [external/FindWDK](https://github.com/SergiusTheBest/FindWDK) from `79a45e7` to `6aaaaf4`.
- [Commits](79a45e7d70...6aaaaf423a)

---
updated-dependencies:
- dependency-name: external/FindWDK
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-09 09:30:54 +00:00
7073f7169a Merge pull request #6 from momo5502/dependabot/submodules/external/FindWDK-79a45e7
Some checks failed
Build / Build binaries (debug) (push) Has been cancelled
Build / Build binaries (release) (push) Has been cancelled
Bump external/FindWDK from `c941028` to `79a45e7`
2024-08-19 10:19:51 +02:00
b74f712975 Bump external/FindWDK from c941028 to 79a45e7
Bumps [external/FindWDK](https://github.com/SergiusTheBest/FindWDK) from `c941028` to `79a45e7`.
- [Commits](c941028b26...79a45e7d70)

---
updated-dependencies:
- dependency-name: external/FindWDK
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-14 09:05:19 +00:00
e9d4b3345c Try accessing the device first 2024-05-21 13:23:38 +02:00
55234c3504 Support disabling syscall hooking 2024-05-11 13:34:00 +02:00
69f66d11e4 Small cleanup and fixes 2024-05-11 10:43:41 +02:00
2d8de2835c Log explorer syscalls 2024-05-10 22:24:15 +02:00
b9c4d85bb0 String equality check 2024-05-10 22:23:47 +02:00
30873e4ebb More progress with syscall hooking 2024-05-10 22:15:15 +02:00
53c24b8325 Fix syscall hooking 2024-05-10 20:20:00 +02:00
0896133821 Add syscall handling 2024-05-10 17:26:57 +02:00
d5bf81d99b Prepare exception handling 2024-05-09 18:33:33 +02:00
761490c808 Prepare syscall hooking 2024-05-09 16:36:21 +02:00
c1d0a354c2 Fixes 2024-04-15 20:39:25 +02:00
08727330e1 Make exceptions copyable 2024-04-13 17:31:30 +02:00
7a7f757f09 Fix compilation 2023-12-10 09:45:17 +01:00
8d2b581adf Small fixes 2023-12-10 09:40:41 +01:00
046df34929 Debug service start 2023-12-10 09:40:15 +01:00
35f18600b8 Switch back to c++ 20 2023-12-10 09:39:36 +01:00
bc4ea8c9a2 Merge pull request #5 from momo5502/dependabot/submodules/external/FindWDK-c941028
Bump external/FindWDK from `76f5f3e` to `c941028`
2023-01-04 18:34:04 +01:00
0e2450f47e Bump external/FindWDK from 76f5f3e to c941028
Bumps [external/FindWDK](https://github.com/SergiusTheBest/FindWDK) from `76f5f3e` to `c941028`.
- [Release notes](https://github.com/SergiusTheBest/FindWDK/releases)
- [Commits](76f5f3e088...c941028b26)

---
updated-dependencies:
- dependency-name: external/FindWDK
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-04 09:07:50 +00:00
083e67e1d7 Create include file 2022-12-28 09:19:39 +01:00
8b4c277f11 Archive dlls 2022-12-28 08:39:47 +01:00
a3f67b20b8 Support multiple instances 2022-12-28 08:38:51 +01:00
4cde82aae1 Improve patches 2022-12-27 21:17:54 +01:00
90889e7d32 Add version info 2022-12-27 16:38:58 +01:00
938d929de6 Log process name 2022-12-27 16:36:52 +01:00
28dd94f2ef Extract into library 2022-12-27 16:27:33 +01:00
f8f636a829 More cleanup 2022-12-27 14:52:19 +01:00
4cbbaed72f Cleanup on process termination 2022-12-27 13:30:20 +01:00
95120b73ab Cleanup 2022-12-27 09:40:34 +01:00
65417e3e7a Add non-threadsafe process callbacks 2022-12-27 09:36:46 +01:00
a6e0d7de47 Update actions 2022-12-26 08:24:28 +01:00
c2587af857 Update readme 2022-12-26 08:20:13 +01:00
05a677a19a Fix compilation 2022-12-26 08:17:40 +01:00
06db3371ad Support global constructors/destructors 2022-12-25 18:00:21 +01:00
6f7f0f74c4 Optimize CMake 2022-12-25 17:54:31 +01:00
e379103e0f Fix bug 2022-12-24 09:28:47 +01:00
531305e104 Logging fix 2022-12-24 08:46:20 +01:00
1d23c10734 Use containers for ept allocations 2022-12-24 08:36:23 +01:00
33b44f1dc1 Start using custom containers 2022-12-23 22:18:07 +01:00
dcab775bb9 Optimize list 2022-12-23 21:21:34 +01:00
129380419d Update wdk 2022-12-23 20:44:41 +01:00
a67e2ae833 Add linked list 2022-12-23 20:42:22 +01:00
d1ad347e84 Formatting 2022-12-23 20:41:13 +01:00
d778a3190a Finish vector 2022-12-21 21:53:18 +01:00
4cd7e711f7 Prepare vector implementation 2022-12-21 10:38:03 +01:00
952e89adae Merge pull request #4 from momo5502/dependabot/submodules/external/FindWDK-76f5f3e
Bump external/FindWDK from `0492964` to `76f5f3e`
2022-10-04 14:09:10 +02:00
10828cff46 Bump external/FindWDK from 0492964 to 76f5f3e
Bumps [external/FindWDK](https://github.com/SergiusTheBest/FindWDK) from `0492964` to `76f5f3e`.
- [Release notes](https://github.com/SergiusTheBest/FindWDK/releases)
- [Commits](0492964004...76f5f3e088)

---
updated-dependencies:
- dependency-name: external/FindWDK
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-04 10:13:59 +00:00
9bf0b94e29 Fix typo 2022-09-02 19:43:15 +02:00
cf013601b8 Don't sign the driver 2022-08-25 20:52:44 +02:00
2fad5d0684 Update memory.cpp 2022-08-22 10:23:21 +02:00
620de17a01 Update memory.cpp 2022-08-22 10:12:15 +02:00
6253a44356 Update README.md 2022-08-21 11:56:32 +02:00
842de71a69 Merge pull request #3 from momo5502/dependabot/submodules/external/FindWDK-0492964
Bump external/FindWDK from `43fd504` to `0492964`
2022-07-11 15:03:50 +02:00
c95f3ce9ce Bump external/FindWDK from 43fd504 to 0492964
Bumps [external/FindWDK](https://github.com/SergiusTheBest/FindWDK) from `43fd504` to `0492964`.
- [Release notes](https://github.com/SergiusTheBest/FindWDK/releases)
- [Commits](43fd504e1d...0492964004)

---
updated-dependencies:
- dependency-name: external/FindWDK
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-11 11:08:44 +00:00
325118892d Fix compilation 2022-06-18 11:19:00 +02:00
1519181150 Merge pull request #2 from momo5502/feature/integrity-analysis
Feature/integrity analysis
2022-06-18 10:59:23 +02:00
58 changed files with 2274 additions and 676 deletions

View File

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

View File

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

View File

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

View File

@ -1,11 +1,15 @@
![license](https://img.shields.io/github/license/momo5502/hypervisor.svg)
[![build](https://github.com/momo5502/hypervisor/workflows/Build/badge.svg)](https://github.com/momo5502/hypervisor/actions)
[![paypal](https://img.shields.io/badge/PayPal-support-blue.svg?logo=paypal)](https://paypal.me/momo5502)
# Hypervisor
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>

View File

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

View File

@ -2,9 +2,9 @@ enable_language(ASM_MASM)
string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
file(GLOB driver_sources ${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
View 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);
}
};
}

View File

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

View File

@ -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([&current_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(&current_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(&current_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;
}
}

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View 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
View File

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

95
src/driver/resource.rc Normal file
View 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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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}