簡介
ptrace
是一種系統調用,也就是進程追蹤(process trace);用於對進程的執行進行干涉以及寄存器狀態(值)的讀取以及設置,內存的寫入與讀取;我們常用的Linux下的gdb主要功能實現就是通過ptrace系統調用的:
#include <sys/ptrace.h>
long ptrace(enum __ptrace_request request, pid_t pid,void *addr, void *data);
通過request
參數的不同來實現不同的功能;
常用的Ptrace參數
PTRACE_ATTACH
:建立不同進程間的跟蹤關係,附加成功後,目標進程將處於掛起狀態;被跟蹤進程,將會發送SIGSTOP
信號給跟蹤者;跟蹤着需要調用wait()
函數接收被跟蹤進程傳來的SIGSTOP
信號,使用此參數時pid,addr,data參數都會被忽略:
ptrace(PTRACE_ATTACH, pid,NULL,NULL);
PTRACE_TRACEME
:指示其父進程,對其進行跟蹤;使用此參數時pid,addr,data參數都會被忽略:
if (ptrace(PTRACE_TRACEME, pid, NULL, NULL) < 0) {
perror("PTRACE_TRACEME");
exit(0);
}
PTRACE_PEEKTEXT
或PTRACE_PEEKDATA
:從addr參數指示的地址開始讀
取一個WORD
的長度的數據並通過返回值
傳遞回來;使用此參數時data參數會被忽略:
data = ptrace(PTRACE_PEEKTEXT, pid, addr, NULL);
if (errno != 0) {
perror("PTRACE_PEEKTEXT");
exit(0);
}
PTRACE_POKETEXT
或PTRACE_POKEDATA
:從addr參數指示的地址開始寫
入一個WORD
長度的數據:
if (ptrace(PTRACE_POKETEXT, pid, addr, data) < 0) {
perror("PTRACE_POKETEXT");
exit(0);
}
PTRACE_GETREGS
:從被跟蹤的進程中讀取
用戶寄存器的值到一個struct user_regs_struct
結構體中,使用此參數時addr和data參數會被忽略:
#include<sys/user.h>
struct user_regs_struct reg;
if (ptrace(PTRACE_GETREGS, pid, NULL, ®) < 0) {
perror("PTRACE_GETREGS");
exit(0);
}
printf("RIP: %p\n",reg.rip);
PTRACE_SETREGS
:將一個struct user_regs_struct
類型的結構體中變量的值設置到被跟蹤進程的用戶寄存器中去;與上一個PTRACE_GETREGS
請求配合使用,使用此參數時addr和data參數會被忽略:
if (ptrace(PTRACE_SETREGS, pid, NULL, ®) < 0) {
perror("PTRACE_SETREGS");
exit(0);
}
PTRACE_CONT
:恢復pid指定的進程執行;當data參數不爲零時,data將會被解釋爲一個signal
,傳遞給被跟蹤進程,使用此參數時addr參數被忽略:
if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) {
perror("PTRACE_CONT");
exit(0);
}
PTRACE_DETACH
或PTRACE_KILL
:將會把SIGKILL
發送給被跟蹤進程,脫離進程間的跟蹤關係,使用此參數時addr和data參數會被忽略:
if (ptrace(PTRACE_DETACH, pid, NULL, NULL) < 0) {
perror("PTRACE_KILL");
exit(0);
}
模擬gdb的break和continue操作
思路:
- 將需要下斷點的地址的代碼讀出並保存;
- 將需要下斷點的地址的代碼修改爲
0xcc
(int 3); - 將需要下斷點的地址的代碼恢復.
被attach的程序
re.c:
#include <stdio.h>
#include <time.h>
#include <unistd.h>
void show()
{
printf("Hello, ptrace! [pid:%d]\n", getpid());
}
int main() {
while(1){
show();
sleep(2);
}
return 0;
}
編譯:
gcc re.c -o re
Ptarce程序
hook.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/reg.h> /* For constants ORIG_EAX etc */
#include <sys/user.h>
void new_show()
{
printf("Hooked by cc-sir!\n");
}
int main(int argc, char *argv[])
{
if(argc!=2) {
printf("Usage: %s pid\n", argv[0]);
return 0;
}
printf("new_show_addr: %p\n",new_show);
struct user_regs_struct reg;
pid_t pid = atoi(argv[1]);
ptrace(PTRACE_ATTACH, pid,NULL,NULL);
wait(NULL);
ptrace(PTRACE_GETREGS,pid,NULL,®);
printf("rip: 0x%lx\n",reg.rip);
long addr = reg.rip;
long show_addr = 0x400586;
long code = 0xcc80cd;
long back_code;
int id;
back_code = ptrace(PTRACE_PEEKTEXT, pid, addr, NULL); //保留源碼
printf("back_code: %llx\n",back_code);
if(ptrace(PTRACE_POKETEXT, pid, addr, code) < 0){ //修改源碼
perror("PTRACE_POKETEXT");
return 0;
}
ptrace(PTRACE_CONT, pid, NULL, NULL);
wait(NULL);
printf("The process has int 0x3!\n");
getchar();
if(ptrace(PTRACE_POKETEXT, pid, addr, back_code) < 0){ //還原代碼
perror("PTRACE_POKETEXT");
return 0;
}
ptrace(PTRACE_SETREGS, pid, NULL, ®); //還原寄存器
ptrace(PTRACE_CONT, pid, NULL, NULL);
printf("The process has continue run!\n");
ptrace(PTRACE_DETACH, pid, NULL, NULL);
return 0;
}
編譯:
gcc hook.c -o hook
結果: