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

これは、exec システム コールのインデックスです。

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.

私が理解している方法は、システムコールがユーザープログラムによって呼び出されるたびに、カーネルはシステムコールルーチンを実行する前にプロセスがトレースされているかどうかを最初に検査し、シグナルでそのプロセスを一時停止し、親に制御を返すということです. それはすでに状態変化ではないでしょうか?

4

4 に答える 4

6

問題は、子プロセスが呼び出さptrace(TRACEME)れると、トレースのために自身をセットアップしますが、実際には停止しないことです。子プロセスは、呼び出すまでexec(この場合は SIGTRAP で停止します)、または他のシグナルを受け取るまで続行します。したがって、親が exec 呼び出しなしで何をするかを確認するには、子がシグナルを受信するように手配する必要があります。これを行う最も簡単な方法は、呼び出しraise(SIGCONT);の直後に子呼び出し (またはその他のシグナル)を行うことです。ptrace(TRACEME)

ここで、親で (1 回) 待機し、子がシステム コールで停止したと想定します。これは、信号で停止した場合には当てはまらないため、代わりに呼び出し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) || WIFSIGNALED(status)) {
        // child has exited or terminated
        break;
    }
    ptrace(PTRACE_SYSCALL, 0, 0, 0);  // ignore any signal and continue the child
}

システム コールごとに 2 回停止することに注意してください。1 回目はシステム コールの前、2 回目はシステム コールの完了直後です。

于 2012-06-19T00:18:14.383 に答える
2

基本的に、プロセスのシステム コールをトレースする strace バイナリを Linux で記述しようとしています。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 コードを見てください。

于 2012-06-18T14:04:05.217 に答える
1

あなたの親では、何回の通話を監視したいですか? 複数が必要な場合は、何らかのループが必要になります。

例の行に注意してください。重要です。

ptrace(PTRACE_TRACEME, 0, NULL, NULL);

man ページを見ると、子は aPTRACE_TRACEMEと を実行するexecか、親は を使用してトレースする必要がありますPTRACE_ATTACH。あなたのコードにはどちらも表示されません:

親は fork(2) を呼び出してトレースを開始し、結果の子に PTRACE_TRACEME を実行させ、(通常は) exec(3) を実行させます。あるいは、親は PTRACE_ATTACH を使用して既存のプロセスのトレースを開始できます。

于 2012-06-18T12:55:07.920 に答える