Dragonfly 发表于 2003-5-26 06:09:35

chrdev&systemcallhook example

here is an example chrdev kernel module. it hooks the execve system call and log all invokes of execve, it is not perfect, but workable. and at least we can learn some from it.
--------------------------------------------------------------------------------
Process Monitoring Linux Kernel Module v0.3
"For paranoid" or curious people like me
By Jacob Bower ([email protected])

Introduction:
This is a small kernel module that allows you to monitor all the executables
being run by users on a linux system. It creates a pseudo character device
called /dev/procmon which produces output whenever a call to execve() is made
(i.e. whenever a program is executed).

Dragonfly 发表于 2003-5-26 06:10:19

Makefile

CC=gcc
INCDIR=/lib/modules/`uname -r`/build/include/
CFLAGS= -Wall -D__KERNEL__ -DLINUX -DMODULE -O -I $(INCDIR)

all: procmon.o

proclog.o : proclog.c $(INCDIR)/linux/version.h
      $(CC) $(CFLAGS) -c proclog.c

install: all
# Just incase I decicde to change the major/minor number we remove any old
# instances of /dev/procmon
      rm -rf /dev/procmon
      mknod /dev/procmon c 240 1
      chmod 400 /dev/procmon

clean:
      rm -f *.o
      rm -f *~
      rm -f a.out
      rm -f test*
      rm -f DEADJOE

dist: clean
      cd .. ; tar cvzf procmon.tar.gz procmon

Dragonfly 发表于 2003-5-26 06:11:45

procmon.c

#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/devfs_fs_kernel.h>
#include <asm/ptrace.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
#include <asm/string.h>
#include <linux/timex.h>

MODULE_AUTHOR("Jacob Bower");
MODULE_DESCRIPTION("Procmon kernel module 0.3");
MODULE_LICENSE("GPL");

int (*system_execve) (struct pt_regs regs);
extern void *sys_call_table[];
typedef struct tagqueue_entry {
        struct tagqueue_entry *next;       
        char *data;
} queue_entry_t;
spinlock_t queue_lock = SPIN_LOCK_UNLOCKED;
queue_entry_t *head;
queue_entry_t *tail;
#define MAX_QUEUE_LENGTH 65535
int queue_len;
int active = 0;
int read_offs;
char *current_log_entry;
DECLARE_WAIT_QUEUE_HEAD(procmon_wait);
void queue_init(void)
{
        head = tail = NULL;
        queue_len = 0;
}

void queue_clear(void)
{
        spin_lock(&queue_lock);
        while(tail) {
                queue_entry_t *this_entry = tail;
                tail = this_entry->next;
                if(this_entry->data)
                        kfree(this_entry->data);
                kfree(this_entry);
        }
        head = NULL;
        queue_len = 0;
        spin_unlock(&queue_lock);
}

char *queue_pop(void)
{
        char *data;
        queue_entry_t *old_entry;
        spin_lock(&queue_lock);
        if(queue_len == 0) {
                spin_unlock(&queue_lock);
                return NULL;
        }
        data = tail->data;
        old_entry = tail;
        if(tail == head)
                head = NULL;
        tail = tail->next;
        kfree(old_entry);
        queue_len--;
        spin_unlock(&queue_lock);
        return data;
}

int queue_push(char *data)
{
        queue_entry_t *new_entry;
        spin_lock(&queue_lock);
        if((queue_len + 1) > MAX_QUEUE_LENGTH) {
                printk("Procmon: proccess logging queue overflow (try increasing MAX_QUEUE_LENGTH)!\n");
                spin_unlock(&queue_lock);
                return 0;
        }
        queue_len++;
        new_entry = kmalloc(sizeof(queue_entry_t), GFP_KERNEL);
        if(!new_entry) {
                printk("Procmon: failed to allocate memory for queue entry!\n");
                spin_unlock(&queue_lock);
                return 0;
        }
        new_entry->data = data;
        new_entry->next = NULL;
        if(!tail)
                tail = new_entry;
        if(head)
                head->next = new_entry;
        head = new_entry;       
        spin_unlock(&queue_lock);
        return 1;
}

long get_timestamp(void)
{
        struct timeval now;
        int i;
        do_gettimeofday(&now);
        i = now.tv_sec;
        return i;
}

void dolog(char *processname, char *args)
{
        if(active) {
                char *data;
                data = kmalloc(strlen(processname) + strlen(args) + 30, GFP_KERNEL);
                if(!data) {
                        printk("Warning: process name/args too long to log!!");
                        return ;
                }
                sprintf(data, "%li:%i:%i:%s%s",
                                (long)get_timestamp(),
                                (int)current->uid,
                                (int)current->pid,
                                processname, args);
                if( !queue_push(data) )
                        kfree(data);
                else       
                        wake_up_interruptible(&procmon_wait);
        }
}

char *buildargstr(char **args)
{
        char **arg;
        int argslen = 0;
        char *argstr;
        arg = ++args;
        while(*arg)
                argslen += 1 + strlen(*arg++);               
        argstr = kmalloc(argslen + 1, GFP_KERNEL);
        if(!argstr) {
                argstr = kmalloc(17, GFP_KERNEL);
                strcpy(argstr, "<arg overflow!!>");
        } else {
                argstr[0] = '\0';       
               
                while(*args) {
                        strcat(argstr, " ");
                        strcat(argstr, *args++);
                }
        }
        return argstr;
}

asmlinkage int proc_log_execve(struct pt_regs regs)
{
        int error;
        char * filename;
        char * argstr;
        MOD_INC_USE_COUNT;
        argstr = buildargstr((char **) regs.ecx);       
        filename = getname((char *) regs.ebx);
        error = PTR_ERR(filename);
        if (IS_ERR(filename))
                goto out;
        error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, &regs);
        if (error == 0) {
                current->ptrace &= ~PT_DTRACE;
                dolog(filename, argstr);               
        }
        putname(filename);
        kfree(argstr);
out:
        MOD_DEC_USE_COUNT;
        return error;
}

static ssize_t read_procmon(struct file *flip, char *dest,
                                                        size_t len, loff_t *off)
{
        int bytes_read = 0;
        while(len && !bytes_read) {
                if(!current_log_entry) {
                        read_offs = 0;
                        current_log_entry = queue_pop();
                }
                while(current_log_entry && len--) {
                        if(read_offs < strlen(current_log_entry))
                                *dest++ = current_log_entry[read_offs++];
                        else {
                                kfree(current_log_entry);
                                current_log_entry = queue_pop();
                                *dest++ = '\n';
                                read_offs = 0;                       
                        }
                        bytes_read++;
                }
                if(flip->f_flags & O_NONBLOCK)
                        return -EAGAIN;
                if(!bytes_read) {
                        interruptible_sleep_on(&procmon_wait);
                        if(signal_pending(current))
                                return -ERESTARTSYS;
                }               
        }
        *off += bytes_read;
        return bytes_read;
}

static int release_procmon(struct inode *inode, struct file *flip)
{
        active = 0;
        if(current_log_entry)
                kfree(current_log_entry);
        queue_clear();
        MOD_DEC_USE_COUNT;
        return 0;
}

static int open_procmon(struct inode *inode, struct file *flip)
{
        if(active)
                return -EBUSY;
        else {
                current_log_entry = NULL;
                queue_init();
                active = 1;
                MOD_INC_USE_COUNT;
                return 0;
        }
}

#define PROCMON_MAJOR 240

static struct file_operations procmon_fops = {
        read:    read_procmon,
        open:    open_procmon,
        release: release_procmon,
};

int procmon_register(void)
{
        if(devfs_register_chrdev(PROCMON_MAJOR, "procmon", &procmon_fops)) {
                printk("Unable to get major %d for log dev(s)", PROCMON_MAJOR);
                return 0;
        }
        return 1;
}

void procmon_deregister(void)
{
        devfs_unregister_chrdev(PROCMON_MAJOR, "procmon");
}

int init_module(void)
{   
        if( procmon_register() ) {
                system_execve = sys_call_table[__NR_execve];      
                sys_call_table[__NR_execve] = proc_log_execve;
                return 0;
        } else       
                return -1;
}

void cleanup_module(void)
{
        if( sys_call_table[__NR_execve] != proc_log_execve ) {
                printk("!!WARNING!! Another module has hooked the system excve function\n");
                printk("original system function restored. System may be screwed.\n");
        }
        sys_call_table[__NR_execve] = system_execve;
      if(current_log_entry)
                kfree(current_log_entry);
        queue_clear();
        procmon_deregister();
}

Dragonfly 发表于 2003-5-26 06:15:01

i think this code is plain to read. and i think instead of use a self constructed queue, the list_head is more easier to use. and too many kmalloc here.
页: [1]
查看完整版本: chrdev&systemcallhook example