#pragma once #include #include #include #include #include #include #include "unicode.h" namespace utils { class loaded_module { HMODULE m_hModule; public: loaded_module() : m_hModule(nullptr) {} loaded_module(const void* hModule) : m_hModule(reinterpret_cast(const_cast(hModule))) {} loaded_module(void* hModule) : m_hModule(reinterpret_cast(hModule)) {} loaded_module(size_t hModule) : m_hModule(reinterpret_cast(hModule)) {} std::filesystem::path path() const; bool is_current_process() const { return m_hModule == GetModuleHandleW(nullptr); } bool owns_address(const void* pAddress) const; operator HMODULE() const { return m_hModule; } size_t address_int() const { return reinterpret_cast(m_hModule); } size_t image_size() const { return is_pe64() ? nt_header64().OptionalHeader.SizeOfImage : nt_header32().OptionalHeader.SizeOfImage; } char* address(size_t offset = 0) const { return reinterpret_cast(m_hModule) + offset; } template T* address_as(size_t offset) const { return reinterpret_cast(address(offset)); } template std::span span_as(size_t offset, size_t count) const { return std::span(reinterpret_cast(address(offset)), count); } template T& ref_as(size_t offset) const { return *reinterpret_cast(address(offset)); } IMAGE_DOS_HEADER& dos_header() const { return ref_as(0); } IMAGE_NT_HEADERS32& nt_header32() const { return ref_as(dos_header().e_lfanew); } IMAGE_NT_HEADERS64& nt_header64() const { return ref_as(dos_header().e_lfanew); } bool is_pe64() const { return nt_header32().OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC; } std::span data_directories() const { return is_pe64() ? nt_header64().OptionalHeader.DataDirectory : nt_header32().OptionalHeader.DataDirectory; } IMAGE_DATA_DIRECTORY& data_directory(size_t index) const { return data_directories()[index]; } std::span section_headers() const; IMAGE_SECTION_HEADER& section_header(const char* pcszSectionName) const; std::span section(size_t index) const; std::span section(const char* pcszSectionName) const; template TFn* get_exported_function(const char* pcszFunctionName) { const auto pAddress = GetProcAddress(m_hModule, pcszFunctionName); if (!pAddress) throw std::out_of_range(std::format("Exported function \"{}\" not found.", pcszFunctionName)); return reinterpret_cast(pAddress); } bool find_imported_function_pointer(const char* pcszDllName, const char* pcszFunctionName, uint32_t hintOrOrdinal, void*& ppFunctionAddress) const; void* get_imported_function_pointer(const char* pcszDllName, const char* pcszFunctionName, uint32_t hintOrOrdinal) const; template TFn** get_imported_function_pointer(const char* pcszDllName, const char* pcszFunctionName, uint32_t hintOrOrdinal) { return reinterpret_cast(get_imported_function_pointer(pcszDllName, pcszFunctionName, hintOrOrdinal)); } static loaded_module current_process(); static std::vector all_modules(); }; class signature_finder { std::vector> m_ranges; std::vector m_patterns; public: signature_finder& look_in(const void* pFirst, size_t length); signature_finder& look_in(const loaded_module& m, const char* sectionName); template signature_finder& look_in(std::span s) { return look_in(s.data(), s.size()); } signature_finder& look_for(std::string_view pattern, std::string_view mask, char cExactMatch = 'x', char cWildcard = '.'); signature_finder& look_for(std::string_view pattern, char wildcardMask); signature_finder& look_for(std::string_view pattern); signature_finder& look_for_hex(std::string_view pattern); template signature_finder& look_for(char pattern[len]) { static_assert(len == 5); } struct result { std::span Match; size_t PatternIndex; size_t MatchIndex; size_t CaptureIndex; }; std::vector find(size_t minCount, size_t maxCount, bool bErrorOnMoreThanMaximum) const; std::span find_one() const; }; class memory_tenderizer { std::span m_data; std::vector m_regions; public: memory_tenderizer(const void* pAddress, size_t length, DWORD dwNewProtect); template&& std::is_standard_layout_v>> memory_tenderizer(const T& object, DWORD dwNewProtect) : memory_tenderizer(&object, sizeof T, dwNewProtect) {} template memory_tenderizer(std::span s, DWORD dwNewProtect) : memory_tenderizer(&s[0], s.size(), dwNewProtect) {} template memory_tenderizer(std::span s, DWORD dwNewProtect) : memory_tenderizer(&s[0], s.size(), dwNewProtect) {} ~memory_tenderizer(); }; std::shared_ptr allocate_executable_heap(size_t len); template std::shared_ptr allocate_executable_heap(std::span data) { auto res = allocate_executable_heap(data.size_bytes()); memcpy(res.get(), data.data(), data.size_bytes()); return res; } std::shared_ptr create_thunk(void* pfnFunction, void* pThis, uint64_t placeholderValue); template class thunk; template class thunk { using TFn = TReturn(TArgs...); static constexpr uint64_t Placeholder = 0xCC90CC90CC90CC90ULL; const std::shared_ptr m_pThunk; std::function m_fnTarget; public: thunk(std::function target) : m_pThunk(utils::create_thunk(&detour_static, this, Placeholder)) , m_fnTarget(std::move(target)) { } void set_target(std::function detour) { m_fnTarget = std::move(detour); } TFn* get_thunk() const { return reinterpret_cast(m_pThunk.get()); } private: // mark it as virtual to prevent compiler from inlining virtual TReturn detour(TArgs... args) { return m_fnTarget(std::forward(args)...); } static TReturn detour_static(TArgs... args) { const volatile auto pThis = reinterpret_cast*>(Placeholder); return pThis->detour(args...); } }; template> std::basic_string_view trim(std::basic_string_view view, bool left = true, bool right = true) { if (left) { while (!view.empty() && (view.front() < 255 && std::isspace(view.front()))) view = view.substr(1); } if (right) { while (!view.empty() && (view.back() < 255 && std::isspace(view.back()))) view = view.substr(0, view.size() - 1); } return view; } template, class TAlloc = std::allocator> std::basic_string trim(std::basic_string view, bool left = true, bool right = true) { return std::basic_string(trim(std::basic_string_view(view), left, right)); } template, class TAlloc = std::allocator> [[nodiscard]] std::vector> split(const std::basic_string& str, const std::basic_string_view& delimiter, size_t maxSplit = SIZE_MAX) { std::vector> result; if (delimiter.empty()) { for (size_t i = 0; i < str.size(); ++i) result.push_back(str.substr(i, 1)); } else { size_t previousOffset = 0, offset; while (maxSplit && (offset = str.find(delimiter, previousOffset)) != std::string::npos) { result.push_back(str.substr(previousOffset, offset - previousOffset)); previousOffset = offset + delimiter.length(); --maxSplit; } result.push_back(str.substr(previousOffset)); } return result; } template, class TAlloc = std::allocator> [[nodiscard]] std::vector> split(const std::basic_string& str, const std::basic_string& delimiter, size_t maxSplit = SIZE_MAX) { return split(str, std::basic_string_view(delimiter), maxSplit); } template, class TAlloc = std::allocator> [[nodiscard]] std::vector> split(const std::basic_string& str, const TElem* pcszDelimiter, size_t maxSplit = SIZE_MAX) { return split(str, std::basic_string_view(pcszDelimiter), maxSplit); } template T get_env(const wchar_t* pcwzName) { static_assert(false); } template<> std::wstring get_env(const wchar_t* pcwzName); template<> std::string get_env(const wchar_t* pcwzName); template<> int get_env(const wchar_t* pcwzName); template<> bool get_env(const wchar_t* pcwzName); template T get_env(const char* pcszName) { return get_env(unicode::convert(pcszName).c_str()); } template std::vector get_env_list(const wchar_t* pcwzName) { static_assert(false); } template<> std::vector get_env_list(const wchar_t* pcwzName); template std::vector get_env_list(const char* pcszName) { return get_env_list(unicode::convert(pcszName).c_str()); } bool is_running_on_linux(); std::filesystem::path get_module_path(HMODULE hModule); }