mode syscall - Jokacer/Learn GitHub Wiki
直接在内核中修改添加系统调用在进行编译的时候需要花费大量时间,调试、debug的代价太大,而利用模块添加系统调用则更加方便快捷,由模块的方便加载和卸载的特点,在测试系统调用功能时会提供很大的帮助。
模块的写法如下:
//必要的头文件
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/syscalls.h>
#include <linux/sched.h>
#include <linux/unistd.h>
#include <linux/delay.h>
//模块许可证声明(必须)
MODULE_LICENSE("Dual BSD/GPL");
void **sys_call_table; //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));
}
unsigned long *find_sys_call_table(void){
unsigned long *p;
p=(unsigned long *)((unsigned long) sys_close+0xb83760);//syscall_table地址距sys_close相差0xb83760
return p;
}
asmlinkage long sys_mycall(int x) //定义自己的系统调用
{
printk("模块系统调用-当前pid:%d,当前comm:%s,get int :%d\n",current->pid,current->comm,x);
printk("hello,world!\n");
return current->pid;
}
//模块加载函数(必须)
static int hello_init(void)
{
printk("call_init......\n");
sys_call_table_my=find_sys_call_table();
if(!sys_call_table_my){
printk(KERN_DEBUG"ERROR:Cannot find");
return -1;
}
printk("after judge-----------\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 hello_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(hello_init);
module_exit(hello_exit);
//声明模块的作者(可选)
MODULE_AUTHOR("Jokacer");
//声明模块的描述(可选)
MODULE_DESCRIPTION("mySyscall\n");
//声明模块的别名(可选)
MODULE_ALIAS("Syscall");
Makefile文件写法如下:
KERNEL_VER = $(shell uname -r)
# the file to compile
obj-m += hello.o
# specify flags for the module compilation
EXTRA_CFLAGS = -g -O0
build: kernel_modules
kernel_modules:
make -C /usr/src/linux-headers-4.15.0-55-generic M=$(shell pwd) modules
clean:
make -C /usr/src/linux-headers-4.15.0-55-generic M=$(shell pwd) clean
由于sys_call_table
地址每次重启电脑后会变化,所有需要注意修改,可以通过
sudo cat /proc/kallsyms | grep sys_call_table
来查询当前的sys_call_table地址。 目前还在寻找动态检测sys_call_table地址的方法。
测试程序:
#include <linux/unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <stdio.h>
int main(void)
{
long pid = 0;
pid = syscall(223,2);
printf("mod-syscall test one ! %ld\n",pid);
return 0;
}