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函數執行流程

clip_image002[4]

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

流程:

clip_image004[4]

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中止它的運行,使它進入調試狀態。

流程:

clip_image006[4]

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處理相反,此處理為寫進程內存

流程:

clip_image007[4]

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指令斷點為一字節)。

流程:

clip_image008[4]

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

流程:

clip_image010[4]

源碼:

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

流程:

clip_image011[4]

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

流程:

clip_image012[4]

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。

流程:

clip_image013[4]

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標志,父進程指針還原。最后喚醒此進程,讓其繼續執行。

流程:

clip_image014[4]

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

 

2.對現有進程進行調試

使用PTRACE_ATTACH

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

2. wait(pid);  
View Code

 

3.退出進程調試

使用PTRACE_DETACH

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

 

4.終止調試進程運行

使用PTRACE_KILL

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM