9

ptraceプロセスのシステムコールを追跡するために使用しています。プロセスをフォークした後、プロセスのPTRACE_TRACEMEトレースを開始するために使用します。コードは次のようになります。

while (true) {
    int status;
    int gotPid;
    gotPid = waitpid(pid, &status, 0);

    if (WIFEXITED(status) || WIFSIGNALED(status)) {
        break;
    }

    if (WIFSTOPPED(status)) {
        handleTrace();
    }
}

次にhandleTrace、次のような関数があります。

long syscall;
syscall = ptrace(PTRACE_PEEKUSER,
     pid, 8 * ORIG_RAX, NULL);

// do something with the syscall

// continue program
ptrace(PTRACE_SYSCALL, pid, NULL, NULL);

これで問題ありませんが、プログラムが分岐する (または新しいスレッドを作成する) 場合は、トレース対象のプロセスが作成する子プロセス (およびプロセスによって作成されるスレッド) もトレースしたいと考えています。PTRACE_O_TRACEFORK、 、PTRACE_O_TRACEVFORKおよびを使用して実行できることは知っていますがPTRACE_O_TRACECLONEmanドキュメントから、それがどのように正確に実行されるかを理解するのは非常に困難です。これについていくつかの例が必要です。

編集:

ここで同様の質問を見つけました:マルチスレッドアプリケーションをptraceする方法は? 以下のコードで試してみました。このコードは、開始されたプロセスのシステム コールを追跡し、フォークされたプロセスも追跡することになっています。fork()親プロセスでa の後に実行されます (子は aPTRACE_TRACEMEおよび an を呼び出しますexec())。

編集2:

コードにさらにいくつかの変更を加え、さらに進歩しました。

long orig_eax;
int status;
int numPrograms = 1;

while(1) {
    int pid;
    CHECK_ERROR_VALUE(pid = waitpid(-1, &status, __WALL));
    std::cout << pid << ": Got event." << std::endl;
    if(WIFEXITED(status) || WIFSIGNALED(status)) {
        std::cout << pid << ": Program exited." << std::endl;
        if (--numPrograms == 0) {
            break;
        }
        continue;
    }

    if (status >> 16 == PTRACE_EVENT_FORK || status >> 16 == PTRACE_EVENT_VFORK ||
            status >> 16 == PTRACE_EVENT_CLONE) {
        int newpid;
        CHECK_ERROR_VALUE(ptrace(PTRACE_GETEVENTMSG, child, NULL, (long) &newpid));
        std::cout << pid << ": Attached to offspring " << newpid << std::endl;
        boost::this_thread::sleep(boost::posix_time::millisec(100));
        CHECK_ERROR_VALUE(ptrace(PTRACE_SETOPTIONS,
                newpid, NULL, PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE));
        CHECK_ERROR_VALUE(ptrace(PTRACE_SYSCALL, newpid, NULL, NULL));
        ++numPrograms;
    } else {
        CHECK_ERROR_VALUE(ptrace(PTRACE_SETOPTIONS,
                pid, NULL, PTRACE_O_TRACEFORK  | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE));
        CHECK_ERROR_VALUE(orig_eax = ptrace(PTRACE_PEEKUSER,
                pid, 8 * ORIG_RAX, NULL));
        std::cout << pid << ": Syscall called: " <<
                SyscallMap::instance().get().right.at(orig_eax) <<
                std::endl;
        CHECK_ERROR_VALUE(ptrace(PTRACE_SYSCALL,
                pid, NULL, NULL));
    }

}

CHECK_ERROR_VALUE結果コードをチェックし、その中に記述された例外をスローする単なるマクロですerrno

どうやら、フォーク/クローンのイベントを取得すると、新しいプロセスはまだ存在せず、ptrace しようとすると「プロセスが存在しません」というエラー メッセージが表示されます。新しいプロセスを ptrace しようとする前にスリープ状態にすると、エラー メッセージは表示されません。プログラムが fork/clone のポイントに到達すると、新しいプロセスのトレースが開始されますが、親プロセスの syscall の戻りポイントには到達しませんclone()。つまり、子は正常に終了しますが、親はその分岐点。

4

1 に答える 1

1

Strace はこれを行い、その README-linux-ptrace ファイルにはこの件に関する情報が含まれています。

https://github.com/strace/strace/blob/master/README-linux-ptrace

このプラットフォームのカーネル バグについて不平を言っているように見えるので、YMMV.

このコードは、子の pid を取得する方法を説明しています。ただし、呼び出すバイナリに setuid または setgid ビットが設定されているため、子が別のユーザー ID を取得する可能性があります。したがって、答えは、子 PID で ptrace を呼び出し、アクセスできるかどうかを確認することです。

関連するセクションは次のとおりです。

PTRACE_EVENT停止は、waitpid が WIFSTOPPED(status) == true, で返されるため、トレーサーによって監視されWSTOPSIG(status) == SIGTRAPます。追加ビットは、ステータス ワードの上位バイトに設定されます。値((status >> 8) & 0xffff) は になります(SIGTRAP | PTRACE_EVENT_foo << 8)。次のイベントがあります。

  • PTRACE_EVENT_VFORK- vfork/clone+CLONE_VFORK から戻る前に停止します。この後も tracee を継続すると、child が exit/exec するのを待ってから実行を継続します (IOW: vfork での通常の動作)。
  • PTRACE_EVENT_FORK- fork/clone+SIGCHLD から戻る前に停止
  • PTRACE_EVENT_CLONE- クローンから戻る前に停止
  • PTRACE_EVENT_VFORK_DONE- vfork/clone+CLONE_VFORK から戻る前に停止しますが、vfork の子プロセスが終了または実行によってこのトレースのブロックを解除した後。

上記の 4 つの停止すべてについて: 停止は、新しく作成されたスレッドではなく、親で発生します。PTRACE_GETEVENTMSG新しいスレッドの tid を取得するために使用できます。

于 2012-11-25T08:20:19.467 に答える