- 实验目的:
通过实验,加深理解进程控制块、进程队列等概念,了解进程管理的具体实施方法。
- 实验内容:
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)考虑权限问题
设计思路和流程图:
- 在 include/linux/sched.h 中修改 task_struct,添加成员 cloak,0 表示进程显示,1 表示进程隐藏:
- 在进程创建时,将task_struct的成员cloak初始化为未隐藏。fork系统调用的实现代码在kernel/fork.c中,具体实现的主要函数为do_fork,do_fork中调用copy_process函数创建子进程,建议将初始化cloak的代码添加在copy_process函数中:
- 打开文件 arch/i386/kernel/syscall_table.S,新增系统调用的名称:
- 在 include/linux 目录下新建 hide.h 头文件:
- 在 kernel 目录下新建 hide.c 文件:
- 修改 kernel/Makefile,添加 hide.o,使得 hide.c 在编译时可见:
- 找到文件 include/asm-i386/unistd.h,加上 hide 系统调用号的宏定义:
- 修改 include/linux/syscalls.h。添加 sys_hide 的声明
- 修改 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
项。
- 测试程序:
用户通过输入 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 文件的代码,并指定其回调函数。
- 结合上面根据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命令。
还有就是,在最开始做的时候,只知道无脑跟着实验手册,也没有看是否有报错,然后会出现各种稀奇古怪的问题:
后来实在没有办法,就选择重装了一次系统,这一次,认真研究,开动脑筋,这才一步步的把实验做完,通过本次实验,我深刻的认识到,千万不要迷信实验手册,一定要自己着手解决问题才行!