2012-06-18 29 views
5

我正試圖編寫一個程序來追蹤系統調用。我做這項工作很困難。我試着調用一個fork()來創建自己的一個實例(代碼),然後監視生成的子進程。如何跟蹤系統調用的進程?

目標是讓父進程返回子進程所做的每個系統調用的索引並將其輸出到屏幕。不知何故,它不按計劃運作。

下面是代碼:

#include <unistd.h>  /* for read(), write(), close(), fork() */ 
#include <fcntl.h>  /* for open() */ 
#include <stdio.h> 
#include <sys/ptrace.h> 
#include <sys/reg.h> 
#include <sys/wait.h> 
#include <sys/types.h> 


int main(int argc, char *argv[]) { 
    pid_t child; 
    long orig_eax; 
    child = fork(); 

    if (0 == child) 
    { 
     ptrace(PTRACE_TRACEME, 0, NULL, NULL); 
     if (argc != 3) { 
      fprintf(stderr, "Usage: copy <filefrom> <fileto>\n"); 
      return 1; 
     } 

     int c; 
     size_t file1_fd, file2_fd; 
     if ((file1_fd = open(argv[1], O_RDONLY)) < 0) { 
      fprintf(stderr, "copy: can't open %s\n", argv[1]); 
      return 1; 
     } 

     if ((file2_fd = open(argv[2], O_WRONLY | O_CREAT)) < 0) { 
      fprintf(stderr, "copy: can't open %s\n", argv[2]); 
      return 1; 
     } 

     while (read(file1_fd, &c, 1) > 0) 
     write(file2_fd, &c, 1); 
    } 
    else 
    { 
     wait(NULL); 
     orig_eax = ptrace (PTRACE_PEEKUSER, child, 4 * ORIG_EAX, NULL); 
     printf("copy made a system call %ld\n", orig_eax); 
     ptrace(PTRACE_CONT, child, NULL, NULL); 
    }   
return 0; 
} 

該代碼是基於這樣的代碼:

#include <sys/ptrace.h> 
#include <sys/types.h> 
#include <sys/wait.h> 
#include <unistd.h> 
#include <linux/user.h> /* For constants 
           ORIG_EAX etc */ 
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; 
} 

這一個的輸出是:

The child made a system call 11 

這對於索引執行系統調用。

按照手冊頁的wait():

All of these system calls are used to wait for state changes in a child 
of the calling process, and obtain information about the child whose 
state has changed. A state change is considered to be: the child terminated; 
the child was stopped by a signal; or the child was resumed by 
a signal. 

我的理解是,每一個系統調用是由用戶程序調用時,內核將首先檢查過程是否存在方式在執行系統調用例程之前跟蹤並用信號暫停該過程並將控制返回給父系。這不會是狀態變化嗎?

+2

「不知怎的,它沒有按計劃進行工作。」謹慎闡述?你期望發生什麼,實際發生了什麼?請編輯問題以添加它,而不是作爲評論。 –

+7

此外,你在父進程中做的第一件事是調用'wait'。這個函數完全是這樣的,等到子進程結束,這意味着'ptrace'調用嘗試跟蹤一個不再存在的進程。 –

+0

[推薦] [1]我想,這可能會有所幫助。 [1]:http://stackoverflow.com/questions/6468896/why-is-orig-eax-provided-in-addition-to-eax –

回答

0

在你的父母你想監聽多少個電話?如果你想要不止一個,你將需要某種循環。

注意該示例中的線,這一點很重要:

ptrace(PTRACE_TRACEME, 0, NULL, NULL); 

望着man page孩子的需要或者做PTRACE_TRACEME和一個exec或家長需要使用PTRACE_ATTACH追查。我沒有看到無論是在你的代碼:

家長可以通過調用fork啓動跟蹤(2),並具有導致孩子做PTRACE_TRACEME,然後(通常)通過執行exec(3)。或者,父級可以使用PTRACE_ATTACH開始對現有進程的跟蹤。

+0

@ 1der,您的評論有什麼意義? –

+0

我忘了TRACEME部分,但它仍然不起作用。 – 1der

2

你基本上是試圖寫在linux strace的二進制,它跟蹤的過程的系統調用。 Linux爲此提供了ptrace(2)系統調用。 ptrace系統調用需要4個參數,第一個參數可以告訴你需要做什麼。 OS通過信號與父進程通信,並通過發送SIGSTOP來停止子進程。廣泛地說,你需要遵循以下步驟。

if(fork() == 0) 

{ 
    //child process 

    ptrace(PTRACE_TRACEME, 0,0, 0); 
    exec(...); 
} 
else 
{ 

start: 

    wait4(...); 

    if (WIFSIGNALED(status)) { 
     //done 
    } 
    if (WIFEXITED(status)) { 
     //done 
    } 
    if(flag == startup) 
    { 
     flag = startupdone; 

     ptrace(PTRACE_SYSCALL, pid,0, 0) ; 
     goto start; 
    } 
    if (if (WSTOPSIG(status) == SIGTRAP) {) { 
      //extract the register 
      ptrace(PTRACE_GETREGS,pid,(char *)&regs,0) 

    } 

注意寄存器的讀取和解釋將取決於您的架構。上面的代碼僅僅是一個例子,你需要深入挖掘。看看strace代碼以便進一步瞭解。

+0

根據手冊頁,只使用PTRACEME的第一個參數,其餘的忽略。 (char *)1是什麼? – 1der

+0

是的,你是正確的地址被忽略,編輯它。 –

5

的問題是,當孩子叫ptrace(TRACEME)將自身設置了追蹤,但實際上並沒有停止 - 它一直走,直到它調用exec(在這種情況下,它與一個SIGTRAP停止),或者獲得一些其他信號。因此,爲了讓您的家長了解它在沒有執行呼叫的情況下執行的操作,您需要安排孩子接收信號。要做到這一點,最簡單的方法可能是將孩子打電話raise(SIGCONT);(或任何其他信號)調用ptrace(TRACEME)

現在的家長,你就等着(一次),並假設孩子現在停在一個系統調用後立即。如果信號停止,情況就不會如此,因此您需要撥打wait(&status)來獲取孩子的狀態,並撥打WIFSTOPPED(status)WSTOPSIG(status)查看它爲什麼停止。如果由於系統調用而停止,信號將是SIGTRAP。

如果你想在客戶端看到多個系統調用,你需要在循環中完成所有這些操作;是這樣的:

while(1) { 
    wait(&status); 
    if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { 
     // stopped before or after a system call -- query the child and print out info 
    } 
    if (WIFEXITED(status) || WIFSIGNALLED(status)) { 
     // child has exited or terminated 
     break; 
    } 
    ptrace(PTRACE_SYSCALL, 0, 0, 0); // ignore any signal and continue the child 
} 

注意,它會兩次停止每個系統調用 - 系統調用和系統調用結束後立即進行第二次前一次。

0

只是放在一起是什麼多德說:

#include <unistd.h>  /* for read(), write(), close(), fork() */ 
#include <fcntl.h>  /* for open() */ 
#include <stdio.h> 
#include <sys/ptrace.h> 
#include <sys/reg.h> 
#include <sys/wait.h> 
#include <sys/types.h> 

int main(int argc, char *argv[]) { 
pid_t child; 
int status; 
long orig_eax; 
child = fork(); 

if (0 == child) 
{ 
    ptrace(PTRACE_TRACEME, 0, NULL, NULL); 
    raise(SIGCONT); 
    if (argc != 3) { 
     fprintf(stderr, "Usage: copy <filefrom> <fileto>\n"); 
     return 1; 
    } 

    int c; 
    size_t file1_fd, file2_fd; 
    if ((file1_fd = open(argv[1], O_RDONLY)) < 0) { 
     fprintf(stderr, "copy: can't open %s\n", argv[1]); 
     return 1; 
    } 

    if ((file2_fd = open(argv[2], O_WRONLY | O_CREAT)) < 0) { 
     fprintf(stderr, "copy: can't open %s\n", argv[2]); 
     return 1; 
    } 

    while (read(file1_fd, &c, 1) > 0) 
     write(file2_fd, &c, 1); 
} 
else 
{ 
    while(1){ 
     wait(&status); 
     if(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP){ 
      orig_eax = ptrace(PTRACE_PEEKUSER, child, sizeof(long) * ORIG_EAX, NULL); 
      printf("copy made a system call %ld\n", orig_eax); 
     } 
     if(WIFEXITED(status) || WIFSIGNALED(status)){ 
      break; 
     } 

     ptrace(PTRACE_SYSCALL, child, 0, 0); 
    }   
} 
return 0; 
}