liunx(3)-内核模块编写与系统调用
文章目錄
- 問題
- 運行環境
- 程序組成
- 實現思路
- helllo.c
- Makefile
- main.c
- 程序運行
問題
??????在Linux內核中增加一個系統調用,并編寫對應的linux應用程序。利用該系統調用能夠遍歷系統當前所有進程的任務描述符,并按進程父子關系將這些描述符所對應的進程id顯示出來。
運行環境
??????Ubuntu-20.04 64位虛擬機
程序組成
??????1,采用hello.c實現新的系統調用的函數 ,按照內核模塊編寫的規范來編寫程序(包含有兩部分:1,修改系統調用表;2,編寫自己的系統調用服務程序)
??????2,main.c 用來進行調用所編寫的中斷例程
實現思路
??????因為之前了解過匯編的中斷向量表,有過一定的實踐,所以直接采用修改linux系統調用表的方法。
1,如何在系統調用表中添加新的表項?
??????先找到系統調用表的入口地址,由于現在的linux系統會對給表做一個安全的保護,不讓用戶輕易的修改系統調用表,所以需要去掉系統的保護之后,才能將我們自己編寫的程序地址加載到系統調用表中,接下來才能被調用。
??????a,查找系統調用表地址 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是我們后面會采用到的。注意:這個地址并非一成不變的,特別的,每次開機都需要執行該命令獲得新的系統調用表的地址。
??????b,需要修改cr0控制寄存器的第16位以便于用來寫系統調用表,將該位清0,禁用系統對調用表的寫保護。在此采用匯編語言來實現。注意:在使用內嵌匯編時,在linux下需要采用AT&T的匯編語法,并且在對寄存器操作時應該禁用中斷響應,防止出錯。禁用寫保護的匯編代碼如下:
??????c,修改系統調用表項,將其替換為自己編寫的程序。首先將得到的系統調用表地址保存到變量SYS_CALL_TABLE_ADDRESS中(每次開機都要進行修改),然后將系統調用表223號表項內容保存起來,禁用系統的寫保護,將我們編寫的函數sys_mycall地址替換223號表項,恢復系統的寫保護,整個替換工作就此結束。代碼如下:
sys_call_table_my=(unsigned long*)(SYS_CALL_TABLE_ADDRESS); //初始化全局變量saveAddress=sys_call_table_my[NUM]; //保存223號表項clear_cr0();sys_call_table_my[NUM]=(unsigned long) &sys_mycall; //設置223號表項為我們自己的函數restore_cr0();2,實現具體想要的功能,將所有進程按照父子關系進行顯示。
??????采用list_for_each函數,從init_task的地址開始遞歸訪問即可,代碼如下:
helllo.c
??????這里需要注意一下內核模塊的編寫規范,包括模塊憑證,入口和退出函數,以及printk函數的使用。
#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對應的地址 #define NUM 223static unsigned long saveAddress;//保存223號表項的入口地址 unsigned long* sys_call_table_my=0;//用來作為系統調用表的首地址 static int clear_cr0(void)//AT&T 64位匯編 {asm volatile("cli;" //禁止中斷"push %rax;""movq %cr0,%rax;""and $0xfffffffffffeffff,%rax;" //第16位置0,禁用寫保護"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;" //恢復中斷);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)//測試自己的系統調用 {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號表項clear_cr0();sys_call_table_my[NUM]=(unsigned long) &sys_mycall; //設置223號表項為我們自己的函數restore_cr0();return 0; }static void call_exit(void) {printk(KERN_INFO"call_exit......\n");clear_cr0();sys_call_table_my[NUM]=saveAddress; //恢復223號表項原有內容restore_cr0(); }module_init(call_init); module_exit(call_exit);MODULE_AUTHOR("cedric");Makefile
??????這是一個可用的Makefile文件,文件名就是Makefile,不需要后綴名。它對hello.c程序進行編譯。
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
??????它僅用來調用新的223號中斷程序,測試最終結果。
#include<unistd.h> #include<sys/syscall.h> #include<stdio.h>int main() {int pid=syscall(223); //在此調用223號中斷例程。printf("The current pid is: %d\n",pid);return 0; }程序運行
1,采用 sudo cat /proc/kallsyms | grep sys_call_table 命令獲取當前電腦的系統調用表地址。
2,修改hello.c 中的對應語句,如下:
#define SYS_CALL_TABLE_ADDRESS 0xffffffff87a013a0
3,進入到root模式下make: sudo su + make
4,加載內核模塊 hello.ko: insmod hello.ko
5,編譯main.c: gcc –o main main.c
6,通過main調用223號服務例程: ./main
7,在系統日志中查看輸出結果: cat /var/log/syslog
8,卸載內核模塊: rmmod hello
總結
以上是生活随笔為你收集整理的liunx(3)-内核模块编写与系统调用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux(2)- 共享内存的实现
- 下一篇: linux(4)-Ptrace 系统调用