From af370088fc178996a46ee508aaa1a2c46318bbf2 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Fri, 26 Dec 2025 23:12:11 +0000 Subject: Move to static trampolines and type-safe hooks For some reason the DLL got a tiny little bit bigger again but that's fine. This will make orig_ calls more efficient in the inline case, and also make it harder to screw up and hook the wrong thing by mistake. Self-explanatory-ish, apart from the fact it's a fairly large API change of course. And it relies on some more bonkers assembler directive hackery. But it works! The only complaint one might have is that the featsetup functions no longer take an explicit string which occasionally yields slightly less perfect error messages, but I've decided this isn't really a problem and makes the API nicer to use. It's a tradeoff, innit. --- src/hook.c | 74 +++++++++++++++++++++++++++----------------------------------- 1 file changed, 32 insertions(+), 42 deletions(-) (limited to 'src/hook.c') diff --git a/src/hook.c b/src/hook.c index baaa710..845d8b1 100644 --- a/src/hook.c +++ b/src/hook.c @@ -28,71 +28,61 @@ // Almost certainly breaks in some weird cases. Oh well! Most of the time, // vtable hooking is more reliable, this is only for, uh, emergencies. -#define SPACE 2048 // NOTE: MUST match SPACE in hook.S! -extern uchar _hook_trampolinespc[]; // defined in hook.S -static uchar *curtrampoline = _hook_trampolinespc; - -struct hook_inline_prep_ret hook_inline_prep(void *func, void **trampoline) { - uchar *p = func; - // dumb hack: if we hit some thunk that immediately jumps elsewhere (which +uchar *_hook_getpos(uchar *func) { + // if we are hooking some thunk that immediately jumps elsewhere (which // seems common for win32 API functions), hook the underlying thing instead. - // later: that dumb hack has now ended up having implications in the - // redesign of the entire API. :-) - while (*p == X86_JMPIW) p += mem_loads32(p + 1) + 5; - void *prologue = p; + // we return this back as the thing that actually ends up getting hooked + // after memory protections are changed. + while (*func == X86_JMPIW) func += mem_loads32(func + 1) + 5; + return func; +} + +struct _hook_prep_ret _hook_prep(uchar *func, uchar *trampoline) { + func = _hook_getpos(func); + const uchar *p = func; int len = 0; for (;;) { if_cold (p[len] == X86_CALL) { - return (struct hook_inline_prep_ret){ - 0, "can't trampoline call instructions" + return (struct _hook_prep_ret){ + 0, 0, "can't trampoline call instructions" }; } int ilen = x86_len(p + len); if_cold (ilen == -1) { - return (struct hook_inline_prep_ret){ - 0, "unknown or invalid instruction" + return (struct _hook_prep_ret){ + 0, 0, "unknown or invalid instruction" }; } len += ilen; if (len >= 5) { - // we should have statically made trampoline buffer size big enough - assume(curtrampoline - (uchar *)_hook_trampolinespc < - SPACE - len - 6); - *curtrampoline = len; // stuff length in there for quick unhooking - uchar *newtrampoline = curtrampoline + 1; - curtrampoline += len + 6; - memcpy(newtrampoline, p, len); - newtrampoline[len] = X86_JMPIW; - u32 diff = p - (newtrampoline + 5); // goto the continuation - memcpy(newtrampoline + len + 1, &diff, 4); - *trampoline = newtrampoline; - return (struct hook_inline_prep_ret){prologue, 0}; + memcpy(trampoline, p, len); + trampoline[len] = X86_JMPIW; + s32 diff = p - (trampoline + 5); // goto the continuation + memcpy(trampoline + len + 1, &diff, 4); + return (struct _hook_prep_ret){func, len, 0}; } if_cold (p[len] == X86_JMPIW) { - return (struct hook_inline_prep_ret){ - 0, "can't trampoline jump instructions" + return (struct _hook_prep_ret){ + 0, 0, "can't trampoline jump instructions" }; } } } -bool hook_inline_mprot(void *prologue) { - return os_mprot(prologue, 5, PAGE_EXECUTE_READWRITE); +bool hook_inline_mprot(void *hookpos) { + return os_mprot(hookpos, 5, PAGE_EXECUTE_READWRITE); } -void hook_inline_commit(void *restrict prologue, void *restrict target) { - uchar *p = prologue; - u32 diff = (uchar *)target - (p + 5); // goto the hook target - p[0] = X86_JMPIW; - memcpy(p + 1, &diff, 4); +void _hook_inline_commit(uchar *restrict hookpos, const uchar *restrict target) { + s32 diff = (uchar *)target - (hookpos + 5); // goto the hook target + hookpos[0] = X86_JMPIW; + memcpy(hookpos + 1, &diff, 4); } -void unhook_inline(void *orig) { - uchar *p = orig; - int len = p[-1]; - int off = mem_loads32(p + len + 1); - uchar *q = p + off + 5; - memcpy(q, p, 5); // XXX: not atomic atm! (does any of it even need to be?) +void _unhook_inline(uchar *trampoline, int len) { + s32 off = mem_loads32(trampoline + len + 1); + uchar *orig = trampoline + off + 5; + memcpy(orig, trampoline, 5); } // vi: sw=4 ts=4 noet tw=80 cc=80 -- cgit v1.2.3-54-g00ecf