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). 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
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();
}
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]