X86 hooking library with functor callbacks support, so you can use lambdas with state, std::bind values etc...
Clone repository and simply include kthook.hpp. C++17 compatible compiler required
Callbacks backend is ktsignal
All hooks are automatically removed in the kthook
destructor
All examples are shown based on this function
int FASTCALL func1(float a, float b) {
print_info(a, b);
a = 50; b = 100;
return 5;
}
Creating hook and binding callback
All callbacks are given a reference to kthook as the first argument. You can get return address and trampoline pointer from hook object.
Hooks are installed after construction by default(last parameter can be set to false to prevent this)
int main() {
// func_ptr is pointer to function
auto func_ptr = &func1;
// func_type is int(CFASTCALL*)(float, float)
using func_type = decltype(&func1);
// Creating simple hook object with function type is template parameter and function pointer in constructor
kthook::kthook_simple<func_type> hook{ func_ptr };
// Connecting lambda callback that receiving function arguments by references
hook.before += [](const auto& hook, float& a, float& b) {
print_info(a, b);
return std::nullopt;
};
/*
[operator () at 31]: a = 30; b = 20
[func1 at 16 ]: a = 30; b = 20
*/
func1(30.f, 20.f);
}
Same thing with common hooks:
int main() {
// func_ptr is pointer to function
auto func_ptr = &func1;
// func_type is int(CFASTCALL*)(float, float)
using func_type = decltype(&func1);
auto cb = [](const auto& hook, float a, float b) {
print_info(a, b);
return hook.get_trampoline()(a, b);
};
// Creating simple hook object with function type is template parameter and function pointer in constructor
kthook::kthook_simple<func_type> hook{ func_ptr, cb };
/*
[operator () at 31]: a = 30; b = 20
[func1 at 16 ]: a = 30; b = 20
*/
func1(30.f, 20.f);
}
Also you can bind after original function execution callbacks
If original function return value is non void, return value reference passed at 2 argument
int main() {
auto func_ptr = &func1;
using func_type = decltype(&func1);
kthook::kthook_simple_t<func_type> hook{ func_ptr };
hook.before.connect([](const auto& hook, float& a, float& b) {
print_info(a, b);
// changing arguments
a = 50.f, b = 30.f;
return std::nullopt;
});
// connect after callback
hook.after.connect([](const auto& hook, int& return_value, float& a, float& b) {
print_info(a, b);
print_return_value(return_value);
// changing return_value
return_value = 20;
});
/*
[operator () at 31]: a = 30; b = 20
[func1 at 16 ]: a = 50; b = 30
[operator () at 34]: a = 50; b = 30
[operator () at 34]: return_value = 5;
[main at 20]: return_value = 20;
*/
auto ret_val = func1(30.f, 20.f);
print_return_value(ret_val)
}
You can bind multiple before/after callbacks
int main() {
auto func_ptr = &func1;
using func_type = decltype(&func1);
kthook::kthook_simple_t<func_type> hook{ func_ptr };
hook.before.connect([](const auto& hook, float& a, float& b) { print_info(a, b); return std::nullopt; });
hook.before.connect([](const auto& hook, float& a, float& b) { a = 20; b = 30; return std::nullopt; });
hook.after.connect([](const auto& hook, int& ret_val, float& a, float& b) { print_info(a, b); });
hook.after.connect([](const auto& hook, int& ret_val, float& a, float& b) { print_info(a, b); });
/*
[operator () at 31]: a = 0; b = 0
[func1 at 16]: a = 20; b = 30
[operator () at 33]: a = 20; b = 30
[operator () at 34]: a = 20; b = 30
[main at 20]: return_value = 5;
*/
auto ret_val = func1(30.f, 20.f);
print_return_value(ret_val)
}
- Function return type must be default-constructible
- If any before callback wiil return false, and function return type is non void, the original function and after callback are not called. Default constructed value is returned
- If all before callbacks will return true, original function and after callbacks will be called
There is a kthook that allows you to change the return value from a function without calling the original function
For generating true return value, you can use std::make_optional(value)
function
For generating false return value, you can use std::nullopt
If function return type is void, then you can just return true/false see notes
int main() {
auto func_ptr = &func1;
using func_type = decltype(&func1);
kthook::kthook_signal<func_type> hook{ func_ptr };
hook.before.connect([](const auto& hook, float& a, float& b) { print_info(a, b); return std::nullopt; });
hook.after.connect([](const auto& hook, int& ret_val, float& a, float& b) { ret_val = 20; print_info(a, b); });
/*
[operator () at 44]: a = 30; b = 20
[func1 at 16]: a = 30; b = 20
[operator () at 45]: a = 30; b = 20
[main at 20]: return_value = 20;
*/
auto ret_val = func1(30.f, 20.f);
print_return_value(ret_val)
}
Return false example
int main() {
auto func_ptr = &func1;
using func_type = decltype(&func1);
kthook::kthook_signal<func_type> hook{ func_ptr };
hook.before.connect([](auto& hook, float& a, float& b) { print_info(a, b); return std::make_optional(20); });
hook.after.connect([](auto& hook, int& ret_val, float& a, float& b) { ret_val = 20; print_info(a, b); });
/*
[operator () at 44]: a = 30; b = 20
[main at 20]: return_value = 20;
*/
auto ret_val = func1(30.f, 20.f);
print_return_value(ret_val)
}
More examples can be found here
xbyak - x86/x86-64 JIT assembler
ktsignal - C++17 signals library
kthook is licensed under the MIT License, see LICENSE.txt for more information.