##プロセスアタッチ系の話
この記事は OIC ITCreate Club Advent Calendar 2015 11日目(12/11)の記事です。
誰も書かなそうだったので放置してたやつをせっかくなので11日目ということで。
人が動かしてるシェルとかプログラムとかにイタズラしたい、とか
バックグラウンドで動かしちゃってるプログラムの入出力を見たい、とか
そういうとき便利そうなやつをまとめてみましt
-
ptrace
ptrace()システムコールで頑張る方法。
- 別のプロセスの監視、制御(メモリやレジスタを見たり、書き込んだり)が出来る。
- デバッガを実装するときに使う。
- 使えるとわりと便利。
軽い説明
#include <sys/ptrace.h>
sys/ptrace.hをインクルード
long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
requestのところにptraceに実行させる命令を入れて使用する。
よく使いそうな例
-
PTRACE_TRACEME
このプロセスを親プロセスにトレースさせる。
ptrace(PTRACE_TRACEME, 0, 0, 0); //request以外の引数は無視
使い方として、親プロセスでfork(2)して、生成された子プロセスでPTRACE_TRACEMEをし、exec(3)をしていく。(らしい このプロセスがexecve(2)を呼び出す度にSIGTRAPが親に行く。
今回は使わない。省略。
-
PTRACE_ATTACH
指定したpidにアタッチする。 アタッチしたプロセスは子プロセスとしてトレース出来る。
一番使いそうな感じ
ptrace(PTRACE_ATTACH, pid, 0, 0); //or ptrace(PTRACE_ATTACH, pid, NULL, NULL); //or ptrace(PTRACE_SEIZE, pid, 0, PTRACE_O_flags);
-
需要なさそうな説明
PTRACE_ATTACH命令はプロセスにSIGSTOPシグナルを送って、プログラムを一時停止させる。子プロセスはPTRACE_TRACEMEをしたかのように振る舞う。呼び出し元プロセスは子プロセスの実際の親として表示される。でもgetppid(2)したときには元の親プロセスのPIDが返される。
停止を待つ場合はwait(2)を使用する。
-
PTRACE_DETACH
停止した子プロセスを再開する。
そのプロセスからの分離を行い、PTRACE_ATTACHでの親の切り換えによる効果とPTRACE_TRACEMEの効果を取り消す。(PTRACE_CONTを使う場合は分離のみ)
Linuxだとトレースされている子プロセスはこれでdetach出来る。
-
PTRACE_PEEKTEXT
メモリのaddrの位置を参照
-
PTRACE_POKETEXT
dataをメモリのaddrの位置に書き込む。
ここがとても参考になった
他面白そうなのはこちらを見て下さい。
-
python-ptrace
pythonのptraceライブラリのバインディング
便利そう(使ってない
-
strace
システムコールをトレース出来る。
ptraceで使って作ってる。
使い方は
strace -p PID
とかで別プロセスにアタッチして使う。
簡単な使い方
testユーザーが実行しているbashの標準入出力とか見る。
-
testユーザーの実行しているbashのpidを調べる
root@test:~# ps au | grep test.*bash
test 15655 0.1 0.4 22012 5052 pts/3 S+ 10:47 0:00 -bash root 15683 0.0 0.1 11464 1772 pts/0 S+ 10:47 0:00 grep test.*bash
``` pidは15655
-
straceでアタッチ
root@test:~# strace -p 15655 -e read Process 15655 attached read(0,
こうやっておくと
test@test:~$ ls
testユーザーが入力している文字が分かる
read(0, "l", 1) = 1 read(0, "s", 1) = 1 read(0, "\r", 1) = 1
writeにしておくと出力が取れる。
root@test:~# strace -p 15760 -e write Process 15760 attached
test@test:~$ pwd /home/test
root@test:~# strace -p 15760 -e write Process 15760 attached write(2, "p", 1) = 1 write(2, "w", 1) = 1 write(2, "d", 1) = 1 write(2, "\n", 1) = 1 write(1, "/home/test\n", 11) = 11
keyloggerにならなくもない
straceでプログラムを開始するのは
strace ./a.out
みたいな感じでltrace使うとプロセスがどういう共有ライブラリの関数を呼び出しているか見れる。
test@test:~$ cat test2.c #include <unistd.h> #include <stdio.h> int main() { printf("test"); sleep(1); printf("test"); return 0; } test@test:~$ gcc test2.c -o test2 test@test:~$ ltrace ./test2 __libc_start_main(0x400546, 1, 0x7ffc46dd7aa8, 0x400580 <unfinished ...> printf("test") = 4 sleep(1) = 0 printf("test") = 4 testtest+++ exited (status 0) +++
-
-
gdb
すごいやつ
とてもすごいので書ききれないので詳しい使い方は省略。
今回はプロセスにアタッチする方法を
-p でアタッチしたいプロセスを指定する
root@test:~# ps au | grep test test 15760 0.0 0.5 22088 5256 pts/2 S+ 12:22 0:00 -bash root@test:~# gdb -p 15760
今回はtestユーザのbashを指定。
testユーザーは何も操作ができなくなる。
sshでやる場合、長時間アタッチすると接続がタイムアウトするので注意。
printでシステムコールが使える。
(gdb) print system("echo omae ha mou nottorarete iru")
$1 = 0
```
```
test@test:~$ omae ha mou nottorarete iru
```
他人のアカウントでコマンドが実行できる。(^u^)
アタッチを解除する場合はdetach
```
(gdb) detach
```
-
lsof
プロセスがオープンしてるファイルとか見れる 強い
root@test:~# lsof -p 15760
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME bash 15760 test cwd DIR 254,3 4096 820272 /home/test bash 15760 test rtd DIR 254,3 4096 2 /
```
-
screenify
gdbの方法が面倒な場合、完全にプロセスを乗っ取りたい場合に使える。gdbを使ったスクリプト。
ダウンロードしてきて実行する
chmod +x screenify ./screenify 16804 64 bytes from 8.8.8.8: icmp_seq=3 ttl=52 time=18.2 ms 64 bytes from 8.8.8.8: icmp_seq=4 ttl=52 time=18.3 ms 64 bytes from 8.8.8.8: icmp_seq=5 ttl=52 time=18.5 ms 64 bytes from 8.8.8.8: icmp_seq=6 ttl=52 time=18.4 ms
-
reptyr
同じようなやつだけどscreenifyで不便なとこをわりとなんとかしてくれてる。
詳しくは