操作系统实验:Linux进程管理及其扩展
一、实验内容
阅读分析Linux内核源代码, 了解进程控制块, 进程队列等数据结构
实现一个系统调用hide,使得可以根据指定的参数隐藏进程,使用户无法使用ps或top观察到进程状态, 要求:
实现系统调用 int hide(pid_t pid, int on); 在进程pid有效的前提下,如果on置1,进程被隐藏,用户无法通过ps或top观察到进程状态;如果on置0且此前为隐藏状态,则恢复正常状态;
考虑权限问题,只有根用户才能隐藏进程;
设计一个新的系统调用int hide_user_processes(uid_t uid, char *binname),参数uid为用户ID号,当binname参数为NULL时,隐藏该用户的所有进程;否则,隐藏二进制映像名为binname的用户进程。该系统调用应与hide系统调用共存。
二、实验目的
通过实验,加深理解进程控制块、进程队列等概念,了解进程管理的具体实施方法。
三、设计思路
ps
ps是用于显示进程瞬时信息的,可以用strace ps
查看ps的系统调用,部分输出如下
可以看出主要调用了”/proc/pid”、”/proc/pid/status” 这两个文件,我们查看/proc 目录:
可以发现,/proc 下有当前所有进程的文件,文件名即为进程的pid。因此若需要实现隐藏进程, 可以在ps读取指定进程文件时修改判断条件, 使其忽略该进程信息。
top
top 命令可以动态地持续监听进程地运行状态,我们一般使用top -u seu
来实现对某个用户(seu)的监听。
用户权限
我们通过cat /etc/group
命令查看group文件获得所有用户的uid:
可以看到,root用户的uid为0,seu用户的uid为500。在hide()
和hide_user_processes()
函数开始前先对用户uid进行判断, 若非root用户则直接
返回.,否则执行相应的系统调用。
四、实验环境
VMware 16
Fedora 7
五、主要数据结构
task_struct 说明:
定义在 linux/sched.h 中. 这个结构体相当于PCB, 其中存储了进程的相关信息, 包括pid, uid 等. 因此可以在其中添加变量指示该进程是否被隐藏。
在每次创建task_struct 时, 需要初始化为NULL 或者init_task. 在遍历整个进程树时, 可以使用for 循环或者for_each_process()
。
六、编译内核
解压源代码
1
2
3cd Desktop
tar zxvf linux-2.6.21.tar.gz
cd linux-2.6.21生成内核配置文件
将当前正在运行的内核对应的配置文件作为模板来生成.config文件,即将/boot目录下的已有的config文件复制到linux-2.6.21目录下
1
2
3
4su root
make mrproper //清理之前编译残留的文件
sudo cp /boot/config-2.6.21-1.3194.fc7 ./config
make oldconfig //更新config文件编译安装内核
1
2
3make all
make modules
make install重启系统
1
reboot
七、系统调用hide()实现
文件修改
linux-2.6.21/include/linux/sched.h
在task_struct结构体中添加
int hide
,指示当前进程是否被隐藏。
linux-2.6.21/kernel/fork.c
在
copy_process()
中初始化hide
为0
。当hide=1
时进程需要被隐藏,当hide=0
时进程不需要被隐藏。linux-2.6.21/fs/proc/base.c
为筛选出需要被隐藏的进程,阻止系统返回信息,在
proc_pid_readdir()
添加判断条件
即只有那些不需要隐藏的进程才能执行下面的代码, 被系统放入缓存。
为阻止系统对隐藏进程的路径名的解析,在
proc_pid_lookup()
函数中添加如下代码:
linux-2.6.21/kernel/hide.c
接下来是定义hide系统调用, 通过用户的传参对给定的pid的hide变量进行修改, 指定其隐藏与否。 在kernel中新建文件 hide.c 添加函数
asmlinkage long sys_hide(pid_t pid, int on)
,定义如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
asmlinkage long sys_hide(pid_t pid, int on)
{
struct task_struct *p=NULL;
if(pid>0 && (current->uid)==0)//only the root user can hide the process
{
p=find_task_by_pid(pid);
p->hide=on;//set the state of the process
if(on==1)
{
printk("Process %d is hidden by root.\n",pid);
}
if(on==0)
{
printk("Process %d is displayed by root.\n",pid);
}
proc_flush_task(p);
}
else
printk("Sorry, you are not root user.Permission denied.\n");
return 0;
}用户权限设置
上述代码中对
(current->uid)==0
的判断可以保证只有root用户可以执行该代码块,若不是root用户则直接退出。在下面介绍的sys_hide_user_processes()
函数中同样设置了用户权限。
系统调用修改
arch/i386/kernel/syscall_table.S
文件末尾添加
1
.long sys_hide
include/asm-i386/unistd.h
添加
1
修改
#define NR_syscalls 320
为1
include/linux/syscalls.h
文件末尾添加声明
1
asmlinkage long sys_hide(pid_t pid, int on);
kernel/Makefile
obj-y =
后添加1
hide.o
编译内核
打开终端
1 | su root |
测试
测试文件
hide_test.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main()
{
printf("start\n");
pid_t pid=3918;
int on=0;//测试中修改
syscall(__NR_hide,pid,on);
return 0;
}
文件编译及运行
1
2gcc hide_test.c -o hide_test
./hide_test
- 测试结果
在终端查看目前用户为seu的进程
选择进程3912,在测试文件中修改
pid = 3912,on = 1
,编译后在普通用户下运行,可以看见,在top后该进程依然被显示出来。进入root用户,重新执行测试文件,可以看见,进程3912被隐藏
修改
on = 0
,编译后在root下运行,可以看见进程3912又被显示出来
hide()系统调用测试通过。
八、系统调用hide_user_processes()实现
文件修改
linux-2.6.21/kernel/hide_uesr_processes.c
相比于sys_hide系统调用, 该系统调用只是改变了传参. 实现的思路类似。在kernel中新建文件 hide_uesr_processes.c ,添加函数asmlinkage long sys_hide_user_processes(uid_t uid,char *binname,int recover)
,uid为该进程的用户id,binname为进程名COMMAND,根据要求隐藏对应进程。第三个参数recover用于恢复被隐藏的进程。如果recover=0那么隐藏相应的进程,如果不为0,则恢复所有进程为显示状态。代码如下:
1 |
|
*系统调用修改与内核编译与hide()相同,不再作过多赘述。
测试
测试文件
hide_user_test.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main()
{
printf("start \n");
uid_t uid=500; //user
char *binname=NULL;
int recover=0;
syscall(__NR_hide_user_process,uid,binname,recover);
return 0;
}测试结果
在终端查看目前用户为seu的进程
修改测试文件参数
uid = 500
(seu用户),binname = NULL , recover = 0
在普通用户下编译并执行程序,seu用户的进程未被隐藏,此处不再过多展示
进入root用户执行程序,seu用户的进程被隐藏
修改recover=1之后,seu用户进程重新被显示出来
我们选择PID=4006的进程,修改测试文件参数
binname = "dbus-launch"
,recover = 0
,重新编译运行代码,可以看见,进程4006被隐藏
hide_user_processes()系统调用测试通过。
九、实验体会
本实验主要对Linux中的进程管理进行了探究, 对上学期的操作系统课程的理论知识进行了实践。
在本次实验中我学到了很多知识。首先我了解到PCB在Linux内核中体现为task_struct结构体;其次我对于如何添加系统调用更加清楚,不仅要在syscall.h中声明, 而且需要在syscall_table.S和unistd.h中添加编译语句和宏定义,还需要在Makefile中加入中间目标文件(*.o);最后我还学会了如何编译内核。
本次实验参考了许多网上的代码,许多文章侧重点不同,在同学的帮助和对文章的整合理解后完成此篇实验报告。
参考文章:
OSEXP/OSEXP-1.md at master · ch1y0q/OSEXP (github.com)