增加-进程隐藏功能的系统调用

一、实验内容

    实验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.o

else

         KDIR:=/lib/modules/$(shell uname -r)/build

         PWD:=$(shell pwd)

default:

         $(MAKE) -C $(KDIR) M=$(PWD) modules

clean:

         $(MAKE) -C $(KDIR) M=$(PWD) clean

Endif

  

测试程序

#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;

}
posted @ 2014-09-17 22:37  coolAlan  阅读(3868)  评论(0编辑  收藏  举报