What to do with references?
I might be stupid, but this code doesn't work:
class UFunction : ... {
public:
// Note the reference here
void Invoke(UObject* obj, FFrame& stack, void* Z_Param__Result);
};
// Results with an address
#define GET_ADDR(...) ...
kthook::kthook_simple<decltype(&UFunction::Invoke)> FuncInvokeHook;
static void Foo() {
FuncInvokeHook.set_dest(GET_ADDR("?Invoke@UFunction@@QEAAXPEAVUObject@@AEAUFFrame@@QEAX@Z"));
FuncInvokeHook.set_cb(
// !!! The problem is here. Neither "FFrame*" nor "FFrame&" doesn't work
[](auto& hk, UFunction* _this, UObject* obj, FFrame* stack, void* Z_Param__Result) {
hk.get_trampoline()(_this, obj, *stack, Z_Param__Result);
}
);
}
When trying to compile "FFrame*":
In file included from D:\votv-mp\Source\Client\Main.cpp:4:
In file included from D:\votv-mp\build\Debug\_deps\kthook-src\include\kthook/kthook.hpp:54:
D:\votv-mp\build\Debug\_deps\kthook-src\include\kthook\x86_64/kthook_x86_64_detail.hpp(502,16): error: no matching function for call to object of type 'std::function<void (const kthook::kthook_simple<void (UFunction::*)(UObject *, FFrame &, void *)> &, UFunction *&, UObject *&, FFrame *&, void *&)>'
502 | return cb(*this_hook, args...);
| ^~
D:\votv-mp\build\Debug\_deps\kthook-src\include\kthook\x64/kthook_detail.hpp(157,16): note: in instantiation of function template specialization 'kthook::detail::common_relay<std::function<void (const kthook::kthook_simple<void (UFunction::*)(UObject *, FFrame &, void *)> &, UFunction *&, UObject *&, FFrame *&, void *&)> &, kthook::kthook_simple<void (UFunction::*)(UObject *, FFrame &, void *)>, void, UFunction *, UObject *, FFrame &, void *>' requested here
157 | return common_relay<decltype(cb), HookType, Ret, Args...>(cb, this_hook, head_args..., tail_args...);
| ^
D:\votv-mp\build\Debug\_deps\kthook-src\include\kthook\x64/kthook_impl.hpp(545,112): note: in instantiation of member function 'kthook::detail::common_relay_generator<kthook::kthook_simple<void (UFunction::*)(UObject *, FFrame &, void *)>, void, std::tuple<UFunction *, UObject *, FFrame &, void *>, std::tuple<>, std::tuple<UFunction *, UObject *, FFrame &, void *>>::relay' requested here
545 | reinterpret_cast<void*>(&detail::common_relay_generator<kthook_simple, Ret, head, tail, Args>::relay);
| ^
D:\votv-mp\build\Debug\_deps\kthook-src\include\kthook\x64/kthook_impl.hpp(635,36): note: in instantiation of member function 'kthook::kthook_simple<void (UFunction::*)(UObject *, FFrame &, void *)>::generate_relay_jump' requested here
635 | this->relay_jump = generate_relay_jump();
| ^
D:\votv-mp\build\Debug\_deps\kthook-src\include\kthook\x64/kthook_impl.hpp(409,22): note: in instantiation of member function 'kthook::kthook_simple<void (UFunction::*)(UObject *, FFrame &, void *)>::patch_hook' requested here
409 | installed = !patch_hook(false);
| ^
D:\votv-mp\build\Debug\_deps\kthook-src\include\kthook\x64/kthook_impl.hpp(392,24): note: in instantiation of member function 'kthook::kthook_simple<void (UFunction::*)(UObject *, FFrame &, void *)>::remove' requested here
392 | ~kthook_simple() { remove(); }
| ^
D:\votv-mp\Source\Client\Main.cpp(9,53): note: in instantiation of member function 'kthook::kthook_simple<void (UFunction::*)(UObject *, FFrame &, void *)>::~kthook_simple' requested here
9 | kthook::kthook_simple<decltype(&UFunction::Invoke)> FuncInvokeHook;
| ^
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\functional(921,10): note: candidate function not viable: no known conversion from 'FFrame' to 'FFrame *&' for 4th argument
921 | _Ret operator()(_Types... _Args) const {
| ^ ~~~~~~~~~~~~~~~
When trying to compile with "FFrame&":
D:\votv-mp\Source\Client\Main.cpp(43,5): error: no viable conversion from '(lambda at D:\votv-mp\Source\Client\Main.cpp:43:5)' to 'cb_type' (aka 'function<void (const kthook::kthook_simple<void (UFunction::*)(UObject *, FFrame &, void *), kthook::kNone> &, UFunction *&, UObject *&, FFrame *&, void *&)>')
43 | [](auto& hk, UFunction* _this, UObject* obj, FFrame& stack, void* Z_Param__Result) {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
44 | hk.get_trampoline()(_this, obj, stack, Z_Param__Result);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
45 | }
| ~
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\functional(1096,5): note: candidate constructor not viable: no known conversion from '(lambda at D:\votv-mp\Source\Client\Main.cpp:43:5)' to 'nullptr_t' (aka 'std::nullptr_t') for 1st argument
1096 | function(nullptr_t) noexcept {}
| ^ ~~~~~~~~~
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\functional(1098,5): note: candidate constructor not viable: no known conversion from '(lambda at D:\votv-mp\Source\Client\Main.cpp:43:5)' to 'const function<void (const kthook_simple<void (UFunction::*)(UObject *, FFrame &, void *)> &, UFunction *&, UObject *&, FFrame *&, void *&)> &' for 1st argument
1098 | function(const function& _Right) {
| ^ ~~~~~~~~~~~~~~~~~~~~~~
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\functional(1149,5): note: candidate constructor not viable: no known conversion from '(lambda at D:\votv-mp\Source\Client\Main.cpp:43:5)' to 'function<void (const kthook_simple<void (UFunction::*)(UObject *, FFrame &, void *)> &, UFunction *&, UObject *&, FFrame *&, void *&)> &&' for 1st argument
1149 | function(function&& _Right) noexcept {
| ^ ~~~~~~~~~~~~~~~~~
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\functional(1103,5): note: candidate template ignored: requirement 'conjunction_v<std::negation<std::is_same<(lambda at D:\votv-mp\Source\Client\Main.cpp:43:5), std::function<void (const kthook::kthook_simple<void (UFunction::*)(UObject *, FFrame &, void *), kthook::kNone> &, UFunction *&, UObject *&, FFrame *&, void *&)>>>, std::_Is_invocable_r<void, (lambda at D:\votv-mp\Source\Client\Main.cpp:43:5) &, const kthook::kthook_simple<void (UFunction::*)(UObject *, FFrame &, void *), kthook::kNone> &, UFunction *&, UObject *&, FFrame *&, void *&>>' was not satisfied [with _Fx = (lambda at D:\votv-mp\Source\Client\Main.cpp:43:5)]
1103 | function(_Fx&& _Func) {
| ^
D:\votv-mp\Source\Client\Main.cpp(43,5): note: candidate template ignored: could not match 'auto (*)(type-parameter-0-0 &, UFunction *, UObject *, FFrame &, void *)' against 'std::function<void (const kthook::kthook_simple<void (UFunction::*)(UObject *, FFrame &, void *)> &, UFunction *&, UObject *&, FFrame *&, void *&)>'
43 | [](auto& hk, UFunction* _this, UObject* obj, FFrame& stack, void* Z_Param__Result) {
| ^
D:\votv-mp\build\Debug\_deps\kthook-src\include\kthook\x64/kthook_impl.hpp(425,25): note: passing argument to parameter 'callback_' here
425 | void set_cb(cb_type callback_) { callback = std::move(callback_); }
| ^
In file included from D:\votv-mp\Source\Client\Main.cpp:4:
In file included from D:\votv-mp\build\Debug\_deps\kthook-src\include\kthook/kthook.hpp:54:
D:\votv-mp\build\Debug\_deps\kthook-src\include\kthook\x86_64/kthook_x86_64_detail.hpp(502,16): error: no matching function for call to object of type 'std::function<void (const kthook::kthook_simple<void (UFunction::*)(UObject *, FFrame &, void *)> &, UFunction *&, UObject *&, FFrame *&, void *&)>'
502 | return cb(*this_hook, args...);
| ^~
D:\votv-mp\build\Debug\_deps\kthook-src\include\kthook\x64/kthook_detail.hpp(157,16): note: in instantiation of function template specialization 'kthook::detail::common_relay<std::function<void (const kthook::kthook_simple<void (UFunction::*)(UObject *, FFrame &, void *)> &, UFunction *&, UObject *&, FFrame *&, void *&)> &, kthook::kthook_simple<void (UFunction::*)(UObject *, FFrame &, void *)>, void, UFunction *, UObject *, FFrame &, void *>' requested here
157 | return common_relay<decltype(cb), HookType, Ret, Args...>(cb, this_hook, head_args..., tail_args...);
| ^
D:\votv-mp\build\Debug\_deps\kthook-src\include\kthook\x64/kthook_impl.hpp(545,112): note: in instantiation of member function 'kthook::detail::common_relay_generator<kthook::kthook_simple<void (UFunction::*)(UObject *, FFrame &, void *)>, void, std::tuple<UFunction *, UObject *, FFrame &, void *>, std::tuple<>, std::tuple<UFunction *, UObject *, FFrame &, void *>>::relay' requested here
545 | reinterpret_cast<void*>(&detail::common_relay_generator<kthook_simple, Ret, head, tail, Args>::relay);
| ^
D:\votv-mp\build\Debug\_deps\kthook-src\include\kthook\x64/kthook_impl.hpp(635,36): note: in instantiation of member function 'kthook::kthook_simple<void (UFunction::*)(UObject *, FFrame &, void *)>::generate_relay_jump' requested here
635 | this->relay_jump = generate_relay_jump();
| ^
D:\votv-mp\build\Debug\_deps\kthook-src\include\kthook\x64/kthook_impl.hpp(409,22): note: in instantiation of member function 'kthook::kthook_simple<void (UFunction::*)(UObject *, FFrame &, void *)>::patch_hook' requested here
409 | installed = !patch_hook(false);
| ^
D:\votv-mp\build\Debug\_deps\kthook-src\include\kthook\x64/kthook_impl.hpp(392,24): note: in instantiation of member function 'kthook::kthook_simple<void (UFunction::*)(UObject *, FFrame &, void *)>::remove' requested here
392 | ~kthook_simple() { remove(); }
| ^
D:\votv-mp\Source\Client\Main.cpp(9,53): note: in instantiation of member function 'kthook::kthook_simple<void (UFunction::*)(UObject *, FFrame &, void *)>::~kthook_simple' requested here
9 | kthook::kthook_simple<decltype(&UFunction::Invoke)> FuncInvokeHook;
| ^
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include\functional(921,10): note: candidate function not viable: no known conversion from 'FFrame' to 'FFrame *&' for 4th argument
921 | _Ret operator()(_Types... _Args) const {
I assume there's some issue with reference-to-pointer conversion:
// Here
using converted_args = typename detail::traits::add_refs_t<detail::traits::convert_refs_t<Args>>;
Or I can just do things wrong
Seems like issue is present only for kthook_simple.
Simple hook generates relay with NOT converted args, but it should pass converted args instead
Seems like even kthook_signal has issues with references. Same code as above but with kthook_signal and even without any callback just crashes the game.
I could pass pointer instead of reference to hook type, but with current architecture this will be nasty crutch
Here's function assembly if it can help:
.text:00000000012E7680 ; __int64 __fastcall UFunction::Invoke(UFunction *this, struct UObject *InterfaceAddress, struct FFrame *, void *const)
.text:00000000012E7680 ?Invoke@UFunction@@QEAAXPEAVUObject@@AEAUFFrame@@QEAX@Z proc near
.text:00000000012E7680 ; CODE XREF: sub_143BC50+20D↓p
.text:00000000012E7680 ; sub_144A1C0+2E0↓p ...
.text:00000000012E7680
.text:00000000012E7680 arg_0 = qword ptr 8
.text:00000000012E7680 arg_8 = qword ptr 10h
.text:00000000012E7680 arg_10 = qword ptr 18h
.text:00000000012E7680 arg_18 = qword ptr 20h
.text:00000000012E7680
.text:00000000012E7680 mov [rsp+arg_0], rbx
.text:00000000012E7685 mov [rsp+arg_8], rbp
.text:00000000012E768A mov [rsp+arg_10], rsi
.text:00000000012E768F mov [rsp+arg_18], rdi
.text:00000000012E7694 push r14
.text:00000000012E7696 sub rsp, 20h
.text:00000000012E769A mov rbx, [rcx+20h]
.text:00000000012E769E mov r14, r9
.text:00000000012E76A1 mov rdi, r8
.text:00000000012E76A4 mov rsi, rdx
.text:00000000012E76A7 mov rbp, rcx
.text:00000000012E76AA call ?GetPrivateStaticClass@UInterface@@CAPEAVUClass@@XZ ; UInterface::GetPrivateStaticClass(void)
.text:00000000012E76AF test rax, rax
.text:00000000012E76B2 jz short loc_12E76DC
.text:00000000012E76B4 lea rdx, [rax+30h]
.text:00000000012E76B8 movsxd rax, dword ptr [rax+38h]
.text:00000000012E76BC cmp eax, [rbx+38h]
.text:00000000012E76BF jg short loc_12E76DC
.text:00000000012E76C1 mov r8, rax
.text:00000000012E76C4 mov rax, [rbx+30h]
.text:00000000012E76C8 cmp [rax+r8*8], rdx
.text:00000000012E76CC jnz short loc_12E76DC
.text:00000000012E76CE mov rdx, rbx
.text:00000000012E76D1 mov rcx, rsi
.text:00000000012E76D4 call ?GetInterfaceAddress@UObjectBaseUtility@@QEAAPEAXPEAVUClass@@@Z ; UObjectBaseUtility::GetInterfaceAddress(UClass *)
.text:00000000012E76D9 mov rsi, rax
.text:00000000012E76DC
.text:00000000012E76DC loc_12E76DC: ; CODE XREF: UFunction::Invoke(UObject *,FFrame &,void * const)+32↑j
.text:00000000012E76DC ; UFunction::Invoke(UObject *,FFrame &,void * const)+3F↑j ...
.text:00000000012E76DC mov rbx, [rdi+88h]
.text:00000000012E76E3 mov r8, r14
.text:00000000012E76E6 mov rdx, rdi
.text:00000000012E76E9 mov [rdi+88h], rbp
.text:00000000012E76F0 mov rcx, rsi
.text:00000000012E76F3 call qword ptr [rbp+0D8h]
.text:00000000012E76F9 mov rbp, [rsp+28h+arg_8]
.text:00000000012E76FE mov rsi, [rsp+28h+arg_10]
.text:00000000012E7703 mov [rdi+88h], rbx
.text:00000000012E770A mov rbx, [rsp+28h+arg_0]
.text:00000000012E770F mov rdi, [rsp+28h+arg_18]
.text:00000000012E7714 add rsp, 20h
.text:00000000012E7718 pop r14
.text:00000000012E771A retn
.text:00000000012E771A ?Invoke@UFunction@@QEAAXPEAVUObject@@AEAUFFrame@@QEAX@Z endp
Using references is not well tested, but should be working. Ty for MRE, i’ll look into your issue today
All references are converted to pointers, so try this:
class UFunction : ... {
public:
// Note the reference here
void Invoke(UObject* obj, FFrame& stack, void* Z_Param__Result);
};
// Results with an address
#define GET_ADDR(...) ...
kthook::kthook_simple<decltype(&UFunction::Invoke)> FuncInvokeHook;
static void Foo() {
FuncInvokeHook.set_dest(GET_ADDR("?Invoke@UFunction@@QEAAXPEAVUObject@@AEAUFFrame@@QEAX@Z"));
FuncInvokeHook.set_cb(
// !!! The problem is here. Neither "FFrame*" nor "FFrame&" doesn't work
[](auto& hk, UFunction* _this, UObject* obj, FFrame* stack, void* Z_Param__Result) {
hk.get_trampoline()(_this, obj, stack, Z_Param__Result);
}
);
}
Вы вроде русский, так что для лучшего понимания буду на русском
Проблема в том, что по какой-то причине вроде kthook не думает, что эта функция берёт параметры через регистры (судя по сгенерированному relay jump):
Еще больше это подтверждает то, что для другой функции всё работает исправно (структура InitializationValues всего 8 байт, следовательно помещается в регистр):
Причём такая ошибка с первой функцией не уходит, даже если заменить ссылку на указатель у FFrame.
Скорее всего корень ошибки где-то здесь
Скорее всего проблема именно из-за ровно 4-х аргументов. Из-за чего конкретно в коде я понять не смог, но нужно копать в этом направлении
Вероятнее всего ты допустил ошибку с выравниванием стека или shadow space (32 байта после аргументов на стеке), потому что пока я делал свою реализацию именно эти два момента доставили проблем.
Но я смог сделать небольшой работающий аналог, который не крашит. Ниже приложу архив с кодом, там всего 5 файлов. Частично брал твой код (конкретно для генерации трамплинов и аллокации памяти).
https://disk.yandex.ru/d/su00sQ33N45rQA