Ch3nyang's blog

home

home

person

about

hive

project

collections_bookmark

mindclip

rss_feed

rss

修改Linux内核

calendar_month 2021-05
archive 系统
tag 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

注释掉,如下图所示。

kernel1

重启

reboot

看到引导菜单。

kernel2

选择 seu,系统正常启动。

kernel3

实验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

看到引导菜单。

kernel4

选择 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

kernel5

实验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

kernel6

创建 4 个测试文件

kernel7

内容分别为

//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

kernel8

测试 ./hideInit

kernel9

没有成功。

进入 root 权限重新测试

kernel10

看到 1 号进程成功屏蔽

测试 ./recoverInit

kernel11

init 又回来了

测试 ./hideRootInit

kernel12

看到 root 的 init 进程成功屏蔽

测试 ./hideRoot

kernel13

kernel14

看到 root 的进程全部被屏蔽

编写程序调用 syscall(322, 0, NULL, 0); 恢复 hidden_process。

然后修改 hidden 为 0

echo "0" > hidden

重新测试 ./hideRoot

kernel15

可以看到,屏蔽不起效果了。

实验总结

本实验 1 和 2 很容易,依葫芦画瓢即可。实验 3 其实也不难,但我在 hide_user_processes 上花了整整 5 个小时:先是 == 写成了 =,查 bug 查了两个小时;然后,本应该比较两个指针内容的地方,我写成了比较两个指针,又 debug 了两个小时😭

通过本次实验,熟悉了 Linux 内核文件结构,学会了修改与增加系统调用,掌握了进程控制和进程管理相关内容。

Comments

Share This Post