ptrace, fork, pipeの備忘録

概要

「ハロー HelloWorld」P.81で ptrace を用いた独自トレーサを作成した際に色々詰まったところがあったので備忘録として書き留めておく

独自トレーサ

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/user.h>

int main(int argc, char *argv[], char *envp[]){
    int pid, status, size, fildes[2];
    fd_set fds;
    struct timeval t;
    char buf[64];
    struct user_regs_struct regs;

    // パイプを生成する
    // fildes[0] : 読み出し, fildes[1] : 書き込み
    pipe(fildes);

    // プロセス生成
    pid = fork();

    // pid == 0 -> 子プロセス
    if(pid == 0){
        close(1);

        // 標準出力にパイプを割り当てる
        dup2(fildes[1], 1);

        // パイプを閉じる
        close(fildes[0]);
        close(fildes[1]);

        // TRACEMEリクエスト
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);

        // 引数のコマンドを実行
        execve(argv[1], argv + 1, envp);
    }

    // 親プロセス
    while(1){
        // 子プロセスのBreak待ち
        waitpid(pid, &status, 0);

        // 子プロセスが正常終了したら
        if(WIFEXITED(status)){
            break;
        }

        // ファイルディスクリプタ(FD)集合の初期化 -> fildes[0] を監視対象に
        FD_ZERO(&fds);
        FD_SET(fildes[0], &fds);

        // タイムアウト設定(0 = すぐに復帰)
        t.tv_sec = t.tv_usec = 0;

        // FD監視
        select(fildes[0] + 1, &fds, NULL, NULL, &t);

        // 利用可能(読み込みが出来る)なFDが存在する
        if(FD_ISSET(fildes[0], &fds)){
            // 子プロセスの出力読み込み
            size = read(fildes[0], buf, sizeof(buf));
            buf[size] = '\0';

            // レジスタ取得 -> 出力 -> トレース終了
            ptrace(PTRACE_GETREGS, pid, NULL, &regs);
            fprintf(stderr, "WROTE : EIP = %08x %s\n", (int)regs.eip, buf);
            ptrace(PTRACE_CONT, pid, NULL, SIGABRT);
            ptrace(PTRACE_DETACH, pid, NULL, NULL);
            break;
        }

        // 子プロセスを1ステップ実行
        ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL);
    }

    return 0;
}

 しつこいくらいにコメントを書いた。動作イメージは多分こんな感じ…だと思う。

f:id:guguru0014:20190531000440p:plain

詰まったところ

fork

プロセスを新しく生成することは分かるが、どんな動きをするのか捉える事ができなかった。

結果

  • forkした時点での親プロセスのコピーが新しく動き始める感じ。
  • 子プロセスで execve などを使って新しくコマンドを実行することができる。

fd_set

単純に知らなかった…。

結果

  • いくつかのファイルディスクリプタ(FD)を監視してくれる。
  • selectでFDに何かしらの動きがあるまで待機する事ができる。タイムアウト する時間を設定可能。0を指定する事ですぐに戻ってきてくれる。
  • FD_ISSETを使って読み込み可能 or 書き込み可能なFDがあるか調べる事ができる。

まとめ

勉強します…。

参考ページ

qiita.com

bussorenre.hatenablog.jp

www.c-lang.net

linuxjm.osdn.jp

surf.ml.seikei.ac.jp