ptrace函数深入分析

ptrace函数:进程跟踪。

形式:#include<sys/ptrace.h>

Int ptrace(int request,int pid,int addr,int data);

概述:

父进程控制子进程运行,检查和改变它的核心Image。Ptrace主要用来实现断点调试。当进程被中止,通知父进程,进程的内存空间可以被读写,父进程可以选择是子进程继续执行,还是中止。

根据ptrace的函数原形 int ptrace(int request, int pid, int addr, int data);

request的不同参数决定了系统调用的功能

PTRACE_TRACEME

本进程被其父进程所跟踪。其父进程应该希望跟踪子进程

PTRACE_PEEKTEXT, PTRACE_PEEKDATA

从内存地址中读取一个字节,内存地址由addr给出。

PTRACE_PEEKUSR

从USER区域中读取一个字节,偏移量为addr。

PTRACE_POKETEXT, PTRACE_POKEDATA

往内存地址中写入一个字节。内存地址由addr给出。

PTRACE_POKEUSR

往USER区域中写入一个字节。偏移量为addr。

PTRACE_SYSCALL, PTRACE_CONT

重新运行。

PTRACE_KILL

杀掉子进程,使它退出。

PTRACE_SINGLESTEP

设置单步执行标志

PTRACE_ATTACH

跟踪指定pid 进程。

PTRACE_DETACH

结束跟踪

Intel386特有:

PTRACE_GETREGS

读取寄存器

PTRACE_SETREGS

设置寄存器

PTRACE_GETFPREGS

读取浮点寄存器

PTRACE_SETFPREGS

设置浮点寄存器

init进程不可以使用此函数

返回值:

成功返回0。错误返回-1。errno被设置

错误:

EPERM

特殊进程不可以被跟踪或进程已经被跟踪。

ESRCH

指定的进程不存在

EIO

请求非法

Ptrace功能:

  1) PTRACE_TRACEME

    形式:ptrace(PTRACE_TRACEME,0 ,0 ,0)

    描述:本进程被其父进程所跟踪。其父进程应该希望跟踪子进程。

  2) PTRACE_PEEKTEXT, PTRACE_PEEKDATA

    形式:ptrace(PTRACE_PEEKTEXT, pid, addr, data)

    ptrace(PTRACE_PEEKDATA, pid, addr, data)

    描述:从内存地址中读取一个字节,pid表示被跟踪的子进程,内存地址由addr给出,data为用户变量地址用于返回读到的数据。在Linux(i386)中用户代码段与用户数据段重合所以读取代码段和数据段数据处理是一样的。

  3) PTRACE_POKETEXT, PTRACE_POKEDATA

    形式:ptrace(PTRACE_POKETEXT, pid, addr, data)

    ptrace(PTRACE_POKEDATA, pid, addr, data)

    描述:往内存地址中写入一个字节。pid表示被跟踪的子进程,内存地址由addr给出,data为所要写入的数据。

  4) PTRACE_PEEKUSR

    形式:ptrace(PTRACE_PEEKUSR, pid, addr, data)

    描述:从USER区域中读取一个字节,pid表示被跟踪的子进程,USER区域地址由addr给出,data为用户变量地址用于返回读到的数据。USER结构为core文件的前面一部分,它描述了进程中止时的一些状态,如:寄存器值,代码、数据段大小,代码、数据段开始地址等。在Linux(i386)中通过PTRACE_PEEKUSER和PTRACE_POKEUSR可以访问USER结构的数据有寄存器和调试寄存器。

  5) PTRACE_POKEUSR

    形式:ptrace(PTRACE_POKEUSR, pid, addr, data)

    描述:往USER区域中写入一个字节,pid表示被跟踪的子进程,USER区域地址由addr给出,data为需写入的数据。

  6) PTRACE_CONT

    形式:ptrace(PTRACE_CONT, pid, 0, signal)

    描述:继续执行。pid表示被跟踪的子进程,signal为0则忽略引起调试进程中止的信号,若不为0则继续处理信号signal。

  7) PTRACE_SYSCALL

    形式:ptrace(PTRACE_SYS, pid, 0, signal)

    描述:继续执行。pid表示被跟踪的子进程,signal为0则忽略引起调试进程中止的信号,若不为0则继续处理信号signal。与PTRACE_CONT不同的是进行系统调用跟踪。在被跟踪进程继续运行直到调用系统调用开始或结束时,被跟踪进程被中止,并通知父进程。

  8) PTRACE_KILL

    形式:ptrace(PTRACE_KILL,pid)

    描述:杀掉子进程,使它退出。pid表示被跟踪的子进程。

  9) PTRACE_SINGLESTEP

    形式:ptrace(PTRACE_KILL, pid, 0, signle)

    描述:设置单步执行标志,单步执行一条指令。pid表示被跟踪的子进程。signal为0则忽略引起调试进程中止的信号,若不为0则继续处理信号signal。当被跟踪进程单步执行完一个指令后,被跟踪进程被中止,并通知父进程。

  10) PTRACE_ATTACH

    形式:ptrace(PTRACE_ATTACH,pid)

    描述:跟踪指定pid 进程。pid表示被跟踪进程。被跟踪进程将成为当前进程的子进程,并进入中止状态。

  11) PTRACE_DETACH

    形式:ptrace(PTRACE_DETACH,pid)

    描述:结束跟踪。 pid表示被跟踪的子进程。结束跟踪后被跟踪进程将继续执行。

  12) PTRACE_GETREGS

    形式:ptrace(PTRACE_GETREGS, pid, 0, data)

    描述:读取寄存器值,pid表示被跟踪的子进程,data为用户变量地址用于返回读到的数据。此功能将读取所有17个基本寄存器的值。

  13) PTRACE_SETREGS

    形式:ptrace(PTRACE_SETREGS, pid, 0, data)

    描述:设置寄存器值,pid表示被跟踪的子进程,data为用户数据地址。此功能将设置所有17个基本寄存器的值。

  14) PTRACE_GETFPREGS

    形式:ptrace(PTRACE_GETFPREGS, pid, 0, data)

    描述:读取浮点寄存器值,pid表示被跟踪的子进程,data为用户变量地址用于返回读到的数据。此功能将读取所有浮点协处理器387的所有寄存器的值。

  15) PTRACE_SETFPREGS

    形式:ptrace(PTRACE_SETREGS, pid, 0, data)

    描述:设置浮点寄存器值,pid表示被跟踪的子进程,data为用户数据地址。此功能将设置所有浮点协处理器387的所有寄存器的值。

代码分析

  与Ptrace函数相关的代码:

    1. sys_ptrace函数,完成ptrace系统调用的代码。

    2. 为完成sys_ptrace功能所需调用的一些辅助函数,寄存器读写函数和内存读写函数。

    3. 信号处理函数中,对被调试进程的处理(中止其运行、继续运行)。

    4. syscall_trace函数,完成了系统调用调试下的处理。

    5. 调试陷阱处理(异常1处理),完成单步执行和断点中断处理。

    6. execve系统调用中对被调试进程装入后中止的实现。

  1.sys_ptrace函数

    sys_ptrace函数完成了ptrace系统调用功能。源码位置/linux/arch/i386/kernel/ptrace.c

    sys_ptrace函数执行流程

ptrace函数深入分析

ptrace函数深入分析
1. asmlinkage int sys_ptrace(long request, long pid, long addr, long data)  

2. {  

3. struct task_struct *child;  

4. struct user * dummy;  

5. int i;  

6.

7.     dummy = NULL;  

8.

9. if (request == PTRACE_TRACEME) {  

10. /* are we already being traced? */

11. if (current->flags & PF_PTRACED)  

12. return -EPERM;  

13. /* set the ptrace bit in the process flags. */

14.         current->flags |= PF_PTRACED;  

15. return 0;  

16.     }  

17. if (pid == 1)       /* 进程不能被调试 */

18. return -EPERM;  

19. if (!(child = get_task(pid)))  

20. return -ESRCH;  

21. if (request == PTRACE_ATTACH) {  

22. if (child == current)  

23. return -EPERM;  

24. if ((!child->dumpable ||  

25.             (current->uid != child->euid) ||  

26.             (current->uid != child->suid) ||  

27.             (current->uid != child->uid) ||  

28.             (current->gid != child->egid) ||  

29.             (current->gid != child->sgid) ||  

30.             (current->gid != child->gid)) && !suser())  

31. return -EPERM;  

32. /* 同一进程不能多次附加 */

33. if (child->flags & PF_PTRACED)  

34. return -EPERM;  

35.         child->flags |= PF_PTRACED;  

36. if (child->p_pptr != current) {  

37.             REMOVE_LINKS(child);  

38.             child->p_pptr = current;  

39.             SET_LINKS(child);  

40.         }  

41.         send_sig(SIGSTOP, child, 1);  

42. return 0;  

43.     }  

44. if (!(child->flags & PF_PTRACED))  

45. return -ESRCH;  

46. if (child->state != TASK_STOPPED) {  

47. if (request != PTRACE_KILL)  

48. return -ESRCH;  

49.     }  

50. if (child->p_pptr != current) 

51. return -ESRCH;  

52.

53. switch (request) {  

54. /* when I and D space are separate, these will need to be fixed. */

55. case PTRACE_PEEKTEXT: /* read word at location addr. */

56. case PTRACE_PEEKDATA: {  

57.             unsigned long tmp;  

58. int res;  

59.

60.             res = read_long(child, addr, &tmp);  

61. if (res < 0)  

62. return res;  

63.             res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));  

64. if (!res)  

65.                 put_fs_long(tmp,(unsigned long *) data);  

66. return res;  

67.         }  

68.

69. /* read the word at location addr in the USER area. */

70. case PTRACE_PEEKUSR: {  

71.             unsigned long tmp;  

72. int res;  

73.

74. if ((addr & 3) || addr < 0 ||   

75.                 addr > sizeof(struct user) - 3)  

76. return -EIO;  

77.

78.             res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));  

79. if (res)  

80. return res;  

81.             tmp = 0;  /* Default return condition */

82. if(addr < 17*sizeof(long)) {  

83.               addr = addr >> 2; /* temporary hack. */

84.

85.               tmp = get_stack_long(child, sizeof(long)*addr - MAGICNUMBER);  

86. if (addr == DS || addr == ES ||  

87.                   addr == FS || addr == GS ||  

88.                   addr == CS || addr == SS)  

89.                 tmp &= 0xffff;  

90.             };  

91. if(addr >= (long) &dummy->u_debugreg[0] &&  

92.                addr <= (long) &dummy->u_debugreg[7]){  

93.                 addr -= (long) &dummy->u_debugreg[0];  

94.                 addr = addr >> 2;  

95.                 tmp = child->debugreg[addr];  

96.             };  

97.             put_fs_long(tmp,(unsigned long *) data);  

98. return 0;  

99.         }  

100.

101. /* when I and D space are separate, this will have to be fixed. */

102. case PTRACE_POKETEXT: /* write the word at location addr. */

103. case PTRACE_POKEDATA:  

104. return write_long(child,addr,data);  

105.

106. case PTRACE_POKEUSR: /* write the word at location addr in the USER area */

107. if ((addr & 3) || addr < 0 ||   

108.                 addr > sizeof(struct user) - 3)  

109. return -EIO;  

110.

111.             addr = addr >> 2; /* temporary hack. */

112.

113. if (addr == ORIG_EAX)  

114. return -EIO;  

115. if (addr == DS || addr == ES ||  

116.                 addr == FS || addr == GS ||  

117.                 addr == CS || addr == SS) {  

118.                     data &= 0xffff;  

119. if (data && (data & 3) != 3)  

120. return -EIO;  

121.             }  

122. if (addr == EFL) {   /* flags. */

123.                 data &= FLAG_MASK;  

124.                 data |= get_stack_long(child, EFL*sizeof(long)-MAGICNUMBER)  & ~FLAG_MASK;  

125.             }  

126. /* Do not allow the user to set the debug register for kernel

127.              address space */

128. if(addr < 17){  

129. if (put_stack_long(child, sizeof(long)*addr-MAGICNUMBER, data))  

130. return -EIO;  

131. return 0;  

132.             };  

133.

134. /* We need to be very careful here.  We implicitly

135.              want to modify a portion of the task_struct, and we

136.              have to be selective about what portions we allow someone

137.              to modify. */

138.

139.           addr = addr << 2;  /* Convert back again */

140. if(addr >= (long) &dummy->u_debugreg[0] &&  

141.              addr <= (long) &dummy->u_debugreg[7]){  

142.

143. if(addr == (long) &dummy->u_debugreg[4]) return -EIO;  

144. if(addr == (long) &dummy->u_debugreg[5]) return -EIO;  

145. if(addr < (long) &dummy->u_debugreg[4] &&  

146.                  ((unsigned long) data) >= 0xbffffffd) return -EIO;  

147.

148. if(addr == (long) &dummy->u_debugreg[7]) {  

149.                   data &= ~DR_CONTROL_RESERVED;  

150. for(i=0; i<4; i++)  

151. if ((0x5f54 >> ((data >> (16 + 4*i)) & 0xf)) & 1)  

152. return -EIO;  

153.               };  

154.

155.               addr -= (long) &dummy->u_debugreg;  

156.               addr = addr >> 2;  

157.               child->debugreg[addr] = data;  

158. return 0;  

159.           };  

160. return -EIO;  

161.

162. case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */

163. case PTRACE_CONT: { /* restart after signal. */

164. long tmp;  

165.

166. if ((unsigned long) data > NSIG)  

167. return -EIO;  

168. if (request == PTRACE_SYSCALL)  

169.                 child->flags |= PF_TRACESYS;  

170. else

171.                 child->flags &= ~PF_TRACESYS;  

172.             child->exit_code = data;  

173.             wake_up_process(child);  

174. /* make sure the single step bit is not set. */

175.             tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;  

176.             put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);  

177. return 0;  

178.         }  

179.

180. /*

181.  * make the child exit.  Best I can do is send it a sigkill. 

182.  * perhaps it should be put in the status that it wants to 

183.  * exit.

184.  */

185. case PTRACE_KILL: {  

186. long tmp;  

187.

188. if (child->state == TASK_ZOMBIE) /* already dead */

189. return 0;  

190.             wake_up_process(child);  

191.             child->exit_code = SIGKILL;  

192. /* make sure the single step bit is not set. */

193.             tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;  

194.             put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);  

195. return 0;  

196.         }  

197.

198. case PTRACE_SINGLESTEP: {  /* set the trap flag. */

199. long tmp;  

200.

201. if ((unsigned long) data > NSIG)  

202. return -EIO;  

203.             child->flags &= ~PF_TRACESYS;  

204.             tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) | TRAP_FLAG;  

205.             put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);  

206.             wake_up_process(child);  

207.             child->exit_code = data;  

208. /* give it a chance to run. */

209. return 0;  

210.         }  

211.

212. case PTRACE_DETACH: { /* detach a process that was attached. */

213. long tmp;  

214.

215. if ((unsigned long) data > NSIG)  

216. return -EIO;  

217.             child->flags &= ~(PF_PTRACED|PF_TRACESYS);  

218.             wake_up_process(child);  

219.             child->exit_code = data;  

220.             REMOVE_LINKS(child);  

221.             child->p_pptr = child->p_opptr;  

222.             SET_LINKS(child);  

223. /* make sure the single step bit is not set. */

224.             tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;  

225.             put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);  

226. return 0;  

227.         }  

228.

229. default:  

230. return -EIO;  

231.     }  

232. }  
sys_ptrace函数

 

1) PTRACE_TRACEME处理

说明:此处理使当前进程进入调试状态。进程是否为调试状态由进程的标志PF_PTRACED表示。

流程:

ptrace函数深入分析

ptrace函数深入分析
1. 9.      if (request == PTRACE_TRACEME) {    

2. 10.         /* 是否被跟踪 */

3. 11.         if (current->flags & PF_PTRACED)    

4. 12.             return -EPERM;    

5. 13.         /* 设置跟踪标志 */

6. 14.         current->flags |= PF_PTRACED;    

7. 15.         return 0;    

8. 16.     } 
PTRACE_TRACEME处理

 

2) PTRACE_ATTACH处理

说明:此处理设置开始调试某一进程,此进程可以是任何进程(init 进程除外)。对某一进程的调试需有对这一进程操作的权限。不能调试自身进程。一个进程不能ATTACH多次。为完成对一个进程的调试设置,首先设置进程标志置PF_PTRACED。再将需调试的进程设置为当前进程的子进程。最后向它发信号SIGSTOP中止它的运行,使它进入调试状态。

流程:

ptrace函数深入分析

ptrace函数深入分析
1. 21.     if (request == PTRACE_ATTACH) {    

2. 22.         if (child == current)    // 不能调试自身进程

3. 23.             return -EPERM;    

4. 24.         if ((!child->dumpable ||    

5. 25.             (current->uid != child->euid) ||    

6. 26.             (current->uid != child->suid) ||    

7. 27.             (current->uid != child->uid) ||    

8. 28.             (current->gid != child->egid) ||    

9. 29.             (current->gid != child->sgid) ||    

10. 30.             (current->gid != child->gid)) && !suser())  // 检验用户权限

11. 31.             return -EPERM;    

12. 32.         /* 同一进程不能多次附加 */

13. 33.         if (child->flags & PF_PTRACED)    

14. 34.             return -EPERM;    

15. 35.         child->flags |= PF_PTRACED;    // 设置进程标志位

16. 36.         if (child->p_pptr != current) { // 设置进程为当前进程的子进程

17. 37.             REMOVE_LINKS(child);    

18. 38.             child->p_pptr = current;    

19. 39.             SET_LINKS(child);    

20. 40.         }    

21. 41.         send_sig(SIGSTOP, child, 1);    // 发送SIGSTOP信号中止运行

22. 42.         return 0;    

23. 43.     }    
PTRACE_ATTACH处理

 

4) PTRACE_POKETEXT,PTRACE_POKEDATA处理

说明:与PTRACE_PEEKTEXT,PTRACE_PEEKDATA处理相反,此处理为写进程内存

流程:

ptrace函数深入分析

ptrace函数深入分析
1. 102.            case PTRACE_POKETEXT: /* write the word at location addr. */

2. 103.            case PTRACE_POKEDATA:    

3. 104.                return write_long(child,addr,data);    
PTRACE_POKETEXT,PTRACE_POKEDATA处理

 

5) PTRACE_PEEKUSR处理

说明:在Linux(i386)中,读写USER区域的数据值有用户寄存器和调试寄存器的值。用户寄存器包括17个寄存器,它们分别是EBX、ECX、EDX、ESI、EDI、EBP、EAX、DS、ES、FS、GS、ORIG_EAX、EIP、CS、EFLAGS、ESP、SS。这些寄存器的读写由辅助函数putreg()和getreg()函数完成。调试寄存器为DR0—DR7。其中DR4和DR5为系统保留的寄存器,不可以写。DR0—DR3中的断点地址必须在用户的3G空间内,在核心内存设置断点非法。DR7中的RWE与LEN数据位必须合法(LEN≠10保留、RWE≠10保留、RWE=00时LEN=00指令断点为一字节)。

流程:

ptrace函数深入分析

ptrace函数深入分析
1. 70.         case PTRACE_PEEKUSR: {    

2. 71.             unsigned long tmp;    

3. 72.             int res;    

4. 73.     

5. 74.             if ((addr & 3) || addr < 0 ||     // 越界或字节码未对齐出错

6. 75.                 addr > sizeof(struct user) - 3)    

7. 76.                 return -EIO;    

8. 77.     

9. 78.             res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));    

10. 79.             if (res)    

11. 80.                 return res;    

12. 81.             tmp = 0;  /* Default return condition */

13. 82.             if(addr < 17*sizeof(long)) {    

14. 83.               addr = addr >> 2; /* temporary hack. */

15. 84.     

16. 85.               tmp = get_stack_long(child, sizeof(long)*addr - MAGICNUMBER);    

17. 86.               if (addr == DS || addr == ES ||    

18. 87.                   addr == FS || addr == GS ||    

19. 88.                   addr == CS || addr == SS)    

20. 89.                 tmp &= 0xffff;    

21. 90.             };    

22. 91.             if(addr >= (long) &dummy->u_debugreg[0] &&    

23. 92.                addr <= (long) &dummy->u_debugreg[7]){    

24. 93.                 addr -= (long) &dummy->u_debugreg[0];    

25. 94.                 addr = addr >> 2;    

26. 95.                 tmp = child->debugreg[addr];    

27. 96.             };    

28. 97.             put_fs_long(tmp,(unsigned long *) data);    

29. 98.             return 0;    

30. 99.         }   
PTRACE_PEEKUSR处理

 

6) PTRACE_POKEUSR处理

说明:与PTRACE_PEEKUSR处理相反,此处理为写USER区域。

流程:

ptrace函数深入分析

源码:

ptrace函数深入分析
1. 106.            case PTRACE_POKEUSR: /* write the word at location addr in the USER area */

2. 107.                if ((addr & 3) || addr < 0 ||     

3. 108.                    addr > sizeof(struct user) - 3)    

4. 109.                    return -EIO;    

5. 110.        

6. 111.                addr = addr >> 2; /* temporary hack. */

7. 112.        

8. 113.                if (addr == ORIG_EAX)    

9. 114.                    return -EIO;    

10. 115.                if (addr == DS || addr == ES ||    

11. 116.                    addr == FS || addr == GS ||    

12. 117.                    addr == CS || addr == SS) {    

13. 118.                        data &= 0xffff;    

14. 119.                        if (data && (data & 3) != 3)    

15. 120.                        return -EIO;    

16. 121.                }    

17. 122.                if (addr == EFL) {   /* flags. */

18. 123.                    data &= FLAG_MASK;    

19. 124.                    data |= get_stack_long(child, EFL*sizeof(long)-MAGICNUMBER)  & ~FLAG_MASK;    

20. 125.                }    

21. 126.              /* Do not allow the user to set the debug register for kernel 

22. 127.                 address space */

23. 128.              if(addr < 17){    

24. 129.                  if (put_stack_long(child, sizeof(long)*addr-MAGICNUMBER, data))    

25. 130.                    return -EIO;    

26. 131.                return 0;    

27. 132.                };    

28. 133.        

29. 134.              /* We need to be very careful here.  We implicitly 

30. 135.                 want to modify a portion of the task_struct, and we 

31. 136.                 have to be selective about what portions we allow someone 

32. 137.                 to modify. */

33. 138.        

34. 139.              addr = addr << 2;  /* Convert back again */

35. 140.              if(addr >= (long) &dummy->u_debugreg[0] &&    

36. 141.                 addr <= (long) &dummy->u_debugreg[7]){    

37. 142.        

38. 143.                  if(addr == (long) &dummy->u_debugreg[4]) return -EIO;    

39. 144.                  if(addr == (long) &dummy->u_debugreg[5]) return -EIO;    

40. 145.                  if(addr < (long) &dummy->u_debugreg[4] &&    

41. 146.                     ((unsigned long) data) >= 0xbffffffd) return -EIO;    

42. 147.                      

43. 148.                  if(addr == (long) &dummy->u_debugreg[7]) {    

44. 149.                      data &= ~DR_CONTROL_RESERVED;    

45. 150.                      for(i=0; i<4; i++)    

46. 151.                          if ((0x5f54 >> ((data >> (16 + 4*i)) & 0xf)) & 1)    

47. 152.                              return -EIO;    

48. 153.                  };    

49. 154.        

50. 155.                  addr -= (long) &dummy->u_debugreg;    

51. 156.                  addr = addr >> 2;    

52. 157.                  child->debugreg[addr] = data;    

53. 158.                  return 0;    

54. 159.              };    

55. 160.              return -EIO;    
PTRACE_POKEUSR处理

 

7) PTRACE_SYSCALL,PTRACE_CONT处理

说明:PTRACE_SYSCALL和PTRACE_CONT有着相同的处理,都是让子进程继续运行,其区别PTRACE_SYSCALL设置了进程标志PF_TRACESYS。这样可以使进程在下一次系统调用开始或结束时中止运行。继续执行要保证清除单步执行标志。用户参数data为用户提供的信号,希望子进程继续处理此信号。如果为0则不处理,如果不为0则在唤醒子进程后向子进程发送此信号(在do_signal()和syscall_trace()函数中完成)。

流程:

ptrace函数深入分析

ptrace函数深入分析
1. 162.            case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */

2. 163.            case PTRACE_CONT: { /* restart after signal. */

3. 164.                long tmp;    

4. 165.        

5. 166.                if ((unsigned long) data > NSIG)    

6. 167.                    return -EIO;    

7. 168.                if (request == PTRACE_SYSCALL)    

8. 169.                    child->flags |= PF_TRACESYS;    

9. 170.                else

10. 171.                    child->flags &= ~PF_TRACESYS;    

11. 172.                child->exit_code = data;    

12. 173.                wake_up_process(child);    

13. 174.        /* make sure the single step bit is not set. */

14. 175.                tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;    

15. 176.                put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);    

16. 177.                return 0;    

17. 178.            }    

18. 179.       
PTRACE_SYSCALL,PTRACE_CONT处理

 

 

8) PTRACE_KILL处理

说明:此功能完成杀死子进程的功能。以往杀死进程只要往此进程发送SIGKILL信号。在此处理类似于PTRACE_CONT处理,只是把子进程继续的信号设置为SIGKILL,则唤醒子进程后,子进程会受到SIGKILL信号。

流程:

ptrace函数深入分析

ptrace函数深入分析
1. 2.          case PTRACE_KILL: {    

2. 3.              long tmp;    

3. 4.      

4. 5.              if (child->state == TASK_ZOMBIE) /* 进程已经退出 */

5. 6.                  return 0;    

6. 7.              wake_up_process(child);    

7. 8.              child->exit_code = SIGKILL;    

8. 9.      /* make sure the single step bit is not set. */

9. 10.             tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;    

10. 11.             put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);    

11. 12.             return 0;    

12. 13.         }    
PTRACE_KILL处理

 

9) PTRACE_SINGLESTEP处理

说明:单步调试,子进程运行一条指令。此处理类似于PTRACE_CONT处理。不同的只是设置类单步调试标志TF。

流程:

ptrace函数深入分析

ptrace函数深入分析
1. 198.            case PTRACE_SINGLESTEP: {  /* set the trap flag. */

2. 199.                long tmp;    

3. 200.        

4. 201.                if ((unsigned long) data > NSIG)    

5. 202.                    return -EIO;    

6. 203.                child->flags &= ~PF_TRACESYS;    

7. 204.                tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) | TRAP_FLAG;    

8. 205.                put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);    

9. 206.                wake_up_process(child);    

10. 207.                child->exit_code = data;    

11. 208.        /* give it a chance to run. */

12. 209.                return 0;    

13. 210.            }    
PTRACE_SINGLESTEP处理

 

10) PTRACE_DETACH处理

说明:终止调试一个子进程。此处理与PTRACE_ATTACH处理相反。在此做了一些清理操作:清除PF_TRACESYS和PF_PTRACED进程标志,清除TF标志,父进程指针还原。最后唤醒此进程,让其继续执行。

流程:

ptrace函数深入分析

ptrace函数深入分析
1. 212.            case PTRACE_DETACH: { /* detach a process that was attached. */

2. 213.                long tmp;    

3. 214.        

4. 215.                if ((unsigned long) data > NSIG)    

5. 216.                    return -EIO;    

6. 217.                child->flags &= ~(PF_PTRACED|PF_TRACESYS);    

7. 218.                wake_up_process(child);    

8. 219.                child->exit_code = data;    

9. 220.                REMOVE_LINKS(child);    

10. 221.                child->p_pptr = child->p_opptr;    

11. 222.                SET_LINKS(child);    

12. 223.                /* make sure the single step bit is not set. */

13. 224.                tmp = get_stack_long(child, sizeof(long)*EFL-MAGICNUMBER) & ~TRAP_FLAG;    

14. 225.                put_stack_long(child, sizeof(long)*EFL-MAGICNUMBER,tmp);    

15. 226.                return 0;    

16. 227.            }   
PTRACE_DETACH处理

 

Ptrace的使用

ptrace:提供了一种父进程可以控制子进程运行,并检查和改变核心功能的调试器

1. 启动、中止调试程序

ptrace函数深入分析
 1 int pid;  
 2 
 3 pid = fork ();  
 4 
 5 if (pid < 0)  
 6 
 7 perror_with_name ("fork");   
 8 
 9 if (pid == 0)  
10 
11 {  
12 
13     ptrace (PTRACE_TRACEME, 0, 0, 0);  
14 
15     execv (program, allargs);     /* char *program;  
16 
17  char **allargs; 指向程序名和参数
18 
19      fprintf (stderr, "Cannot exec %s: %s.\n", program,  
20 
21      errno < sys_nerr ? sys_errlist[errno] : "unknown error");  
22 
23      fflush (stderr);  
24 
25      _exit (0177);  
26 
27  }  
28 
29  wait(pid);  
30 
31  ptrace (PTRACE_CONT, pid, 0, 0);  
32 
33  wait(pid);  
View Code

 

2.对现有进程进行调试

使用PTRACE_ATTACH

ptrace函数深入分析
1. ptrace(PTRACE_ATTACH,pid, 0,0)  

2. wait(pid);  
View Code

 

3.退出进程调试

使用PTRACE_DETACH

ptrace函数深入分析
1. ptrace(PTRACE_DETACH,pid,  0,0)  
View Code

 

4.终止调试进程运行

使用PTRACE_KILL

ptrace函数深入分析
1. ptrace(PTRACE_DETACH,pid,  0,0)  
View Code

 

上一篇:如何使用ptrace(linux,x86 / x86_64)获得“回溯”(如gdb)


下一篇:spring boot基础介绍1