添加一個簡單的系統調用——內核模塊法

操作系統作業:添加一個簡單的系統調用(內核模塊法)


一、實驗目的

  1. 學習Linux的內核的系統調用。
  2. 理解、掌握Linux系統調用的實現框架、用戶界面、參數傳遞、進入/返回過程。

二、實驗內容

在系統調用中添加一個不用傳遞參數的系統調用,實現一個簡單的系統調用的添加。執行這個系統調用,在屏幕上顯示輸出“Hello World! ”。


三、實驗環境

虛擬機軟件:Vmware Workstation
Linux發行版:CentOS 7 內置內核:linux 3.10.0
待編譯內核:linux 4.15.10


四、操作方法和步驟

(1)準備工作

1.查詢syscall_table的地址,執行命令:

sudo cat /proc/kallsyms | grep sys_call_table

查詢syscall_table地址
2. 查詢可用的系統調用號,使用vim編輯器打開unistd_32.h,執行命令:

vim /usr/include/asm/unistd_32.h

在普通模式下,輸入G或者shift+g定位到文尾,可以看到最後一個是383,因此384是可用的系統調用號
查詢可用的系統調用號

(2)創建hello.c文件

  • sys_call_table爲上面查詢到的系統調用表的地址,NUM爲待添加的系統調用號
  • 使用vim命令創建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 0xffffffffa8400160  //sys_call_table對應的地址
#define NUM 384  //系統調用號爲384
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文件

  • 使用vim命令創建Makefile文件,添加以下內容
obj-m:=hello.o
CURRENT_PATH:=$(shell pwd)
LINUX_KERNEL_PATH:=/usr/src/linux-4.15.10
all:
	make -C  $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
clean:
	make -C  $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
  • 注意:LINUX_KERNEL_PATH是內核代碼的位置

(4)安裝內核模塊

1.執行命令:

make

2.查看make是否成功,執行命令:

ls | grep 'hello.*'

看到有komod.cmod.o文件就說明成功了

3.使用insmod插入模塊,執行命令:

insmod hello.ko

4.使用lsmod查看模塊是否插入成功,執行命令:

lsmod

Module出現hello,表示模塊插入成功

(5)測試系統調用

1.使用vim創建test.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(483);        //測試483號系統調用
        printf("17計科-1727405169 syscall result: %ld\n", x);
        return 0;
}

2.使用gcc編譯,執行命令:

gcc test.c

3.運行a.out,執行命令:

./a.out

出現以下結果就成功了。

成功運行


五、總結

  • 兩個字高度概括,簡單!
  • 剩下的你們自己慢慢寫吧,有問題歡迎在留言區交
    流,謝謝觀看。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章