Last active
March 9, 2023 09:44
-
-
Save imkiva/11800263048c415d68335e8c3a95ea99 to your computer and use it in GitHub Desktop.
Hook demo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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