├── .gitignore ├── src ├── driver │ ├── resource.hpp │ ├── globals.hpp │ ├── logging.hpp │ ├── assembly.hpp │ ├── irp.hpp │ ├── std_include.hpp │ ├── new.hpp │ ├── string.cpp │ ├── allocator.hpp │ ├── string.hpp │ ├── sleep_callback.hpp │ ├── process_callback.hpp │ ├── stdint.hpp │ ├── exception.hpp │ ├── thread.hpp │ ├── new.cpp │ ├── finally.hpp │ ├── functional.hpp │ ├── process.hpp │ ├── hypervisor.hpp │ ├── process_callback.cpp │ ├── sleep_callback.cpp │ ├── unique_ptr.hpp │ ├── vmx.hpp │ ├── CMakeLists.txt │ ├── resource.rc │ ├── memory.hpp │ ├── assembly.asm │ ├── type_traits.hpp │ ├── globals.cpp │ ├── process.cpp │ ├── driver_main.cpp │ ├── nt_ext.hpp │ ├── ept.hpp │ ├── thread.cpp │ ├── memory.cpp │ ├── vector.hpp │ ├── list.hpp │ ├── irp.cpp │ └── ept.cpp ├── runner │ ├── resources │ │ └── icon.ico │ ├── CMakeLists.txt │ ├── resource.rc │ └── main.cpp ├── CMakeLists.txt ├── library │ ├── process.hpp │ ├── std_include.hpp │ ├── service_handle.hpp │ ├── native_handle.hpp │ ├── driver.hpp │ ├── CMakeLists.txt │ ├── utils │ │ ├── io.hpp │ │ ├── nt.hpp │ │ ├── io.cpp │ │ └── nt.cpp │ ├── driver_device.hpp │ ├── service_handle.cpp │ ├── native_handle.cpp │ ├── finally.hpp │ ├── driver.cpp │ ├── process.cpp │ ├── driver_device.cpp │ ├── resource.rc │ └── main.cpp ├── shared │ ├── CMakeLists.txt │ └── irp_data.hpp └── include │ └── hyperhook.h ├── cert ├── RunAsDate.exe ├── signtool.exe ├── current_cert.pfx └── 1111222.cer ├── external ├── ia32-doc.cmake ├── CMakeLists.txt └── vcrtl.cmake ├── .editorconfig ├── .github ├── dependabot.yml └── workflows │ └── build.yml ├── .gitmodules ├── README.md ├── CMakePresets.json ├── CMakeLists.txt ├── cmake └── utils.cmake └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.aps -------------------------------------------------------------------------------- /src/driver/resource.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | -------------------------------------------------------------------------------- /cert/RunAsDate.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momo5502/hypervisor/HEAD/cert/RunAsDate.exe -------------------------------------------------------------------------------- /cert/signtool.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momo5502/hypervisor/HEAD/cert/signtool.exe -------------------------------------------------------------------------------- /cert/current_cert.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momo5502/hypervisor/HEAD/cert/current_cert.pfx -------------------------------------------------------------------------------- /src/runner/resources/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/momo5502/hypervisor/HEAD/src/runner/resources/icon.ico -------------------------------------------------------------------------------- /external/ia32-doc.cmake: -------------------------------------------------------------------------------- 1 | add_library(ia32_doc INTERFACE) 2 | 3 | target_include_directories(ia32_doc INTERFACE ia32-doc/out) 4 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(shared) 2 | add_subdirectory(driver) 3 | add_subdirectory(library) 4 | add_subdirectory(runner) 5 | -------------------------------------------------------------------------------- /src/driver/globals.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace globals 4 | { 5 | void run_constructors(); 6 | void run_destructors(); 7 | } 8 | -------------------------------------------------------------------------------- /external/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(ExternalProject) 2 | 3 | ############################# 4 | 5 | include(vcrtl.cmake) 6 | include(ia32-doc.cmake) -------------------------------------------------------------------------------- /src/driver/logging.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef NDEBUG__ 4 | #define debug_log(...) 5 | #else 6 | #define debug_log(...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[MOMO] " __VA_ARGS__) 7 | #endif 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{cpp,hpp}] 2 | end_of_line = crlf 3 | insert_final_newline = true 4 | indent_style = tab 5 | indent_size = 4 6 | trim_trailing_whitespace = true 7 | charset = utf-8 8 | brace_style = next_line 9 | namespace_indentation = all 10 | cpp_indent_namespace_contents = true -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gitsubmodule 4 | directory: "/" 5 | schedule: 6 | interval: monthly 7 | open-pull-requests-limit: 10 8 | 9 | - package-ecosystem: "github-actions" 10 | directory: "/" 11 | schedule: 12 | interval: monthly 13 | -------------------------------------------------------------------------------- /src/library/process.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "native_handle.hpp" 3 | 4 | namespace process 5 | { 6 | native_handle open(uint32_t process_id, DWORD access); 7 | std::vector get_modules(const native_handle& process); 8 | std::string get_module_filename(const native_handle& process, HMODULE module); 9 | } 10 | -------------------------------------------------------------------------------- /src/shared/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB shared_sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) 2 | file(GLOB shared_headers CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp) 3 | 4 | add_library(shared INTERFACE 5 | ${shared_headers} 6 | ) 7 | 8 | target_include_directories(shared INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/FindWDK"] 2 | path = external/FindWDK 3 | url = https://github.com/SergiusTheBest/FindWDK.git 4 | [submodule "external/vcrtl"] 5 | path = external/vcrtl 6 | url = https://github.com/avakar/vcrtl.git 7 | ignore = dirty 8 | [submodule "external/ia32-doc"] 9 | path = external/ia32-doc 10 | url = https://github.com/wbenny/ia32-doc.git 11 | -------------------------------------------------------------------------------- /src/library/std_include.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #pragma comment(lib, "Shlwapi.lib") 18 | -------------------------------------------------------------------------------- /src/driver/assembly.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | extern "C" 4 | { 5 | void _sldt(uint16_t* ldtr); 6 | void _ltr(uint16_t tr); 7 | void _str(uint16_t* tr); 8 | void __lgdt(void* gdtr); 9 | 10 | void _sgdt(void*); 11 | 12 | void __invept(size_t type, invept_descriptor* descriptor); 13 | 14 | [[ noreturn ]] void vm_launch(); 15 | [[ noreturn ]] void vm_exit(); 16 | [[ noreturn ]] void restore_context(CONTEXT* context); 17 | } 18 | -------------------------------------------------------------------------------- /src/include/hyperhook.h: -------------------------------------------------------------------------------- 1 | #ifndef EXTERN_C 2 | #ifdef __cplusplus 3 | #define EXTERN_C extern "C" 4 | #else 5 | #define EXTERN_C 6 | #endif 7 | #endif 8 | 9 | #ifndef DLL_IMPORT 10 | #define DLL_IMPORT __declspec(dllimport) 11 | #endif 12 | 13 | EXTERN_C DLL_IMPORT 14 | int hyperhook_initialize(); 15 | 16 | EXTERN_C DLL_IMPORT 17 | int hyperhook_write(unsigned int process_id, unsigned long long address, const void* data, 18 | unsigned long long size); 19 | -------------------------------------------------------------------------------- /src/driver/irp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class irp 4 | { 5 | public: 6 | irp() = default; 7 | irp(PDRIVER_OBJECT driver_object, const wchar_t* device_name, const wchar_t* dos_device_name); 8 | ~irp(); 9 | 10 | irp(irp&& obj) noexcept; 11 | irp& operator=(irp&& obj) noexcept; 12 | 13 | irp(const irp&) = delete; 14 | irp& operator=(const irp&) = delete; 15 | 16 | private: 17 | UNICODE_STRING device_name_{}; 18 | UNICODE_STRING dos_device_name_{}; 19 | PDEVICE_OBJECT device_object_{}; 20 | }; 21 | -------------------------------------------------------------------------------- /src/driver/std_include.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #pragma warning(push) 8 | #pragma warning(disable: 4201) 9 | #include 10 | #pragma warning(pop) 11 | 12 | #define STRINGIFY_(a) #a 13 | #define STRINGIFY(a) STRINGIFY_(a) 14 | 15 | #include "stdint.hpp" 16 | #include "nt_ext.hpp" 17 | #include "new.hpp" 18 | #include "exception.hpp" 19 | 20 | // Not sure if this is good, but fuck it. 21 | using process_id = uint32_t; 22 | -------------------------------------------------------------------------------- /src/library/service_handle.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class service_handle 4 | { 5 | public: 6 | service_handle(); 7 | service_handle(SC_HANDLE handle); 8 | ~service_handle(); 9 | 10 | service_handle(const service_handle&) = delete; 11 | service_handle& operator=(const service_handle&) = delete; 12 | 13 | service_handle(service_handle&& obj) noexcept; 14 | service_handle& operator=(service_handle&& obj) noexcept; 15 | 16 | operator SC_HANDLE() const; 17 | 18 | private: 19 | SC_HANDLE handle_{nullptr}; 20 | }; 21 | -------------------------------------------------------------------------------- /src/library/native_handle.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class native_handle 4 | { 5 | public: 6 | native_handle(); 7 | native_handle(HANDLE handle); 8 | ~native_handle(); 9 | 10 | native_handle(const native_handle&) = delete; 11 | native_handle& operator=(const native_handle&) = delete; 12 | 13 | native_handle(native_handle&& obj) noexcept; 14 | native_handle& operator=(native_handle&& obj) noexcept; 15 | 16 | operator HANDLE() const; 17 | operator bool() const; 18 | 19 | private: 20 | HANDLE handle_{INVALID_HANDLE_VALUE}; 21 | }; 22 | -------------------------------------------------------------------------------- /src/driver/new.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace std 4 | { 5 | enum class align_val_t : size_t 6 | { 7 | }; 8 | } 9 | 10 | void* operator new(size_t size); 11 | void* operator new[](size_t size); 12 | 13 | void* operator new(size_t, void* where); 14 | 15 | void operator delete(void* ptr, size_t); 16 | void operator delete(void* ptr); 17 | void operator delete[](void* ptr, size_t); 18 | void operator delete[](void* ptr); 19 | 20 | void operator delete(void* ptr, size_t, std::align_val_t); 21 | void operator delete[](void* ptr, size_t, std::align_val_t); 22 | -------------------------------------------------------------------------------- /src/library/driver.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "service_handle.hpp" 3 | 4 | class driver 5 | { 6 | public: 7 | driver() = default; 8 | driver(const std::filesystem::path& driver_file, const std::string& service_name); 9 | ~driver(); 10 | 11 | driver(const driver&) = delete; 12 | driver& operator=(const driver&) = delete; 13 | 14 | driver(driver&& obj) noexcept = default; 15 | driver& operator=(driver&& obj) noexcept = default; 16 | 17 | operator bool() const 18 | { 19 | return this->service_; 20 | } 21 | 22 | private: 23 | service_handle manager_{}; 24 | service_handle service_{}; 25 | }; 26 | -------------------------------------------------------------------------------- /src/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB_RECURSE runner_sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) 2 | file(GLOB_RECURSE runner_headers CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp) 3 | 4 | add_executable(runner #WIN32 5 | ${runner_sources} 6 | ${runner_headers} 7 | ) 8 | 9 | set_property(TARGET runner APPEND_STRING PROPERTY LINK_FLAGS " /MANIFESTUAC:\"level='requireAdministrator'\"") 10 | 11 | target_link_libraries(runner PRIVATE 12 | library 13 | ) 14 | 15 | set_source_files_properties(resource.rc PROPERTIES LANGUAGE RC) 16 | target_sources(runner PRIVATE 17 | resource.rc 18 | ) 19 | -------------------------------------------------------------------------------- /src/driver/string.cpp: -------------------------------------------------------------------------------- 1 | #include "std_include.hpp" 2 | #include "string.hpp" 3 | 4 | namespace string 5 | { 6 | _IRQL_requires_max_(DISPATCH_LEVEL) 7 | 8 | UNICODE_STRING get_unicode_string(const wchar_t* string) 9 | { 10 | UNICODE_STRING unicode_string{}; 11 | RtlInitUnicodeString(&unicode_string, string); 12 | return unicode_string; 13 | } 14 | 15 | char* get_va_buffer() 16 | { 17 | constexpr auto va_buffer_count = 0x10; 18 | static char buffers[va_buffer_count][VA_BUFFER_SIZE]; 19 | static volatile long current_buffer = 0; 20 | 21 | const auto index = InterlockedIncrement(¤t_buffer); 22 | return buffers[index % va_buffer_count]; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/library/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB_RECURSE library_sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) 2 | file(GLOB_RECURSE library_headers CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp) 3 | 4 | add_library(library SHARED 5 | ${library_sources} 6 | ${library_headers} 7 | ) 8 | 9 | target_precompile_headers(library PRIVATE 10 | std_include.hpp 11 | ) 12 | 13 | target_link_libraries(library PRIVATE 14 | shared 15 | driver_file 16 | ) 17 | 18 | target_include_directories(library PUBLIC 19 | ${CMAKE_CURRENT_SOURCE_DIR}/../include 20 | ) 21 | 22 | set_source_files_properties(resource.rc PROPERTIES LANGUAGE RC) 23 | target_sources(library PRIVATE 24 | resource.rc 25 | ) 26 | 27 | set_target_properties(library PROPERTIES OUTPUT_NAME "hyperhook") 28 | -------------------------------------------------------------------------------- /src/driver/allocator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "memory.hpp" 3 | 4 | namespace utils 5 | { 6 | template 7 | concept is_allocator = requires(size_t size, void* ptr) 8 | { 9 | T().free(T().allocate(size)); 10 | T().free(ptr); 11 | }; 12 | 13 | struct AlignedAllocator 14 | { 15 | void* allocate(const size_t size) 16 | { 17 | return memory::allocate_aligned_memory(size); 18 | } 19 | 20 | void free(void* ptr) 21 | { 22 | memory::free_aligned_memory(ptr); 23 | } 24 | }; 25 | 26 | struct NonPagedAllocator 27 | { 28 | void* allocate(const size_t size) 29 | { 30 | return memory::allocate_non_paged_memory(size); 31 | } 32 | 33 | void free(void* ptr) 34 | { 35 | memory::free_non_paged_memory(ptr); 36 | } 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /src/driver/string.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "type_traits.hpp" 3 | 4 | #define VA_BUFFER_SIZE 0x1000 5 | 6 | namespace string 7 | { 8 | _IRQL_requires_max_(DISPATCH_LEVEL) 9 | UNICODE_STRING get_unicode_string(const wchar_t* string); 10 | 11 | char* get_va_buffer(); 12 | 13 | template 14 | const char* va(const char* message, Args&&... args) 15 | { 16 | auto* buffer = get_va_buffer(); 17 | RtlStringCchPrintfA(buffer, VA_BUFFER_SIZE, message, std::forward(args)...); 18 | return buffer; 19 | } 20 | 21 | inline bool equal(const char* s1, const char* s2) 22 | { 23 | if (!s1) 24 | { 25 | return !s2; 26 | } 27 | 28 | while (*s1) 29 | { 30 | if (*(s1++) != *(s2++)) 31 | { 32 | return false; 33 | } 34 | } 35 | 36 | return !*s2; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/driver/sleep_callback.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "functional.hpp" 3 | 4 | class sleep_callback 5 | { 6 | public: 7 | enum class type 8 | { 9 | sleep, 10 | wakeup, 11 | }; 12 | 13 | using callback_function = std::function; 14 | 15 | sleep_callback() = default; 16 | sleep_callback(callback_function&& callback); 17 | ~sleep_callback(); 18 | 19 | sleep_callback(sleep_callback&& obj) noexcept = delete; 20 | sleep_callback& operator=(sleep_callback&& obj) noexcept = delete; 21 | 22 | sleep_callback(const sleep_callback& obj) = delete; 23 | sleep_callback& operator=(const sleep_callback& obj) = delete; 24 | 25 | private: 26 | void* handle_{nullptr}; 27 | callback_function callback_{}; 28 | 29 | void dispatcher(type type) const; 30 | 31 | _Function_class_(CALLBACK_FUNCTION) 32 | static void static_callback(void* context, void* argument1, void* argument2); 33 | }; 34 | -------------------------------------------------------------------------------- /src/shared/irp_data.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define HOOK_DRV_IOCTL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_NEITHER, FILE_ANY_ACCESS) 4 | #define UNHOOK_DRV_IOCTL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_NEITHER, FILE_ANY_ACCESS) 5 | #define WATCH_DRV_IOCTL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_NEITHER, FILE_ANY_ACCESS) 6 | #define GET_RECORDS_DRV_IOCTL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_NEITHER, FILE_ANY_ACCESS) 7 | 8 | static_assert(sizeof(void*) == 8); 9 | 10 | struct hook_request 11 | { 12 | uint32_t process_id{}; 13 | const void* target_address{}; 14 | const void* source_data{}; 15 | uint64_t source_data_size{}; 16 | }; 17 | 18 | struct watch_region 19 | { 20 | const void* virtual_address{}; 21 | size_t length{}; 22 | }; 23 | 24 | struct watch_request 25 | { 26 | uint32_t process_id{}; 27 | const watch_region* watch_regions{}; 28 | uint64_t watch_region_count{}; 29 | }; 30 | -------------------------------------------------------------------------------- /src/library/utils/io.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace utils::io 8 | { 9 | bool remove_file(const std::string& file); 10 | bool move_file(const std::string& src, const std::string& target); 11 | bool file_exists(const std::string& file); 12 | bool write_file(const std::string& file, const std::string& data, bool append = false); 13 | bool read_file(const std::string& file, std::string* data); 14 | std::string read_file(const std::string& file); 15 | size_t file_size(const std::string& file); 16 | bool create_directory(const std::string& directory); 17 | bool directory_exists(const std::string& directory); 18 | bool directory_is_empty(const std::string& directory); 19 | std::vector list_files(const std::string& directory); 20 | void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target); 21 | } 22 | -------------------------------------------------------------------------------- /src/library/driver_device.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "native_handle.hpp" 3 | 4 | class driver_device 5 | { 6 | public: 7 | driver_device() = default; 8 | driver_device(const std::string& driver_device); 9 | ~driver_device() = default; 10 | 11 | driver_device(const driver_device&) = delete; 12 | driver_device& operator=(const driver_device&) = delete; 13 | 14 | driver_device(driver_device&& obj) noexcept = default; 15 | driver_device& operator=(driver_device&& obj) noexcept = default; 16 | 17 | operator bool() const 18 | { 19 | return this->device_; 20 | } 21 | 22 | using data = std::vector; 23 | bool send(DWORD ioctl_code, const data& input) const; 24 | bool send(DWORD ioctl_code, const data& input, data& output) const; 25 | bool send(DWORD ioctl_code, const void* input, size_t input_length, void* output, size_t* output_length) const; 26 | 27 | private: 28 | native_handle device_{}; 29 | }; 30 | -------------------------------------------------------------------------------- /src/library/service_handle.cpp: -------------------------------------------------------------------------------- 1 | #include "std_include.hpp" 2 | #include "service_handle.hpp" 3 | 4 | service_handle::service_handle() 5 | : service_handle(nullptr) 6 | { 7 | } 8 | 9 | service_handle::service_handle(const SC_HANDLE handle) 10 | : handle_{handle} 11 | { 12 | } 13 | 14 | service_handle::~service_handle() 15 | { 16 | if (this->handle_) 17 | { 18 | CloseServiceHandle(this->handle_); 19 | this->handle_ = nullptr; 20 | } 21 | } 22 | 23 | service_handle::service_handle(service_handle&& obj) noexcept 24 | : service_handle() 25 | { 26 | this->operator=(std::move(obj)); 27 | } 28 | 29 | service_handle& service_handle::operator=(service_handle&& obj) noexcept 30 | { 31 | if (this != &obj) 32 | { 33 | this->~service_handle(); 34 | this->handle_ = obj.handle_; 35 | obj.handle_ = nullptr; 36 | } 37 | 38 | return *this; 39 | } 40 | 41 | service_handle::operator SC_HANDLE() const 42 | { 43 | return this->handle_; 44 | } 45 | -------------------------------------------------------------------------------- /src/library/native_handle.cpp: -------------------------------------------------------------------------------- 1 | #include "std_include.hpp" 2 | #include "native_handle.hpp" 3 | 4 | native_handle::native_handle() 5 | : native_handle(INVALID_HANDLE_VALUE) 6 | { 7 | } 8 | 9 | native_handle::native_handle(const HANDLE handle) 10 | : handle_{handle} 11 | { 12 | } 13 | 14 | native_handle::~native_handle() 15 | { 16 | if (this->operator bool()) 17 | { 18 | CloseHandle(this->handle_); 19 | this->handle_ = INVALID_HANDLE_VALUE; 20 | } 21 | } 22 | 23 | native_handle::native_handle(native_handle&& obj) noexcept 24 | : native_handle() 25 | { 26 | this->operator=(std::move(obj)); 27 | } 28 | 29 | native_handle& native_handle::operator=(native_handle&& obj) noexcept 30 | { 31 | if (this != &obj) 32 | { 33 | this->~native_handle(); 34 | this->handle_ = obj.handle_; 35 | obj.handle_ = INVALID_HANDLE_VALUE; 36 | } 37 | 38 | return *this; 39 | } 40 | 41 | native_handle::operator HANDLE() const 42 | { 43 | return this->handle_; 44 | } 45 | 46 | native_handle::operator bool() const 47 | { 48 | return this->handle_ != INVALID_HANDLE_VALUE; 49 | } 50 | -------------------------------------------------------------------------------- /src/driver/process_callback.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "functional.hpp" 3 | 4 | namespace process_callback 5 | { 6 | enum class type 7 | { 8 | create, 9 | destroy, 10 | }; 11 | 12 | using callback = void(process_id parent_id, process_id process_id, type type); 13 | using callback_function = std::function; 14 | 15 | void* add(callback_function callback); 16 | void remove(void* handle); 17 | 18 | class scoped_process_callback 19 | { 20 | public: 21 | scoped_process_callback() = default; 22 | 23 | scoped_process_callback(callback_function function) 24 | : handle_(add(std::move(function))) 25 | { 26 | } 27 | 28 | ~scoped_process_callback() 29 | { 30 | remove(this->handle_); 31 | } 32 | 33 | scoped_process_callback(scoped_process_callback&& obj) noexcept = delete; 34 | scoped_process_callback& operator=(scoped_process_callback&& obj) noexcept = delete; 35 | 36 | scoped_process_callback(const scoped_process_callback& obj) = delete; 37 | scoped_process_callback& operator=(const scoped_process_callback& obj) = delete; 38 | 39 | private: 40 | void* handle_{}; 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /src/driver/stdint.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef signed char int8_t; 4 | typedef short int16_t; 5 | typedef int int32_t; 6 | typedef long long int64_t; 7 | typedef unsigned char uint8_t; 8 | typedef unsigned short uint16_t; 9 | typedef unsigned int uint32_t; 10 | typedef unsigned long long uint64_t; 11 | 12 | typedef signed char int_least8_t; 13 | typedef short int_least16_t; 14 | typedef int int_least32_t; 15 | typedef long long int_least64_t; 16 | typedef unsigned char uint_least8_t; 17 | typedef unsigned short uint_least16_t; 18 | typedef unsigned int uint_least32_t; 19 | typedef unsigned long long uint_least64_t; 20 | 21 | typedef signed char int_fast8_t; 22 | typedef int int_fast16_t; 23 | typedef int int_fast32_t; 24 | typedef long long int_fast64_t; 25 | typedef unsigned char uint_fast8_t; 26 | typedef unsigned int uint_fast16_t; 27 | typedef unsigned int uint_fast32_t; 28 | typedef unsigned long long uint_fast64_t; 29 | 30 | typedef long long intmax_t; 31 | typedef unsigned long long uintmax_t; 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🧪 Hypervisor with EPT Hooking Support 2 | ![license](https://img.shields.io/github/license/momo5502/hypervisor.svg) 3 | [![build](https://github.com/momo5502/hypervisor/workflows/Build/badge.svg)](https://github.com/momo5502/hypervisor/actions) 4 | [![paypal](https://img.shields.io/badge/PayPal-support-blue.svg?logo=paypal)](https://paypal.me/momo5502) 5 | 6 | A lightweight experimental hypervisor that leverages Intel's VT-x virtualization technology to create stealthy memory hooks using EPT (Extended Page Tables). By manipulating second-level address translation, it enables invisible code execution interception that bypasses traditional memory integrity checks. 7 | 8 | ## Safety Warnings 9 | 10 | - **System Instability**: Improper hypervisor implementation can cause BSODs 11 | - **Data Loss Risk**: Always backup important data before testing 12 | - **Ethical Usage**: Only use for legitimate research and educational purposes 13 | 14 | ## Credits 15 | 16 | SimpleVisor 17 | gbhv 18 | 19 | Icon 20 | -------------------------------------------------------------------------------- /src/driver/exception.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef ASSERT 4 | #undef ASSERT 5 | #endif 6 | 7 | #define ASSERT( exp ) if(!(exp)) throw std::runtime_error("Assertion failed: " STRINGIFY(exp)) 8 | 9 | #ifdef NT_ASSERT 10 | #undef NT_ASSERT 11 | #endif 12 | 13 | #define NT_ASSERT( exp ) ASSERT( exp ) 14 | 15 | namespace std 16 | { 17 | class exception 18 | { 19 | public: 20 | exception() = default; 21 | 22 | exception& operator=(const exception& obj) noexcept = default; 23 | exception& operator=(exception&& obj) noexcept = default; 24 | 25 | exception(const exception& obj) noexcept = default; 26 | exception(exception&& obj) noexcept = default; 27 | 28 | virtual ~exception() = default; 29 | virtual const char* what() const noexcept = 0; 30 | }; 31 | 32 | class runtime_error : public exception 33 | { 34 | public: 35 | runtime_error(const char* message) 36 | : message_(message) 37 | { 38 | } 39 | 40 | runtime_error(const runtime_error& obj) noexcept = default; 41 | runtime_error& operator=(const runtime_error& obj) noexcept = default; 42 | 43 | runtime_error(runtime_error&& obj) noexcept = default; 44 | runtime_error& operator=(runtime_error&& obj) noexcept = default; 45 | 46 | const char* what() const noexcept override 47 | { 48 | return message_; 49 | } 50 | 51 | private: 52 | const char* message_{}; 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /src/driver/thread.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "functional.hpp" 3 | 4 | namespace thread 5 | { 6 | uint32_t get_processor_count(); 7 | uint32_t get_processor_index(); 8 | 9 | _IRQL_requires_min_(PASSIVE_LEVEL) 10 | _IRQL_requires_max_(APC_LEVEL) 11 | bool sleep(uint32_t milliseconds); 12 | 13 | _IRQL_requires_max_(APC_LEVEL) 14 | _IRQL_requires_min_(PASSIVE_LEVEL) 15 | _IRQL_requires_same_ 16 | void dispatch_on_all_cores(void (*callback)(void*), void* data, bool sequential = false); 17 | 18 | _IRQL_requires_max_(APC_LEVEL) 19 | _IRQL_requires_min_(PASSIVE_LEVEL) 20 | _IRQL_requires_same_ 21 | 22 | template 23 | void dispatch_on_all_cores(F&& callback, bool sequential = false) 24 | { 25 | dispatch_on_all_cores([](void* data) 26 | { 27 | (*static_cast(data))(); 28 | }, &callback, sequential); 29 | } 30 | 31 | class kernel_thread 32 | { 33 | public: 34 | kernel_thread() = default; 35 | kernel_thread(std::function&& callback); 36 | ~kernel_thread(); 37 | 38 | kernel_thread(kernel_thread&& obj) noexcept; 39 | kernel_thread& operator=(kernel_thread&& obj) noexcept; 40 | 41 | kernel_thread(const kernel_thread& obj) = delete; 42 | kernel_thread& operator=(const kernel_thread& obj) = delete; 43 | 44 | bool joinable() const; 45 | void join(); 46 | void detach(); 47 | 48 | private: 49 | PETHREAD handle_{nullptr}; 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /src/driver/new.cpp: -------------------------------------------------------------------------------- 1 | #include "std_include.hpp" 2 | #include "new.hpp" 3 | #include "exception.hpp" 4 | #include "memory.hpp" 5 | 6 | namespace 7 | { 8 | void* perform_checked_non_paged_allocation(const size_t size) 9 | { 10 | auto* memory = memory::allocate_non_paged_memory(size); 11 | if (!memory) 12 | { 13 | throw std::runtime_error("Memory allocation failed"); 14 | } 15 | 16 | return memory; 17 | } 18 | } 19 | 20 | void* operator new(const size_t size) 21 | { 22 | return perform_checked_non_paged_allocation(size); 23 | } 24 | 25 | void* operator new[](const size_t size) 26 | { 27 | return perform_checked_non_paged_allocation(size); 28 | } 29 | 30 | // Placement new 31 | void* operator new(size_t, void* where) 32 | { 33 | return where; 34 | } 35 | 36 | void operator delete(void* ptr, size_t) 37 | { 38 | memory::free_non_paged_memory(ptr); 39 | } 40 | 41 | void operator delete(void* ptr) 42 | { 43 | memory::free_non_paged_memory(ptr); 44 | } 45 | 46 | void operator delete[](void* ptr, size_t) 47 | { 48 | memory::free_non_paged_memory(ptr); 49 | } 50 | 51 | void operator delete[](void* ptr) 52 | { 53 | memory::free_non_paged_memory(ptr); 54 | } 55 | 56 | void operator delete(void*, size_t, std::align_val_t) 57 | { 58 | } 59 | 60 | void operator delete[](void*, size_t, std::align_val_t) 61 | { 62 | } 63 | 64 | extern "C" void __std_terminate() 65 | { 66 | KeBugCheckEx(DRIVER_VIOLATION, 14, 0, 0, 0); 67 | } 68 | -------------------------------------------------------------------------------- /src/driver/finally.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "type_traits.hpp" 3 | 4 | namespace utils 5 | { 6 | /* 7 | * Copied from here: https://github.com/microsoft/GSL/blob/e0880931ae5885eb988d1a8a57acf8bc2b8dacda/include/gsl/util#L57 8 | */ 9 | 10 | template 11 | class final_action 12 | { 13 | public: 14 | /*static_assert(!std::is_reference::value && !std::is_const::value && 15 | !std::is_volatile::value, 16 | "Final_action should store its callable by value");*/ 17 | 18 | explicit final_action(F f) noexcept : f_(std::move(f)) 19 | { 20 | } 21 | 22 | final_action(final_action&& other) noexcept 23 | : f_(std::move(other.f_)), invoke_(other.invoke_) 24 | { 25 | other.invoke_ = false; 26 | } 27 | 28 | final_action(const final_action&) = delete; 29 | final_action& operator=(const final_action&) = delete; 30 | final_action& operator=(final_action&&) = delete; 31 | 32 | ~final_action() noexcept 33 | { 34 | if (invoke_) f_(); 35 | } 36 | 37 | // Added by momo5502 38 | void cancel() 39 | { 40 | invoke_ = false; 41 | } 42 | 43 | private: 44 | F f_; 45 | bool invoke_{true}; 46 | }; 47 | 48 | template 49 | final_action::type>::type> 50 | finally(F&& f) noexcept 51 | { 52 | return final_action::type>::type>( 53 | std::forward(f)); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/library/finally.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace utils 6 | { 7 | /* 8 | * Copied from here: https://github.com/microsoft/GSL/blob/e0880931ae5885eb988d1a8a57acf8bc2b8dacda/include/gsl/util#L57 9 | */ 10 | 11 | template 12 | class final_action 13 | { 14 | public: 15 | static_assert(!std::is_reference::value && !std::is_const::value && 16 | !std::is_volatile::value, 17 | "Final_action should store its callable by value"); 18 | 19 | explicit final_action(F f) noexcept : f_(std::move(f)) 20 | { 21 | } 22 | 23 | final_action(final_action&& other) noexcept 24 | : f_(std::move(other.f_)), invoke_(std::exchange(other.invoke_, false)) 25 | { 26 | } 27 | 28 | final_action(const final_action&) = delete; 29 | final_action& operator=(const final_action&) = delete; 30 | final_action& operator=(final_action&&) = delete; 31 | 32 | ~final_action() noexcept 33 | { 34 | if (invoke_) f_(); 35 | } 36 | 37 | // Added by momo5502 38 | void cancel() 39 | { 40 | invoke_ = false; 41 | } 42 | 43 | private: 44 | F f_; 45 | bool invoke_{true}; 46 | }; 47 | 48 | template 49 | final_action::type>::type> 50 | finally(F&& f) noexcept 51 | { 52 | return final_action::type>::type>( 53 | std::forward(f)); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/driver/functional.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "unique_ptr.hpp" 3 | 4 | namespace std 5 | { 6 | template 7 | struct function; 8 | 9 | template 10 | struct function 11 | { 12 | private: 13 | struct fn_interface 14 | { 15 | virtual ~fn_interface() = default; 16 | virtual Result operator()(Args ...) const = 0; 17 | }; 18 | 19 | template 20 | struct fn_implementation : fn_interface 21 | { 22 | fn_implementation(F&& f) : f_(std::forward(f)) 23 | { 24 | } 25 | 26 | Result operator()(Args ... args) const override 27 | { 28 | return f_(std::forward(args)...); 29 | } 30 | 31 | F f_; 32 | }; 33 | 34 | std::unique_ptr fn_{}; 35 | 36 | public: 37 | function() = default; 38 | 39 | template 40 | function(T&& t) 41 | : fn_(new fn_implementation(std::forward(t))) 42 | { 43 | } 44 | 45 | ~function() = default; 46 | function(function&&) noexcept = default; 47 | function& operator=(function&&) noexcept = default; 48 | 49 | function(const function&) = delete; 50 | function& operator=(const function&) = delete; 51 | 52 | Result operator()(Args ... args) const 53 | { 54 | return (*this->fn_)(std::forward(args)...); 55 | } 56 | 57 | operator bool() const 58 | { 59 | return this->fn_; 60 | } 61 | }; 62 | } 63 | -------------------------------------------------------------------------------- /src/library/driver.cpp: -------------------------------------------------------------------------------- 1 | #include "std_include.hpp" 2 | #include "driver.hpp" 3 | 4 | driver::driver(const std::filesystem::path& driver_file, const std::string& service_name) 5 | { 6 | this->manager_ = OpenSCManagerA(nullptr, nullptr, SC_MANAGER_ALL_ACCESS); 7 | if (!this->manager_) 8 | { 9 | throw std::runtime_error("Unable to open SC manager"); 10 | } 11 | 12 | this->service_ = OpenServiceA(this->manager_, service_name.data(), SERVICE_ALL_ACCESS); 13 | 14 | if (!this->service_) 15 | { 16 | this->service_ = CreateServiceA(this->manager_, service_name.data(), 17 | service_name.data(), SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, 18 | SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, 19 | driver_file.generic_string().data(), nullptr, nullptr, 20 | nullptr, nullptr, nullptr); 21 | } 22 | 23 | if (!this->service_) 24 | { 25 | this->service_ = OpenServiceA(this->manager_, service_name.data(), SERVICE_ALL_ACCESS); 26 | } 27 | 28 | if (!this->service_) 29 | { 30 | this->manager_ = {}; 31 | throw std::runtime_error("Unable to create service"); 32 | } 33 | 34 | if(!StartServiceA(this->service_, 0, nullptr)) 35 | { 36 | printf("Failed to start service: %d\n", GetLastError()); 37 | } 38 | } 39 | 40 | driver::~driver() 41 | { 42 | if (this->service_) 43 | { 44 | SERVICE_STATUS status{}; 45 | ControlService(this->service_, SERVICE_CONTROL_STOP, &status); 46 | 47 | DeleteService(this->service_); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/driver/process.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace process 4 | { 5 | class process_handle 6 | { 7 | public: 8 | process_handle() = default; 9 | process_handle(PEPROCESS handle, bool own = true); 10 | ~process_handle(); 11 | 12 | process_handle(process_handle&& obj) noexcept; 13 | process_handle& operator=(process_handle&& obj) noexcept; 14 | 15 | process_handle(const process_handle& obj); 16 | process_handle& operator=(const process_handle& obj); 17 | 18 | operator bool() const; 19 | operator PEPROCESS() const; 20 | 21 | bool is_alive() const; 22 | process_id get_id() const; 23 | 24 | const char* get_image_filename() const; 25 | 26 | private: 27 | bool own_{true}; 28 | PEPROCESS handle_{nullptr}; 29 | 30 | void release(); 31 | }; 32 | 33 | process_id process_id_from_handle(HANDLE handle); 34 | HANDLE handle_from_process_id(process_id process); 35 | 36 | process_handle find_process_by_id(process_id process); 37 | process_handle get_current_process(); 38 | 39 | process_id get_current_process_id(); 40 | 41 | class scoped_process_attacher 42 | { 43 | public: 44 | scoped_process_attacher(const process_handle& process); 45 | ~scoped_process_attacher(); 46 | 47 | scoped_process_attacher(scoped_process_attacher&& obj) noexcept = delete; 48 | scoped_process_attacher& operator=(scoped_process_attacher&& obj) noexcept = delete; 49 | 50 | scoped_process_attacher(const scoped_process_attacher&) = delete; 51 | scoped_process_attacher& operator=(const scoped_process_attacher&) = delete; 52 | 53 | private: 54 | KAPC_STATE apc_state_{}; 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /external/vcrtl.cmake: -------------------------------------------------------------------------------- 1 | get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 2 | 3 | if(IS_MULTI_CONFIG) 4 | set(VCRTL_LIBRARY "${CMAKE_CURRENT_BINARY_DIR}/vcrtl/$/vcrtl_driver.lib") 5 | else() 6 | set(VCRTL_LIBRARY "${CMAKE_CURRENT_BINARY_DIR}/vcrtl/vcrtl_driver.lib") 7 | endif() 8 | 9 | ExternalProject_Add( 10 | vcrtl_build 11 | SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/vcrtl 12 | BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/vcrtl 13 | BUILD_COMMAND "${CMAKE_COMMAND}" --build ${CMAKE_CURRENT_BINARY_DIR}/vcrtl --config $ --target vcrtl_driver 14 | PATCH_COMMAND cmd /C "echo. > ${CMAKE_CURRENT_SOURCE_DIR}/vcrtl/src/runtime.cpp" 15 | INSTALL_COMMAND "" 16 | USES_TERMINAL_CONFIGURE 1 17 | USES_TERMINAL_BUILD 1 18 | BUILD_ALWAYS 1 19 | BUILD_BYPRODUCTS "${VCRTL_LIBRARY}" 20 | CMAKE_ARGS 21 | "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" 22 | 23 | "-DCMAKE_SHARED_LINKER_FLAGS=${CMAKE_SHARED_LINKER_FLAGS}" 24 | "-DCMAKE_MODULE_LINKER_FLAGS=${CMAKE_MODULE_LINKER_FLAGS}" 25 | "-DCMAKE_EXE_LINKER_FLAGS=${CMAKE_EXE_LINKER_FLAGS}" 26 | 27 | "-DCMAKE_C_FLAGS=${CMAKE_C_FLAGS}" 28 | "-DCMAKE_C_FLAGS_DEBUG=${CMAKE_C_FLAGS_DEBUG}" 29 | "-DCMAKE_C_FLAGS_RELEASE=${CMAKE_C_FLAGS_RELEASE}" 30 | 31 | "-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}" 32 | "-DCMAKE_CXX_FLAGS_DEBUG=${CMAKE_CXX_FLAGS_DEBUG}" 33 | "-DCMAKE_CXX_FLAGS_RELEASE=${CMAKE_CXX_FLAGS_RELEASE}" 34 | 35 | "-DCMAKE_MSVC_RUNTIME_LIBRARY=${CMAKE_MSVC_RUNTIME_LIBRARY}" 36 | ) 37 | 38 | add_library(vcrtl_driver INTERFACE) 39 | add_dependencies(vcrtl_driver vcrtl_build) 40 | target_link_libraries(vcrtl_driver INTERFACE "${VCRTL_LIBRARY}") 41 | -------------------------------------------------------------------------------- /src/driver/hypervisor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vmx.hpp" 4 | 5 | class hypervisor 6 | { 7 | public: 8 | hypervisor(); 9 | ~hypervisor(); 10 | 11 | hypervisor(hypervisor&& obj) noexcept = delete; 12 | hypervisor& operator=(hypervisor&& obj) noexcept = delete; 13 | 14 | hypervisor(const hypervisor& obj) = delete; 15 | hypervisor& operator=(const hypervisor& obj) = delete; 16 | 17 | void enable(); 18 | void disable(); 19 | 20 | bool is_enabled() const; 21 | 22 | bool install_ept_hook(const void* destination, const void* source, size_t length, process_id source_pid, 23 | process_id target_pid, const utils::list& hints = {}); 24 | 25 | bool install_ept_code_watch_point(uint64_t physical_page, process_id source_pid, process_id target_pid, 26 | bool invalidate = true) const; 27 | bool install_ept_code_watch_points(const uint64_t* physical_pages, size_t count, process_id source_pid, 28 | process_id target_pid) const; 29 | 30 | void disable_all_ept_hooks() const; 31 | 32 | vmx::ept& get_ept() const; 33 | 34 | static hypervisor* get_instance(); 35 | 36 | bool cleanup_process(process_id process); 37 | 38 | private: 39 | uint32_t vm_state_count_{0}; 40 | vmx::state** vm_states_{nullptr}; 41 | vmx::ept* ept_{nullptr}; 42 | 43 | void enable_core(uint64_t system_directory_table_base); 44 | bool try_enable_core(uint64_t system_directory_table_base); 45 | void disable_core(); 46 | 47 | void allocate_vm_states(); 48 | void free_vm_states(); 49 | 50 | void invalidate_cores() const; 51 | 52 | vmx::state* get_current_vm_state() const; 53 | }; 54 | -------------------------------------------------------------------------------- /src/driver/process_callback.cpp: -------------------------------------------------------------------------------- 1 | #include "std_include.hpp" 2 | #include "process_callback.hpp" 3 | #include "process.hpp" 4 | #include "list.hpp" 5 | #include "logging.hpp" 6 | 7 | namespace process_callback 8 | { 9 | namespace 10 | { 11 | utils::list& get_callback_list() 12 | { 13 | static utils::list list{}; 14 | return list; 15 | } 16 | 17 | void process_notification_callback(const HANDLE parent, const HANDLE process, const BOOLEAN create) 18 | { 19 | const auto& list = get_callback_list(); 20 | for (const auto& callback : list) 21 | { 22 | callback(process::process_id_from_handle(parent), process::process_id_from_handle(process), 23 | create == FALSE ? type::destroy : type::create); 24 | } 25 | } 26 | 27 | class process_notifier 28 | { 29 | public: 30 | process_notifier() 31 | : added_(PsSetCreateProcessNotifyRoutine(process_notification_callback, FALSE) == STATUS_SUCCESS) 32 | { 33 | get_callback_list(); 34 | 35 | if (!added_) 36 | { 37 | debug_log("Failed to register process notification callback\n"); 38 | } 39 | } 40 | 41 | ~process_notifier() 42 | { 43 | if (this->added_) 44 | { 45 | PsSetCreateProcessNotifyRoutine(process_notification_callback, TRUE); 46 | } 47 | } 48 | 49 | private: 50 | bool added_{}; 51 | }; 52 | } 53 | 54 | void* add(callback_function callback) 55 | { 56 | static process_notifier _; 57 | return &get_callback_list().push_back(std::move(callback)); 58 | } 59 | 60 | void remove(void* handle) 61 | { 62 | if (handle) 63 | { 64 | get_callback_list().erase(*static_cast(handle)); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 20, 6 | "patch": 0 7 | }, 8 | "configurePresets": [{ 9 | "name": "release", 10 | "displayName": "Default Release Config", 11 | "description": "Release build using Ninja generator", 12 | "generator": "Ninja", 13 | "binaryDir": "${sourceDir}/build/release", 14 | "cacheVariables": { 15 | "CMAKE_BUILD_TYPE": "Release" 16 | } 17 | }, 18 | { 19 | "name": "debug", 20 | "displayName": "Default Debug Config", 21 | "description": "Debug build using Ninja generator", 22 | "generator": "Ninja", 23 | "binaryDir": "${sourceDir}/build/debug", 24 | "cacheVariables": { 25 | "CMAKE_BUILD_TYPE": "Debug" 26 | } 27 | }, 28 | { 29 | "name": "vs2019", 30 | "displayName": "Visual Studio 2019", 31 | "description": "Visual Studio 2019 on Windows", 32 | "generator": "Visual Studio 16 2019", 33 | "binaryDir": "${sourceDir}/build/vs2019" 34 | }, 35 | { 36 | "name": "vs2022", 37 | "displayName": "Visual Studio 2022", 38 | "description": "Visual Studio 2022 on Windows", 39 | "generator": "Visual Studio 17 2022", 40 | "binaryDir": "${sourceDir}/build/vs2022" 41 | } 42 | ], 43 | "buildPresets": [{ 44 | "name": "release", 45 | "configurePreset": "release" 46 | }, 47 | { 48 | "name": "debug", 49 | "configurePreset": "debug" 50 | } 51 | ] 52 | } -------------------------------------------------------------------------------- /src/library/process.cpp: -------------------------------------------------------------------------------- 1 | #include "std_include.hpp" 2 | #include "process.hpp" 3 | 4 | namespace process 5 | { 6 | native_handle open(const uint32_t process_id, const DWORD access) 7 | { 8 | const auto handle = ::OpenProcess(access, FALSE, process_id); 9 | if (handle) 10 | { 11 | return {handle}; 12 | } 13 | 14 | return {}; 15 | } 16 | 17 | std::vector get_modules(const native_handle& process) 18 | { 19 | if (!process) 20 | { 21 | return {}; 22 | } 23 | 24 | DWORD needed = 1024; 25 | std::vector result{}; 26 | 27 | do 28 | { 29 | result.resize(needed); 30 | if (!EnumProcessModulesEx(process, result.data(), static_cast(result.size()), &needed, 31 | LIST_MODULES_ALL)) 32 | { 33 | return {}; 34 | } 35 | } 36 | while (needed > result.size()); 37 | 38 | result.resize(needed); 39 | 40 | // Remove duplicates 41 | std::ranges::sort(result); 42 | const auto last = std::ranges::unique(result).begin(); 43 | result.erase(last, result.end()); 44 | 45 | // Remove nullptr 46 | for (auto i = result.begin(); i != result.end();) 47 | { 48 | if (*i == nullptr) 49 | { 50 | i = result.erase(i); 51 | break; 52 | } 53 | else 54 | { 55 | ++i; 56 | } 57 | } 58 | 59 | return result; 60 | } 61 | 62 | std::string get_module_filename(const native_handle& process, const HMODULE module) 63 | { 64 | if (!process) 65 | { 66 | return {}; 67 | } 68 | 69 | std::string buffer{}; 70 | buffer.resize(1024); 71 | 72 | const auto length = GetModuleFileNameExA(process, module, buffer.data(), static_cast(buffer.size())); 73 | if (length > 0) 74 | { 75 | buffer.resize(length); 76 | return buffer; 77 | } 78 | 79 | return {}; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - "*" 7 | pull_request: 8 | branches: 9 | - "*" 10 | types: [opened, synchronize, reopened] 11 | 12 | jobs: 13 | build: 14 | name: Build binaries 15 | runs-on: windows-latest 16 | strategy: 17 | matrix: 18 | configuration: 19 | - debug 20 | - release 21 | steps: 22 | - name: Check out files 23 | uses: actions/checkout@v5 24 | with: 25 | submodules: true 26 | fetch-depth: 0 27 | lfs: false 28 | 29 | - name: Install WDK 30 | run: | 31 | curl -L --output wdksetup.exe https://go.microsoft.com/fwlink/?linkid=2196230 32 | cmd /c start /wait wdksetup.exe /ceip off /quiet /features + 33 | 34 | - name: Setup CMake 35 | uses: lukka/get-cmake@latest 36 | 37 | - name: Setup problem matching 38 | uses: ammaraskar/msvc-problem-matcher@master 39 | 40 | - name: Setup DevCmd 41 | uses: ilammy/msvc-dev-cmd@v1.13.0 42 | with: 43 | arch: x64 44 | 45 | - name: Configure CMake 46 | run: cmake --preset=${{matrix.configuration}} 47 | 48 | - name: Build ${{matrix.configuration}} 49 | run: cmake --build --preset=${{matrix.configuration}} 50 | 51 | - name: Upload ${{matrix.configuration}} binaries 52 | uses: actions/upload-artifact@v5 53 | with: 54 | name: ${{matrix.configuration}} binaries 55 | path: | 56 | build/${{matrix.configuration}}/artifacts/*.exe 57 | build/${{matrix.configuration}}/artifacts/*.dll 58 | build/${{matrix.configuration}}/artifacts/*.pdb 59 | build/${{matrix.configuration}}/artifacts/*.sys 60 | -------------------------------------------------------------------------------- /src/driver/sleep_callback.cpp: -------------------------------------------------------------------------------- 1 | #include "std_include.hpp" 2 | #include "sleep_callback.hpp" 3 | #include "exception.hpp" 4 | #include "finally.hpp" 5 | 6 | sleep_callback::sleep_callback(callback_function&& callback) 7 | : callback_(std::move(callback)) 8 | { 9 | PCALLBACK_OBJECT callback_object{}; 10 | UNICODE_STRING callback_name = RTL_CONSTANT_STRING(L"\\Callback\\PowerState"); 11 | OBJECT_ATTRIBUTES object_attributes = RTL_CONSTANT_OBJECT_ATTRIBUTES( 12 | &callback_name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE); 13 | 14 | const auto _ = utils::finally([&callback_object]() 15 | { 16 | ObDereferenceObject(callback_object); 17 | }); 18 | 19 | const auto status = ExCreateCallback(&callback_object, &object_attributes, FALSE, TRUE); 20 | if (!NT_SUCCESS(status)) 21 | { 22 | throw std::runtime_error("Unable to create callback object"); 23 | } 24 | 25 | this->handle_ = ExRegisterCallback(callback_object, sleep_callback::static_callback, this); 26 | if (!this->handle_) 27 | { 28 | throw std::runtime_error("Unable to register callback"); 29 | } 30 | } 31 | 32 | sleep_callback::~sleep_callback() 33 | { 34 | if (this->handle_) 35 | { 36 | ExUnregisterCallback(this->handle_); 37 | } 38 | } 39 | 40 | void sleep_callback::dispatcher(const type type) const 41 | { 42 | try 43 | { 44 | if (this->callback_) 45 | { 46 | this->callback_(type); 47 | } 48 | } 49 | catch (...) 50 | { 51 | } 52 | } 53 | 54 | _Function_class_(CALLBACK_FUNCTION) 55 | 56 | void sleep_callback::static_callback(void* context, void* argument1, void* argument2) 57 | { 58 | if (!context || argument1 != reinterpret_cast(PO_CB_SYSTEM_STATE_LOCK)) 59 | { 60 | return; 61 | } 62 | 63 | auto type = type::sleep; 64 | if (ARGUMENT_PRESENT(argument2)) 65 | { 66 | type = type::wakeup; 67 | } 68 | 69 | static_cast(context)->dispatcher(type); 70 | } 71 | -------------------------------------------------------------------------------- /src/driver/unique_ptr.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "type_traits.hpp" 3 | 4 | namespace std 5 | { 6 | template 7 | class unique_ptr 8 | { 9 | public: 10 | using value_type = typename remove_extent::type; 11 | 12 | unique_ptr() = default; 13 | 14 | unique_ptr(value_type* pointer) 15 | : pointer_(pointer) 16 | { 17 | } 18 | 19 | ~unique_ptr() 20 | { 21 | if (this->pointer_) 22 | { 23 | delete_pointer(); 24 | this->pointer_ = nullptr; 25 | } 26 | } 27 | 28 | unique_ptr(unique_ptr&& obj) noexcept 29 | : unique_ptr() 30 | { 31 | this->operator=(std::move(obj)); 32 | } 33 | 34 | unique_ptr& operator=(unique_ptr&& obj) noexcept 35 | { 36 | if (this != &obj) 37 | { 38 | this->~unique_ptr(); 39 | this->pointer_ = obj.pointer_; 40 | obj.pointer_ = nullptr; 41 | } 42 | 43 | return *this; 44 | } 45 | 46 | unique_ptr(const unique_ptr& obj) = delete; 47 | unique_ptr& operator=(const unique_ptr& obj) = delete; 48 | 49 | value_type* get() 50 | { 51 | return this->pointer_; 52 | } 53 | 54 | value_type* operator->() 55 | { 56 | return this->pointer_; 57 | } 58 | 59 | const value_type* operator->() const 60 | { 61 | return this->pointer_; 62 | } 63 | 64 | value_type& operator*() 65 | { 66 | return *this->pointer_; 67 | } 68 | 69 | const value_type& operator*() const 70 | { 71 | return *this->pointer_; 72 | } 73 | 74 | operator bool() const 75 | { 76 | return this->pointer_; 77 | } 78 | 79 | private: 80 | static constexpr auto is_array_type = is_array::value; 81 | value_type* pointer_{nullptr}; 82 | 83 | void delete_pointer() const 84 | { 85 | if (is_array_type) 86 | { 87 | delete[] this->pointer_; 88 | } 89 | else 90 | { 91 | delete this->pointer_; 92 | } 93 | } 94 | }; 95 | } 96 | -------------------------------------------------------------------------------- /src/driver/vmx.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ept.hpp" 3 | 4 | #define HYPERV_HYPERVISOR_PRESENT_BIT 0x80000000 5 | #define HYPERV_CPUID_INTERFACE 0x40000001 6 | 7 | namespace vmx 8 | { 9 | struct vmcs 10 | { 11 | uint32_t revision_id; 12 | uint32_t abort_indicator; 13 | uint8_t data[PAGE_SIZE - 8]; 14 | }; 15 | 16 | struct special_registers 17 | { 18 | uint64_t cr0; 19 | uint64_t cr3; 20 | uint64_t cr4; 21 | uint64_t msr_gs_base; 22 | uint16_t tr; 23 | uint16_t ldtr; 24 | uint64_t debug_control; 25 | uint64_t kernel_dr7; 26 | segment_descriptor_register_64 idtr; 27 | segment_descriptor_register_64 gdtr; 28 | }; 29 | 30 | struct launch_context 31 | { 32 | special_registers special_registers; 33 | CONTEXT context_frame; 34 | uint64_t system_directory_table_base; 35 | ULARGE_INTEGER msr_data[17]; 36 | uint64_t vmx_on_physical_address; 37 | uint64_t vmcs_physical_address; 38 | uint64_t msr_bitmap_physical_address; 39 | ia32_vmx_procbased_ctls2_register ept_controls; 40 | bool launched; 41 | }; 42 | 43 | struct state 44 | { 45 | union 46 | { 47 | DECLSPEC_PAGE_ALIGN uint8_t stack_buffer[KERNEL_STACK_SIZE]{}; 48 | DECLSPEC_PAGE_ALIGN launch_context launch_context; 49 | }; 50 | 51 | DECLSPEC_PAGE_ALIGN uint8_t msr_bitmap[PAGE_SIZE]{}; 52 | 53 | DECLSPEC_PAGE_ALIGN vmcs vmx_on{}; 54 | DECLSPEC_PAGE_ALIGN vmcs vmcs{}; 55 | 56 | DECLSPEC_PAGE_ALIGN ept* ept{}; 57 | }; 58 | 59 | struct gdt_entry 60 | { 61 | uint64_t base; 62 | uint32_t limit; 63 | vmx_segment_access_rights access_rights; 64 | segment_selector selector; 65 | }; 66 | 67 | struct guest_context 68 | { 69 | PCONTEXT vp_regs; 70 | uintptr_t guest_rip; 71 | uintptr_t guest_rsp; 72 | uintptr_t guest_e_flags; 73 | uintptr_t guest_physical_address; 74 | uint16_t exit_reason; 75 | uintptr_t exit_qualification; 76 | bool exit_vm; 77 | bool increment_rip; 78 | }; 79 | } 80 | -------------------------------------------------------------------------------- /src/library/driver_device.cpp: -------------------------------------------------------------------------------- 1 | #include "std_include.hpp" 2 | #include "driver_device.hpp" 3 | 4 | driver_device::driver_device(const std::string& driver_device) 5 | { 6 | this->device_ = CreateFileA(driver_device.data(), 7 | GENERIC_READ | GENERIC_WRITE, 8 | NULL, 9 | nullptr, 10 | OPEN_EXISTING, 11 | NULL, 12 | nullptr); 13 | 14 | if (!this->device_) 15 | { 16 | throw std::runtime_error("Unable to access device"); 17 | } 18 | } 19 | 20 | bool driver_device::send(const DWORD ioctl_code, const data& input) const 21 | { 22 | data output{}; 23 | return this->send(ioctl_code, input, output); 24 | } 25 | 26 | bool driver_device::send(const DWORD ioctl_code, const data& input, data& output) const 27 | { 28 | size_t out_len = output.size(); 29 | if (this->send(ioctl_code, input.data(), input.size(), output.data(), &out_len)) 30 | { 31 | output.resize(out_len); 32 | return true; 33 | } 34 | 35 | return false; 36 | } 37 | 38 | bool driver_device::send(const DWORD ioctl_code, const void* input, const size_t input_length, void* output, 39 | size_t* output_length) const 40 | { 41 | DWORD size_returned = 0; 42 | const auto success = DeviceIoControl(this->device_, 43 | ioctl_code, 44 | const_cast(input), 45 | static_cast(input_length), 46 | output, 47 | static_cast(*output_length), 48 | &size_returned, 49 | nullptr 50 | ) != FALSE; 51 | 52 | *output_length = 0; 53 | if (success) 54 | { 55 | *output_length = size_returned; 56 | } 57 | 58 | return success; 59 | } 60 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | #set(CMAKE_DISABLE_SOURCE_CHANGES ON) 4 | set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) 5 | 6 | project(hypervisor LANGUAGES C CXX) 7 | 8 | ########################################## 9 | 10 | set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE) 11 | 12 | set(CMAKE_CXX_STANDARD 20) 13 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 14 | 15 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 16 | 17 | ########################################## 18 | 19 | #set(WDK_WINVER "0x0603" CACHE STRING "Default WINVER for WDK targets") 20 | 21 | ########################################## 22 | set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded$<$:Debug>) 23 | 24 | if(MSVC) 25 | add_link_options( 26 | $<$>:/NODEFAULTLIB:libcmt.lib> 27 | $<$>:/NODEFAULTLIB:msvcrt.lib> 28 | $<$>:/NODEFAULTLIB:libcmtd.lib> 29 | $<$>:/NODEFAULTLIB:msvcrtd.lib> 30 | ) 31 | endif() 32 | 33 | ########################################## 34 | 35 | include(cmake/utils.cmake) 36 | 37 | ########################################## 38 | 39 | set_new_artifact_directory() 40 | enable_driver_support() 41 | 42 | ########################################## 43 | 44 | set(OPT_DEBUG "/Od /Ob0 /Zi") 45 | set(OPT_RELEASE "/O2 /Ob2 /Zi") 46 | 47 | add_link_options(/DEBUG) 48 | 49 | set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${OPT_DEBUG}") 50 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${OPT_DEBUG}") 51 | 52 | set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${OPT_RELEASE}") 53 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${OPT_RELEASE}") 54 | 55 | 56 | ########################################## 57 | 58 | add_subdirectory_and_get_targets("external" EXTERNAL_TARGETS) 59 | add_subdirectory_and_get_targets("src" SRC_TARGETS) 60 | targets_set_warnings_as_errors(${SRC_TARGETS}) 61 | -------------------------------------------------------------------------------- /src/driver/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | enable_language(ASM_MASM) 2 | 3 | string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) 4 | 5 | file(GLOB driver_sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) 6 | file(GLOB driver_headers CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp) 7 | file(GLOB driver_asm_sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.asm) 8 | 9 | wdk_add_driver(driver 10 | ${driver_sources} 11 | ${driver_headers} 12 | ${driver_asm_sources} 13 | ) 14 | target_precompile_headers(driver 15 | PRIVATE std_include.hpp 16 | ) 17 | 18 | cmake_path(NATIVE_PATH PROJECT_SOURCE_DIR NORMALIZE WINDOWS_PROJECT_DIR) 19 | 20 | #add_custom_command(TARGET driver 21 | # POST_BUILD 22 | # 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" "$" 23 | # COMMENT "Signing using Nvidia certificate (Revoked with KB5013942)" 24 | #) 25 | 26 | target_link_libraries(driver 27 | vcrtl_driver 28 | ia32_doc 29 | shared 30 | ) 31 | 32 | target_compile_options(driver PRIVATE 33 | "/Zc:threadSafeInit-" 34 | ) 35 | 36 | target_link_options(driver PRIVATE 37 | "/IGNORE:4210" 38 | ) 39 | 40 | set_source_files_properties(resource.rc PROPERTIES LANGUAGE RC) 41 | target_sources(driver PRIVATE 42 | resource.rc 43 | ) 44 | 45 | set_target_properties(driver PROPERTIES OUTPUT_NAME "hyperhook") 46 | 47 | ################################################ 48 | 49 | set(DRIVER_FILE "$") 50 | set(DRIVER_NAME "$") 51 | 52 | file (GENERATE 53 | OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/$>/driver_file.h" 54 | CONTENT "#define DRIVER_FILE \"${DRIVER_FILE}\"\n#define DRIVER_NAME \"${DRIVER_NAME}\"\n" 55 | ) 56 | 57 | add_library(driver_file INTERFACE) 58 | 59 | target_include_directories(driver_file INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/$>) 60 | 61 | add_dependencies(driver_file driver) 62 | -------------------------------------------------------------------------------- /cert/1111222.cer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFmjCCA4KgAwIBAgIKYRmT5AAAAAAAHDANBgkqhkiG9w0BAQUFADB/MQswCQYD 3 | VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe 4 | MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQDEyBNaWNyb3Nv 5 | ZnQgQ29kZSBWZXJpZmljYXRpb24gUm9vdDAeFw0xMTAyMjIxOTI1MTdaFw0yMTAy 6 | MjIxOTM1MTdaMIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIElu 7 | Yy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShj 8 | KSAyMDA2IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkx 9 | RTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlm 10 | aWNhdGlvbiBBdXRob3JpdHkgLSBHNTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC 11 | AQoCggEBAK8kCAgpejWeYAyq50s7Ttx8vDxFHLsr4P4pAvlXCKNkhRUn9fGtyDGJ 12 | XSLoKqqmQrOP+LlVt7G3S7P+j34HV+zvQ9tmYhVhz2ANpNje+ODDYgg9VBPrScpZ 13 | VIUm5SuPG5/r9aGRwjNJ2ENjalJL0o/ocFFN0Ylpe8dw9rPcEnTbe11LVtOWvxV3 14 | obD0oiXyrxySZxjl9AYE75C55ADk3Tq1Gf8CuvQ87uCL6zeL7PTXrPL28D2v3XWR 15 | MxkdHEDLdCQZIZPZFP6sKlLHj9UESeSNY0eIPGmDy/5HvSt+T8WVrg6d1NFDwGdz 16 | 4xQIfuU/n3O4MwrPXT80h5aK7lPoJRUCAwEAAaOByzCByDARBgNVHSAECjAIMAYG 17 | BFUdIAAwDwYDVR0TAQH/BAUwAwEB/zALBgNVHQ8EBAMCAYYwHQYDVR0OBBYEFH/T 18 | ZafC3ey78DAJ80M5+gKvMzEzMB8GA1UdIwQYMBaAFGL7CiFbf0NuEdoJVFBr9dKW 19 | cfGeMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9w 20 | a2kvY3JsL3Byb2R1Y3RzL01pY3Jvc29mdENvZGVWZXJpZlJvb3QuY3JsMA0GCSqG 21 | SIb3DQEBBQUAA4ICAQCBKoIWjDRnK+UD6zR7jKKjUIr0VYbxHoyOrn3uAxnOcpUY 22 | SK1iEf0g/T9HBgFa4uBvjBUsTjxqUGwLNqPPeg2cQrxc+BnVYONp5uIjQWeMaIN2 23 | K4+Toyq1f75Z+6nJsiaPyqLzghuYPpGVJ5eGYe5bXQdrzYao4mWAqOIV4rK+IwVq 24 | ugzzR5NNrKSMB3k5wGESOgUNiaPsn1eJhPvsynxHZhSR2LYPGV3muEqsvEfIcUOW 25 | 5jIgpdx3hv0844tx23ubA/y3HTJk6xZSoEOj+i6tWZJOfMfyM0JIOFE6fDjHGyQi 26 | KEAeGkYfF9sY9/AnNWy4Y9nNuWRdK6Ve78YptPLH+CHMBLpX/QG2q8Zn+efTmX/0 27 | 9SL6cvX9/zocQjqh+YAYpe6NHNRmnkUB/qru//sXjzD38c0pxZ3stdVJAD2FuMu7 28 | kzonaknAMK5myfcjKDJ2+aSDVshIzlqWqqDMDMR/tI6Xr23jVCfDn4bA1uRzCJcF 29 | 29BUYl4DSMLVn3+nZozQnbBP1NOYX0t6yX+yKVLQEoDHD1S2HmfNxqBsEQOE00h1 30 | 5yr+sDtuCjqma3aZBaPxd2hhMxRHBvxTf1K9khRcSiRqZ4yvjZCq0PZ5IRuTJnzD 31 | zh69iDiSrkXGGWpJULMF+K5ZN4pqJQOUsVmBUOi6g4C3IzX0drlnHVkYrSCNlA== 32 | -----END CERTIFICATE----- 33 | -------------------------------------------------------------------------------- /src/library/resource.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "windows.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // English (United States) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 19 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 20 | 21 | #ifdef APSTUDIO_INVOKED 22 | ///////////////////////////////////////////////////////////////////////////// 23 | // 24 | // TEXTINCLUDE 25 | // 26 | 27 | 1 TEXTINCLUDE 28 | BEGIN 29 | "#include ""windows.h""\r\n" 30 | "\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "\r\n" 36 | "\0" 37 | END 38 | 39 | #endif // APSTUDIO_INVOKED 40 | 41 | ///////////////////////////////////////////////////////////////////////////// 42 | // 43 | // Version 44 | // 45 | 46 | VS_VERSION_INFO VERSIONINFO 47 | FILEVERSION 1,0,0,0 48 | PRODUCTVERSION 1,0,0,0 49 | FILEFLAGSMASK 0x3fL 50 | #ifdef _DEBUG 51 | FILEFLAGS 0x1L 52 | #else 53 | FILEFLAGS 0x0L 54 | #endif 55 | FILEOS 0x40004L 56 | FILETYPE VFT_DLL 57 | FILESUBTYPE 0x0L 58 | BEGIN 59 | BLOCK "StringFileInfo" 60 | BEGIN 61 | BLOCK "040904b0" 62 | BEGIN 63 | VALUE "CompanyName", "momo5502" 64 | VALUE "FileDescription", "HyperHook" 65 | VALUE "FileVersion", "1.0.0.0" 66 | VALUE "InternalName", "HyperHook" 67 | VALUE "LegalCopyright", "All rights reserved." 68 | VALUE "OriginalFilename", "hyperhook.dll" 69 | VALUE "ProductName", "hyperhook" 70 | VALUE "ProductVersion", "1.0.0.0" 71 | END 72 | END 73 | BLOCK "VarFileInfo" 74 | BEGIN 75 | VALUE "Translation", 0x409, 1200 76 | END 77 | END 78 | 79 | 80 | 81 | #endif // English (United States) resources 82 | ///////////////////////////////////////////////////////////////////////////// 83 | 84 | 85 | 86 | #ifndef APSTUDIO_INVOKED 87 | ///////////////////////////////////////////////////////////////////////////// 88 | // 89 | // Generated from the TEXTINCLUDE 3 resource. 90 | // 91 | 92 | 93 | ///////////////////////////////////////////////////////////////////////////// 94 | #endif // not APSTUDIO_INVOKED 95 | -------------------------------------------------------------------------------- /src/driver/resource.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "windows.h" 11 | #include "resource.hpp" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // English (United States) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "#include ""windows.h""\r\n" 31 | "\0" 32 | END 33 | 34 | 2 TEXTINCLUDE 35 | BEGIN 36 | "\r\n" 37 | "\0" 38 | END 39 | 40 | #endif // APSTUDIO_INVOKED 41 | 42 | ///////////////////////////////////////////////////////////////////////////// 43 | // 44 | // Version 45 | // 46 | 47 | VS_VERSION_INFO VERSIONINFO 48 | FILEVERSION 1,0,0,0 49 | PRODUCTVERSION 1,0,0,0 50 | FILEFLAGSMASK 0x3fL 51 | #ifdef _DEBUG 52 | FILEFLAGS 0x1L 53 | #else 54 | FILEFLAGS 0x0L 55 | #endif 56 | FILEOS 0x40004L 57 | FILETYPE VFT_DLL 58 | FILESUBTYPE 0x0L 59 | BEGIN 60 | BLOCK "StringFileInfo" 61 | BEGIN 62 | BLOCK "040904b0" 63 | BEGIN 64 | VALUE "CompanyName", "momo5502" 65 | VALUE "FileDescription", "HyperHook Driver" 66 | VALUE "FileVersion", "1.0.0.0" 67 | VALUE "InternalName", "HyperHook Driver" 68 | VALUE "LegalCopyright", "All rights reserved." 69 | VALUE "OriginalFilename", "hyperhook.sys" 70 | VALUE "ProductName", "hyperhook" 71 | VALUE "ProductVersion", "1.0.0.0" 72 | END 73 | END 74 | BLOCK "VarFileInfo" 75 | BEGIN 76 | VALUE "Translation", 0x409, 1200 77 | END 78 | END 79 | 80 | 81 | 82 | #endif // English (United States) resources 83 | ///////////////////////////////////////////////////////////////////////////// 84 | 85 | 86 | 87 | #ifndef APSTUDIO_INVOKED 88 | ///////////////////////////////////////////////////////////////////////////// 89 | // 90 | // Generated from the TEXTINCLUDE 3 resource. 91 | // 92 | 93 | 94 | ///////////////////////////////////////////////////////////////////////////// 95 | #endif // not APSTUDIO_INVOKED 96 | -------------------------------------------------------------------------------- /src/driver/memory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "type_traits.hpp" 3 | 4 | namespace memory 5 | { 6 | _IRQL_requires_max_(DISPATCH_LEVEL) 7 | void free_aligned_memory(void* memory); 8 | 9 | _Must_inspect_result_ 10 | _IRQL_requires_max_(DISPATCH_LEVEL) 11 | void* allocate_aligned_memory(size_t size); 12 | 13 | _IRQL_requires_max_(APC_LEVEL) 14 | bool read_physical_memory(void* destination, uint64_t physical_address, size_t size); 15 | 16 | uint64_t get_physical_address(void* address); 17 | void* get_virtual_address(uint64_t address); 18 | 19 | _Must_inspect_result_ 20 | _IRQL_requires_max_(DISPATCH_LEVEL) 21 | void* map_physical_memory(const uint64_t address, const size_t size); 22 | 23 | _IRQL_requires_max_(DISPATCH_LEVEL) 24 | void unmap_physical_memory(void* address, const size_t size); 25 | 26 | _Must_inspect_result_ 27 | _IRQL_requires_max_(DISPATCH_LEVEL) 28 | void* allocate_non_paged_memory(size_t size); 29 | 30 | _IRQL_requires_max_(DISPATCH_LEVEL) 31 | void free_non_paged_memory(void* memory); 32 | 33 | bool probe_for_read(const void* address, size_t length, uint64_t alignment = 1); 34 | void assert_readability(const void* address, size_t length, uint64_t alignment = 1); 35 | 36 | bool probe_for_write(const void* address, size_t length, uint64_t alignment = 1); 37 | void assert_writability(const void* address, size_t length, uint64_t alignment = 1); 38 | 39 | template 40 | T* allocate_non_paged_object(Args ... args) 41 | { 42 | auto* object = static_cast(allocate_non_paged_memory(sizeof(T))); 43 | if (object) 44 | { 45 | new(object) T(std::forward(args)...); 46 | } 47 | 48 | return object; 49 | } 50 | 51 | template 52 | void free_non_paged_object(T* object) 53 | { 54 | if (object) 55 | { 56 | object->~T(); 57 | free_non_paged_memory(object); 58 | } 59 | } 60 | 61 | template 62 | T* allocate_aligned_object(Args ... args) 63 | { 64 | auto* object = static_cast(allocate_aligned_memory(sizeof(T))); 65 | if (object) 66 | { 67 | new(object) T(std::forward(args)...); 68 | } 69 | 70 | return object; 71 | } 72 | 73 | template 74 | void free_aligned_object(T* object) 75 | { 76 | if (object) 77 | { 78 | object->~T(); 79 | free_aligned_memory(object); 80 | } 81 | } 82 | } 83 | 84 | inline uint64_t operator"" _kb(const uint64_t size) 85 | { 86 | return size * 1024; 87 | } 88 | 89 | inline uint64_t operator"" _mb(const uint64_t size) 90 | { 91 | return size * 1024_kb; 92 | } 93 | 94 | inline uint64_t operator"" _gb(const uint64_t size) 95 | { 96 | return size * 1024_mb; 97 | } 98 | -------------------------------------------------------------------------------- /src/library/main.cpp: -------------------------------------------------------------------------------- 1 | #include "std_include.hpp" 2 | 3 | #include "driver.hpp" 4 | #include "driver_device.hpp" 5 | #include 6 | #include 7 | 8 | #include "utils/io.hpp" 9 | 10 | #define DLL_IMPORT __declspec(dllexport) 11 | #include 12 | 13 | namespace 14 | { 15 | void patch_data(const driver_device& driver_device, const uint32_t pid, const uint64_t address, 16 | const uint8_t* buffer, 17 | const size_t length) 18 | { 19 | hook_request hook_request{}; 20 | hook_request.process_id = pid; 21 | hook_request.target_address = reinterpret_cast(address); 22 | 23 | hook_request.source_data = buffer; 24 | hook_request.source_data_size = length; 25 | 26 | driver_device::data input{}; 27 | input.assign(reinterpret_cast(&hook_request), 28 | reinterpret_cast(&hook_request) + sizeof(hook_request)); 29 | 30 | (void)driver_device.send(HOOK_DRV_IOCTL, input); 31 | } 32 | 33 | driver_device create_driver_device() 34 | { 35 | return driver_device{R"(\\.\HyperHook)"}; 36 | } 37 | 38 | driver create_driver() 39 | { 40 | return driver{std::filesystem::absolute(DRIVER_NAME), "HyperHookDriver"}; 41 | } 42 | 43 | driver_device& get_driver_device() 44 | { 45 | static driver hypervisor{}; 46 | static driver_device device{}; 47 | 48 | if (!device) 49 | { 50 | try 51 | { 52 | device = create_driver_device(); 53 | } 54 | catch (...) 55 | { 56 | } 57 | } 58 | 59 | if (device) 60 | { 61 | return device; 62 | } 63 | 64 | if (!hypervisor) 65 | { 66 | hypervisor = create_driver(); 67 | } 68 | 69 | if (!device) 70 | { 71 | device = create_driver_device(); 72 | } 73 | 74 | return device; 75 | } 76 | } 77 | 78 | int hyperhook_initialize() 79 | { 80 | try 81 | { 82 | const auto& device = get_driver_device(); 83 | if (device) 84 | { 85 | return 1; 86 | } 87 | } 88 | catch (const std::exception& e) 89 | { 90 | printf("%s\n", e.what()); 91 | } 92 | 93 | return 0; 94 | } 95 | 96 | int hyperhook_write(const unsigned int process_id, const unsigned long long address, const void* data, 97 | const unsigned long long size) 98 | { 99 | if (hyperhook_initialize() == 0) 100 | { 101 | return 0; 102 | } 103 | 104 | try 105 | { 106 | const auto& device = get_driver_device(); 107 | if (device) 108 | { 109 | patch_data(device, process_id, address, static_cast(data), size); 110 | return 1; 111 | } 112 | } 113 | catch (const std::exception& e) 114 | { 115 | printf("%s\n", e.what()); 116 | } 117 | 118 | return 0; 119 | } 120 | -------------------------------------------------------------------------------- /src/runner/resource.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "windows.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // English (United States) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 19 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 20 | 21 | #ifdef APSTUDIO_INVOKED 22 | ///////////////////////////////////////////////////////////////////////////// 23 | // 24 | // TEXTINCLUDE 25 | // 26 | 27 | 1 TEXTINCLUDE 28 | BEGIN 29 | "#include ""windows.h""\r\n" 30 | "\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "\r\n" 36 | "\0" 37 | END 38 | 39 | #endif // APSTUDIO_INVOKED 40 | 41 | ///////////////////////////////////////////////////////////////////////////// 42 | // 43 | // Version 44 | // 45 | 46 | VS_VERSION_INFO VERSIONINFO 47 | FILEVERSION 1,0,0,0 48 | PRODUCTVERSION 1,0,0,0 49 | FILEFLAGSMASK 0x3fL 50 | #ifdef _DEBUG 51 | FILEFLAGS 0x1L 52 | #else 53 | FILEFLAGS 0x0L 54 | #endif 55 | FILEOS 0x40004L 56 | FILETYPE VFT_DLL 57 | FILESUBTYPE 0x0L 58 | BEGIN 59 | BLOCK "StringFileInfo" 60 | BEGIN 61 | BLOCK "040904b0" 62 | BEGIN 63 | VALUE "CompanyName", "momo5502" 64 | VALUE "FileDescription", "HyperHook Runner" 65 | VALUE "FileVersion", "1.0.0.0" 66 | VALUE "InternalName", "HyperHook Runner" 67 | VALUE "LegalCopyright", "All rights reserved." 68 | VALUE "OriginalFilename", "runner.exe" 69 | VALUE "ProductName", "runner" 70 | VALUE "ProductVersion", "1.0.0.0" 71 | END 72 | END 73 | BLOCK "VarFileInfo" 74 | BEGIN 75 | VALUE "Translation", 0x409, 1200 76 | END 77 | END 78 | 79 | ///////////////////////////////////////////////////////////////////////////// 80 | // 81 | // Binary Data 82 | // 83 | 84 | 102 ICON "resources/icon.ico" 85 | 86 | 87 | 88 | #endif // English (United States) resources 89 | ///////////////////////////////////////////////////////////////////////////// 90 | 91 | 92 | 93 | #ifndef APSTUDIO_INVOKED 94 | ///////////////////////////////////////////////////////////////////////////// 95 | // 96 | // Generated from the TEXTINCLUDE 3 resource. 97 | // 98 | 99 | 100 | ///////////////////////////////////////////////////////////////////////////// 101 | #endif // not APSTUDIO_INVOKED 102 | -------------------------------------------------------------------------------- /src/driver/assembly.asm: -------------------------------------------------------------------------------- 1 | include ksamd64.inc 2 | 3 | .code 4 | 5 | ; ----------------------------------------------------- 6 | 7 | LEAF_ENTRY _str, _TEXT$00 8 | str word ptr [rcx] 9 | ret 10 | LEAF_END _str, _TEXT$00 11 | 12 | ; ----------------------------------------------------- 13 | 14 | LEAF_ENTRY _sldt, _TEXT$00 15 | sldt word ptr [rcx] 16 | ret 17 | LEAF_END _sldt, _TEXT$00 18 | 19 | ; ----------------------------------------------------- 20 | 21 | LEAF_ENTRY __lgdt, _TEXT$00 22 | lgdt fword ptr [rcx] 23 | ret 24 | LEAF_END __lgdt, _TEXT$00 25 | 26 | ; ----------------------------------------------------- 27 | 28 | LEAF_ENTRY __invept, _TEXT$00 29 | invept rcx, OWORD PTR [rdx] 30 | ret 31 | LEAF_END __invept, _TEXT$00 32 | 33 | ; ----------------------------------------------------- 34 | 35 | LEAF_ENTRY restore_context, _TEXT$00 36 | movaps xmm0, CxXmm0[rcx] 37 | movaps xmm1, CxXmm1[rcx] 38 | movaps xmm2, CxXmm2[rcx] 39 | movaps xmm3, CxXmm3[rcx] 40 | movaps xmm4, CxXmm4[rcx] 41 | movaps xmm5, CxXmm5[rcx] 42 | movaps xmm6, CxXmm6[rcx] 43 | movaps xmm7, CxXmm7[rcx] 44 | movaps xmm8, CxXmm8[rcx] 45 | movaps xmm9, CxXmm9[rcx] 46 | movaps xmm10, CxXmm10[rcx] 47 | movaps xmm11, CxXmm11[rcx] 48 | movaps xmm12, CxXmm12[rcx] 49 | movaps xmm13, CxXmm13[rcx] 50 | movaps xmm14, CxXmm14[rcx] 51 | movaps xmm15, CxXmm15[rcx] 52 | ldmxcsr CxMxCsr[rcx] 53 | 54 | mov rax, CxRax[rcx] 55 | mov rdx, CxRdx[rcx] 56 | mov r8, CxR8[rcx] 57 | mov r9, CxR9[rcx] 58 | mov r10, CxR10[rcx] 59 | mov r11, CxR11[rcx] 60 | 61 | mov rbx, CxRbx[rcx] 62 | mov rsi, CxRsi[rcx] 63 | mov rdi, CxRdi[rcx] 64 | mov rbp, CxRbp[rcx] 65 | mov r12, CxR12[rcx] 66 | mov r13, CxR13[rcx] 67 | mov r14, CxR14[rcx] 68 | mov r15, CxR15[rcx] 69 | 70 | cli 71 | push CxEFlags[rcx] 72 | popfq 73 | mov rsp, CxRsp[rcx] 74 | push CxRip[rcx] 75 | mov rcx, CxRcx[rcx] 76 | ret 77 | LEAF_END restore_context, _TEXT$00 78 | 79 | ; ----------------------------------------------------- 80 | 81 | extern vm_exit_handler:proc 82 | extern vm_launch_handler:proc 83 | extern RtlCaptureContext:proc 84 | 85 | ; ----------------------------------------------------- 86 | 87 | vm_launch PROC 88 | mov rcx, rsp 89 | sub rsp, 30h 90 | jmp vm_launch_handler 91 | vm_launch ENDP 92 | 93 | ; ----------------------------------------------------- 94 | 95 | vm_exit PROC 96 | ; Load CONTEXT pointer 97 | push rcx 98 | lea rcx, [rsp+8h] 99 | 100 | sub rsp, 30h ; Home-space 101 | call RtlCaptureContext 102 | add rsp, 30h 103 | 104 | mov rcx, [rsp+CxRsp+8h] 105 | add rcx, 8h ; Fixup push rcx 106 | add rcx, 30h ; Fixup home-space 107 | mov [rsp+CxRsp+8h], rcx 108 | 109 | pop rcx 110 | mov [rsp+CxRcx], rcx 111 | 112 | mov rcx, rsp 113 | sub rsp, 30h 114 | jmp vm_exit_handler 115 | vm_exit ENDP 116 | 117 | end 118 | -------------------------------------------------------------------------------- /src/library/utils/nt.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../std_include.hpp" 4 | 5 | // min and max is required by gdi, therefore NOMINMAX won't work 6 | #ifdef max 7 | #undef max 8 | #endif 9 | 10 | #ifdef min 11 | #undef min 12 | #endif 13 | 14 | namespace utils::nt 15 | { 16 | class library final 17 | { 18 | public: 19 | static library load(const std::string& name); 20 | static library load(const std::filesystem::path& path); 21 | static library get_by_address(void* address); 22 | 23 | library(); 24 | explicit library(const std::string& name); 25 | explicit library(HMODULE handle); 26 | 27 | library(const library& a) : module_(a.module_) 28 | { 29 | } 30 | 31 | bool operator!=(const library& obj) const { return !(*this == obj); }; 32 | bool operator==(const library& obj) const; 33 | 34 | operator bool() const; 35 | operator HMODULE() const; 36 | 37 | void unprotect() const; 38 | void* get_entry_point() const; 39 | size_t get_relative_entry_point() const; 40 | 41 | bool is_valid() const; 42 | std::string get_name() const; 43 | std::string get_path() const; 44 | std::string get_folder() const; 45 | std::uint8_t* get_ptr() const; 46 | void free(); 47 | 48 | HMODULE get_handle() const; 49 | 50 | template 51 | T get_proc(const std::string& process) const 52 | { 53 | if (!this->is_valid()) T{}; 54 | return reinterpret_cast(GetProcAddress(this->module_, process.data())); 55 | } 56 | 57 | template 58 | std::function get(const std::string& process) const 59 | { 60 | if (!this->is_valid()) return std::function(); 61 | return static_cast(this->get_proc(process)); 62 | } 63 | 64 | template 65 | T invoke(const std::string& process, Args ... args) const 66 | { 67 | auto method = this->get(process); 68 | if (method) return method(args...); 69 | return T(); 70 | } 71 | 72 | template 73 | T invoke_pascal(const std::string& process, Args ... args) const 74 | { 75 | auto method = this->get(process); 76 | if (method) return method(args...); 77 | return T(); 78 | } 79 | 80 | template 81 | T invoke_this(const std::string& process, void* this_ptr, Args ... args) const 82 | { 83 | auto method = this->get(this_ptr, process); 84 | if (method) return method(args...); 85 | return T(); 86 | } 87 | 88 | std::vector get_section_headers() const; 89 | 90 | PIMAGE_NT_HEADERS get_nt_headers() const; 91 | PIMAGE_DOS_HEADER get_dos_header() const; 92 | PIMAGE_OPTIONAL_HEADER get_optional_header() const; 93 | 94 | void** get_iat_entry(const std::string& module_name, const std::string& proc_name) const; 95 | 96 | private: 97 | HMODULE module_; 98 | }; 99 | 100 | __declspec(noreturn) void raise_hard_exception(); 101 | std::string load_resource(int id); 102 | 103 | void relaunch_self(); 104 | __declspec(noreturn) void terminate(uint32_t code = 0); 105 | } 106 | -------------------------------------------------------------------------------- /src/library/utils/io.cpp: -------------------------------------------------------------------------------- 1 | #include "io.hpp" 2 | #include "nt.hpp" 3 | #include 4 | 5 | namespace utils::io 6 | { 7 | bool remove_file(const std::string& file) 8 | { 9 | return DeleteFileA(file.data()) == TRUE; 10 | } 11 | 12 | bool move_file(const std::string& src, const std::string& target) 13 | { 14 | return MoveFileA(src.data(), target.data()) == TRUE; 15 | } 16 | 17 | bool file_exists(const std::string& file) 18 | { 19 | return std::ifstream(file).good(); 20 | } 21 | 22 | bool write_file(const std::string& file, const std::string& data, const bool append) 23 | { 24 | const auto pos = file.find_last_of("/\\"); 25 | if (pos != std::string::npos) 26 | { 27 | create_directory(file.substr(0, pos)); 28 | } 29 | 30 | std::ofstream stream( 31 | file, std::ios::binary | std::ofstream::out | (append ? std::ofstream::app : 0)); 32 | 33 | if (stream.is_open()) 34 | { 35 | stream.write(data.data(), data.size()); 36 | stream.close(); 37 | return true; 38 | } 39 | 40 | return false; 41 | } 42 | 43 | std::string read_file(const std::string& file) 44 | { 45 | std::string data; 46 | read_file(file, &data); 47 | return data; 48 | } 49 | 50 | bool read_file(const std::string& file, std::string* data) 51 | { 52 | if (!data) return false; 53 | data->clear(); 54 | 55 | if (file_exists(file)) 56 | { 57 | std::ifstream stream(file, std::ios::binary); 58 | if (!stream.is_open()) return false; 59 | 60 | stream.seekg(0, std::ios::end); 61 | const std::streamsize size = stream.tellg(); 62 | stream.seekg(0, std::ios::beg); 63 | 64 | if (size > -1) 65 | { 66 | data->resize(static_cast(size)); 67 | stream.read(const_cast(data->data()), size); 68 | stream.close(); 69 | return true; 70 | } 71 | } 72 | 73 | return false; 74 | } 75 | 76 | size_t file_size(const std::string& file) 77 | { 78 | if (file_exists(file)) 79 | { 80 | std::ifstream stream(file, std::ios::binary); 81 | 82 | if (stream.good()) 83 | { 84 | stream.seekg(0, std::ios::end); 85 | return static_cast(stream.tellg()); 86 | } 87 | } 88 | 89 | return 0; 90 | } 91 | 92 | bool create_directory(const std::string& directory) 93 | { 94 | return std::filesystem::create_directories(directory); 95 | } 96 | 97 | bool directory_exists(const std::string& directory) 98 | { 99 | return std::filesystem::is_directory(directory); 100 | } 101 | 102 | bool directory_is_empty(const std::string& directory) 103 | { 104 | return std::filesystem::is_empty(directory); 105 | } 106 | 107 | std::vector list_files(const std::string& directory) 108 | { 109 | std::vector files; 110 | 111 | for (auto& file : std::filesystem::directory_iterator(directory)) 112 | { 113 | files.push_back(file.path().generic_string()); 114 | } 115 | 116 | return files; 117 | } 118 | 119 | void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target) 120 | { 121 | std::filesystem::copy(src, target, 122 | std::filesystem::copy_options::overwrite_existing | 123 | std::filesystem::copy_options::recursive); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/driver/type_traits.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace std 4 | { 5 | using size_t = ::size_t; 6 | 7 | // TEMPLATE CLASS remove_reference 8 | template 9 | struct remove_reference 10 | { 11 | // remove reference 12 | typedef _Ty type; 13 | }; 14 | 15 | template 16 | struct remove_reference<_Ty&> 17 | { 18 | // remove reference 19 | typedef _Ty type; 20 | }; 21 | 22 | template 23 | struct remove_reference<_Ty&&> 24 | { 25 | // remove rvalue reference 26 | typedef _Ty type; 27 | }; 28 | 29 | template 30 | typename remove_reference::type&& move(T&& arg) noexcept 31 | { 32 | return static_cast::type&&>(arg); 33 | } 34 | 35 | template 36 | using remove_reference_t = typename remove_reference<_Ty>::type; 37 | 38 | // TEMPLATE FUNCTION forward 39 | template 40 | constexpr _Ty&& forward( 41 | typename remove_reference<_Ty>::type& _Arg) 42 | { 43 | // forward an lvalue as either an lvalue or an rvalue 44 | return (static_cast<_Ty&&>(_Arg)); 45 | } 46 | 47 | template 48 | constexpr _Ty&& forward( 49 | typename remove_reference<_Ty>::type&& _Arg) 50 | { 51 | // forward an rvalue as an rvalue 52 | return (static_cast<_Ty&&>(_Arg)); 53 | } 54 | 55 | template 56 | struct remove_cv 57 | { 58 | typedef T type; 59 | }; 60 | 61 | template 62 | struct remove_cv 63 | { 64 | typedef T type; 65 | }; 66 | 67 | template 68 | struct remove_cv 69 | { 70 | typedef T type; 71 | }; 72 | 73 | template 74 | struct remove_cv 75 | { 76 | typedef T type; 77 | }; 78 | 79 | template 80 | struct remove_const 81 | { 82 | typedef T type; 83 | }; 84 | 85 | template 86 | struct remove_const 87 | { 88 | typedef T type; 89 | }; 90 | 91 | template 92 | struct remove_volatile 93 | { 94 | typedef T type; 95 | }; 96 | 97 | template 98 | struct remove_volatile 99 | { 100 | typedef T type; 101 | }; 102 | 103 | 104 | template 105 | struct integral_constant 106 | { 107 | static constexpr T value = v; 108 | using value_type = T; 109 | using type = integral_constant; 110 | constexpr operator value_type() const noexcept { return value; } 111 | constexpr value_type operator()() const noexcept { return value; } 112 | }; 113 | 114 | // ALIAS TEMPLATE bool_constant 115 | template 116 | using bool_constant = integral_constant; 117 | 118 | using true_type = bool_constant; 119 | using false_type = bool_constant; 120 | 121 | template 122 | struct is_array : std::false_type 123 | { 124 | }; 125 | 126 | template 127 | struct is_array : std::true_type 128 | { 129 | }; 130 | 131 | template 132 | struct is_array : std::true_type 133 | { 134 | }; 135 | 136 | 137 | template 138 | struct remove_extent 139 | { 140 | typedef T type; 141 | }; 142 | 143 | template 144 | struct remove_extent 145 | { 146 | typedef T type; 147 | }; 148 | 149 | template 150 | struct remove_extent 151 | { 152 | typedef T type; 153 | }; 154 | } 155 | -------------------------------------------------------------------------------- /src/driver/globals.cpp: -------------------------------------------------------------------------------- 1 | #include "std_include.hpp" 2 | #include "list.hpp" 3 | #include "globals.hpp" 4 | 5 | #include "logging.hpp" 6 | 7 | #define _CRTALLOC(x) __declspec(allocate(x)) 8 | 9 | typedef void (__cdecl* _PVFV)(void); 10 | typedef int (__cdecl* _PIFV)(void); 11 | 12 | #pragma section(".CRT$XIA", long, read) // First C Initializer 13 | #pragma section(".CRT$XIZ", long, read) // Last C Initializer 14 | 15 | #pragma section(".CRT$XTA", long, read) // First Terminator 16 | #pragma section(".CRT$XTZ", long, read) // Last Terminator 17 | 18 | #pragma section(".CRT$XCA", long, read) // First C++ Initializer 19 | #pragma section(".CRT$XCZ", long, read) // Last C++ Initializer 20 | 21 | #pragma section(".CRT$XPA", long, read) // First Pre-Terminator 22 | #pragma section(".CRT$XPZ", long, read) // Last Pre-Terminator 23 | 24 | extern "C" _CRTALLOC(".CRT$XIA") _PIFV __xi_a[] = {nullptr}; // C initializers (first) 25 | extern "C" _CRTALLOC(".CRT$XIZ") _PIFV __xi_z[] = {nullptr}; // C initializers (last) 26 | extern "C" _CRTALLOC(".CRT$XCA") _PVFV __xc_a[] = {nullptr}; // C++ initializers (first) 27 | extern "C" _CRTALLOC(".CRT$XCZ") _PVFV __xc_z[] = {nullptr}; // C++ initializers (last) 28 | extern "C" _CRTALLOC(".CRT$XPA") _PVFV __xp_a[] = {nullptr}; // C pre-terminators (first) 29 | extern "C" _CRTALLOC(".CRT$XPZ") _PVFV __xp_z[] = {nullptr}; // C pre-terminators (last) 30 | extern "C" _CRTALLOC(".CRT$XTA") _PVFV __xt_a[] = {nullptr}; // C terminators (first) 31 | extern "C" _CRTALLOC(".CRT$XTZ") _PVFV __xt_z[] = {nullptr}; // C terminators (last) 32 | 33 | namespace globals 34 | { 35 | namespace 36 | { 37 | using destructor = void(*)(); 38 | using destructor_list = utils::list; 39 | 40 | destructor_list* destructors = nullptr; 41 | 42 | int run_callbacks(_PIFV* begin, const _PIFV* end) 43 | { 44 | int ret = 0; 45 | 46 | while (begin < end && ret == 0) 47 | { 48 | if (*begin) 49 | { 50 | ret = (**begin)(); 51 | } 52 | ++begin; 53 | } 54 | 55 | return ret; 56 | } 57 | 58 | void run_callbacks(_PVFV* begin, const _PVFV* end) 59 | { 60 | while (begin < end) 61 | { 62 | if (*begin) 63 | { 64 | (**begin)(); 65 | } 66 | ++begin; 67 | } 68 | } 69 | } 70 | 71 | void run_constructors() 72 | { 73 | if (!destructors) 74 | { 75 | destructors = new destructor_list(); 76 | } 77 | 78 | run_callbacks(__xp_a, __xp_z); 79 | run_callbacks(__xc_a, __xc_z); 80 | } 81 | 82 | void run_destructors() 83 | { 84 | if (!destructors) 85 | { 86 | return; 87 | } 88 | 89 | run_callbacks(__xi_a, __xi_z); 90 | run_callbacks(__xt_a, __xt_z); 91 | 92 | for (auto* destructor : *destructors) 93 | { 94 | try 95 | { 96 | destructor(); 97 | } 98 | catch (const std::exception& e) 99 | { 100 | debug_log("Running destructor failed: %s\n", e.what()); 101 | } 102 | } 103 | 104 | delete destructors; 105 | destructors = nullptr; 106 | } 107 | } 108 | 109 | int atexit(const globals::destructor destructor) 110 | { 111 | if (!globals::destructors) 112 | { 113 | return 1; 114 | } 115 | 116 | try 117 | { 118 | globals::destructors->push_front(destructor); 119 | } 120 | catch (const std::exception& e) 121 | { 122 | debug_log("Registering destructor failed: %s\n", e.what()); 123 | } 124 | 125 | return 0; 126 | } 127 | -------------------------------------------------------------------------------- /src/driver/process.cpp: -------------------------------------------------------------------------------- 1 | #include "std_include.hpp" 2 | #include "process.hpp" 3 | #include "type_traits.hpp" 4 | #include "exception.hpp" 5 | 6 | namespace process 7 | { 8 | process_handle::process_handle(const PEPROCESS handle, const bool own) 9 | : own_(own), handle_(handle) 10 | { 11 | } 12 | 13 | process_handle::~process_handle() 14 | { 15 | this->release(); 16 | } 17 | 18 | process_handle::process_handle(process_handle&& obj) noexcept 19 | : process_handle() 20 | { 21 | this->operator=(std::move(obj)); 22 | } 23 | 24 | process_handle& process_handle::operator=(process_handle&& obj) noexcept 25 | { 26 | if (this != &obj) 27 | { 28 | this->release(); 29 | this->own_ = obj.own_; 30 | this->handle_ = obj.handle_; 31 | obj.own_ = false; 32 | obj.handle_ = nullptr; 33 | } 34 | 35 | return *this; 36 | } 37 | 38 | process_handle::process_handle(const process_handle& obj) 39 | { 40 | this->operator=(obj); 41 | } 42 | 43 | process_handle& process_handle::operator=(const process_handle& obj) 44 | { 45 | if (this != &obj) 46 | { 47 | this->release(); 48 | this->own_ = obj.own_; 49 | this->handle_ = obj.handle_; 50 | 51 | if (this->own_ && this->handle_) 52 | { 53 | ObReferenceObject(this->handle_); 54 | } 55 | } 56 | 57 | return *this; 58 | } 59 | 60 | process_handle::operator bool() const 61 | { 62 | return this->handle_ != nullptr; 63 | } 64 | 65 | process_handle::operator PEPROCESS() const 66 | { 67 | return this->handle_; 68 | } 69 | 70 | bool process_handle::is_alive() const 71 | { 72 | if (!this->handle_) 73 | { 74 | return false; 75 | } 76 | 77 | LARGE_INTEGER zero_time{}; 78 | zero_time.QuadPart = 0; 79 | 80 | return KeWaitForSingleObject(this->handle_, Executive, KernelMode, FALSE, &zero_time) != STATUS_WAIT_0; 81 | } 82 | 83 | process_id process_handle::get_id() const 84 | { 85 | if (!this->handle_) 86 | { 87 | return 0; 88 | } 89 | 90 | return process_id_from_handle(PsGetProcessId(this->handle_)); 91 | } 92 | 93 | const char* process_handle::get_image_filename() const 94 | { 95 | if (!this->handle_) 96 | { 97 | return nullptr; 98 | } 99 | 100 | return PsGetProcessImageFileName(this->handle_); 101 | } 102 | 103 | void process_handle::release() 104 | { 105 | if (this->own_ && this->handle_) 106 | { 107 | ObDereferenceObject(this->handle_); 108 | } 109 | 110 | this->handle_ = nullptr; 111 | this->own_ = false; 112 | } 113 | 114 | process_id process_id_from_handle(HANDLE handle) 115 | { 116 | return process_id(size_t(handle)); 117 | } 118 | 119 | HANDLE handle_from_process_id(const process_id process) 120 | { 121 | return HANDLE(size_t(process)); 122 | } 123 | 124 | process_handle find_process_by_id(const process_id process) 125 | { 126 | PEPROCESS process_obj{}; 127 | if (PsLookupProcessByProcessId(handle_from_process_id(process), &process_obj) != STATUS_SUCCESS) 128 | { 129 | return {}; 130 | } 131 | 132 | return process_handle{process_obj, true}; 133 | } 134 | 135 | process_handle get_current_process() 136 | { 137 | return process_handle{PsGetCurrentProcess(), false}; 138 | } 139 | 140 | process_id get_current_process_id() 141 | { 142 | return get_current_process().get_id(); 143 | } 144 | 145 | scoped_process_attacher::scoped_process_attacher(const process_handle& process) 146 | { 147 | if (!process || !process.is_alive()) 148 | { 149 | throw std::runtime_error("Invalid process"); 150 | } 151 | 152 | KeStackAttachProcess(process, &this->apc_state_); 153 | } 154 | 155 | scoped_process_attacher::~scoped_process_attacher() 156 | { 157 | KeUnstackDetachProcess(&this->apc_state_); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/driver/driver_main.cpp: -------------------------------------------------------------------------------- 1 | #include "std_include.hpp" 2 | #include "logging.hpp" 3 | #include "sleep_callback.hpp" 4 | #include "irp.hpp" 5 | #include "exception.hpp" 6 | #include "hypervisor.hpp" 7 | #include "globals.hpp" 8 | #include "process.hpp" 9 | #include "process_callback.hpp" 10 | 11 | #define DOS_DEV_NAME L"\\DosDevices\\HyperHook" 12 | #define DEV_NAME L"\\Device\\HyperHook" 13 | 14 | class global_driver 15 | { 16 | public: 17 | global_driver(const PDRIVER_OBJECT driver_object) 18 | : sleep_callback_([this](const sleep_callback::type type) 19 | { 20 | this->sleep_notification(type); 21 | }), 22 | process_callback_( 23 | [this](const process_id parent_id, const process_id process_id, const process_callback::type type) 24 | { 25 | this->process_notification(parent_id, process_id, type); 26 | }), 27 | irp_(driver_object, DEV_NAME, DOS_DEV_NAME) 28 | { 29 | debug_log("Driver started\n"); 30 | } 31 | 32 | ~global_driver() 33 | { 34 | debug_log("Unloading driver\n"); 35 | } 36 | 37 | global_driver(global_driver&&) noexcept = delete; 38 | global_driver& operator=(global_driver&&) noexcept = delete; 39 | 40 | global_driver(const global_driver&) = delete; 41 | global_driver& operator=(const global_driver&) = delete; 42 | 43 | void pre_destroy(const PDRIVER_OBJECT /*driver_object*/) 44 | { 45 | } 46 | 47 | private: 48 | bool hypervisor_was_enabled_{false}; 49 | hypervisor hypervisor_{}; 50 | sleep_callback sleep_callback_{}; 51 | process_callback::scoped_process_callback process_callback_{}; 52 | irp irp_{}; 53 | 54 | void sleep_notification(const sleep_callback::type type) 55 | { 56 | if (type == sleep_callback::type::sleep) 57 | { 58 | debug_log("Going to sleep...\n"); 59 | this->hypervisor_was_enabled_ = this->hypervisor_.is_enabled(); 60 | this->hypervisor_.disable(); 61 | } 62 | 63 | if (type == sleep_callback::type::wakeup && this->hypervisor_was_enabled_) 64 | { 65 | debug_log("Waking up...\n"); 66 | this->hypervisor_.enable(); 67 | } 68 | } 69 | 70 | void process_notification(process_id /*parent_id*/, const process_id process_id, const process_callback::type type) 71 | { 72 | if (type == process_callback::type::destroy) 73 | { 74 | if (this->hypervisor_.cleanup_process(process_id)) 75 | { 76 | const auto proc = process::find_process_by_id(process_id); 77 | if(proc) 78 | { 79 | debug_log("Handled termination of %s\n", proc.get_image_filename()); 80 | } 81 | } 82 | } 83 | } 84 | }; 85 | 86 | global_driver* global_driver_instance{nullptr}; 87 | 88 | _Function_class_(DRIVER_UNLOAD) void unload(const PDRIVER_OBJECT driver_object) 89 | { 90 | try 91 | { 92 | if (global_driver_instance) 93 | { 94 | global_driver_instance->pre_destroy(driver_object); 95 | delete global_driver_instance; 96 | global_driver_instance = nullptr; 97 | } 98 | 99 | globals::run_destructors(); 100 | } 101 | catch (std::exception& e) 102 | { 103 | debug_log("Destruction error occured: %s\n", e.what()); 104 | } 105 | catch (...) 106 | { 107 | debug_log("Unknown destruction error occured. This should not happen!"); 108 | } 109 | } 110 | 111 | extern "C" NTSTATUS DriverEntry(const PDRIVER_OBJECT driver_object, PUNICODE_STRING /*registry_path*/) 112 | { 113 | try 114 | { 115 | driver_object->DriverUnload = unload; 116 | globals::run_constructors(); 117 | global_driver_instance = new global_driver(driver_object); 118 | } 119 | catch (std::exception& e) 120 | { 121 | debug_log("Error: %s\n", e.what()); 122 | return STATUS_INTERNAL_ERROR; 123 | } 124 | catch (...) 125 | { 126 | debug_log("Unknown initialization error occured"); 127 | return STATUS_INTERNAL_ERROR; 128 | } 129 | 130 | return STATUS_SUCCESS; 131 | } 132 | -------------------------------------------------------------------------------- /cmake/utils.cmake: -------------------------------------------------------------------------------- 1 | include_guard() 2 | 3 | ########################################## 4 | 5 | macro(set_artifact_directory directory) 6 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${directory}) 7 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${directory}) 8 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${directory}) 9 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${directory}) 10 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${directory}) 11 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${directory}) 12 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${directory}) 13 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${directory}) 14 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${directory}) 15 | endmacro() 16 | 17 | ########################################## 18 | 19 | macro(set_new_artifact_directory) 20 | get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 21 | if(IS_MULTI_CONFIG) 22 | set(ARTIFACT_FOLDER_NAME "artifacts-$>") 23 | else() 24 | set(ARTIFACT_FOLDER_NAME "artifacts") 25 | endif() 26 | 27 | set(ARTIFACT_DIRECTORY "${CMAKE_BINARY_DIR}/${ARTIFACT_FOLDER_NAME}") 28 | set_artifact_directory(${ARTIFACT_DIRECTORY}) 29 | endmacro() 30 | 31 | ########################################## 32 | 33 | macro(enable_driver_support) 34 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/external/FindWDK/cmake") 35 | find_package(WDK REQUIRED) 36 | list(REMOVE_ITEM WDK_COMPILE_FLAGS /kernel) 37 | endmacro() 38 | 39 | ########################################## 40 | 41 | function(target_set_warnings_as_errors target) 42 | get_target_property(target_type ${target} TYPE) 43 | if(("${target_type}" STREQUAL "INTERFACE_LIBRARY") OR ("${target_type}" STREQUAL "UTILITY")) 44 | return() 45 | endif() 46 | 47 | set(compile_options) 48 | 49 | if(MSVC) 50 | set(compile_options /W4 /WX) 51 | if (CLANG) 52 | set(compile_options ${compile_options} -Xclang -Wconversion) 53 | endif() 54 | else() 55 | # lots of warnings and all warnings as errors 56 | set(compile_options -Wall -Wextra -Wconversion -pedantic -Werror) 57 | endif() 58 | 59 | target_compile_options(${target} PRIVATE 60 | $<$:$<$:${compile_options}>> 61 | $<$:$<$:${compile_options}>> 62 | ) 63 | endfunction() 64 | 65 | ########################################## 66 | 67 | function(targets_set_warnings_as_errors) 68 | foreach(target ${ARGV}) 69 | target_set_warnings_as_errors(${target}) 70 | endforeach() 71 | endfunction() 72 | 73 | ########################################## 74 | 75 | function(get_all_targets var) 76 | set(targets) 77 | get_all_targets_recursive(targets ${CMAKE_CURRENT_SOURCE_DIR}) 78 | set(${var} ${targets} PARENT_SCOPE) 79 | endfunction() 80 | 81 | ########################################## 82 | 83 | macro(get_all_targets_recursive targets dir) 84 | get_property(subdirectories DIRECTORY ${dir} PROPERTY SUBDIRECTORIES) 85 | foreach(subdir ${subdirectories}) 86 | get_all_targets_recursive(${targets} ${subdir}) 87 | endforeach() 88 | 89 | get_property(current_targets DIRECTORY ${dir} PROPERTY BUILDSYSTEM_TARGETS) 90 | list(APPEND ${targets} ${current_targets}) 91 | endmacro() 92 | 93 | ########################################## 94 | 95 | macro(list_difference list_a list_to_remove result) 96 | set(${result} ${list_a}) 97 | list(REMOVE_ITEM ${result} ${list_to_remove}) 98 | endmacro() 99 | 100 | ########################################## 101 | 102 | macro(add_subdirectory_and_get_targets directory targets) 103 | get_all_targets(EXISTING_TARGETS) 104 | add_subdirectory(${directory}) 105 | get_all_targets(ALL_TARGETS) 106 | 107 | list_difference("${ALL_TARGETS}" "${EXISTING_TARGETS}" ${targets}) 108 | endmacro() 109 | -------------------------------------------------------------------------------- /src/driver/nt_ext.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" 5 | { 6 | #endif 7 | 8 | // ---------------------------------------- 9 | 10 | NTKERNELAPI 11 | _IRQL_requires_max_(APC_LEVEL) 12 | _IRQL_requires_min_(PASSIVE_LEVEL) 13 | _IRQL_requires_same_ 14 | VOID 15 | KeGenericCallDpc( 16 | _In_ PKDEFERRED_ROUTINE Routine, 17 | _In_opt_ PVOID Context 18 | ); 19 | 20 | // ---------------------------------------- 21 | 22 | NTKERNELAPI 23 | _IRQL_requires_(DISPATCH_LEVEL) 24 | _IRQL_requires_same_ 25 | VOID 26 | KeSignalCallDpcDone( 27 | _In_ PVOID SystemArgument1 28 | ); 29 | 30 | // ---------------------------------------- 31 | 32 | NTKERNELAPI 33 | _IRQL_requires_(DISPATCH_LEVEL) 34 | _IRQL_requires_same_ 35 | LOGICAL 36 | KeSignalCallDpcSynchronize( 37 | _In_ PVOID SystemArgument2 38 | ); 39 | 40 | // ---------------------------------------- 41 | 42 | #if (NTDDI_VERSION < NTDDI_WIN8) 43 | _Must_inspect_result_ 44 | _IRQL_requires_max_(DISPATCH_LEVEL) 45 | NTKERNELAPI 46 | _When_(return != NULL, _Post_writable_byte_size_ (NumberOfBytes)) PVOID 47 | MmAllocateContiguousNodeMemory( 48 | _In_ SIZE_T NumberOfBytes, 49 | _In_ PHYSICAL_ADDRESS LowestAcceptableAddress, 50 | _In_ PHYSICAL_ADDRESS HighestAcceptableAddress, 51 | _In_opt_ PHYSICAL_ADDRESS BoundaryAddressMultiple, 52 | _In_ ULONG Protect, 53 | _In_ NODE_REQUIREMENT PreferredNode 54 | ); 55 | #endif 56 | 57 | // ---------------------------------------- 58 | 59 | typedef struct _MM_COPY_ADDRESS { 60 | union { 61 | PVOID VirtualAddress; 62 | PHYSICAL_ADDRESS PhysicalAddress; 63 | }; 64 | } MM_COPY_ADDRESS, * PMMCOPY_ADDRESS; 65 | 66 | #define MM_COPY_MEMORY_PHYSICAL 0x1 67 | #define MM_COPY_MEMORY_VIRTUAL 0x2 68 | 69 | _IRQL_requires_max_(APC_LEVEL) 70 | NTKERNELAPI 71 | NTSTATUS 72 | MmCopyMemory( 73 | _In_ PVOID TargetAddress, 74 | _In_ MM_COPY_ADDRESS SourceAddress, 75 | _In_ SIZE_T NumberOfBytes, 76 | _In_ ULONG Flags, 77 | _Out_ PSIZE_T NumberOfBytesTransferred 78 | ); 79 | 80 | // ---------------------------------------- 81 | 82 | NTSYSAPI 83 | VOID 84 | NTAPI 85 | RtlCaptureContext( 86 | _Out_ PCONTEXT ContextRecord 87 | ); 88 | 89 | // ---------------------------------------- 90 | 91 | typedef struct _KAPC_STATE 92 | { 93 | LIST_ENTRY ApcListHead[MaximumMode]; 94 | struct _KPROCESS* Process; 95 | BOOLEAN KernelApcInProgress; 96 | BOOLEAN KernelApcPending; 97 | BOOLEAN UserApcPending; 98 | } KAPC_STATE, *PKAPC_STATE, *PRKAPC_STATE; 99 | 100 | // ---------------------------------------- 101 | 102 | NTKERNELAPI 103 | VOID 104 | KeStackAttachProcess( 105 | __inout PEPROCESS PROCESS, 106 | __out PRKAPC_STATE ApcState 107 | ); 108 | 109 | // ---------------------------------------- 110 | 111 | NTKERNELAPI 112 | VOID 113 | KeUnstackDetachProcess( 114 | __in PRKAPC_STATE ApcState 115 | ); 116 | 117 | // ---------------------------------------- 118 | 119 | NTKERNELAPI 120 | NTSTATUS 121 | PsLookupProcessByProcessId( 122 | IN HANDLE ProcessId, 123 | OUT PEPROCESS* Process 124 | ); 125 | 126 | // ---------------------------------------- 127 | 128 | NTKERNELAPI 129 | PVOID 130 | PsGetProcessSectionBaseAddress( 131 | __in PEPROCESS Process 132 | ); 133 | 134 | // ---------------------------------------- 135 | 136 | NTKERNELAPI 137 | PPEB 138 | NTAPI 139 | PsGetProcessPeb( 140 | IN PEPROCESS Process 141 | ); 142 | 143 | // ---------------------------------------- 144 | 145 | NTKERNELAPI 146 | PCSTR 147 | PsGetProcessImageFileName( 148 | __in PEPROCESS Process 149 | ); 150 | 151 | // ---------------------------------------- 152 | 153 | __kernel_entry NTSYSCALLAPI 154 | NTSTATUS 155 | NTAPI 156 | NtCreateFile( 157 | _Out_ PHANDLE FileHandle, 158 | _In_ ACCESS_MASK DesiredAccess, 159 | _In_ POBJECT_ATTRIBUTES ObjectAttributes, 160 | _Out_ PIO_STATUS_BLOCK IoStatusBlock, 161 | _In_opt_ PLARGE_INTEGER AllocationSize, 162 | _In_ ULONG FileAttributes, 163 | _In_ ULONG ShareAccess, 164 | _In_ ULONG CreateDisposition, 165 | _In_ ULONG CreateOptions, 166 | _In_reads_bytes_opt_(EaLength) PVOID EaBuffer, 167 | _In_ ULONG EaLength 168 | ); 169 | 170 | #ifdef __cplusplus 171 | } 172 | #endif 173 | -------------------------------------------------------------------------------- /src/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define WIN32_LEAN_AND_MEAN 7 | #include 8 | 9 | #include 10 | 11 | 12 | bool patch_data(const uint32_t process_id, const uint64_t address, const void* buffer, 13 | const size_t length) 14 | { 15 | return hyperhook_write(process_id, address, buffer, length) != 0; 16 | } 17 | 18 | bool insert_nop(const uint32_t process_id, const uint64_t address, const size_t length) 19 | { 20 | std::vector buffer{}; 21 | buffer.resize(length); 22 | memset(buffer.data(), 0x90, buffer.size()); 23 | 24 | return patch_data(process_id, address, buffer.data(), buffer.size()); 25 | } 26 | 27 | std::optional get_process_id_from_window(const char* class_name, const char* window_name) 28 | { 29 | const auto window = FindWindowA(class_name, window_name); 30 | if (!window) 31 | { 32 | return {}; 33 | } 34 | 35 | DWORD process_id{}; 36 | GetWindowThreadProcessId(window, &process_id); 37 | return static_cast(process_id); 38 | } 39 | 40 | void patch_iw5(const uint32_t pid) 41 | { 42 | insert_nop(pid, 0x4488A8, 2); // Force calling CG_DrawFriendOrFoeTargetBoxes 43 | insert_nop(pid, 0x47F6C7, 2); // Ignore blind-eye perks 44 | //insert_nop(driver_device, pid, 0x44894C, 2); // Miniconsole 45 | 46 | // Always full alpha 47 | constexpr uint8_t data1[] = {0xD9, 0xE8, 0xC3}; 48 | patch_data(pid, 0x47F0D0, data1, sizeof(data1)); 49 | 50 | // Compass show enemies 51 | constexpr uint8_t data2[] = {0xEB, 0x13}; 52 | patch_data(pid, 0x4437A8, data2, sizeof(data2)); 53 | 54 | // Enemy arrows 55 | constexpr uint8_t data3[] = {0xEB}; 56 | patch_data(pid, 0x443A2A, data3, sizeof(data3)); 57 | patch_data(pid, 0x443978, data3, sizeof(data3)); 58 | } 59 | 60 | void try_patch_iw5() 61 | { 62 | const auto pid = get_process_id_from_window("IW5", nullptr); 63 | if (pid) 64 | { 65 | printf("Patching IW5...\n"); 66 | patch_iw5(*pid); 67 | } 68 | } 69 | 70 | void patch_t6(const uint32_t pid) 71 | { 72 | // Force calling SatellitePingEnemyPlayer 73 | insert_nop(pid, 0x7993B1, 2); 74 | insert_nop(pid, 0x7993C1, 2); 75 | 76 | // Better vsat updates 77 | insert_nop(pid, 0x41D06C, 2); // No time check 78 | insert_nop(pid, 0x41D092, 2); // No perk check 79 | insert_nop(pid, 0x41D0BB, 2); // No fadeout 80 | 81 | // Enable chopper boxes 82 | insert_nop(pid, 0x7B539C, 6); // ShouldDrawPlayerTargetHighlights 83 | insert_nop(pid, 0x7B53AE, 6); // Enable chopper boxes 84 | insert_nop(pid, 0x7B5461, 6); // Ignore player not visible 85 | insert_nop(pid, 0x7B5471, 6); // Ignore blind-eye perks 86 | } 87 | 88 | void try_patch_t6() 89 | { 90 | auto pid = get_process_id_from_window(nullptr, "Call of Duty" "\xAE" ": Black Ops II - Multiplayer"); 91 | if (!pid) 92 | { 93 | pid = get_process_id_from_window("CoDBlackOps", nullptr); 94 | } 95 | 96 | if (pid) 97 | { 98 | printf("Patching T6...\n"); 99 | patch_t6(*pid); 100 | } 101 | } 102 | 103 | 104 | int safe_main(const int /*argc*/, char* /*argv*/[]) 105 | { 106 | if (hyperhook_initialize() == 0) 107 | { 108 | throw std::runtime_error("Failed to initialize HyperHook"); 109 | } 110 | 111 | while (true) 112 | { 113 | try_patch_iw5(); 114 | try_patch_t6(); 115 | 116 | printf("Press any key to exit!\n"); 117 | if (_getch() != 'r') 118 | { 119 | break; 120 | } 121 | } 122 | 123 | return 0; 124 | } 125 | 126 | int main(const int argc, char* argv[]) 127 | { 128 | try 129 | { 130 | return safe_main(argc, argv); 131 | } 132 | catch (std::exception& e) 133 | { 134 | printf("Error: %s\n", e.what()); 135 | _getch(); 136 | return 1; 137 | } 138 | catch (...) 139 | { 140 | printf("An unknown error occured!\n"); 141 | _getch(); 142 | return 1; 143 | } 144 | } 145 | 146 | int __stdcall WinMain(HINSTANCE, HINSTANCE, char*, int) 147 | { 148 | AllocConsole(); 149 | AttachConsole(GetCurrentProcessId()); 150 | 151 | FILE* fp; 152 | freopen_s(&fp, "conin$", "r", stdin); 153 | freopen_s(&fp, "conout$", "w", stdout); 154 | freopen_s(&fp, "conout$", "w", stderr); 155 | 156 | return main(__argc, __argv); 157 | } 158 | -------------------------------------------------------------------------------- /src/driver/ept.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define DECLSPEC_PAGE_ALIGN DECLSPEC_ALIGN(PAGE_SIZE) 4 | #include "list.hpp" 5 | 6 | 7 | #define MTRR_PAGE_SIZE 4096 8 | #define MTRR_PAGE_MASK (~(MTRR_PAGE_SIZE-1)) 9 | 10 | #define ADDRMASK_EPT_PML1_OFFSET(_VAR_) ((_VAR_) & 0xFFFULL) 11 | 12 | #define ADDRMASK_EPT_PML1_INDEX(_VAR_) (((_VAR_) & 0x1FF000ULL) >> 12) 13 | #define ADDRMASK_EPT_PML2_INDEX(_VAR_) (((_VAR_) & 0x3FE00000ULL) >> 21) 14 | #define ADDRMASK_EPT_PML3_INDEX(_VAR_) (((_VAR_) & 0x7FC0000000ULL) >> 30) 15 | #define ADDRMASK_EPT_PML4_INDEX(_VAR_) (((_VAR_) & 0xFF8000000000ULL) >> 39) 16 | 17 | 18 | namespace vmx 19 | { 20 | using pml4 = ept_pml4e; 21 | using pml3 = ept_pdpte; 22 | using pml2 = ept_pde_2mb; 23 | using pml2_ptr = ept_pde; 24 | using pml1 = ept_pte; 25 | 26 | using pml4_entry = pml4e_64; 27 | using pml3_entry = pdpte_64; 28 | using pml2_entry = pde_64; 29 | using pml1_entry = pte_64; 30 | 31 | struct ept_split 32 | { 33 | DECLSPEC_PAGE_ALIGN pml1 pml1[EPT_PTE_ENTRY_COUNT]{}; 34 | 35 | union 36 | { 37 | pml2 entry{}; 38 | pml2_ptr pointer; 39 | }; 40 | }; 41 | 42 | struct ept_code_watch_point 43 | { 44 | uint64_t physical_base_address{}; 45 | pml1* target_page{}; 46 | process_id source_pid{0}; 47 | process_id target_pid{0}; 48 | }; 49 | 50 | struct ept_hook 51 | { 52 | ept_hook(uint64_t physical_base); 53 | ~ept_hook(); 54 | 55 | DECLSPEC_PAGE_ALIGN uint8_t fake_page[PAGE_SIZE]{}; 56 | DECLSPEC_PAGE_ALIGN uint8_t diff_page[PAGE_SIZE]{}; 57 | 58 | uint64_t physical_base_address{}; 59 | void* mapped_virtual_address{}; 60 | 61 | pml1* target_page{}; 62 | pml1 original_entry{}; 63 | pml1 execute_entry{}; 64 | pml1 readwrite_entry{}; 65 | 66 | process_id source_pid{0}; 67 | process_id target_pid{0}; 68 | }; 69 | 70 | struct ept_translation_hint 71 | { 72 | DECLSPEC_PAGE_ALIGN uint8_t page[PAGE_SIZE]{}; 73 | 74 | uint64_t physical_base_address{}; 75 | const void* virtual_base_address{}; 76 | }; 77 | 78 | struct guest_context; 79 | 80 | class ept 81 | { 82 | public: 83 | ept(); 84 | ~ept(); 85 | 86 | ept(ept&&) = delete; 87 | ept(const ept&) = delete; 88 | ept& operator=(ept&&) = delete; 89 | ept& operator=(const ept&) = delete; 90 | 91 | void initialize(); 92 | 93 | void install_code_watch_point(uint64_t physical_page, process_id source_pid, process_id target_pid); 94 | 95 | void install_hook(const void* destination, const void* source, size_t length, process_id source_pid, 96 | process_id target_pid, 97 | const utils::list& hints = {}); 98 | void disable_all_hooks(); 99 | 100 | void handle_violation(guest_context& guest_context); 101 | void handle_misconfiguration(guest_context& guest_context) const; 102 | 103 | ept_pointer get_ept_pointer() const; 104 | void invalidate() const; 105 | 106 | static utils::list generate_translation_hints(const void* destination, size_t length); 107 | 108 | uint64_t* get_access_records(size_t* count); 109 | 110 | bool cleanup_process(process_id process); 111 | 112 | private: 113 | DECLSPEC_PAGE_ALIGN pml4 epml4[EPT_PML4E_ENTRY_COUNT]; 114 | DECLSPEC_PAGE_ALIGN pml3 epdpt[EPT_PDPTE_ENTRY_COUNT]; 115 | DECLSPEC_PAGE_ALIGN pml2 epde[EPT_PDPTE_ENTRY_COUNT][EPT_PDE_ENTRY_COUNT]; 116 | 117 | uint64_t access_records[1024]; 118 | 119 | utils::list ept_splits{}; 120 | utils::list ept_hooks{}; 121 | utils::list ept_code_watch_points{}; 122 | 123 | pml2* get_pml2_entry(uint64_t physical_address); 124 | pml1* get_pml1_entry(uint64_t physical_address); 125 | pml1* find_pml1_table(uint64_t physical_address); 126 | 127 | ept_split& allocate_ept_split(); 128 | ept_hook& allocate_ept_hook(uint64_t physical_address); 129 | ept_hook* find_ept_hook(uint64_t physical_address); 130 | 131 | ept_code_watch_point& allocate_ept_code_watch_point(); 132 | ept_code_watch_point* find_ept_code_watch_point(uint64_t physical_address); 133 | 134 | ept_hook* get_or_create_ept_hook(void* destination, const ept_translation_hint* translation_hint = nullptr); 135 | 136 | void split_large_page(uint64_t physical_address); 137 | 138 | void install_page_hook(void* destination, const void* source, size_t length, process_id source_pid, 139 | process_id target_pid, const ept_translation_hint* translation_hint = nullptr); 140 | 141 | void record_access(uint64_t rip); 142 | }; 143 | } 144 | -------------------------------------------------------------------------------- /src/driver/thread.cpp: -------------------------------------------------------------------------------- 1 | #include "thread.hpp" 2 | #include "std_include.hpp" 3 | #include "logging.hpp" 4 | #include "exception.hpp" 5 | #include "finally.hpp" 6 | 7 | namespace thread 8 | { 9 | namespace 10 | { 11 | struct dispatch_data 12 | { 13 | void (*callback)(void*){}; 14 | void* data{}; 15 | }; 16 | 17 | void dispatch_callback(const void* context) 18 | { 19 | try 20 | { 21 | const auto* const data = static_cast(context); 22 | data->callback(data->data); 23 | } 24 | catch (std::exception& e) 25 | { 26 | debug_log("Exception during dpc on core %d: %s\n", get_processor_index(), e.what()); 27 | } 28 | catch (...) 29 | { 30 | debug_log("Unknown exception during dpc on core %d\n", get_processor_index()); 31 | } 32 | } 33 | 34 | 35 | _Function_class_(KDEFERRED_ROUTINE) 36 | 37 | void NTAPI callback_dispatcher(struct _KDPC* /*Dpc*/, 38 | const PVOID param, 39 | const PVOID arg1, 40 | const PVOID arg2) 41 | { 42 | dispatch_callback(param); 43 | 44 | KeSignalCallDpcSynchronize(arg2); 45 | KeSignalCallDpcDone(arg1); 46 | } 47 | 48 | _Function_class_(KDEFERRED_ROUTINE) 49 | 50 | void NTAPI sequential_callback_dispatcher(struct _KDPC* /*Dpc*/, 51 | const PVOID param, 52 | const PVOID arg1, 53 | const PVOID arg2) 54 | { 55 | const auto cpu_count = get_processor_count(); 56 | const auto current_cpu = get_processor_index(); 57 | 58 | for (auto i = 0u; i < cpu_count; ++i) 59 | { 60 | if (i == current_cpu) 61 | { 62 | dispatch_callback(param); 63 | } 64 | 65 | KeSignalCallDpcSynchronize(arg2); 66 | } 67 | 68 | KeSignalCallDpcDone(arg1); 69 | } 70 | 71 | void thread_starter(void* context) 72 | { 73 | auto* function_ptr = static_cast*>(context); 74 | const auto function = std::move(*function_ptr); 75 | delete function_ptr; 76 | 77 | try 78 | { 79 | function(); 80 | } 81 | catch (std::exception& e) 82 | { 83 | debug_log("Kernel thread threw an exception: %s\n", e.what()); 84 | } 85 | catch (...) 86 | { 87 | debug_log("Kernel thread threw an unknown exception\n"); 88 | } 89 | } 90 | } 91 | 92 | kernel_thread::kernel_thread(std::function&& callback) 93 | { 94 | auto* function_object = new std::function(std::move(callback)); 95 | 96 | auto destructor = utils::finally([&function_object]() 97 | { 98 | delete function_object; 99 | }); 100 | 101 | 102 | HANDLE handle{}; 103 | const auto status = PsCreateSystemThread(&handle, 0, nullptr, nullptr, nullptr, thread_starter, 104 | function_object); 105 | 106 | if (status != STATUS_SUCCESS) 107 | { 108 | throw std::runtime_error("Failed to create thread!"); 109 | } 110 | 111 | ObReferenceObjectByHandle(handle, THREAD_ALL_ACCESS, nullptr, KernelMode, 112 | reinterpret_cast(&this->handle_), nullptr); 113 | 114 | ZwClose(handle); 115 | 116 | destructor.cancel(); 117 | } 118 | 119 | kernel_thread::~kernel_thread() 120 | { 121 | this->join(); 122 | } 123 | 124 | kernel_thread::kernel_thread(kernel_thread&& obj) noexcept 125 | { 126 | this->operator=(std::move(obj)); 127 | } 128 | 129 | kernel_thread& kernel_thread::operator=(kernel_thread&& obj) noexcept 130 | { 131 | if (this != &obj) 132 | { 133 | this->join(); 134 | this->handle_ = obj.handle_; 135 | obj.handle_ = nullptr; 136 | } 137 | 138 | return *this; 139 | } 140 | 141 | bool kernel_thread::joinable() const 142 | { 143 | return this->handle_ != nullptr; 144 | } 145 | 146 | void kernel_thread::join() 147 | { 148 | if (this->joinable()) 149 | { 150 | KeWaitForSingleObject(this->handle_, Executive, KernelMode, FALSE, nullptr); 151 | this->detach(); 152 | } 153 | } 154 | 155 | void kernel_thread::detach() 156 | { 157 | if (this->joinable()) 158 | { 159 | ObDereferenceObject(this->handle_); 160 | this->handle_ = nullptr; 161 | } 162 | } 163 | 164 | uint32_t get_processor_count() 165 | { 166 | return static_cast(KeQueryActiveProcessorCountEx(0)); 167 | } 168 | 169 | uint32_t get_processor_index() 170 | { 171 | return static_cast(KeGetCurrentProcessorNumberEx(nullptr)); 172 | } 173 | 174 | bool sleep(const uint32_t milliseconds) 175 | { 176 | LARGE_INTEGER interval{}; 177 | interval.QuadPart = -(10000ll * milliseconds); 178 | 179 | return STATUS_SUCCESS == KeDelayExecutionThread(KernelMode, FALSE, &interval); 180 | } 181 | 182 | void dispatch_on_all_cores(void (*callback)(void*), void* data, const bool sequential) 183 | { 184 | dispatch_data callback_data{}; 185 | callback_data.callback = callback; 186 | callback_data.data = data; 187 | 188 | KeGenericCallDpc(sequential ? sequential_callback_dispatcher : callback_dispatcher, &callback_data); 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/driver/memory.cpp: -------------------------------------------------------------------------------- 1 | #include "std_include.hpp" 2 | #include "memory.hpp" 3 | #include "string.hpp" 4 | 5 | namespace memory 6 | { 7 | namespace 8 | { 9 | using mm_allocate_contiguous_node_memory = decltype(MmAllocateContiguousNodeMemory)*; 10 | 11 | mm_allocate_contiguous_node_memory get_mm_allocate_contiguous_node_memory() 12 | { 13 | static bool fetched{false}; 14 | static mm_allocate_contiguous_node_memory address{nullptr}; 15 | 16 | if (!fetched) 17 | { 18 | fetched = true; 19 | auto function_name = string::get_unicode_string(L"MmAllocateContiguousNodeMemory"); 20 | address = static_cast(MmGetSystemRoutineAddress(&function_name)); 21 | } 22 | 23 | return address; 24 | } 25 | 26 | void* allocate_aligned_memory_internal(const size_t size) 27 | { 28 | PHYSICAL_ADDRESS lowest{}, highest{}; 29 | lowest.QuadPart = 0; 30 | highest.QuadPart = lowest.QuadPart - 1; 31 | 32 | const auto allocate_node_mem = get_mm_allocate_contiguous_node_memory(); 33 | if (allocate_node_mem) 34 | { 35 | return allocate_node_mem(size, 36 | lowest, 37 | highest, 38 | lowest, 39 | PAGE_READWRITE, 40 | KeGetCurrentNodeNumber()); 41 | } 42 | 43 | return MmAllocateContiguousMemory(size, highest); 44 | } 45 | } 46 | 47 | _IRQL_requires_max_(DISPATCH_LEVEL) 48 | 49 | void free_aligned_memory(void* memory) 50 | { 51 | if (memory) 52 | { 53 | MmFreeContiguousMemory(memory); 54 | } 55 | } 56 | 57 | _Must_inspect_result_ 58 | _IRQL_requires_max_(DISPATCH_LEVEL) 59 | 60 | void* allocate_aligned_memory(const size_t size) 61 | { 62 | void* memory = allocate_aligned_memory_internal(size); 63 | if (memory) 64 | { 65 | RtlSecureZeroMemory(memory, size); 66 | } 67 | 68 | return memory; 69 | } 70 | 71 | _IRQL_requires_max_(APC_LEVEL) 72 | 73 | bool read_physical_memory(void* destination, uint64_t physical_address, const size_t size) 74 | { 75 | size_t bytes_read{}; 76 | MM_COPY_ADDRESS source{}; 77 | source.PhysicalAddress.QuadPart = static_cast(physical_address); 78 | 79 | return MmCopyMemory(destination, source, size, MM_COPY_MEMORY_PHYSICAL, &bytes_read) == STATUS_SUCCESS && 80 | bytes_read == size; 81 | } 82 | 83 | uint64_t get_physical_address(void* address) 84 | { 85 | return static_cast(MmGetPhysicalAddress(address).QuadPart); 86 | } 87 | 88 | void* get_virtual_address(const uint64_t address) 89 | { 90 | PHYSICAL_ADDRESS physical_address{}; 91 | physical_address.QuadPart = static_cast(address); 92 | return MmGetVirtualForPhysical(physical_address); 93 | } 94 | 95 | _Must_inspect_result_ 96 | _IRQL_requires_max_(DISPATCH_LEVEL) 97 | 98 | void* map_physical_memory(const uint64_t address, const size_t size) 99 | { 100 | PHYSICAL_ADDRESS physical_address{}; 101 | physical_address.QuadPart = static_cast(address); 102 | return MmMapIoSpace(physical_address, size, MmNonCached); 103 | } 104 | 105 | _IRQL_requires_max_(DISPATCH_LEVEL) 106 | 107 | void unmap_physical_memory(void* address, const size_t size) 108 | { 109 | MmUnmapIoSpace(address, size); 110 | } 111 | 112 | _Must_inspect_result_ 113 | _IRQL_requires_max_(DISPATCH_LEVEL) 114 | 115 | void* allocate_non_paged_memory(const size_t size) 116 | { 117 | #pragma warning(push) 118 | #pragma warning(disable: 4996) 119 | void* memory = ExAllocatePoolWithTag(NonPagedPool, size, 'MOMO'); 120 | #pragma warning(pop) 121 | if (memory) 122 | { 123 | RtlSecureZeroMemory(memory, size); 124 | } 125 | 126 | return memory; 127 | } 128 | 129 | _IRQL_requires_max_(DISPATCH_LEVEL) 130 | 131 | void free_non_paged_memory(void* memory) 132 | { 133 | if (memory) 134 | { 135 | ExFreePool(memory); 136 | } 137 | } 138 | 139 | bool probe_for_read(const void* address, const size_t length, const uint64_t alignment) 140 | { 141 | __try 142 | { 143 | ProbeForRead(const_cast(address), length, static_cast(alignment)); 144 | return true; 145 | } 146 | __except (EXCEPTION_EXECUTE_HANDLER) 147 | { 148 | return false; 149 | } 150 | } 151 | 152 | void assert_readability(const void* address, const size_t length, const uint64_t alignment) 153 | { 154 | if (!probe_for_read(address, length, alignment)) 155 | { 156 | throw std::runtime_error("Access violation"); 157 | } 158 | } 159 | 160 | bool probe_for_write(const void* address, const size_t length, const uint64_t alignment) 161 | { 162 | __try 163 | { 164 | ProbeForWrite(const_cast(address), length, static_cast(alignment)); 165 | return true; 166 | } 167 | __except (EXCEPTION_EXECUTE_HANDLER) 168 | { 169 | return false; 170 | } 171 | } 172 | 173 | void assert_writability(const void* address, const size_t length, const uint64_t alignment) 174 | { 175 | if (!probe_for_write(address, length, alignment)) 176 | { 177 | throw std::runtime_error("Access violation"); 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/driver/vector.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "allocator.hpp" 3 | #include "exception.hpp" 4 | #include "finally.hpp" 5 | 6 | namespace utils 7 | { 8 | template 9 | requires is_allocator 10 | class vector 11 | { 12 | public: 13 | using type = T; 14 | 15 | vector() = default; 16 | 17 | ~vector() 18 | { 19 | this->clear(); 20 | } 21 | 22 | vector(const vector& obj) 23 | : vector() 24 | { 25 | this->operator=(obj); 26 | } 27 | 28 | vector(vector&& obj) noexcept 29 | : vector() 30 | { 31 | this->operator=(std::move(obj)); 32 | } 33 | 34 | vector& operator=(const vector& obj) 35 | { 36 | if (this != &obj) 37 | { 38 | this->clear(); 39 | this->reserve(obj.size_); 40 | 41 | for (const auto& i : obj) 42 | { 43 | this->push_back(i); 44 | } 45 | } 46 | 47 | return *this; 48 | } 49 | 50 | vector& operator=(vector&& obj) noexcept 51 | { 52 | if (this != &obj) 53 | { 54 | this->clear(); 55 | 56 | this->storage_ = obj.storage_; 57 | this->capacity_ = obj.capacity_; 58 | this->size_ = obj.size_; 59 | 60 | obj.storage_ = nullptr; 61 | obj.capacity_ = 0; 62 | obj.size_ = 0; 63 | } 64 | 65 | return *this; 66 | } 67 | 68 | void reserve(size_t capacity) 69 | { 70 | if (this->capacity_ >= capacity) 71 | { 72 | return; 73 | } 74 | 75 | auto* old_mem = this->storage_; 76 | auto* old_data = this->data(); 77 | 78 | this->storage_ = this->allocate_memory_for_capacity(capacity); 79 | this->capacity_ = capacity; 80 | 81 | auto _ = utils::finally([&old_mem, this] 82 | { 83 | this->free_memory(old_mem); 84 | }); 85 | 86 | auto* data = this->data(); 87 | for (size_t i = 0; i < this->size_; ++i) 88 | { 89 | new(data + i) T(std::move(old_data[i])); 90 | old_data[i].~T(); 91 | } 92 | } 93 | 94 | T& push_back(const T& obj) 95 | { 96 | auto& entry = this->add_uninitialized_entry(); 97 | 98 | new(&entry) T(obj); 99 | 100 | return entry; 101 | } 102 | 103 | T& push_back(T&& obj) 104 | { 105 | auto& entry = this->add_uninitialized_entry(); 106 | 107 | new(&entry) T(std::move(obj)); 108 | 109 | return entry; 110 | } 111 | 112 | template 113 | T& emplace_back(Args&&... args) 114 | { 115 | auto& entry = this->add_uninitialized_entry(); 116 | 117 | new(&entry) T(std::forward(args)...); 118 | 119 | return entry; 120 | } 121 | 122 | T& operator[](const size_t index) 123 | { 124 | return this->at(index); 125 | } 126 | 127 | const T& operator[](const size_t index) const 128 | { 129 | return this->at(index); 130 | } 131 | 132 | T& at(const size_t index) 133 | { 134 | if (index >= this->size_) 135 | { 136 | throw std::runtime_error("Out of bounds access"); 137 | } 138 | 139 | return this->data()[index]; 140 | } 141 | 142 | const T& at(const size_t index) const 143 | { 144 | if (index >= this->size_) 145 | { 146 | throw std::runtime_error("Out of bounds access"); 147 | } 148 | 149 | return this->data()[index]; 150 | } 151 | 152 | void clear() 153 | { 154 | auto* data = this->data(); 155 | for (size_t i = 0; i < this->size_; ++i) 156 | { 157 | data[i].~T(); 158 | } 159 | 160 | free_memory(this->storage_); 161 | this->storage_ = nullptr; 162 | this->capacity_ = 0; 163 | this->size_ = 0; 164 | } 165 | 166 | [[nodiscard]] size_t capacity() const 167 | { 168 | return this->capacity_; 169 | } 170 | 171 | [[nodiscard]] size_t size() const 172 | { 173 | return this->size_; 174 | } 175 | 176 | T* data() 177 | { 178 | if (!this->storage_) 179 | { 180 | return nullptr; 181 | } 182 | 183 | return static_cast(align_pointer(this->storage_)); 184 | } 185 | 186 | const T* data() const 187 | { 188 | if (!this->storage_) 189 | { 190 | return nullptr; 191 | } 192 | 193 | return static_cast(align_pointer(this->storage_)); 194 | } 195 | 196 | T* begin() 197 | { 198 | return this->data(); 199 | } 200 | 201 | const T* begin() const 202 | { 203 | return this->data(); 204 | } 205 | 206 | T* end() 207 | { 208 | return this->data() + this->size_; 209 | } 210 | 211 | const T* end() const 212 | { 213 | return this->data() + this->size_; 214 | } 215 | 216 | T* erase(T* iterator) 217 | { 218 | auto index = iterator - this->begin(); 219 | if (index < 0 || static_cast(index) > this->size_) 220 | { 221 | throw std::runtime_error("Bad iterator"); 222 | } 223 | 224 | const auto data = this->data(); 225 | for (size_t i = index + 1; i < this->size_; ++i) 226 | { 227 | data[i - 1] = std::move(data[i]); 228 | } 229 | 230 | data[this->size_--].~T(); 231 | 232 | return iterator; 233 | } 234 | 235 | bool empty() const 236 | { 237 | return this->size_ == 0; 238 | } 239 | 240 | private: 241 | Allocator allocator_{}; 242 | void* storage_{nullptr}; 243 | size_t capacity_{0}; 244 | size_t size_{0}; 245 | 246 | T& add_uninitialized_entry() 247 | { 248 | if (this->size_ + 1 > this->capacity_) 249 | { 250 | this->reserve(max(this->capacity_, 5) * 2); 251 | } 252 | 253 | auto* data = this->data() + this->size_++; 254 | return *data; 255 | } 256 | 257 | void* allocate_memory_for_capacity(const size_t capacity) 258 | { 259 | constexpr auto alignment = alignof(T); 260 | auto* memory = this->allocator_.allocate(capacity * sizeof(T) + alignment); 261 | if (!memory) 262 | { 263 | throw std::runtime_error("Failed to allocate memory"); 264 | } 265 | 266 | return memory; 267 | } 268 | 269 | void free_memory(void* memory) 270 | { 271 | this->allocator_.free(memory); 272 | } 273 | 274 | template 275 | static U* align_pointer(U* pointer) 276 | { 277 | const auto align_bits = alignof(T) - 1; 278 | auto ptr = reinterpret_cast(pointer); 279 | ptr = (ptr + align_bits) & (~align_bits); 280 | 281 | return reinterpret_cast(ptr); 282 | } 283 | }; 284 | } 285 | -------------------------------------------------------------------------------- /src/library/utils/nt.cpp: -------------------------------------------------------------------------------- 1 | #include "nt.hpp" 2 | 3 | namespace utils::nt 4 | { 5 | library library::load(const std::string& name) 6 | { 7 | return library(LoadLibraryA(name.data())); 8 | } 9 | 10 | library library::load(const std::filesystem::path& path) 11 | { 12 | return library::load(path.generic_string()); 13 | } 14 | 15 | library library::get_by_address(void* address) 16 | { 17 | HMODULE handle = nullptr; 18 | GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, static_cast(address), &handle); 19 | return library(handle); 20 | } 21 | 22 | library::library() 23 | { 24 | this->module_ = GetModuleHandleA(nullptr); 25 | } 26 | 27 | library::library(const std::string& name) 28 | { 29 | this->module_ = GetModuleHandleA(name.data()); 30 | } 31 | 32 | library::library(const HMODULE handle) 33 | { 34 | this->module_ = handle; 35 | } 36 | 37 | bool library::operator==(const library& obj) const 38 | { 39 | return this->module_ == obj.module_; 40 | } 41 | 42 | library::operator bool() const 43 | { 44 | return this->is_valid(); 45 | } 46 | 47 | library::operator HMODULE() const 48 | { 49 | return this->get_handle(); 50 | } 51 | 52 | PIMAGE_NT_HEADERS library::get_nt_headers() const 53 | { 54 | if (!this->is_valid()) return nullptr; 55 | return reinterpret_cast(this->get_ptr() + this->get_dos_header()->e_lfanew); 56 | } 57 | 58 | PIMAGE_DOS_HEADER library::get_dos_header() const 59 | { 60 | return reinterpret_cast(this->get_ptr()); 61 | } 62 | 63 | PIMAGE_OPTIONAL_HEADER library::get_optional_header() const 64 | { 65 | if (!this->is_valid()) return nullptr; 66 | return &this->get_nt_headers()->OptionalHeader; 67 | } 68 | 69 | std::vector library::get_section_headers() const 70 | { 71 | std::vector headers; 72 | 73 | auto nt_headers = this->get_nt_headers(); 74 | auto section = IMAGE_FIRST_SECTION(nt_headers); 75 | 76 | for (uint16_t i = 0; i < nt_headers->FileHeader.NumberOfSections; ++i, ++section) 77 | { 78 | if (section) headers.push_back(section); 79 | else OutputDebugStringA("There was an invalid section :O"); 80 | } 81 | 82 | return headers; 83 | } 84 | 85 | std::uint8_t* library::get_ptr() const 86 | { 87 | return reinterpret_cast(this->module_); 88 | } 89 | 90 | void library::unprotect() const 91 | { 92 | if (!this->is_valid()) return; 93 | 94 | DWORD protection; 95 | VirtualProtect(this->get_ptr(), this->get_optional_header()->SizeOfImage, PAGE_EXECUTE_READWRITE, 96 | &protection); 97 | } 98 | 99 | size_t library::get_relative_entry_point() const 100 | { 101 | if (!this->is_valid()) return 0; 102 | return this->get_nt_headers()->OptionalHeader.AddressOfEntryPoint; 103 | } 104 | 105 | void* library::get_entry_point() const 106 | { 107 | if (!this->is_valid()) return nullptr; 108 | return this->get_ptr() + this->get_relative_entry_point(); 109 | } 110 | 111 | bool library::is_valid() const 112 | { 113 | return this->module_ != nullptr && this->get_dos_header()->e_magic == IMAGE_DOS_SIGNATURE; 114 | } 115 | 116 | std::string library::get_name() const 117 | { 118 | if (!this->is_valid()) return ""; 119 | 120 | auto path = this->get_path(); 121 | const auto pos = path.find_last_of("/\\"); 122 | if (pos == std::string::npos) return path; 123 | 124 | return path.substr(pos + 1); 125 | } 126 | 127 | std::string library::get_path() const 128 | { 129 | if (!this->is_valid()) return ""; 130 | 131 | char name[MAX_PATH] = {0}; 132 | GetModuleFileNameA(this->module_, name, sizeof name); 133 | 134 | return name; 135 | } 136 | 137 | std::string library::get_folder() const 138 | { 139 | if (!this->is_valid()) return ""; 140 | 141 | const auto path = std::filesystem::path(this->get_path()); 142 | return path.parent_path().generic_string(); 143 | } 144 | 145 | void library::free() 146 | { 147 | if (this->is_valid()) 148 | { 149 | FreeLibrary(this->module_); 150 | this->module_ = nullptr; 151 | } 152 | } 153 | 154 | HMODULE library::get_handle() const 155 | { 156 | return this->module_; 157 | } 158 | 159 | void** library::get_iat_entry(const std::string& module_name, const std::string& proc_name) const 160 | { 161 | if (!this->is_valid()) return nullptr; 162 | 163 | const library other_module(module_name); 164 | if (!other_module.is_valid()) return nullptr; 165 | 166 | auto* const target_function = other_module.get_proc(proc_name); 167 | if (!target_function) return nullptr; 168 | 169 | auto* header = this->get_optional_header(); 170 | if (!header) return nullptr; 171 | 172 | auto* import_descriptor = reinterpret_cast(this->get_ptr() + header->DataDirectory 173 | [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); 174 | 175 | while (import_descriptor->Name) 176 | { 177 | if (!_stricmp(reinterpret_cast(this->get_ptr() + import_descriptor->Name), module_name.data())) 178 | { 179 | auto* original_thunk_data = reinterpret_cast(import_descriptor-> 180 | OriginalFirstThunk + this->get_ptr()); 181 | auto* thunk_data = reinterpret_cast(import_descriptor->FirstThunk + this-> 182 | get_ptr()); 183 | 184 | while (original_thunk_data->u1.AddressOfData) 185 | { 186 | const size_t ordinal_number = original_thunk_data->u1.AddressOfData & 0xFFFFFFF; 187 | 188 | if (ordinal_number > 0xFFFF) continue; 189 | 190 | if (GetProcAddress(other_module.module_, reinterpret_cast(ordinal_number)) == 191 | target_function) 192 | { 193 | return reinterpret_cast(&thunk_data->u1.Function); 194 | } 195 | 196 | ++original_thunk_data; 197 | ++thunk_data; 198 | } 199 | 200 | //break; 201 | } 202 | 203 | ++import_descriptor; 204 | } 205 | 206 | return nullptr; 207 | } 208 | 209 | void raise_hard_exception() 210 | { 211 | int data = false; 212 | const library ntdll("ntdll.dll"); 213 | ntdll.invoke_pascal("RtlAdjustPrivilege", 19, true, false, &data); 214 | ntdll.invoke_pascal("NtRaiseHardError", 0xC000007B, 0, nullptr, nullptr, 6, &data); 215 | } 216 | 217 | std::string load_resource(const int id) 218 | { 219 | auto* const res = FindResource(library(), MAKEINTRESOURCE(id), RT_RCDATA); 220 | if (!res) return {}; 221 | 222 | auto* const handle = LoadResource(nullptr, res); 223 | if (!handle) return {}; 224 | 225 | return std::string(LPSTR(LockResource(handle)), SizeofResource(nullptr, res)); 226 | } 227 | 228 | void relaunch_self() 229 | { 230 | const utils::nt::library self; 231 | 232 | STARTUPINFOA startup_info; 233 | PROCESS_INFORMATION process_info; 234 | 235 | ZeroMemory(&startup_info, sizeof(startup_info)); 236 | ZeroMemory(&process_info, sizeof(process_info)); 237 | startup_info.cb = sizeof(startup_info); 238 | 239 | char current_dir[MAX_PATH]; 240 | GetCurrentDirectoryA(sizeof(current_dir), current_dir); 241 | auto* const command_line = GetCommandLineA(); 242 | 243 | CreateProcessA(self.get_path().data(), command_line, nullptr, nullptr, false, NULL, nullptr, current_dir, 244 | &startup_info, &process_info); 245 | 246 | if (process_info.hThread && process_info.hThread != INVALID_HANDLE_VALUE) CloseHandle(process_info.hThread); 247 | if (process_info.hProcess && process_info.hProcess != INVALID_HANDLE_VALUE) CloseHandle(process_info.hProcess); 248 | } 249 | 250 | void terminate(const uint32_t code) 251 | { 252 | TerminateProcess(GetCurrentProcess(), code); 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /src/driver/list.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "allocator.hpp" 3 | #include "exception.hpp" 4 | #include "finally.hpp" 5 | 6 | namespace utils 7 | { 8 | template 9 | requires is_allocator && is_allocator 10 | class list 11 | { 12 | struct list_entry 13 | { 14 | T* entry{nullptr}; 15 | list_entry* next{nullptr}; 16 | 17 | void* this_base{nullptr}; 18 | void* entry_base{nullptr}; 19 | }; 20 | 21 | public: 22 | using type = T; 23 | 24 | class iterator 25 | { 26 | friend list; 27 | 28 | public: 29 | iterator(list_entry* entry = nullptr) 30 | : entry_(entry) 31 | { 32 | } 33 | 34 | T& operator*() const 35 | { 36 | return *this->entry_->entry; 37 | } 38 | 39 | T* operator->() const 40 | { 41 | return this->entry_->entry; 42 | } 43 | 44 | bool operator==(const iterator& i) const 45 | { 46 | return this->entry_ == i.entry_; 47 | } 48 | 49 | iterator operator++() 50 | { 51 | this->entry_ = this->entry_->next; 52 | return *this; 53 | } 54 | 55 | iterator operator+(const size_t num) const 56 | { 57 | auto entry = this->entry_; 58 | 59 | for (size_t i = 0; i < num; ++i) 60 | { 61 | if (!entry) 62 | { 63 | return {}; 64 | } 65 | 66 | entry = entry->next; 67 | } 68 | 69 | 70 | return {entry}; 71 | } 72 | 73 | private: 74 | list_entry* entry_{nullptr}; 75 | 76 | list_entry* get_entry() const 77 | { 78 | return entry_; 79 | } 80 | }; 81 | 82 | class const_iterator 83 | { 84 | friend list; 85 | 86 | public: 87 | const_iterator(list_entry* entry = nullptr) 88 | : entry_(entry) 89 | { 90 | } 91 | 92 | const T& operator*() const 93 | { 94 | return *this->entry_->entry; 95 | } 96 | 97 | const T* operator->() const 98 | { 99 | return this->entry_->entry; 100 | } 101 | 102 | bool operator==(const const_iterator& i) const 103 | { 104 | return this->entry_ == i.entry_; 105 | } 106 | 107 | const_iterator operator++() 108 | { 109 | this->entry_ = this->entry_->next; 110 | return *this; 111 | } 112 | 113 | private: 114 | list_entry* entry_{nullptr}; 115 | 116 | list_entry* get_entry() const 117 | { 118 | return entry_; 119 | } 120 | }; 121 | 122 | list() = default; 123 | 124 | ~list() 125 | { 126 | this->clear(); 127 | } 128 | 129 | list(const list& obj) 130 | : list() 131 | { 132 | this->operator=(obj); 133 | } 134 | 135 | list(list&& obj) noexcept 136 | : list() 137 | { 138 | this->operator=(std::move(obj)); 139 | } 140 | 141 | list& operator=(const list& obj) 142 | { 143 | if (this != &obj) 144 | { 145 | this->clear(); 146 | 147 | for (const auto& i : obj) 148 | { 149 | this->push_back(i); 150 | } 151 | } 152 | 153 | return *this; 154 | } 155 | 156 | list& operator=(list&& obj) noexcept 157 | { 158 | if (this != &obj) 159 | { 160 | this->clear(); 161 | 162 | this->entries_ = obj.entries_; 163 | obj.entries_ = nullptr; 164 | } 165 | 166 | return *this; 167 | } 168 | 169 | T& push_back(const T& obj) 170 | { 171 | auto& entry = this->add_uninitialized_entry_back(); 172 | new(&entry) T(obj); 173 | return entry; 174 | } 175 | 176 | T& push_back(T&& obj) 177 | { 178 | auto& entry = this->add_uninitialized_entry_back(); 179 | new(&entry) T(std::move(obj)); 180 | return entry; 181 | } 182 | 183 | template 184 | T& emplace_back(Args&&... args) 185 | { 186 | auto& entry = this->add_uninitialized_entry_back(); 187 | new(&entry) T(std::forward(args)...); 188 | return entry; 189 | } 190 | 191 | T& push_front(const T& obj) 192 | { 193 | auto& entry = this->add_uninitialized_entry_front(); 194 | new(&entry) T(obj); 195 | return entry; 196 | } 197 | 198 | T& push_front(T&& obj) 199 | { 200 | auto& entry = this->add_uninitialized_entry_front(); 201 | new(&entry) T(std::move(obj)); 202 | return entry; 203 | } 204 | 205 | template 206 | T& emplace_front(Args&&... args) 207 | { 208 | auto& entry = this->add_uninitialized_entry_front(); 209 | new(&entry) T(std::forward(args)...); 210 | return entry; 211 | } 212 | 213 | T& operator[](const size_t index) 214 | { 215 | return this->at(index); 216 | } 217 | 218 | const T& operator[](const size_t index) const 219 | { 220 | return this->at(index); 221 | } 222 | 223 | T& at(const size_t index) 224 | { 225 | size_t i = 0; 226 | for (auto& obj : *this) 227 | { 228 | if (++i == index) 229 | { 230 | return obj; 231 | } 232 | } 233 | 234 | throw std::runtime_error("Out of bounds access"); 235 | } 236 | 237 | const T& at(const size_t index) const 238 | { 239 | size_t i = 0; 240 | for (const auto& obj : *this) 241 | { 242 | if (++i == index) 243 | { 244 | return obj; 245 | } 246 | } 247 | 248 | throw std::runtime_error("Out of bounds access"); 249 | } 250 | 251 | void clear() 252 | { 253 | while (!this->empty()) 254 | { 255 | this->erase(this->begin()); 256 | } 257 | } 258 | 259 | [[nodiscard]] size_t size() const 260 | { 261 | size_t i = 0; 262 | for (const auto& obj : *this) 263 | { 264 | ++i; 265 | } 266 | 267 | return i; 268 | } 269 | 270 | iterator begin() 271 | { 272 | return {this->entries_}; 273 | } 274 | 275 | const_iterator begin() const 276 | { 277 | return {this->entries_}; 278 | } 279 | 280 | iterator end() 281 | { 282 | return {}; 283 | } 284 | 285 | const_iterator end() const 286 | { 287 | return {}; 288 | } 289 | 290 | void erase(T& entry) 291 | { 292 | auto** insertion_point = &this->entries_; 293 | while (*insertion_point) 294 | { 295 | if ((*insertion_point)->entry != &entry) 296 | { 297 | insertion_point = &(*insertion_point)->next; 298 | continue; 299 | } 300 | 301 | auto* list_entry = *insertion_point; 302 | *insertion_point = list_entry->next; 303 | 304 | list_entry->entry->~T(); 305 | this->object_allocator_.free(list_entry->entry_base); 306 | this->list_allocator_.free(list_entry->this_base); 307 | 308 | return; 309 | } 310 | 311 | throw std::runtime_error("Bad entry"); 312 | } 313 | 314 | iterator erase(iterator iterator) 315 | { 316 | auto* list_entry = iterator.get_entry(); 317 | auto** insertion_point = &this->entries_; 318 | while (*insertion_point && list_entry) 319 | { 320 | if (*insertion_point != list_entry) 321 | { 322 | insertion_point = &(*insertion_point)->next; 323 | continue; 324 | } 325 | 326 | *insertion_point = list_entry->next; 327 | 328 | list_entry->entry->~T(); 329 | this->object_allocator_.free(list_entry->entry_base); 330 | this->list_allocator_.free(list_entry->this_base); 331 | 332 | return {*insertion_point}; 333 | } 334 | 335 | throw std::runtime_error("Bad iterator"); 336 | } 337 | 338 | bool empty() const 339 | { 340 | return this->entries_ == nullptr; 341 | } 342 | 343 | private: 344 | friend iterator; 345 | 346 | ObjectAllocator object_allocator_{}; 347 | ListAllocator list_allocator_{}; 348 | list_entry* entries_{nullptr}; 349 | 350 | template 351 | static U* align_pointer(V* pointer) 352 | { 353 | const auto align_bits = alignof(U) - 1; 354 | auto ptr = reinterpret_cast(pointer); 355 | ptr = (ptr + align_bits) & (~align_bits); 356 | 357 | return reinterpret_cast(ptr); 358 | } 359 | 360 | void allocate_entry(void*& list_base, void*& entry_base) 361 | { 362 | list_base = nullptr; 363 | entry_base = nullptr; 364 | 365 | auto destructor = utils::finally([&] 366 | { 367 | if (list_base) 368 | { 369 | this->list_allocator_.free(list_base); 370 | } 371 | 372 | if (entry_base) 373 | { 374 | this->object_allocator_.free(entry_base); 375 | } 376 | }); 377 | 378 | list_base = this->list_allocator_.allocate(sizeof(list_entry) + alignof(list_entry)); 379 | if (!list_base) 380 | { 381 | throw std::runtime_error("Memory allocation failed"); 382 | } 383 | 384 | entry_base = this->object_allocator_.allocate(sizeof(T) + alignof(T)); 385 | if (!entry_base) 386 | { 387 | throw std::runtime_error("Memory allocation failed"); 388 | } 389 | 390 | destructor.cancel(); 391 | } 392 | 393 | list_entry& create_uninitialized_list_entry() 394 | { 395 | void* list_base = {}; 396 | void* entry_base = {}; 397 | this->allocate_entry(list_base, entry_base); 398 | 399 | auto* obj = align_pointer(entry_base); 400 | auto* entry = align_pointer(list_base); 401 | 402 | entry->this_base = list_base; 403 | entry->entry_base = entry_base; 404 | entry->next = nullptr; 405 | entry->entry = obj; 406 | 407 | return *entry; 408 | } 409 | 410 | T& add_uninitialized_entry_back() 411 | { 412 | auto** insertion_point = &this->entries_; 413 | while (*insertion_point) 414 | { 415 | insertion_point = &(*insertion_point)->next; 416 | } 417 | 418 | auto& entry = this->create_uninitialized_list_entry(); 419 | *insertion_point = &entry; 420 | 421 | return *entry.entry; 422 | } 423 | 424 | T& add_uninitialized_entry_front() 425 | { 426 | auto& entry = this->create_uninitialized_list_entry(); 427 | entry.next = this->entries_; 428 | this->entries_ = &entry; 429 | 430 | return *entry.entry; 431 | } 432 | }; 433 | } 434 | -------------------------------------------------------------------------------- /src/driver/irp.cpp: -------------------------------------------------------------------------------- 1 | #include "std_include.hpp" 2 | #include "irp.hpp" 3 | #include "finally.hpp" 4 | #include "logging.hpp" 5 | #include "string.hpp" 6 | #include "memory.hpp" 7 | 8 | #include 9 | 10 | #include "process.hpp" 11 | #include "thread.hpp" 12 | #include "hypervisor.hpp" 13 | 14 | namespace 15 | { 16 | _Function_class_(DRIVER_DISPATCH) NTSTATUS not_supported_handler(PDEVICE_OBJECT /*device_object*/, const PIRP irp) 17 | { 18 | PAGED_CODE() 19 | 20 | irp->IoStatus.Information = 0; 21 | irp->IoStatus.Status = STATUS_NOT_SUPPORTED; 22 | 23 | IoCompleteRequest(irp, IO_NO_INCREMENT); 24 | 25 | return STATUS_NOT_SUPPORTED; 26 | } 27 | 28 | _Function_class_(DRIVER_DISPATCH) NTSTATUS success_handler(PDEVICE_OBJECT /*device_object*/, const PIRP irp) 29 | { 30 | PAGED_CODE() 31 | 32 | irp->IoStatus.Information = 0; 33 | irp->IoStatus.Status = STATUS_SUCCESS; 34 | 35 | IoCompleteRequest(irp, IO_NO_INCREMENT); 36 | 37 | return STATUS_SUCCESS; 38 | } 39 | 40 | utils::list generate_translation_hints(uint32_t process_id, const void* target_address, 41 | size_t size) 42 | { 43 | utils::list translation_hints{}; 44 | 45 | thread::kernel_thread([&translation_hints, process_id, target_address, size] 46 | { 47 | debug_log("Looking up process: %d\n", process_id); 48 | 49 | const auto process_handle = process::find_process_by_id(process_id); 50 | if (!process_handle || !process_handle.is_alive()) 51 | { 52 | debug_log("Bad process\n"); 53 | return; 54 | } 55 | 56 | if (const auto name = process_handle.get_image_filename()) 57 | { 58 | debug_log("Attaching to %s\n", name); 59 | } 60 | 61 | process::scoped_process_attacher attacher{process_handle}; 62 | 63 | debug_log("Generating translation hints for address: %p\n", target_address); 64 | translation_hints = vmx::ept::generate_translation_hints(target_address, size); 65 | }).join(); 66 | 67 | return translation_hints; 68 | } 69 | 70 | void apply_hook(const hook_request& request) 71 | { 72 | auto* hypervisor = hypervisor::get_instance(); 73 | if (!hypervisor) 74 | { 75 | throw std::runtime_error("Hypervisor not installed"); 76 | } 77 | 78 | std::unique_ptr buffer(new uint8_t[request.source_data_size]); 79 | if (!buffer) 80 | { 81 | throw std::runtime_error("Failed to copy buffer"); 82 | } 83 | 84 | memcpy(buffer.get(), request.source_data, request.source_data_size); 85 | const auto translation_hints = generate_translation_hints(request.process_id, request.target_address, 86 | request.source_data_size); 87 | 88 | if (translation_hints.empty()) 89 | { 90 | debug_log("Failed to generate tranlsation hints\n"); 91 | return; 92 | } 93 | 94 | hypervisor->install_ept_hook(request.target_address, buffer.get(), request.source_data_size, 95 | process::get_current_process_id(), request.process_id, translation_hints); 96 | } 97 | 98 | void unhook() 99 | { 100 | const auto instance = hypervisor::get_instance(); 101 | if (instance) 102 | { 103 | instance->disable_all_ept_hooks(); 104 | } 105 | } 106 | 107 | void try_apply_hook(const PIO_STACK_LOCATION irp_sp) 108 | { 109 | memory::assert_readability(irp_sp->Parameters.DeviceIoControl.Type3InputBuffer, 110 | irp_sp->Parameters.DeviceIoControl.InputBufferLength); 111 | 112 | if (irp_sp->Parameters.DeviceIoControl.InputBufferLength < sizeof(hook_request)) 113 | { 114 | throw std::runtime_error("Invalid hook request"); 115 | } 116 | 117 | const auto& request = *static_cast(irp_sp->Parameters.DeviceIoControl.Type3InputBuffer); 118 | memory::assert_readability(request.source_data, request.source_data_size); 119 | memory::assert_readability(request.target_address, request.source_data_size); 120 | 121 | apply_hook(request); 122 | } 123 | 124 | void watch_regions(const watch_request& watch_request) 125 | { 126 | const auto* hypervisor = hypervisor::get_instance(); 127 | if (!hypervisor) 128 | { 129 | throw std::runtime_error("Hypervisor not installed"); 130 | } 131 | 132 | std::unique_ptr buffer(new watch_region[watch_request.watch_region_count]); 133 | if (!buffer) 134 | { 135 | throw std::runtime_error("Failed to copy buffer"); 136 | } 137 | 138 | memcpy(buffer.get(), watch_request.watch_regions, watch_request.watch_region_count * sizeof(watch_region)); 139 | 140 | auto watch_request_copy = watch_request; 141 | watch_request_copy.watch_regions = buffer.get(); 142 | 143 | size_t page_count = 0; 144 | for (size_t i = 0; i < watch_request_copy.watch_region_count; ++i) 145 | { 146 | const auto& watch_region = watch_request_copy.watch_regions[i]; 147 | 148 | auto start = static_cast(watch_region.virtual_address); 149 | auto end = start + watch_region.length; 150 | 151 | start = static_cast(PAGE_ALIGN(start)); 152 | end = static_cast(PAGE_ALIGN(reinterpret_cast(end) + (PAGE_SIZE - 1))); 153 | page_count += (end - start) / PAGE_SIZE; 154 | } 155 | 156 | volatile long index = 0; 157 | std::unique_ptr page_buffer(new uint64_t[page_count]); 158 | if (!page_buffer) 159 | { 160 | throw std::runtime_error("Failed to copy buffer"); 161 | } 162 | 163 | thread::kernel_thread t([watch_request_copy, &index, &page_buffer] 164 | { 165 | debug_log("Looking up process: %d\n", watch_request_copy.process_id); 166 | 167 | const auto process_handle = process::find_process_by_id(watch_request_copy.process_id); 168 | if (!process_handle || !process_handle.is_alive()) 169 | { 170 | debug_log("Bad process\n"); 171 | return; 172 | } 173 | 174 | const auto name = process_handle.get_image_filename(); 175 | if (name) 176 | { 177 | debug_log("Attaching to %s\n", name); 178 | } 179 | 180 | process::scoped_process_attacher attacher{process_handle}; 181 | 182 | for (size_t i = 0; i < watch_request_copy.watch_region_count; ++i) 183 | { 184 | const auto& watch_region = watch_request_copy.watch_regions[i]; 185 | 186 | auto start = static_cast(watch_region.virtual_address); 187 | auto end = start + watch_region.length; 188 | 189 | start = static_cast(PAGE_ALIGN(start)); 190 | end = static_cast(PAGE_ALIGN(reinterpret_cast(end) + (PAGE_SIZE - 1))); 191 | 192 | for (auto current = start; current < end; current += PAGE_SIZE) 193 | { 194 | const auto physical_address = memory::get_physical_address(const_cast(current)); 195 | if (physical_address) 196 | { 197 | debug_log("Resolved %p -> %llX\n", current, physical_address); 198 | page_buffer.get()[index] = physical_address; 199 | InterlockedIncrement(&index); 200 | //(void)hypervisor->install_ept_code_watch_point(physical_address); 201 | } 202 | else 203 | { 204 | debug_log("Failed to resovle physical address for %p\n", current); 205 | } 206 | } 207 | } 208 | }); 209 | 210 | t.join(); 211 | 212 | debug_log("Installing watch points...\n"); 213 | (void)hypervisor->install_ept_code_watch_points(page_buffer.get(), index, process::get_current_process_id(), 214 | watch_request_copy.process_id); 215 | debug_log("Watch points installed\n"); 216 | } 217 | 218 | void try_watch_regions(const PIO_STACK_LOCATION irp_sp) 219 | { 220 | memory::assert_readability(irp_sp->Parameters.DeviceIoControl.Type3InputBuffer, 221 | irp_sp->Parameters.DeviceIoControl.InputBufferLength); 222 | 223 | if (irp_sp->Parameters.DeviceIoControl.InputBufferLength < sizeof(watch_request)) 224 | { 225 | throw std::runtime_error("Invalid watch request"); 226 | } 227 | 228 | const auto& request = *static_cast(irp_sp->Parameters.DeviceIoControl.Type3InputBuffer); 229 | memory::assert_readability(request.watch_regions, request.watch_region_count * sizeof(watch_region)); 230 | 231 | watch_regions(request); 232 | } 233 | 234 | void get_records(const PIRP irp, const PIO_STACK_LOCATION irp_sp) 235 | { 236 | const auto* hypervisor = hypervisor::get_instance(); 237 | if (!hypervisor) 238 | { 239 | throw std::runtime_error("Hypervisor not installed"); 240 | } 241 | 242 | size_t count{}; 243 | const auto records = hypervisor->get_ept().get_access_records(&count); 244 | 245 | memset(irp->UserBuffer, 0, irp_sp->Parameters.DeviceIoControl.OutputBufferLength); 246 | memcpy(irp->UserBuffer, records, min(irp_sp->Parameters.DeviceIoControl.OutputBufferLength, count * 8)); 247 | } 248 | 249 | void handle_irp(const PIRP irp) 250 | { 251 | irp->IoStatus.Information = 0; 252 | irp->IoStatus.Status = STATUS_SUCCESS; 253 | 254 | const auto irp_sp = IoGetCurrentIrpStackLocation(irp); 255 | if (irp_sp) 256 | { 257 | const auto ioctr_code = irp_sp->Parameters.DeviceIoControl.IoControlCode; 258 | 259 | switch (ioctr_code) 260 | { 261 | case HOOK_DRV_IOCTL: 262 | try_apply_hook(irp_sp); 263 | break; 264 | case UNHOOK_DRV_IOCTL: 265 | unhook(); 266 | break; 267 | case WATCH_DRV_IOCTL: 268 | try_watch_regions(irp_sp); 269 | break; 270 | case GET_RECORDS_DRV_IOCTL: 271 | get_records(irp, irp_sp); 272 | break; 273 | default: 274 | debug_log("Invalid IOCTL Code: 0x%X\n", ioctr_code); 275 | irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; 276 | break; 277 | } 278 | } 279 | } 280 | 281 | _Function_class_(DRIVER_DISPATCH) NTSTATUS io_ctl_handler( 282 | PDEVICE_OBJECT /*device_object*/, const PIRP irp) 283 | { 284 | PAGED_CODE() 285 | 286 | try 287 | { 288 | handle_irp(irp); 289 | } 290 | catch (std::exception& e) 291 | { 292 | debug_log("Handling IRP failed: %s\n", e.what()); 293 | irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; 294 | } 295 | catch (...) 296 | { 297 | debug_log("Handling IRP failed\n"); 298 | irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; 299 | } 300 | 301 | IoCompleteRequest(irp, IO_NO_INCREMENT); 302 | 303 | return irp->IoStatus.Status; 304 | } 305 | } 306 | 307 | irp::irp(const PDRIVER_OBJECT driver_object, const wchar_t* device_name, const wchar_t* dos_device_name) 308 | { 309 | PAGED_CODE() 310 | 311 | this->device_name_ = string::get_unicode_string(device_name); 312 | this->dos_device_name_ = string::get_unicode_string(dos_device_name); 313 | 314 | auto destructor = utils::finally([this]() 315 | { 316 | if (this->device_object_) 317 | { 318 | IoDeleteDevice(this->device_object_); 319 | } 320 | }); 321 | 322 | auto status = IoCreateDevice(driver_object, 0, &this->device_name_, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, 323 | FALSE, &this->device_object_); 324 | if (!NT_SUCCESS(status)) 325 | { 326 | throw std::runtime_error("Unable to create device"); 327 | } 328 | 329 | for (auto i = 0u; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) 330 | { 331 | driver_object->MajorFunction[i] = not_supported_handler; 332 | } 333 | 334 | driver_object->MajorFunction[IRP_MJ_CREATE] = success_handler; 335 | driver_object->MajorFunction[IRP_MJ_CLOSE] = success_handler; 336 | driver_object->MajorFunction[IRP_MJ_DEVICE_CONTROL] = io_ctl_handler; 337 | 338 | this->device_object_->Flags |= DO_DIRECT_IO; 339 | this->device_object_->Flags &= ~DO_DEVICE_INITIALIZING; 340 | 341 | status = IoCreateSymbolicLink(&this->dos_device_name_, &this->device_name_); 342 | if (!NT_SUCCESS(status)) 343 | { 344 | throw std::runtime_error("Unable to create symbolic link"); 345 | } 346 | 347 | destructor.cancel(); 348 | } 349 | 350 | irp::~irp() 351 | { 352 | try 353 | { 354 | PAGED_CODE() 355 | 356 | if (this->device_object_) 357 | { 358 | IoDeleteSymbolicLink(&this->dos_device_name_); 359 | IoDeleteDevice(this->device_object_); 360 | } 361 | } 362 | catch (...) 363 | { 364 | } 365 | } 366 | 367 | irp::irp(irp&& obj) noexcept 368 | : irp() 369 | { 370 | this->operator=(std::move(obj)); 371 | } 372 | 373 | irp& irp::operator=(irp&& obj) noexcept 374 | { 375 | if (this != &obj) 376 | { 377 | this->~irp(); 378 | 379 | this->device_name_ = obj.device_name_; 380 | this->dos_device_name_ = obj.dos_device_name_; 381 | this->device_object_ = obj.device_object_; 382 | 383 | obj.device_object_ = nullptr; 384 | } 385 | 386 | return *this; 387 | } 388 | -------------------------------------------------------------------------------- /src/driver/ept.cpp: -------------------------------------------------------------------------------- 1 | #include "std_include.hpp" 2 | #include "ept.hpp" 3 | 4 | #include "assembly.hpp" 5 | #include "finally.hpp" 6 | #include "logging.hpp" 7 | #include "memory.hpp" 8 | #include "vmx.hpp" 9 | 10 | namespace vmx 11 | { 12 | namespace 13 | { 14 | struct mtrr_range 15 | { 16 | uint32_t enabled; 17 | uint32_t type; 18 | uint64_t physical_address_min; 19 | uint64_t physical_address_max; 20 | }; 21 | 22 | using mtrr_list = mtrr_range[16]; 23 | 24 | void initialize_mtrr(mtrr_list& mtrr_data) 25 | { 26 | ia32_mtrr_capabilities_register mtrr_capabilities{}; 27 | mtrr_capabilities.flags = __readmsr(IA32_MTRR_CAPABILITIES); 28 | 29 | for (auto i = 0u; i < mtrr_capabilities.variable_range_count; i++) 30 | { 31 | ia32_mtrr_physbase_register mtrr_base{}; 32 | ia32_mtrr_physmask_register mtrr_mask{}; 33 | 34 | mtrr_base.flags = __readmsr(IA32_MTRR_PHYSBASE0 + i * 2); 35 | mtrr_mask.flags = __readmsr(IA32_MTRR_PHYSMASK0 + i * 2); 36 | 37 | mtrr_data[i].type = static_cast(mtrr_base.type); 38 | mtrr_data[i].enabled = static_cast(mtrr_mask.valid); 39 | if (mtrr_data[i].enabled != FALSE) 40 | { 41 | mtrr_data[i].physical_address_min = mtrr_base.page_frame_number * 42 | MTRR_PAGE_SIZE; 43 | 44 | unsigned long bit{}; 45 | _BitScanForward64(&bit, mtrr_mask.page_frame_number * MTRR_PAGE_SIZE); 46 | mtrr_data[i].physical_address_max = mtrr_data[i]. 47 | physical_address_min + 48 | (1ULL << bit) - 1; 49 | } 50 | } 51 | } 52 | 53 | uint32_t mtrr_adjust_effective_memory_type(const mtrr_list& mtrr_data, const uint64_t large_page_address, 54 | uint32_t candidate_memory_type) 55 | { 56 | for (const auto& mtrr_entry : mtrr_data) 57 | { 58 | if (mtrr_entry.enabled && large_page_address + (2_mb - 1) >= mtrr_entry.physical_address_min && 59 | large_page_address <= mtrr_entry.physical_address_max) 60 | { 61 | candidate_memory_type = mtrr_entry.type; 62 | } 63 | } 64 | 65 | return candidate_memory_type; 66 | } 67 | 68 | void update_fake_page(ept_hook& hook) 69 | { 70 | if (!hook.mapped_virtual_address) 71 | { 72 | return; 73 | } 74 | 75 | uint8_t page_copy[PAGE_SIZE]; 76 | memcpy(page_copy, hook.mapped_virtual_address, PAGE_SIZE); 77 | 78 | for (size_t i = 0; i < PAGE_SIZE; ++i) 79 | { 80 | if (hook.diff_page[i] != page_copy[i]) 81 | { 82 | hook.diff_page[i] = page_copy[i]; 83 | hook.fake_page[i] = page_copy[i]; 84 | } 85 | } 86 | } 87 | 88 | void reset_all_watch_point_pages(utils::list& watch_points) 89 | { 90 | for (const auto& watch_point : watch_points) 91 | { 92 | if (watch_point.target_page) 93 | { 94 | watch_point.target_page->write_access = 0; 95 | watch_point.target_page->read_access = 0; 96 | watch_point.target_page->execute_access = 1; 97 | } 98 | } 99 | } 100 | } 101 | 102 | ept_hook::ept_hook(const uint64_t physical_base) 103 | : physical_base_address(physical_base) 104 | , mapped_virtual_address(memory::map_physical_memory(physical_base_address, PAGE_SIZE)) 105 | { 106 | if (!mapped_virtual_address) 107 | { 108 | throw std::runtime_error("Failed to map physical memory"); 109 | } 110 | } 111 | 112 | ept_hook::~ept_hook() 113 | { 114 | this->target_page->flags = this->original_entry.flags; 115 | 116 | if (mapped_virtual_address) 117 | { 118 | memory::unmap_physical_memory(mapped_virtual_address, PAGE_SIZE); 119 | } 120 | } 121 | 122 | ept::ept() 123 | { 124 | // Directly initializing these fields kills the compiler ._. 125 | // https://developercommunity.visualstudio.com/t/clexe-using-20gb-of-memory-compiling-small-file-in/407999 126 | memset(this->epml4, 0, sizeof(this->epml4)); 127 | memset(this->epdpt, 0, sizeof(this->epdpt)); 128 | memset(this->epde, 0, sizeof(this->epde)); 129 | 130 | memset(this->access_records, 0, sizeof(this->access_records)); 131 | } 132 | 133 | ept::~ept() 134 | { 135 | this->disable_all_hooks(); 136 | } 137 | 138 | void ept::install_page_hook(void* destination, const void* source, const size_t length, const process_id source_pid, 139 | const process_id target_pid, 140 | const ept_translation_hint* translation_hint) 141 | { 142 | auto* hook = this->get_or_create_ept_hook(destination, translation_hint); 143 | hook->source_pid = source_pid; 144 | hook->target_pid = target_pid; 145 | 146 | const auto page_offset = ADDRMASK_EPT_PML1_OFFSET(reinterpret_cast(destination)); 147 | memcpy(hook->fake_page + page_offset, source, length); 148 | } 149 | 150 | void ept::record_access(const uint64_t rip) 151 | { 152 | for (auto& access_record : this->access_records) 153 | { 154 | if (access_record == 0) 155 | { 156 | access_record = rip; 157 | return; 158 | } 159 | 160 | if (access_record == rip) 161 | { 162 | return; 163 | } 164 | } 165 | } 166 | 167 | void ept::install_hook(const void* destination, const void* source, const size_t length, 168 | const process_id source_pid, const process_id target_pid, 169 | const utils::list& hints) 170 | { 171 | auto current_destination = reinterpret_cast(destination); 172 | auto current_source = reinterpret_cast(source); 173 | auto current_length = length; 174 | 175 | while (current_length != 0) 176 | { 177 | const auto aligned_destination = PAGE_ALIGN(current_destination); 178 | const auto page_offset = ADDRMASK_EPT_PML1_OFFSET(current_destination); 179 | const auto page_remaining = PAGE_SIZE - page_offset; 180 | const auto data_to_write = min(page_remaining, current_length); 181 | 182 | 183 | const ept_translation_hint* relevant_hint = nullptr; 184 | for (const auto& hint : hints) 185 | { 186 | if (hint.virtual_base_address == aligned_destination) 187 | { 188 | relevant_hint = &hint; 189 | break; 190 | } 191 | } 192 | 193 | this->install_page_hook(reinterpret_cast(current_destination), 194 | reinterpret_cast(current_source), data_to_write, source_pid, 195 | target_pid, relevant_hint); 196 | 197 | current_length -= data_to_write; 198 | current_destination += data_to_write; 199 | current_source += data_to_write; 200 | } 201 | } 202 | 203 | void ept::disable_all_hooks() 204 | { 205 | this->ept_hooks.clear(); 206 | } 207 | 208 | void ept::handle_violation(guest_context& guest_context) 209 | { 210 | vmx_exit_qualification_ept_violation violation_qualification{}; 211 | violation_qualification.flags = guest_context.exit_qualification; 212 | 213 | if (!violation_qualification.caused_by_translation) 214 | { 215 | guest_context.exit_vm = true; 216 | } 217 | 218 | const auto physical_base_address = reinterpret_cast(PAGE_ALIGN(guest_context.guest_physical_address)); 219 | 220 | // watch-point stuff 221 | 222 | auto* watch_point = this->find_ept_code_watch_point(physical_base_address); 223 | if (watch_point) 224 | { 225 | reset_all_watch_point_pages(this->ept_code_watch_points); 226 | 227 | if (!violation_qualification.ept_executable && violation_qualification.execute_access) 228 | { 229 | watch_point->target_page->execute_access = 1; 230 | watch_point->target_page->write_access = 0; 231 | watch_point->target_page->read_access = 0; 232 | guest_context.increment_rip = false; 233 | } 234 | else if (violation_qualification.ept_executable && (violation_qualification.read_access || 235 | violation_qualification. 236 | write_access)) 237 | { 238 | watch_point->target_page->execute_access = 0; 239 | watch_point->target_page->read_access = 1; 240 | watch_point->target_page->write_access = 1; 241 | guest_context.increment_rip = false; 242 | if (violation_qualification.read_access) 243 | { 244 | this->record_access(guest_context.guest_rip); 245 | } 246 | } 247 | 248 | return; 249 | } 250 | 251 | // ept-hooking stuff 252 | 253 | auto* hook = this->find_ept_hook(physical_base_address); 254 | if (!hook) 255 | { 256 | return; 257 | } 258 | 259 | if (!violation_qualification.ept_executable && violation_qualification.execute_access) 260 | { 261 | update_fake_page(*hook); 262 | hook->target_page->flags = hook->execute_entry.flags; 263 | guest_context.increment_rip = false; 264 | } 265 | 266 | if (violation_qualification.ept_executable && (violation_qualification.read_access || violation_qualification. 267 | write_access)) 268 | { 269 | hook->target_page->flags = hook->readwrite_entry.flags; 270 | guest_context.increment_rip = false; 271 | } 272 | } 273 | 274 | void ept::handle_misconfiguration(guest_context& guest_context) const 275 | { 276 | // We can actually not recover from this, but this should not occur anyways 277 | guest_context.increment_rip = false; 278 | guest_context.exit_vm = true; 279 | } 280 | 281 | void ept::initialize() 282 | { 283 | mtrr_list mtrr_data{}; 284 | initialize_mtrr(mtrr_data); 285 | 286 | this->epml4[0].read_access = 1; 287 | this->epml4[0].write_access = 1; 288 | this->epml4[0].execute_access = 1; 289 | this->epml4[0].page_frame_number = memory::get_physical_address(&this->epdpt) / 290 | PAGE_SIZE; 291 | 292 | // -------------------------- 293 | 294 | pml1 temp_epdpte{}; 295 | temp_epdpte.flags = 0; 296 | temp_epdpte.read_access = 1; 297 | temp_epdpte.write_access = 1; 298 | temp_epdpte.execute_access = 1; 299 | 300 | __stosq(reinterpret_cast(&this->epdpt[0]), temp_epdpte.flags, EPT_PDPTE_ENTRY_COUNT); 301 | 302 | for (auto i = 0; i < EPT_PDPTE_ENTRY_COUNT; i++) 303 | { 304 | this->epdpt[i].page_frame_number = memory::get_physical_address(&this->epde[i][0]) / PAGE_SIZE; 305 | } 306 | 307 | // -------------------------- 308 | 309 | pml2 temp_epde{}; 310 | temp_epde.flags = 0; 311 | temp_epde.read_access = 1; 312 | temp_epde.write_access = 1; 313 | temp_epde.execute_access = 1; 314 | temp_epde.large_page = 1; 315 | 316 | __stosq(reinterpret_cast(this->epde), temp_epde.flags, EPT_PDPTE_ENTRY_COUNT * EPT_PDE_ENTRY_COUNT); 317 | 318 | for (auto i = 0; i < EPT_PDPTE_ENTRY_COUNT; i++) 319 | { 320 | for (auto j = 0; j < EPT_PDE_ENTRY_COUNT; j++) 321 | { 322 | this->epde[i][j].page_frame_number = (i * 512) + j; 323 | this->epde[i][j].memory_type = mtrr_adjust_effective_memory_type( 324 | mtrr_data, this->epde[i][j].page_frame_number * 2_mb, MEMORY_TYPE_WRITE_BACK); 325 | } 326 | } 327 | } 328 | 329 | void ept::install_code_watch_point(const uint64_t physical_page, const process_id source_pid, 330 | const process_id target_pid) 331 | { 332 | const auto physical_base_address = reinterpret_cast(PAGE_ALIGN(physical_page)); 333 | 334 | if (this->find_ept_code_watch_point(physical_base_address)) 335 | { 336 | return; 337 | } 338 | 339 | auto& watch_point = this->allocate_ept_code_watch_point(); 340 | watch_point.source_pid = source_pid; 341 | watch_point.target_pid = target_pid; 342 | 343 | this->split_large_page(physical_base_address); 344 | 345 | watch_point.physical_base_address = physical_base_address; 346 | watch_point.target_page = this->get_pml1_entry(physical_base_address); 347 | if (!watch_point.target_page) 348 | { 349 | throw std::runtime_error("Failed to get PML1 entry for target address"); 350 | } 351 | 352 | watch_point.target_page->write_access = 0; 353 | watch_point.target_page->read_access = 0; 354 | } 355 | 356 | ept_pointer ept::get_ept_pointer() const 357 | { 358 | const auto ept_pml4_physical_address = memory::get_physical_address(const_cast(&this->epml4[0])); 359 | 360 | ept_pointer vmx_eptp{}; 361 | vmx_eptp.flags = 0; 362 | vmx_eptp.page_walk_length = 3; 363 | vmx_eptp.memory_type = MEMORY_TYPE_WRITE_BACK; 364 | vmx_eptp.page_frame_number = ept_pml4_physical_address / PAGE_SIZE; 365 | 366 | return vmx_eptp; 367 | } 368 | 369 | void ept::invalidate() const 370 | { 371 | const auto ept_pointer = this->get_ept_pointer(); 372 | 373 | invept_descriptor descriptor{}; 374 | descriptor.ept_pointer = ept_pointer.flags; 375 | descriptor.reserved = 0; 376 | 377 | __invept(1, &descriptor); 378 | } 379 | 380 | pml2* ept::get_pml2_entry(const uint64_t physical_address) 381 | { 382 | const auto directory = ADDRMASK_EPT_PML2_INDEX(physical_address); 383 | const auto directory_pointer = ADDRMASK_EPT_PML3_INDEX(physical_address); 384 | const auto pml4_entry = ADDRMASK_EPT_PML4_INDEX(physical_address); 385 | 386 | if (pml4_entry > 0) 387 | { 388 | return nullptr; 389 | } 390 | 391 | return &this->epde[directory_pointer][directory]; 392 | } 393 | 394 | pml1* ept::get_pml1_entry(const uint64_t physical_address) 395 | { 396 | auto* pml2_entry = this->get_pml2_entry(physical_address); 397 | if (!pml2_entry || pml2_entry->large_page) 398 | { 399 | return nullptr; 400 | } 401 | 402 | const auto* pml2 = reinterpret_cast(pml2_entry); 403 | auto* pml1_table = this->find_pml1_table(pml2->page_frame_number * PAGE_SIZE); 404 | if (!pml1_table) 405 | { 406 | pml1_table = static_cast(memory::get_virtual_address(pml2->page_frame_number * PAGE_SIZE)); 407 | } 408 | 409 | if (!pml1_table) 410 | { 411 | return nullptr; 412 | } 413 | 414 | return &pml1_table[ADDRMASK_EPT_PML1_INDEX(physical_address)]; 415 | } 416 | 417 | pml1* ept::find_pml1_table(const uint64_t physical_address) 418 | { 419 | for (auto& split : this->ept_splits) 420 | { 421 | if (memory::get_physical_address(&split.pml1[0]) == physical_address) 422 | { 423 | return split.pml1; 424 | } 425 | } 426 | 427 | return nullptr; 428 | } 429 | 430 | ept_split& ept::allocate_ept_split() 431 | { 432 | return this->ept_splits.emplace_back(); 433 | } 434 | 435 | ept_hook& ept::allocate_ept_hook(const uint64_t physical_address) 436 | { 437 | return this->ept_hooks.emplace_back(physical_address); 438 | } 439 | 440 | ept_hook* ept::find_ept_hook(const uint64_t physical_address) 441 | { 442 | for (auto& hook : this->ept_hooks) 443 | { 444 | if (hook.physical_base_address == physical_address) 445 | { 446 | return &hook; 447 | } 448 | } 449 | 450 | return nullptr; 451 | } 452 | 453 | ept_code_watch_point& ept::allocate_ept_code_watch_point() 454 | { 455 | return this->ept_code_watch_points.emplace_back(); 456 | } 457 | 458 | ept_code_watch_point* ept::find_ept_code_watch_point(const uint64_t physical_address) 459 | { 460 | for (auto& watch_point : this->ept_code_watch_points) 461 | { 462 | if (watch_point.physical_base_address == physical_address) 463 | { 464 | return &watch_point; 465 | } 466 | } 467 | 468 | return nullptr; 469 | } 470 | 471 | ept_hook* ept::get_or_create_ept_hook(void* destination, const ept_translation_hint* translation_hint) 472 | { 473 | const auto virtual_target = PAGE_ALIGN(destination); 474 | 475 | uint64_t physical_address = 0; 476 | if (translation_hint) 477 | { 478 | physical_address = translation_hint->physical_base_address + ADDRMASK_EPT_PML1_OFFSET( 479 | reinterpret_cast(destination)); 480 | } 481 | else 482 | { 483 | physical_address = memory::get_physical_address(virtual_target); 484 | } 485 | 486 | if (!physical_address) 487 | { 488 | throw std::runtime_error("No physical address for destination"); 489 | } 490 | 491 | const auto physical_base_address = reinterpret_cast(PAGE_ALIGN(physical_address)); 492 | auto* hook = this->find_ept_hook(physical_base_address); 493 | if (hook) 494 | { 495 | if (hook->target_page->flags == hook->original_entry.flags) 496 | { 497 | const auto* data_source = translation_hint ? &translation_hint->page[0] : virtual_target; 498 | memcpy(&hook->fake_page[0], data_source, PAGE_SIZE); 499 | 500 | hook->target_page->flags = hook->readwrite_entry.flags; 501 | } 502 | 503 | return hook; 504 | } 505 | 506 | hook = &this->allocate_ept_hook(physical_base_address); 507 | 508 | this->split_large_page(physical_address); 509 | 510 | const auto* data_source = translation_hint ? &translation_hint->page[0] : virtual_target; 511 | memcpy(&hook->fake_page[0], data_source, PAGE_SIZE); 512 | memcpy(&hook->diff_page[0], data_source, PAGE_SIZE); 513 | hook->physical_base_address = physical_base_address; 514 | 515 | hook->target_page = this->get_pml1_entry(physical_address); 516 | if (!hook->target_page) 517 | { 518 | throw std::runtime_error("Failed to get PML1 entry for target address"); 519 | } 520 | 521 | hook->original_entry = *hook->target_page; 522 | hook->readwrite_entry = hook->original_entry; 523 | 524 | hook->readwrite_entry.read_access = 1; 525 | hook->readwrite_entry.write_access = 1; 526 | hook->readwrite_entry.execute_access = 0; 527 | 528 | hook->execute_entry.flags = 0; 529 | hook->execute_entry.read_access = 0; 530 | hook->execute_entry.write_access = 0; 531 | hook->execute_entry.execute_access = 1; 532 | hook->execute_entry.page_frame_number = memory::get_physical_address(&hook->fake_page) / PAGE_SIZE; 533 | 534 | hook->target_page->flags = hook->readwrite_entry.flags; 535 | 536 | return hook; 537 | } 538 | 539 | void ept::split_large_page(const uint64_t physical_address) 540 | { 541 | auto* target_entry = this->get_pml2_entry(physical_address); 542 | if (!target_entry) 543 | { 544 | throw std::runtime_error("Invalid physical address"); 545 | } 546 | 547 | if (!target_entry->large_page) 548 | { 549 | return; 550 | } 551 | 552 | auto& split = this->allocate_ept_split(); 553 | 554 | pml1 pml1_template{}; 555 | pml1_template.flags = 0; 556 | pml1_template.read_access = 1; 557 | pml1_template.write_access = 1; 558 | pml1_template.execute_access = 1; 559 | pml1_template.memory_type = target_entry->memory_type; 560 | pml1_template.ignore_pat = target_entry->ignore_pat; 561 | pml1_template.suppress_ve = target_entry->suppress_ve; 562 | 563 | __stosq(reinterpret_cast(&split.pml1[0]), pml1_template.flags, EPT_PTE_ENTRY_COUNT); 564 | 565 | for (auto i = 0; i < EPT_PTE_ENTRY_COUNT; ++i) 566 | { 567 | split.pml1[i].page_frame_number = ((target_entry->page_frame_number * 2_mb) / PAGE_SIZE) + i; 568 | } 569 | 570 | pml2_ptr new_pointer{}; 571 | new_pointer.flags = 0; 572 | new_pointer.read_access = 1; 573 | new_pointer.write_access = 1; 574 | new_pointer.execute_access = 1; 575 | 576 | new_pointer.page_frame_number = memory::get_physical_address(&split.pml1[0]) / PAGE_SIZE; 577 | 578 | target_entry->flags = new_pointer.flags; 579 | } 580 | 581 | utils::list ept::generate_translation_hints(const void* destination, const size_t length) 582 | { 583 | utils::list hints{}; 584 | 585 | auto current_destination = reinterpret_cast(destination); 586 | auto current_length = length; 587 | 588 | while (current_length != 0) 589 | { 590 | const auto aligned_destination = PAGE_ALIGN(current_destination); 591 | const auto page_offset = ADDRMASK_EPT_PML1_OFFSET(current_destination); 592 | const auto page_remaining = PAGE_SIZE - page_offset; 593 | const auto data_to_write = min(page_remaining, current_length); 594 | 595 | const auto physical_base_address = memory::get_physical_address(aligned_destination); 596 | if (!physical_base_address) 597 | { 598 | throw std::runtime_error("Failed to resolve physical address"); 599 | } 600 | 601 | auto& current_hint = hints.emplace_back(); 602 | 603 | current_hint.virtual_base_address = aligned_destination; 604 | current_hint.physical_base_address = physical_base_address; 605 | 606 | memcpy(¤t_hint.page[0], aligned_destination, PAGE_SIZE); 607 | 608 | current_length -= data_to_write; 609 | current_destination += data_to_write; 610 | } 611 | 612 | return hints; 613 | } 614 | 615 | uint64_t* ept::get_access_records(size_t* count) 616 | { 617 | size_t i = 0; 618 | for (const auto& record : this->access_records) 619 | { 620 | if (record == 0) break; 621 | ++i; 622 | } 623 | 624 | *count = i; 625 | return this->access_records; 626 | } 627 | 628 | bool ept::cleanup_process(const process_id process) 629 | { 630 | bool changed = false; 631 | 632 | for (auto i = this->ept_hooks.begin(); i != this->ept_hooks.end();) 633 | { 634 | if (i->source_pid == process || i->target_pid == process) 635 | { 636 | i = this->ept_hooks.erase(i); 637 | changed = true; 638 | } 639 | else 640 | { 641 | ++i; 642 | } 643 | } 644 | 645 | for (auto i = this->ept_code_watch_points.begin(); i != this->ept_code_watch_points.end();) 646 | { 647 | if (i->source_pid == process || i->target_pid == process) 648 | { 649 | i = this->ept_code_watch_points.erase(i); 650 | changed = true; 651 | } 652 | else 653 | { 654 | ++i; 655 | } 656 | } 657 | 658 | return changed; 659 | } 660 | } 661 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . --------------------------------------------------------------------------------