Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

initial commit

  • Loading branch information...
commit 23295210cdd28565b1fecb129527b1f9bfa4cf8d 0 parents
@mnunberg authored
23 Makefile
@@ -0,0 +1,23 @@
+TARGETS=testmain libmonkey.so test_inject.so
+
+all: $(TARGETS)
+ LD_PRELOAD=$(shell pwd)/test_inject.so ./testmain
+
+clean:
+ -rm -f $(TARGETS)
+
+CFLAGS=-ggdb3 -O0
+
+testmain: testmain.c
+ $(CC) $(CFLAGS) -o $@ $^
+
+libmonkey.so: libmonkey.c fn_override.c
+ $(CC) $(CFLAGS) -Wall -shared -o $@ -fPIC $^
+
+test_inject.so: libmonkey.so test_inject.c
+ $(CC) $(CFLAGS) -shared -fPIC test_inject.c -o $@ -Wl,-rpath='$$ORIGIN' -L. -lmonkey
+
+paste:
+ head -n 1000 *.{c,h} Makefile | nopaste -lc
+
+
26 README
@@ -0,0 +1,26 @@
+Hello.
+
+I often find myself needing to do really odd and weird things
+in order to avoid administrative overhead of 'fixing' a distro's
+library, or installing multiple versions thereof.
+
+Sometimes the actual task to be fixed is trivial, but it's difficult
+to make the external library (or even binary) do what you want it
+to do.
+
+libmonkey will completely replace a function for you with another
+function of your choosing.
+
+The function must match the exact prototype of the old one, and should
+return the same type of value as the old one.
+
+Optionally you can obtain the machine code which formerly belonged
+to the 'pristine' original function, for later analysis.
+
+This currently only works on ia32/amd64 architectures with the ELF
+format and linux/glibc (it's possible it can work on other platforms
+supporting ELF, but I doubt that).
+
+See the Makefile and test_inject.c for example usage
+
+- M. Nunberg
70 fn_override.c
@@ -0,0 +1,70 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef _WIN32
+#include <sys/mman.h>
+#else
+#include <windows.h>
+#endif
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+int libmonkey_override_by_ptr(void *orig, const void *target) {
+#ifdef _LP64
+#define JMP_SIZE 12
+#define PTR_TYPE uint64_t
+#define DST_TYPE PTR_TYPE
+#define DST_ARG (PTR_TYPE)target
+ char op[JMP_SIZE];
+ /*two-byte movq*/
+ op[0] = 0x48;
+ op[1] = 0xb8;
+ /*two byte jmpq*/
+ op[10] = 0xff;
+ op[11] = 0xe0;
+ void *addrptr = op+2;
+#else
+#define JMP_SIZE 5
+#define PTR_TYPE uint32_t
+#define DST_TYPE int32_t
+#define DST_ARG (DST_TYPE)(target-orig-JMP_SIZE)
+ char op[JMP_SIZE];
+ op[0] = 0xe9; /*jmp*/
+ void *addrptr = op+1;
+#endif
+#define FN_OFFSET orig - (((PTR_TYPE)orig)%4096)
+#define MPROTECT_SIZE (((PTR_TYPE)orig+4095+JMP_SIZE)/4096)*4096 - \
+ ((PTR_TYPE)orig-((PTR_TYPE)orig%4096))
+
+#ifdef _WIN32
+ DWORD wtf;
+ if(!VirtualProtect(FN_OFFSET, MPROTECT_SIZE, PAGE_EXECUTE_READWRITE, &wtf))
+ {
+ DWORD dw = GetLastError();
+ LPVOID buf;
+ printf("got error\n");
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
+ FORMAT_MESSAGE_FROM_SYSTEM|
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ dw,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPSTR)&buf,
+ 0, NULL);
+ printf("VirtualProtect() failed!\n");
+ printf(buf);
+ return 0;
+ }
+#else
+ if(mprotect(FN_OFFSET, MPROTECT_SIZE,PROT_READ|PROT_WRITE|PROT_EXEC) == -1) {
+ printf("mprotect failed!: %s\n", strerror(errno));
+ return 0;
+ }
+#endif
+ *(DST_TYPE*)addrptr = DST_ARG;
+ memcpy(orig, op, JMP_SIZE);
+ return 1;
+}
185 libmonkey.c
@@ -0,0 +1,185 @@
+#ifdef __linux__
+#include <link.h>
+#include <elf.h>
+#else
+#include <libelf.h>
+#define ElfW(x) Elf64_ ## x
+#endif
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/mman.h>
+
+#include "libmonkey.h"
+
+int libmonkey_patch(libmonkey_t monkey,
+ const char *victim,
+ const void *injection,
+ void **old,
+ size_t *nold)
+{
+ const void *mapaddr = monkey->addr;
+ ElfW(Ehdr) *ehdr = mapaddr;
+ ElfW(Shdr) *shdr, *sym_shdr = NULL, *dynsym_shdr = NULL;
+ ElfW(Sym) *syments = NULL;
+ ElfW(Sym) *symvictim = NULL;
+
+ char *strtable, *symstrs = NULL, *dynsymstrs = NULL;
+ size_t ii;
+ size_t stridx;
+ size_t nsyms;
+
+ stridx = ehdr->e_shstrndx;
+
+ printf("String Table Index=%d\n", ehdr->e_shstrndx);
+
+ shdr = mapaddr + ehdr->e_shoff;
+ strtable = mapaddr + shdr[stridx].sh_offset;
+
+ for (ii = 0; ii < ehdr->e_shnum; ii++) {
+ size_t nameidx = shdr[ii].sh_name;
+ char *section_name = strtable + nameidx;
+ printf("\nSection idx=%lu, nameidx=%lx, type=%x, flags=%x, info=%x\n",
+ ii,
+ nameidx,
+ shdr[ii].sh_type,
+ shdr[ii].sh_flags,
+ shdr[ii].sh_info);
+ if (nameidx) {
+ printf("\tName is %s\n", section_name);
+ }
+
+ if (shdr[ii].sh_type == SHT_SYMTAB) {
+ sym_shdr = shdr + ii;
+ } else if(shdr[ii].sh_type == SHT_DYNSYM) {
+ dynsym_shdr = shdr + ii;
+ } else if (shdr[ii].sh_type == SHT_STRTAB &&
+ strcmp(section_name, ".strtab") == 0) {
+ printf("Found symbol string table!\n");
+ symstrs = mapaddr + (size_t)shdr[ii].sh_offset;
+ } else if (shdr[ii].sh_type == SHT_STRTAB &&
+ strcmp(section_name, ".dynstr") == 0) {
+ printf("Found .dynstr\n");
+ dynsymstrs = mapaddr + (size_t)shdr[ii].sh_offset;
+ }
+ }
+
+
+ if (sym_shdr == NULL) {
+ if (dynsym_shdr) {
+ printf("Using .dynsym instead\n");
+ sym_shdr = dynsym_shdr;
+ symstrs = dynsymstrs;
+ } else {
+ printf("Couldn't find symbol table. Aborting\n");
+ abort();
+ }
+ }
+
+ if (symstrs == NULL) {
+ printf("Couldn't find symbol string table!\n");
+ abort();
+ }
+
+ nsyms = sym_shdr->sh_size / sizeof(ElfW(Sym));
+ syments = mapaddr + sym_shdr->sh_offset;
+
+
+ printf("\n\nLooking At symbols\n\n");
+
+ for (ii = 0; ii < nsyms; ii++) {
+ char *symname;
+ size_t nameidx = syments[ii].st_name;
+ if (nameidx == 0
+ || syments[ii].st_value == 0
+ || syments[ii].st_info == STT_SECTION
+ || syments[ii].st_info == STT_FILE) {
+ continue;
+ }
+ symname = symstrs + nameidx;
+ printf("Symbol name=%s, shndx=%d, addr=%x, sz=%lu\n",
+ symname,
+ syments[ii].st_shndx,
+ syments[ii].st_value,
+ syments[ii].st_size);
+
+ if (strcmp(symname, victim) == 0) {
+ printf("Found function to override!\n");
+ symvictim = syments + ii;
+ break;
+ }
+ }
+
+ if (!symvictim) {
+ fprintf(stderr, "LIBMONKEY: couldn't find symbol \"%s\"\n", victim);
+ return -1;
+ }
+
+ if (!symvictim->st_value) {
+ fprintf(stderr, "LIBMONKEY: Symbol \"%s\" referenced but not defined\n", victim);
+ return -1;
+ }
+
+ if (old) {
+ *old = malloc(symvictim->st_size);
+ memcpy(*old, (void*)symvictim->st_value, symvictim->st_size);
+ *nold = symvictim->st_size;
+ }
+
+ {
+ int ybret = libmonkey_override_by_ptr(
+ (void*)symvictim->st_value, injection);
+ if (ybret == 1) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+
+ return 0xdeadbeef; /*not reached*/
+
+}
+
+libmonkey_t libmonkey_new_from_exe(void)
+{
+ char path[4096];
+ pid_t my_pid = getpid();
+ sprintf(path, "/proc/%d/exe", my_pid);
+ return libmonkey_new_from_path(path);
+}
+
+libmonkey_t libmonkey_new_from_path(const char *path)
+{
+ struct libmonkey_st *ret = malloc(sizeof(struct libmonkey_st));
+ struct stat sb;
+ ret->fd = open(path, O_RDONLY);
+
+ if (ret->fd < 0) {
+ fprintf(stderr, "LIBMONKEY: %s: %s\n", path, strerror(errno));
+ free(ret);
+ return NULL;
+ }
+
+ assert( fstat(ret->fd, &sb) == 0);
+ ret->maplen = sb.st_size;
+
+ assert( (ret->addr =
+ mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, ret->fd, 0)) != NULL);
+
+ return ret;
+}
+
+void libmonkey_free(libmonkey_t monkey)
+{
+ close(monkey->fd);
+ munmap(monkey->addr, monkey->maplen);
+ free(monkey);
+}
45 libmonkey.h
@@ -0,0 +1,45 @@
+#ifndef MONKEY_H_
+#define MONKEY_H_
+#include <sys/types.h>
+
+int some_function(int,int,int);
+int libmonkey_override_by_ptr(void *orig, const void *target);
+
+struct libmonkey_st {
+ void *addr;
+ size_t maplen;
+ int fd;
+};
+
+typedef struct libmonkey_st* libmonkey_t;
+
+
+/**
+ * These functions will replace a non-static symbol with anything you want,
+ * provided the symbol can be located by its name AND you are using the ELF
+ * format.
+ */
+
+/* This returns a handle based on the current running executable */
+libmonkey_t libmonkey_new_from_exe(void);
+
+/* Returns a new handle based on the path */
+libmonkey_t libmonkey_new_from_path(const char *path);
+
+/* Replaces the global symbol victim with the function
+ * found at injection.
+ * If old is not-null, the raw code previously beloning to the victim
+ * function will be placed there.
+ */
+int libmonkey_patch(libmonkey_t monkey,
+ const char *victim,
+ const void *injection,
+ void **old,
+ size_t *nold);
+
+/*
+ * Free the libmonkey structure
+ */
+void libmonkey_free(libmonkey_t monkey);
+
+#endif /* MONKEY_H_ */
19 test_inject.c
@@ -0,0 +1,19 @@
+#include "libmonkey.h"
+#include <stdio.h>
+#include <assert.h>
+
+int injected_function(int a, int b, int c)
+{
+ printf("I am the injected function!\n");
+ return a-b-c;
+}
+
+static void
+__attribute__((constructor))
+do_inject(void)
+{
+ libmonkey_t my_monkey = libmonkey_new_from_exe();
+ assert(libmonkey_patch(my_monkey, "some_function",
+ injected_function, NULL, NULL) == 0);
+ libmonkey_free(my_monkey);
+}
14 testmain.c
@@ -0,0 +1,14 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+int some_function(int a, int b, int c)
+{
+ printf("I am the original function\n");
+ return a + b +c;
+}
+
+int main(void) {
+ int ret = some_function(1,2,3);
+ printf("Returned: %d\n", ret);
+ return 0;
+}
Please sign in to comment.
Something went wrong with that request. Please try again.