横取りというか覗き見る。リベンジ
- http://www.linux.or.jp/JM/html/LDP_man-pages/man2/ptrace.2.html
- 50行straceもどき - memologue
- Playing with ptrace, Part I | Linux Journal
このあたりを参考に、write()しているところでデータを盗み見る。
ptrace()の使い方
- ptrace( PTRACE_ATTACH, pid, NULL, NULL );
- プロセス番号pidをアタッチする宣言。wait()すると止まる。
- ptrace( PTRACE_SYSCALL, pid, NULL, NULL );
- システムコールで止まれ!と命令する。wait()すると止まる。入るときと出るときとの両方で止まるので、出るときのは無視しよう。
- ptrace( PTRACE_GETREGS, pid, NULL, ®s );
- システムコール時のレジスタを取得する。
- ptrace( PTRACE_PEEKDATA, pid, addr, NULL );
- 指定したアドレスからデータを読む。普通にアクセスするとアドレス例外になるからね。戻り値が読み出したデータ、つまり、1ワードずつしか読めない。
レジスタの見方
write()の場合
regs.orig_eax | システムコール番号 |
regs.ebx | ファイルディスクリプタ |
regs.ecx | 文字列のあるアドレス |
regs.edx | サイズ |
システムコール番号
SYS_writeとか__NR_writeとかの一覧は sys/syscall.h から順に辿ると定義されてる。
SYS_なんとか の形式を使ってると安定っぽい。
コード
usage: ptracetest
[ ] $ ptracetest `pgrep hoge` 1
プロセス番号pidの出力を見る。fdがあればそのfdだけを、無ければ全部見る。
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <sys/ptrace.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/syscall.h> #include <asm/user.h> void getdata(pid_t p, long addr, char *str, int len) { int i; long data; for( i=0; i<len; i+=sizeof(long) ){ // 文字列をコピーする data = ptrace( PTRACE_PEEKDATA, p, addr+i, NULL ); memcpy( str+i, &data, sizeof(long) ); } str[len] = '\0'; return; } int main( int argc, char **argv ) { pid_t p; int st; int fd=0; int in_syscall=0; char *str; struct user_regs_struct regs; if( argc<2 ){ printf( "usage: %s <pid> [<fd>]\n", argv[0] ); return 0; } if( argc>2 ) fd=atoi(argv[2]); // ディスクリプタ指定のあるとき setbuf(stdout,NULL); // 標準出力をバッファリングしない // アタッチする p = atoi(argv[1]); if( ptrace(PTRACE_ATTACH,p,NULL,NULL)<0 ){ perror("ptrace"); exit(1); } wait(&st); // 無いと安定しない while( ptrace(PTRACE_SYSCALL,p,NULL,NULL)==0 ){ // 止まるのを待つ wait(&st); if(WIFEXITED(st)) break; // レジスタを取り出す // regs.orig_eax システムコール番号 // regs.ebx ファイルディスクリプタ // regs.ecx 文字列のあるアドレス // regs.edx サイズ ptrace(PTRACE_GETREGS, p, 0, ®s); if( regs.orig_eax!=SYS_write ) continue; // write以外無視 in_syscall = 1-in_syscall; // 交互に if(in_syscall){ if( fd==0 || fd==regs.ebx ){ str = malloc( regs.edx+sizeof(long) ); // 少し余計に getdata( p, regs.ecx, str, regs.edx ); fputs(str,stdout); free(str); // 後始末 } } } return 0; }