gdb hacks - ターゲットプロセス内の関数を呼び出す
gdb は ptrace(2) システムコールの便利なフロントエンドインタフェースとして使えます。特にターゲットプロセス内の関数を式の中で呼ぶことができる機能は強力で、ptrace の attach 機能と組み合わせて使えば、動作中のプロセスに対してちょっかいを出すことが簡単にできます。
単純な活用例をいくつか紹介します。以下は i386 の Debian GNU/Linux (sid) のシステムで実験しました。gdb は 6.4 より前のバージョンではうまく動かない例があります。
% ps x | grep firefox
3616 ? Rl 19:40 /usr/lib/firefox/firefox-bin -a firefox
% gdb -q -p 3616
(gdb) p chdir("/")
[Switching to Thread -1221168480 (LWP 3616)]
$1 = 0
(gdb) detach
Detaching from program: /usr/lib/firefox/firefox-bin, process 3616
(gdb)
この例は gdb -p で attach したターゲットプロセス内で chdir(2) システムコールを呼び出しています。デーモンや X クライアントといったバックグラウンドプロセスがマウントポイントの下にいるおかげで、device is busy で umount できないといった状況で有用です。
ファイル操作も比較的簡単に行うことができます。まず cat を起動しておき、別のシェルでは以下のように gdb を起動して cat プロセスをいじってみます。
% gdb -q -p $(pidof cat)
(gdb) p write(1, "hoge", 4)
$1 = 4
(gdb) p open("/etc/passwd", 0) ← 0 は O_RDONLY です
$2 = 3
(gdb) p dup2(3, 0)
$3 = 0
(gdb) c
Continuing.
Program exited normally.
(gdb)
cat プロセスが hoge という文字列と /etc/passwd ファイルの内容を出力していれば成功です。根性があればソケットを使って新たに TCP コネクションを張ることも可能でしょう。
execlp(2) を呼んで、途中から別のコマンドにしてしまうこともできます。
(gdb) p execlp("ls", "ls", "/", 0)
Program exited normally.
The program being debugged stopped while in a function called from GDB.
When the function (execlp) is done executing, GDB will silently
stop (instead of continuing to evaluate the expression containing
the function call).
(gdb)
"ls /" が実行されたと思います。最後のメッセージは、gdb が想定した場所に制御が戻ってこなかったことを警告しています。
この他にもたとえば、ターゲットプロセスが libdl をリンクしているプログラムなら、dlopen(3) を呼んで共有オブジェクトをリンクして使うことができます。しかし libdl がリンクされていなかった場合 dlopen() は使えず、gdb にもリンカの機能はないので、代わりに livepatch のようなリンカ機能を備えたプログラムに頼ることになるでしょう。
なお上記用例はターゲットプロセスの状態によっては期待どおりに動作しないことがままあります。個人的には live patching はまともに使うには難易度が高すぎる無謀なテクニックだと思っていますが、たとえばバグって暴走したネットワークデーモンプロセスのファイル記述子を閉じることなく (コネクションを維持したまま)、新しいプロセスに引き継ぐような用途 (live executing?) であれば、成功確率は高くそれなりに実用になるかもしれません。
つづく。
カテゴリ
gdb hacksトラックバック(0)
このブログ記事を参照しているブログ一覧: gdb hacks - ターゲットプロセス内の関数を呼び出す
このブログ記事に対するトラックバックURL: http://www.keshi.org/mt/mt-tb.cgi/31

コメントする