Linux4.18.9添加系统调用传递参数示例

Linux4.18.9添加系统调用传递参数示例

做过系统调用的朋友们应该都注意到了,在Linux4.15之后的版本因为KPTI(内核页表隔离)的出现,添加自己的系统调用传递参数时并不能像以前版本一样处理,这里我介绍下自己的解决方案(完整步骤)。

1.下载最新源码

在https://www.kernel.org/上下载最新内核源码
本文编辑时最新稳定版本为4.18.9,下载后拷贝到/usr/src目录下,解压linux-4.18.9.tar.xz

sudo xz -d linux-4.18.9.tar.xz
sudo tar -xvf linux-4.18.9.tar

2.安装开发工具

这里安装的软件可能不够全面,在下面操作中出现问题再具体处理

sudo apt-get install build-essential kernel-package libncurses5-dev libssl-dev bc

3.添加自定义的系统调用

打开64位系统的系统调用表,添加自定义的系统调用号

cd /usr/src/linux-4.18.9/
sudo vim arch/x86/entry/syscalls/syscall_64.tbl

test_one是我添加的系统调用

333     common  io_pgetevents           __x64_sys_io_pgetevents
334     common  rseq                    __x64_sys_rseq
335     common  test_one                __x64_sys_test_one

之后在头文件中添加函数声明

sudo vim include/linux/syscalls.h

/* kernel/sys.c */
asmlinkage long sys_test_one(int count, const char __user *buf);
asmlinkage long sys_setpriority(int which, int who, int niceval);

最后在kernel/sys.c文档末尾添加函数原型

sudo vim kernel/sys.c

SYSCALL_DEFINE2(test_one, int, count, const char __user *, buf)
{
        char *p;
        printk("This is Syscall Test One.\n");
        printk("buf addr is %p \n",buf);
        if(count > 1024)
        {
                printk("Your input str is too long.\n");
                return 1;
        }
        p = (char *)kmalloc(sizeof(char) * count, GFP_KERNEL);
        if(!copy_from_user(p, buf, count))
        {
                printk("input is %s \n", p);
        }
        kfree(p);
        return 1;
}

4.配置内核

查看当前内核版本,将其目录下的.config文件拷贝到最新的内核目录下

sudo cp linux-headers-3.19.0-25-generic/.config linux-4.18.9
sudo make menuconfig

在执行sudo make menuconfig时如遇到如下错误可尝试安装对应的软件包

/bin/sh: 1: bison: not found
/bin/sh: 1: flex: not found
执行以下命令安装对应软件包
sudo apt-get install bison flex

解决问题后执行sudo make menuconfig进入图形界面菜单可按以下步骤进行选择:
Load–>OK–>Save–>OK–>Exit–>Exit
这个界面的样子如下图
Linux4.18.9添加系统调用传递参数示例_第1张图片
将内核配置默认为原来内核的配置

sudo make olddefconfig

5.编译内核

编译启动映像,这个步骤时间较长

sudo make bzImage
出现问题可根据提示安装对应的软件包
sudo apt-get install libelf
解决问题后重新执行sudo make bzImage

编译模块,这个步骤时间也很长,我在编译时这两步大概耗时3h

sudo make modules

6.安装内核

首先安装模块,然后安装内核

sudo make modules_install
sudo make install

修改引导

sudo update-grub2

重启系统

sudo reboot
查看内核版本
uname -a
Linux ubuntu 4.18.9 #18 SMP Mon Oct 15 22:10:03 PDT 2018 x86_64 x86_64 x86_64 GNU/Linux

7.编写测试程序

编写程序测试添加的系统调用
te335.c:

#include
#include
#include

int main(int argc, char * argv[])
{
        long ret;
        char *buf = "335 syscall";
        printf("buf addr is %p \n", buf);
        ret = syscall(335, strlen(buf) + 1, buf);
        if (ret == 1) {
                printf("Syscall Succeed!\n");
        } else {
                printf("Syscall Failed!\n");
        }
        return 0;
}

编译执行,使用dmesg查看结果

gcc te335.c -o 335
./335
buf addr is 0x400714
Syscall Succeed!

dmesg结果:

[ 6923.127942] This is Syscall Test One.
[ 6923.127944] buf addr is 0000000007ba6a15
[ 6923.127946] input is 335 syscall

8.小结

1.从测试程序结果来看,buf在用户空间的地址与在内核中的地址是不同的,这说明用户空间与内核空间是隔离的(前面提到的KPTI),在以前版本中,这两个地址的结果是一致的(我在4.4.160版本中测试过)
2.SYSCALL_DEFINEn较以前版本发生改变了,详见include/linux/syscalls.h头文件。我还记得去年在做系统调用时,直接使用asmlinkage long sys_test_one(int count, char *buf)就可以成功传递参数,在4.4.160版本中我再次做了尝试,也是可以成功的,至于4.15后的版本不能传递参数的原因主要还是KPTI 。

9.参考

[翻译]神秘的Linux页表隔离(PTI)补丁
KASLR is Dead: Long Live KASLR
linux如何设置为低内核启动(降核)

你可能感兴趣的:(Linux)