




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、选题要求:在 Linux 内核中增加一个系统调用,并编写对应的 linux 应用程序。 利用该系统调用能够遍历系统当前所有进程的任务描述符, 并按进程父子关系将 这些描述符所对应的进程id ( PID)组织成树形结构显示。目录一. 程序的主要设计思路,实现方式 41.1 添加系统调用的两种方法 4.1.1.1 编译内核法 41.1.2 内核模块法 41.2 程序的主要设计思路 5.1.3 环境 5.二. 程序的模块划分,及对每个模块的说明 52.1 通过内核模块实现添加系统调用 5.2.1.1 修改系统调用的模块 52.1.2 获取 sys_call_table 的地址 52.1.3 清除内存
2、区域的写保护 62.2 编写系统调用指定自己的系统调用 7.2.2.1 内核的初始化函数 72.2.2自己的系统调用服务例程 72.2.3 移除内核模块时,将原有的系统调用进行还原 92.2.4 模块注册相关 92.3 编写用户态的测试程序 9.2.4 编写 Makefile 文件 1.0.三 . 所遇到的问题及解决的方法 113.1 进程个数确定 1.13.2 被更改的系统调用号的选择 1.13.3 获取系统调用表的地址 1.13.4 内核和用户态数据交换 1.1四. 程序运行结果及使用说明 114.1 将编译出来的内核模块 hello.ko 加载到内核中 114.2 通过 dmesg 查看
3、输出信息是否正确 1.14.3 运行测试程序,输出树状打印结果(部分结果截图) 124.4 卸载自定义模块 1.3五 . 附录 145.1 内核模块程序 hello.c1.45.2 测试程序 hello_test.c 1.7.5.3 Makefile 文件 1.7.程序的主要设计思路,实现方式1.1添加系统调用的两种方法1.1.1编译内核法编写好源码之后?修改内核的系统调用库函数usri nclude/asm-ge neric/u nistd.h,在这里面可以使用在SySCall_table 中没有用到的 223号?添加系统调用号,让系统根据这个号,去找到SySCall_table中的相应表项
4、。在archx86kernelSySCaILtable_32.s文件中添加系统调用号和调用函数的对应关系?接着就是my_syscall的实现了,在这里有两种方法:第一种方法是在kernel下自己新建一个目录添加自己的文件,但是要编写 Makefile ,而且要修改全局的Makefile 。第二种比较简便的方法是,在kernel/sys.c中添加自己的服务函数,这样子不用修改Makefile.以上准备工作做完之后,然后就要进行编译内核了,以下是编译内核的一个过程1. make menuconfig ( 使用图形化的工具,更新 .config 文件)2. make -j3 bzImage(编译,-
5、j3指的是同时使用 3个CPU来编译,bzImage指的是更新grub ,以便重新引导)3. make modules(对模块进行编译)4. make modules_i nstall(安装编译好的模块)5. depmod(进行依赖关系的处理)6. reboot(重启看到自己编译好的内核)1.1.2内核模块法内核模块可以作为独立程序来编译的函数和数据类型的集合。之所以提供模块机制,是因为LinUX本身是一个单内核。单内核由于所有内容都集成在一起,效率很高,但可扩展性和可维护性相对较差,模块机制可以弥补这一缺陷。LinUX模块可以通过静态或动态的方法加载到内核空间,静态加载是指在内核启动过程中加
6、载;动态加载是指在内核运行的过程中随时加载。一个模块被加载到内核中时,就成为内核代码的一部分。模块加载入系统时,系统修改内核中的符号表,将新加载的模块提供的资源和符号添加到内核符号表中,以便模块间通信。这种方法是采用系统调用拦截的一种方式,改变某一个系统调用号对应的服务程序为我们自己的编写的程序,从而相当于添加了我们自己的系统调用。下面的内容,会详述用内核模块法实现目标的过程。1.2程序的主要设计思路二是编写系统调用指定自己程序分三部分,一部分是通过内核模块实现添加系统调用, 的系统调用,最后是编写用户态的测试程序。1.3环境Ubu ntu14.04 + 3.13.0 内核版本内核版本:vir
7、tual nachine:/boot# nae aLiriuX Iinux irtual nachic 3.13.0 24 gcrerlc *46 UbUrItU SMP ThU APr 10 19i0; 14Irr匚 2014 i60e CthIOn 6Sfi CNUZLtnJX二.程序的模块划分,及对每个模块的说明2.1通过内核模块实现添加系统调用这种方法其实是系统调用拦截的实现。系统调用服务程序的地址是放在sys_caILtable中通过系统调用号定位到具体的系统调用地址,那么我们通过编写内核模块来修改SyS_call_table中的系统调用的地址为我们自己定义的函数的地址,就可以实现系
8、统调用的拦截。通过模块加载时,将系统调用表里面的那个系统调用号的那个系统调用号对应的系统调用服务例程改为我们自己实现的系统历程函数地址。2.1.1修改系统调用的模块在 usri ncludei386-li nux-g nu/asm/u nistd_32.h 文件中查看系统调用序号:rootinux virtual machine:/usr/includc/i.3B6 AiPUX gnu/osn# gcdt UrliEtd 32rh找到结果(部分截图)tfdefte _NR_fCritl.64 221«defte _NR_getttd 224 defte _NR_readahead 2
9、Z5#defte _NR-SetXattr 226 defte NR_lSetXattr 227可以看到,222号和223号系统调用是空的,因此选取223作为新的系统调用号。2.1.2 获取 SyS_call_table的地址 在/boot/SyStem.map-3.16.0-30-generic查看系统调用表的内存地址:rootnuX-ViLrtUal-FIeChie: / boot# gedtt SyStenknaP-3.13.-24-generic找到结果:cl6Sel40 R SyS_c5ll_table为 0xc165e1402.1.3清除内存区域的写保护得到了 sys_caILta
10、bIe的地址,该符号对应的内存区域是只读的。所以我们要修改它,必须对它进行清除写保护,这里介绍两种方法:第一种方法:我们知道控制寄存器 Cro的第16位是写保护位。Cro的第16位置为了禁止超 级权限,若清零了则允许超级权限往内核中写入数据,这样我们可以再写入之前,将那一位清零,使我们可以写入。然后写完后,又将那一位复原就行了。/使cr0寄存器的第17位设置为0 (即是内核空间可写)Un Sig ned int clear_a nd_retur n_cr0(Void)Un Sig ned int cr0 = 0;Un Sig ned int ret;asm("movl %cr0, %
11、eax":"=a"(cr0);/将cr0寄存器的值移动到 eax寄存器中,同时输出到cr0变量中ret = cr0;cr0 &= OXfffeffff;/将 cr0 变量的第 17 位清 0asm("movl %eax, %cr0":"a"(cr0);/将cr0变量的值放入寄存器 eax中,并且放入cr0寄存器中return ret;/读取val的值到eax寄存器,再将eax寄存器的值放入 cr0寄存器中-改变内核地址空间参数void SetbaCk_cr0 (Un Sig ned int val)asm volat
12、ile("movl %eax, %cr0":"a"(val);第二种方法:通过设置虚拟地址对应的也表项的读写属性来设置。int make_rw (Un Sig ned Iong address)Un Sig ned int level;Pte_t *pte = lookup_address(address, & level);/查找虚拟地址所在的页表地址if (pte->pte & _PAGE_RW)/设置页表读写属性pte->pte I=_PAGE_RW;return 0;int make_r O(Un Sig ned Io
13、ng address)Un Sig ned int level;Pte_t *pte = IookUP_address(address, &level);pte->pte &= _PAGE_RW; Zz 设置只读属性return 0;2.2编写系统调用指定自己的系统调用2.2.1内核的初始化函数此函数内采用的是2.1.3中的第一种方法。StatiC int _in it ini t_addsyscall(void)Prin tk("hello, yinyu kern eln");ZZ获取系统调用服务首地址SyS_call_table = (Un Sig
14、 ned Iong *)sys_caILtabIe_address;Prin tk("%xn",sys_call_table);ZZ保存系统调用表中的NUM位置上的系统调用anythin g_SaVed = (in t(*)(void) (SyS_call_tablemy_SySCall_ nu m);ZZ使内核地址空间可写orig_cr0 = clear_a nd_retur n_crO();ZZ用自己的系统调用替换NUM位置上的系统调用SyS_call_tablemy_SySCall_ num= (Un Sig ned Iong)& SyS_mycall;ZZ使
15、内核地址空间不可写SetbaCk_cr0(orig_cr0);return 0;2.2.2自己的系统调用服务例程部分一:创建进程树void PrOCeSStree(StrUCt task_StrUCt * p,i nt b);结果需要以树状形式展示所有进程的父子关系。为此,我们定义PrOCeSStree()归函数来访问遍历,并且将结果存储在数组中,以便提供给用户态访问。void PrOCeSStree(StrUCt task_StrUCt * p,i nt b)ZZ创建进程树(进程,深度)StrUCt list_head * I;aco Un ter.pid = P -> pid;aco
16、 Un ter.depth = b;CoUn ter +;for(l = P -> ChiIdre n.n ext; I != & (p->childre n); I = l->n ext)StrUCt task_StrUCt *t = list_e ntry(l,struct task_StrUCt,sibli ng); PrOCeSStree(t,b+1);其中,特别使用了宏:#defi ne list_e ntry(ptr, type, member) / (type*)(Char*)(ptr)-( Un Sig nedIong)(&(type *)0)
17、->member)Ptr是指向list_head类型链表的指针;type为一个结构;member为结构type中的一个域,类型为 list_head ;这个宏返回指向type结构的指针。目的:从一个结构的成员指针找到其容器的指针部分二:创建自己的系统调用服务asmli nkage Iong SyS_mycall(char _USer * buf);在SyS_mycall()中,从当前进程开始,递归调用 PrOCeSStree()函数,将进程信息存储在数 组中。然后利用copy_to_USer函数将内核信息传递给用户态下,用户态下的测试程序对结果进行展示。asmli nkage Iong
18、SyS_mycall(char _USer * buf)int b = 0;StrUCt task_StrUCt * p;Prin tk("This is yinyU _SySCall!n");for(p = CUrre nt; P != &ini t_task; P = p->pare nt ); PrOCeSStree(P,b);if(copy_to_USer(StrUCt PrOCeSS *)bUf,a,512*sizeof(strUCt PrOCeSS)retUrn -EFAULT;elseretUr n SiZeOf(a);223移除内核模块时,将原
19、有的系统调用进行还原StatiC Void _exit exit_addsyscall(Void)/设置Cro中对SyS_call_table 的更改权限。orig_cr0 = clear_a nd_retur n_cr0();/恢复原有的中断向量表中的函数指针的值。SyS_call_tablemy_SySCall_num= (UnSigned Iong)anything_saved;恢复原有的cr0的值SetbaCk_cr0(orig_cr0);Prin tk("call yinyu exit n");2.2.4模块注册相关模块构造函数module_ in it(i ni
20、 t_addsyscall);执行insmod或modprobe指令加载内核模块时会调用的初始化函数。函数原型必须是module_init(),内是函数指针。模块析构函数module_exit(exit_addsyscall);执行rmmod指令卸载模块时调用的函数。函数原型是module_exit();模块许可声明MODULE_LICENSE("GPL");函数原型是MoDULE_LICENSE(),告诉内核程序使用的许可证,不然在加载时它会提示该模块污染内核。一般会写GPL。2.3编写用户态的测试程序#in clude <li nux/uni std.h>#
21、in clude VSySCaIl.h>asmli nkage#in clude <systypes.h>#i nclude <stdio.h>StrUCt PrOCeSSint pid;int depth;StrUCt ProCeSS a512;int mai n()int i,j;SyS_mycall()中。通/在内核中将223本来对应的系统调用,临时链到我们自定义的 过该系统调用后获得数组aPrin tf("the result is:%dn",syscall(223, &a);for(i = 0; i < 512; i+)
22、for(j = 0; j V ai.depth; j+)Prin tf("-");Prin tf("%dn",ai.pid); if(ai+1.pid = 0)break;return 0;2.4编写Makefile文件KVERS = $(shell Un ame -r)# Kernel modulesobj-m += hello.o# SPeCify flags for the module compilati on.#EXTRA_CFLAGS=-g -OObuild: kern el_modules USer_testkern el_modules:
23、make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modulesUSer_test:gcc -o hello_test hello_test.cclea n:make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clea n三所遇到的问题及解决的方法3.1进程个数确定系统可运行的最大进程数,通过Ulimit - U查看有7863个rootlinux-Vtrtual-Piachtne:usricludei386-Iinux-guasnff IJIiFIit u 7863我们通过PS - efwc - l命令实
24、际查看当前运行进程数量为191个rootliu-Virtualnachtne:usrtcude 1386*Iinux-guas PS *efc -I 191存储进程信息的数组大小为512是够用的。3.2被更改的系统调用号的选择见 2.1.1。3.3获取系统调用表的地址见 2.1.2。3.4内核和用户态数据交换我们在内核模块程序中,将进程遍历信息存储在数组中,然后需要将其传递给用户态下。采用copy_from_user()和copy_to_user()这两个函数,这两个函数负责在用户空间和内核空间传递数据。因此我们在测试程序中,将空数组a的地址作为参数传递给内核模块程序,在内核中使用copy_t
25、o_user()函数将内核中的数组信息传递给用户态下的地址。四.程序运行结果及使用说明4.1将编译出来的内核模块hello.ko加载到内核中加载内核模块命令:in smodhello.koroot®ItnUX-VtrtUal-machine:h!elluxUixl.tJ3,i-2 Insmod hell.o + ko roctlinux - VirtUaX rIJaChine; o,eltuxUniX/1LnUX li 2fl ISrlOd headModuleSLZeUsed Ihello16743SndcrlSI3712A5A72bnd_d<.97_cadetID57091
26、Snd-tfS1371ac97-bs126421snd.ac97_COdeCgaeport151S91sdes1371sndc3 5501nddt_97 codec J J e13715nd_Page_alloc142331snd,pen12967Ut1«P1&S9524.2通过dmesg查看输出信息是否正确rootltux-vtrtual-nachte:h01e/Linux/UriiX/linux_ll_2# dmesg tail1974.23S391 task: e6O676 ti: e6256 task.ti: e6250GOL8O74.23352548074÷
27、;Z389Z;1H74.7S96;“B的 4 23999rlfi74.23903'1874,2399' ;2fl303.625174 2<3309.55936EIP; 0073;<b752d886> EFLGS; ODOIOZe6 CPU; O EIP Is t b7>2d836FAX: bfb4ef77 -BX: b76Sd0f)f) E£I: b63646S EDI: bfb4cf77FCX : OaOOf)fi FrJX :EBP: O&OQ0O1-4 ESPi63mb6c6aldcOO7b ES;07b FS; COoeGS:
28、0033 SSI 007bCall yinyu pit hcllo>yi.nyu kernel20309.955930 Cl5el4O4.3运行测试程序,输出树状打印结果(部分结果截图)root®ILnUX-Vrtual-nachme:/home/nuxLJntX/lInUX_Ii_2# helo-testth* result is:405& lI-II -324I-II 323I-II -463ITI 469I-II -499I-I -509I-513I-I -563I-II 十578卜I 616I-II -b02I-II 769I-I -854I-I -028blI
29、 -933I-99MI -941I-I M4LdI -yaHbbl-I-234Hl-hblI -2322I-I-PldI -2340m-I *2334-I-I-I-II -2355I-I-I-I-I -237&HlJ'blI -2461IlI十!* -2468I-I-I-I-I! - I -2469-I-I-I-I- -2930-*l-PlH 十 I-;931Hl-I-I-II - I -3237I-I-I-I-I - -1-3555I-I-I-1<I-I-HMI 479I-I-I-I-卜卜卜I-II-4680I-I-I-I-Ikl-kl-lI F I -6334I-Id
30、-MI -2990'I-I-I-II -31CI - I-1018I-I-W61I-I-112I-1116I - I-12534.4卸载自定义模块卸载内核模块命令:in Smodhello.koI root linux-v Irtual-achlfetjhoellif Urititj/Ltu,Lt2tf FpIOd hellorootgli-i<j- virtualmachtre: hoe Yiux UnLxltux-lt2ffIsnodI hedMOdUleStZeLlSed bysd-es1371?4S472sndc97COdeC1057D91Snd_CnSl371ac57_
31、bu&L26421Snd_ac97_CodeCgameport151891Snd_ensl371sdp 5SSS012Sndac97cdec,SndenS1371SndPdgeaIVoc1423015ftd_PCIrlCC32-PCTFlJI12967QIbneP188952rcomn536640五附录5.1内核模块程序hello.c#in elude <li nux/in it.h>#i nclude <li nu xmodule.h>#i nclude <li nu x/kernel.h>/list_head#in elude <li nu
32、x/uni std.h>#in elude <asm/UaCCeSs.h>#in elude <li nu x/sched.h>/task_StrUCt#defi ne my_SySCall_ num 223#defi ne SyS_call_table_address 0xc165e140StatiC int coun ter = 0;StrUCt PrOCeSSint pid;int depth;;StrUCt PrOCeSS a512;Un Sig ned int clear_a nd_retur n_crO(Void);void SetbaCk_cr0 (
33、Un Sig ned int val);asmli nkage Iong SyS_mycall(char _USer *buf);int orig_cr0;Un Sig ned Iong *sys_call_table = 0;StatiC in t (*a nythin g_SaVed)(Void);创建进程树(进程,深度)void PrOCeSStree(StrUCt task_StrUCt * p,i nt b)/ StrUCt list_head * l;aco Un ter.pid = P -> pid;aco Un ter.depth = b;CoUn ter +;for(l
34、 = P -> ChiIdre n.n ext; l != & (p->childre n); l = l->n ext)StrUCt task_StrUCt *t = list_e ntry(l,struct task_StrUCt,sibli ng);PrOCeSStree(t,b+1);UnSigned int clear_and_return_crO(Void)/使 cr0 寄存器的第 17 位设置为 O (即是内核空间可写)Un Sig ned int cr0 = 0;Un Sig ned int ret;asm("movl %cr0, %eax&
35、quot;:"=a"(cr0);/将cr0寄存器的值移动到 eax寄存器中,同时输出到cr0变量中ret = cr0;cr0 &= OXfffeffff;/将 cr0 变量的第 17 位清 0asm("movl %eax, %cr0":"a"(cr0);/将cr0变量的值放入寄存器eax中,并且放入cr0寄存器中return ret;void SetbaCk_cr0(unsigned int val)/读取val的值到eax寄存器,再将 eax寄存器的值放入cr0寄存器中-改变内核地址空间参数asm volatile(&quo
36、t;movl %eax, %cr0":"a"(val);StatiC int _in it ini t_addsyscall(Void)/保存原来系统调用表中此地址中的系统调用Prin tk("hello, yinyu kern eln");SyS_call_table= (Un Sig nedIong *)sys_caILtable_address;获取系统调用服务首地址Prin tk("%xn",sys_call_table);any thi ng_saved = (in t(*)(void) (SyS_call_tab
37、lemy_SySCall_ num);/保存系统调用表中的NUM位置上的系统调用orig_cr0 = clear_a nd_retur n_cr0();使内核地址空间可写sys_caILtablemy_syscall_ num= (Un Sig ned Iong)& sys_mycall;用自己的系统调用替换NUM位置上的系统调用SetbaCk_crO(orig_crO);使内核地址空间不可写return 0;asmli nkage Iong SyS_mycall(char _USer * buf)int b = 0;StrUCt task_StrUCt * p;Prin tk(&qu
38、ot;This is yinyu _SySCall!n");for(p = CUrre nt; P != &ini t_task; P = p->pare nt );PrOCeSStree(P,b);if(copy_to_USer(StrUCt PrOCeSS *)buf,a,512*sizeof(struct process)/将内核空间内容复制到用户空间return -EFAULT;elseretur n SiZeOf(a);StatiC void _exit exit_addsyscall(void)/设置cr0中对SyS_call_table 的更改权限。orig_cr0 = clear_a nd_retur n_crO
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 生态保护与书画艺术创作考核试卷
- 艺术品市场规范考核试卷
- 航班机组人员沟通技巧考核试卷
- 花卉画法的分类与特点考核试卷
- 一次函数应用举例教学课件
- 共建文明社区共享和谐生活:课件教程
- 中国古代教育长善救失
- 2019-2025年咨询工程师之工程项目组织与管理能力提升试卷B卷附答案
- 2025年投资项目管理师之投资建设项目决策真题练习试卷A卷附答案
- 扈中平现代教育改革理论与实践
- 创造心智与创新训练智慧树知到期末考试答案2024年
- 创伤性前房积血
- 供水企业安全生产培训课件
- 2024年《大学语文》期末考试复习题库(含答案)
- 早产的护理查房课件
- 国家智慧教育平台培训课件
- 针灸科出科个人小结
- 语感与语言习得-【中职专用】高一语文同步课件(高教版2023·基础模块上册)
- 2024年中国石化集团资本有限公司招聘笔试参考题库含答案解析
- 普通高中地理课程标准(2023年版)
- 检验批划分方案14
评论
0/150
提交评论