在用戶模式中,雖然只有一個函數(shù)可用,即ptrace(int _request, pid_t _pid, caddr_t _addr, int _data),但是這個函數(shù)能做所有的事情!如果你愿意,也可以花費幾個小時來編寫自己的小調試器,以解決特定的問題。
ptrace函數(shù)的_request參數(shù)是最重要的一個參數(shù),因為它確定你將做什么。BSD和Linux的頭文件使用不同的定義,這使得將ptrace應用從一個平臺移植到另一個平臺變得很復雜。默認地,我們使用BSD頭文件中的定義。
r PT_TRACE_ME(PTRACE_TRACEME)將當前進程切換到停止狀態(tài)。它通常總是與fork/exec一起使用,雖然也能遇到自我追蹤的應用程序。對于每一個進程,PT_TRACE_ME只能被調用一次。追蹤一個正被追蹤的進程是會失敗的(另一個較不重要的結果是進程不能追蹤它自己。如果要這樣做,應該首先從自身派生一個進程)。大量的反調試技術都是以這一事實為基礎的。為了克服這類技術,必須使用繞過ptrace的調試器。一個信號被發(fā)送到正被調試的進程,并將該進程切換到停止狀態(tài),該進程可以使用從父進程上下文中調用的PT_CONTINUE和PT_STEP命令從停止狀態(tài)退出。wait函數(shù)會延遲父進程的執(zhí)行,直到被調試的進程切換為停止狀態(tài)或者終止為止(終止時,返回值為1407)。其他的所有參數(shù)都被忽略。
r PT_ATTACH(PTRACE_ATTACH)將進程標志為pid的運行進程切換為停止狀態(tài),在這種情形下,調試器進程成為“父進程”。其他的所有參數(shù)都被忽略。進程必須具有與調試進程相同的用戶標志(UID),并且不能是setuid/setduid進程(否則就要用root來調試)。
r PT_DETACH(PTRACE_DETACH)停止進程標志為pid進程(由PT_ATTACH和PT_TRACE_ME指定)的調試,并繼續(xù)其常態(tài)運行。其他的所有參數(shù)都被忽略。
r PT_CONTINUE(PTRACE_CONT)繼續(xù)進程標志為pid的被調試進程的執(zhí)行,而不中斷與調試器進程的通信。如果addr == 1(在Linux中為0),從上次停止的地址繼續(xù)執(zhí)行;否則,從指定的地址繼續(xù)執(zhí)行。參數(shù)_data指定發(fā)送到被調試進程的信號數(shù)量(零說明沒有信號)。
r PT_STEP(PTRACE_SINGLESTEP)進行進程標志為pid的進程的單步執(zhí)行,即執(zhí)行下一條機器指令并切換為停止狀態(tài)(在i386中,這是根據(jù)設置追蹤標志來實現(xiàn)的,雖然有些“黑客”函數(shù)庫使用硬件斷點)。BSD要求將參數(shù)addr置為1,而Linux要求將該參數(shù)置為0。其他的所有參數(shù)都被忽略。
r PT_READ_I和PT_READ_D(PTRACE_PEEKTEXT和PTRACE_PEEKDATA)分別從代碼區(qū)和正被調試進程的地址空間區(qū)讀取機器字。在許多當代的平臺中,這兩個指令是等價的。ptrace函數(shù)接收目標地址addr,并返回讀到的結果。
r PT_WRITE_I和PR_READ_D(PTRACE_POKETEXT和PTRACE_POKEDATA)將由_data傳入的機器字寫入addr所指定的地址。
r PT_GETREGS,PT_GETFPREGS和PT_GETDBREGS(PTRACE_GETREGS,PTRACE_ FPREGS和PT_GETFPXREGS)將一般用途寄存器、段寄存器和調試寄存器的值讀入到地址由_addr指針所指定的調試器進程的內存區(qū)中。只有i386平臺接收這些與系統(tǒng)相關的命令。寄存器結構的描述放在頭文件machine/reg.h文件中。
r PT_SETREGS,PT_SETFPREGS和PT_SETDBREGS(PTRACE_SETREGS,PTRACE_ SETFPREGS和PT_SETFPXREGS)通過拷貝由_addr指針所指定的內存區(qū)域的內容來設置被調試進程的寄存器的值。
r PT_KILL(PTRACE_KILL)將sigkill發(fā)送到被調試進程,以終止其執(zhí)行。