標籤:blog http io os 使用 ar strong for 檔案
一、實驗內容
實驗3:Linux進程管理及其擴充
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、進程按照pid、uid、進程名進行隱藏和恢複
2、實現了動態模組載入和核心靜態編譯兩種方式產生proc檔案
3、編寫了測試程式
(1)修改進程式控制制塊(相當於PCB)的資料結構,增加cloak參數,1表示該進程隱藏。在使用ps或者top指令的時候,事實上也就是系統調用,所以要在顯示進程的系統調用的地方按照cloak參數進行過濾。
A、Include/linux/sched.h檔案中儲存進程式控制制塊(struct task_struct)。
B、首先要在進程建立的時候對cloak進行初始化,這裡預設初始化為0,表示不隱藏。
fork系統調用的實現代碼在kernel/fork.c中,具體實現的主要函數為do_fork,do_fork中調用copy_process函數建立子進程,建議將初始化cloak的代碼添加在copy_process函數中。
C、然後,建立hide系統調用,這裡不再像實驗2一樣建立檔案,而是直接在fs/proc/base.c中添加。通過pid擷取進程task_struct的核心功能為find_task_by_pid。在隱藏後最好調用函數proc_flush_task來清空VFS層的緩衝,解除已有的dentry項。
修改proc_pid_readdir函數(在fs/proc/base.c檔案中)。其中使用for迴圈遍曆進程,在遍曆過程中添加判斷,過濾掉被隱藏的進程。
修改proc_pid_lookup函數,在進程尋找完成前過濾掉被隱藏的進程。
(2)考慮許可權時,需要修改cloak的hide系統調用中增加對許可權的判斷,使用全域變數current->uid擷取目前使用者許可權,值為0代表root使用者。
(3)int hide_user_processes(uid_t uid, char *binname),系統調用按照uid或者uid和進程名對進程進行隱藏的系統調用。在fs/proc/base.c添加系統調用。
(4)在/proc目錄下建立一個檔案/proc/hidden,該檔案可讀可寫,對應一個全域變數hidden_flag,當hidden_flag為0時,所有進程都無法隱藏,即便此前進程被hide系統調用要求隱藏。只有當hidden_flag為1時,此前通過hide調用要求被屏蔽的進程才隱藏起來。
這個hidden_flag相當於更進階別的控制,當其為0時,仍然可以對cloak操作,但是隱藏的作用失效。
實現proc檔案的建立和讀寫,可以直接寫在核心中也可以通過動態載入模組的方式實現。這裡兩種都做介紹:
核心中建立proc檔案方式:
A、 首先在fs/proc/proc_misc.c檔案中聲明全域變數,EXPORT_SYMBOL()函數可以使該變數在整個核心中可見。使用時只要extern int hidden_flag;即可訪問同一變數。
B、 proc檔案系統在初始化函數proc_root_init中會調用proc_misc_init函數,此函數用於建立/proc根目錄下的檔案,那麼將建立hidden檔案的代碼插入到此函數中就可以在proc初始化時得到執行。(在fs/proc/proc_misc.c檔案中)
其中proc_read_hidden和proc_write_hidden分別是對hidden檔案讀寫時的回呼函數。
即當使用者讀取hidden檔案時,返回全域變數hidden_flag的值;
即當使用者寫入hidden檔案時,修改全域變數hidden_flag的值;
這樣就可以實現檔案的值和全域變數實現同步。達到使用者態對核心進行設定的目的。
C、 修改proc_pid_readdir函數和proc_pid_lookup函數(在fs/proc/base.c檔案中),增加對hidden_flag的判斷邏輯。
proc_pid_lookup:
proc_pid_readdir:
動態載入模組方式:
A、 只需要在核心fs/proc/proc_misc.c檔案中,添加全域變數
B、 建立一個動態模組檔案hide_module.c
在其初始化函數init_hide(void)中編寫:
在其關閉函數exit_hide(void)中編寫:
C、 將這兩個函數綁定為初始化和退出函數
D、 讀寫檔案的回呼函數
(5)在/proc目錄下建立一個檔案/proc/hidden_process,該檔案的內容包含所有被隱藏進程的pid,各pid之間用空格分開。那麼也可以分為靜態核心和動態模組兩種實現方式。
核心中建立proc檔案方式:
A、proc檔案系統在初始化函數proc_root_init中會調用proc_misc_init函數,此函數用於建立/proc根目錄下的檔案,那麼將建立hidden檔案的代碼插入到此函數中就可以在proc初始化時得到執行。(在fs/proc/proc_misc.c檔案中)
B、由於這個檔案只需要使用者讀取,所以唯寫讀操作的回呼函數即可:
這裡需要注意的是,page即為返回給使用者的字串,這個回呼函數的傳回值,為這個字串的長度。
動態載入模組方式:
A、 與hidden檔案的動態載入模組方式相似
在其初始化函數init_hide(void)中編寫:
在其關閉函數exit_hide(void)中編寫:
B、 其讀操作回呼函數為:
四、主要資料結構及其說明task_struct資料結構說明
源自:
http://www.baidu.com/link?url=SO-vtkUs6iRdOHI9se20ZDyI8zjNisNDVxeW7ccNtCfx3YZbVlQFptbKekoIcG9zyDe0PuRvvV7yNn-5zc46_q
proc檔案操作說明建立proc檔案
struct proc_dir_entry * hidefile = create_proc_entry("hidden",0644,NULL);
1、 第一個參數是檔案名稱
2、 第二個參數是檔案的讀寫權限
3、 第三個參數是路徑,因為在proc檔案的根目錄所以為NULL
然後綁定讀寫回呼函數:
hidefile->read_proc = proc_read_hidden;// call back : read
hidefile->write_proc = proc_write_hidden;// call back : write
也可以採用以下方式建立並綁定回呼函數:
struct proc_dir_entry * hideprocessfile = create_proc_read_entry("hidden_process",
0444,
NULL,
proc_read_hidden_process,
NULL);
讀proc檔案
proc檔案不能像普通檔案一樣開啟然後閱讀,需使用如下指令
cat < hidden
使用如下指令寫入檔案
echo “1” > hidden
載入動態模組
在載入模組的目錄下編寫Makefile檔案,然後編譯
$ make
進入到root使用者下,載入模組
# insmod hide_module.ko
卸載模組
# rmmod hide_module
五、來源程式動態載入模組hide_module.h
#include<linux/init.h>#include<linux/module.h>#include <linux/fs.h>#include <asm/unistd.h>#include <asm/uaccess.h>#include<linux/proc_fs.h>#include<linux/sched.h>#include<linux/proc_fs.h>#include<linux/slab.h>#include <asm/uaccess.h>MODULE_LICENSE("GPL");extern int hidden_flag;static int proc_read_hidden(char* page,char**start,off_t off,int count,int *eof,void *data){ // hidden ‘s read callback function int length; length = sprintf(page,"%d",hidden_flag);//before user read this file, we write the ‘hidden_flag‘ to the file return length;} static int proc_write_hidden(struct file * file,const char *buffer,unsigned long count,void *data){ // hidden ‘s write callback function hidden_flag = buffer[0]-‘0‘;//before user write this file , we catch the user‘s input and modify the ‘hidden_flag‘ return count;}static int proc_read_hidden_process(char* page,char**start,off_t off,int count,int *eof,void *data){ // hidden_process ‘s read callback function struct task_struct *task=NULL; int num = 0; for_each_process(task){ if(task->cloak==1){ sprintf(page+num,"%-4d ",task->pid); num+=5; } } return num-5;}static int init_hide(void){ // init module -> create proc file (hidden and hidden_process) struct proc_dir_entry * hidefile = create_proc_entry("hidden",0644,NULL);// create hidden file and its privilage is read&write hidefile->read_proc = proc_read_hidden;// call back : read hidefile->write_proc = proc_write_hidden;// call back : write struct proc_dir_entry * hideprocessfile = create_proc_read_entry("hidden_process", 0444, NULL, proc_read_hidden_process, NULL); printk("<0>""hidden file ok\n"); return 0;}static void exit_hide(void){ // delete proc file remove_proc_entry("hidden",NULL); remove_proc_entry("hidden_process",NULL); printk("<0>""hidden file close\n"); return;}module_init(init_hide);module_exit(exit_hide);
動態載入模組的Makefile
ifneq ($(KERNELRELEASE),) obj-m:=hide_module.oelse KDIR:=/lib/modules/$(shell uname -r)/build PWD:=$(shell pwd)default: $(MAKE) -C $(KDIR) M=$(PWD) modulesclean: $(MAKE) -C $(KDIR) M=$(PWD) cleanEndif
測試程式
#include<sys/syscall.h>#include<unistd.h>#include<stdio.h>#include<stdlib.h>int main(){ int func=0; int pid; int on; char name[200]; int uid; while(1){ printf("###please select test function:\n0:my system call\n1:hide\n2hide_process by uid\n3hide_process by name&uid\n4:exit\n"); scanf("%d",&func); switch(func){ case 0: syscall(320); break; case 1: printf("input: pid on\n"); scanf("%d %d",&pid,&on); syscall(321,pid,on); break; case 2: printf("input: uid on\n"); scanf("%d %d",&uid,&on); syscall(322,uid,NULL,on); break; case 3: printf("input: uid on name\n"); scanf("%d %d %s",&uid,&on,&name); syscall(322,uid,name,on); break; default: return 0; } } return 0;}
進程隱藏系統調用