ウェブサイト検索

strace コマンドを使った Linux システムコールの理解


デスクトップ上で実行されているすべてのプロセスは、OSとの通信にシステムコールを使用します。straceを使用すると、そのようなシステムコールを簡単に追跡できます。

Linux上で動作するプログラムがオペレーティングシステムによって管理されているリソース(ファイルの読み取り、プロセスの作成など)を使用したいとき、彼らはOSにシステムコールを行います。システムコールはカーネルレベルで動作し、必要な操作を実行した後、呼び出されたプログラムに制御を戻します。straceツールは、Linux上でこれらのシステムコールをトレースする能力を提供します。

straceコマンドの典型的な使用方法

アプリケーションのシステムコールを監視するには、以下の形式でstraceコマンドを呼び出すだけです:

strace ls /tmp

ただし、しばしばはるか以前に始まり、バックグラウンドで動作し続けるプロセスがあります。問題が発生した場合、そのようなプロセスに関連する追加情報を収集したくなるかもしれません。実行中のアプリケーションには、プロセスのIDを-pパラメータに指定することでstraceをアタッチできます。

strace -p 2759

出力 :

アプリのスレッドとフォークを追跡する

straceを使用すると、-fフラグを使用して、アプリケーションのフォークであるすべてのスレッドや他の子プロセスを確認できます。

strace -f -p 2759

出力 :

straceで特定のシステムコールを確認する

デフォルトのstrace出力は、時々かなり混雑して読みづらくなることがあります。特定のシステムコールのみを追跡したい場合は、-eパラメータを使うことができます:

strace -f -e trace=open,write,close,connect,select -p 19770

ファイル操作に関連するシステムコールのみをトレースするには、-e trace=fileを使用してください。

strace -e trace=file -p 19770

ネットワーク関連のシステムコールのみをフィルタリングするには、コマンドに-e trace=networkを指定します。

strace -e trace=network -p 19770

時間情報を秒で取得する

システムコールを出力する際には、-tパラメータを使用して、秒単位の精度で時間情報を取得できます。しかし、ほとんどの場合、その精度ではあなたのニーズには不十分です。そのような状況では、-ttパラメータを使用してマイクロ秒単位の精度で時間情報を取得できます。

strace -tt ls /tmp

システムコールに関する統計を収集する

-c パラメータを使うことで、必要に応じてシステムコールの統計を収集することができます:

strace -f -c -p 19770

ログをファイルに保存する

長時間 strace を実行して、後で結果のログをより詳しく調べたい場合は、ログを保存する必要があります。-o パラメータを使用すると、strace がログを保存するファイルを指定できます:

strace -f -o /tmp/strace.log -e trace=file ls /tmp

ptraceブロッキングプロセス

prctlシステムコールを使用することで、Linuxの任意のアプリケーションは、ptraceを使用して非ルートユーザーから制御されるのを防ぐことができます。アプリケーションがprctlを介して自己の<強>PR_SET_DUMPABLEフラグをクリアすると、ルート以外のユーザーは、アプリケーションにシグナルを送る権限があったとしても、ptraceを使用してこのアプリケーションを制御できなくなります。

この機能の最も典型的な使用例は、OpenSSH認証エージェントソフトウェアに見られます。したがって、ユーザー認証時に別のアプリケーションによる<強>ptraceを通じたアプリケーションの制御が防止されます。

ptraceとセキュリティ

従来のLinuxプロセスモデルに設定されたptrace機能のため、システムで実行するユーザーが持つすべてのソフトウェアは、それに悪意のあるコードを挿入する権限を持っています。最もシンプルなxtermツールから高度なウェブブラウザアプリケーションに至るまで、そのようなマルウェアはptraceシステムコールのおかげで、他のすべての実行中のアプリケーションを制御し、あなたが気づかないうちに重要な情報をコピーすることができます。

この状況に対応して、多くのユーザーが知らないもので、LinuxカーネルのYamaというセキュリティモジュールを用いた保護機構が開発されました。

ptraceシステムコールへの応答は、/proc/sys/kernel/yama/ptrace_scopeファイルを介して制御できます。このファイルはデフォルトで0の値を書き込みます。

以下の値が許容されます:

価値

意味

従来の動作: ptrace の権利を持つすべてのアプリケーションを確認できます。

1

制限されたptrace: アプリケーションの直接の親または、PR_SET_PTRACERオプションで許可されたデバッグアプリケーションのみが制御を持ちます。そのため、gdb program_nameやstrace program_nameの使用は引き続き機能しますが、その後、実行中のアプリケーションにアタッチすることはできなくなります。

2

システム管理者へのPtrace: 定義されたCAP_SYS_PTRACE属性を持つアプリケーションのみ、またはprctlでPTRACE_TRACEMEオプションを定義した子プロセスのみが制御可能です。

3

完全に無効: どんな状況でも ptrace は許可されません。このプロパティが一度定義されると、実行時に再度変更することはできません。

多くの開発者は、アプリケーションが自身でptraceを無効にできることを知らず、これはルートユーザーを除いてのことです。OpenSSHエージェントのようなセキュリティ関連のソフトウェアがこれらの操作を行うことはありますが、システム上で動作するすべてのソフトウェアに同様の動作を期待することは適切ではありません。

最近、一部のLinuxディストリビューションでは、上記で説明したptrace_scopeファイルのデフォルト値を1に設定し始めました。そのため、ptrace操作が制限され、システム全体でより安全な作業環境が提供されます。

例としてstraceを使用する

以下のサンプルアプリケーションをministrace.cという名前で登録してください。次に、以下のコマンドでコンパイルできます:

gcc -o ministrace ministrace.c

コード :

#include <sys/ptrace.h>
#include <sys/reg.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h> 
int wait_for_syscall (pid_t child)
 {
 int status;
 while (1) {
 ptrace(PTRACE_SYSCALL, child, 0, 0);
 waitpid(child, &status, 0);
 if (WIFSTOPPED(status) && WSTOPSIG(status) & 0x80)
 return 0;
 if (WIFEXITED(status))
 return 1;
 }
 }
 
int do_child (int argc, char **argv)
 {
 char *args [argc+1];
 memcpy(args, argv, argc * sizeof(char*));
 args[argc] = NULL;
 ptrace(PTRACE_TRACEME);
 kill(getpid(), SIGSTOP);
 return execvp(args[0], args);
 }
 
int do_trace (pid_t child)
 {
 int status, syscall, retval;
 waitpid(child, &status, 0);
 ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACESYSGOOD);
 while(1) {
 if (wait_for_syscall(child) != 0) break;
 
 syscall = ptrace(PTRACE_PEEKUSER, child, sizeof(long)*ORIG_RAX);
 fprintf(stderr, "syscall(%d) = ", syscall);
 
 if (wait_for_syscall(child) != 0) break;
 
 retval = ptrace(PTRACE_PEEKUSER, child, sizeof(long)*RAX);
 fprintf(stderr, "%d\n", retval);
 }
 return 0;
 }
int main (int argc, char **argv)
 {
 if (argc < 2) {
 fprintf(stderr, "Usage: %s prog args\n", argv[0]);
 exit(1);
 }
 pid_t child = fork();
 if (child == 0) {
 return do_child(argc-1, argv+1);
 } else {
 return do_trace(child);
 }
 }

アプリケーションをコンパイルした後、ministrace を使って任意のコマンドを実行し、その出力を確認できます:

straceは多くの目的に使用できます。

straceは、システムリソースを不必要に使用するプログラムのバグを見つけるのに役立ちます。同様に、プログラムがオペレーティングシステムリソースを使用している際に示す特性もstraceで明らかにすることができます。

straceはシステムコールを直接リスンするため、実行中のプログラムのコードがオープンかクローズかにかかわらず、ランタイムの動的挙動を明らかにすることができます。straceを使用してプログラムを起動した際にエラーが発生する理由を把握することも可能です。

同様に、straceはプログラムが予期せず終了する理由を理解するのに役立ちます。したがって、straceに慣れていることはLinuxカーネル開発やシステム管理において非常に重要です。

関連記事: