linux 隱藏進程 – crux實現

來源:互聯網
上載者:User

    本文在不修改ps或top命令的任何代碼與採用將進程號置0的方法的前提下,實現隱藏進程,本程式在CRUX 2.2上實現

 

1、原理

   Linux中,可以通過/proc檔案系統訪問到許多核心的內部資訊。/proc檔案系統最初的設計也是用於方便地訪問進程相關的資訊,因此命名為proc。現在這個檔案系統已用於反映系統中方方面面的資訊,例如/proc/modules是模組的列表,/proc/meminfo則是記憶體使用量的統計。/proc檔案系統中的目錄並非持久儲存的資訊,也就是說,其目錄並不“真實”地存在於磁碟,而是在訪問時動態產生。

     本項目中我們感興趣的是/proc檔案系統中關於進程的資訊。每一個進程在/proc檔案系統中有一個目錄即(/proc/<pid>),目錄名即進程號。self目錄是一個連結,指向當前進程。

     ps命令和top命令從/proc檔案系統中讀取進程資訊並顯示出來。因此,如果一個進程的進程號沒有在/proc檔案系統中反映出來,則這個進程被“隱藏”了,“隱藏”進程在ps或top命令的輸出不出現。

 

2、實現

2.1 為task_struct添加變數hide如下:(include/linux/sched.h)

 

    struct task_struct {<br /> unsigned did_exec:1;<br /> pid_t pid;<br /> pid_t tgid;<br /> ...(省略)<br /> char hide;(在最後一行添加該句)<br />} 

 

2.2 哪裡修改proc代碼

     在linux代碼樹中,所有檔案系統的代碼都放在linux/fs/目錄中,其中,proc檔案系統的原始碼在linux/fs/proc中,下面我簡單介紹一下proc目錄中的源檔案。 

     在目錄中共有11個相關檔案,他們是:
     procfs_syms.c inode.c generic.c base.c
     array.c root.c proc_tty.c proc_misc.c
     kmsg.c kcore.c proc_devtree.c

     這麼多的檔案我們如何去找呢,借鑒網上的資料,對各個檔案進行尋找可知:
     其中,procfs_syms.c,generic.c及inode.c和proc檔案系統的管理相關,包括proc檔案系統的註冊,及向核心其他子系統提供的常式等等。
     源檔案root.c和proc檔案系統的根結點的管理相關。
     而base.c,array.c則用來處理/proc目錄中進程的資訊,包括命令列,進程狀態,記憶體狀態等等和進程相關的內容。proc_tty.c用來處理/proc/tty資訊,proc_misc.c則用來管理和/proc目錄中的大多數檔案。
     除此之外,更有兩個非常重要的標頭檔proc_fs.h,proc_fs_i.h,我們能在/linux/include/linux/目錄中找到。

     同時,最重要的是,我們確定了我們所要需要的相關資訊在base.c檔案中,主要是在這2個函數之間:proc_pid_readdir,proc_task_readdir,但是是哪個函數呢?由於網上的資料很少,沒辦法一個一個試。通過查看著2個看書的原始碼:

 

    nt proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir)<br />{<br />unsigned int tgid_array[PROC_MAXPIDS];<br />char buf[PROC_NUMBUF];<br />unsigned int nr = filp->f_pos - FIRST_PROCESS_ENTRY;<br />unsigned int nr_tgids, i;<br />int next_tgid;<br />if (!nr) {<br />ino_t ino = fake_ino(0,PROC_TGID_INO);<br />if (filldir(dirent, "self", 4, filp->f_pos, ino, DT_LNK) < 0)<br />return 0;<br />filp->f_pos++;<br />nr++;<br />}<br />/* f_version caches the tgid value that the last readdir call couldn't<br /> * return. lseek aka telldir automagically resets f_version to 0.<br /> */<br />next_tgid = filp->f_version;<br />filp->f_version = 0;<br />for (;;) {<br />nr_tgids = get_tgid_list(nr, next_tgid, tgid_array);<br />if (!nr_tgids) {<br />/* no more entries ! */<br />break;<br />}<br />next_tgid = 0;<br />/* do not use the last found pid, reserve it for next_tgid */<br />if (nr_tgids == PROC_MAXPIDS) {<br />nr_tgids--;<br />next_tgid = tgid_array[nr_tgids];<br />}<br />for (i=0;i<nr_tgids;i++) {<br />int tgid = tgid_array[i];<br />ino_t ino = fake_ino(tgid,PROC_TGID_INO);<br />unsigned long j = PROC_NUMBUF;<br />do<br />buf[--j] = '0' + (tgid % 10);<br />while ((tgid /= 10) != 0);<br />if (filldir(dirent, buf+j, PROC_NUMBUF-j, filp->f_pos, ino, DT_DIR) < 0) {<br />/* returning this tgid failed, save it as the first<br />* pid for the next readir call */<br />filp->f_version = tgid_array[i];<br />goto out;<br />}<br />filp->f_pos++;<br />nr++;<br />}<br />}<br />out:<br />return 0;<br />}<br /> 

 

    /* for the /proc/TGID/task/ directories */<br />static int proc_task_readdir(struct file * filp, void * dirent, filldir_t filldir)<br />{<br />unsigned int tid_array[PROC_MAXPIDS];<br />char buf[PROC_NUMBUF];<br />unsigned int nr_tids, i;<br />struct dentry *dentry = filp->f_dentry;<br />struct inode *inode = dentry->d_inode;<br />int retval = -ENOENT;<br />ino_t ino;<br />unsigned long pos = filp->f_pos; /* avoiding "long long" filp->f_pos */<br />if (!pid_alive(proc_task(inode)))<br />goto out;<br />retval = 0;<br />switch (pos) {<br />case 0:<br />ino = inode->i_ino;<br />if (filldir(dirent, ".", 1, pos, ino, DT_DIR) < 0)<br />goto out;<br />pos++;<br />/* fall through */<br />case 1:<br />ino = parent_ino(dentry);<br />if (filldir(dirent, "..", 2, pos, ino, DT_DIR) < 0)<br />goto out;<br />pos++;<br />/* fall through */<br />}<br />nr_tids = get_tid_list(pos, tid_array, inode);<br />inode->i_nlink = pos + nr_tids;<br />for (i = 0; i < nr_tids; i++) {<br />unsigned long j = PROC_NUMBUF;<br />int tid = tid_array[i];<br />ino = fake_ino(tid,PROC_TID_INO);<br />do<br />buf[--j] = '0' + (tid % 10);<br />while ((tid /= 10) != 0);<br />if (filldir(dirent, buf+j, PROC_NUMBUF-j, pos, ino, DT_DIR) < 0)<br />break;<br />pos++;<br />}<br />out:<br />filp->f_pos = pos;<br />return retval;<br /> 

 

    通過觀察,我們可以大膽的猜測,filldir()函數 就是往/proc添加<pid>目錄的函數,我們可以做個實驗,即,在filldir()下面再添加一個filldir()函數,這樣的話,一個進程在/proc目錄下就會有2個目錄,同時,ps -ax顯示的同一個進程也會有2個。

    修改完後編譯核心:

    cd /usr/src/linux-<版本號碼>

    make bzImage

    cp arch/i386/boot/bzImage /boot/vmlinuz-0.1

    cp System.map /boot/System.map-0.1

    修改grub,為修改的核心添加相應的啟動項

    vim /boot/grub/menu.lst

    

    reboot重新啟動,在啟動後選擇CRUX_TEST選項,使用我們修改完的核心,

    輸入ps -ax或者查看/proc檔案便會出現我們預先猜測的結果,同時,經過驗證可知,ps, top命令調用的是proc_pid_readdir函數,而不是proc_task_readdir函數。嘿,在這,我們就發現新大陸了!

 

 

2.3 怎麼修改proc

     由2.2可知,我們需要修改在函數為proc_pid_readdir,仔細閱讀相關代碼可知,我們只需在if (filldir(dirent, buf+j, PROC_NUMBUF-j, filp->f_pos, ino, DT_DIR) < 0) 前判斷進程是否是否有設定為隱藏,而在之前,我們要先擷取該進程的task_struct,具體代碼如下(注釋處即為添加的代碼):

 

     int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir)<br />{<br />unsigned int tgid_array[PROC_MAXPIDS];<br />char buf[PROC_NUMBUF];<br />unsigned int nr = filp->f_pos - FIRST_PROCESS_ENTRY;<br />unsigned int nr_tgids, i;<br />int next_tgid;<br />task_t *task; //聲明一個 task_struct<br />if (!nr) {<br />ino_t ino = fake_ino(0,PROC_TGID_INO);<br />if (filldir(dirent, "self", 4, filp->f_pos, ino, DT_LNK) < 0)<br />return 0;<br />filp->f_pos++;<br />nr++;<br />}<br />/* f_version caches the tgid value that the last readdir call couldn't<br /> * return. lseek aka telldir automagically resets f_version to 0.<br /> */<br />next_tgid = filp->f_version;<br />filp->f_version = 0;<br />for (;;) {<br />nr_tgids = get_tgid_list(nr, next_tgid, tgid_array);<br />if (!nr_tgids) {<br />/* no more entries ! */<br />break;<br />}<br />next_tgid = 0;<br />/* do not use the last found pid, reserve it for next_tgid */<br />if (nr_tgids == PROC_MAXPIDS) {<br />nr_tgids--;<br />next_tgid = tgid_array[nr_tgids];<br />}<br />for (i=0;i<nr_tgids;i++) {<br />int tgid = tgid_array[i];<br />ino_t ino = fake_ino(tgid,PROC_TGID_INO);<br />unsigned long j = PROC_NUMBUF;<br />//獲得進程的pid<br />task = find_task_by_pid(tgid);<br />do<br />buf[--j] = '0' + (tgid % 10);<br />while ((tgid /= 10) != 0);</p><p>printk(KERN_DEBUG "pid:%d, hide:%d/n", task->pid, task->hide);<br />//判斷進程是否為隱藏<br />if(task != NULL && task->hide == 0)<br />{<br />printk(KERN_DEBUG "task:%d no hide/n", task->pid);<br />if (filldir(dirent, buf+j, PROC_NUMBUF-j, filp->f_pos, ino, DT_DIR) < 0) {<br />/* returning this tgid failed, save it as the first<br /> * pid for the next readir call */<br />filp->f_version = tgid_array[i];<br />goto out;<br />}<br />}<br />filp->f_pos++;<br />nr++;</p><p>}<br />}<br />out:<br />return 0;<br />} 

 

     另外需要在kernel/fork.c的copy_process()函數中添加初始化hide代碼(雖可不用,但為了保證邏輯,還是添加吧)

     ...省略...<br />if (p->binfmt && !try_module_get(p->binfmt->module))<br />goto bad_fork_cleanup_put_domain;<br />p->did_exec = 0;<br />copy_flags(clone_flags, p);<br />p->pid = pid;<br />p->hide = 0;<br />retval = -EFAULT;<br />...省略... 

     重新編譯核心

 

2.4 添加系統調用

     為了驗證我們2.3所修改的核心代碼,我們需添加相應的系統調用,設定task_struct的hide,為此,我們添加2個系統調用,修改如下

     2.4.1 kernel/sys.c添加原始碼

    asmlinkage long sys_hide()<br />{<br />if(current->hide == 1)<br />return -EFAULT;<br />else<br />current->hide = 1;<br />return 0;<br />}<br />asmlinkage long sys_unhide()<br />{<br />if(current->hide == 0)<br />return -EFAULT;<br />else<br />current->hide = 0;<br />return 0;<br />}

 

    2.4.2 include/asm-i386/unistd.h添加系統調用號及系統的調用總數

    #define __NR_inotify_add_watch292<br />#define __NR_inotify_rm_watch293<br />#define __NR_hide 294<br />#define __NR_unhide 295<br />#define NR_syscalls 296 //系統調用總數 

     2.4.3 arch/i386/kernel/syscall_table.S在系統調用表中添加相應項,在最後一行添加

     .long sys_hide<br />.long sys_unhide 

     2.4.4 在include/linux/syscalls.h添加函式宣告(貌似這個可有可無)

     asmlinkage long hide();<br />asmlinkage long unhide(); 

 

     至此,添加系統調用完成,重新編譯核心

 

3 測試

    我們編寫了一個測試函數,用來我們修改的核心是否成功,代碼hide.c如下:

 

     <br />#include <stdio.h><br />#include <sys/types.h><br />#include <linux/unistd.h><br />#define __NR_hide 294<br />#define __NR_unhide 295<br />int main(int argc, char **argv)<br />{<br />int i=0;<br />printf("original/n");<br />system("ps");<br />int i = syscall(__NR_hide);//調用系統號為294的系統調用<br />printf("hide:/n");<br />system("ps");<br />i = syscall(__NR_unhide);<br />printf("unhide:/n");<br />system("ps");<br />return 0;<br />}<br />

 

     gcc hide.c -o hide後,執行 ./hide  ,查看結果

 

    

 

 

      測試成功,成功隱藏進程!

 

4、結束語

     這隻是實現隱藏的一種方式,網上看到的一個是攔截系統調用,我之前想的是攔截中斷,直接在中斷裡面修改,這個有待驗證,以後有機會實現的話也會貼出來和大家分享。

     這個程式從開始到完成大概花了我6,7個小時的時間,說起來難度也不大,核心代碼的修改量很小,問題的關鍵處是找到在何處修改代碼,這個俺也是邊查資料邊做實驗才找到的。

     [2011-2-25]現已實現通過攔截系統調用隱藏進程的方式,分享與大家:http://blog.csdn.net/billpig/archive/2011/02/20/6196163.aspx

 

     著作權,轉載請出明出處!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.