// // Created by Vlad on 8/8/2025. // #pragma once #include #include #include #include #ifdef _WIN32 #include "omath/utility/pe_pattern_scan.hpp" #include #elif defined(__APPLE__) #include "omath/utility/macho_pattern_scan.hpp" #include #else #include "omath/utility/elf_pattern_scan.hpp" #include #endif namespace omath::rev_eng { template struct FixedString final { char data[N]{}; // ReSharper disable once CppNonExplicitConvertingConstructor constexpr FixedString(const char (&str)[N]) noexcept // NOLINT(*-explicit-constructor) { for (std::size_t i = 0; i < N; ++i) data[i] = str[i]; } // ReSharper disable once CppNonExplicitConversionOperator constexpr operator std::string_view() const noexcept // NOLINT(*-explicit-constructor) { return {data, N - 1}; } }; template FixedString(const char (&)[N]) -> FixedString; class InternalReverseEngineeredObject { protected: template [[nodiscard]] Type& get_by_offset(const std::ptrdiff_t offset) { return *reinterpret_cast(reinterpret_cast(this) + offset); } template [[nodiscard]] const Type& get_by_offset(const std::ptrdiff_t offset) const { return *reinterpret_cast(reinterpret_cast(this) + offset); } template ReturnType call_method(const void* ptr, auto... arg_list) { #ifdef _MSC_VER using MethodType = ReturnType(__thiscall*)(void*, decltype(arg_list)...); #else using MethodType = ReturnType (*)(void*, decltype(arg_list)...); #endif return reinterpret_cast(const_cast(ptr))(this, arg_list...); } template ReturnType call_method(const void* ptr, auto... arg_list) const { #ifdef _MSC_VER using MethodType = ReturnType(__thiscall*)(const void*, decltype(arg_list)...); #else using MethodType = ReturnType (*)(const void*, decltype(arg_list)...); #endif return reinterpret_cast(const_cast(ptr))(this, arg_list...); } template ReturnType call_method(auto... arg_list) { static const auto* address = resolve_pattern(ModuleName, Pattern); return call_method(address, arg_list...); } template ReturnType call_method(auto... arg_list) const { static const auto* address = resolve_pattern(ModuleName, Pattern); return call_method(address, arg_list...); } template ReturnType call_method(const std::string_view& module_name,const std::string_view& pattern, auto... arg_list) { static const auto* address = resolve_pattern(module_name, pattern); return call_method(address, arg_list...); } template ReturnType call_method(const std::string_view& module_name,const std::string_view& pattern, auto... arg_list) const { static const auto* address = resolve_pattern(module_name, pattern); return call_method(address, arg_list...); } template ReturnType call_virtual_method(auto... arg_list) { const auto vtable = *reinterpret_cast(this); return call_method(vtable[Id], arg_list...); } template ReturnType call_virtual_method(auto... arg_list) const { const auto vtable = *reinterpret_cast(this); return call_method(vtable[Id], arg_list...); } template ReturnType call_virtual_method(auto... arg_list) { const auto vtable = *reinterpret_cast( reinterpret_cast(this) + TableIndex * sizeof(std::uintptr_t)); return call_method(vtable[Id], arg_list...); } template ReturnType call_virtual_method(auto... arg_list) const { const auto vtable = *reinterpret_cast( reinterpret_cast(this) + TableIndex * sizeof(std::uintptr_t)); return call_method(vtable[Id], arg_list...); } private: [[nodiscard]] static const void* resolve_pattern(const std::string_view module_name, const std::string_view pattern) { const auto* base = get_module_base(module_name); assert(base && "Failed to find module"); #ifdef _WIN32 const auto result = PePatternScanner::scan_for_pattern_in_loaded_module(base, pattern); #elif defined(__APPLE__) const auto result = MachOPatternScanner::scan_for_pattern_in_loaded_module(base, pattern); #else const auto result = ElfPatternScanner::scan_for_pattern_in_loaded_module(base, pattern); #endif assert(result.has_value() && "Pattern scan failed"); return reinterpret_cast(*result); } [[nodiscard]] static const void* get_module_base(const std::string_view module_name) { #ifdef _WIN32 return GetModuleHandleA(module_name.data()); #elif defined(__APPLE__) // On macOS, iterate loaded images to find the module by name const auto count = _dyld_image_count(); for (std::uint32_t i = 0; i < count; ++i) { const auto* name = _dyld_get_image_name(i); if (name && std::string_view{name}.find(module_name) != std::string_view::npos) return static_cast(_dyld_get_image_header(i)); } return nullptr; #else // On Linux, use dl_iterate_phdr to find loaded module by name struct CallbackData { std::string_view name; const void* base; } cb_data{module_name, nullptr}; dl_iterate_phdr( [](dl_phdr_info* info, std::size_t, void* data) -> int { auto* cb = static_cast(data); if (info->dlpi_name && std::string_view{info->dlpi_name}.find(cb->name) != std::string_view::npos) { cb->base = reinterpret_cast(info->dlpi_addr); return 1; } return 0; }, &cb_data); return cb_data.base; #endif } }; } // namespace omath::rev_eng