修改Linux内核
本文为东南大学的祖传操作系统实验。实现如下任务:
- 实验1:编译内核代码
- 实验2:新建系统调用
- 实验3:实现可以屏蔽进程的系统调用 hide 及可以根据用户来屏蔽进程的系统调用 hide_user_process
实验使用 Fedora 7 虚拟机环境,提供 linux-2.6.21 内核文件,可在此处下载。
实验目的
熟悉 Linux 内核文件结构,学会修改与增加系统调用,掌握进程控制和进程管理相关内容。
实验1 Linux 内核代码分析
依次执行命令
cd Desktop
tar zxvf linux-2.6.21.tar.gz
cd linux-2.6.21
make mrproper
cp /boot/config-2.6.21-1.3194.fc7 ./config
make oldconfig
make all
su
make modules_install
make install
make headers_install
vi /boot/grub/menu.lst
将
hiddenmenu
注释掉,如下图所示。
重启
reboot
看到引导菜单。
选择 seu,系统正常启动。
实验2 :新增系统调用
./arch/i386/kernel/syscall_table.S
最后添加一个系统调用
.long sys_psta
./include/linux/psta.h
编辑内容为
#ifndef _LINUX_PSTA_H
#define _LINUX_PSTA_H
struct pinfo {
int nice;
pid_t pid;
uid_t uid;
};
#endif
./include/linux/Kbuild
添加一行头文件
header-y += psta.h
./kernel/psta.c
编辑内容为
#include <linux/linkage.h>
#include <linux/types.h>
#include <linux/psta.h>
#include <linux/kernel.h>
asmlinkage int sys_psta(struct pinfo *buf) {
printk("Hello world\n");
return 0;
}
./kernel/Makefile
增加目标文件 psta.o
obj-y = psta.o sched.o fork.o exec_domain.o panic.o printk.o profile.o \
exit.o itimer.o time.o softirq.o resource.o \
sysctl.o capability.o ptrace.o timer.o user.o \
signal.o sys.o kmod.o workqueue.o pid.o \
rcupdate.o extable.o params.o posix-timers.o \
kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \
hrtimer.o rwsem.o latency.o nsproxy.o srcu.o
./include/asm-i386/unistd.h
增加宏
#define __NR_psta 320
同时,将 NR_syscalls
修改为 321
./include/linux/syscalls.h
增加头文件
#include <linux/psta.h>
并把函数的定义加进来
asmlinkage int sys_psta(struct pinfo *buf)
./Makefile
修改内核编号为 seu2
EXTRAVERSION = -seu2
然后重新编译重启
make mrproper
cp /boot/config-2.6.21-1.3194.fc7 ./config
make oldconfig
make all
su
make modules_install
make install
make headers_install
reboot
看到引导菜单。
选择 seu2,系统正常启动。
./test.c
编辑内容为
#include <sys/syscall.h>
#include <unistd.h>
#include <linux/psta.h>
int main()
{
struct pinfo info;
int ret = syscall(320,&info);
return 0;
}
编译并运行,然后查看内核消息列表
gcc -o test test.c -I/home/seu/Desktop/linux-2.6.21/usr/include
./test
dmesg
在内核日志的最后看到了 Hello world
实验3:Linux进程管理及其扩展
做以下编辑:
./include/linux/sched.h
修改 task_struct
int cloak;
./kernel/fork.c
修改 copy_process
p->cloak = 0;
./fs/proc/base.c
添加语句
extern int hidden_flag;
创建 sys_hide
asmlinkage int sys_hide(pid_t pid, int on){
if (current->uid == 0){
struct task_struct *task = find_task_by_pid(pid);
if (on == 0 || hidden_flag == 0)
task->cloak = 0;
else
task->cloak = 1;
proc_flush_task(task);
}
return 0;
}
修改 proc_pid_readdir
if (hidden_flag == 0 &&
proc_pid_fill_cache(filp,dirent,filldir,task,tgid)<0){
put_task_struct(task);
goto out;
}
if (hidden_flag == 1 && task->cloak == 0 &&
proc_pid_fill_cache(filp, dirent, filldir, task, tgid) < 0){
put_task_struct(task);
goto out;
}
修改 proc_pid_lookup
if (task->cloak == 1 && hidden_flag == 1)
goto out;
创建 sys_hide_user_processes
asmlinkage int sys_hide_user_processes(uid_t uid, char *comm, int on){
if (current->uid == 0){
struct task_struct *task = NULL;
for_each_process(task){
char *s = task->comm;
if (hidden_flag == 0 && task->uid == uid){ //judge hidden_flag
task->cloak = 0;
}
else if (comm == NULL){ //hide all
if (task->uid == uid){
task->cloak = on;
}
}
else if (task->uid == uid && strcmp(s, comm) == 0){ //hide comm
task->cloak = on;
}
proc_flush_task(task);
}
}
return 0;
}
./fs/proc/proc_misc.c
添加以下变量和函数
int hidden_flag = 0;
EXPORT_SYMBOL(hidden_flag);
static int proc_read_hidden(char *page, char **start,
off_t off, int count, int *eof, void *data)
{
int len = 0;
len = sprintf(page,"%d",hidden_flag);
return len;
}
static int proc_write_hidden(struct file *file, const char *buffer,
unsigned long count, void *data)
{
hidden_flag = buffer[0] - '0';
return count;
}
static int proc_read_hidden_processes(char *page, char **start, off_t off,
int count, int *eof, void *data){
static char buf[1024*8]="";
char tmp[128];
struct task_struct *p;
if (off>0)
return 0;
sprintf(buf,"%s","");
for_each_process(p){
if (p->cloak == 1){
sprintf(tmp, "%d", p->pid);
strcat(buf,tmp);
}
}
sprintf(page, "%s", buf);
return strlen(buf);
}
在 proc_misc_init
最后添加
struct proc_dir_entry *ptr = create_proc_entry("hidden", 0644, NULL);
ptr->read_proc = proc_read_hidden;
ptr->write_proc = proc_write_hidden;
struct proc_dir_entry *hideprocessfile =
create_proc_entry("hidden_process", 0644, NULL);
hideprocessfile->read_proc=proc_read_hidden_processes;
./arch/i386/kernel/syscall_table.S
最后添加系统调用
.long sys_hide
.long sys_hide_user_process
./include/asm-i386/unistd.h
增加宏
#define __NR_hide 321
#define __NR_hide_user_processes 322
同时,将 NR_syscalls
修改为 323
./include/linux/syscalls.h
把函数的定义加进来
asmlinkage int sys_hide(pid_t pid, int on);
asmlinkage int sys_hide_user_processes(uid_t uid, char *comm, int on);
然后重新编译重启
make mrproper
cp /boot/config-2.6.21-1.3194.fc7 ./config
make oldconfig
make all
su
make modules_install
make install
make headers_install
reboot
启动后在 /proc
文件夹下看到了 hidden 和 hidden_process
创建 4 个测试文件
内容分别为
//hideInit.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
int main()
{
syscall(321, 1, 1);
return 0;
}
//recoverInit.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
int main()
{
syscall(321, 1, 0);
return 0;
}
//hideRootInit.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
int main()
{
syscall(322, 0, "init", 1);
return 0;
}
//hideRoot.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
int main()
{
syscall(322, 0, NULL, 1);
return 0;
}
分别编译
cd Desktop
gcc -o hideInit hideInit.c
gcc -o recoverInit recoverInit.c
gcc -o hideRootInit hideRootInit.c
gcc -o hideRoot hideRoot.c
然后进行测试
cd /proc
su
echo "1" > hidden
然后 top
测试 ./hideInit
没有成功。
进入 root 权限重新测试
看到 1 号进程成功屏蔽
测试 ./recoverInit
init 又回来了
测试 ./hideRootInit
看到 root 的 init 进程成功屏蔽
测试 ./hideRoot
看到 root 的进程全部被屏蔽
编写程序调用 syscall(322, 0, NULL, 0);
恢复 hidden_process。
然后修改 hidden 为 0
echo "0" > hidden
重新测试 ./hideRoot
可以看到,屏蔽不起效果了。
实验总结
本实验 1 和 2 很容易,依葫芦画瓢即可。实验 3 其实也不难,但我在 hide_user_processes 上花了整整 5 个小时:先是 ==
写成了 =
,查 bug 查了两个小时;然后,本应该比较两个指针内容的地方,我写成了比较两个指针,又 debug 了两个小时😭
通过本次实验,熟悉了 Linux 内核文件结构,学会了修改与增加系统调用,掌握了进程控制和进程管理相关内容。
Comments