From e991c1293124fbfac057c842238f728c09186a0e Mon Sep 17 00:00:00 2001 From: baldurk Date: Mon, 26 Sep 2016 17:02:27 +0200 Subject: [PATCH] Add plthook for hooking libraries on linux even through DEEPBIND * Based on a fork with slight added extra functionality at https://github.com/baldurk/plthook --- LICENSE.md | 4 + docs/credits_acknowledgements.rst | 7 +- installer/LICENSE.rtf | Bin 6570 -> 6814 bytes renderdoc/3rdparty/plthook/README.md | 58 +++ renderdoc/3rdparty/plthook/plthook.h | 65 +++ renderdoc/3rdparty/plthook/plthook_elf.c | 611 +++++++++++++++++++++++ 6 files changed, 744 insertions(+), 1 deletion(-) create mode 100644 renderdoc/3rdparty/plthook/README.md create mode 100644 renderdoc/3rdparty/plthook/plthook.h create mode 100644 renderdoc/3rdparty/plthook/plthook_elf.c diff --git a/LICENSE.md b/LICENSE.md index 8f391b15f..db7295ecc 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -98,3 +98,7 @@ RenderDoc also uses several external libraries and components which include thei - [include-bin](https://github.com/Marqin/include-bin) distributed under the zlib license. Copyright 2016, Hubert Jarosz + +- [plthook](https://github.com/kubo/plthook) + distributed under the 2-clause BSD license. + Copyright 2013-2014, Kubo Takehiro diff --git a/docs/credits_acknowledgements.rst b/docs/credits_acknowledgements.rst index fc21f6bbe..27ac23d7d 100644 --- a/docs/credits_acknowledgements.rst +++ b/docs/credits_acknowledgements.rst @@ -74,7 +74,11 @@ The following libraries and components are incorporated into RenderDoc, listed h .. |cmdline_link| raw:: html - cmdline + cmdline + +.. |plthook_link| raw:: html + + plthook * |mhook_link| DLL hooking library, used to inject RenderDoc into applications * |treeview_link| An invaluable control filling a much needed niche in .NET winforms. @@ -93,6 +97,7 @@ The following libraries and components are incorporated into RenderDoc, listed h * |glslang_link| Used for compiling GLSL to SPIR-V. * |qt_link| Used for QRenderDoc replay UI program. * |cmdline_link| Used for parsing command line arguments to renderdoccmd. +* |plthook_link| Used for hooking some libraries loaded with DEEPBIND on linux. Thanks ------ diff --git a/installer/LICENSE.rtf b/installer/LICENSE.rtf index 27d8643ba883200cbe8cacb22efc516bfbac18be..d302a005ca3b2170c83256c3701c0eadb9b931c6 100644 GIT binary patch delta 122 zcmZ2wJkNB)63NLsgj{*DOOx{T3vx;_^7FGN8}Q0ajuGNkK^Cm7t&K@Dh)F9pGK$H` z%u7{(DQ@Hx=P=Su&PgmSPMy4gUrO1~Sl7tF&_qYU8)&9NNMd$sMrKjIUQ9t^5ieIQ JFBbzZ7XXenD3t&J delta 14 VcmbPdy2^ON5=pjNUM>b+E&wK-1J3{e diff --git a/renderdoc/3rdparty/plthook/README.md b/renderdoc/3rdparty/plthook/README.md new file mode 100644 index 000000000..fc304e1ce --- /dev/null +++ b/renderdoc/3rdparty/plthook/README.md @@ -0,0 +1,58 @@ +PLT Hook +======== + +What is plthook. +---------------- + +A utility library to hook library function calls issued by +specified object files (executable and libraries). + +Usage +----- + +If you have a library `libfoo.so.1` and want to intercept +a function call `recv()` without modifying the library, +put `plthook.c` (or `plthook_win32.c` for Windows) and `plthook.h` +in your source tree and add the following code. + + + static ssize_t my_recv(int sockfd, void *buf, size_t len, int flags) + { + ssize_t rv; + + ... do your task: logging, etc. ... + rv = recv(sockfd, buf, len, flags); /* call real recv(). */ + ... do your task: logging, check received data, etc. ... + return rv; + } + + int install_hook_function() + { + plthook_t *plthook; + + if (plthook_open(&plthook, "libfoo.so.1") != 0) { + printf("plthook_open error: %s\n", plthook_error()); + return -1; + } + if (plthook_replace(plthook, "recv", (void*)my_recv, NULL) != 0) { + printf("plthook_replace error: %s\n", plthook_error()); + plthook_close(plthook); + return -1; + } + plthook_close(plthook); + return 0; + } + +Supported Platforms +------------------- + +* Linux i386 and x86_64 by plthook_elf.c +* Windows 32-bit and x64 (MSVC, Mingw32 and Cygwin) by plthook_win32.c +* OS X (tested on Mavericks) by plthook_osx.c +* Solaris x86_64 by plthook_elf.c +* FreeBSD i386 and x86_64 except i386 program on x86_64 OS by plthook_elf.c + +License +------- + +2-clause BSD-style license. diff --git a/renderdoc/3rdparty/plthook/plthook.h b/renderdoc/3rdparty/plthook/plthook.h new file mode 100644 index 000000000..1324642c2 --- /dev/null +++ b/renderdoc/3rdparty/plthook/plthook.h @@ -0,0 +1,65 @@ +/* -*- indent-tabs-mode: nil -*- + * + * plthook.h -- the header file of plthook + * + * URL: https://github.com/kubo/plthook + * + * ------------------------------------------------------ + * + * Copyright 2013-2014 Kubo Takehiro + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of the authors. + * + */ +#ifndef PLTHOOK_H +#define PLTHOOK_H 1 + +#define PLTHOOK_SUCCESS 0 +#define PLTHOOK_FILE_NOT_FOUND 1 +#define PLTHOOK_INVALID_FILE_FORMAT 2 +#define PLTHOOK_FUNCTION_NOT_FOUND 3 +#define PLTHOOK_INVALID_ARGUMENT 4 +#define PLTHOOK_OUT_OF_MEMORY 5 +#define PLTHOOK_INTERNAL_ERROR 6 + +typedef struct plthook plthook_t; + +#ifdef __cplusplus +extern "C" { +#endif + +int plthook_open(plthook_t **plthook_out, const char *filename); +int plthook_open_by_handle(plthook_t **plthook_out, void *handle); +int plthook_open_by_address(plthook_t **plthook_out, void *address); +int plthook_enum(plthook_t *plthook, unsigned int *pos, const char **name_out, void ***addr_out); +int plthook_replace(plthook_t *plthook, const char *funcname, void *funcaddr, void **oldfunc); +void plthook_close(plthook_t *plthook); +const char *plthook_error(void); + +#ifdef __cplusplus +}; /* extern "C" */ +#endif + +#endif diff --git a/renderdoc/3rdparty/plthook/plthook_elf.c b/renderdoc/3rdparty/plthook/plthook_elf.c new file mode 100644 index 000000000..9d6d37174 --- /dev/null +++ b/renderdoc/3rdparty/plthook/plthook_elf.c @@ -0,0 +1,611 @@ +/* -*- indent-tabs-mode: nil -*- + * + * plthook_elf.c -- implemention of plthook for ELF format + * + * URL: https://github.com/kubo/plthook + * + * ------------------------------------------------------ + * + * Copyright 2013-2016 Kubo Takehiro + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of the authors. + * + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __sun +#include +#define ELF_TARGET_ALL +#endif /* __sun */ +#include +#include +#include "plthook.h" + +#ifndef __GNUC__ +#define __attribute__(arg) +#endif + +#if defined __linux__ +#define ELF_OSABI ELFOSABI_SYSV +#define ELF_OSABI_ALT ELFOSABI_LINUX +#elif defined __sun +#define ELF_OSABI ELFOSABI_SOLARIS +#elif defined __FreeBSD__ +#define ELF_OSABI ELFOSABI_FREEBSD +#if defined __i386__ && __ELF_WORD_SIZE == 64 +#error 32-bit application on 64-bit OS is not supported. +#endif +#else +#error unsupported OS +#endif + +#if defined __x86_64__ || defined __x86_64 +#define ELF_DATA ELFDATA2LSB +#define E_MACHINE EM_X86_64 +#ifdef R_X86_64_JUMP_SLOT +#define R_JUMP_SLOT R_X86_64_JUMP_SLOT +#else +#define R_JUMP_SLOT R_X86_64_JMP_SLOT +#endif +#define SHT_PLT_REL SHT_RELA +#define Elf_Plt_Rel Elf_Rela +#define PLT_SECTION_NAME ".rela.plt" +#elif defined __i386__ || defined __i386 +#define ELF_DATA ELFDATA2LSB +#define E_MACHINE EM_386 +#define R_JUMP_SLOT R_386_JMP_SLOT +#define SHT_PLT_REL SHT_REL +#define Elf_Plt_Rel Elf_Rel +#define PLT_SECTION_NAME ".rel.plt" +#else +#error E_MACHINE is not defined. +#endif + +#if defined __LP64__ +#ifndef ELF_CLASS +#define ELF_CLASS ELFCLASS64 +#endif +#define SIZE_T_FMT "lu" +#define Elf_Half Elf64_Half +#define Elf_Addr Elf64_Addr +#define Elf_Ehdr Elf64_Ehdr +#define Elf_Phdr Elf64_Phdr +#define Elf_Shdr Elf64_Shdr +#define Elf_Sym Elf64_Sym +#define Elf_Rel Elf64_Rel +#define Elf_Rela Elf64_Rela +#ifndef ELF_R_SYM +#define ELF_R_SYM ELF64_R_SYM +#endif +#ifndef ELF_R_TYPE +#define ELF_R_TYPE ELF64_R_TYPE +#endif +#else /* __LP64__ */ +#ifndef ELF_CLASS +#define ELF_CLASS ELFCLASS32 +#endif +#define SIZE_T_FMT "u" +#define Elf_Half Elf32_Half +#define Elf_Addr Elf32_Addr +#define Elf_Ehdr Elf32_Ehdr +#define Elf_Phdr Elf32_Phdr +#define Elf_Shdr Elf32_Shdr +#define Elf_Sym Elf32_Sym +#define Elf_Rel Elf32_Rel +#define Elf_Rela Elf32_Rela +#ifndef ELF_R_SYM +#define ELF_R_SYM ELF32_R_SYM +#endif +#ifndef ELF_R_TYPE +#define ELF_R_TYPE ELF32_R_TYPE +#endif +#endif /* __LP64__ */ + +struct plthook { + const char *base; + const Elf_Phdr *phdr; + size_t phnum; + Elf_Shdr *shdr; + size_t shnum; + char *shstrtab; + size_t shstrtab_size; + const Elf_Sym *dynsym; + size_t dynsym_cnt; + const char *dynstr; + size_t dynstr_size; + const Elf_Plt_Rel *plt; + size_t plt_cnt; +#ifdef PT_GNU_RELRO + const char *relro_start; + const char *relro_end; +#endif +}; + +static char errmsg[512]; + +#ifdef PT_GNU_RELRO +static size_t page_size; +#endif + +static int plthook_open_executable(plthook_t **plthook_out); +static int plthook_open_shared_library(plthook_t **plthook_out, const char *filename); +static int plthook_open_real(plthook_t **plthook_out, const char *base, const char *filename); +static int check_elf_header(const Elf_Ehdr *ehdr); +static int find_section(plthook_t *image, const char *name, const Elf_Shdr **out); +static void set_errmsg(const char *fmt, ...) __attribute__((__format__ (__printf__, 1, 2))); + +int plthook_open(plthook_t **plthook_out, const char *filename) +{ + *plthook_out = NULL; + if (filename == NULL) { + return plthook_open_executable(plthook_out); + } else { + return plthook_open_shared_library(plthook_out, filename); + } +} + +int plthook_open_by_handle(plthook_t **plthook_out, void *hndl) +{ + struct link_map *lmap = NULL; + + if (hndl == NULL) { + set_errmsg("NULL handle"); + return PLTHOOK_FILE_NOT_FOUND; + } + if (dlinfo(hndl, RTLD_DI_LINKMAP, &lmap) != 0) { + set_errmsg("dlinfo error"); + return PLTHOOK_FILE_NOT_FOUND; + } + return plthook_open_real(plthook_out, (const char*)lmap->l_addr, lmap->l_name); +} + +int plthook_open_by_address(plthook_t **plthook_out, void *address) +{ + Dl_info info; + + *plthook_out = NULL; + if (dladdr(address, &info) == 0) { + set_errmsg("dladdr error"); + return PLTHOOK_FILE_NOT_FOUND; + } + return plthook_open_real(plthook_out, info.dli_fbase, info.dli_fname); +} + +static int plthook_open_executable(plthook_t **plthook_out) +{ +#if defined __linux__ + /* Open the main program. */ + char buf[128]; + FILE *fp = fopen("/proc/self/maps", "r"); + unsigned long base; + + if (fp == NULL) { + set_errmsg("Could not open /proc/self/maps: %s", + strerror(errno)); + return PLTHOOK_INTERNAL_ERROR; + } + if (fgets(buf, sizeof(buf), fp) == NULL) { + set_errmsg("Could not read /proc/self/maps: %s", + strerror(errno)); + fclose(fp); + return PLTHOOK_INTERNAL_ERROR; + } + fclose(fp); + if (sscanf(buf, "%lx-%*x r-xp %*x %*x:%*x %*u ", &base) != 1) { + set_errmsg("invalid /proc/self/maps format: %s", buf); + return PLTHOOK_INTERNAL_ERROR; + } + return plthook_open_real(plthook_out, (const char*)base, "/proc/self/exe"); +#elif defined __sun + prmap_t prmap; + pid_t pid = getpid(); + char fname[128]; + int fd; + + sprintf(fname, "/proc/%d/map", pid); + fd = open(fname, O_RDONLY); + if (fd == -1) { + set_errmsg("Could not open %s: %s", fname, + strerror(errno)); + return PLTHOOK_INTERNAL_ERROR; + } + if (read(fd, &prmap, sizeof(prmap)) != sizeof(prmap)) { + set_errmsg("Could not read %s: %s", fname, + strerror(errno)); + close(fd); + return PLTHOOK_INTERNAL_ERROR; + } + close(fd); + sprintf(fname, "/proc/%d/object/a.out", pid); + return plthook_open_real(plthook_out, (const char*)prmap.pr_vaddr, fname); +#elif defined __FreeBSD__ + return plthook_open_shared_library(plthook_out, NULL); +#else +#error unsupported OS +#endif +} + +static int plthook_open_shared_library(plthook_t **plthook_out, const char *filename) +{ + void *hndl = dlopen(filename, RTLD_LAZY | RTLD_NOLOAD); + struct link_map *lmap = NULL; + + if (hndl == NULL) { + set_errmsg("dlopen error: %s", dlerror()); + return PLTHOOK_FILE_NOT_FOUND; + } + if (dlinfo(hndl, RTLD_DI_LINKMAP, &lmap) != 0) { + set_errmsg("dlinfo error"); + dlclose(hndl); + return PLTHOOK_FILE_NOT_FOUND; + } + dlclose(hndl); + return plthook_open_real(plthook_out, (const char*)lmap->l_addr, lmap->l_name); +} + +static int plthook_open_real(plthook_t **plthook_out, const char *base, const char *filename) +{ + const Elf_Ehdr *ehdr = (Elf_Ehdr *)base; + const Elf_Shdr *shdr; + size_t shdr_size; + int fd = -1; + off_t offset; + plthook_t *plthook; + int rv; +#ifdef PT_GNU_RELRO + size_t idx; +#endif + + if (base == NULL) { + set_errmsg("The base address is zero."); + return PLTHOOK_FILE_NOT_FOUND; + } + + if (filename == NULL) { + set_errmsg("failed to get the file name on the disk."); + return PLTHOOK_FILE_NOT_FOUND; + } + + plthook = calloc(1, sizeof(plthook_t)); + if (plthook == NULL) { + set_errmsg("failed to allocate memory: %" SIZE_T_FMT " bytes", sizeof(plthook_t)); + return PLTHOOK_OUT_OF_MEMORY; + } + + /* sanity check */ + rv = check_elf_header(ehdr); + if (rv != 0) { + goto error_exit; + } + if (ehdr->e_type == ET_DYN) { + plthook->base = base; + } + plthook->phdr = (const Elf_Phdr *)(plthook->base + ehdr->e_phoff); + plthook->phnum = ehdr->e_phnum; + fd = open(filename, O_RDONLY, 0); + if (fd == -1) { + set_errmsg("Could not open %s: %s", filename, strerror(errno)); + rv = PLTHOOK_FILE_NOT_FOUND; + goto error_exit; + } + shdr_size = ehdr->e_shnum * ehdr->e_shentsize; + plthook->shdr = calloc(1, shdr_size); + if (plthook->shdr == NULL) { + set_errmsg("failed to allocate memory: %" SIZE_T_FMT " bytes", shdr_size); + rv = PLTHOOK_OUT_OF_MEMORY; + goto error_exit; + } + offset = ehdr->e_shoff; + if ((rv = lseek(fd, offset, SEEK_SET)) != offset) { + set_errmsg("failed to seek to the section header table."); + rv = PLTHOOK_INVALID_FILE_FORMAT; + goto error_exit; + } + if (read(fd, plthook->shdr, shdr_size) != shdr_size) { + set_errmsg("failed to read the section header table."); + rv = PLTHOOK_INVALID_FILE_FORMAT; + goto error_exit; + } + plthook->shnum = ehdr->e_shnum; + plthook->shstrtab_size = plthook->shdr[ehdr->e_shstrndx].sh_size; + plthook->shstrtab = malloc(plthook->shstrtab_size); + if (plthook->shstrtab == NULL) { + set_errmsg("failed to allocate memory: %" SIZE_T_FMT " bytes", plthook->shstrtab_size); + rv = PLTHOOK_OUT_OF_MEMORY; + goto error_exit; + } + offset = plthook->shdr[ehdr->e_shstrndx].sh_offset; + if (lseek(fd, offset, SEEK_SET) != offset) { + set_errmsg("failed to seek to the section header string table."); + rv = PLTHOOK_INVALID_FILE_FORMAT; + goto error_exit; + } + if (read(fd, plthook->shstrtab, plthook->shstrtab_size) != plthook->shstrtab_size) { + set_errmsg("failed to read the section header string table."); + rv = PLTHOOK_INVALID_FILE_FORMAT; + goto error_exit; + } +#ifdef PT_GNU_RELRO + if (page_size == 0) { + page_size = sysconf(_SC_PAGESIZE); + } + offset = ehdr->e_phoff; + if ((rv = lseek(fd, offset, SEEK_SET)) != offset) { + set_errmsg("failed to seek to the program header table."); + rv = PLTHOOK_INVALID_FILE_FORMAT; + goto error_exit; + } + for (idx = 0; idx < ehdr->e_phnum; idx++) { + Elf_Phdr phdr; + if (read(fd, &phdr, sizeof(phdr)) != sizeof(phdr)) { + set_errmsg("failed to read the program header table."); + rv = PLTHOOK_INVALID_FILE_FORMAT; + goto error_exit; + } + if (phdr.p_type == PT_GNU_RELRO) { + plthook->relro_start = plthook->base + phdr.p_vaddr; + plthook->relro_end = plthook->relro_start + phdr.p_memsz; + } + } +#endif + close(fd); + fd = -1; + + rv = find_section(plthook, ".dynsym", &shdr); + if (rv != 0) { + goto error_exit; + } + if (shdr->sh_type != SHT_DYNSYM) { + set_errmsg("The type of .dynsym section should be SHT_DYNSYM but %d.", shdr->sh_type); + rv = PLTHOOK_INVALID_FILE_FORMAT; + goto error_exit; + } + if (shdr->sh_entsize != sizeof(Elf_Sym)) { + set_errmsg("The size of a section header entry should be sizeof(Elf_Sym)(%" SIZE_T_FMT ") but %" SIZE_T_FMT ".", + sizeof(Elf_Sym), shdr->sh_entsize); + rv = PLTHOOK_INVALID_FILE_FORMAT; + goto error_exit; + } + plthook->dynsym = (const Elf_Sym*)(plthook->base + shdr->sh_addr); + plthook->dynsym_cnt = shdr->sh_size / shdr->sh_entsize; + + rv = find_section(plthook, ".dynstr", &shdr); + if (rv != 0) { + goto error_exit; + } + if (shdr->sh_type != SHT_STRTAB) { + set_errmsg("The type of .dynstrx section should be SHT_STRTAB but %d.", shdr->sh_type); + rv = PLTHOOK_INVALID_FILE_FORMAT; + goto error_exit; + } + plthook->dynstr = (const char*)(plthook->base + shdr->sh_addr); + plthook->dynstr_size = shdr->sh_size; + + rv = find_section(plthook, PLT_SECTION_NAME, &shdr); + if (rv != 0) { + goto error_exit; + } + if (shdr->sh_entsize != sizeof(Elf_Plt_Rel)) { + set_errmsg("invalid " PLT_SECTION_NAME " table entry size: %" SIZE_T_FMT, shdr->sh_entsize); + rv = PLTHOOK_INVALID_FILE_FORMAT; + goto error_exit; + } + plthook->plt = (Elf_Plt_Rel *)(plthook->base + shdr->sh_addr); + plthook->plt_cnt = shdr->sh_size / sizeof(Elf_Plt_Rel); + + *plthook_out = plthook; + return 0; + error_exit: + if (fd != -1) { + close(fd); + } + plthook_close(plthook); + return rv; +} + +int plthook_enum(plthook_t *plthook, unsigned int *pos, const char **name_out, void ***addr_out) +{ + while (*pos < plthook->plt_cnt) { + const Elf_Plt_Rel *plt = plthook->plt + *pos; + if (ELF_R_TYPE(plt->r_info) == R_JUMP_SLOT) { + size_t idx = ELF_R_SYM(plt->r_info); + + if (idx >= plthook->dynsym_cnt) { + set_errmsg(".dynsym index %" SIZE_T_FMT " should be less than %" SIZE_T_FMT ".", idx, plthook->dynsym_cnt); + return PLTHOOK_INVALID_FILE_FORMAT; + } + idx = plthook->dynsym[idx].st_name; + if (idx + 1 > plthook->dynstr_size) { + set_errmsg("too big section header string table index: %" SIZE_T_FMT, idx); + return PLTHOOK_INVALID_FILE_FORMAT; + } + *name_out = plthook->dynstr + idx; + *addr_out = (void**)(plthook->base + plt->r_offset); + (*pos)++; + return 0; + } + (*pos)++; + } + *name_out = NULL; + *addr_out = NULL; + return EOF; +} + +int plthook_replace(plthook_t *plthook, const char *funcname, void *funcaddr, void **oldfunc) +{ + size_t funcnamelen = strlen(funcname); + unsigned int pos = 0; + const char *name; + void **addr; + int rv; + + if (plthook == NULL) { + set_errmsg("invalid argument: The first argument is null."); + return PLTHOOK_INVALID_ARGUMENT; + } + while ((rv = plthook_enum(plthook, &pos, &name, &addr)) == 0) { + if (strncmp(name, funcname, funcnamelen) == 0) { + if (name[funcnamelen] == '\0' || name[funcnamelen] == '@') { +#ifdef PT_GNU_RELRO + void *maddr = NULL; + if (plthook->relro_start <= (char*)addr && (char*)addr < plthook->relro_end) { + maddr = (void*)((size_t)addr & ~(page_size - 1)); + if (mprotect(maddr, page_size, PROT_READ | PROT_WRITE) != 0) { + set_errmsg("Could not change the process memory protection at %p: %s", + maddr, strerror(errno)); + return PLTHOOK_INTERNAL_ERROR; + } + } +#endif + if (oldfunc) { + *oldfunc = *addr; + } + *addr = funcaddr; +#ifdef PT_GNU_RELRO + if (maddr != NULL) { + mprotect(maddr, page_size, PROT_READ); + } +#endif + return 0; + } + } + } + if (rv == EOF) { + set_errmsg("no such function: %s", funcname); + rv = PLTHOOK_FUNCTION_NOT_FOUND; + } + return rv; +} + +void plthook_close(plthook_t *plthook) +{ + if (plthook != NULL) { + free(plthook->shdr); + free(plthook->shstrtab); + free(plthook); + } +} + +const char *plthook_error(void) +{ + return errmsg; +} + +static int check_elf_header(const Elf_Ehdr *ehdr) +{ + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) { + set_errmsg("invalid file signature: 0x%02x,0x%02x,0x%02x,0x%02x", + ehdr->e_ident[0], ehdr->e_ident[1], ehdr->e_ident[2], ehdr->e_ident[3]); + return PLTHOOK_INVALID_FILE_FORMAT; + } + if (ehdr->e_ident[EI_CLASS] != ELF_CLASS) { + set_errmsg("invalid elf class: 0x%02x", ehdr->e_ident[EI_CLASS]); + return PLTHOOK_INVALID_FILE_FORMAT; + } + if (ehdr->e_ident[EI_DATA] != ELF_DATA) { + set_errmsg("invalid elf data: 0x%02x", ehdr->e_ident[EI_DATA]); + return PLTHOOK_INVALID_FILE_FORMAT; + } + if (ehdr->e_ident[EI_VERSION] != EV_CURRENT) { + set_errmsg("invalid elf version: 0x%02x", ehdr->e_ident[EI_VERSION]); + return PLTHOOK_INVALID_FILE_FORMAT; + } + if (ehdr->e_ident[EI_OSABI] != ELF_OSABI) { +#ifdef ELF_OSABI_ALT + if (ehdr->e_ident[EI_OSABI] != ELF_OSABI_ALT) { + set_errmsg("invalid OS ABI: 0x%02x", ehdr->e_ident[EI_OSABI]); + return PLTHOOK_INVALID_FILE_FORMAT; + } +#else + set_errmsg("invalid OS ABI: 0x%02x", ehdr->e_ident[EI_OSABI]); + return PLTHOOK_INVALID_FILE_FORMAT; +#endif + } + if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) { + set_errmsg("invalid file type: 0x%04x", ehdr->e_type); + return PLTHOOK_INVALID_FILE_FORMAT; + } + if (ehdr->e_machine != E_MACHINE) { + set_errmsg("invalid machine type: %u", ehdr->e_machine); + return PLTHOOK_INVALID_FILE_FORMAT; + } + if (ehdr->e_version != EV_CURRENT) { + set_errmsg("invalid object file version: %u", ehdr->e_version); + return PLTHOOK_INVALID_FILE_FORMAT; + } + if (ehdr->e_ehsize != sizeof(Elf_Ehdr)) { + set_errmsg("invalid elf header size: %u", ehdr->e_ehsize); + return PLTHOOK_INVALID_FILE_FORMAT; + } + if (ehdr->e_phentsize != sizeof(Elf_Phdr)) { + set_errmsg("invalid program header table entry size: %u", ehdr->e_phentsize); + return PLTHOOK_INVALID_FILE_FORMAT; + } + if (ehdr->e_shentsize != sizeof(Elf_Shdr)) { + set_errmsg("invalid section header table entry size: %u", ehdr->e_shentsize); + return PLTHOOK_INVALID_FILE_FORMAT; + } + return 0; +} + +static int find_section(plthook_t *image, const char *name, const Elf_Shdr **out) +{ + const Elf_Shdr *shdr = image->shdr; + const Elf_Shdr *shdr_end = shdr + image->shnum; + size_t namelen = strlen(name); + + while (shdr < shdr_end) { + if (shdr->sh_name + namelen >= image->shstrtab_size) { + set_errmsg("too big section header string table index: %u", shdr->sh_name); + return PLTHOOK_INVALID_FILE_FORMAT; + } + if (strcmp(image->shstrtab + shdr->sh_name, name) == 0) { + *out = shdr; + return 0; + } + shdr++; + } + set_errmsg("failed to find the section header: %s", name); + return PLTHOOK_INVALID_FILE_FORMAT; +} + +static void set_errmsg(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vsnprintf(errmsg, sizeof(errmsg) - 1, fmt, ap); + va_end(ap); +}