ptrace作为应用程序调试的基石,要想对其有深入的了解,最好的方法是分析它的源代码。选取linux2.6.8,更高版本的内容基本相同。实现ptrace系统调用功能的主要是sys_ptrace函数,当然还包括一些读写寄存器的辅助函数。该函数的基本结构比较简单:

(1)判断该进程是否被跟踪,即request==PTRACE_TRACEME,如果是,对其进行处理。

(2)根据被跟踪子进程的pid找到其task结构体

(3)判断是否为init进程(pid==1)或者是自身进程current,init进程是计算机上电启动后执行的第一个进程,也是所有进程的父进程,它不能被跟踪。

(4)如果request==PTRACE_ATTACH,则将父进程附着在子进程上,并检查是否扶着成功。该命令实现的功能是父进程监视一个已经在运行的子进程。

(5)上述步骤完成后,就可以根据request的命令对子进程进行各种操作。

该函数有个关键词asmlinkage是指明该函数用堆栈来传递参数。是汇编程序向相应的C语言程序传递参数的一种方式。其源代码如下:(linux/arch/i386/kernel/ptrace.c)

233 asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
234 {
235         struct task_struct *child;
236         struct user * dummy = NULL;
237         int i, ret;
238         unsigned long __user *datap = (unsigned long __user *)data;
239
240         lock_kernel();
241         ret = -EPERM;
242         if (request == PTRACE_TRACEME) {
243                 /* are we already being traced? */
244                 if (current->ptrace & PT_PTRACED)
245                         goto out;
246                 ret = security_ptrace(current->parent, current);
247                 if (ret)
248                         goto out;
249                 /* set the ptrace bit in the process flags. */
250                 current->ptrace |= PT_PTRACED;
251                 ret = 0;
252                 goto out;
253         }
254         ret = -ESRCH;
255         read_lock(&tasklist_lock);
256         child = find_task_by_pid(pid);
257         if (child)
258                 get_task_struct(child);
259         read_unlock(&tasklist_lock);
260         if (!child)
261                 goto out;
262
263         ret = -EPERM;
264         if (pid == 1)           /* you may not mess with init */
265                 goto out_tsk;
266
267         if (request == PTRACE_ATTACH) {
268                 ret = ptrace_attach(child);
269                 goto out_tsk;
270         }
271
272         ret = ptrace_check_attach(child, request == PTRACE_KILL);
273         if (ret < 0)
274                 goto out_tsk;
275
276         switch (request) {
277         /* when I and D space are separate, these will need to be fixed. */
278         case PTRACE_PEEKTEXT: /* read word at location addr. */
279         case PTRACE_PEEKDATA: {
280                 unsigned long tmp;
281                 int copied;
282
283                 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
284                 ret = -EIO;
285                 if (copied != sizeof(tmp))
286                         break;
287                 ret = put_user(tmp, datap);
288                 break;
289         }
290
291         /* read the word at location addr in the USER area. */
292         case PTRACE_PEEKUSR: {
293                 unsigned long tmp;
294
295                 ret = -EIO;
296                 if ((addr & 3) || addr < 0 ||
297                     addr > sizeof(struct user) - 3)
298                         break;
299
300                 tmp = 0;  /* Default return condition */
301                 if(addr < FRAME_SIZE*sizeof(long))
302                         tmp = getreg(child, addr);
303                 if(addr >= (long) &dummy->u_debugreg[0] &&
304                    addr <= (long) &dummy->u_debugreg[7]){
305                         addr -= (long) &dummy->u_debugreg[0];
306                         addr = addr >> 2;
307                         tmp = child->thread.debugreg[addr];
308                 }
309                 ret = put_user(tmp, datap);
310                 break;
311         }
312
313         /* when I and D space are separate, this will have to be fixed. */
314         case PTRACE_POKETEXT: /* write the word at location addr. */
315         case PTRACE_POKEDATA:
316                 ret = 0;
317                 if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
318                         break;
319                 ret = -EIO;
320                 break;
321
322         case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
323                 ret = -EIO;
324                 if ((addr & 3) || addr < 0 ||
325                     addr > sizeof(struct user) - 3)
326                         break;
327
328                 if (addr < FRAME_SIZE*sizeof(long)) {
329                         ret = putreg(child, addr, data);
330                         break;
331                 }
332                 /* We need to be very careful here.  We implicitly
333                    want to modify a portion of the task_struct, and we
334                    have to be selective about what portions we allow someone
335                    to modify. */
336
337                   ret = -EIO;
338                   if(addr >= (long) &dummy->u_debugreg[0] &&
339                      addr <= (long) &dummy->u_debugreg[7]){
340
341                           if(addr == (long) &dummy->u_debugreg[4]) break;
342                           if(addr == (long) &dummy->u_debugreg[5]) break;
343                           if(addr < (long) &dummy->u_debugreg[4] &&
344                              ((unsigned long) data) >= TASK_SIZE-3) break;
345
346                           if(addr == (long) &dummy->u_debugreg[7]) {
347                                   data &= ~DR_CONTROL_RESERVED;
348                                   for(i=0; i<4; i++)
349                                           if ((0x5f54 >> ((data >> (16 + 4*i)) & 0xf)) & 1)
350                                                   goto out_tsk;
351                           }
352
353                           addr -= (long) &dummy->u_debugreg;
354                           addr = addr >> 2;
355                           child->thread.debugreg[addr] = data;
356                           ret = 0;
357                   }
358                   break;
359
360         case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
361         case PTRACE_CONT: { /* restart after signal. */
362                 long tmp;
363
364                 ret = -EIO;
365                 if ((unsigned long) data > _NSIG)
366                         break;
367                 if (request == PTRACE_SYSCALL) {
368                         set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
369                 }
370                 else {
371                         clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
372                 }
373                 child->exit_code = data;
374         /* make sure the single step bit is not set. */
375                 tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
376                 put_stack_long(child, EFL_OFFSET,tmp);
377                 wake_up_process(child);
378                 ret = 0;
379                 break;
380         }
381
382 /*
383  * make the child exit.  Best I can do is send it a sigkill.
384  * perhaps it should be put in the status that it wants to
385  * exit.
386  */
387         case PTRACE_KILL: {
388                 long tmp;
389
390                 ret = 0;
391                 if (child->state == TASK_ZOMBIE)        /* already dead */
392                         break;
393                 child->exit_code = SIGKILL;
394                 /* make sure the single step bit is not set. */
395                 tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
396                 put_stack_long(child, EFL_OFFSET, tmp);
397                 wake_up_process(child);
398                 break;
399         }
400
401         case PTRACE_SINGLESTEP: {  /* set the trap flag. */
402                 long tmp;
403
404                 ret = -EIO;
405                 if ((unsigned long) data > _NSIG)
406                         break;
407                 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
408                 if ((child->ptrace & PT_DTRACE) == 0) {
409                         /* Spurious delayed TF traps may occur */
410                         child->ptrace |= PT_DTRACE;
411                 }
412                 tmp = get_stack_long(child, EFL_OFFSET) | TRAP_FLAG;
413                 put_stack_long(child, EFL_OFFSET, tmp);
414                 child->exit_code = data;
415                 /* give it a chance to run. */
416                 wake_up_process(child);
417                 ret = 0;
418                 break;
419         }
420
421         case PTRACE_DETACH:
422                 /* detach a process that was attached. */
423                 ret = ptrace_detach(child, data);
424                 break;
425
426         case PTRACE_GETREGS: { /* Get all gp regs from the child. */
427                 if (!access_ok(VERIFY_WRITE, datap, FRAME_SIZE*sizeof(long))) {
428                         ret = -EIO;
429                         break;
430                 }
431                 for ( i = 0; i < FRAME_SIZE*sizeof(long); i += sizeof(long) ) {
432                         __put_user(getreg(child, i), datap);
433                         datap++;
434                 }
435                 ret = 0;
436                 break;
437         }
438
439         case PTRACE_SETREGS: { /* Set all gp regs in the child. */
440                 unsigned long tmp;
441                 if (!access_ok(VERIFY_READ, datap, FRAME_SIZE*sizeof(long))) {
442                         ret = -EIO;
443                         break;
444                 }
445                 for ( i = 0; i < FRAME_SIZE*sizeof(long); i += sizeof(long) ) {
446                         __get_user(tmp, datap);
447                         putreg(child, i, tmp);
448                         datap++;
449                 }
450                 ret = 0;
451                 break;
452         }
453
454         case PTRACE_GETFPREGS: { /* Get the child FPU state. */
455                 if (!access_ok(VERIFY_WRITE, datap,
456                                sizeof(struct user_i387_struct))) {
457                         ret = -EIO;
458                         break;
459                 }
460                 ret = 0;
461                 if (!child->used_math)
462                         init_fpu(child);
463                 get_fpregs((struct user_i387_struct __user *)data, child);
464                 break;
465         }
466
467         case PTRACE_SETFPREGS: { /* Set the child FPU state. */
468                 if (!access_ok(VERIFY_READ, datap,
469                                sizeof(struct user_i387_struct))) {
470                         ret = -EIO;
471                         break;
472                 }
473                 child->used_math = 1;
474                 set_fpregs(child, (struct user_i387_struct __user *)data);
475                 ret = 0;
476                 break;
477         }
478
479         case PTRACE_GETFPXREGS: { /* Get the child extended FPU state. */
480                 if (!access_ok(VERIFY_WRITE, datap,
481                                sizeof(struct user_fxsr_struct))) {
482                         ret = -EIO;
483                         break;
484                 }
485                 if (!child->used_math)
486                         init_fpu(child);
487                 ret = get_fpxregs((struct user_fxsr_struct __user *)data, child);
488                 break;
489         }
490
491         case PTRACE_SETFPXREGS: { /* Set the child extended FPU state. */
492                 if (!access_ok(VERIFY_READ, datap,
493                                sizeof(struct user_fxsr_struct))) {
494                         ret = -EIO;
495                         break;
496                 }
497                 child->used_math = 1;
498                 ret = set_fpxregs(child, (struct user_fxsr_struct __user *)data);
499                 break;
500         }
501
502         case PTRACE_GET_THREAD_AREA:
503                 ret = ptrace_get_thread_area(child, addr,
504                                         (struct user_desc __user *) data);
505                 break;
506
507         case PTRACE_SET_THREAD_AREA:
508                 ret = ptrace_set_thread_area(child, addr,
509                                         (struct user_desc __user *) data);
510                 break;
511
512         default:
513                 ret = ptrace_request(child, request, addr, data);
514                 break;
515         }
516 out_tsk:
517         put_task_struct(child);
518 out:
519         unlock_kernel();
520         return ret;
521 }

主要分析一下PEEKUSER命令实现的部分:其他的requset命令实现类似。

292         case PTRACE_PEEKUSR: {
293                 unsigned long tmp;
294
295                 ret = -EIO;
296                 if ((addr & 3) || addr < 0 ||
297                     addr > sizeof(struct user) - 3)
298                         break;
299
300                 tmp = 0;  /* Default return condition */
301                 if(addr < FRAME_SIZE*sizeof(long))
302                         tmp = getreg(child, addr);
303                 if(addr >= (long) &dummy->u_debugreg[0] &&
304                    addr <= (long) &dummy->u_debugreg[7]){
305                         addr -= (long) &dummy->u_debugreg[0];
306                         addr = addr >> 2;
307                         tmp = child->thread.debugreg[addr];
308                 }
309                 ret = put_user(tmp, datap);
310                 break;
311         }

PEEKUSER实现的功能是读取用户user的寄存器值包括调试寄存器的值。第296行判断地址是否对齐,越界,合法。第301行宏定义FRAME_SIZE=17,是通用寄存器的个数。它们分别是EBX、ECX、EDX、ESI、EDI、EBP、EAX、DS,  ES、FS、GS、ORIG_EAX、EIP、CS、EFLAGS、ESP、SS。用getreg来读取这些寄存器的值.getreg函数原型如下:

114 static unsigned long getreg(struct task_struct *child,
115         unsigned long regno)
116 {
117         unsigned long retval = ~0UL;
118
119         switch (regno >> 2) {
120                 case FS:
121                         retval = child->thread.fs;
122                         break;
123                 case GS:
124                         retval = child->thread.gs;
125                         break;
126                 case DS:
127                 case ES:
128                 case SS:
129                 case CS:
130                         retval = 0xffff;
131                         /* fall through */
132                 default:
133                         if (regno > GS*4)
134                                 regno -= 2*4;
135                         regno = regno - sizeof(struct pt_regs);
136                         retval &= get_stack_long(child, regno);
137         }
138         return retval;
139 }
140 
函数中的形参regno表示寄存器的编号,在该文件中

linux/include/asm-i386/ptrace.h定义

 4 #define EBX 05 #define ECX 16 #define EDX 27 #define ESI 38 #define EDI 49 #define EBP 510 #define EAX 611 #define DS 712 #define ES 813 #define FS 914 #define GS 1015 #define ORIG_EAX 1116 #define EIP 1217 #define CS  1318 #define EFL 1419 #define UESP 1520 #define SS   1621 #define FRAME_SIZE 17
     进程结构体TSS中存有所有寄存器的值,但在子进程被调试时处于核心态,不能够直接读取寄存器的值,所以getreg只能读取用户堆栈中的寄存器的值。不过fs,gs寄存器的值需要从TSS中读取。ds,ss,cs es均为16位,故高16位值不管。
     第136行即利用get_stack_long函数从堆栈中读出其他寄存器的值。该函数定义如下:
51 static inline int get_stack_long(struct task_struct *task, int offset)52 {53         unsigned char *stack;54 55         stack = (unsigned char *)task->thread.esp0;56         stack += offset;57         return (*((int *)stack));58 }

esp0是堆栈指针,通用的寄存器在堆栈中按顺序排放,通过偏移量0ffset便可以依次读取。第303行到308行是读取调试寄存器的值。

因此,总的来说,ptrace系统调用最主要的是核心函数是sys_ptarce函数,并在该函数中调用了寄存器的辅助读写函数,内存辅助读写函数,通过传入各种request命令,实现了强大的调试功能。

ptrace源代码分析相关推荐

  1. 《LINUX3.0内核源代码分析》第二章:中断和异常 【转】

    转自:http://blog.chinaunix.net/uid-25845340-id-2982887.html 摘要:第二章主要讲述linux如何处理ARM cortex A9多核处理器的中断.异 ...

  2. Linux内核源代码分析-目录

    第一部分 Linux 内核源代码 arch/i386/kernel/entry.S 2 arch/i386/kernel/init_task.c 8 arch/i386/kernel/irq.c 8 ...

  3. LSM内核源代码分析与测试(二)

    LSM内核相关源代码分析见:http://blog.csdn.net/lwyeluo/article/details/55215686 本文修改内核代码来测试自定义的安全模块 测试 操作系统ubunt ...

  4. Android系统默认Home应用程序(Launcher)的启动过程源代码分析

    在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还需要有一个Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home应 ...

  5. 《LINUX3.0内核源代码分析》第一章:内存寻址

    https://blog.csdn.net/ekenlinbing/article/details/7613334 摘要:本章主要介绍了LINUX3.0内存寻址方面的内容,重点对follow_page ...

  6. Scrapy源代码分析-经常使用的爬虫类-CrawlSpider(三)

    CrawlSpider classscrapy.contrib.spiders.CrawlSpider 爬取一般站点经常使用的spider.其定义了一些规则(rule)来提供跟进link的方便的机制. ...

  7. Android 中View的绘制机制源代码分析 三

    到眼下为止,measure过程已经解说完了,今天開始我们就来学习layout过程.只是在学习layout过程之前.大家有没有发现我换了编辑器,哈哈.最终下定决心从Html编辑器切换为markdown编 ...

  8. Android应用程序进程启动过程的源代码分析(1)

    Android应用程序框架层创建的应用程序进程具有两个特点,一是进程的入口函数是ActivityThread.main,二是进程天然支持Binder进程间通信机制:这两个特点都是在进程的初始化过程中实 ...

  9. AFNetworking 源代码分析

    关于其他 AFNetworking 源代码分析的其他文章: AFNetworking 概述(一) AFNetworking 的核心 AFURLSessionManager(二) 处理请求和响应 AFU ...

最新文章

  1. OKR怎么使用比较好?
  2. Spring Web MVC 随笔
  3. HDU - 4300 Clairewd’s message(扩展KMP)
  4. 《剑指offer》按之字行顺序打印二叉树
  5. 为什么要用Redis
  6. 判断整数_2021暑期强化不定方程整数解问题
  7. 鸿蒙os运行内存,体验亮点满满!鸿蒙OS系统6月份开启适配,不只有华为手机
  8. 没有发现必备补丁文件‘NewopUI.pak’?
  9. Android 十年之路: 主屏与导航
  10. java实现zip文件压缩和解压
  11. 【我的OpenGL学习进阶之旅】EGL简介
  12. AutoSar FlashDriver
  13. c35是什么意思_混凝土C35P6是什么意思
  14. Spark 和hadoop的一些面试题(准备)
  15. 详解GaussDB(DWS) 资源监控
  16. 生产事故总结篇(2) 接口响应超时优化方案
  17. 啊屋童装商城android,我们采访了100位漂亮妈妈 她们手机里居然都有一款叫啊屋童装商城的app...
  18. 2019牛客暑期多校maximum clique 1 求最大独立团点集
  19. WEB 视频开发系列——千万级流量弹幕
  20. IE之“错误:Automation 服务器不能创建对象”问题

热门文章

  1. 龙海天气预报软件测试,龙海天气预报15天
  2. leetcode:1154. 一年中的第几天
  3. The 2019 ICPC Asia-East Continent Final A-City
  4. 腾讯会议桌面版怎么上传腾讯会议文件
  5. 第15届研电赛圆满落幕,RT-Thread企业专项奖花落谁家?
  6. 《市场调查与分析》:在校大学生对微信小程序的使用情况
  7. android mipmap的作用,Android 中的mipmap 和drawable的区别
  8. 坦桑尼亚签证办理攻略
  9. pytorch 1.1 零维tensor的乘法问题
  10. 机器学习新概念-MLOps简介