Linux內(nèi)核中增加一個(gè)系統(tǒng)調(diào)用_第1頁
Linux內(nèi)核中增加一個(gè)系統(tǒng)調(diào)用_第2頁
Linux內(nèi)核中增加一個(gè)系統(tǒng)調(diào)用_第3頁
Linux內(nèi)核中增加一個(gè)系統(tǒng)調(diào)用_第4頁
Linux內(nèi)核中增加一個(gè)系統(tǒng)調(diào)用_第5頁
已閱讀5頁,還剩13頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

選題要求:在Linux內(nèi)核中增加一個(gè)系統(tǒng)調(diào)用,并編寫對應(yīng)的linux應(yīng)用程序。利用該系統(tǒng)調(diào)用能夠遍歷系統(tǒng)當(dāng)前所有進(jìn)程的任務(wù)描述符,并按進(jìn)程父子關(guān)系將這些描述符所對應(yīng)的進(jìn)程id(PID)組織成樹形結(jié)構(gòu)顯示。

目錄TOC\h\z\t"一,1,1,2,1.1,3,1.1.1,4"一.程序的主要設(shè)計(jì)思路,實(shí)現(xiàn)方式 11.1添加系統(tǒng)調(diào)用的兩種方法 11.1.1編譯內(nèi)核法 11.1.2內(nèi)核模塊法 11.2程序的主要設(shè)計(jì)思路 11.3環(huán)境 2二.程序的模塊劃分,及對每個(gè)模塊的說明 22.1通過內(nèi)核模塊實(shí)現(xiàn)添加系統(tǒng)調(diào)用 22.1.1修改系統(tǒng)調(diào)用的模塊 22.1.2獲取sys_call_table的地址 22.1.3清除內(nèi)存區(qū)域的寫保護(hù) 32.2編寫系統(tǒng)調(diào)用指定自己的系統(tǒng)調(diào)用 42.2.1內(nèi)核的初始化函數(shù) 42.2.2自己的系統(tǒng)調(diào)用服務(wù)例程 42.2.3移除內(nèi)核模塊時(shí),將原有的系統(tǒng)調(diào)用進(jìn)行還原 62.2.4模塊注冊相關(guān) 62.3編寫用戶態(tài)的測試程序 62.4編寫Makefile文件 7三.所遇到的問題及解決的方法 83.1進(jìn)程個(gè)數(shù)確定 83.2被更改的系統(tǒng)調(diào)用號(hào)的選擇 83.3獲取系統(tǒng)調(diào)用表的地址 83.4內(nèi)核和用戶態(tài)數(shù)據(jù)交換 8四.程序運(yùn)行結(jié)果及使用說明 84.1將編譯出來的內(nèi)核模塊hello.ko加載到內(nèi)核中 84.2通過dmesg查看輸出信息是否正確 94.3運(yùn)行測試程序,輸出樹狀打印結(jié)果(部分結(jié)果截圖) 94.4卸載自定義模塊 10五.附錄 115.1內(nèi)核模塊程序hello.c 115.2測試程序hello_test.c 145.3Makefile文件 14

一.程序的主要設(shè)計(jì)思路,實(shí)現(xiàn)方式1.1添加系統(tǒng)調(diào)用的兩種方法1.1.1編譯內(nèi)核法編寫好源碼之后修改內(nèi)核的系統(tǒng)調(diào)用庫函數(shù)/usr/include/asm-generic/unistd.h,在這里面可以使用在syscall_table中沒有用到的223號(hào)添加系統(tǒng)調(diào)用號(hào),讓系統(tǒng)根據(jù)這個(gè)號(hào),去找到syscall_table中的相應(yīng)表項(xiàng)。在/arch/x86/kernel/syscall_table_32.s文件中添加系統(tǒng)調(diào)用號(hào)和調(diào)用函數(shù)的對應(yīng)關(guān)系接著就是my_syscall的實(shí)現(xiàn)了,在這里有兩種方法:第一種方法是在kernel下自己新建一個(gè)目錄添加自己的文件,但是要編寫Makefile,而且要修改全局的Makefile。第二種比較簡便的方法是,在kernel/sys.c中添加自己的服務(wù)函數(shù),這樣子不用修改Makefile.以上準(zhǔn)備工作做完之后,然后就要進(jìn)行編譯內(nèi)核了,以下是編譯內(nèi)核的一個(gè)過程1.makemenuconfig(使用圖形化的工具,更新.config文件)2.make-j3bzImage(編譯,-j3指的是同時(shí)使用3個(gè)cpu來編譯,bzImage指的是更新grub,以便重新引導(dǎo))3.makemodules(對模塊進(jìn)行編譯)4.makemodules_install(安裝編譯好的模塊)5.depmod(進(jìn)行依賴關(guān)系的處理)6.reboot(重啟看到自己編譯好的內(nèi)核)1.1.2內(nèi)核模塊法內(nèi)核模塊可以作為獨(dú)立程序來編譯的函數(shù)和數(shù)據(jù)類型的集合。之所以提供模塊機(jī)制,是因?yàn)長inux本身是一個(gè)單內(nèi)核。單內(nèi)核由于所有內(nèi)容都集成在一起,效率很高,但可擴(kuò)展性和可維護(hù)性相對較差,模塊機(jī)制可以彌補(bǔ)這一缺陷。Linux模塊可以通過靜態(tài)或動(dòng)態(tài)的方法加載到內(nèi)核空間,靜態(tài)加載是指在內(nèi)核啟動(dòng)過程中加載;動(dòng)態(tài)加載是指在內(nèi)核運(yùn)行的過程中隨時(shí)加載。一個(gè)模塊被加載到內(nèi)核中時(shí),就成為內(nèi)核代碼的一部分。模塊加載入系統(tǒng)時(shí),系統(tǒng)修改內(nèi)核中的符號(hào)表,將新加載的模塊提供的資源和符號(hào)添加到內(nèi)核符號(hào)表中,以便模塊間通信。這種方法是采用系統(tǒng)調(diào)用攔截的一種方式,改變某一個(gè)系統(tǒng)調(diào)用號(hào)對應(yīng)的服務(wù)程序?yàn)槲覀冏约旱木帉懙某绦?,從而相?dāng)于添加了我們自己的系統(tǒng)調(diào)用。下面的內(nèi)容,會(huì)詳述用內(nèi)核模塊法實(shí)現(xiàn)目標(biāo)的過程。1.2程序的主要設(shè)計(jì)思路程序分三部分,一部分是通過內(nèi)核模塊實(shí)現(xiàn)添加系統(tǒng)調(diào)用,二是編寫系統(tǒng)調(diào)用指定自己的系統(tǒng)調(diào)用,最后是編寫用戶態(tài)的測試程序。1.3環(huán)境Ubuntu14.04+3.13.0內(nèi)核版本內(nèi)核版本:二.程序的模塊劃分,及對每個(gè)模塊的說明2.1通過內(nèi)核模塊實(shí)現(xiàn)添加系統(tǒng)調(diào)用這種方法其實(shí)是系統(tǒng)調(diào)用攔截的實(shí)現(xiàn)。系統(tǒng)調(diào)用服務(wù)程序的地址是放在sys_call_table中通過系統(tǒng)調(diào)用號(hào)定位到具體的系統(tǒng)調(diào)用地址,那么我們通過編寫內(nèi)核模塊來修改sys_call_table中的系統(tǒng)調(diào)用的地址為我們自己定義的函數(shù)的地址,就可以實(shí)現(xiàn)系統(tǒng)調(diào)用的攔截。通過模塊加載時(shí),將系統(tǒng)調(diào)用表里面的那個(gè)系統(tǒng)調(diào)用號(hào)的那個(gè)系統(tǒng)調(diào)用號(hào)對應(yīng)的系統(tǒng)調(diào)用服務(wù)例程改為我們自己實(shí)現(xiàn)的系統(tǒng)歷程函數(shù)地址。2.1.1修改系統(tǒng)調(diào)用的模塊在/usr/include/i386-linux-gnu/asm/unistd_32.h文件中查看系統(tǒng)調(diào)用序號(hào):找到結(jié)果(部分截圖):可以看到,222號(hào)和223號(hào)系統(tǒng)調(diào)用是空的,因此選取223作為新的系統(tǒng)調(diào)用號(hào)。2.1.2獲取sys_call_table的地址在/boot/System.map-3.16.0-30-generic查看系統(tǒng)調(diào)用表的內(nèi)存地址:找到結(jié)果:為0xc165e1402.1.3清除內(nèi)存區(qū)域的寫保護(hù)得到了sys_call_table的地址,該符號(hào)對應(yīng)的內(nèi)存區(qū)域是只讀的。所以我們要修改它,必須對它進(jìn)行清除寫保護(hù),這里介紹兩種方法:第一種方法:我們知道控制寄存器cr0的第16位是寫保護(hù)位。cr0的第16位置為了禁止超級(jí)權(quán)限,若清零了則允許超級(jí)權(quán)限往內(nèi)核中寫入數(shù)據(jù),這樣我們可以再寫入之前,將那一位清零,使我們可以寫入。然后寫完后,又將那一位復(fù)原就行了。//使cr0寄存器的第17位設(shè)置為0(即是內(nèi)核空間可寫)

unsignedintclear_and_return_cr0(void)//使cr0寄存器的第17位設(shè)置為0(即是內(nèi)核空間可寫)

unsignedintclear_and_return_cr0(void){unsignedintcr0=0;unsignedintret;asm("movl%%cr0,%%eax":"=a"(cr0));//將cr0寄存器的值移動(dòng)到eax寄存器中,同時(shí)輸出到cr0變量中ret=cr0;cr0&=0xfffeffff;//將cr0變量的第17位清0asm("movl%%eax,%%cr0"::"a"(cr0));//將cr0變量的值放入寄存器eax中,并且放入cr0寄存器中returnret;}

//讀取val的值到eax寄存器,再將eax寄存器的值放入cr0寄存器中改變內(nèi)核地址空間參數(shù)

voidsetback_cr0(unsignedintval){

asmvolatile("movl%%eax,%%cr0"::"a"(val));}第二種方法:通過設(shè)置虛擬地址對應(yīng)的也表項(xiàng)的讀寫屬性來設(shè)置。intmake_rw(unsignedlongaddress){unsignedintlevel;pte_t*pte=lookup_address(address,&level);//查找虛擬地址所在的頁表地址if(pte->pte&~_PAGE_RW)//設(shè)置頁表讀寫屬性pte->pte|=_PAGE_RW;return0;}intmake_ro(unsignedlongaddress){unsignedintlevel;pte_t*pte=lookup_address(address,&level);pte->pte&=~_PAGE_RW;//設(shè)置只讀屬性return0;}2.2編寫系統(tǒng)調(diào)用指定自己的系統(tǒng)調(diào)用2.2.1內(nèi)核的初始化函數(shù)此函數(shù)內(nèi)采用的是2.1.3中的第一種方法。staticint__initinit_addsyscall(void)

{

printk("hello,yinyukernel\n");

//獲取系統(tǒng)調(diào)用服務(wù)首地址

sys_call_table=(unsignedlong*)sys_call_table_address;printk("%x\n",sys_call_table);//保存系統(tǒng)調(diào)用表中的NUM位置上的系統(tǒng)調(diào)用

anything_saved=(int(*)(void))(sys_call_table[my_syscall_num]);

//使內(nèi)核地址空間可寫orig_cr0=clear_and_return_cr0();//用自己的系統(tǒng)調(diào)用替換NUM位置上的系統(tǒng)調(diào)用

sys_call_table[my_syscall_num]=(unsignedlong)&sys_mycall;

//使內(nèi)核地址空間不可寫setback_cr0(orig_cr0);

return0;}2.2.2自己的系統(tǒng)調(diào)用服務(wù)例程部分一:創(chuàng)建進(jìn)程樹voidprocesstree(structtask_struct*p,intb);結(jié)果需要以樹狀形式展示所有進(jìn)程的父子關(guān)系。為此,我們定義processtree()遞歸函數(shù)來訪問遍歷,并且將結(jié)果存儲(chǔ)在數(shù)組中,以便提供給用戶態(tài)訪問。voidprocesstree(structtask_struct*p,intb)//創(chuàng)建進(jìn)程樹(進(jìn)程,深度)

{

structlist_head*l;

a[counter].pid=p->pid;

a[counter].depth=b;

counter++;

for(l=p->children.next;l!=&(p->children);l=l->next)

{

structtask_struct*t=list_entry(l,structtask_struct,sibling);

processtree(t,b+1);

}}其中,特別使用了宏:#definelist_entry(ptr,type,member)/((type*)((char*)(ptr)-(unsignedlong)(&((type*)0)->member)))ptr是指向list_head類型鏈表的指針;type為一個(gè)結(jié)構(gòu);member為結(jié)構(gòu)type中的一個(gè)域,類型為list_head;這個(gè)宏返回指向type結(jié)構(gòu)的指針。目的:從一個(gè)結(jié)構(gòu)的成員指針找到其容器的指針部分二:創(chuàng)建自己的系統(tǒng)調(diào)用服務(wù)asmlinkagelongsys_mycall(char__user*buf);在sys_mycall()中,從當(dāng)前進(jìn)程開始,遞歸調(diào)用processtree()函數(shù),將進(jìn)程信息存儲(chǔ)在數(shù)組中。然后利用copy_to_user函數(shù)將內(nèi)核信息傳遞給用戶態(tài)下,用戶態(tài)下的測試程序?qū)Y(jié)果進(jìn)行展示。asmlinkagelongsys_mycall(char__user*buf)

{

intb=0;

structtask_struct*p;

printk("Thisisyinyu_syscall!\n");

for(p=current;p!=&init_task;p=p->parent);

processtree(p,b);

if(copy_to_user((structprocess*)buf,a,512*sizeof(structprocess)))return-EFAULT;

else

returnsizeof(a);

}2.2.3移除內(nèi)核模塊時(shí),將原有的系統(tǒng)調(diào)用進(jìn)行還原staticvoid__exitexit_addsyscall(void)

{

//設(shè)置cr0中對sys_call_table的更改權(quán)限。

orig_cr0=clear_and_return_cr0();

//恢復(fù)原有的中斷向量表中的函數(shù)指針的值。

sys_call_table[my_syscall_num]=(unsignedlong)anything_saved;

//恢復(fù)原有的cr0的值

setback_cr0(orig_cr0);

printk("callyinyuexit\n");}2.2.4模塊注冊相關(guān)模塊構(gòu)造函數(shù)module_init(init_addsyscall);執(zhí)行insmod或modprobe指令加載內(nèi)核模塊時(shí)會(huì)調(diào)用的初始化函數(shù)。函數(shù)原型必須是module_init(),內(nèi)是函數(shù)指針。模塊析構(gòu)函數(shù)module_exit(exit_addsyscall);執(zhí)行rmmod指令卸載模塊時(shí)調(diào)用的函數(shù)。函數(shù)原型是module_exit();模塊許可聲明MODULE_LICENSE("GPL");函數(shù)原型是MODULE_LICENSE(),告訴內(nèi)核程序使用的許可證,不然在加載時(shí)它會(huì)提示該模塊污染內(nèi)核。一般會(huì)寫GPL。2.3編寫用戶態(tài)的測試程序#include<linux/unistd.h>

#include<syscall.h>//asmlinkage

#include<sys/types.h>

#include<stdio.h>

structprocess

{

intpid;

intdepth;

};

structprocessa[512];

intmain()

{

inti,j;

//在內(nèi)核中將223本來對應(yīng)的系統(tǒng)調(diào)用,臨時(shí)鏈到我們自定義的sys_mycall()中。通過該系統(tǒng)調(diào)用后獲得數(shù)組a

printf("theresultis:%d\n",syscall(223,&a));

for(i=0;i<512;i++)

{

for(j=0;j<a[i].depth;j++)

printf("|-");

printf("%d\n",a[i].pid);

if(a[i+1].pid==0)

break;

}

return0;}2.4編寫Makefile文件KVERS=$(shelluname-r)

#Kernelmodules

obj-m+=hello.o

#Specifyflagsforthemodulecompilation.

#EXTRA_CFLAGS=-g-O0

build:kernel_modulesuser_test

kernel_modules:

make-C/lib/modules/$(KVERS)/buildM=$(CURDIR)modules

user_test:

gcc-ohello_testhello_test.c

clean:make-C/lib/modules/$(KVERS)/buildM=$(CURDIR)clean三.所遇到的問題及解決的方法3.1進(jìn)程個(gè)數(shù)確定系統(tǒng)可運(yùn)行的最大進(jìn)程數(shù),通過ulimit–u查看有7863個(gè)我們通過ps–ef|wc–l命令實(shí)際查看當(dāng)前運(yùn)行進(jìn)程數(shù)量為191個(gè)存儲(chǔ)進(jìn)程信息的數(shù)組大小為512是夠用的。3.2被更改的系統(tǒng)調(diào)用號(hào)的選擇見2.1.1。3.3獲取系統(tǒng)調(diào)用表的地址見2.1.2。3.4內(nèi)核和用戶態(tài)數(shù)據(jù)交換我們在內(nèi)核模塊程序中,將進(jìn)程遍歷信息存儲(chǔ)在數(shù)組中,然后需要將其傳遞給用戶態(tài)下。采用copy_from_user()和copy_to_user()這兩個(gè)函數(shù),這兩個(gè)函數(shù)負(fù)責(zé)在用戶空間和內(nèi)核空間傳遞數(shù)據(jù)。因此我們在測試程序中,將空數(shù)組a的地址作為參數(shù)傳遞給內(nèi)核模塊程序,在內(nèi)核中使用copy_to_user()函數(shù)將內(nèi)核中的數(shù)組信息傳遞給用戶態(tài)下的地址。四.程序運(yùn)行結(jié)果及使用說明4.1將編譯出來的內(nèi)核模塊hello.ko加載到內(nèi)核中加載內(nèi)核模塊命令:insmodhello.ko4.2通過dmesg查看輸出信息是否正確4.3運(yùn)行測試程序,輸出樹狀打印結(jié)果(部分結(jié)果截圖)4.4卸載自定義模塊卸載內(nèi)核模塊命令:insmodhello.ko

五.附錄5.1內(nèi)核模塊程序hello.c#include<linux/init.h>

#include<linux/module.h>

#include<linux/kernel.h>//list_head

#include<linux/unistd.h>

#include<asm/uaccess.h>

#include<linux/sched.h>//task_struct

#definemy_syscall_num223

#definesys_call_table_address0xc165e140

staticintcounter=0;

structprocess

{

intpid;

intdepth;

};

structprocessa[512];

unsignedintclear_and_return_cr0(void);

voidsetback_cr0(unsignedintval);

asmlinkagelongsys_mycall(char__user*buf);intorig_cr0;

unsignedlong*sys_call_table=0;

staticint(*anything_saved)(void);

voidprocesstree(structtask_struct*p,intb)//創(chuàng)建進(jìn)程樹(進(jìn)程,深度)

{

structlist_head*l;

a[counter].pid=p->pid;

a[counter].depth=b;

counter++;

for(l=p->children.next;l!=&(p->children);l=l->next)

{

structtask_struct*t=list_entry(l,structtask_struct,sibling);

processtree(t,b+1);

}

}

unsignedintclear_and_return_cr0(void)//使cr0寄存器的第17位設(shè)置為0(即是內(nèi)核空間可寫)

{

unsignedintcr0=0;

unsignedintret;

asm("movl%%cr0,%%eax":"=a"(cr0));

//將cr0寄存器的值移動(dòng)到eax寄存器中,同時(shí)輸出到cr0變量中

ret=cr0;

cr0&=0xfffeffff;//將cr0變量的第17位清0

asm("movl%%eax,%%cr0"::"a"(cr0));

//將cr0變量的值放入寄存器eax中,并且放入cr0寄存器中

returnret;

}

voidsetback_cr0(unsignedintval)//讀取val的值到eax寄存器,再將eax寄存器的值放入cr0寄存器中改變內(nèi)核地址空間參數(shù)

{

asmvolatile("movl%%eax,%%cr0"::"a"(val));

}

staticint__initinit_addsyscall(void)//保存原來系統(tǒng)調(diào)用表中此地址中的系統(tǒng)調(diào)用

{

printk("hello,yinyukernel\n");

sys_call_table=(unsignedlong*)sys_call_table_address;//獲取系統(tǒng)調(diào)用服務(wù)首地址

printk("%x\n",sys_call_table);

anything_saved=(int(*)(void))(sys_call_table[my_syscall_num]);//保存系統(tǒng)調(diào)用表中的NUM位置上的系統(tǒng)調(diào)用

orig_cr0=clear_and_return_cr0();//使內(nèi)核地址空間可寫

sys_call_table[my_syscall_num]=(unsignedlong)&sys_mycall;//用自己的系統(tǒng)調(diào)用替換NUM位置上的系統(tǒng)調(diào)用

setback_cr0(orig_cr0);//使內(nèi)核地址空間不可寫

return0;

}

asmlinkagelongsys_mycall(char__user*buf)

{

intb=0;

structtask_struct*p;

printk("Thisisyinyu_syscall!\n");

for(p=current;p!=&init_task;p=p->parent);

processtree(p,b);

if(copy_to_user((structprocess*)buf,a,512*sizeof(structprocess)))//將內(nèi)核空間內(nèi)容復(fù)制到用戶空間

return-EFAULT;

else

returnsizeof(a);

}

staticvoid__exitexit_addsyscall(void)

{

//設(shè)置cr0中對sys_call_table的更改權(quán)限。

orig_cr0=clear_and_return_cr0();

//恢復(fù)原有的中斷向量表中的函數(shù)指針的值。

sys_call_table[my_syscall_num]=(unsignedlong)anything_saved;

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論