From 59f6d7a36152acd3ea9daca8b495d03624c7d858 Mon Sep 17 00:00:00 2001 From: Orange Date: Wed, 18 Mar 2026 19:58:46 +0300 Subject: [PATCH 1/8] added call_method --- include/omath/rev_eng/internal_rev_object.hpp | 144 ++++++++++++++++-- 1 file changed, 134 insertions(+), 10 deletions(-) diff --git a/include/omath/rev_eng/internal_rev_object.hpp b/include/omath/rev_eng/internal_rev_object.hpp index 56f205a9..32f7f6e2 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_virtual_method(auto... arg_list) + 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 VirtualMethodType = ReturnType(__thiscall*)(void*, decltype(arg_list)...); + using MethodType = ReturnType(__thiscall*)(const void*, decltype(arg_list)...); #else - using VirtualMethodType = ReturnType (*)(void*, decltype(arg_list)...); + using MethodType = ReturnType (*)(const void*, decltype(arg_list)...); #endif - return (*reinterpret_cast(this))[id](this, arg_list...); + 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) + { + 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 From a3e93ac25945cd96d368a9b72b42ae2a801a17ff Mon Sep 17 00:00:00 2001 From: Orange Date: Wed, 18 Mar 2026 20:05:32 +0300 Subject: [PATCH 2/8] added nttp --- include/omath/rev_eng/internal_rev_object.hpp | 69 ++++++------------- 1 file changed, 20 insertions(+), 49 deletions(-) diff --git a/include/omath/rev_eng/internal_rev_object.hpp b/include/omath/rev_eng/internal_rev_object.hpp index 32f7f6e2..64d40084 100644 --- a/include/omath/rev_eng/internal_rev_object.hpp +++ b/include/omath/rev_eng/internal_rev_object.hpp @@ -3,7 +3,6 @@ // #pragma once -#include #include #include #include @@ -22,20 +21,6 @@ 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: @@ -72,47 +57,17 @@ namespace omath::rev_eng return reinterpret_cast(const_cast(ptr))(this, arg_list...); } - template + 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); - }(); - + static const auto* address = resolve_pattern(module_name, pattern); return call_method(address, arg_list...); } - template + 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); - }(); - + static const auto* address = resolve_pattern(module_name, pattern); return call_method(address, arg_list...); } @@ -130,6 +85,22 @@ namespace omath::rev_eng } private: + 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 + 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); + } + static const void* get_module_base(const std::string_view module_name) { #ifdef _WIN32 From 9cdffcbdb18f21ef201ddce61e53745cfd6ae34b Mon Sep 17 00:00:00 2001 From: Orange Date: Wed, 18 Mar 2026 20:12:46 +0300 Subject: [PATCH 3/8] added tests --- .../general/unit_test_reverse_enineering.cpp | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/tests/general/unit_test_reverse_enineering.cpp b/tests/general/unit_test_reverse_enineering.cpp index b1b0eb4a..1b90428d 100644 --- a/tests/general/unit_test_reverse_enineering.cpp +++ b/tests/general/unit_test_reverse_enineering.cpp @@ -20,6 +20,11 @@ class Player final int m_health{123}; }; +// Free functions that mimic member function calling convention (this as first arg) +inline int free_add(void* /*this_ptr*/, int a, int b) { return a + b; } +inline float free_scale(void* /*this_ptr*/, float val, float factor) { return val * factor; } +inline int free_get_42(const void* /*this_ptr*/) { return 42; } + class RevPlayer final : omath::rev_eng::InternalReverseEngineeredObject { public: @@ -51,6 +56,22 @@ class RevPlayer final : omath::rev_eng::InternalReverseEngineeredObject { return call_virtual_method<1, int>(); } + + // Wrappers exposing call_method for testing + int call_add(int a, int b) + { + return call_method(reinterpret_cast(&free_add), a, b); + } + + float call_scale(float val, float factor) + { + return call_method(reinterpret_cast(&free_scale), val, factor); + } + + int call_get_42() const + { + return call_method(reinterpret_cast(&free_get_42)); + } }; TEST(unit_test_reverse_enineering, read_test) @@ -64,4 +85,50 @@ TEST(unit_test_reverse_enineering, read_test) EXPECT_EQ(player_original.bar(), player_reversed->rev_bar()); EXPECT_EQ(player_original.foo(), player_reversed->rev_foo()); EXPECT_EQ(player_original.bar(), player_reversed->rev_bar_const()); +} + +TEST(unit_test_reverse_enineering, call_method_with_args) +{ + Player player_original; + auto* player_reversed = reinterpret_cast(&player_original); + + EXPECT_EQ(free_add(nullptr, 3, 4), player_reversed->call_add(3, 4)); + EXPECT_EQ(7, player_reversed->call_add(3, 4)); +} + +TEST(unit_test_reverse_enineering, call_method_float_args) +{ + Player player_original; + auto* player_reversed = reinterpret_cast(&player_original); + + EXPECT_FLOAT_EQ(6.0f, player_reversed->call_scale(2.0f, 3.0f)); + EXPECT_FLOAT_EQ(0.0f, player_reversed->call_scale(0.0f, 100.0f)); + EXPECT_FLOAT_EQ(-5.0f, player_reversed->call_scale(5.0f, -1.0f)); +} + +TEST(unit_test_reverse_enineering, call_method_const) +{ + Player player_original; + const auto* player_reversed = reinterpret_cast(&player_original); + + EXPECT_EQ(42, player_reversed->call_get_42()); +} + +TEST(unit_test_reverse_enineering, call_method_no_extra_args) +{ + Player player_original; + const auto* player_reversed = reinterpret_cast(&player_original); + + // call_get_42 takes no arguments beyond this — verifies zero-arg pack works + EXPECT_EQ(42, player_reversed->call_get_42()); +} + +TEST(unit_test_reverse_enineering, call_virtual_method_delegates_to_call_method) +{ + // call_virtual_method now internally uses call_method — verify both vtable slots + Player player_original; + auto* player_reversed = reinterpret_cast(&player_original); + + EXPECT_EQ(1, player_reversed->rev_foo()); + EXPECT_EQ(2, player_reversed->rev_bar()); } \ No newline at end of file From 91136a61c49a9bad3fe693442feae59863429fd7 Mon Sep 17 00:00:00 2001 From: Orange Date: Wed, 18 Mar 2026 21:12:18 +0300 Subject: [PATCH 4/8] improvement --- include/omath/rev_eng/internal_rev_object.hpp | 43 +++++++++++++------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/include/omath/rev_eng/internal_rev_object.hpp b/include/omath/rev_eng/internal_rev_object.hpp index 64d40084..601301a9 100644 --- a/include/omath/rev_eng/internal_rev_object.hpp +++ b/include/omath/rev_eng/internal_rev_object.hpp @@ -21,6 +21,25 @@ 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: @@ -57,31 +76,31 @@ namespace omath::rev_eng return reinterpret_cast(const_cast(ptr))(this, arg_list...); } - template + template ReturnType call_method(auto... arg_list) { - static const auto* address = resolve_pattern(module_name, pattern); + static const auto* address = resolve_pattern(ModuleName, Pattern); return call_method(address, arg_list...); } - template + template ReturnType call_method(auto... arg_list) const { - static const auto* address = resolve_pattern(module_name, pattern); + static const auto* address = resolve_pattern(ModuleName, Pattern); return call_method(address, arg_list...); } - template + template ReturnType call_virtual_method(auto... arg_list) { const auto vtable = *reinterpret_cast(this); - return call_method(vtable[id], arg_list...); + return call_method(vtable[Id], arg_list...); } - template + template ReturnType call_virtual_method(auto... arg_list) const { const auto vtable = *reinterpret_cast(this); - return call_method(vtable[id], arg_list...); + return call_method(vtable[Id], arg_list...); } private: @@ -91,11 +110,11 @@ namespace omath::rev_eng assert(base && "Failed to find module"); #ifdef _WIN32 - auto result = PePatternScanner::scan_for_pattern_in_loaded_module(base, pattern); + const 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); + const auto result = MachOPatternScanner::scan_for_pattern_in_loaded_module(base, pattern); #else - auto result = ElfPatternScanner::scan_for_pattern_in_loaded_module(base, pattern); + const auto result = ElfPatternScanner::scan_for_pattern_in_loaded_module(base, pattern); #endif assert(result.has_value() && "Pattern scan failed"); return reinterpret_cast(*result); @@ -104,7 +123,7 @@ namespace omath::rev_eng static const void* get_module_base(const std::string_view module_name) { #ifdef _WIN32 - return static_cast(GetModuleHandleA(module_name.data())); + 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(); From 06dc36089f21d9fdf4e49429bcb49b2335147e94 Mon Sep 17 00:00:00 2001 From: Orange Date: Wed, 18 Mar 2026 21:19:09 +0300 Subject: [PATCH 5/8] added overload --- include/omath/rev_eng/internal_rev_object.hpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/include/omath/rev_eng/internal_rev_object.hpp b/include/omath/rev_eng/internal_rev_object.hpp index 601301a9..34dffde6 100644 --- a/include/omath/rev_eng/internal_rev_object.hpp +++ b/include/omath/rev_eng/internal_rev_object.hpp @@ -90,6 +90,19 @@ namespace omath::rev_eng 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) { From 6236c8fd6865fa446178765cc225ec5ae63faa2d Mon Sep 17 00:00:00 2001 From: Orange Date: Wed, 18 Mar 2026 21:24:35 +0300 Subject: [PATCH 6/8] added nodiscard --- include/omath/rev_eng/internal_rev_object.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/omath/rev_eng/internal_rev_object.hpp b/include/omath/rev_eng/internal_rev_object.hpp index 34dffde6..d8eac41f 100644 --- a/include/omath/rev_eng/internal_rev_object.hpp +++ b/include/omath/rev_eng/internal_rev_object.hpp @@ -117,6 +117,7 @@ namespace omath::rev_eng } 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); @@ -133,6 +134,7 @@ namespace omath::rev_eng return reinterpret_cast(*result); } + [[nodiscard]] static const void* get_module_base(const std::string_view module_name) { #ifdef _WIN32 From 012d837e8b050f0cfd93ef8667f70981b466182a Mon Sep 17 00:00:00 2001 From: Orange Date: Thu, 19 Mar 2026 00:57:54 +0300 Subject: [PATCH 7/8] fix windows x32 bit --- .../general/unit_test_reverse_enineering.cpp | 64 ++++++++++++------- 1 file changed, 41 insertions(+), 23 deletions(-) diff --git a/tests/general/unit_test_reverse_enineering.cpp b/tests/general/unit_test_reverse_enineering.cpp index 1b90428d..c314c8b2 100644 --- a/tests/general/unit_test_reverse_enineering.cpp +++ b/tests/general/unit_test_reverse_enineering.cpp @@ -20,10 +20,28 @@ class Player final int m_health{123}; }; -// Free functions that mimic member function calling convention (this as first arg) -inline int free_add(void* /*this_ptr*/, int a, int b) { return a + b; } -inline float free_scale(void* /*this_ptr*/, float val, float factor) { return val * factor; } -inline int free_get_42(const void* /*this_ptr*/) { return 42; } +// Helper to extract a member function pointer address as const void* +template +const void* member_fn_to_ptr(T fn) +{ + union + { + T member; + const void* ptr; + } u{}; + static_assert(sizeof(T) == sizeof(const void*), "Only simple member function pointers supported"); + u.member = fn; + return u.ptr; +} + +// Target class with non-virtual member functions for call_method testing +class MethodTarget +{ +public: + int add(int a, int b) { return a + b; } + float scale(float val, float factor) { return val * factor; } + int get_42() const { return 42; } +}; class RevPlayer final : omath::rev_eng::InternalReverseEngineeredObject { @@ -57,20 +75,20 @@ class RevPlayer final : omath::rev_eng::InternalReverseEngineeredObject return call_virtual_method<1, int>(); } - // Wrappers exposing call_method for testing + // Wrappers exposing call_method for testing with real member function addresses int call_add(int a, int b) { - return call_method(reinterpret_cast(&free_add), a, b); + return call_method(member_fn_to_ptr(&MethodTarget::add), a, b); } float call_scale(float val, float factor) { - return call_method(reinterpret_cast(&free_scale), val, factor); + return call_method(member_fn_to_ptr(&MethodTarget::scale), val, factor); } int call_get_42() const { - return call_method(reinterpret_cast(&free_get_42)); + return call_method(member_fn_to_ptr(&MethodTarget::get_42)); } }; @@ -89,38 +107,38 @@ TEST(unit_test_reverse_enineering, read_test) TEST(unit_test_reverse_enineering, call_method_with_args) { - Player player_original; - auto* player_reversed = reinterpret_cast(&player_original); + MethodTarget target; + auto* rev = reinterpret_cast(&target); - EXPECT_EQ(free_add(nullptr, 3, 4), player_reversed->call_add(3, 4)); - EXPECT_EQ(7, player_reversed->call_add(3, 4)); + EXPECT_EQ(target.add(3, 4), rev->call_add(3, 4)); + EXPECT_EQ(7, rev->call_add(3, 4)); } TEST(unit_test_reverse_enineering, call_method_float_args) { - Player player_original; - auto* player_reversed = reinterpret_cast(&player_original); + MethodTarget target; + auto* rev = reinterpret_cast(&target); - EXPECT_FLOAT_EQ(6.0f, player_reversed->call_scale(2.0f, 3.0f)); - EXPECT_FLOAT_EQ(0.0f, player_reversed->call_scale(0.0f, 100.0f)); - EXPECT_FLOAT_EQ(-5.0f, player_reversed->call_scale(5.0f, -1.0f)); + EXPECT_FLOAT_EQ(6.0f, rev->call_scale(2.0f, 3.0f)); + EXPECT_FLOAT_EQ(0.0f, rev->call_scale(0.0f, 100.0f)); + EXPECT_FLOAT_EQ(-5.0f, rev->call_scale(5.0f, -1.0f)); } TEST(unit_test_reverse_enineering, call_method_const) { - Player player_original; - const auto* player_reversed = reinterpret_cast(&player_original); + MethodTarget target; + const auto* rev = reinterpret_cast(&target); - EXPECT_EQ(42, player_reversed->call_get_42()); + EXPECT_EQ(42, rev->call_get_42()); } TEST(unit_test_reverse_enineering, call_method_no_extra_args) { - Player player_original; - const auto* player_reversed = reinterpret_cast(&player_original); + MethodTarget target; + const auto* rev = reinterpret_cast(&target); // call_get_42 takes no arguments beyond this — verifies zero-arg pack works - EXPECT_EQ(42, player_reversed->call_get_42()); + EXPECT_EQ(42, rev->call_get_42()); } TEST(unit_test_reverse_enineering, call_virtual_method_delegates_to_call_method) From f390b386d7d655fd55b546149743b889edecc7dc Mon Sep 17 00:00:00 2001 From: Orange Date: Thu, 19 Mar 2026 01:06:16 +0300 Subject: [PATCH 8/8] fix --- .../general/unit_test_reverse_enineering.cpp | 92 ++++++------------- 1 file changed, 30 insertions(+), 62 deletions(-) diff --git a/tests/general/unit_test_reverse_enineering.cpp b/tests/general/unit_test_reverse_enineering.cpp index c314c8b2..55b783af 100644 --- a/tests/general/unit_test_reverse_enineering.cpp +++ b/tests/general/unit_test_reverse_enineering.cpp @@ -20,29 +20,13 @@ class Player final int m_health{123}; }; -// Helper to extract a member function pointer address as const void* -template -const void* member_fn_to_ptr(T fn) +// Extract a raw function pointer from an object's vtable +inline const void* get_vtable_entry(const void* obj, const std::size_t index) { - union - { - T member; - const void* ptr; - } u{}; - static_assert(sizeof(T) == sizeof(const void*), "Only simple member function pointers supported"); - u.member = fn; - return u.ptr; + const auto vtable = *static_cast(obj); + return vtable[index]; } -// Target class with non-virtual member functions for call_method testing -class MethodTarget -{ -public: - int add(int a, int b) { return a + b; } - float scale(float val, float factor) { return val * factor; } - int get_42() const { return 42; } -}; - class RevPlayer final : omath::rev_eng::InternalReverseEngineeredObject { public: @@ -75,20 +59,15 @@ class RevPlayer final : omath::rev_eng::InternalReverseEngineeredObject return call_virtual_method<1, int>(); } - // Wrappers exposing call_method for testing with real member function addresses - int call_add(int a, int b) - { - return call_method(member_fn_to_ptr(&MethodTarget::add), a, b); - } - - float call_scale(float val, float factor) + // Wrappers exposing call_method for testing — use vtable entries as known-good function pointers + int call_foo_via_ptr(const void* fn_ptr) const { - return call_method(member_fn_to_ptr(&MethodTarget::scale), val, factor); + return call_method(fn_ptr); } - int call_get_42() const + int call_bar_via_ptr(const void* fn_ptr) const { - return call_method(member_fn_to_ptr(&MethodTarget::get_42)); + return call_method(fn_ptr); } }; @@ -105,48 +84,37 @@ TEST(unit_test_reverse_enineering, read_test) EXPECT_EQ(player_original.bar(), player_reversed->rev_bar_const()); } -TEST(unit_test_reverse_enineering, call_method_with_args) +TEST(unit_test_reverse_enineering, call_method_with_vtable_ptr) { - MethodTarget target; - auto* rev = reinterpret_cast(&target); + // Extract raw function pointers from Player's vtable, then call them via call_method + Player player; + const auto* rev = reinterpret_cast(&player); - EXPECT_EQ(target.add(3, 4), rev->call_add(3, 4)); - EXPECT_EQ(7, rev->call_add(3, 4)); -} - -TEST(unit_test_reverse_enineering, call_method_float_args) -{ - MethodTarget target; - auto* rev = reinterpret_cast(&target); - - EXPECT_FLOAT_EQ(6.0f, rev->call_scale(2.0f, 3.0f)); - EXPECT_FLOAT_EQ(0.0f, rev->call_scale(0.0f, 100.0f)); - EXPECT_FLOAT_EQ(-5.0f, rev->call_scale(5.0f, -1.0f)); -} - -TEST(unit_test_reverse_enineering, call_method_const) -{ - MethodTarget target; - const auto* rev = reinterpret_cast(&target); + const auto* foo_ptr = get_vtable_entry(&player, 0); + const auto* bar_ptr = get_vtable_entry(&player, 1); - EXPECT_EQ(42, rev->call_get_42()); + EXPECT_EQ(player.foo(), rev->call_foo_via_ptr(foo_ptr)); + EXPECT_EQ(player.bar(), rev->call_bar_via_ptr(bar_ptr)); + EXPECT_EQ(1, rev->call_foo_via_ptr(foo_ptr)); + EXPECT_EQ(2, rev->call_bar_via_ptr(bar_ptr)); } -TEST(unit_test_reverse_enineering, call_method_no_extra_args) +TEST(unit_test_reverse_enineering, call_method_same_result_as_virtual) { - MethodTarget target; - const auto* rev = reinterpret_cast(&target); + // call_virtual_method delegates to call_method — both paths must agree + Player player; + const auto* rev = reinterpret_cast(&player); - // call_get_42 takes no arguments beyond this — verifies zero-arg pack works - EXPECT_EQ(42, rev->call_get_42()); + EXPECT_EQ(rev->rev_foo(), rev->call_foo_via_ptr(get_vtable_entry(&player, 0))); + EXPECT_EQ(rev->rev_bar(), rev->call_bar_via_ptr(get_vtable_entry(&player, 1))); } TEST(unit_test_reverse_enineering, call_virtual_method_delegates_to_call_method) { - // call_virtual_method now internally uses call_method — verify both vtable slots - Player player_original; - auto* player_reversed = reinterpret_cast(&player_original); + Player player; + auto* rev = reinterpret_cast(&player); - EXPECT_EQ(1, player_reversed->rev_foo()); - EXPECT_EQ(2, player_reversed->rev_bar()); + EXPECT_EQ(1, rev->rev_foo()); + EXPECT_EQ(2, rev->rev_bar()); + EXPECT_EQ(2, rev->rev_bar_const()); } \ No newline at end of file