Skip to content

Instantly share code, notes, and snippets.

@imkiva
Last active March 9, 2023 09:44
Show Gist options
  • Save imkiva/11800263048c415d68335e8c3a95ea99 to your computer and use it in GitHub Desktop.
Save imkiva/11800263048c415d68335e8c3a95ea99 to your computer and use it in GitHub Desktop.
Hook demo
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <stdint.h>
int some(int n) {
return n + 1;
}
/**
* Replace the `origin` function with `hook`, and generate code
* for calling the original `origin` function to `*trampoline`.
*
* This is done by replacing some instructions at the begining
* of `origin` with a "jump" that branches to `hook`.
* The `trampoline` actually stores the backup of the replaced
* instructions in `origin`, plus a "jump" that branches to the
* first safe instruction in `origin`.
*/
void replace(char *origin, char *hook, char **trampoline) {
size_t pageSize = sysconf(_SC_PAGESIZE);
uintptr_t start = (uintptr_t) origin;
uintptr_t pageEnd = start + 1;
uintptr_t pageStart = start & -pageSize;
// Setup trampoline code for jumping to the original function
if (trampoline != NULL) {
// NOTE: The generated trampoline is not correct since we need to disassemble
// the instructions in the original function, in order to know:
// 1. how many bytes should be copied to the trampoline, and
// 2. the offset that the jump-to-original instruction needs.
// The following code are shown for demonstration. We assume:
// 1. no instruction in the original function are truncated, so only 5 bytes
// should be copied, since they will be overwritten by the hook.
// 2. and the jump offset should also be 5.
int overwritten = 5;
int nextInst = 5;
*trampoline = malloc(overwritten + 5); // may use `mmap()`
// the prologue replaced by `jump hook` below
memcpy(*trampoline, origin, overwritten);
// the code that jumps to `origin + nextInst`
int64_t offset = *trampoline - (origin + 5) + nextInst;
char *inst = *trampoline;
*inst = 0xe9;
*((int64_t *) inst) = offset;
// make the trampoline executable
uintptr_t pageStart = ((uintptr_t) *trampoline) & -pageSize;
uintptr_t pageEnd = ((uintptr_t) *trampoline) + 1;
// note: may violate W^X
mprotect((void *) pageStart, pageEnd - pageStart,
PROT_READ | PROT_WRITE | PROT_EXEC);
}
// make the entire page that contains the function writable
// note: the mprotect() below may violate W^X
mprotect((void *) pageStart, pageEnd - pageStart,
PROT_READ | PROT_WRITE | PROT_EXEC);
// replace the first 5 bytes in `origin` with instruction that jumps to hook
int64_t offset = hook - (origin + 5);
char *inst = origin;
*inst++ = 0xe9;
*((int64_t *) inst) = offset;
// restore page permission
mprotect((void *) pageStart, pageEnd - pageStart,
PROT_READ | PROT_EXEC);
}
void call_some() {
printf("I am going to call some(1)\n");
int x = some(1);
printf("x = %d, but 2 is expected\n", x);
}
int hook(int n) {
return n + 2;
}
int main() {
char *trampoline = NULL;
replace((char *) some, (char *) hook, &trampoline);
call_some();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment