轉載原文信息:
作者:M_天河
鏈接:https://www.jianshu.com/p/b1f9d6911c90
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。
ptrace 提供了一種機制使得父進程可以觀察和控制子進程的執行過程,ptrace 還可以檢查和修改子進程的可執行文件在內存中的image及子進程所使用的寄存器中的值。通常來說,主要用於實現對進程插入斷點和跟蹤子進程的系統調用。
用法示例
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <linux/user.h>
int main()
{ pid_t child;
long orig_eax;
child = fork();
if(child == 0) {
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
execl("/bin/ls", "ls", NULL);
}
else {
wait(NULL);
orig_eax = ptrace(PTRACE_PEEKUSER,child, 4 * ORIG_EAX,NULL);
printf("The child made a ""system call %ld\n", orig_eax);
ptrace(PTRACE_CONT, child, NULL, NULL);
}
return 0;
}
上面代碼被編譯後除了執行ls命令外還會執行第17行代碼的打印內容。
具體過程:父進程 fork() 出子進程,子進程中執行我們所想要 trace 的程序,在子進程調用 exec() 之前,子進程需要先調用一次 ptrace,以 PTRACE_TRACEME 爲參數。這個調用是爲了告訴內核,當前進程已經正在被 traced,當子進程執行 execve() 之後,子進程會進入暫停狀態,把控制權轉給它的父進程(SIG_CHLD信號), 而父進程在fork()之後,就調用 wait() 等子進程停下來,當 wait() 返回後,父進程就可以去查看子進程的寄存器或者對子進程做其它的事情了。
當系統調用發生時,內核會把當前的寄存器中的內容(即系統調用的編號)保存到子進程的用戶態代碼段中,我們可以像上面的例子那樣通過調用Ptrace(傳入PTRACE_PEEKUSER作爲第一個參數)來讀取這個寄存器的值,當我們做完這些檢查數據的事情之後,通過調用ptrace(PTRACE_CONT),可以讓子進程重新恢復運行。
ptrace共有四個參數:
long ptrace(enum __ptrace_request request,pid_t pid,void *addr,void *data);
其中第一個參數可以取如下內容:
PTRACE_TRACEME, 本進程被其父進程所跟蹤。其父進程應該希望跟蹤子進程
PTRACE_PEEKTEXT, 從內存地址中讀取一個字節,內存地址由addr給出
PTRACE_PEEKDATA, 同上
PTRACE_PEEKUSER, 可以檢查用戶態內存區域(USER area),從USER區域中讀取一個字節,偏移量爲addr
PTRACE_POKETEXT, 往內存地址中寫入一個字節。內存地址由addr給出
PTRACE_POKEDATA, 往內存地址中寫入一個字節。內存地址由addr給出
PTRACE_POKEUSER, 往USER區域中寫入一個字節,偏移量爲addr
PTRACE_GETREGS, 讀取寄存器
PTRACE_GETFPREGS, 讀取浮點寄存器
PTRACE_SETREGS, 設置寄存器
PTRACE_SETFPREGS, 設置浮點寄存器
PTRACE_CONT, 重新運行
PTRACE_SYSCALL, 重新運行
PTRACE_SINGLESTEP, 設置單步執行標誌
PTRACE_ATTACH,追蹤指定pid的進程
PTRACE_DETACH, 結束追蹤
具體用法:
- PTRACE_TRACEME
ptrace(PTRACE_TRACEME,0 ,0 ,0)
本進程被其父進程所跟蹤。其父進程應該希望跟蹤子進程。 - PTRACE_PEEKTEXT, PTRACE_PEEKDATA
ptrace(PTRACE_PEEKTEXT, pid, addr, data)
ptrace(PTRACE_PEEKDATA, pid, addr, data)
從內存地址中讀取一個字節,pid表示被跟蹤的子進程,內存地址由addr給出,data爲用戶變量地址用於返回讀到的數據。在Linux(i386)中用戶代碼段與用戶數據段重合所以讀取代碼段和數據段數據處理是一樣的。 - PTRACE_POKETEXT, PTRACE_POKEDATA
ptrace(PTRACE_POKETEXT, pid, addr, data)
ptrace(PTRACE_POKEDATA, pid, addr, data)
往內存地址中寫入一個字節。pid表示被跟蹤的子進程,內存地址由addr給出,data爲所要寫入的數據。 - PTRACE_PEEKUSR
ptrace(PTRACE_PEEKUSR, pid, addr, data)
從USER區域中讀取一個字節,pid表示被跟蹤的子進程,USER區域地址由addr給出,data爲用戶變量地址用於返回讀到的數據。USER結構爲core文件的前面一部分,它描述了進程中止時的一些狀態,如:寄存器值,代碼、數據段大小,代碼、數據段開始地址等。在Linux(i386)中通過PTRACE_PEEKUSER和PTRACE_POKEUSR可以訪問USER結構的數據有寄存器和調試寄存器。 - PTRACE_POKEUSR
ptrace(PTRACE_POKEUSR, pid, addr, data)
往USER區域中寫入一個字節,pid表示被跟蹤的子進程,USER區域地址由addr給出,data爲需寫入的數據。 - PTRACE_CONT
ptrace(PTRACE_CONT, pid, 0, signal)
繼續執行。pid表示被跟蹤的子進程,signal爲0則忽略引起調試進程中止的信號,若不爲0則繼續處理信號signal。 - PTRACE_SYSCALL
ptrace(PTRACE_SYS, pid, 0, signal)
繼續執行。pid表示被跟蹤的子進程,signal爲0則忽略引起調試進程中止的信號,若不爲0則繼續處理信號signal。與PTRACE_CONT不同的是進行系統調用跟蹤。在被跟蹤進程繼續運行直到調用系統調用開始或結束時,被跟蹤進程被中止,並通知父進程。 - PTRACE_KILL
ptrace(PTRACE_KILL,pid)
殺掉子進程,使它退出。pid表示被跟蹤的子進程。 - PTRACE_SINGLESTEP
ptrace(PTRACE_KILL, pid, 0, signle)
設置單步執行標誌,單步執行一條指令。pid表示被跟蹤的子進程。signal爲0則忽略引起調試進程中止的信號,若不爲0則繼續處理信號signal。當被跟蹤進程單步執行完一個指令後,被跟蹤進程被中止,並通知父進程。 - PTRACE_ATTACH
ptrace(PTRACE_ATTACH,pid)
跟蹤指定pid 進程。pid表示被跟蹤進程。被跟蹤進程將成爲當前進程的子進程,並進入中止狀態。 - PTRACE_DETACH
ptrace(PTRACE_DETACH,pid)
結束跟蹤。 pid表示被跟蹤的子進程。結束跟蹤後被跟蹤進程將繼續執行。 - PTRACE_GETREGS
ptrace(PTRACE_GETREGS, pid, 0, data)
讀取寄存器值,pid表示被跟蹤的子進程,data爲用戶變量地址用於返回讀到的數據。此功能將讀取所有17個基本寄存器的值。 - PTRACE_SETREGS
ptrace(PTRACE_SETREGS, pid, 0, data)
設置寄存器值,pid表示被跟蹤的子進程,data爲用戶數據地址。此功能將設置所有17個基本寄存器的值。 - PTRACE_GETFPREGS
ptrace(PTRACE_GETFPREGS, pid, 0, data)
讀取浮點寄存器值,pid表示被跟蹤的子進程,data爲用戶變量地址用於返回讀到的數據。此功能將讀取所有浮點協處理器387的所有寄存器的值。 - PTRACE_SETFPREGS
ptrace(PTRACE_SETREGS, pid, 0, data)
設置浮點寄存器值,pid表示被跟蹤的子進程,data爲用戶數據地址。此功能將設置所有浮點協處理器387的所有寄存器的值。