文章目录

  • 添加系统调用--内核编译法(centos7 下4.15.10内核)
  • 1、【实验目的】
  • 2、【实验原理】
  • 3、【实验环境】
  • 4、【实验方法与步骤】| 请先看完6避坑
  • **一、添加新函数**
  • **二、更新头文件**
  • **三、更新系统调用表**
  • **四、重新编译内核**
  • 5、【实验记录及数据处理】
  • 6、【实验总结与问题讨论】
  • **1)做完本实验后的收获?**
  • **2)实验中遇到什么问题?如何解决?**
  • 7、【拓展实验】
  • 1)用模块添加法添加系统调用。


添加系统调用–内核编译法(centos7 下4.15.10内核)

1、【实验目的】

阅读Linux内核源代码,通过添加一个简单的系统调用实验,进一步理解Linux操作系统处理系统调用的统一流程。

2、【实验原理】

Linux操作系统处理系统调用的统一流程。

3、【实验环境】

PC机1台,Windows操作系统和其上的虚拟Linux操作系统。

4、【实验方法与步骤】| 请先看完6避坑

【注:因为在实验过程中,有些部分未能截下步骤的图像,将以文本的形式呈现。】

到官网或者相关下载点下载内核源代码linux-4.15.10.tar.gz,

把源码移动到/usr/src/目录下,然后解压,目录linux-4.15.10里的内容就是版本4.15.10的内核源码树。

centos7编译安装_系统调用

centos7编译安装_centos7编译安装_02

一、添加新函数

在/usr/src/linux-4.15.10/kernel/sys.c中,添加一个系统调用函数到内核,命名和功能如下:

compat_sys_mysyscall()。在现有的系统中添加一个不用传递参数的系统调用,调用这个系统调用,使用户的uid变成0。

centos7编译安装_寄存器_03

asmlinkage 说明参数传递不是靠寄存器,而是靠栈来传递。

centos7编译安装_centos7编译安装_04

这里之后编译的时候报错了,原因是没有task_struct没有uid这个变量(版本原因),故这里修改为如下功能:输出信息。

centos7编译安装_linux_05

二、更新头文件

在/usr/src/linux-4.15.10/include/linux/syscalls.h中,添加系统调用号.如下图所示:

centos7编译安装_系统调用_06

三、更新系统调用表

• 在/usr/src/linux4.15.10/arch/x86/entry/syscalls/syscall_64.tbl中,添加一个系统调用表项,如下图所示:

centos7编译安装_寄存器_07

这里的abi有三种可以填(common、64、x32) ,经过测试发现x32没有效果,故修改成common。

centos7编译安装_寄存器_08

四、重新编译内核

centos7编译安装_linux_09

centos7编译安装_linux_10

这之后新内核已经‘安装’在了系统中, 只需输入reboot,重启一下系统,用新内核引导。

centos7编译安装_寄存器_11

5、【实验记录及数据处理】

测试验证

① 编辑源代码test.c如下:

centos7编译安装_centos7编译安装_12

② 运行结果如下

centos7编译安装_系统调用_13

6、【实验总结与问题讨论】

1)做完本实验后的收获?

\1. 编译内核比较花时间,需要有耐心,同时可以增加CPU核数,以加快编译过程。

\2. 编译内和比较耗空间,需要提前为虚拟机准备充足的内存。

\3. 添加系统调用:更新系统调用表、更新头文件、添加性函数。要注意不同版本间,存放的地址也会不同;

\4. Printk函数属于内核调用的函数,输出结果不在窗口,需要使用dmesg查看。

\5. 主要是要有耐心,不断去网上查资料,比对,以及不要畏难。

2)实验中遇到什么问题?如何解决?

\1. 查看该centos能够支持的最低版本,不然编译后重启,会出现“kernel too old”的错误;

centos7编译安装_系统调用_14

进入/lib64路径,‘file libc-2.17.so’查看支持的最低内核版本。这里改为4.15.10版本。

centos7编译安装_linux_15

\2. 设置虚拟机的配置以及主机的调整;

① 虚拟机CPU根据主机的情况设置,这里我改为4核,方便提高‘make’的执行速度;

② 扩充/boot挂载的分区,以免执行‘make install’时出现“/boot分区空间不足”;

③ 扩充虚拟机内存大小。

④ 主机的D盘(虚拟机所在的磁盘)也要内存。

centos7编译安装_系统调用_16

centos7编译安装_linux_17

centos7编译安装_寄存器_18

可以看出内核编译非常占用内存。

centos7编译安装_寄存器_19

D盘空间不足。清理D盘空间,make clean 保留配置文件等,再重新编译即可。

centos7编译安装_linux_20

\3. 各种包工具的安装,以免编译内核时停止。【这里我用history查看命令记录】

centos7编译安装_寄存器_21

7、【拓展实验】

自己查阅相关的资料,完成下面的实验:

1)用模块添加法添加系统调用。

\1. 查询syscall_table的地址

centos7编译安装_centos7编译安装_22

\2. 查看可用的系统调用号

centos7编译安装_寄存器_23

从下图知383可用。

centos7编译安装_系统调用_24

\3. 创建执行文件

#include <linux/kernel.h>

#include <linux/init.h>

#include <linux/module.h>

#include <linux/unistd.h>

#include <linux/sched.h>

 

MODULE_LICENSE("Dual BSD/GPL");

 

#define SYS_CALL_TABLE_ADDRESS 0x0000000000000000 //sys_call_table对应的地址

#define NUM 383 //系统调用号为383

int orig_cr0; //用来存储cr0寄存器原来的值

unsigned long *sys_call_table_my=0;

static int(*anything_saved)(void); //定义一个函数指针,用来保存一个系统调用

static int clear_cr0(void) //使cr0寄存器的第17位设置为0(内核空间可写)

{

  unsigned int cr0=0;

  unsigned int ret;

  asm volatile("movq %%cr0,%%rax":"=a"(cr0));//将cr0寄存器的值移动到eax寄存器中,同时

输出到cr0变量中

  ret=cr0;

  cr0&=0xfffffffffffeffff;//将cr0变量值中的第17位清0,将修改后的值写入cr0寄存器

  asm volatile("movq %%rax,%%cr0"::"a"(cr0));//将cr0变量的值作为输入,输入到寄存器eax中,同时移动到寄存器cr0中

  return ret;

}

 

static void setback_cr0(int val) //使cr0寄存器设置为内核不可写

{

  asm volatile("movq %%rax,%%cr0"::"a"(val));

}

 

asmlinkage long sys_mycall(void) //定义自己的系统调用

{

  printk("模块系统调用-当前pid:%d,当前comm:%s\n",current->pid,current->comm);

  printk("hello,xieyixing!\n");

  return current->pid;

}

static int __init call_init(void)

{

  sys_call_table_my=(unsigned long*)(SYS_CALL_TABLE_ADDRESS);

  printk("call_init......\n");

  anything_saved=(int(*)(void))(sys_call_table_my[NUM]);//保存系统调用表中的NUM位置上

的系统调用

  orig_cr0=clear_cr0();//使内核地址空间可写

  sys_call_table_my[NUM]=(unsigned long) &sys_mycall;//用自己的系统调用替换NUM位置上>的系统调用

  setback_cr0(orig_cr0);//使内核地址空间不可写

  return 0;

}

 

static void __exit call_exit(void)

{

  printk("call_exit......\n");

  orig_cr0=clear_cr0();

  sys_call_table_my[NUM]=(unsigned long)anything_saved;//将系统调用恢复

  setback_cr0(orig_cr0);

}

 

module_init(call_init);

module_exit(call_exit);

centos7编译安装_centos7编译安装_25

\4. 创建Makefile

centos7编译安装_linux_26

\5. 安装内核模块

① Make

centos7编译安装_系统调用_27

centos7编译安装_linux_28

**系统调用表的地址在每次出现致命错误后,会发生变化!**因此,在出现致命错误后,除了要修改 asmlinkage 部分的代码,不要忘了再次检查系统调用表地址,并在程序中修改!

centos7编译安装_linux_29

Invalid module format:重新make就好了

centos7编译安装_寄存器_30

修改系统调用的地址

centos7编译安装_系统调用_31

② Insmod插入模块

centos7编译安装_寄存器_32

③ 测试

centos7编译安装_寄存器_33

centos7编译安装_寄存器_34