Skip to content

Hooking API

MCMrARM edited this page Sep 17, 2018 · 6 revisions

C API

The launcher provides the following basic C API for hooking:

// #include <modloader/hook.h>

modloader_hook_t *modloader_hook(void *sym, void *hook, void **orig);
void modloader_destroy_hook(modloader_hook_t *hook);

The modloader_hook function replaces the function given by sym with hook and stores a pointer that lets you call the original function in *orig. All passed arguments must not be NULL. Note that *orig will not exactly be the original function, but instead a trampoline function, but this should be considered an implementation detail. modloader_hook returns a pointer that you can use with modloader_destroy_hook to remove the hook at runtime. In case an error happens, modloader_hook will return NULL. It's possible that the function will modify the orig pointer, even if hooking fails.

The modloader_destroy_hook function removes the hook specified by hook. hook must not be NULL. The implementation will try to remove the hook, however this is not guaranteed to always be the case. The API currently does not provide a way to tell if removing the hook succeeded.

Example

#include <modloader/hook.h>

const char* what_we_will_replace(int x) {
  return x == 0 ? "value is zero" : "value is not zero";
}

static const char* (*test_orig)(int x);
const char* test_hook(int x) {
  // NOTE that calling what_we_will_replace directly from here would result in it calling test_hook recursively
  return test_orig(1337);
}

void init() {
  printf("%s\n", what_we_will_replace(0)); // will print "value is zero"
  modloader_hook((void*) what_we_will_replace, (void*) test_hook, (void**) &test_orig);
  printf("%s\n", what_we_will_replace(0)); // will print "value is not zero"
}

C++ API

The library provides a simple C++ scoped-based hook (modloader::AutoHook), and a macro-based static hook API is built upon it:

// #include <modloader/statichook.h>

#define THook2(iname, ret, sym, args...) ...
#define THook(ret, sym, args...) ...
#define TClasslessInstanceHook2(iname, ret, sym, args...) ...
#define TClasslessInstanceHook(ret, sym, args...) ...
#define TInstanceHook2(iname, ret, sym, type, args...) ...
#define TInstanceHook(ret, sym, type, args...) ...
#define TStaticHook2(iname, ret, sym, type, args...) ...
#define TStaticHook(ret, sym, type, args...) ...

The macros hook the function with the mangled symbol name sym, with the return type of ret and with the specified args. In the case of TInstanceHook and TStaticHook a valid class name type must also be given.

In the case of C++ executables, mangled symbol names start with _ZN and look like the following: _ZN6Common22getServerVersionStringB5cxx11Ev. C does not perform any sort of symbol mangling, and symbols marked as extern "C" in C++ are also not mangled.

Variants explanation

  • You should use THook for hooking global functions, that are not part of any class.
  • You should use TInstanceHook for hooking member functions. Requires a full class declaration
    • Use TClasslessInstanceHook if you do not want to use a full class declaration
  • You should use TStaticHook for hooking static (class) functions. Requires a full class declaration
    • Use TClasslessInstanceHook if you do not want to use a full class declaration

Requires a full class declaration means that to use it you need to declare the class somewhere and include it (class DedicatedServer { }; in an included header is OK, but a forward declaration like class DedicatedServer; is not enough).

If you need to hook a function more than once within the same mod, you'll need to use the macros suffixed with 2, and pass different names as the first additional argument (eg. MenuHudRenderHook and CoordinatesHudRenderHook, make sure to maintain some sensible internal naming).

Usage explanations/examples

  • THook - global C++ functions like bool ON_MAIN_THREAD(void), C functions (eg. printf)
  • TInstanceHook/TClasslessInstanceHook - for functions that are part of a class and are not static, eg. DedicatedServer::run()
  • TStaticHook/THook - for functions that are part of a class and are static, eg. Common::getServerVersionString()

Example

#include <modloader/statichook.h>

extern "C" const char* what_we_will_replace(int x) {
  return x == 0 ? "value is zero" : "value is not zero";
}

THook(const char*, what_we_will_replace, int x) {
  return original(1337);
}

void init() {
  printf("%s\n", what_we_will_replace(0)); // will print "value is not zero"
}
Clone this wiki locally