Home

Awesome

Build Status Build Status - Windows

SubHook is a simple library for hooking arbitrary functions at run time. It's written in C and also provides an optional C++ wrapper API.

The library was originally developed to intercept a bunch of API calls in the SA-MP server, which is a Windows/Linux 32-bit app, in order to extend a plugin that I wrote for it. Since then, it has been adapted to better support x86_64, macOS, and more common use-cases, thanks to the contributors.

Installation

Easy method:

  1. Copy the source and header files to your project and include subhook.c in your build.
  2. On Windows only: Define SUBHOOK_STATIC before including subhook.h.

With CMake:

  1. Copy the subhook repo to your project tree.

  2. Call add_subdirectory(path/to/subhook) in your CMakeLists.txt.

  3. Optional: configure how the library is built by setting these varaible prior to add_subdirectory(...):

    • SUBHOOK_STATIC - Build as static library (OFF by default)
    • SUBHOOK_INSTALL - Enable installation and packaging of targets/files with CPack (OFF by default)
    • SUBHOOK_TESTS - Enable tests (ON by default)
    • SUBHOOK_FORCE_32BIT - Configure for compiling 32-bit binaries on 64-bit systems (default is OFF)

Use of CMake is not mandatory, the library can be built without it (no extra build configuration is required).

Examples

In the following examples foo is some function or a function pointer that takes a single argument of type int and uses the same calling convention as my_foo (depends on compiler).

Basic usage

#include <stdio.h>
#include <subhook.h>

subhook_t foo_hook;

void my_foo(int x) {
  /* Remove the hook so that you can call the original function. */
  subhook_remove(foo_hook);

  printf("foo(%d) called\n", x);
  foo(x);

  /* Install the hook back to intercept further calls. */
  subhook_install(foo_hook);
}

int main() {
  /* Create a hook that will redirect all foo() calls to to my_foo(). */
  foo_hook = subhook_new((void *)foo, (void *)my_foo, 0);

  /* Install it. */
  subhook_install(foo_hook);

  foo(123);

  /* Remove the hook and free memory when you're done. */
  subhook_remove(foo_hook);
  subhook_free(foo_hook);
}

Trampolines

Using trampolines allows you to jump to the original code without removing and re-installing hooks every time your function gets called.

typedef void (*foo_func)(int x);

void my_foo(int x) {
  printf("foo(%d) called\n", x);

  /* Call foo() via trampoline. */
  ((foo_func)subhook_get_trampoline(foo_hook))(x);
}

int main() {
   /* Same code as in the previous example. */
}

Please note that subhook has a very simple length disassmebler engine (LDE) that works only with most common prologue instructions like push, mov, call, etc. When it encounters an unknown instruction subhook_get_trampoline() will return NULL. You can delegate instruction decoding to a custom disassembler of your choice via subhook_set_disasm_handler().

C++

#include <iostream>
#include <subhook.h>

subhook::Hook foo_hook;
subhook::Hook foo_hook_tr;

typedef void (*foo_func)(int x);

void my_foo(int x) {
  // ScopedHookRemove removes the specified hook and automatically re-installs
  // it when the object goes out of scope (thanks to C++ destructors).
  subhook::ScopedHookRemove remove(&foo_hook);

  std::cout << "foo(" << x << ") called" << std::endl;
  foo(x + 1);
}

void my_foo_tr(int x) {
  std::cout << "foo(" << x << ") called" << std::endl;

  // Call the original function via trampoline.
  ((foo_func)foo_hook_tr.GetTrampoline())(x + 1);
}

int main() {
  foo_hook.Install((void *)foo, (void *)my_foo);
  foo_hook_tr.Install((void *)foo, (void *)my_foo_tr);
}

Known issues/limitations

License

Licensed under the 2-clause BSD license.