Loading... ## 实验目的 学习 Linux 内核的系统调用,理解、掌握 Linux 系统调用的实现框架、用户界面、参数传递、进入\返回过程。 ## 实验内容 用两种方法添加系统调用。 第一种方法:在系统中添加一个不用传递参数的系统调用;执行这个系统调用,使用户的 `uid` 等于 0 。显然,这不是一个有实际意义的系统调用。我们的目的是通过最简单的例子,帮助熟悉对系统调用的添加过程,为下面添加更复杂的系统调用打好基础。 第二种方法:用 kernel module 机制,实现系统调用 `gettimeofday` 的简化版,返回调用时刻的日期和时间。 ## 实验准备 * 了解了 Linux 系统调用的流程 ![](https://blog.super0.xyz/usr/uploads/2021/03/4262872650.png) * 了解了 Linux 系统调用的相关文件:`syscall_64.tbl`、`syscalls.h`、`sys.c` ## 实验预习 ### 第一种方法 * 切换到 root 用户 * 下载并解压内核源码 * 分配系统调用号 `gedit ~/linux-5.11.8/arch/x86/entry/syscalls/syscall_64.tbl` ![](https://blog.super0.xyz/usr/uploads/2021/03/1506950170.png) * 申明系统调用服务例程原型 `gedit ~/linux-5.11.8/include/linux/syscalls.h` ![](https://blog.super0.xyz/usr/uploads/2021/03/3853320160.png) * 实现系统调用服务( 0 代表的是 `mysyscall` 需要 0 个参数) `gedit ~/linux-5.11.8/kernel/sys.c` ![](https://blog.super0.xyz/usr/uploads/2021/03/415207273.png) * 编译、安装内核并重启系统 * 测试 ![](https://blog.super0.xyz/usr/uploads/2021/03/140556116.png) ![](https://blog.super0.xyz/usr/uploads/2021/03/3510898321.png) ### 第二种方法 * 切换到 root 用户 * 创建并进入临时目录 `mkdir lkm && cd $_` * 编辑模块文件 ```c /*pedagogictime.c*/ #include<linux/kernel.h> #include<linux/module.h> #include<linux/init.h> /*在这个头文件里面包含了所有的系统调用号__NR_... */ #include<linux/unistd.h> /* for struct time */ #include<linux/time.h> /* for copy_to_user()*/ #include<asm/uaccess.h> #include<linux/uaccess.h> /*for current macro */ #include<linux/sched.h> #define __NR_pedagogictime 442 #define SYS_CALL_TABLE_ADDRESS 0xffffffff820002a0 inline void mywrite_cr0(unsigned long cr0) { asm volatile("mov %0,%%cr0" : "+r"(cr0) : : "memory"); } void enable_write_protection(void) { unsigned long cr0 = read_cr0(); set_bit(16, &cr0); mywrite_cr0(cr0); } void disable_write_protection(void) { unsigned long cr0 = read_cr0(); clear_bit(16, &cr0); mywrite_cr0(cr0); } MODULE_DESCRIPTION("My sys_pedagogictime()"); MODULE_AUTHOR( "1827405106 HUO CHAO"); MODULE_LICENSE("GPL"); /*用来保存旧系统调用的地址*/ static int(*anything_saved)(void); /*这个是我们自己的系统调用函数 sys_pedagogictime() */ static int sys_pedagogictime(void) { printk("syscall from module"); return 666; } /*和这里是初始化函数,_init标志表明这个函数使用后就可以丢弃了*/ int __init init_addsyscall(void) { unsigned long* sys_call_table = (unsigned long*)(SYS_CALL_TABLE_ADDRESS); /*保存原来系统调用表中此位置的系统调用*/ anything_saved = (int(*)( void))( sys_call_table[__NR_pedagogictime]); disable_write_protection();//使内核地址空间可写 /*把我们自己的系统调用放入系统调用表,注意进行类型转换*/ sys_call_table[__NR_pedagogictime] = (unsigned long)sys_pedagogictime; enable_write_protection();//使内核地址空间不可写 return 0; } /*这里是退出函数。__exit标志表明如果我们不是以模块方式编译这段程序,则这个标志后的·函数可以丢弃。也就是说,模块被编译进内核,只要内核还在运行,就不会被卸载*/ void __exit exit_addsyscall(void) { unsigned long* sys_call_table = (unsigned long*)(SYS_CALL_TABLE_ADDRESS);; /*中恢复原先的系统调用*/ disable_write_protection(); sys_call_table[__NR_pedagogictime] = (unsigned long )anything_saved; enable_write_protection(); } /*这两个宏告诉系统我们真正的初始化和退出函数*/ module_init(init_addsyscall); module_exit(exit_addsyscall); ``` * 编辑 Makefile ![](https://blog.super0.xyz/usr/uploads/2021/03/2155389915.png) * 编译 make ![](https://blog.super0.xyz/usr/uploads/2021/03/1452811503.png) * 安装模块 `insmod pedagogictime.ko` ![](https://blog.super0.xyz/usr/uploads/2021/03/920320718.png) * 编写测试程序 ![](https://blog.super0.xyz/usr/uploads/2021/03/4056565838.png) * 编译运行测试程序 ![](https://blog.super0.xyz/usr/uploads/2021/03/1125341145.png) ## 预习总结 * 大致走了一下两种添加系统调用方法的流程 * 第一种较为简单,但是需要重新编译内核,时间较长 * 第二种方法较为复杂,但是不需要编译内核 * 教材使用的 Linux 版本过于老旧,需要做不少修改,比如 sys_call_table 在之前的版本中可以直接访问,但是在新版本的内核中只能通过内存地址访问,并且还需要修改 cr0 的读写位,不仅如此,如果之前在编译内核时 CONFIG_RANDOMIZE_BASE=y 的话,还需要手动关闭 `kaslr` 选项,否则每次 sys_call_table 的地址都会改变,就不能通过内存地址访问了 ![](https://blog.super0.xyz/usr/uploads/2021/03/69238857.png) * 本次预习只是实现了两种系统调用的方法,具体的修改 uid 和 实现 gettimeofday 的简化版留到实验课再做 最后修改:2021 年 03 月 24 日 09 : 55 PM © 允许规范转载 赞赏 如果觉得我的文章对你有用,请随意赞赏 赞赏作者 支付宝微信
4 条评论
《生死牌》剧情片高清在线免费观看:https://www.jgz518.com/xingkong/12417.html
《生死牌》剧情片高清在线免费观看:https://www.jgz518.com/xingkong/12417.html
《女优粤语》剧情片高清在线免费观看:https://www.jgz518.com/xingkong/134181.html
博主太厉害了!