一个FreeBSD下的内核级文件隐藏程序

栏目: 服务器 · 发布时间: 7年前

内容简介:很多情况下,我们会有这样的需求,隐藏文件,隐藏端口,隐藏进程等。例如

这是一个在内核里hook来隐藏文件的程序,可以在FreeBSD 11上使用。

背景

很多情况下,我们会有这样的需求,隐藏文件,隐藏端口,隐藏进程等。

例如

某服务需要的关键性文件为了防止被攻击,需要隐藏;

病毒为了躲避追杀,隐藏自己的文件、进程。

这里,就如何隐藏文件,做一个简单的hook。其它的需求,例如隐藏进程等,思路相似,很容易就可以迁移使用。

思路

单就实现方法来说,有两种比较容易想到的思路:

1. 首先请求当前列表,再除去想要隐藏的东西,返回给上层。

这个东西可以做在用户层,也可以做在内核层。

共通的一点,都是使用hook的方法。

2. 直接在资源列表内删除该项目,再写一个特殊的方法来访问这个资源。

这个方法有一定的危险性,这里不作讨论。

本文使用第一种方案,来达到隐藏文件的目的。

作为举例,使用FreeBSD 11作为实现平台。

如果你对FreeBSD不太熟悉,可以先看下我之前在FreeBuf写的这篇文章。

一个FreeBSD下的通信协议监控程序: http://www.freebuf.com/geek/179036.html

基础知识

首先,我们需要知道在系统内核是怎样处理文件列表请求的。

如果你在安装FreeBSD时,安装了FreeBSD源码,那么这个函数在/usr/src/sys/kern/vfs_syscalls.c内。

intsys_getdirentries(struct thread *td, struct getdirentries_args *uap){    long base;    int error;    error = kern_getdirentries(td, uap->fd, uap->buf, uap->count, &base,        NULL, UIO_USERSPACE);    if (error != 0)        return (error);    if (uap->basep != NULL)        error = copyout(&base, uap->basep, sizeof(long));    return (error);}intkern_getdirentries(struct thread *td, int fd, char *buf, u_int count,    long *basep, ssize_t *residp, enum uio_seg bufseg){    struct vnode *vp;    struct file *fp;    struct uio auio;    struct iovec aiov;    cap_rights_t rights;    long loff;    int error, eofflag;    off_t foffset;    AUDIT_ARG_FD(fd);    if (count > IOSIZE_MAX)        return (EINVAL);    auio.uio_resid = count;    error = getvnode(td, fd, cap_rights_init(&rights, CAP_READ), &fp);    if (error != 0)        return (error);    if ((fp->f_flag & FREAD) == 0) {        fdrop(fp, td);        return (EBADF);    }    vp = fp->f_vnode;    foffset = foffset_lock(fp, 0);unionread:    if (vp->v_type != VDIR) {        error = EINVAL;        goto fail;    }    aiov.iov_base = buf;    aiov.iov_len = count;    auio.uio_iov = &aiov;    auio.uio_iovcnt = 1;    auio.uio_rw = UIO_READ;    auio.uio_segflg = bufseg;    auio.uio_td = td;    vn_lock(vp, LK_SHARED | LK_RETRY);    AUDIT_ARG_VNODE1(vp);    loff = auio.uio_offset = foffset;#ifdef MAC    error = mac_vnode_check_readdir(td->td_ucred, vp);    if (error == 0)#endif        error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, NULL,            NULL);    foffset = auio.uio_offset;    if (error != 0) {        VOP_UNLOCK(vp, 0);        goto fail;    }    if (count == auio.uio_resid &&        (vp->v_vflag & VV_ROOT) &&        (vp->v_mount->mnt_flag & MNT_UNION)) {        struct vnode *tvp = vp;        vp = vp->v_mount->mnt_vnodecovered;        VREF(vp);        fp->f_vnode = vp;        fp->f_data = vp;        foffset = 0;        vput(tvp);        goto unionread;    }    VOP_UNLOCK(vp, 0);    *basep = loff;    if (residp != NULL)        *residp = auio.uio_resid;    td->td_retval[0] = count - auio.uio_resid;fail:    foffset_unlock(fp, foffset, 0);    fdrop(fp, td);    return (error);}

内核在收到用户层对某个fd的getdirentries请求时,调用kern_getdirentries()返回结果。

在kern_getdirentries()内,会寻找目标fd的vnode,然后针对这个vnode,加锁,复制列表到iovec。

如果全部介绍vnode和iovec的话,可能会比较耗费篇幅,这里简单的说一下。

vnode可以简单的理解成内核在操作文件、目录时使用的东西。

iovec则可以看作是操作缓存。

如果对这些感兴趣的话,可以在FreeBSD内核源码中查看具体的实现。

那么,经过以上步骤,入参fd的目录信息就被复制到uap->buf内了。

这个缓存实质上是一系列的dirent的结构体,如果需要隐藏某个文件,把dirent里的对应内容删掉就可以了。

开始干活。

具体实现

首先,定义一个和sys_getdirentries()一样的函数。

static intgetdirentries_hook(struct thread *td, struct getdirentries_args *uap);

然后,写一个载入内核组建的载入函数,在组件加载时替换函数指针,在卸载时还原。

static int
load(struct module *module, int cmd, void *arg)
{
    int error = 0;

    switch (cmd) {
    case MOD_LOAD:
        sysent[SYS_getdirentries].sy_call = (sy_call_t *)getdirentries_hook;
        break;

    case MOD_UNLOAD:
        sysent[SYS_getdirentries].sy_call = (sy_call_t *)sys_getdirentries;
        break;

    default:
        error = EOPNOTSUPP;
        break;
    }

    return(error);
}

下面就可以设计这个hook函数了。

首先理清思路。

1. 使用原始的函数,获取输出;
2. 检查输出list里是否含有隐藏的文件,有则将其删除。

在之前的kern_getdirentries()可以看到,size是存在td->td_retval[0]内的。

所以,步骤1的实现应该是,获取返回数据,检查是否真正存在数据。

sys_getdirentries(td, uap);size = td->td_retval[0];if (size > 0) {    ...}

在确认数据存在后,把uap->buf的数据放回内核层,然后就可以读取数据了。

那么进行步骤2。

在读取数据时,由于最后一个路径是NULL,因此需要一个和size同样值的缓存sum来计数。

假设在一开始指向实际数据区的dirent型指针为p,读取数据的过程应该是这样。

loop_start:if ((p->d_reclen != 0) && (sum > 0)) {    sum -= ct->d_reclen;    ...    if (sum != 0) {        p = (struct dirent *)((char *)p + p->d_reclen);    }    goto loop_start;}
loop_end:
...

接下来,假设需要隐藏的文件叫做rochek。

对比文件名,如果对比成功,覆盖掉。

if (strcmp((char *)&(p->d_name), "rochek") == 0) {    if (sum != 0) {    bcopy((char *)p + p->d_reclen, p, sum);    }    size -= p->d_reclen;    goto loop_end;}

最后,把缓存的size还给td->td_retval[0],再把数据复制回去,这个函数就完成了。

实际效果:

一个FreeBSD下的内核级文件隐藏程序

分别为rootkit未加载,rootkit加载后,rootkit卸载后查看test目录的结果。

总结

这样,就完成了一个FreeBSD下内核级的文件隐藏程序。

使用类似思路,可以做一些其它的事情,例如隐藏进程、隐藏端口等,这里就不做过多的讨论了。

实时上,这种隐藏很容易就可以使用更加专业的检测工具,例如ossec检测到。

至于如何反检测,思路和隐藏文件的思路相似,这里不做过多的讨论。

最后,祝享受hack内核的过程。

*本文作者:rochek,转载请注明来自FreeBuf.COM


以上所述就是小编给大家介绍的《一个FreeBSD下的内核级文件隐藏程序》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Mastering Regular Expressions, Second Edition

Mastering Regular Expressions, Second Edition

Jeffrey E F Friedl / O'Reilly Media / 2002-07-15 / USD 39.95

Regular expressions are an extremely powerful tool for manipulating text and data. They have spread like wildfire in recent years, now offered as standard features in Perl, Java, VB.NET and C# (and an......一起来看看 《Mastering Regular Expressions, Second Edition》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

html转js在线工具
html转js在线工具

html转js在线工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换