2012-06-18 46 views
5

Tôi đang cố gắng mã hóa một chương trình theo dõi chính nó cho các cuộc gọi hệ thống. Tôi đang gặp khó khăn khi thực hiện công việc này. Tôi đã thử gọi một ngã ba() để tạo ra một thể hiện của chính nó (mã), sau đó theo dõi quá trình con kết quả.Cách theo dõi quy trình cho cuộc gọi hệ thống?

Mục tiêu là cho quá trình cha mẹ trả về chỉ mục của mọi cuộc gọi hệ thống được thực hiện bởi quá trình con và xuất nó ra màn hình. Bằng cách nào đó nó không hoạt động như kế hoạch.

Đây là mã:

#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; 
} 

Mã này được dựa trên mã này:

#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; 
} 

Kết quả của một này là:

The child made a system call 11 

mà là chỉ số cho cuộc gọi hệ thống exec.

Theo các trang người đàn ông cho 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. 

Con đường tôi hiểu nó là mỗi khi một cuộc gọi hệ thống được gọi bởi một chương trình sử dụng, hạt nhân đầu tiên sẽ kiểm tra nếu quá trình này đang được truy tìm trước khi thực hiện thường trình gọi hệ thống và tạm dừng quá trình đó bằng tín hiệu và trả về điều khiển cho phụ huynh. Đó không phải là một sự thay đổi trạng thái rồi sao?

+2

Chăm sóc để xây dựng? Bạn mong đợi điều gì sẽ xảy ra và điều gì thực sự xảy ra? Vui lòng chỉnh sửa câu hỏi để thêm nó, không phải là nhận xét. –

+7

Bên cạnh đó, điều đầu tiên bạn làm trong tiến trình cha mẹ là gọi 'wait'. Hàm này thực hiện chính xác điều đó, đợi cho đến khi quá trình con hoàn thành, có nghĩa là cuộc gọi 'ptrace' cố gắng theo dõi một tiến trình không còn tồn tại nữa. –

+0

[Tham khảo] [1] Tôi nghĩ, điều đó có thể hữu ích. [1]: http://stackoverflow.com/questions/6468896/why-is-orig-eax-provided-in-addition-to-eax –

Trả lời

0

Trong cha mẹ bạn có bao nhiêu cuộc gọi bạn muốn theo dõi? Nếu bạn muốn nhiều hơn một bạn sẽ cần một số loại vòng lặp.

Lưu ý dòng trong ví dụ này, điều quan trọng là:

ptrace(PTRACE_TRACEME, 0, NULL, NULL); 

Nhìn vào man page các Childs cần phải hoặc là làm một PTRACE_TRACEME và exec hoặc cha mẹ cần theo dõi sử dụng PTRACE_ATTACH. Tôi không thấy mã nào trong mã của bạn:

Cha mẹ có thể bắt đầu một dấu vết bằng cách gọi fork (2) và theo sau (thường) bởi người thực hiện (3). Ngoài ra, phụ huynh có thể bắt đầu theo dõi một quá trình hiện có bằng cách sử dụng PTRACE_ATTACH.

+0

@ 1der, ý kiến ​​của bạn là gì? –

+0

Tôi quên phần TRACEME nhưng vẫn không hoạt động. – 1der

2

bạn về cơ bản đang cố gắng viết nhị phân strace trong linux, dấu vết hệ thống các cuộc gọi của quá trình. Linux cung cấp ptrace (2) gọi hệ thống cho việc này. cuộc gọi hệ thống ptrace mất 4 cuộc tranh luận và tranh luận đầu tiên cho biết những gì bạn cần làm. Hệ điều hành giao tiếp với quá trình cha mẹ với các tín hiệu và quá trình con được dừng lại bằng cách gửi SIGSTOP. rộng rãi bạn cần phải làm theo các bước dưới đây.

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) 

    } 

Lưu ý việc đọc và diễn giải đăng ký sẽ tùy thuộc vào kiến ​​trúc của bạn. Đoạn mã trên chỉ là một ví dụ để làm đúng, bạn cần phải đào sâu hơn. hãy xem mã strace để hiểu thêm.

+0

Theo các trang của người đàn ông, chỉ có đối số đầu tiên của một PTRACEME được sử dụng và phần còn lại bị bỏ qua. Có gì với (char *) 1? – 1der

+0

có bạn đúng addr được bỏ qua, chỉnh sửa nó. –

5

Vấn đề là khi trẻ gọi ptrace(TRACEME) nó tự thiết lập để truy tìm nhưng không thực sự dừng - nó tiếp tục cho đến khi nó gọi exec (trong trường hợp nó dừng bằng SIGTRAP) hoặc nó nhận được một số khác tín hiệu. Vì vậy, để bạn có cha mẹ nhìn thấy những gì nó KHÔNG CÓ một cuộc gọi exec, bạn cần phải sắp xếp cho đứa trẻ để nhận được một tín hiệu.Cách dễ nhất để thực hiện điều đó có lẽ là để trẻ gọi raise(SIGCONT); (hoặc bất kỳ tín hiệu nào khác) ngay sau khi gọi ptrace(TRACEME)

Bây giờ, bạn chỉ cần đợi (một lần) và giả sử rằng đứa trẻ hiện đang bị dừng tại cuộc gọi hệ thống . Điều này sẽ không xảy ra nếu nó dừng lại ở tín hiệu, vì vậy bạn cần gọi số wait(&status) để nhận trạng thái con và gọi WIFSTOPPED(status)WSTOPSIG(status) để xem tại sao nó dừng lại. Nếu nó dừng lại do một syscall, tín hiệu sẽ là SIGTRAP.

Nếu bạn muốn xem nhiều cuộc gọi hệ thống trong ứng dụng khách, bạn sẽ cần thực hiện tất cả điều này trong một vòng lặp; cái gì đó như:

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 
} 

Lưu ý rằng nó sẽ dừng lại hai lần cho mỗi cuộc gọi hệ thống - một lần trước khi cuộc gọi hệ thống và lần thứ hai chỉ sau khi cuộc gọi hệ thống hoàn tất.

0

Chỉ cần đặt lại với nhau những gì Chris Dodd cho biết: "Bằng cách nào đó nó không hoạt động theo đúng kế hoạch"

#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; 
} 
Các vấn đề liên quan