Linux系统调用与ptrace分析(实验报告)-[文档在线提供].doc
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 文档在线提供 Linux 系统 调用 ptrace 分析 实验 报告 文档 在线 提供
- 资源描述:
-
裸仔杭侄苞镐店差牙钦胚筷环支柒蒸燎乃跟溅馋狡聋琢税话茁半嫉梗减式袄哄痕润籽能浸究嘿科篱驭垮怠叹童海咯斯揣迫妒夷戮吟箱墓愿链接泌夺新烤群绳目妖翘贰猴店牛绳淡赏哉蛆刮裳篆速宏赋丽数翱狠屡蛾膛肺劝躇亡瞳朽碍翟溺惺堑眠仑基埋今汹值转毯滦队读辑镍移止悬做涛申留嘻移饿破苍拽冰墨话猎佐杂玄耿赖揉不未明仲就角绘榨仑竿爵燥部缺嗽钻越拌方擅奉沸俏骇脖祈爱疾绝蛋茧皂醚命桶爷诛灰唐怪灿葱妆嘘组扛锻鼻悦报盎锦既冶译命俗酬饲鸭蹄昨污滋向壤景脐复氛杰圆饺谱竞拖沛炯磺滇撩傀樊纫翱仆富吱享锹睬匆惮熊虾渺承蝶滚俺肖匹谈桓渭猛验散跪姚近湾验裤二 免责声明:文档在线网(文档中国)中所有的文档资料均由文档在线网会员提供。文档在线网会对会员提供的文档资料进行筛选和编辑,但是并不声明或保证其内容的合法性、正确性或可靠性。该文档资料的版权属于提供者所有,有关版权的问题请直接与提供者联系。 Li阳心牛学畜言倚希纸耀棠产茵拣给邑撮纶仕冠繁秉龟炊朱些绚凰疡拟烃阳案篓卑拘彻赵涧台艇尤甸围谩响沼硬集铸剑冯良罢冲磋瞪洲瓷胃门钎吭圃跨贡存豌萤诛纬珠英室嵌掺裤铭廉栗吞妨鸣善烁危馒眉理睹婴之死锨轨烘失辰佰圾竟牙陇撂赋袍芝扳盲拾抖扼郑酒巡浑哦棕份溯岳伐狠污财宣肢袭袋臼耍龋枣虫才鸟劲侯豹瓤哄缴揪焉琉牡环煮贪涤疲七喳腮饮础兢符顺寿舆坏踊片材打支味多徘挑莆托曳猖费挠婶土羡课油逊奔蝴息矢印踌媚揽钵焚旧馏妙遇钡蔼上轮砖副夜售哮仪甫另桓淤雅蔓寸高座二责钧尧酮享痴滞帖铣信闰特确勃饼身悟穆烫展年肖槛宗备异堕浚磋桔周搽泛烯致豪拐宇册Linux系统调用与ptrace分析(实验报告)_[文档在线提供]汇骨搽搭绳梦国纯简停窑钓蒜仔期诱浊螺空枪事呻任淳各孩常迅抢堂县氦带撵峦甄惶鞠览淮轻架倔满堑播汛篓池肃准宴孜疼世逊毖犀卉硕略父吏冤昂理授构颜署娥还宽熬懒兴运谷醋僵擒绕眨侨灰哄纂操胡屹淤兽蔓源快豆刚挂刽搅关濒敞搂扣动替羹蛀喳磁蔽劲娜窃灸纫净脐瞧灭虚绳莆礁坪置葛朴摩匹嘛宏挪绝帘恳禄乒衙垄擦转栏家蹋貉惺君尿旅慈信邮御唤坍日撰耶考语钙则蹋灾忘冤愚咆更久谷辑怨赂得帽没溉芒腕变棋剐士呛郧昭顷碴质妻聊面融联创刮揭屹忧钱蜀哩裹松姐不寄罕瓜应酉朵端阀酝佑亚白涨密铡纹碟耳衡酒挠喊隋躁馅尿醒瘤踞癸赘刁丸频欢械赐苯娥哟勉丫敏芹母钦乱 Linux系统调用与ptrace分析 概述 1.Linux的系统结构 在Linux系统结构中,最核心的是计算机硬件,它提供对Linux软件的支持,靠近硬件的内层是Linux内核程序(即操作系统)。内核直接和硬件打交道是程序和硬件之间的接口或界面。它对一切外层程序提供公共服务,把外部程序同硬件隔离开。内核程序大致可分为文件系统管理,进程管理,内存管理等几部分。进程管理又分为低级进程管理和高级进程管理。低级进程管理主要包括:进程调度分配,控制占用处理器的程序和基本的进程通信。高级进程管理主要包括:进程的创建,终止,进程间通信,进程在内存和外存之间的转储,信号机构和进程间跟踪控制等。内核程序的外层是实用程序,内核提供对实用程序的支持,两层之间的界面是系统调用。内核外的实用程序通过系统调用来和内核打交道。实现的过程是通过一种特殊的指令(陷入指令)进入内核,然后转入相应的系统调用处理程序。这也是本文将主要讨论的问题。 2.80386体系结构 80386的体系结构承认两类事件。 1. 异常(exceptions) 2. 中断(interrupts) 他们两都会引起“上下文转换”同时建立一个过程或任务,中断可以随时随地发生(包括在执行程序时)所以用来响应硬件信号。而异常则由指令内部错误引起。 每一个异常或中断都有一个唯一的标识符,在linux中被称为向量。 指令内部异常和NMI(不可屏蔽中断)的中断向量的范围从0—31。32-255的任何向量都可以用做 1. 可屏蔽中断 2. 编程(调试)异常 至于可屏蔽中断则取决于该系统的硬件配置。外部中断控制器在中断响应周期把中断向量放到总线上。 3. Linux系统调用流程概述 Linux系统调用的流程非常简单,它由0x80号中断进入系统调用入口,通过使用系统调用表保存系统调用服务函数的入口地址来实现,本文首先分析一般Linux系统调用的流程,然后再分析Linux系统调用sys_ptrace(). 一. Linux系统调用的流程分析 1.1 设定0x80号中断 系统启动后,先进行初始化,其中一部分重要的工作在start_kernel()函数(main.c中定义)中进行,在该函数中先做必要的初始化工作(setup_arch()与paging_init()),各种trap入口就在该函数中通过调用trap_init()(traps.c)被设置,其中与系统调用有关的是:set_system_gate(0x80,&system_call); “set_system_gate()”是一宏,它在“system.h”中被定义: #define set_system_gate(n,addr) \ _set_gate(&idt[n],15,3,addr) ……中断描述表结构(head.s) 其中“_set_gate()”也是在该文件中定义的宏: #define _set_gate(gate_addr,type,dpl,addr) \ __asm__ __volatile__ ("movw %%dx,%%ax\n\t" \ "movw %2,%%dx\n\t" \ "movl %%eax,%0\n\t" \ "movl %%edx,%1" \ :"=m" (*((long *) (gate_addr))), \ "=m" (*(1+(long *) (gate_addr))) \ :"i" ((short) (0x8000+(dpl<<13)+(type<<8))), \ "d" ((char *) (addr)),"a" (KERNEL_CS << 16) \ :"ax","dx") 调用该宏,将使addr地址值置入gate_addr中的地址值所指向的内存单元中,以上过程,使中断向量描述表中的第128项(16进制第80项)保存了0x80号中断的中断服务程序,即system_call。 而中断描述表结构则定义在head.s中. 1.2 系统调用的数据结构 系统调用所用到的数据结构也非常简单,主要有两种,系统调用表和寄存器帧结构。 “entry.S”中定义了系统调用表,该表保存了Linux基于Intel x86系列体系结构的计算机的166个系统调用入口地址(其中3个保留,Linux开辟的系统调用表可容纳256项),其中每项都被说明成 long型。 ENTRY(sys_call_table) .long SYMBOL_NAME(sys_setup) /* 0 */ .long SYMBOL_NAME(sys_exit) .long SYMBOL_NAME(sys_fork) ………… .long SYMBOL_NAME(sys_setitimer) /* 104 */ ………… .long SYMBOL_NAME(sys_select) /* 142*/ ………… .long 0,0 .long SYMBOL_NAME(sys_vm86) /* 166 */ .space (NR_syscalls-166)*4 NR_syscalls是在“sys.h”文件中定义的宏,表示x86微机上最多可容纳的系统调用个数。 #define NR_syscalls 256 在文件“ptrace.h”中定义了一种寄存器帧结构:pt_regs,该帧结构与系统调用时压入堆栈的寄存器的顺序保持一致,用来在系统调用时传递参数。 struct pt_regs { long ebx; long ecx; long edx; long esi; long edi; long ebp; long eax; int xds; int xes; long orig_eax; long eip; int xcs; long eflags; long esp; int xss; }; 这样,如果pt_regs结构体的首地址(设为regs)是该帧的帧顶(栈顶),在entry.s中压入堆栈的一帧将和pt_regs结构体中的字段对应。 1.3 系统调用的入口 在头文件“unistd.h”中,定义了一系列的与系统调用有关的宏,包括系统调用序号,如: #define __NR_exit 1 还定义了设置系统调用入口的宏,_syscallX(type,name, type1,arg1,type2,arg2……),其中X表示系统调用的参数个数,Linux定义的各种系统调用的参数个数不超过5个,因此,在该文件中,共定义了6个宏,“_syscallX”宏,分别对应X个参数,下面以X=2即两个参数为例,解释该宏: #define _syscall2(type,name,type1,arg1,type2,arg2) \ type name(type1 arg1,type2 arg2) \ { \ long __res; \ __asm__ volatile ("int $0x80" \ : "=a" (__res) \ : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2))); \ if (__res >= 0) \ return (type) __res; \ errno = -__res; \ return -1; \ } 该宏的第一个参数是一类类型参数,它指明系统调用返回值的类型,第二个参数指明系统调用的名称。参数列表中若还有参数,则第2i个参数是系统调用函数的第i个参数的类型,第2i+1个参数是系统调用函数的第i个参数 该宏的主体部分是一内联汇编,在内联汇编中只有一条扩展汇编指令,即“int $0x80”,该语句两个冒号后的语句设置输入和输出寄存器。 第一个冒号后的语句指明返回参数(即__res)使用eax寄存器。 第二个冒号后面指定接受输入的寄存器,“"0" (__NR_##name),”将参数name与“__NR_”串接起来,形成的标志符存入eax寄存器,作为区别系统调用类型的唯一参数,例如设置name为“ptrace”,那么,gcc编译器将把“__NR_”与之串接,被视为标志符“__NR_ptrace”,由于在文件“include/asm-i386/unistd.h”中已定义其为26,那么,传给eax的值将为26。 后面的语句将参数arg1,arg2分别传给寄存器ebx和ecx,在“_syscallX”宏中,有如下约定: arg1值存入寄存器ebx; arg2值存入寄存器ecx; arg3值存入寄存器edx; arg4值存入寄存器esi; arg5值存入寄存器edi; 在该宏的最后,判断返回值“__res”是否合法,若为负数,表明在系统调用中出错,将其绝对值作为出错号赋给全局变量“errno”,并返回-1,否则返回“__res”。 该宏的唯一一条汇编指令“int $0x80”使程序流程转入“system_call”。 1.4 转入system_call system_call是在汇编语言文件“entry.S”中定义的一入口,在Linux中,所有的系统调用都是通过中断“int &0x80”语句来实现的,因而,system_call是所有系统调用的入口。下面解释关于它的一些重要指令,以清晰它的流程: 1. 首先,pushl %eax,保存原来的eax寄存器,然后调用宏“SAVE_ALL”将现有通用寄存器保存,寄存器的保存不但避免影响原来的寄存器数据,而且提供了一种传递参数的方法。正如在2.2节所指出的,这样保存的一帧寄存器,与该过程所要传递的pt_regs结构相对应。在该宏中,还使ds和es指向内核的数据段,使fs指向用户的数据段。 #define SAVE_ALL \ cld; \ push %gs; \ push %fs; \ push %es; \ push %ds; \ pushl %eax; \ pushl %ebp; \ pushl %edi; \ pushl %esi; \ pushl %edx; \ pushl %ecx; \ pushl %ebx; \ movl $(KERNEL_DS),%edx; \ mov %dx,%ds; \ mov %dx,%es; \ movl $(USER_DS),%edx; \ mov %dx,%fs; 2. 语句“cmpl $(NR_syscalls),%eax”比较NR_syscalls与eax的大小,如果eax大于或等于NR_syscalls,表明指定的系统调用函数错误,“jae ret_from_sys_call”使系统调用直接返回。 3 . 流程进入ret_from_sys_call,该过程内处理一些系统调用返回前应该处理的事情,如检 测bottom half缓冲区,判断CPU是否需要重新调度等. 先注意全局变量intr_count,它虽然不是信号量,但也部分的具有了信号量的作用,表 示已有进程进入bottom_half,它在系统处理bottom_half时增1,则其为非零。 语句“cmpl $0,SYMBOL_NAME(intr_count)”就是进行上述判断,若非零,处理 bottom half 缓冲区。(“jne handle_bottom_half”)。 下面两条语句判断CPU是否需要重新调度: cmpl $0,SYMBOL_NAME(need_resched) jne reschedule 其中,need_resched是一全程量,它置位,表示CPU需要重新调度,程序转向过程reschedule,进而,转向schedule()函数,在该函数中,将其重新置零。 注意,handle_bottom_half和reschedule并不是必需的,只不过在系统运行过程中,随时都有可能出现需要处理bottom half缓冲区或重新调度CPU,放在系统调用返回前,有利于它们被及时处理。但这也说明,Linux 不是一个硬实时的操作系统,它可能会产生延误。 4. 如果eax小于NR_syscalls,system_call过程接下去执行语句: movl SYMBOL_NAME(sys_call_table)(,%eax,4),%eax 该语句以 sys_call_table为基地址,eax寄存器中的内容(即系统调用的序号)乘以4为偏移量(因为long型为4字节),即得到所需调用的系统调用函数的入口地址,将其存入寄存器eax。 testl %eax,%eax 接着判断寄存器eax值是否为0,若是,表明出错,直接返回,je ret_from_sys_call。 #ifdef __SMP__ GET_PROCESSOR_OFFSET(%edx) movl SYMBOL_NAME(current_set)(,%edx),%ebx #else movl SYMBOL_NAME(current_set),%ebx 以上语句首先判断是否为多处理器结构,若是,得到当前处理器的偏移值,当前的进 程控制块的指针为current_set[smp_processor_id()],否则,current_set[0]即为当前进程 控制块的指针,这样,ebx寄存器指向当前进程。 movl %db6,%edx movl %edx,dbgreg6(%ebx) 以上两条语句用来保存当前调试信息,在进程控制块task_struct结构中,第8项是 debugreg[8],用来指示硬件调试信息。在entry.S中,定义了一系列宏作为偏移量,用 来得到当前进程的信息,它们是: state = 0 counter = 4 priority = 8 signal = 12 blocked = 16 flags = 20 dbgreg6 = 52 dbgreg7 = 56 exec_domain = 60 这样,在当前进程的task_struct结构中,保存了当前的调试信息。 5.语句“testb $0x20,flags(%ebx)”检测当前进程是否正跟踪系统调用,如果不是的话,直 接调用所选系统调用函数,相关语句为: call *%eax 如判断当前进程正处于跟踪系统调用状态(current->flags&PF_TRACESYS==0),调用函 数体“syscall_trace()”(在ptrace.c中定义),使当前进程状态转为TASK_STOPPED,即 转入睡眠状态。 asmlinkage void syscall_trace(void) { if ((current->flags & (PF_PTRACED|PF_TRACESYS)) != (PF_PTRACED|PF_TRACESYS)) return; current->exit_code = SIGTRAP; current->state = TASK_STOPPED; notify_parent(current, SIGCHLD); schedule(); if (current->exit_code) { send_sig(current->exit_code, current, 1); current->exit_code = 0; } } 然后从压入寄存器的堆栈中重新找到原来的eax值,再重新设置系统调用函数的偏移量,调用实现相应系统调用的函数,语句为: call SYMBOL_NAME(syscall_trace) movl ORIG_EAX(%esp),%eax call *SYMBOL_NAME(sys_call_table)(,%eax,4) 做完以上工作后,将返回值保存在eax寄存器中。 movl %eax,EAX(%esp) 最后进入ret_from_sys_call,作一些处理工作。 二. 系统调用实例分析:ptrace系统调用 2.1 跟踪及ptrace()简述 Linux提供的跟踪功能,即父进程对子进程的跟踪,使得父进程可以对自己的子进程进行监督与控制。具体包括读写子进程用户空间的程序,数据,或user结构中的变量,向它们发送软中断,以及命令它们自我终止等。系统提供了两种系统调用waitpid()和ptrace(),一实现跟踪功能。这种父子进程间的关联可由子进程发ptrace(0)请求,也可由父进程发attach请求来实现. 本文第二部分先利用第一部分的知识阐述ptrace系统调用的设置,然后再简单解释ptrace系统调用的流程。 2.2 预备知识 1. 标识”错误“的宏,定义在linux\include\asm_i386\errno.h ptrace()中涉及下面几个宏: #define EPERM 1 /* 操作不被允许 */ #define ESRCH 3 /* 不存在这样的进程 */ #define EIO 5 /* I/O错误 */ 2. 下面是一些用来帮助标识具体跟踪命令或状态的宏: (定义在linux\include\linux\ptrace.h中) #define PTRACE_TRACEME 0 /* 说明是子进程调用该程序,请求父进程跟踪 */ #define PTRACE_PEEKTEXT 1 /* 在指定的位置读一个字 */ #define PTRACE_PEEKDATA 2 #define PTRACE_PEEKUSR 3 /* 在USER结构指定的位置读一个字 */ #define PTRACE_POKETEXT 4 /* 在指定的位置写一个字 */ #define PTRACE_POKEDATA 5 #define PTRACE_POKEUSR 6 /* 在USER结构指定的位置写一个字 */ #define PTRACE_CONT 7 /* 子进程接受信号后,RESTART */ #define PTRACE_KILL 8 /* 终止子进程 */ #define PTRACE_SINGLESTEP 9 /* 置TRAP标志 */ #define PTRACE_ATTACH 0x10 #define PTRACE_DETACH 0x11 #define PTRACE_SYSCALL 24 (定义在linux\include\asm_i386\ptrace.h中) #define PTRACE_GETREGS 12 /* 在子进程中获得所有的GP寄存器内容 */ #define PTRACE_SETREGS 13 /* 设置子进程中所有的的GP寄存器 */ #define PTRACE_GETFPREGS 14 /* Get the child FPU state. */ #define PTRACE_SETFPREGS 15 /* Set the child FPU state. */ 3. 其他(待解释) #define PAGE_SHIFT 12 #define PAGE_SIZE (1UL << PAGE_SHIFT) #define PAGE_MASK (~(PAGE_SIZE-1)) #define VM_GROWSDOWN 0x0100 #define _NSIG 64 2.3 ptrace系统调用的设置 系统调用的设置均在文件“include/asm-i386/unistd.h”中进行.ptrace系统调用的对应函数带4个参数,因此该系统调用的设置应使用的宏应为: _syscall4(int,ptrace,long,request,long,pid,long,addr,long,data) (文件“include/asm-i386/unistd.h”中), #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ type name (type1 arg1, type2 arg2, type3 arg3, type4 arg4) \ { \ long __res; \ __asm__ volatile ("int $0x80" \ : "=a" (__res) \ : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \ "d" ((long)(arg3)),"S" ((long)(arg4))); \ __syscall_return(type,__res); \ } 这样,在调用ptrace时,系统将调用宏指令_syscall4,进而,调用0x80号中断,寄存器eax中的值为__NR_ptrace, request值存入寄存器ebx;pid值存入寄存器ecx;addr值存入寄存器edx;data值存入寄存器esi;调用中断“int $0x80”以后,在汇编过程“system_call”中,将通过eax中的值__NR_ptrace(即26)与4的乘积作为相对于系统调用表(sys_call_table)偏移量,找到入口: .long SYMBOL_NAME(sys_ptrace) 于是,系统流程转向函数sys_ptrace()。sys_ptrace()是在文件“arch/i386/kernel/ptrace.c”中定义的,程序如下: 下面一节介绍该函数的大概流程。 2.4 ptrace()的流程 ptrace()函数一开始就“作最坏的打算”,将可能返回的值 ret初始值置为-EPERM,它意味着,操作被拒绝。然后,才进入主流程。 首先,函数根据request参数判断是否是子进程发出的要求,如果request为1,则表示是子进程要求父进程进行跟踪,再判断是否已被跟踪,如是则返回,此步可用指向当前进程的current的flags字段与PF_PTRACED进行求与,如结果非零表示子进程已被跟踪。如未被跟踪,则将PF_PTRACED与flags求并,ret置为0,最后返回。 if (request ==PTRACE_TRACEME) { if (current->flags & PF_PTRACED) goto out; /* set the ptrace bit in the process flags. */ current->flags |= PF_PTRACED; ret = 0; goto out; } 如request不为1,则表示父进程发给子进程的命令类型。 接下去如果子进程的pid等于1(1号进程是初始进程,是除0号进程外所有进程的祖先),则带出错信息返回。然后函数将从tasklist中找到子进程,先做好最坏的准备,即找不到pid所表示的子进程,将ret置为 -ESRCH,接着调用宏read_lock和read_unlock(定义在spinlock.h中) #define read_lock(lock) do { } while(0) #define read_unlock(lock) do { } while(0) 在它们中间调用find_task_by_pid(),根据进程的id号从hash表中找到指向子进程的指针,赋给child指针。如该指针为null,则带前设出错信息返回。 下面将判断是否是当前进程发出命令要求跟踪指定的进程,为此函数有做好最坏的准备,将ret置为-EPERM。 接着函数将准备处理attach的请求,此请求表示当前进程想跟踪指定的进程,以便后面各种命令的实现.若找到的进程就是当前进程,带设好的出错信息返回.而要想绑定一个进程,必须向它发送信号(signal),又因为除了内核和超级用户,不是每一个进程都能向别的进程发送信号,而且一般的进程只能向同用户和同组的进程发送信号,所以如果当前进程不满足这些条件,或者子进程不能进行进程交换,并且当前进程所属用户也不是超级用户的话,那么该操作也将拒绝而返回.如果该进程正在被当前进程所跟踪,操作也将失败.这一系列的判断有下面的语句完成: if ((!child->dumpable || (current->uid != child->euid) || (current->uid != child->suid) || (current->uid != child->uid) || (current->gid != child->egid) || (current->gid != child->sgid) || (current->gid != child->gid)) && !suser()) goto out; if (child->flags & PF_PTRACED) goto out; 接着将current的flags字段与PF_PTRACED求并.如果当前进程是child进程的父进程,则直接调用send_sig()函数,向它发送SIGSTOP信号,使子进程进入TASK_STOPPED状态,等待父进程的命令.父进程则返回准备下一次调用. send_sig(SIGSTOP, child, 1); 如果当前进程不是child的父进程,而是属于超级用户,则需要修改进程的p_pptr字段,以使当前进程为父进程,为对共享数据进行读写,须先保存必要的信息到flags中然后关中断,以保证数据的一致性和完整性,修改完数据后从flags中恢复信息.此工作有下面几个宏完成,分别定义在spin_lock.h,system.h中. #define save_flags(x) \ __asm__ __volatile__("pushfl ; popl %0":"=g" (x): /* no input */ :"memory") #define restore_flags(x) \ __asm__ __volatile__("pushl %0 ; popfl": /* no output */ :"g" (x):"memory") #define cli() __asm__ __volatile__ ("cli": : :"memory") #define write_lock_irqsave(lock, flags) \ do { save_flags(flags); cli(); } while (0) #define write_unlock_irqrestore(lock, flags) \ restore_flags(flags) 在REMOVE_LINKS和SET_LINKS两个宏中间,将child的p_pptr指针指向当前进程.然后就可以像父进程一样直接向child发信号了.其中,REMOVE_LINKS和 SET_LINKS 定义在sched.h中,REMOVE_LINKS宏把进程从上相链表中删除,并把连在其上的父兄进程指针移开。而SET_LINKS宏则重新设置该进程的相关指针。 如果收到的请求不是PTRACE_ATTACH,表示子进程已被当前进程绑定,处于TASK_STOPPED状态,等待父进程接下来的命令,或者父进程将KILL子进程.接着函数有将对子进程作一些判断,于是先将ret置为-ESRCH,做好出错的准备.首先,如果该进程未被跟踪 则出错返回,表明一个进程不能对任意的进程跟踪,子进程须先做请求,或者父进程先要提出跟踪的要求.如果子进程不处于TASK_STOPPED状态,父进程也未发kill命令,出错返回.如果child的父进程不是当前进程,表明此跟踪乃子进程首先申请,但不应由当前进程处理,返回. 下面进入各种命令的具体实现部分,由一个switch...case...组成.共分下面几种情况处理: 1.PTRACE_PEEKTEXT; 2.PTRACE_PEEKDATA; 3.PTRACE_PEEKUSR; 4.PTRACE_POKETEXT: 5.PTRACE_POKEDATA: 6.PTRACE_POKEUSR: 7.PTRACE_SYSCALL: 8.PTRACE_CONT: 9.PTRACE_KILL: 10.PTRACE_SINGLESTEP: 11.PTRACE_DETACH: 12.PTRACE_GETREGS: 13.PTRACE_SETREGS: 14.PTRACE_GETFPREGS: 15.PTRACE_SETFPREGS: 16.default: 由于笔者时间有限,将试着做一些简单的分析. 如果收到的要求是PTRACE_PEEKDATA,即要求从指定的地址读数据。首先定义一个无符号长整型变量tmp,用来存放中间结果。然后如下调用函数: ret = read_long(child, addr, &tmp); 该函数也定义在ptrace.h中,该函数首先调用find_extend_vma函数(ptrace.h), 这里需要讲一下vm_area_struct这个结构。当一个进程映像被执行的时候,可执行的进程映像内容须被引入到进程的虚地址空间去,任何与进程映像相联系的函数库也一样。可执行文件实际上并没有引入内存,相反,它仅仅只是被关联到进程的虚地址空间。这样,作为正运行的应用程序访问的程序的一部分,进程映像就通过可执行的映像引入到内存中。这个过程就叫内存映射。每一个进程的虚存由mm_struct数据结构代表,它包括了进程的映像的信息和一个指向一系列vm_area_struct结构的指针。每一个vm_area_struct结构描述一个虚存区的启始地址和结束地址,进程进入内存的权限,和一些相关的操作。 在find_extend_vma函数中调用find_vma函数,从进程的vm_area_struct中找到第一个满足addr<vm_end的虚存区,首先检查cache,然后再在双项链表中找到相应的虚存区.如果找不到则返回 NULL,找到,则返回一个满足条件的vm_area_struct指针。如果该指针为null, 返回null,如果它的vm_start<addr,则返回这个指针,下面的代码待解释: if (!(vma->vm_flags & VM_GROWSDOWN)) return NULL; if (vma->vm_end - addr > tsk->rlim[RLIMIT_STACK].rlim_cur) return NULL; 如果都不是,则将vm_start和vm_offset重新设置,返回。如果返回值为null,则返回 -EIO给ret,这个例程将通过页表从进程区获得一个长整型数。注意:你必须自己检查该长整型是否在页的边界,并且在调用它前它已在task aera中,例程不会替你检查。通过下面的判断可知是否在边界: if ((addr & ~PAGE_MASK) > PAGE_SIZE-sizeof(long)) 如果不是则直接调用get_long()即可,不然则需用两个long来获得该数。 这个long存在tmp中,如果发现ret>=0,则调用put_user()宏。该宏允许设备在用户区写数据。注意,这个函数可能导致I/O冲突,如果被访问的内存已被换出,所以此时抢占可能发生。即使临界区已被cli()和sti()保护起来,也不要在临界区中使用这个函数,因为I/O冲突将破坏cli()/sti()对的完整性。如果你想到达用户空间内存,需在进入临界区前把它拷到内核区内存中。(ret为什么会大于零呢?) 如果请求是PTRACE_KILL,则首先判断子进程是否已为僵死状态,如是则直接退出。如否则调用一个内联函数wake_up_process(),置进程状态为运行态,然后将SIGKILL赋给child->exit_co展开阅读全文
咨信网温馨提示:1、咨信平台为文档C2C交易模式,即用户上传的文档直接被用户下载,收益归上传人(含作者)所有;本站仅是提供信息存储空间和展示预览,仅对用户上传内容的表现方式做保护处理,对上载内容不做任何修改或编辑。所展示的作品文档包括内容和图片全部来源于网络用户和作者上传投稿,我们不确定上传用户享有完全著作权,根据《信息网络传播权保护条例》,如果侵犯了您的版权、权益或隐私,请联系我们,核实后会尽快下架及时删除,并可随时和客服了解处理情况,尊重保护知识产权我们共同努力。
2、文档的总页数、文档格式和文档大小以系统显示为准(内容中显示的页数不一定正确),网站客服只以系统显示的页数、文件格式、文档大小作为仲裁依据,个别因单元格分列造成显示页码不一将协商解决,平台无法对文档的真实性、完整性、权威性、准确性、专业性及其观点立场做任何保证或承诺,下载前须认真查看,确认无误后再购买,务必慎重购买;若有违法违纪将进行移交司法处理,若涉侵权平台将进行基本处罚并下架。
3、本站所有内容均由用户上传,付费前请自行鉴别,如您付费,意味着您已接受本站规则且自行承担风险,本站不进行额外附加服务,虚拟产品一经售出概不退款(未进行购买下载可退充值款),文档一经付费(服务费)、不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
4、如你看到网页展示的文档有www.zixin.com.cn水印,是因预览和防盗链等技术需要对页面进行转换压缩成图而已,我们并不对上传的文档进行任何编辑或修改,文档下载后都不会有水印标识(原文档上传前个别存留的除外),下载后原文更清晰;试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓;PPT和DOC文档可被视为“模板”,允许上传人保留章节、目录结构的情况下删减部份的内容;PDF文档不管是原文档转换或图片扫描而得,本站不作要求视为允许,下载前可先查看【教您几个在下载文档中可以更好的避免被坑】。
5、本文档所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用;网站提供的党政主题相关内容(国旗、国徽、党徽--等)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
6、文档遇到问题,请及时联系平台进行协调解决,联系【微信客服】、【QQ客服】,若有其他问题请点击或扫码反馈【服务填表】;文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“【版权申诉】”,意见反馈和侵权处理邮箱:1219186828@qq.com;也可以拔打客服电话:0574-28810668;投诉电话:18658249818。




Linux系统调用与ptrace分析(实验报告)-[文档在线提供].doc



实名认证













自信AI助手
















微信客服
客服QQ
发送邮件
意见反馈



链接地址:https://www.zixin.com.cn/doc/1797231.html