安全指南 > 安全技术 > 自制Linux Rootkit检测工具

自制Linux Rootkit检测工具

### 前言

Linux系统中存在用户态与内核态,当用户态的进程需要申请某些系统资源时便会发起系统调用。而内核态如何将系统的相关信息实时反馈给用户态呢,便是通过proc文件系统。如此便营造了一个相对隔离的环境。


那么proc文件系统是如何呈现我们平时最关心的进程/网络连接信息的呢?在/proc目录下存在着一些以数字命名的目录,这些数字便对应了系统中正在运行的进程的pid。自然,对应进程的一些相关信息便保存在/proc/{pid}目录下。比如/proc/{pid}/fd目录中就保存了进程打开的文件描述符。



系统会将tcp协议的连接信息保存在/proc/net/tcp文件中。其他网络协议的连接信息也均保存在/proc/net对应协议的文件中。需要特别指出的是,上图中socket文件名中括号里的数字与下图中inode列是存在对应关系的。




综上所述,像ps和netstat这类命令便是读取了proc文件系统中提供的数据,然后将数据格式化输出给用户的。出于隐藏指定进程/网络连接的目的,大部分Rootkit对写proc文件的回调函数做了些手脚,造成黑客指定的一些进程/网络连接的信息不会出现在proc文件系统中。于是,也就实现了进程/网络连接的隐藏。


### 探索


那么,如何发现那些被隐藏的进程/网络连接呢?


首先是进程,这个其实并不复杂。系统给我们提供了一个方便的全局变量current。current永远指向当前正在运行的进程的task_struct数据结构,而进程的主要信息都包含在这个名为task_struct的巨大结构体中。当我们遍历到系统中所有进程task_struct结构体中的pid值,然后再去/proc目录下去寻找是否有对应的值。一但我们得到的pid未出现在ps的返回结果中,那么说明该进程被隐藏了。


如何发现被隐藏的网络连接呢?


其实还是基于上一步进一步挖掘,因为网络IO请求都是由进程发起的,那么理论上来说我们肯定可以从进程一步步“捋”到socket。Linux一切皆文件的准则告诉我们,所谓网络IO其实就是读写socket文件,而每个socket结构体中都会有一个对应的inet_sock结构体,来记录IP协议下网络连接的四元组信息。那么我们把这些四元组再和netstat命令的返回结果比对一下便可知是否存在隐藏的网络信道了。


总结出来在Kernel2.6.32中,进程-文件-网络三者之间大致的关系如下图所示。

fdtable结构体的成员fd字符数组中存储的是file结构体,每当进程开启了一个文件描述符之后会自动在fd数组中新增一个对应的file结构体。通过获取file结构体中的f_path成员,我们可以获得对应的path结构体。系统内置的d_path函数可以返回path结构体对应文件的绝对路径信息。当d_path返回了一个“socket:[xxx]”的信息时,就说明这是个socket文件,该进程在试图进行网络IO。我们引用这个file结构体的private_data成员就可以获得这个socket文件对应的socket结构体了。最后通过内置的inet_sk()函数我们就可以找到对应socket文件的inet_sock结构体,我们需要的四元组便存在这个结构体中。


### 代码

#include <linux/init.h> 
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/file.h>
#include <linux/dcache.h>
#include <linux/fdtable.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/limits.h>
#include <linux/string.h>
#include <linux/net.h>
#include <net/inet_sock.h>
  
static int __init mychk_init(void); 
static void __exit mychk_exit(void); 
  
module_init(mychk_init); 
module_exit(mychk_exit); 
  
MODULE_LICENSE("GPL"); 
  
void get_socket(struct file *f,char* sock_name){
        struct socket *st = NULL;
        struct inet_sock *is = NULL;
        st = f->private_data;
        is = inet_sk(st->sk);
        printk(KERN_ALERT"%s: LA: %d.%d.%d.%d:%u  FA: %d.%d.%d.%d:%u\n",sock_name,NIPQUAD(is->saddr),ntohs(is->sport),NIPQUAD(is->daddr),ntohs(is->dport));
}
  
char* get_path(struct path p) {
        char *buff = NULL;
        char *path = NULL;
        buff = kmalloc(PATH_MAX,GFP_KERNEL);
        if (!buff) 
                return NULL;
        path = d_path(&p,buff,PATH_MAX);
        return path;
}
  
static int __init mychk_init(void) {
        struct task_struct *t = NULL;
        struct files_struct *f = NULL;
        struct file *file = NULL;
        struct fdtable *fdt = NULL;
        struct path p;
        char *msg = NULL;
        int i;
        list_for_each_entry(t,&current->tasks,tasks) {
                printk(KERN_ALERT"Process:%s [%d]\n", t->comm, t->pid);
                task_lock(t);
                f = t->files;
                if(NULL != f){
                        fdt = f->fdt;
                        for (i = 0;i<NR_OPEN_DEFAULT;i++) {
                                if (fdt != NULL) {
                                        file = fdt->fd[i];
                                        if(file != NULL && file->f_path.dentry != NULL) {
                                                p = file->f_path;
                                                msg = get_path(p);
                                                if (msg != NULL && msg != strstr(msg,"socket"))
                                                        printk(KERN_ALERT"-- %s\n",msg);
                                                else
                                                        get_socket(file,msg);
                                        }
                                }
                        }
                }
                task_unlock(t);
        }
        return 0;
}
  
static void __exit mychk_exit(void) {
        printk(KERN_ALERT"Remove Module!\n"); 
}
 

### 运行结果


Process:sshd [1840]
-- /dev/null
-- /dev/null
-- /dev/null
socket:[16185]: LA:192.168.11.144:22  FA: 192.168.11.1:61493
-- pipe:[16256]
-- pipe:[16256]
-- /dev/ptmx
-- /dev/ptmx
-- /dev/ptmx
Process:bash [1844]
-- /dev/pts/1
-- /dev/pts/1
-- /dev/pts/1
Process:mysqld_safe [3668]
-- /dev/null
-- /dev/null
-- /dev/null
Process:mysqld [3770]
-- /dev/null
-- /var/log/mysqld.log
-- /var/log/mysqld.log
-- /var/lib/mysql/ibdata1
-- /tmp/ibABwsCH (deleted)
-- /tmp/ibLWJRBw (deleted)
-- /tmp/ibVCthBl (deleted)
-- /tmp/ibogYHLa (deleted)
-- /var/lib/mysql/ib_logfile0
-- /var/lib/mysql/ib_logfile1
socket:[18993]: LA:0.0.0.0:3306  FA: 0.0.0.0:0
-- /tmp/ibgDa0v2 (deleted)
-- /var/lib/mysql/mysql/host.MYI
-- /var/lib/mysql/mysql/host.MYD
-- /var/lib/mysql/mysql/user.MYI
-- /var/lib/mysql/mysql/user.MYD
-- /var/lib/mysql/mysql/db.MYI
-- /var/lib/mysql/mysql/db.MYD
-- /var/lib/mysql/mysql/tables_priv.MYI
-- /var/lib/mysql/mysql/tables_priv.MYD
-- /var/lib/mysql/mysql/columns_priv.MYI
-- /var/lib/mysql/mysql/columns_priv.MYD
-- /var/lib/mysql/mysql/procs_priv.MYI
-- /var/lib/mysql/mysql/procs_priv.MYD
-- /var/lib/mysql/mysql/servers.MYI
-- /var/lib/mysql/mysql/servers.MYD
-- /var/lib/mysql/mysql/event.MYI
-- /var/lib/mysql/mysql/event.MYD



网易安全应急响应中心微信公众号,精湛漏洞技术分享,丰厚奖励活动公告,网易周边风景推送,一个不容错过的微信公众号,喜欢的赶快来关注吧!

推荐阅读

    About NetEase- 公司简介- 联系方法- 招聘信息- 客户服务- 隐私政策- 网络营销- 网站地图- 意见反馈- 合作伙伴
    关注我们 /
    网易版权公司所有 © 1997-