使用内核模块添加系统调用

来源:互联网 发布:vscode c 函数插件 编辑:程序博客网 时间:2024/05/17 04:57

使用内核模块添加系统调用

1. 为什么要使用内核模块的方式添加系统调用?

  • 编译内核的方式费时间,一般的PC机都要两三个小时。
  • 不方便调试,一旦出现问题前面的工作都前功尽弃。

2. 首先要获取系统调用表sys_call_table的地址(虚拟地址)

因为sys_call_table在内核中没有导出,可以使用如下命令查看。

cat /boot/System.map-`uname -r`|grep sys_call_table

注意点:当我把模块在一个机子上运行成功后,如果移植到另外一个机子上马上就会出现错误,为什么呢?因为每个机子上sys_call_table的地址可能不一样。


3. 需要查看预留的系统调用号。

可以到arch/x86/include/asm/unistd.h文件中查看预留的系统调用号。
可以看出223就是一个预留的系统调用号。


4. 创建c文件。实例:syscall.c

 #include <linux/init.h>    #include <linux/module.h>    #include <linux/kernel.h>    #include <linux/unistd.h>    #include <asm/uaccess.h>    #include <linux/sched.h>    #define my_syscall_num 223    //如下的这个值要到你机子上查。cat /boot/System.map-`uname -r`|grep sys_call_table    #define sys_call_table_adress 0xc0582130//(此处是上面命令查出的地址)    unsigned int clear_and_return_cr0(void);    void setback_cr0(unsigned int val);    asmlinkage long sys_mycall(long num);    int orig_cr0;    unsigned long *sys_call_table = 0;    static int (*anything_saved)(void);    unsigned int clear_and_return_cr0(void)    {     unsigned int cr0 = 0;     unsigned int ret;     asm("movl %%cr0, %%eax":"=a"(cr0));     ret = cr0;     printk("cr0 = %d\n",ret);     cr0 &= 0xfffeffff;     asm("movl %%eax, %%cr0"::"a"(cr0));     return ret;    }    void setback_cr0(unsigned int val) //读取val的值到eax寄存器,再将eax寄存器的值放入cr0中    {     asm volatile("movl %%eax, %%cr0"::"a"(val));    }    static int __init init_addsyscall(void)    {     printk("hello, kernel\n");     sys_call_table = (unsigned long *)sys_call_table_adress;//获取系统调用服务首地址     anything_saved = (int(*)(void)) (sys_call_table[my_syscall_num]);//保存原始系统调用的地址     orig_cr0 = clear_and_return_cr0();//设置cr0可更改     sys_call_table[my_syscall_num] = (unsigned long)&sys_mycall;//更改原始的系统调用服务地址     setback_cr0(orig_cr0);//设置为原始的只读cr0     return 0;    }    asmlinkage long sys_mycall(long num)    {            printk("This is my_syscall!\n");         if(num%2==0)                   printk("my id is 0%ld\n",num%10000);         else                   printk("my id is %ld\n",num%100000);            return current->pid;    }    static void __exit exit_addsyscall(void)    {     //设置cr0中对sys_call_table的更改权限。     orig_cr0 = clear_and_return_cr0();//设置cr0可更改     //恢复原有的中断向量表中的函数指针的值。     sys_call_table[my_syscall_num] = (unsigned long)anything_saved;     //恢复原有的cr0的值     setback_cr0(orig_cr0);     printk("call exit \n");    }    module_init(init_addsyscall);    module_exit(exit_addsyscall);    MODULE_LICENSE("GPL");

5. 创建Makefile文件(与syscall.c文件创建在同一文件夹下)

obj-m := syscall.o //(与自己创建的c文件名对应)KERNELDIR := /lib/modules/$(shell uname -r)/buildPWD := $(shell pwd)modules:         $(MAKE) -C $(KERNELDIR) M=$(PWD) modulesmodules_install:         $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_installclean:         rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

6. 执行make命令


7. 执行sudo insmod syscall.ko 命令


8. 将模块插入成功后,剩下的就是在用户态下测试是否成功


9. 编写c程序(test.c)

#include<stdio.h>int main(){         syscall(223,13130000);//第二个参数填你的学号         return 0;}

10. 编译c程序 gcc –o test test.c


11. dmesg

0 0