Skip to content

Latest commit

 

History

History
164 lines (127 loc) · 6.09 KB

File metadata and controls

164 lines (127 loc) · 6.09 KB

添加系统函数——模块插入法

说明:该方法是在linux内核版本为4.4.4下进行操作(高版本可能会出现无法修改系统调用表的问题)

  • 内核版本的查看:打开终端,输入命令uname -r进行查看

在确定linux内核版本是4.4.4之后进行以下一系列操作:

  • /usr/include/x86_64-linux-gnu/asm/uniste_64.h中查找一个未使用的系统调用编号。
    • 如果在系统调用编号列表中间有未使用的系统调用编号,可以直接使用它,例如,225和227号被使用,但是226没有被使用,则可以使用226该编号。
    • 如果在系统调用编号列表中间没有未使用的系统调用编号,可以查看列表最后一个编号,例如,最后一个编号是325,则可以使用326该编号,或者更大的数字,如327,328等。

在此次测试中,使用的是326编号(根据自己机器情况进行确定)

  • 获取本机的sys_call_table的地址,命令为:sudo cat /boot/System map-'uname -r'|grep sys_call_table (注意命令中的将uname -r括起的符号为键盘Tab按键上方的按键在英文输入法下的符号``) 在此次测试中,本机的sys_call_table的地址是ffffffff81a001c0

说明:以上两步获得的编号和地址要在插入模块的代码中使用

  • 编写代码user_define_mode.c 使用touch user_define_mode.c命令进行user_define_mode.c文件的新建,gedit user_deifne_mode.c命令进行对user_define_mode.c的编辑(当然也可以直接在图形界面进行新建与编辑),user_define_mode.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>
//从usr/include/x86_64-linux-gnu/asm/unistd_64.h最后查找到的一个未使用的系统调用编号
#define my_syscall_num 326
//如下的这个就是上一步获得的sys_call_table的地址值
#define sys_call_table_adress 0xffffffff81a001c0
        
unsigned int clear_and_return_cr0(void);
void setback_cr0(unsigned int val);
asmlinkage long sys_mycall(long);
        
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("movq %%cr0, %%rax":"=a"(cr0));
   ret = cr0;
   cr0 &= 0xfffffffffffeffff;
   asm("movq %%rax, %%cr0"::"a"(cr0));
   return ret;
}
        
void setback_cr0(unsigned int val) //读取val的值到rax寄存器,再将rax寄存器的值放入cr0中
{
   asm volatile("movq %%rax, %%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 system call!\n");
   return num;
}
        
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");

在编写完代码user_define_mode.c之后,要先新建一个Makefile文件,Makefile文件要和user_define_mode.c文件放在同一个文件夹中

  • 编写Makefile脚本(Makefile中内容的格式一定要正确,obj-m :=后的内容一定要与前边插入模块的文件名相同,例如,前边使用的是user_define_mode.c,那么此处就应该是user_define_mode.o)
obj-m := user_define_mode.o
	KERNELDIR := /lib/modules/$(shell uname -r)/build
	PWD := $(shell pwd)
modules: 
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install: 
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean: 
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
  • make user_define_mode模块,并将模块插入(以下几步需要在上边user_define_mode.c和Makefile文件所在的文件夹下进行)
make #执行该命令之后会生成一个user_define_mode.ko文件
sudo insmod user_define_mode.ko   # 插入模块
lsmod  #查看模块是否插入成功

插入成功的显示如下图(注意Used by的信息为0则插入成功):

  • 进行测试 此次的测试代码1:
#include<stdio.h>
#include<unistd.h>
    
int main(){
    long pid = 0;
    long abc = 500;   
    pid = syscall(326,abc);  //这里的326要与在user_define_mode.c中#define my_syscall_num的编号一致
    printf("num:%ld\n",pid);
    return 0;
}

在模块插入成功的情况下,使用该测试代码可看出代码执行结果:num:500,当给abc赋值为负数的时候,由于linux机制,返回的总是-1,并不是表示错误。

测试代码2:

#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <stdlib.h>

int main(void)
{
    long in;
    printf("-----------------------------------------\n");
    in = syscall(326,malloc(5));
    printf("use syscall(326) : in = %ld\n",in);
    return 0;
}

在模块插入成功的情况下,使用该测试代码可看出代码执行结果:返回开辟空间的首地址,当给malloc(负数)时,返回0,表示开辟空间失败。 使用命令dmesg可以看到所添加的系统函数的打印结果:This is my system call!

多次测试,可以使用命令sudo rmmod mode 删除之前加上的模块(如本次测试的删除模块的命令为sudo rmmod user_define_mode)