diff --git a/include/omath/rev_eng/internal_rev_object.hpp b/include/omath/rev_eng/internal_rev_object.hpp index 56f205a..32f7f6e 100644 --- a/include/omath/rev_eng/internal_rev_object.hpp +++ b/include/omath/rev_eng/internal_rev_object.hpp @@ -3,11 +3,39 @@ // #pragma once +#include +#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 + { + char data[N]{}; + constexpr FixedString(const char (&str)[N]) + { + std::copy_n(str, N, data); + } + constexpr operator std::string_view() const + { + return {data, N - 1}; + } + }; + class InternalReverseEngineeredObject { protected: @@ -23,26 +51,122 @@ namespace omath::rev_eng 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 = []() -> const void* + { + const auto* base = get_module_base(module_name); + assert(base && "Failed to find module"); + +#ifdef _WIN32 + auto result = PePatternScanner::scan_for_pattern_in_loaded_module(base, pattern); +#elif defined(__APPLE__) + auto result = MachOPatternScanner::scan_for_pattern_in_loaded_module(base, pattern); +#else + auto result = ElfPatternScanner::scan_for_pattern_in_loaded_module(base, pattern); +#endif + assert(result.has_value() && "Pattern scan failed"); + return reinterpret_cast(*result); + }(); + + return call_method(address, arg_list...); + } + + template + ReturnType call_method(auto... arg_list) const + { + static const auto address = []() -> const void* + { + const auto* base = get_module_base(module_name); + assert(base && "Failed to find module"); + +#ifdef _WIN32 + auto result = PePatternScanner::scan_for_pattern_in_loaded_module(base, pattern); +#elif defined(__APPLE__) + auto result = MachOPatternScanner::scan_for_pattern_in_loaded_module(base, pattern); +#else + auto result = ElfPatternScanner::scan_for_pattern_in_loaded_module(base, pattern); +#endif + assert(result.has_value() && "Pattern scan failed"); + return reinterpret_cast(*result); + }(); + + return call_method(address, arg_list...); + } + template ReturnType call_virtual_method(auto... arg_list) { -#ifdef _MSC_VER - using VirtualMethodType = ReturnType(__thiscall*)(void*, decltype(arg_list)...); -#else - using VirtualMethodType = ReturnType (*)(void*, decltype(arg_list)...); -#endif - return (*reinterpret_cast(this))[id](this, arg_list...); + const auto vtable = *reinterpret_cast(this); + return call_method(vtable[id], arg_list...); } template ReturnType call_virtual_method(auto... arg_list) const { -#ifdef _MSC_VER - using VirtualMethodType = ReturnType(__thiscall*)(void*, decltype(arg_list)...); + const auto vtable = *reinterpret_cast(this); + return call_method(vtable[id], arg_list...); + } + + private: + static const void* get_module_base(const std::string_view module_name) + { +#ifdef _WIN32 + return static_cast(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 - using VirtualMethodType = ReturnType (*)(void*, decltype(arg_list)...); + // 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 - return (*static_cast((void*)(this)))[id]( - const_cast(static_cast(this)), arg_list...); } }; } // namespace omath::rev_eng