web-dev-qa-db-ja.com

ルート以外の権限でトレー​​スされたコマンドをdtraceで実行するにはどうすればよいですか?

OS Xにはlinuxのstraceがありませんが、dtraceがあり、はるかに優れているはずです。

ただし、個々のコマンドで単純なトレースを実行する機能がありません。たとえば、Linuxではstrace -f gcc hello.cすべてのシステムコールをキャプチャします。これにより、プログラムをコンパイルするためにコンパイラが必要とするallファイル名のリストが表示されます(優れた memoize スクリプトはこのトリックに基づいて構築された)

Macにポートメモ化したいので、straceが必要です。実際に必要なのは、gccが読み書きするファイルのリストなので、必要なのはtrussだけです。案の定、dtruss -f gcc hello.cと多少同じ機能を取得しますが、コンパイラーはルート特権で実行されます。これは明らかに望ましくありません(巨大なセキュリティリスクを除いて、1つの問題はa.outファイルは現在rootが所有しています:-)

その後、dtruss -f Sudo -u myusername gcc hello.c、これは少し間違っていると感じ、とにかく機能しません(私はa.outこの時点では常にファイル、理由は不明)

その長い話はすべて私の元の質問を動機づけようとします:dtraceがLinuxでstraceが行うように、通常のユーザー権限でコマンドを実行するにはどうすればよいですか?

編集:これを行う方法を疑問に思っているのは私だけではないようです:質問 #1204256 は私のものとほとんど同じです(そして同じ次善のSudo回答があります:-)

57
Gyom

あなたの質問に対する答えではなく、知っておくべきこと。 OpenSolarisはこの問題を(部分的に)「特権」で解決しました- このページ を参照してください。 OpenSolarisであっても、追加の権限がなければ、ユーザーが自分のプロセスを破棄することはできません。その理由は、dtraceの動作方法です。これにより、カーネル内のプローブが有効になります。したがって、非特権ユーザーがカーネルをプローブできるようにすると、ユーザーは多くの不要なことを実行できることになります。キーボードドライバーでプローブを有効にして他のユーザーのパスワードを盗聴!

5
netcharmer

最も簡単な方法は、Sudoを使用することです。

Sudo dtruss -f Sudo -u $USER whoami

他の解決策は、最初にデバッガを実行し、新しい特定のプロセスを監視することです。例えば。

Sudo dtruss -fn whoami

次に、別のターミナルで単に実行します:

whoami

そのような単純な。

マニュアルで見つけることができるよりトリッキーな引数:man dtruss


または、実行中のユーザープロセスにdtrussをアタッチすることもできます。 Macの場合:

Sudo dtruss -fp PID

またはstraceを使用してLinux/Unixで同様:

Sudo strace -fp PID

別のハックトリックは、コマンドを実行し、その直後にプロセスにアタッチすることです。ここではいくつかの例を示します。

Sudo true; (./Pages &); Sudo dtruss -fp `pgrep -n -x Pages`
Sudo true; (sleep 1 &); Sudo dtruss -fp `pgrep -n -x sleep`
Sudo true; (tail -f /var/log/system.log &); Sudo dtruss -fp `pgrep -n -x tail`

注意:

  • 最初のSudoは、最初の実行時にパスワードをキャッシュするためのものです。

  • このトリックは、ls, dateのようなクイックコマンドラインでは機能しません。デバッガーがプロセスに接続するまでには時間がかかるため、

  • コマンドを2か所に入力する必要があります。

  • &を無視して、プロセスをバックグラウンドで実行できます(既に実行している場合)。

  • デバッグが終了したら、バックグラウンドプロセスを手動で強制終了する必要があります(例:killall -v tail

44
kenorb

dtrussへの-n引数は、dtrussを待機させ、-nへの引数に一致するプロセスを調べます。 -fオプションは、-nで一致したプロセスから分岐されたプロセスを追跡するために引き続き機能します。

つまり、特権を持たないユーザーとして実行しているプロセス(引数としてwhoamiだとしましょう)を破棄する場合は、次の手順に従います。

  1. ルートシェルを開く
  2. dtruss -fn whoami を実行します
    • これは「whoami」という名前のプロセスが存在するまで待機します。
  3. 非特権シェルを開く
  4. whoami を実行します
    • これは正常に実行されて終了します
  5. Dtrussウィンドウでシステムコールトレースを観察します
    • dtrussはそれ自体では終了しません—一致するプロセスを待機し続けます—終わったら、それから抜け出します

この回答は@kenorbの応答の後半部分を複製したものですが、ファーストクラスの回答に値するものです。

8
wfaulk

Dtrussをstraceのように非侵襲的にできるかどうかはわかりません。

「Sudo [ルートへ] dtruss Sudo [非ルートへ戻る] cmd」の変種は、いくつかの簡単なテストでうまくいくようです:

Sudo dtruss -f su -l `whoami` cd `pwd` && cmd....

外側のSudoはもちろん、dtrussはrootとして実行されます。

内側のsuが私に戻ってきました。-lを使用すると、環境が適切に再作成されます。その時点で、開始した場所にcdで戻す必要があります。

「su -l user」は「sudo -u user」よりも、そのユーザーが通常取得する環境にしたい場合に適していると思います。それがログイン環境になります。代わりに、2つのユーザーの変更を介して環境に継承させる良い方法があるかどうかはわかりません。

あなたの質問では、醜さ以外の「Sudo dtruss Sudo」の回避策に関してあなたが持っていたもう1つの不満は、「現時点ではa.outファイルがまったく表示されない、理由がわからない」というものでした。理由もわかりませんが、私の小さなテストスクリプトでは、 "Sudo dtruss Sudo"バリアントもテスト出力ファイルへの書き込みに失敗し、上記の "Sudo dtruss su"バリアントは出力ファイルを作成しました。

5
metamatt

OS Xは、必要なstraceのすべての機能を複製するためのdtraceの使用をサポートしていないようです。ただし、適切なsyscallのラッパーを作成することをお勧めします。 DYLD_INSERT_LIBRARIES は、少しハッキングしたい環境変数のようです。これは基本的にLinuxのLD_PRELOADと同じです。

ライブラリー関数のオーバーライドを行うはるかに簡単な方法は、DY​​LD_INSERT_LIBRARIES環境変数(LinuxのLD_PRELOADに類似)を使用することです。概念は単純です。ロード時に、動的リンカー(dyld)は、実行可能ファイルをロードする前に、DYLD_INSERT_LIBRARIESで指定された動的ライブラリをロードします。ライブラリ関数の関数と同じ関数に名前を付けることにより、元の関数への呼び出しを上書きします。

元の関数もロードされ、dlsym(RTLD_NEXT、“ function_name”);を使用して取得できます。関数。これにより、既存のライブラリ関数をラップする簡単な方法が可能になります。

による Tom Robinson によると、DYLD_FORCE_FLAT_NAMESPACE=1も設定する必要がある場合があります。

fopenのみをオーバーライドする元の例(lib_overrides.c)のコピー:

#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>

// for caching the original fopen implementation
FILE * (*original_fopen) (const char *, const char *) = NULL;

// our fopen override implmentation
FILE * fopen(const char * filename, const char * mode)
{
    // if we haven’t already, retrieve the original fopen implementation
    if (!original_fopen)
        original_fopen = dlsym(RTLD_NEXT, "fopen");

    // do our own processing; in this case just print the parameters
    printf("== fopen: {%s,%s} ==\n", filename, mode);

    // call the original fopen with the same arugments
    FILE* f = original_fopen(filename, mode);

    // return the result
    return f;
}

使用法:

$ gcc -Wall -o lib_overrides.dylib -dynamiclib lib_overrides.c
$ DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES=lib_overrides.dylib command-to-test
3

免責事項:これは@kenorbの answer から派生しています。ただし、PIDはexecnameよりも具体的です。また、存続期間の短いプロセスを開始する前にDTraceを待機させることもできます。

これは少しレースコンディションですが…

cat /etc/hostsをトレースするとします。

Sudo true && \
(sleep 1; cat /etc/hosts) &; \
Sudo dtrace -n 'syscall:::entry /pid == $1/ {@[probefunc] = count();}' $!; \
kill $!

時間依存の処理を開始する前に、Sudo trueを使用して、Sudoのパスワードプロンプトを必ずクリアしてください。

バックグラウンドプロセスを開始します(「1秒待ってから、何か面白いことをします」)。その間、DTraceを起動します。バックグラウンドプロセスのPIDを$!にキャプチャしたので、それを引数としてDTraceに渡すことができます。

kill $!は、DTraceを閉じた後に実行されます。 catの例では必要ありません(プロセスは自動的に終了します)が、pingのような長時間実行されているバックグラウンドプロセスを終了するのに役立ちます。 -p $!をDTraceに渡すことは、これを行う好ましい方法ですが、macOSでは明らかにコード署名された実行可能ファイルが必要です。


他にできることは、コマンドを別のシェルで実行し、そのシェルをスヌープすることです。私の answer を参照してください。

2
Birchlabs

Dtraceを使用するdtrussはsu特権を必要とするため、通常のユーザーとして実行する方法を知りません。

しかし、あなたが探していたコマンドの代わりに

dtruss -f Sudo -u myusername gcc hello.c

です

Sudo dtruss -f gcc hello.c

パスワードを入力すると、dtrussが実行され、dtraceがSudo権限を実行して、トレースとa.outファイルを取得します。

申し訳ありません、それ以上のサポートはありませんでした。

1
aqua