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函数执行流程

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表示。

流程:

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中止它的运行,使它进入调试状态。

流程:

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处理相反,此处理为写进程内存

流程:

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指令断点为一字节)。

流程:

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区域。

流程:

源码:

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()函数中完成)。

流程:

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信号。

流程:

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。

流程:

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标志,父进程指针还原。最后唤醒此进程,让其继续执行。

流程:

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. 启动、中止调试程序

 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);  

2.对现有进程进行调试

使用PTRACE_ATTACH

1. ptrace(PTRACE_ATTACH,pid, 0,0)  

2. wait(pid);  

3.退出进程调试

使用PTRACE_DETACH

1. ptrace(PTRACE_DETACH,pid,  0,0)  

4.终止调试进程运行

使用PTRACE_KILL

1. ptrace(PTRACE_DETACH,pid,  0,0)  

原文地址:https://www.cnblogs.com/heixiang/p/10988992.html

时间: 2024-10-16 21:03:54

ptrace函数深入分析的相关文章

Hook ptrace 调试加入了ptrace函数的程序

#import <substrate.h> #if !defined(PT_DENY_ATTACH)#define PT_DENY_ATTACH 31#endif static int (*_ptraceHook)(int request, pid_t pid, caddr_t addr, int data); static int $ptraceHook(int request, pid_t pid, caddr_t addr, int data) {        if (request

玩转ptrace(转)

下面是转帖的内容,写的很详细.但是不同的linux发行版中头文件的路径和名称并不相同.如在某些发行版中<linux/user.h>就不存在,其中定义的变量出现在<asm/ptrace-abi.h>和<sys/user.h>中. ================================================================================================== by Pradeep Padala Create

“聊天剽窃手”--ptrace进程注入型病毒

近日,百度安全实验室发现了一款"聊天剽窃手"病毒,该病毒能够通过ptrace方式注入恶意代码至QQ.微信程序进程,恶意代码能够实时监控手机QQ.微信的聊天内容及联系人信息.该病毒是目前发现的首款通过ptrace进程注入方式进行恶意窃取私密资料的病毒. 简介 该病毒主要是通过ptrace注入QQ和微信进程进行信息窃取的,主程序调用assets中的inject_appso,libcall.so以及conn.jar联合进行"作案",在conn.jar中获取聊天信息/最近联

深入浅出scanf、getcha、gets、cin函数

转:问题描述一:(分析scanf()和getchar()读取字符) scanf(), getchar()等都是标准输入函数,一般人都会觉得这几个函数非常简单,没什么特殊的.但是有时候却就是因为使用这些函数除了问题,却找不出其中的原因.下面先看一个很简单的程序:程序1:    #include <stdio.h>    int main()    {char ch1, ch2;scanf("%c", &ch1); scanf("%c", &

linux ptrace II

第一篇 linux ptrace I 在之前的文章中我们用ptrace函数实现了查看系统调用参数的功能.在这篇文章中,我们会用ptrace函数实现设置断点,跟代码注入功能. 参考资料 Playing with ptrace, Part I Playing with ptrace, Part II 英文好的推荐直接看老外的文章.但是其代码是运行在x86系统上的,本文中将其移植到了x86_64系统. 进程附加 在之前的文章中,我们都是trace自己程序fork出来的子进程,现在我们来看一下如何tra

深入了解scanf/getchar/gets/cin等函数

scanf(), getchar()等都是标准输入函数,一般人都会觉得这几个函数非常简单,没什么特殊的.但是有时候却就是因为使用这些函数除了问题,却找不出其中的原因.下面先看一个很简单的程序: 程序1: #include <stdio.h> int main() { char ch1, ch2; scanf("%c", &ch1); scanf("%c", &ch2); printf("%d %d/n", ch1, c

Android Ptrace Inject

之前介绍了Android平台上3种常见的hook方法,而hook的前提是进程注入,通过进程注入我们可以将模块或代码注入到目标进程中以便对其空间内的数据进行操作,本篇文章介绍基于ptrace函数的注入技术. 对ptrace函数不熟悉的朋友可以参考我之前写的linux ptrace I和linux ptrace II,跟hook相比,在熟悉了ptrace函数的使用方式后注入过程并不复杂,但在细节的处理上要多加留意,稍有不慎就会造成目标进程发生崩溃. 注入流程如下: 附加目标进程 保存寄存器环境 远程

Linux Ptrace 详解

转 https://blog.csdn.net/u012417380/article/details/60470075 Linux Ptrace 详解 2017年03月05日 18:59:58 阅读数:6331 一.系统调用 操作系统提供一系列系统调用函数来为应用程序提供服务.关于系统调用的详细相关知识,可以查看<<程序员的自我修养>第十二章. 对于x86操作系统来说,用中断命令"int 0x80"来进行系统调用,系统调用前,需要将系统调用号放入到%EAX寄存器中,将

xposed学习四:结尾与开始

xposed的学习要告一段落,接下来去学习android apk和so加固,这里我们回顾下xposed吧. xposed是个hook框架,它是基于dalvik(确切来说是目前的版本,作者说ART也快出来了哦)的,也就是说他只是hook java函数.ok,既然xposed是个hook框架我们就来看下它是怎么搭建的呢? xposed核心代码在core中,实际就是把java函数标识成native函数,这样在运行java函数时就是在在执行我们自己定义的native函数从而去执行hooked的callb