日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux(4)-Ptrace 系统调用的使用

發(fā)布時間:2025/6/17 linux 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux(4)-Ptrace 系统调用的使用 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

    • 問題
    • 運行環(huán)境
    • 程序組成
    • 實現(xiàn)思路
    • 模塊劃分
    • 完整代碼
    • 程序運行及結(jié)果


問題

??????利用ptrace系統(tǒng)調(diào)用實現(xiàn)一個簡單的軟件調(diào)試器。基本功能包括能夠截獲被調(diào)試進程的信號,被調(diào)試進程進入斷點后能夠查看被調(diào)試進程任意內(nèi)存區(qū)域的內(nèi)容,能夠查看任意CPU寄存器的內(nèi)容,能夠使被調(diào)試進程恢復運行。


運行環(huán)境

??????Ubuntu-20.04 64位虛擬機

程序組成

??????1,測試程序為test.c,這是后面要被測試的程序;它的可執(zhí)行程序為test(采用-g進行編譯)。
??????2,Ptrace系統(tǒng)調(diào)用的實現(xiàn)程序為ptrace.c。

實現(xiàn)思路

??????查看閱讀ptrace官方文檔(man ptrace),從文檔的相關(guān)說明中有了大體的實現(xiàn)思路,摘自手冊:
A process can initiate a trace by calling fork and having the resulting child do a PTRACE_TRACEME, followed (typically) by an execve,Alternatively, one process may commence tracing another process using PTRACE_ATTACH or PTRACE_SEIZE.
??????其中第一種是被動的,讓別人來調(diào)試自己;第二種是主動去調(diào)試別人。后面采用第一種方法。
??????Ptrace的函數(shù)原型為:
#include <sys/ptrace.h>
long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
??????參數(shù)request:請求ptrace執(zhí)行的操作;參數(shù)pid:目標進程的ID;參數(shù)addr:目標進程的地址值;參數(shù)data:作用則根據(jù)request的不同而變化,如果需要向目標進程中寫入數(shù)據(jù),data存放的是需要寫入的數(shù)據(jù);如果從目標進程中讀數(shù)據(jù),data將存放返回的數(shù)據(jù)。如下圖:

??????需注意:當采用參數(shù)request為PTRACE_PEEKTEXT時,代碼段中的數(shù)據(jù)被保存在返回值中。返回值為一個long類型。(經(jīng)測試long和long long在當前環(huán)境上均為8字節(jié)長)

??????在子進程中采用ptrace(PTRACE_TRACEME,0,0,0),來讓父進程來調(diào)試它,這個方法的執(zhí)行應(yīng)該在exec()函數(shù)執(zhí)行前,來保證執(zhí)行exec()時,子進程已經(jīng)處于被調(diào)試狀態(tài)。

??????采用fork(),將父進程作為tracer,將子進程作為tracee;再通過exec ()函數(shù)族將待調(diào)試的程序裝入到子進程中,并且exec()函數(shù)族在執(zhí)行時若發(fā)現(xiàn)原進程處于調(diào)試狀態(tài)下的話,當將新的代碼裝入后,會向自身發(fā)送信號SIGTRAP,中止執(zhí)行,方便在父進程中來進行操作。


??????父進程中通過簡單的循環(huán)操作來接受用戶的輸入,對不同的輸入進行不同的操作。所接受的用戶指令有:退出(exit),查看大部分寄存器內(nèi)容(regs),繼續(xù)執(zhí)行被調(diào)試進程(continue),列出子進程代碼,查看特定內(nèi)存處的內(nèi)容(examin),添加斷點,查看一共有多少條指令(insc),單步調(diào)試(step),列出當前rip指向的指令(list)。

模塊劃分

??????1,獲取用戶輸入的待調(diào)試程序,(這里輸入的程序要求:不能帶有參數(shù),單線程,且需要在當前目錄下)
??????2,fork出一個子進程,采用execvp函數(shù)來裝入待調(diào)試程序。
??????3,父進程實現(xiàn)具體的操作來對子進程進行調(diào)試工作。

完整代碼

??????ptrace.c:

#include<stdio.h> #include<unistd.h> #include<sys/types.h> #include<sys/ptrace.h> #include<sys/user.h>//存放結(jié)構(gòu)體 user_regs_struct 信息 #include<signal.h> #include<string.h> #include<stdlib.h> #include<sys/wait.h>int main() {const int SIZE=32;//表明用戶輸入的最大長度char* arg[2];for(int i=0;i<2;i++)arg[i]=NULL;printf("Input the program name as the tracee.(no parameter and in the pwd.)\n");arg[0]=(char*)malloc(SIZE);fgets(arg[0],SIZE-1,stdin);//獲得用戶輸入,包括'\n'int len=strlen(arg[0]);arg[0][len-1]='\0';//將最后的'\n'變?yōu)?#39;\0'pid_t pid = fork();if (pid < 0)printf("error in fork().\n");else if (pid == 0){//printf("my pid is %d,I will be traced.\n\n", getpid());if(ptrace(PTRACE_TRACEME,0,0,0)<0){printf("error in ptrace().\n");exit(-2);}if (execvp(arg[0],arg))//執(zhí)行用戶命令,收到系統(tǒng)產(chǎn)生的SIGTRAP信號。{printf("error in execvp().\n");free(arg[0]);exit(-1);}else{free(arg[0]);exit(0);}}else{int status;int i,j,k;char instruction[SIZE];struct user_regs_struct regs;//存儲子進程當前寄存器的值int count;//memset(instruction,0,SIZE*sizeof(char));const char* e="exit";const char* r="regs";const char* c="continue";const char* l="list";const char* ic="insc";const char* x="examin";//默認查看當前內(nèi)存地址之后40個字const char* s="step";printf("Please wait...\n");sleep(0.5);while(1){//wait(&status);//printf("The signal child got: %s\n",strsignal(WSTOPSIG(status)));printf("ptrace> ");fgets(instruction,SIZE-1,stdin);while(instruction[i]!='\n')++i;instruction[i]='\0';if(strcmp(e,instruction)==0)//退出操作{ptrace(PTRACE_KILL,pid,NULL,NULL);break;}else if(strcmp(c,instruction)==0){ptrace(PTRACE_CONT,pid,NULL,NULL);sleep(1);break;}else if(strcmp(r,instruction)==0)//查詢寄存器內(nèi)容操作{ptrace(PTRACE_GETREGS,pid,NULL,&regs);printf("rax %llx\nrbx %llx\nrcx %llx\nrdx %llx\nrsi %llx\n""rdi %llx\nrbp %llx\nrsp %llx\nrip %llx\neflags %llx\n""cs %llx\nss %llx\nds %llx\nes %llx\n",regs.rax,regs.rbx,regs.rcx,regs.rdx,regs.rsi,regs.rdi,regs.rbp,regs.rsp,regs.rip,regs.eflags,regs.cs,regs.ss,regs.ds,regs.es);}else if(strcmp(l,instruction)==0){// long 和long long在此都是8字節(jié)。ptrace(PTRACE_GETREGS,pid,NULL,&regs);long data=ptrace(PTRACE_PEEKTEXT,pid,regs.rip,NULL);printf("The rip is %llx, The present instruction is: %lx\n",regs.rip,data);}else if(strcmp(ic,instruction)==0){count=0;while(1){wait(&status);if(WIFEXITED(status)){printf("count is %d\n",count);break;}ptrace(PTRACE_SINGLESTEP,pid,NULL,NULL);count++;}}else if(strcmp(x,instruction)==0){printf("Please input 1,if you want to watch the 40 bytes after rip\n");printf("Please input 2,if you want to watch the 40 bytes atrer the memory you will assign\n");char ch;ch=getc(stdin);getchar();unsigned char temp[40];union u{unsigned long data;unsigned char t[8];}d; //這里采用union聯(lián)合類型來實現(xiàn)ptrace(PTRACE_GETREGS,pid,NULL,&regs);if(ch=='1'){for(int i=0;i<5;++i){d.data=ptrace(PTRACE_PEEKTEXT,pid,regs.rip+i*8,NULL);memcpy(temp+8*i,d.t,8);}printf("The current location is %llx:\n The 40 bytes later are : ",regs.rip);}else if(ch =='2'){printf("The current rip is %llx: ,Please input offset: \n",regs.rip);//偏移單位為字節(jié)數(shù),正負均可。int offset;scanf("%d",&offset);getchar();for(int i=0;i<5;++i){d.data=ptrace(PTRACE_PEEKTEXT,pid,regs.rip+offset+i*8,NULL);memcpy(temp+8*i,d.t,8);}printf("The current location is %llx:\n The 40 bytes later are : ",regs.rip);}count=0;for(int i=0;i<40;i++){printf("%.2x",temp[i]);count++;if(count==8){count=0;printf(" ");}}printf("\n");}else if(strcmp(s,instruction)==0){wait(&status);if(WIFEXITED(status)){printf("Done.\n");break;}ptrace(PTRACE_SINGLESTEP,pid,NULL,NULL);}else{printf("Invalid instruction!\n");}}wait(&status);printf("\nchild process quit with status %d.\n", status);}return 0;}

??????test.c:

#include<stdio.h> #include<unistd.h> int main() {int i,j;i=5;j=10;return 0; }

程序運行及結(jié)果

1,編譯兩個.c文件:gcc –o p ptrace.c gcc –g test.c –o test
2,最終,父進程支持的命令操作如下:
??????a, exit 終止被調(diào)試程序(子進程),并且父進程退出
??????b, continue 恢復子進程的執(zhí)行, 父進程稍后退出
??????c, regs 查看當前子進程的寄存器內(nèi)容(很少用到的就不列出了)
??????d, list 顯示當前rip的內(nèi)容,以及需要待執(zhí)行的指令(更好的說法應(yīng)該是:rip指向的內(nèi)存處的8B長度的值)。
??????e, step 單步執(zhí)行
??????f, examin 查看當前rip所指向的內(nèi)存后面40B長的內(nèi)容。按照字節(jié)來顯示。
??????g, insc 查看子進程需要單步執(zhí)行的步數(shù)。

1,運行程序(此時被調(diào)試程序相當于進入斷點,等待輸入命令)

2,查看寄存器內(nèi)容 regs list 和step命令

3,查看寄存器rip所指向地址后40個字節(jié)的內(nèi)容。 命令 examin 后輸入 1 即可。
查看指定內(nèi)存處內(nèi)容,內(nèi)存地址不好輸入,在此選擇相對于rip的偏移來間接查看任意內(nèi)存處的值。 命令 examin 后, 輸入 2 ,輸入相對于rip的偏移地址,
比如:下面分別輸入+20 和 -20,內(nèi)存處的內(nèi)容如下圖所示。并且通過 命令 continue 讓被調(diào)試程序繼續(xù)執(zhí)行。此時退出狀態(tài)為0

4,命令insc 會將被調(diào)試程序單步運行一遍,算出所需的機器指令數(shù)。 再通過命令 exit 退出。

總結(jié)

以上是生活随笔為你收集整理的linux(4)-Ptrace 系统调用的使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。