一、實驗內容
1.閱讀並分析Linux內核源代碼,了解進程控制塊、進程隊列等數據結構;
2.實現一個系統調用,使得可以根據指定的參數隱藏進程,使用戶無法使用ps或top觀察到進程狀態。具體要求如下:
(1)實現系統調用int hide(pid_t pid, int on),在進程pid有效的前提下,如果on置1,進程被隱藏,用戶無法通過ps或top觀察到進程狀態;如果on置0且此前為隱藏狀態,則恢復正常狀態。
(2)考慮權限問題,只有根用戶才能隱藏進程。
(3)設計一個新的系統調用int hide_user_processes(uid_t uid, char *binname),參數uid為用戶ID號,當binname參數為NULL時,隱藏該用戶的所有進程;否則,隱藏二進制映像名為binname的用戶進程。該系統調用應與hide系統調用共存。
(4)在/proc目錄下創建一個文件/proc/hidden,該文件可讀可寫,對應一個全局變量hidden_flag,當hidden_flag為0時,所有進程都無法隱藏,即便此前進程被hide系統調用要求隱藏。只有當hidden_flag為1時,此前通過hide調用要求被屏蔽的進程才隱藏起來。
(5)在/proc目錄下創建一個文件/proc/hidden_process,該文件的內容包含所有被隱藏進程的pid,各pid之間用空格分開。
二、實驗目的
1.加深理解進程控制塊、進程隊列等概念
2.了解進程管理的具體實施方法
自<a>dfx</a>己獨立<span>adsf</span>完成別<b>fdas</b>總是<xxx>fdas</xxx>有事沒事<zzz>fdas</zzz>抄<qq>435fff</qq>別人的哦
三、新增系統調用hide
1、設計思路和流程圖
(1)設置標識
通過設置一個標記位來控制進程是否隱藏。
在include/linux/sched.h中修改結構體 task_struct ,添加一個成員 cloak ,0表示顯示,1表示隱藏。
(2)初始化
進程剛創建時是處於顯示的狀態,所以需要將標記cloak初始化為0。
fork系統調用的實現位於kernel/fork.c中,具體實現的主要函數為do_fork,do_fork中調用copy_process函數創建子進程,在該函數中初始化cloak。
(3)添加hide系統調用
具體流程參考實驗二,具體解釋見代碼部分。
(4)過濾隱藏的進程
修改fs/proc/base.c中的proc_pid_readdir函數以及proc_pid_lookup函數,過濾掉被隱藏的進程。
2、主要數據結構及說明
使用了結構體task_struct,通過對結構體里的cloak等變量進行賦值,改變進程的狀態。
3、源程序並附上注釋
(1)hide.c
使用內核函數find_task_by_pid,通過pid獲取進程task_struct。在隱藏后調用函數proc_flush_task來清空VFS層的緩沖,解除已有的dentry項。
其中包括對用戶權限的判斷,必須是root用戶,即uid=0才能進行相關操作。
#include<linux/linkage.h> #include<linux/types.h> #include<linux/sched.h> #include<linux/pid.h> #include<linux/proc_fs.h> asmlinkage int sys_hide(pid_t pid, int on) { struct task_struct *p=NULL; if(pid>0 && (current->uid)==0)//only the root user can hide the process { p=find_task_by_pid(pid); p->cloak=on;//set the state of the process if(on==1) { printk("Process %d is hidden by root.\n",pid); } if(on==0) { printk("Process %d is displayed by root.\n",pid); } proc_flush_task(p); } else printk("Sorry, you are not root user.Permission denied.\n"); return 0; }
(2)測試程序
比如隱藏進程號為1的進程,代碼如下
#include<stdio.h> #include<sys/syscall.h> #include<unistd.h> int main() { int syscallNum=321; pid_t pid=1; int on=1; syscall(syscallNum,pid,on); return 0; }
4、程序運行結果及分析
(1)初始狀態
目前進程都是處於顯示的狀態,我們的目的是隱藏1號進程
(2)在非root用戶下執行測試程序
進程未被隱藏,用 dmesg 命令查看輸出
(3)切換到root用戶
再次執行程序,結果如下,1號進程被隱藏了
(4)更改參數on=0
被隱藏的進程將再次出現
四、新增系統調用hide_user_processes
1、設計思路和流程圖
基於實驗二以及新增系統調用hidden這兩個實驗,這個實驗很容易完成。
遍歷系統中所有的進程,隱藏滿足要求的進程。使用函數for_each_process遍歷所有進程,每個進程的task_struct中有成員變量uid和comm,uid為該進程的用戶id,comm為進程名,根據要求隱藏對應進程。具體解釋見代碼部分。
2、主要數據結構及說明
使用了結構體task_struct,通過其中的cloak等變量,控制進程的狀態。
3、源程序並附上注釋
(1)hide_user_processes.c
包括三個參數,前兩個參數是根據實驗要求設置的,第三個參數recover用於恢復被隱藏的進程。如果recover=0那么隱藏相應的進程,如果不為0,則恢復所有進程為顯示狀態。
#include<linux/linkage.h> #include<linux/types.h> #include<linux/sched.h> #include<linux/pid.h> #include<linux/proc_fs.h> #include<linux/string.h> asmlinkage int sys_hide_user_processes(uid_t uid,char *binname,int recover){ struct task_struct *p=NULL; if(recover==0) { if(current->uid==0)//1.Paragram recover=0;2.root => you can hide the process { if(binname==NULL)//when null, hide all the processes of the corresponding user { for_each_process(p) { if((p->uid)==uid) { p->cloak=1; proc_flush_task(p); } } printk("All of the processes of uid %d are hidden.\n",uid); } else//otherwise, hide the process of the corresponding name { for_each_process(p) { char *s=p->comm; if(strcmp(s,binname)==0 && p->uid==uid) { p->cloak=1; printk("Process %s of uid %d is hidden.\n",binname,uid); proc_flush_task(p); } } } } else printk("Sorry, you are not root user. Permission denied.\n"); } else if(recover != 0 && (current->uid)==0)//display all of the processes, including which are hidden { for_each_process(p) { p->cloak=0; } } return 0; }
(2)測試程序(可以修改參數,從而實現不同的要求)
#include<stdio.h> #include<sys/syscall.h> #include<unistd.h> int main() { int syscallNum=322; uid_t uid=500; char *binname=NULL;//Author:Kong int recover=0; syscall(syscallNum,uid,binname,recover); return 0; }
4、程序運行結果及分析
(1)與上個實驗一樣,如果非root用戶,則沒有隱藏進程的權限
初始狀態如下
(2)隱藏uid為0,進程名為init的進程
修改參數uid_t uid=0; char *binname="init";
結果如下,對應的進程被隱藏了
(3)隱藏uid=500(對應用戶名為seu)的所有進程
修改參數uid_t uid=500; char *binname=NULL;
結果如下,seu用戶的所有進程被隱藏了
(4)更改參數recover=1,所有進程將恢復為顯示狀態
結果如下
五、在/proc目錄下創建一個文件/proc/hidden
1、設計思路和流程圖
(1)全局變量hidden_flag
因為這個實驗又涉及到隱藏進程的問題,所以需要再設置一個標識作為判斷,hidden文件的讀寫對它進行操作即可。
在/fs/proc目錄下新建一個頭文件,里面包含一個全局變量,其它文件中需要用到這個全局變量的時候,需使用include包含這個頭文件。
1 extern int hidden_flag;
(2)hidden文件的創建及讀寫
proc文件系統在初始化函數proc_root_init中會調用proc_misc_init函數,此函數用於創建/proc根目錄下的文件,那么將創建hidden文件的代碼插入到此函數中就可以在proc初始化時得到執行。在/fs/proc/proc_misc.c中添加回調函數,在/fs/proc/proc_misc.c中proc_misc_init函數的最后添加創建hidden文件的代碼,並指定其回調函數,具體說明見代碼部分。
(3)根據hidden_flag顯示/隱藏進程
結合上面根據cloak判斷進程,這個實驗與之類似,只需在fs/proc/base.c文件中,修改proc_pid_readdir函數以及proc_pid_lookup函數,在cloak判斷之前,增加hidden_flag對進程的約束。
(4)重新編譯安裝內核,重啟之后進行測試。
2、主要數據結構及說明
使用到了結構體、進程控制塊等數據結構。
3、源程序並附上注釋
主要是hidden文件的創建及讀寫。
在/fs/proc/proc_misc.c中添加回調函數,首先初始化全局變量hidden_flag為1;讀操作中sprintf將hidden_flag的值傳給page,然后顯示出來;寫操作中使用copy_from_user先將用戶輸入的值傳給temp,然后從temp中得到值傳給hidden_flag。
int hidden_flag=1; static int proc_read_hidden(char *page,char **start,off_t off,int count,int *eof,void *data) { int len=0; len=sprintf(page,"%d",hidden_flag); return len; } static int proc_write_hidden(struct file *file,const char *buffer,unsigned long count,void *data) { int len=0; char temp[BUF_LEN]; if(count > BUF_LEN) len = BUF_LEN; else len = count; copy_from_user(temp, buffer, len);//convert user's input to temp temp[len]='\0'; hidden_flag=temp[0]-'0';//set the value of hidden_flag return len; }
下面是創建hidden文件的代碼,使用的是create_proc_entry函數,然后指定其回調函數。
struct proc_dir_entry *ptr=create_proc_entry("hidden",0644,NULL); ptr->read_proc=proc_read_hidden; ptr->write_proc=proc_write_hidden;
4、程序運行結果及分析
(1)首先默認設置hidden_flag=1,使用hide_user_processes隱藏uid=500,即seu用戶的所有進程。
(2)將hidden_flag的值改為0,這時再查看進程,所有被隱藏的進程又出現了。
(3)將hidden_flag改回為1,查看進程,seu用戶的進程又處於隱藏狀態了。
六、在/proc目錄下創建一個文件/proc/hidden_process
1、設計思路和流程圖
該文件用於存儲所有被隱藏進程的pid,因為這個文件暫時不涉及用戶寫入,所以只需設置其讀回調函數。具體說明見代碼部分。
然后重新編譯安裝內核,重啟后測試。
2、主要數據結構及說明
使用了結構體、進程控制塊、進程隊列等數據結構。
3、源程序並附上注釋
通過for_each_process函數,將被隱藏的進程的pid傳給buf,最后將buf傳給page顯示在終端上。
static int proc_read_hidden_processes(char *page,char **start,off_t off,int count,int *eof,void *data) { static char buf[1024*8]=""; char tmp[128]; struct task_struct *p; if(off>0) return 0; sprintf(buf,"%s",""); for_each_process(p) { //自<a>dfx</a>己獨立<span>adsf</span>完成別<b>fdas</b>總是<xxx>fdas</xxx>有事沒事<zzz>fdas</zzz>抄<qq>435fff</qq>別人的哦 if(p->cloak==1) { sprintf(tmp,"%d ",p->pid); //a important step strcat(buf,tmp); } } sprintf(page,"%s",buf);//convert buf to page to display on the terminal return strlen(buf);//Author:Kong }
4、程序運行結果及分析
(1)首先隱藏uid=500,即seu用戶的所有進程
查看hidden_process文件里的內容,發現所有被隱藏進程的pid都存在這里。
(2)恢復所有進程為顯示狀態
這時沒有被隱藏的進程,hidden_process文件里的內容也為空。
(3)經過多次不同的測試,實現了效果,沒有bug發生
七、實驗體會
這次實驗的前兩個,增加系統函數的使用很容易就完成了。我主要在后面的文件系統部分遇到了一些問題,使用指針的時候總是遇到很大的問題,要么是重啟之后不能進入內核,要么是測試的時候虛擬機卡住一動不動,然后必須進入原來的內核當中修改新內核,耗費了很多的時間。其實該做好備份的,這樣也許可以節省很多時間。
最令我不解的是,使用char *a的形式總是出問題,改為char a[]的形式問題便消失了,具體原因還有待我繼續尋找。
這次實驗之后,我對linux的進程控制、命令、系統調用、文件系統等方面都有了更深的認識,使用起來比以前熟練多了,但問題依然有很多,需要我繼續想方設法解決。以前看的資料大多是籠統地講解linux,雖然對整體了解有幫助,但是內部實現我涉獵不廣,這次實驗幫助我對內部實現有了更多的認識。