Linux 内核添加自定义系统调用
网上的教程 1 大多使用老版本内核,许多内容已经不再适用了。本文依托于我的项目 mycall 进行讲解,旨在把自己踩过的坑全部记录下来,具体实现请参考源码。实验在 Ubuntu 20.04 amd64 虚拟机(内核版本 5.15.0-105-generic)中进行。 本文将先后介绍给内核添加自定义系统调用的两种方式: 通过内核模块将系统调用插入正在运行的内核中。 将系统调用添加到内核源码中,再重新编译安装内核。 通过内核模块添加系统调用 这个方法最简单但是坑也最多,因为内核开发组显然不希望我们通过内核模块修改/覆盖系统调用 2,并且做了诸多限制,为此我们只能用一些 trick。 定义系统调用 从 Linux 4.17 开始,x86 下系统调用服务例程只接收 struct pt_regs * 一个参数 3。因此系统调用的定义为如下形式: 1 2 3 4 asmlinkage long sys_mycall(struct pt_regs *regs) { ... } 根据 Linux x86 calling convention,Linux 系统调用通过寄存器传递参数:rax 存储系统调用号,rdi 存储第一个参数, etc. pt_regs 就是一个包含了寄存器值的结构体 4,需要从中读取参数。 获取系统调用表地址 要插入系统调用,首先需要能够找到系统调用表的地址 sys_call_table。可惜 2.6 版本以后内核就不再 export sys_call_table 了 5,只能寻求其他办法: 从 System.map 读取 编译内核时生成的内核符号表中包含系统调用表的地址,可以通过以下命令获取: 1 cat /boot/System.map-$(uname -r) | grep sys_call_table 但是若内核开启了 KASLR (Kernel Address Space Layout Randomization),实际地址会和 System....