liunx(3)-内核模块编写与系统调用
文章目錄
- 問(wèn)題
- 運(yùn)行環(huán)境
- 程序組成
- 實(shí)現(xiàn)思路
- helllo.c
- Makefile
- main.c
- 程序運(yùn)行
問(wèn)題
??????在Linux內(nèi)核中增加一個(gè)系統(tǒng)調(diào)用,并編寫(xiě)對(duì)應(yīng)的linux應(yīng)用程序。利用該系統(tǒng)調(diào)用能夠遍歷系統(tǒng)當(dāng)前所有進(jìn)程的任務(wù)描述符,并按進(jìn)程父子關(guān)系將這些描述符所對(duì)應(yīng)的進(jìn)程id顯示出來(lái)。
運(yùn)行環(huán)境
??????Ubuntu-20.04 64位虛擬機(jī)
程序組成
??????1,采用hello.c實(shí)現(xiàn)新的系統(tǒng)調(diào)用的函數(shù) ,按照內(nèi)核模塊編寫(xiě)的規(guī)范來(lái)編寫(xiě)程序(包含有兩部分:1,修改系統(tǒng)調(diào)用表;2,編寫(xiě)自己的系統(tǒng)調(diào)用服務(wù)程序)
??????2,main.c 用來(lái)進(jìn)行調(diào)用所編寫(xiě)的中斷例程
實(shí)現(xiàn)思路
??????因?yàn)橹傲私膺^(guò)匯編的中斷向量表,有過(guò)一定的實(shí)踐,所以直接采用修改linux系統(tǒng)調(diào)用表的方法。
1,如何在系統(tǒng)調(diào)用表中添加新的表項(xiàng)?
??????先找到系統(tǒng)調(diào)用表的入口地址,由于現(xiàn)在的linux系統(tǒng)會(huì)對(duì)給表做一個(gè)安全的保護(hù),不讓用戶(hù)輕易的修改系統(tǒng)調(diào)用表,所以需要去掉系統(tǒng)的保護(hù)之后,才能將我們自己編寫(xiě)的程序地址加載到系統(tǒng)調(diào)用表中,接下來(lái)才能被調(diào)用。
??????a,查找系統(tǒng)調(diào)用表地址 sudo cat /proc/kallsyms | grep sys_call_table 演示如下:
cedric@ubuntu:~/Desktop/C$ sudo cat /proc/kallsyms | grep sys_call_table ffffffffb7400280 R x32_sys_call_table ffffffffb74013a0 R sys_call_table ffffffffb74023e0 R ia32_sys_call_table??????其中中間的地址ffffffffb74013a0是我們后面會(huì)采用到的。注意:這個(gè)地址并非一成不變的,特別的,每次開(kāi)機(jī)都需要執(zhí)行該命令獲得新的系統(tǒng)調(diào)用表的地址。
??????b,需要修改cr0控制寄存器的第16位以便于用來(lái)寫(xiě)系統(tǒng)調(diào)用表,將該位清0,禁用系統(tǒng)對(duì)調(diào)用表的寫(xiě)保護(hù)。在此采用匯編語(yǔ)言來(lái)實(shí)現(xiàn)。注意:在使用內(nèi)嵌匯編時(shí),在linux下需要采用AT&T的匯編語(yǔ)法,并且在對(duì)寄存器操作時(shí)應(yīng)該禁用中斷響應(yīng),防止出錯(cuò)。禁用寫(xiě)保護(hù)的匯編代碼如下:
??????c,修改系統(tǒng)調(diào)用表項(xiàng),將其替換為自己編寫(xiě)的程序。首先將得到的系統(tǒng)調(diào)用表地址保存到變量SYS_CALL_TABLE_ADDRESS中(每次開(kāi)機(jī)都要進(jìn)行修改),然后將系統(tǒng)調(diào)用表223號(hào)表項(xiàng)內(nèi)容保存起來(lái),禁用系統(tǒng)的寫(xiě)保護(hù),將我們編寫(xiě)的函數(shù)sys_mycall地址替換223號(hào)表項(xiàng),恢復(fù)系統(tǒng)的寫(xiě)保護(hù),整個(gè)替換工作就此結(jié)束。代碼如下:
sys_call_table_my=(unsigned long*)(SYS_CALL_TABLE_ADDRESS); //初始化全局變量saveAddress=sys_call_table_my[NUM]; //保存223號(hào)表項(xiàng)clear_cr0();sys_call_table_my[NUM]=(unsigned long) &sys_mycall; //設(shè)置223號(hào)表項(xiàng)為我們自己的函數(shù)restore_cr0();2,實(shí)現(xiàn)具體想要的功能,將所有進(jìn)程按照父子關(guān)系進(jìn)行顯示。
??????采用list_for_each函數(shù),從init_task的地址開(kāi)始遞歸訪(fǎng)問(wèn)即可,代碼如下:
helllo.c
??????這里需要注意一下內(nèi)核模塊的編寫(xiě)規(guī)范,包括模塊憑證,入口和退出函數(shù),以及printk函數(shù)的使用。
#include <linux/init.h> #include <linux/module.h> #include <linux/sched.h> //定義task_struct #include <linux/list.h> //定義list_head #include <linux/init_task.h> //定義init_task MODULE_LICENSE("Dual BSD/GPL");#define SYS_CALL_TABLE_ADDRESS 0xffffffff87a013a0 //本電腦sys_call_table對(duì)應(yīng)的地址 #define NUM 223static unsigned long saveAddress;//保存223號(hào)表項(xiàng)的入口地址 unsigned long* sys_call_table_my=0;//用來(lái)作為系統(tǒng)調(diào)用表的首地址 static int clear_cr0(void)//AT&T 64位匯編 {asm volatile("cli;" //禁止中斷"push %rax;""movq %cr0,%rax;""and $0xfffffffffffeffff,%rax;" //第16位置0,禁用寫(xiě)保護(hù)"movq %rax,%cr0;""popq %rax;"//"sti;");printk(KERN_INFO " Hello World enter\n");return 0; }static void restore_cr0(void) {asm volatile(//"cli;""pushq %rax;""movq %cr0,%rax;""or $0x0000000000010000,%rax;""movq %rax,%cr0;""popq %rax;""sti;" //恢復(fù)中斷);printk(KERN_INFO " Cruel World exit\n"); }static void getTasks(struct task_struct* root) {struct task_struct* p;struct list_head* list;list_for_each(list,&root->children) {p = list_entry(list,struct task_struct,sibling);printk("parent: id and name: %d %s myself: id and name: %d %s\n",p->parent->pid,p->parent->comm,p->pid,p->comm);getTasks(p);} }static int sys_mycall(void)//測(cè)試自己的系統(tǒng)調(diào)用 {printk(KERN_INFO"The pids will be show...: \n");getTasks(&init_task);return current->pid; }static int call_init(void) {sys_call_table_my=(unsigned long*)(SYS_CALL_TABLE_ADDRESS); //初始化全局變量printk(KERN_INFO"call_init......\n");saveAddress=sys_call_table_my[NUM]; //保存223號(hào)表項(xiàng)clear_cr0();sys_call_table_my[NUM]=(unsigned long) &sys_mycall; //設(shè)置223號(hào)表項(xiàng)為我們自己的函數(shù)restore_cr0();return 0; }static void call_exit(void) {printk(KERN_INFO"call_exit......\n");clear_cr0();sys_call_table_my[NUM]=saveAddress; //恢復(fù)223號(hào)表項(xiàng)原有內(nèi)容restore_cr0(); }module_init(call_init); module_exit(call_exit);MODULE_AUTHOR("cedric");Makefile
??????這是一個(gè)可用的Makefile文件,文件名就是Makefile,不需要后綴名。它對(duì)hello.c程序進(jìn)行編譯。
ifneq ($(KERNELRELEASE),) # call from kernel build systemobj-m := hello.o elseKERNELDIR := /lib/modules/$(shell uname -r)/buildPWD := $(shell pwd) default:make -C $(KERNELDIR) M=$(PWD) modules endifmain.c
??????它僅用來(lái)調(diào)用新的223號(hào)中斷程序,測(cè)試最終結(jié)果。
#include<unistd.h> #include<sys/syscall.h> #include<stdio.h>int main() {int pid=syscall(223); //在此調(diào)用223號(hào)中斷例程。printf("The current pid is: %d\n",pid);return 0; }程序運(yùn)行
1,采用 sudo cat /proc/kallsyms | grep sys_call_table 命令獲取當(dāng)前電腦的系統(tǒng)調(diào)用表地址。
2,修改hello.c 中的對(duì)應(yīng)語(yǔ)句,如下:
#define SYS_CALL_TABLE_ADDRESS 0xffffffff87a013a0
3,進(jìn)入到root模式下make: sudo su + make
4,加載內(nèi)核模塊 hello.ko: insmod hello.ko
5,編譯main.c: gcc –o main main.c
6,通過(guò)main調(diào)用223號(hào)服務(wù)例程: ./main
7,在系統(tǒng)日志中查看輸出結(jié)果: cat /var/log/syslog
8,卸載內(nèi)核模塊: rmmod hello
總結(jié)
以上是生活随笔為你收集整理的liunx(3)-内核模块编写与系统调用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: linux(2)- 共享内存的实现
- 下一篇: 操作系统储存管理功能