操作系统实验一: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)新增系统调用 hide 并且(2)考虑权限问题

设计思路和流程图:

  1. 在 include/linux/sched.h 中修改 task_struct,添加成员 cloak,0 表示进程显示,1 表示进程隐藏:

  1. 在进程创建时,将task_struct的成员cloak初始化为未隐藏。fork系统调用的实现代码在kernel/fork.c中,具体实现的主要函数为do_fork,do_fork中调用copy_process函数创建子进程,建议将初始化cloak的代码添加在copy_process函数中:
  1. 打开文件 arch/i386/kernel/syscall_table.S,新增系统调用的名称:
  1. 在 include/linux 目录下新建 hide.h 头文件:
  2. 在 kernel 目录下新建 hide.c 文件:
  3. 修改 kernel/Makefile,添加 hide.o,使得 hide.c 在编译时可见:
  4. 找到文件 include/asm-i386/unistd.h,加上 hide 系统调用号的宏定义:
  5. 修改 include/linux/syscalls.h。添加 sys_hide 的声明
  6. 修改 fs/proc/base.c 中的 proc_pid_readdir 函数以及proc_pid_lookup 函数,当 cloak=1 时隐藏,当 cloak=0 时不隐藏:

主要数据结构及其说明:

使用 task_struct,通过 hide.c 中 on 对结构体中 cloak 的赋值,改变进程

的状态。

源程序并附上注释(关键部分):

1. hide.c 程序

首先进行判断,只有 root 用户才可以进行操作。通过使用 find_task_by_pid 获取给定进程 pid 的结构体 task_struct。

进行判断,如果参数 on=1 则隐藏该进程;如果 on=0 则显示该进程。

在隐藏操作之后通过函数 proc_flush_task 清空缓存,接触现有的 dentry

项。

  1. 测试程序:

用户通过输入 uid 选择需要隐藏的进程;通过输入 status 设置 on 值,on=0

显示进程,on=1 隐藏进程。

#include<stdio.h>

#include<sys/syscall.h>

#include<unistd.h>

int main()

{

/*

 * user input first number as uid

*/

    int pid_input=0;

    printf("Please input the pid: \n");

    scanf("%d",&pid_input);

    int syscallNum=321;//hide

    pid_t pid=pid_input;

/*

 * user input second number as on(status)

*/

    int status=0;

    printf("Please input the on status: (1->the process hidden;0->the process shown)\n");

    scanf("%d",&status);

    int on=status;//hidden/shown

    syscall(syscallNum,pid,on);

    return 0;

}

程序运行结果及分析:

首先使用 ps aux 指令查看所有进程:

尝试在用户态隐藏进程 76:

用户可以根据自身需求通过输入 pid 隐藏进程,同时可以根据需求设置on值选择隐藏或是显示某进程,再通过 ps aux 查看进程:

可以看到76号进程依然显示。这是因为考虑权限问题,只有 root 用户可以通过 hide 隐藏进程。

下面进入 root 再次重复上面的操作:

可以看到在 root 态,通过调用 hide 函数隐藏76号进程可以实现将该进程隐藏。

下面通过将 on 值设置为 0 恢复 76号进程的显示:

可以看到通过调用 hide 函数,选择进程76并将 on 值设置为 0,恢复了

进程76号的显示。

(3) 新增系统调用 hide_user_processes

设计思路和流程图:

1. 打开文件 arch/i386/kernel/syscall_table.S,新增系统调用的名称。

2. 在 include/linux 目录下新建 hide_user_processes.h 头文件

3. 在 kernel 目录下新建 hide_user_processes.c 文件

4.修改 kernel/Makefile,添加 hide_user_processes.o 使得hide_user_processes.c 在编译时可见

5. 找到文件 include/asm-i386/unistd.h,加上 hide_user_processes 系统调

用号的宏定义:

6. 修改 include/linux/syscalls.h。添加 sys_hide_user_processes 的声明

主要数据结构及其说明:

通过改变结构体中 cloak 的值,控制进程的隐藏/显示状态。

源程序并附上注释(关键部分):

hide_user_processes.c

首先进入判断:如果参show_all=0,则隐藏相应进程;如果 show_all=1且当前为 root,则显示所有进程,即置所有进程cloak=0。

判断只有 root 用户才能隐藏相应进程。

当 binname=NULL 时,隐藏所有给定 uid 的进程,即将该 uid 所有进程 cloak

置为 1.当binname为给定值时,只隐藏该进程。:

#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 show_all)

{

/*

 * if binname == NULL,hide all processes of given uid

*/

    struct task_struct *p=NULL;

    if(show_all==0)

    {

        if(current->uid==0)//root only

        {

            /*

             * if binname == NULL,hide all processes of given uid

             */

            if(binname==NULL)

            {

                for_each_process(p)

                {

                    if((p->uid)==uid)

                    {

                        p->cloak=1;//hide all processes

                        proc_flush_task(p);

                    }

                }

                printk("All processes of uid %d are hidden. \n",uid);

            }

            else

            {

               /*

                *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//not root

             printk("Permission denied. \n");

    }

    else if(show_all  != 0 && (current->uid)==0)//show all of the processes,hidden or not

    {

        for_each_process(p)

        {

            p->cloak=0;

        }

    }

    return 0;

测试程序:

#include<stdio.h>

#include<stdlib.h>

#include<sys/syscall.h>

#include<unistd.h>

int main()

{

/*

 * 用户输入uid

 *0->root;500->seu

*/

    int uid_input=0;

    printf("Please input the uid: (0->root;500->seu)\n");

    scanf("%d",&uid_input);

    int syscallNum=322;

    uid_t uid=uid_input;

/*

 *用户输入binname,如果binname==NULL所有进程都会被隐藏

*/

    printf("Please input the binname: (NULL->all processes)\n");

    char *binname=(char*)malloc(1024*sizeof(char));

    scanf("%s",binname);

    char *judge="NULL";//to judge ? NULL

    if(strcmp(binname,judge)==0)binname=NULL;//NULL

    int status=0;

/*

 *用户输入status,0->default;1->show all processes

*/

    printf("Please input the recover_status: (0->default;1->show all processes)\n");

    scanf("%d",&status);

    int recovery_status=status;

    syscall(syscallNum,uid,binname,recovery_status);

    free(binname);

    return 0;

}

程序运行结果及分析:

首先使用 ps aux 查看进程

隐藏 seu 的所用进程(即输入 uid=500):

用户可以根据自身需求选择 root(0)或者 seu(500),同时可以根据需

求设置 binname 隐藏某特定进程,同时可以设置 recover_status 选择恢

复进程或者隐藏进程,使用 ps aux 查看进程:

seu的所有进程都被隐藏了。

隐藏 pid=1 的 init 进程(即 binname 输入 init):

查看进程:

pid=1 的进程被隐藏。

通过设置 recover_status=1 恢复已经被隐藏的进程:

查看进程:

被隐藏的进程恢复正常。

隐藏所有 root 进程:

查看进程:

root 进程都已经被隐藏,除了4487和4587两个root 进程。这是因为4487号进程是当前打开的 terminal,而4587号进程即 ps aux 指令是在执行 hide_user_processes 之后执行的指令。所以这两个 root 进程依旧正常显示。

(4) 在/proc 目录下创建一个文件/proc/hidden

设计思路和流程图:

1.设置全局变量 hidden_flag。在 fs/proc 目录下创建 var.h 文件,定义全局变量 hidden_flag,其它文件中需要用到这个全局变量的时候,需使用include包含这个头文件。

2. 实现 hidden 文件的创建和读写

proc 文件系统在初始化函数 proc_root_init 中会调用 proc_misc_init 函

数,此函数用于创建/proc 根目录下的文件,那么将创建 hidden 文件的代码

插入到此函数中就可以在 proc 初始化时得到执行。

添加回调函数。在/fs/proc/proc_misc.c 中 proc_misc_init 函数的最后添

加创建 hidden 文件的代码,并指定其回调函数。

  1. 结合上面根据cloak判断进程,这个实验与之类似,只需在fs/proc/base.c文件中,修改proc_pid_readdir函数以及proc_pid_lookup函数,在cloak判断之前,增加hidden_flag对进程的约束:

主要数据结构及其说明:

通过使用 PCB 和结构体完成 hidden 相应功能。

源程序并附上注释(关键部分):

在 proc_misc.c 中添加回调函数。初始化 hidden_flag=1,BUF_LEN=128。在

函数 proc_read_hidden 中通过 sprintf 函数将 hidden_flag 的值传给 page,

再返回 len 值。

在 proc_write_hidden 函数中通过 copy_from_user 函数将所输入的值传递

给 temp,再从 temp 中得到值传递给 hidden_flag。

创建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;

程序运行结果及分析:

首先通过调用 hide_user_process 隐藏进程,查看进程,所有 seu 进程都已经被隐藏:

设置 hidden_flag 值,将 hidden_flag 值设置为 0:

查看进程,发现所有进程都处于显示状态,即无法隐藏:

将 hidden_flag 值设置为 1:

查看进程:

之前被隐藏的 seu 进程又处于隐藏状态。

(5) 在/proc 目录下创建一个文件/proc/hidden_process  

设计思路和流程图:

hidden_process 用于存储所有被隐藏进程的 pid。该文件只需要设计回调函

数即可。

进行判断,只有当 hidden_flag=1 时,才将被隐藏的进程的 pid 写入该文件。

还是上一次的那个proc_misc.c程序

然后重新编译安装内核,重启后测试。

主要数据结构及其说明:

使用了结构体、进程控制块、进程队列等数据结构。

源程序并附上注释(关键部分):

hidden_process 用于存储所有被隐藏进程的 pid。该文件只需要设计回调函

数即可。

进行判断,只有当 hidden_flag=1 时,才将被隐藏的进程的 pid 写入该文件。

程序运行结果及分析:

首先,ps aux,查看当前进程:

通过调用 hide 隐藏 pid=1 的进程:

ps aux查看进程,发现 pid=1 进程已经被隐藏:

通过指令:cd /proc 继而cat hidden_process 查看文件内容,可以发现文件包含所有被隐藏进程的 pid,此处即 pid=1:

恢复 pid=1 进程:

再次查看 hidden_process 文件内容,发现内容为空:

通过 hide_user_processes 隐藏 seu 所有进程:

通过指令 cat hidden_process 查看该文件内容,可以发现该文件包含所

有被隐藏进程的 pid:

恢复所有被隐藏进程:

再次打开 hidden_process:

发现 hidden_process 内容为空,即没有被隐藏的进程。

  • 实验体会:

实验中有一次遇到过这样的问题:

当时百思不得其解,反复检验代码,最后通过对比文件中原有的头文件,终于发下端倪,因为我添加的头文件var.h与该程序在同一级文件夹下,所以不应该使用#include<var.h>而应该是#include”var.h”,果然这样改过之后,就正确的make all了。

在做实验0的时候,在这个地方一直报错,后来在群里看了老师的解答,原来是实验手册出了问题,正确做法不是把头文件加在最开始,而是应该加在其他头文件的后面。这样修改以后,果然不再有问题了。

最开始做的时候,make mrproper一直出现报错,后来发现,在root下做,就不会有问题,原来是权限的问题。

在做的时候,想过使用vim,但是发现系统并没有预装,参照网上教程也一直出现问题:

最后请教老师,但是老师也不知道因为什么,只好不了了之,全程使用vi命令。

还有就是,在最开始做的时候,只知道无脑跟着实验手册,也没有看是否有报错,然后会出现各种稀奇古怪的问题:

后来实在没有办法,就选择重装了一次系统,这一次,认真研究,开动脑筋,这才一步步的把实验做完,通过本次实验,我深刻的认识到,千万不要迷信实验手册,一定要自己着手解决问题才行!

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值