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


    一、实验内容

        实验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;
    
    }
  • 相关阅读:
    课堂练习-找水王绪
    输入法评估
    课堂练习-找水王
    课堂练习—电梯调度
    第一阶段冲刺总结
    《你的灯亮着吗》阅读笔记Ⅱ
    《你的灯亮着吗》阅读笔记Ⅰ
    软件工程——寻找水桶
    软件工程——寻找水王
    软件工程——典型用户
  • 原文地址:https://www.cnblogs.com/coolalan/p/3978215.html
Copyright © 2020-2023  润新知