Re[2]: Перехват ввода из терминала (Linux)
От: Murr Россия  
Дата: 05.08.03 17:01
Оценка: 6 (1)
Написал пока перехват через модуль. Идеи 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
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.