-
-
Save louieli/8a9bf77aeb29f50c83d6 to your computer and use it in GitHub Desktop.
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
| #define _GNU_SOURCE | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <stdbool.h> | |
| #include <dlfcn.h> | |
| #include <errno.h> | |
| #include <sys/mman.h> | |
| #include <unistd.h> | |
| #if 0 | |
| #define HOOK_DBG(fmt, args...) fprintf(stderr, fmt, ##args) | |
| #else | |
| #define HOOK_DBG(fmt, args...) do {} while (0) | |
| #endif | |
| #ifndef __x86_64__ | |
| #error "Hmm, this is implemented only for __x86_64__" | |
| #endif | |
| #define JMPQ_INSN_LEN 6ULL | |
| #define JMPQ_ARG_MASK ((1ULL << 32ULL) - 1ULL) | |
| static size_t* findFunctionGotEntry(void *plt) { | |
| size_t got_offset = 0; | |
| size_t *got_entry = 0; | |
| HOOK_DBG("DLS %llx\n", plt); | |
| got_offset = *((size_t*)(2 + ((char*)plt))); | |
| got_offset &= JMPQ_ARG_MASK; | |
| got_entry = (size_t*)(((char*)plt) + got_offset + JMPQ_INSN_LEN); | |
| HOOK_DBG("GOT %s %llx\n", got_entry); | |
| return got_entry; | |
| } | |
| static int setProtection(void *ptr, bool writable) { | |
| size_t PAGE_SIZE = sysconf(_SC_PAGESIZE); | |
| size_t PAGE_MASK = PAGE_SIZE - 1; | |
| void *page_ptr = (void*)(((size_t)ptr) & ~PAGE_MASK); | |
| if (mprotect(page_ptr, PAGE_SIZE, | |
| PROT_READ | (writable ? PROT_WRITE : 0)) < 0) | |
| { | |
| HOOK_DBG("failed to mprotect %p, error %s\n", ptr, strerror(errno)); | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| /* This function accepts the PLT pointer | |
| * the reason is that assignment from the function name | |
| * like "void* = (void*)read" will always return the PLT address | |
| * while dlsym may return the already resolved function address | |
| */ | |
| static int swapFunctionsImpl(void *f1_plt, void *f2_plt, const char *f1, const char *f2) { | |
| int ret = -1; | |
| void *f1_real = 0; | |
| void *f2_real = 0; | |
| size_t* f1_got = 0; | |
| size_t* f2_got = 0; | |
| /* This calls the resolver and returns the function pointer | |
| * if we look at the GOT before calling the function or doing a dlsym(RTLD_NEXT), | |
| * it will contain the address of the PLT + JMPQ_INSN_LEN to force a jump | |
| * to the resolver | |
| */ | |
| f1_real = dlsym(RTLD_NEXT, f1); | |
| if (!f1_real) { | |
| HOOK_DBG("failed to resolve %s, error %s\n", f1, strerror(errno)); | |
| goto fail_dlsym; | |
| } | |
| f2_real = dlsym(RTLD_NEXT, f2); | |
| if (!f2_real) { | |
| HOOK_DBG("failed to resolve %s, error %s\n", f2, strerror(errno)); | |
| goto fail_dlsym; | |
| } | |
| f1_got = findFunctionGotEntry(f1_plt); | |
| if (!f1_got) { | |
| goto fail_dlsym; | |
| } | |
| f2_got = findFunctionGotEntry(f2_plt); | |
| if (!f2_got) { | |
| goto fail_dlsym; | |
| } | |
| if (setProtection(f1_got, true)) { | |
| goto fail_prot_rw; | |
| } | |
| if (setProtection(f2_got, true)) { | |
| goto fail_prot_rw; | |
| } | |
| HOOK_DBG("f1_got=%p\nf2_got=%p\nf1_real=%llx\nf2_real=%llx\n", | |
| f1_got, f2_got, f1_real, f2_real); | |
| *f1_got = ((size_t)f2_real); | |
| *f2_got = ((size_t)f1_real); | |
| fail_prot_rw: | |
| //unfortunately, we cannot get the protection flags to restore them later | |
| //and PLT interferes with __do_global_dtors_aux() which causes a segfault | |
| //if we mprotect the memory as read-only. Maybe we should remove write | |
| //access here but explicitely restore it for libc functions | |
| //setProtection(f1_got, false); | |
| //setProtection(f2_got, false); | |
| fail_dlsym: | |
| return ret; | |
| } | |
| #define swapFunctions(a, b) swapFunctionsImpl((void*)a, (void*)b, #a, #b) | |
| int main() { | |
| swapFunctions(read, write); | |
| char *msg = "foo\n"; | |
| read(1, msg, 4); | |
| swapFunctions(read, write); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment