summaryrefslogtreecommitdiff
path: root/src/plugins
diff options
context:
space:
mode:
authorVaxry <vaxry@vaxry.net>2025-10-07 12:37:21 +0100
committerVaxry <vaxry@vaxry.net>2025-10-07 12:49:36 +0100
commit5a208621264cff4d4fe6f1d5f018b28ca43e88bd (patch)
tree5e3dd6356fe879ad2a8015c87d530f74a217328e /src/plugins
parentc3747fab5615ede222559a00501160773b82a2c2 (diff)
hookSystem: use a full trampo setup for hooks
instead of planting a longjmp at the fn head, make a shortjmp to a launch trampo this helps with shortjmps that can be in the fn and will break when relocated. They are very unlikely to occur within the first 5 bytes (jmp rel32) but can happen in the first 10 or so (longjmp) fixes csgo-vk-fix on latest main with release building on gcc / clang
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/HookSystem.cpp82
-rw-r--r--src/plugins/HookSystem.hpp16
2 files changed, 55 insertions, 43 deletions
diff --git a/src/plugins/HookSystem.cpp b/src/plugins/HookSystem.cpp
index 242b8d30..31124d4f 100644
--- a/src/plugins/HookSystem.cpp
+++ b/src/plugins/HookSystem.cpp
@@ -106,7 +106,7 @@ CFunctionHook::SAssembly CFunctionHook::fixInstructionProbeRIPCalls(const SInstr
if (ADDREND == std::string::npos || ADDRSTART == std::string::npos)
return {};
- const uint64_t PREDICTEDRIP = rc<uint64_t>(m_trampolineAddr) + currentDestinationOffset + len;
+ const uint64_t PREDICTEDRIP = rc<uint64_t>(m_landTrampolineAddr) + currentDestinationOffset + len;
const int32_t NEWRIPOFFSET = DESTINATION - PREDICTEDRIP;
size_t ripOffset = 0;
@@ -144,25 +144,25 @@ bool CFunctionHook::hook() {
return false;
#endif
- // movabs $0,%rax | jmpq *%rax
- // offset for addr: 2
+ // jmp rel32
+ // offset for relative addr: 1
+ static constexpr uint8_t RELATIVE_JMP_ADDRESS[] = {0xE9, 0x00, 0x00, 0x00, 0x00};
+ static constexpr size_t RELATIVE_JMP_ADDRESS_OFFSET = 1;
+ // movabs $0,%rax | jmpq *rax
static constexpr uint8_t ABSOLUTE_JMP_ADDRESS[] = {0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0};
static constexpr size_t ABSOLUTE_JMP_ADDRESS_OFFSET = 2;
- // pushq %rax
- static constexpr uint8_t PUSH_RAX[] = {0x50};
- // popq %rax
- static constexpr uint8_t POP_RAX[] = {0x58};
// nop
static constexpr uint8_t NOP = 0x90;
- // alloc trampoline
+ // alloc trampolines
const auto MAX_TRAMPOLINE_SIZE = HOOK_TRAMPOLINE_MAX_SIZE; // we will never need more.
- m_trampolineAddr = rc<void*>(g_pFunctionHookSystem->getAddressForTrampo());
+ m_launchTrampolineAddr = rc<void*>(g_pFunctionHookSystem->getAddressForTrampo());
+ m_landTrampolineAddr = rc<void*>(g_pFunctionHookSystem->getAddressForTrampo());
// probe instructions to be trampolin'd
SInstructionProbe probe;
try {
- probe = probeMinimumJumpSize(m_source, sizeof(ABSOLUTE_JMP_ADDRESS) + sizeof(PUSH_RAX) + sizeof(POP_RAX));
+ probe = probeMinimumJumpSize(m_source, sizeof(RELATIVE_JMP_ADDRESS));
} catch (std::exception& e) { return false; }
const auto PROBEFIXEDASM = fixInstructionProbeRIPCalls(probe);
@@ -172,10 +172,15 @@ bool CFunctionHook::hook() {
return false;
}
+ if (rc<int64_t>(m_source) - rc<int64_t>(m_destination) > 2000000000 /* 2 GB */) {
+ Debug::log(ERR, "[functionhook] failed, source and dest are over 2GB apart");
+ return false;
+ }
+
const size_t HOOKSIZE = PROBEFIXEDASM.bytes.size();
const size_t ORIGSIZE = probe.len;
- const auto TRAMPOLINE_SIZE = sizeof(ABSOLUTE_JMP_ADDRESS) + HOOKSIZE + sizeof(PUSH_RAX);
+ const auto TRAMPOLINE_SIZE = sizeof(RELATIVE_JMP_ADDRESS) + HOOKSIZE;
if (TRAMPOLINE_SIZE > MAX_TRAMPOLINE_SIZE) {
Debug::log(ERR, "[functionhook] failed, not enough space in trampo to alloc:\n{}", probe.assembly);
@@ -185,39 +190,46 @@ bool CFunctionHook::hook() {
m_originalBytes.resize(ORIGSIZE);
memcpy(m_originalBytes.data(), m_source, ORIGSIZE);
- // populate trampoline
- memcpy(m_trampolineAddr, PROBEFIXEDASM.bytes.data(), HOOKSIZE); // first, original but fixed func bytes
- memcpy(sc<uint8_t*>(m_trampolineAddr) + HOOKSIZE, PUSH_RAX, sizeof(PUSH_RAX)); // then, pushq %rax
- memcpy(sc<uint8_t*>(m_trampolineAddr) + HOOKSIZE + sizeof(PUSH_RAX), ABSOLUTE_JMP_ADDRESS, sizeof(ABSOLUTE_JMP_ADDRESS)); // then, jump to source
+ // populate land trampoline
+ memcpy(m_landTrampolineAddr, PROBEFIXEDASM.bytes.data(), HOOKSIZE); // first, original but fixed func bytes
+ memcpy(sc<uint8_t*>(m_landTrampolineAddr) + HOOKSIZE, RELATIVE_JMP_ADDRESS, sizeof(RELATIVE_JMP_ADDRESS)); // then, jump to source
+
+ // populate short jump addr
+ *rc<int32_t*>(sc<uint8_t*>(m_landTrampolineAddr) + TRAMPOLINE_SIZE - sizeof(RELATIVE_JMP_ADDRESS) + RELATIVE_JMP_ADDRESS_OFFSET) =
+ sc<int64_t>((sc<uint8_t*>(m_source) + probe.len) // jump to source + probe len (skip header)
+ - (sc<uint8_t*>(m_landTrampolineAddr) + TRAMPOLINE_SIZE) // from trampo + size - jmp (not - size because jmp is rel to rip after instr)
+ );
+
+ // populate launch trampoline
+ memcpy(m_launchTrampolineAddr, ABSOLUTE_JMP_ADDRESS, sizeof(ABSOLUTE_JMP_ADDRESS)); // long jump to our hk
- // fixup trampoline addr
- *rc<uint64_t*>(sc<uint8_t*>(m_trampolineAddr) + TRAMPOLINE_SIZE - sizeof(ABSOLUTE_JMP_ADDRESS) + ABSOLUTE_JMP_ADDRESS_OFFSET) =
- rc<uint64_t>(sc<uint8_t*>(m_source) + sizeof(ABSOLUTE_JMP_ADDRESS));
+ // populate long jump addr
+ *rc<uint64_t*>(sc<uint8_t*>(m_launchTrampolineAddr) + ABSOLUTE_JMP_ADDRESS_OFFSET) = rc<uint64_t>(m_destination); // long jump to hk fn
- // make jump to hk
+ // make short jump to launch trampoile
const auto PAGESIZE_VAR = sysconf(_SC_PAGE_SIZE);
const uint8_t* PROTSTART = sc<uint8_t*>(m_source) - (rc<uint64_t>(m_source) % PAGESIZE_VAR);
const size_t PROTLEN = std::ceil(sc<float>(ORIGSIZE + (rc<uint64_t>(m_source) - rc<uint64_t>(PROTSTART))) / sc<float>(PAGESIZE_VAR)) * PAGESIZE_VAR;
mprotect(const_cast<uint8_t*>(PROTSTART), PROTLEN, PROT_READ | PROT_WRITE | PROT_EXEC);
- memcpy(m_source, ABSOLUTE_JMP_ADDRESS, sizeof(ABSOLUTE_JMP_ADDRESS));
+ memcpy(m_source, RELATIVE_JMP_ADDRESS, sizeof(RELATIVE_JMP_ADDRESS));
- // make popq %rax and NOP all remaining
- memcpy(sc<uint8_t*>(m_source) + sizeof(ABSOLUTE_JMP_ADDRESS), POP_RAX, sizeof(POP_RAX));
- size_t currentOp = sizeof(ABSOLUTE_JMP_ADDRESS) + sizeof(POP_RAX);
+ size_t currentOp = sizeof(RELATIVE_JMP_ADDRESS);
memset(sc<uint8_t*>(m_source) + currentOp, NOP, ORIGSIZE - currentOp);
- // fixup jump addr
- *rc<uint64_t*>(sc<uint8_t*>(m_source) + ABSOLUTE_JMP_ADDRESS_OFFSET) = rc<uint64_t>(m_destination);
+ // populate short jump addr
+ *rc<int32_t*>(sc<uint8_t*>(m_source) + RELATIVE_JMP_ADDRESS_OFFSET) = sc<int32_t>( //
+ rc<uint64_t>(m_launchTrampolineAddr) // jump to the launch trampoline which jumps to hk
+ - (rc<uint64_t>(m_source) + 5) // from source
+ );
// revert mprot
mprotect(const_cast<uint8_t*>(PROTSTART), PROTLEN, PROT_READ | PROT_EXEC);
- // set original addr to trampo addr
- m_original = m_trampolineAddr;
+ // set original addr to land trampo addr
+ m_original = m_landTrampolineAddr;
- m_active = true;
- m_hookLen = ORIGSIZE;
- m_trampoLen = TRAMPOLINE_SIZE;
+ m_active = true;
+ m_hookLen = ORIGSIZE;
return true;
}
@@ -241,11 +253,11 @@ bool CFunctionHook::unhook() {
mprotect(sc<uint8_t*>(m_source) - rc<uint64_t>(m_source) % sysconf(_SC_PAGE_SIZE), sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_EXEC);
// reset vars
- m_active = false;
- m_hookLen = 0;
- m_trampoLen = 0;
- m_trampolineAddr = nullptr; // no unmapping, it's managed by the HookSystem
- m_original = nullptr;
+ m_active = false;
+ m_hookLen = 0;
+ m_landTrampolineAddr = nullptr; // no unmapping, it's managed by the HookSystem
+ m_launchTrampolineAddr = nullptr; // no unmapping, it's managed by the HookSystem
+ m_original = nullptr;
m_originalBytes.clear();
return true;
diff --git a/src/plugins/HookSystem.hpp b/src/plugins/HookSystem.hpp
index 95b4c972..cf098dd1 100644
--- a/src/plugins/HookSystem.hpp
+++ b/src/plugins/HookSystem.hpp
@@ -6,7 +6,7 @@
#include "../helpers/memory/Memory.hpp"
#define HANDLE void*
-#define HOOK_TRAMPOLINE_MAX_SIZE 64
+#define HOOK_TRAMPOLINE_MAX_SIZE 32
class CFunctionHook {
public:
@@ -24,13 +24,13 @@ class CFunctionHook {
void* m_original = nullptr;
private:
- void* m_source = nullptr;
- void* m_trampolineAddr = nullptr;
- void* m_destination = nullptr;
- size_t m_hookLen = 0;
- size_t m_trampoLen = 0;
- HANDLE m_owner = nullptr;
- bool m_active = false;
+ void* m_source = nullptr;
+ void* m_launchTrampolineAddr = nullptr;
+ void* m_landTrampolineAddr = nullptr;
+ void* m_destination = nullptr;
+ size_t m_hookLen = 0;
+ HANDLE m_owner = nullptr;
+ bool m_active = false;
std::vector<unsigned char> m_originalBytes;