QQ登录

只需一步,快速开始

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 894|回复: 3

chrdev&systemcallhook example

[复制链接]
发表于 2003-5-26 06:09:35 | 显示全部楼层 |阅读模式
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).
 楼主| 发表于 2003-5-26 06:10:19 | 显示全部楼层
Makefile
[code:1]
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
[/code:1]
回复

使用道具 举报

 楼主| 发表于 2003-5-26 06:11:45 | 显示全部楼层
procmon.c
[code:1]
#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();
}
[/code:1]
回复

使用道具 举报

 楼主| 发表于 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.
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

GMT+8, 2024-9-19 18:53 , Processed in 0.060771 second(s), 15 queries .

© 2021 Powered by Discuz! X3.5.

快速回复 返回顶部 返回列表