吐槽一下操作系统实践,教材代码全是内核3.3之前的版本。。。
记录一下系统调用的实验,希望对各位有所帮助。
方法一:内核编译法
这里我给一个链接,这个博客写的很好,能顺利运行(失败的话就是自己的操作有问题):https://blog.csdn.net/qq_41175905/article/details/80529245
方法二:通过module进行内核添加
1.这里在任何一个文件夹下编写即可
2.以hello.c为例,这里我看了一些博客,直接拿他们代码过来用了,只不过需要需要修改一些旧的版本,例如movl写成movq,寄存器eax写成rax,cr0 &= 0xfffeffff应该写成0xfffffffffffeffff,就是添加8个f。
需要注意的时,下面sys_call_table的地址应该修改为你的电脑此时此刻显示的地址,0x不能省略,同时每次重启地址都会变化。
查询syscall_table地址的命令为:
sudo cat /proc/kallsyms | grep sys_call_table
hello.c
#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 0xffffffffa04001a0 //sys_call_table对应的地址
#define NUM 223 //系统调用号为223
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,world!\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);
3.Makefile文件的编写(M必须大写)
obj-m:=hello.o
CURRENT_PATH:=$(shell pwd)
LINUX_KERNEL_PATH:=/usr/src/linux-headers-4.15.0-46-generic
all:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
依次输入如下命令:
sudo make
sudo insmod hello.ko
lsmod //查看hello是否插入模块
4.编写测试c文件
#include<stdio.h>
#include<stdlib.h>
#include<linux/kernel.h>
#include<sys/syscall.h>
#include<unistd.h>
int main()
{
unsigned long x = 0;
x = syscall(223); //测试223号系统调用
printf("图灵-1627405131 syscall result: %ld\n", x);
return 0;
}