项目作者: Smertig

项目描述 :
C++17, multi-architecture cross-platform hooking library with clean API.
高级语言: C++
项目地址: git://github.com/Smertig/rcmp.git
创建时间: 2020-10-03T19:43:50Z
项目社区:https://github.com/Smertig/rcmp

开源协议:MIT License

下载



License: MIT
GitHub Actions
GitHub Actions


rcmp - C++17, multi-architecture cross-platform hooking library with clean API.

Features

  • Intuitive, modern, compiler/platform-independent API
  • x86/x86-64 support (more soon)
  • Windows/Linux support
  • Calling convention support (cdecl, stdcall, thiscall, fastcall, native-x64)

Building

With CMake (as a subproject)

Clone repository to subfolder and link rcmp to your project:

  1. add_subdirectory(path/to/rcmp)
  2. target_link_libraries(your-project-name PRIVATE rcmp)

Examples

  • The most common case: hook function to modify its argument and/or result
    ```c++
    int foo(float arg) { / body / }

rcmp::hook_function<&foo>( {
return original_foo(arg * 2) + 1;
});

  1. - However, in most cases you probably want to hook function **knowing only its address and signature** (in fact, that's everything you need to make hook)
  2. ```c++
  3. rcmp::hook_function<0xDEADBEEF, int(float)>([](auto original_foo, float arg) {
  4. return original_foo(arg * 2) + 1;
  5. });
  • Trace function calls
    ```c++
    void do_something(int id, const char action) { / body */ }

rcmp::hook_function<&do_something>( {
std::cout << “do_something(“ << id << “, “ << action << “) called..\n”;
original_function(id, action);
});

  1. - Replace return value
  2. ```c++
  3. bool check_license() { /* body */ }
  4. rcmp::hook_function<&check_license>([](auto /* original_function */) {
  5. return true;
  6. });
  • Accept arguments as a variadic pack
    ```c++
    template void print(const Args& …) { / implementation / }

rcmp::hook_function<0xDEADBEEF, unsigned int(int, float, bool, double, void*, long)>( {
print(“args are: “, args…);
return original(args…);
});

  1. - Function address can be known at runtime or compile-time
  2. ```c++
  3. // compile-time address
  4. rcmp::hook_function<0xDEADBEEF, int(int)>([](auto original, int arg) { ... });
  5. // runtime address (i.e. from GetProcAddress/dlopen)
  6. rcmp::hook_function<int(int)>(0xDEADBEEF, [](auto original, int arg) { ... });
  • Calling convention support
    ```c++
    /// x86, the following calls are synonyms

// good:
rcmp::hookfunction(…); // default convention
rcmp::hook_function(…); // default convention, but 3 more symbols
rcmp::hook_function>(…); // explicit convention (rcmp::cdecl_t is an alias for rcmp::generic_signature_t<S, rcmp::cconv::cdecl
>)

// bad, compiler-specific
rcmp::hook_function(…); // MSVC
rcmp::hook_function(…); // gcc/clang

// x86 supported conventions
rcmp::cdeclt // same as rcmp::generic_signature_t<void(int), rcmp::cconv::cdecl>
rcmp::stdcallt // same as rcmp::generic_signature_t<void(int), rcmp::cconv::stdcall>
rcmp::thiscallt // same as rcmp::generic_signature_t<void(int), rcmp::cconv::thiscall>
rcmp::fastcallt // same as rcmp::generic_signature_t<void(int), rcmp::cconv::fastcall>

// x64
rcmp::hook_function(…); // default convention
rcmp::hook_function(…); // default convention, but more letters
rcmp::hook_function:native_x64>>(…); // explicit convention

  1. - VTable hooking (`hook_indirect_function`)
  2. ```c++
  3. // Let's assume:
  4. // 5 - index of function in vtable
  5. // int A::f(int) - function signature
  6. using signature_t = rcmp::thiscall_t<int(A*, int)>; // x86, MSVC
  7. using signature_t = rcmp::cdecl_t<int(A*, int)>; // x86, gcc/clang
  8. using signature_t = int(A*, int); // x64
  9. // vtable address can be known at compile-time (0xDEADBEEF)
  10. rcmp::hook_indirect_function<0xDEADBEEF + 5 * sizeof(void*), signature_t>([](auto original, A* self, int arg) { ... });
  11. // ..or at runtime
  12. rcmp::hook_indirect_function<signature_t>(get_vtable_address() + 5 * sizeof(void*), [](auto original, A* self, int arg) { ... });

Motivation

Why yet another hooking library?

There are too many libraries with similar or even more powerful features. Most of them have been perfectly designed; however, they don’t provide all the features I need.

Mods, cheats, plugins etc

I like to develop unofficial mods (plugins) and cheats for various games (both for client and server-side).
This is a very specific area of development that requires continuous experimentation with function hooking.

Suppose you want to hook a function. All you need to know about this function - its address and signature, that’s it.
Just write an interceptor (replacement function) and call something like cool_lib::hook_function. Very simple, isn’t it? Of course it’s not.
Most libraries require:

  • Create global variable, that holds pointer to replaced original function.
  • Write global function, that contains interceptor logic.
  • Write same function signature/name multiple times (DRY).
  • Use C-style casts or MACRO to call original function from the interceptor.
  • Manually create and destroy auxiliary context (i.e. disassembler backend) required for hooking.

That’s really annoying. I want to express my intentions in a single expression without boilerplate, code repetitions and ugly C-style code.

So I ended up with developing my own library - rcmp.

Cross-platform and compiler support

At work I need both windows (.dll) and linux (.so) support, but most libraries aren’t cross-platform (some of them also use compiler-specific extensions, that’s not portable).
Moreover, there are moddable games for Oculus Quest VR that works on ARM64 architecture.
rcmp was designed to be easily extendable, so I was able to use it even for Android apps (ARM64 support comes soon!).

Modern C++ and canonical code

Most libraries use not-so-modern C++ standards (C++11 and below), so they have limited capabilities.
Modern C++ features allow developer to write compact and type-safe code without boilerplate and repetitions (especially in case of hooking).
Due to C++17, rcmp has convenient API as well as minimalistic and readable implementation.

Dependencies

rcmp has single-header bundled lightweight dependency - nmd by Nomade040 (only as a length disassembler for x86/x86-64).
Most of hooking libraries depend on big, verbose or even deprecated frameworks.

Build & Install

rcmp can be easily added to any cmake-based project. No external requirements or dependencies, no installation or manual non-trivial actions to build - just add two lines in CMakeLists.txt.

Missing features

  • No documentation (yet)
  • No way to disable hook
  • No ellipsis (...) support

References

License

  • MIT