Skip to content

Instantly share code, notes, and snippets.

@Frityet
Created April 16, 2025 04:13
Show Gist options
  • Save Frityet/af63f6d06797c76d145a883a8322081b to your computer and use it in GitHub Desktop.
Save Frityet/af63f6d06797c76d145a883a8322081b to your computer and use it in GitHub Desktop.
#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