Created
April 16, 2025 04:13
-
-
Save Frityet/af63f6d06797c76d145a883a8322081b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cxxabi.h> | |
#include <execinfo.h> | |
#include <format> | |
#include <functional> | |
#include <string> | |
#include <unordered_map> | |
#include <vector> | |
#define println(...) puts(std::format(__VA_ARGS__).c_str()) | |
struct HijackEntryBase { | |
virtual ~HijackEntryBase() = default; | |
}; | |
template <typename TRet, typename ...TArgs> | |
struct HijackEntry : HijackEntryBase { | |
std::vector<std::function<void(TArgs &...)>> entryHooks; | |
TRet invoke(std::function<TRet(TArgs...)> fn, TArgs... args) | |
{ | |
for (auto &hook : entryHooks) { hook(args...); } | |
return fn(std::forward<TArgs>(args)...); | |
} | |
}; | |
struct SymbolInfo { | |
std::string name; | |
const std::type_info &type; | |
bool operator==(const SymbolInfo &other) const { return name == other.name && type == other.type; } | |
}; | |
namespace std | |
{ | |
template <> | |
struct hash<SymbolInfo> { | |
std::size_t operator()(const SymbolInfo &sym) const { return std::hash<std::string>()(sym.name) ^ sym.type.hash_code(); } | |
}; | |
} | |
class HijackContext { | |
public: | |
template <typename TRet, typename... TArgs> | |
void register_function(std::string_view n) | |
{ | |
auto sym = SymbolInfo { std::string(n), typeid(TRet(TArgs...)) }; | |
entries[sym] = std::make_unique<HijackEntry<TRet, TArgs...>>(); | |
} | |
template <typename TRet, typename... TArgs> | |
HijackEntry<TRet, TArgs...> &get_entry(std::string_view n) | |
{ | |
auto sym = SymbolInfo { std::string(n), typeid(TRet(TArgs...)) }; | |
auto it = entries.find(sym); | |
if (it == entries.end()) { throw std::runtime_error("Function not registered"); } | |
return *static_cast<HijackEntry<TRet, TArgs...> *>(it->second.get()); | |
} | |
template <typename TRet, typename... TArgs> | |
constexpr inline HijackEntry<TRet, TArgs...> &operator[](std::string_view n) | |
{ return get_entry<TRet, TArgs...>(n); } | |
static HijackContext &instance() | |
{ | |
if (not _instanceCreated) { _instance = HijackContext(); _instanceCreated = true; } | |
return _instance; | |
} | |
private: | |
static bool _instanceCreated; | |
static HijackContext _instance; | |
std::unordered_map<SymbolInfo, std::unique_ptr<HijackEntryBase>> entries; | |
}; | |
bool HijackContext::_instanceCreated = false; | |
HijackContext HijackContext::_instance; | |
template<typename Sig> | |
struct FunctionSignature; | |
template<typename T> | |
struct FunctionSignature : FunctionSignature<decltype(&T::operator())> {}; | |
template<typename TRet, typename ...TArgs> | |
struct FunctionSignature<TRet(TArgs...)> { | |
using Result = TRet; | |
using Parametres = std::tuple<TArgs...>; | |
}; | |
template<typename TRet, typename ...TArgs> | |
struct FunctionSignature<TRet(*)(TArgs...)> { | |
using Result = TRet; | |
using Parametres = std::tuple<TArgs...>; | |
}; | |
template<typename TRet, typename TObj, typename...TArgs> | |
struct FunctionSignature<TRet(TObj::*)(TArgs...)>{ | |
using Result = TRet; | |
using Object = TObj; | |
using Parametres = std::tuple<TArgs...>; | |
}; | |
template<typename TRet, typename TObj, typename...TArgs> | |
struct FunctionSignature<TRet(TObj::*)(TArgs...) const>{ | |
using Result = TRet; | |
using Object = TObj; | |
using Parametres = std::tuple<TArgs...>; | |
}; | |
template<typename Sig> | |
struct HijackRegister; | |
template<typename T> | |
struct HijackRegister : public HijackRegister<decltype(&T::operator())> { | |
using Result = typename HijackRegister<decltype(&T::operator())>::Result; | |
using Object = typename HijackRegister<decltype(&T::operator())>::Object; | |
using Parametres = typename HijackRegister<decltype(&T::operator())>::Parametres; | |
HijackRegister(std::string_view n): HijackRegister<decltype(&T::operator())>(n) | |
{ | |
HijackContext::instance().register_function<Result, Parametres>(n); | |
} | |
}; | |
template<typename TRet, typename ...TArgs> | |
struct HijackRegister<TRet(TArgs...)> { | |
using Result = TRet; | |
using Parametres = std::tuple<TArgs...>; | |
HijackRegister(std::string_view n) | |
{ | |
HijackContext::instance().register_function<TRet, TArgs...>(n); | |
} | |
}; | |
template<typename TRet, typename ...TArgs> | |
struct HijackRegister<TRet(*)(TArgs...)> { | |
using Result = TRet; | |
using Parametres = std::tuple<TArgs...>; | |
HijackRegister(std::string_view n) | |
{ | |
HijackContext::instance().register_function<TRet, TArgs...>(n); | |
} | |
}; | |
template<typename TRet, typename TObj, typename...TArgs> | |
struct HijackRegister<TRet(TObj::*)(TArgs...)> { | |
using Result = TRet; | |
using Object = TObj; | |
using Parametres = std::tuple<TArgs...>; | |
HijackRegister(std::string_view n) | |
{ | |
HijackContext::instance().register_function<TRet, TArgs...>(n); | |
} | |
}; | |
template<typename TRet, typename TObj, typename...TArgs> | |
struct HijackRegister<TRet(TObj::*)(TArgs...) const> { | |
using Result = TRet; | |
using Object = TObj; | |
using Parametres = std::tuple<TArgs...>; | |
HijackRegister(std::string_view n) | |
{ | |
HijackContext::instance().register_function<TRet, TArgs...>(n); | |
} | |
}; | |
#define $_CONCAT(x, y) x##y | |
#define $concat(...) $_CONCAT(__VA_ARGS__) | |
#define $function(name, lambda)\ | |
template <typename ...TArgs> requires std::is_same_v<std::tuple<TArgs...>, typename FunctionSignature<decltype(lambda)>::Parametres>\ | |
constexpr inline auto name(TArgs &&...args) -> decltype(lambda(std::forward<TArgs>(args)...))\ | |
{\ | |
using TResult = decltype(lambda(std::forward<TArgs>(args)...));\ | |
return HijackContext::instance().get_entry<TResult, TArgs...>(__FUNCTION__).invoke(lambda, std::forward<TArgs>(args)...);\ | |
}\ | |
namespace {\ | |
auto $concat(_register_##name##_, __COUNTER__) = HijackRegister<decltype(lambda)>(#name);\ | |
}\ | |
// Example functions | |
namespace Math | |
{ | |
$function(Divide, [](int x, int y) -> int { return (y != 0) ? x / y : 0; }); | |
$function(Divide, [](float x, float y) -> float { return (y != 0) ? x / y : 0.0f; }); | |
$function(Divide, [](double x, double y) -> double { return (y != 0) ? x / y : 0.0; }); | |
$function(Add, [](int x, int y) -> int { return x + y; }); | |
} | |
// Hooks | |
void DivideHook1(int &x, int &) { x += 10; } | |
void DivideHook2(int &x, int &) { x *= 2; } | |
void AddHook1(int &x, int &y) { x *= y; } | |
int main() | |
{ | |
HijackContext::instance().get_entry<int, int, int>("Divide").entryHooks.push_back(DivideHook1); | |
HijackContext::instance().get_entry<int, int, int>("Divide").entryHooks.push_back(DivideHook2); | |
HijackContext::instance().get_entry<int, int, int>("Add").entryHooks.push_back(AddHook1); | |
// Test | |
std::println("Divide: {}", Math::Divide(20, 10)); // (20 + 10) * 2 / 10 = 6 | |
std::println("Add: {}", Math::Add(20, 10)); // 20 * 10 + 10 = 210 | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment