mirror of
https://github.com/hyprwm/Hyprland
synced 2025-12-20 07:30:05 +01:00
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
This commit is contained in:
parent
c3747fab56
commit
5a20862126
2 changed files with 55 additions and 43 deletions
|
|
@ -106,7 +106,7 @@ CFunctionHook::SAssembly CFunctionHook::fixInstructionProbeRIPCalls(const SInstr
|
||||||
if (ADDREND == std::string::npos || ADDRSTART == std::string::npos)
|
if (ADDREND == std::string::npos || ADDRSTART == std::string::npos)
|
||||||
return {};
|
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;
|
const int32_t NEWRIPOFFSET = DESTINATION - PREDICTEDRIP;
|
||||||
|
|
||||||
size_t ripOffset = 0;
|
size_t ripOffset = 0;
|
||||||
|
|
@ -144,25 +144,25 @@ bool CFunctionHook::hook() {
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// movabs $0,%rax | jmpq *%rax
|
// jmp rel32
|
||||||
// offset for addr: 2
|
// 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 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;
|
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
|
// nop
|
||||||
static constexpr uint8_t NOP = 0x90;
|
static constexpr uint8_t NOP = 0x90;
|
||||||
|
|
||||||
// alloc trampoline
|
// alloc trampolines
|
||||||
const auto MAX_TRAMPOLINE_SIZE = HOOK_TRAMPOLINE_MAX_SIZE; // we will never need more.
|
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
|
// probe instructions to be trampolin'd
|
||||||
SInstructionProbe probe;
|
SInstructionProbe probe;
|
||||||
try {
|
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; }
|
} catch (std::exception& e) { return false; }
|
||||||
|
|
||||||
const auto PROBEFIXEDASM = fixInstructionProbeRIPCalls(probe);
|
const auto PROBEFIXEDASM = fixInstructionProbeRIPCalls(probe);
|
||||||
|
|
@ -172,10 +172,15 @@ bool CFunctionHook::hook() {
|
||||||
return false;
|
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 HOOKSIZE = PROBEFIXEDASM.bytes.size();
|
||||||
const size_t ORIGSIZE = probe.len;
|
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) {
|
if (TRAMPOLINE_SIZE > MAX_TRAMPOLINE_SIZE) {
|
||||||
Debug::log(ERR, "[functionhook] failed, not enough space in trampo to alloc:\n{}", probe.assembly);
|
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);
|
m_originalBytes.resize(ORIGSIZE);
|
||||||
memcpy(m_originalBytes.data(), m_source, ORIGSIZE);
|
memcpy(m_originalBytes.data(), m_source, ORIGSIZE);
|
||||||
|
|
||||||
// populate trampoline
|
// populate land trampoline
|
||||||
memcpy(m_trampolineAddr, PROBEFIXEDASM.bytes.data(), HOOKSIZE); // first, original but fixed func bytes
|
memcpy(m_landTrampolineAddr, 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_landTrampolineAddr) + HOOKSIZE, RELATIVE_JMP_ADDRESS, sizeof(RELATIVE_JMP_ADDRESS)); // then, jump to source
|
||||||
memcpy(sc<uint8_t*>(m_trampolineAddr) + HOOKSIZE + sizeof(PUSH_RAX), ABSOLUTE_JMP_ADDRESS, sizeof(ABSOLUTE_JMP_ADDRESS)); // then, jump to source
|
|
||||||
|
|
||||||
// fixup trampoline addr
|
// populate short jump addr
|
||||||
*rc<uint64_t*>(sc<uint8_t*>(m_trampolineAddr) + TRAMPOLINE_SIZE - sizeof(ABSOLUTE_JMP_ADDRESS) + ABSOLUTE_JMP_ADDRESS_OFFSET) =
|
*rc<int32_t*>(sc<uint8_t*>(m_landTrampolineAddr) + TRAMPOLINE_SIZE - sizeof(RELATIVE_JMP_ADDRESS) + RELATIVE_JMP_ADDRESS_OFFSET) =
|
||||||
rc<uint64_t>(sc<uint8_t*>(m_source) + sizeof(ABSOLUTE_JMP_ADDRESS));
|
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)
|
||||||
|
);
|
||||||
|
|
||||||
// make jump to hk
|
// populate launch trampoline
|
||||||
|
memcpy(m_launchTrampolineAddr, ABSOLUTE_JMP_ADDRESS, sizeof(ABSOLUTE_JMP_ADDRESS)); // long jump to our hk
|
||||||
|
|
||||||
|
// 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 short jump to launch trampoile
|
||||||
const auto PAGESIZE_VAR = sysconf(_SC_PAGE_SIZE);
|
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 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;
|
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);
|
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
|
size_t currentOp = sizeof(RELATIVE_JMP_ADDRESS);
|
||||||
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);
|
|
||||||
memset(sc<uint8_t*>(m_source) + currentOp, NOP, ORIGSIZE - currentOp);
|
memset(sc<uint8_t*>(m_source) + currentOp, NOP, ORIGSIZE - currentOp);
|
||||||
|
|
||||||
// fixup jump addr
|
// populate short jump addr
|
||||||
*rc<uint64_t*>(sc<uint8_t*>(m_source) + ABSOLUTE_JMP_ADDRESS_OFFSET) = rc<uint64_t>(m_destination);
|
*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
|
// revert mprot
|
||||||
mprotect(const_cast<uint8_t*>(PROTSTART), PROTLEN, PROT_READ | PROT_EXEC);
|
mprotect(const_cast<uint8_t*>(PROTSTART), PROTLEN, PROT_READ | PROT_EXEC);
|
||||||
|
|
||||||
// set original addr to trampo addr
|
// set original addr to land trampo addr
|
||||||
m_original = m_trampolineAddr;
|
m_original = m_landTrampolineAddr;
|
||||||
|
|
||||||
m_active = true;
|
m_active = true;
|
||||||
m_hookLen = ORIGSIZE;
|
m_hookLen = ORIGSIZE;
|
||||||
m_trampoLen = TRAMPOLINE_SIZE;
|
|
||||||
|
|
||||||
return true;
|
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);
|
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
|
// reset vars
|
||||||
m_active = false;
|
m_active = false;
|
||||||
m_hookLen = 0;
|
m_hookLen = 0;
|
||||||
m_trampoLen = 0;
|
m_landTrampolineAddr = nullptr; // no unmapping, it's managed by the HookSystem
|
||||||
m_trampolineAddr = nullptr; // no unmapping, it's managed by the HookSystem
|
m_launchTrampolineAddr = nullptr; // no unmapping, it's managed by the HookSystem
|
||||||
m_original = nullptr;
|
m_original = nullptr;
|
||||||
m_originalBytes.clear();
|
m_originalBytes.clear();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
#include "../helpers/memory/Memory.hpp"
|
#include "../helpers/memory/Memory.hpp"
|
||||||
|
|
||||||
#define HANDLE void*
|
#define HANDLE void*
|
||||||
#define HOOK_TRAMPOLINE_MAX_SIZE 64
|
#define HOOK_TRAMPOLINE_MAX_SIZE 32
|
||||||
|
|
||||||
class CFunctionHook {
|
class CFunctionHook {
|
||||||
public:
|
public:
|
||||||
|
|
@ -24,13 +24,13 @@ class CFunctionHook {
|
||||||
void* m_original = nullptr;
|
void* m_original = nullptr;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void* m_source = nullptr;
|
void* m_source = nullptr;
|
||||||
void* m_trampolineAddr = nullptr;
|
void* m_launchTrampolineAddr = nullptr;
|
||||||
void* m_destination = nullptr;
|
void* m_landTrampolineAddr = nullptr;
|
||||||
size_t m_hookLen = 0;
|
void* m_destination = nullptr;
|
||||||
size_t m_trampoLen = 0;
|
size_t m_hookLen = 0;
|
||||||
HANDLE m_owner = nullptr;
|
HANDLE m_owner = nullptr;
|
||||||
bool m_active = false;
|
bool m_active = false;
|
||||||
|
|
||||||
std::vector<unsigned char> m_originalBytes;
|
std::vector<unsigned char> m_originalBytes;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue