文章目录
提示:注意给虚拟机足够大的磁盘,60G以上
一、设计思路
-
下载linux内核源码
-
在系统调用表中添加系统调用号
-
系统调用头文件中写出自定义的系统调用函数
-
修改内核源码,添加自定义系统调用函数的实现
-
安装依赖工具,重新编译内核,并使用新的内核
-
编写测试代码并测试
二、实现过程
1. 下载linux内核源码
官网下载内核源码,网址为:https://mirrors.edge.kernel.org/pub/linux/kernel/
国内源码下载地址:http://ftp.sjtu.edu.cn/sites/ftp.kernel.org/pub/linux/kernel/
查看自己系统的内核版本,然后找一个相近的linux版本进行内核升级,这里我选择的是5.14.1
下载源码包,解压至 /usr/src
mv linux-5.14.1.tar.gz /usr/src
tar xvf linux-5.14.1.tar.gz
2. 在系统调用表中添加系统调用号
ABI(Application Binary Interface):二进制接口。它定义了应用程序内部如何交互,应用程序如何与内核交互,以及如何和库交互,保证了二进制兼容,对于同一个ABI,目标代码可以在任何系统上正常运行,而不需要重新编译。它关注的是函数调用约定,字节序,寄存器的使用,系统调用,链接,库的行为以及二进制目标的格式。ABI是操作系统和体系结构共同提供的功能。为一个体系结构定义一套ABI是比较困难的,二进制可移植比较困难的。
系统调用表路径:/usr/src/linux-5.14.1/arch/x86/entry/syscalls/syscall_64.tbl
<number> <abi> <name> <entry point>
331 common pkey_free sys_pkey_free
332 common statx sys_statx
333 common io_pgetevents sys_io_pgetevents
334 common rseq sys_rseq
335 64 print_all_process sys_print_all_process
3. 系统调用头文件中写出自定义的系统调用函数
头文件中加入自定义系统调用声明,头文件路径:/usr/src/linux-5.14.1/arch/x86/include/asm/syscalls.h
#ifndef _ASM_X86_SYSCALLS_H
#define _ASM_X86_SYSCALLS_H
/* Common in X86_32 and X86_64 */
/* kernel/ioport.c */
long ksys_ioperm(unsigned long from, unsigned long num, int turn_on);
/* added by me */
asmlinkage long __x64_sys_print_all_process(void);
#endif /* _ASM_X86_SYSCALLS_H */
4. 修改内核源码,添加自定义系统调用函数的实现
在 /usr/src/linux-5.14.1/kernel/sys.c
中添加即可,注意不能写到宏里面
// 335 64 print_all_process sys_print_all_process
static void print_children(struct task_struct* task, int level){
struct list_head* pos;
struct task_struct* ptr;
// 树形结构输出
int i;
for(i = level; i > 0; i--){
printk(KERN_CONT " ");
}
// 进程名称以及pid
printk(KERN_CONT "|————");
printk(KERN_CONT "%s[%d]\n", task->comm, task->pid);
// 利用父task_struct的children双向链表,遍历其子进程
list_for_each(pos, &task->children){
// 返回task_struct指针,寻找pos对应的task_struct,便宜相当于sibling
ptr = list_entry(pos, struct task_struct, sibling);
if(ptr != NULL){
level++;
// 递归打印子进程树
print_children(ptr, level);
level--;
}
}
}
asmlinkage long __x64_sys_print_all_process(void){
printk("The Process Tree Is As Follows:");
print_children(&init_task, 0);
return 0;
}
5. 安装依赖工具,重新编译内核,并使用新的内核
切换到root用户进行操作
apt update
apt install libncurses5-dev libssl-dev
apt install build-essential openssl
apt install zlibc minizip
apt install libidn11-dev libidn11
apt install flex bison
apt install libdw-dev
apt install libncurses-dev
apt install dwarves
apt-get install libdw-dev
make mrproper
make clean
make menuconfig
弹出配置菜单,Save->ok->exit->exit即可
编译内核,这个过程非常长,还会有各种bug,我使用的版本为5.14.1,至少需要60G磁盘,编译至少4小时
cd /usr/src/linux-5.14.1
编译内核:make
编译完成后在控制台中输入命令开始安装模块:sudo make modules_install
模块安装完成后在控制台中输入命令安装内核:sudo make install
安装完成后:reboot
可以看到内核版本已经更新为5.14.1
6. 编写测试代码并测试
#include <linux/unistd.h>
#include <sys/syscall.h>
int main(){
syscall(335);
return 0;
}
由于添加的系统调用的输出语句为printk写的,它将消息送到系统日志中,所以控制台没有输出,输入命令查看日志:dmesg