Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/Smertig/rcmp
C++17, multi-architecture cross-platform hooking library with clean API.
https://github.com/Smertig/rcmp
cpp cpp17 hooking-library modding-library rcmp
Last synced: about 1 month ago
JSON representation
C++17, multi-architecture cross-platform hooking library with clean API.
- Host: GitHub
- URL: https://github.com/Smertig/rcmp
- Owner: Smertig
- License: mit
- Created: 2020-10-03T19:43:50.000Z (about 4 years ago)
- Default Branch: master
- Last Pushed: 2024-05-06T09:54:09.000Z (8 months ago)
- Last Synced: 2024-08-03T13:04:14.923Z (5 months ago)
- Topics: cpp, cpp17, hooking-library, modding-library, rcmp
- Language: C++
- Homepage:
- Size: 283 KB
- Stars: 68
- Watchers: 3
- Forks: 4
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
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:
```cmake
add_subdirectory(path/to/rcmp)
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>([](auto original_foo, float arg) {
return original_foo(arg * 2) + 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)
```c++
rcmp::hook_function<0xDEADBEEF, int(float)>([](auto original_foo, float arg) {
return original_foo(arg * 2) + 1;
});
```- Trace function calls
```c++
void do_something(int id, const char* action) { /* body */ }rcmp::hook_function<&do_something>([](auto original_function, int id, const char* action) {
std::cout << "do_something(" << id << ", " << action << ") called..\n";
original_function(id, action);
});
```- Replace return value
```c++
bool check_license() { /* body */ }rcmp::hook_function<&check_license>([](auto /* original_function */) {
return true;
});
```- 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)>([](auto original, auto... args) {
print("args are: ", args...);
return original(args...);
});
```- Function address can be known at runtime or compile-time
```c++
// compile-time address
rcmp::hook_function<0xDEADBEEF, int(int)>([](auto original, int arg) { ... });// runtime address (i.e. from GetProcAddress/dlopen)
rcmp::hook_function(0xDEADBEEF, [](auto original, int arg) { ... });
```- Calling convention support
```c++
/// x86, the following calls are synonyms// good:
rcmp::hook_function(...); // 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)// bad, compiler-specific
rcmp::hook_function(...); // MSVC
rcmp::hook_function(...); // gcc/clang// x86 supported conventions
rcmp::cdecl_t // same as rcmp::generic_signature_t
rcmp::stdcall_t // same as rcmp::generic_signature_t
rcmp::thiscall_t // same as rcmp::generic_signature_t
rcmp::fastcall_t // same as rcmp::generic_signature_t// x64
rcmp::hook_function(...); // default convention
rcmp::hook_function(...); // default convention, but more letters
rcmp::hook_function>(...); // explicit convention```
- VTable hooking (`hook_indirect_function`)
```c++
// Let's assume:
// 5 - index of function in vtable
// int A::f(int) - function signature
using signature_t = rcmp::thiscall_t; // x86, MSVC
using signature_t = rcmp::cdecl_t; // x86, gcc/clang
using signature_t = int(A*, int); // x64// vtable address can be known at compile-time (0xDEADBEEF)
rcmp::hook_indirect_function<0xDEADBEEF + 5 * sizeof(void*), signature_t>([](auto original, A* self, int arg) { ... });// ..or at runtime
rcmp::hook_indirect_function(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](https://ru.wikipedia.org/wiki/Don%E2%80%99t_repeat_yourself)).
- 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](https://beatsaberquest.com/bmbf/bmbf-mods/bmbf-mods/) 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](https://github.com/Nomade040/nmd) by [Nomade040](https://github.com/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
- [catch2](https://github.com/catchorg/Catch2) for unit-testing
- [nmd library](https://github.com/Nomade040/nmd) for x86/x86-64 length disassembly
- [x86](http://ref.x86asm.net/coder32.html) and [x86-64](http://ref.x86asm.net/coder64.html) opcode and instruction reference## License
- MIT