prev Translate | Page next |
This document is also available as:
"sole guardian of numerous children running and playing in a huge rye field on the edge of a cliff"
"sole guardian of numerous processes running and playing in a huge binary field on the edge of a syscalls"
「自分は、広いバイナリ畑で遊んでいるプログラムたちが、気付かずにシステムコールから落ちそうになったときに、捕まえてあげるような、そんなサービスを作りたい」
is a simple CGI that runs a given program in a chrooted field
Directory Hierarchy
% find . -xdev | sort ./0123456789abcdef ./0123456789abcdef/eval.pl ./bin ./dev ./etc ./etc/localtime ./etc/protocols ./etc/services ./lib ./libexec ./libexec/ld-elf.so.1 ./libexec/ld-elf.so.1.old ./tmp ./usr ./usr/bin ./usr/bin/bwbasic ... ./usr/bin/perl ... ./usr/bin/tclsh ./usr/lib ./usr/local ./usr/local/bin ./usr/local/lib ./usr/local/libexec ./usr/local/perl6 ./usr/local/share ./var ./var/run ./var/run/ld-elf.so.hints ./var/run/ld.so.hints
Directory Hierarchy
libraries are mounted read-only via nullfs
/lib /home/chroot/lib nullfs ro,noatime,nosuid 0 0 /usr/lib /home/chroot/usr/lib nullfs ro,noatime,nosuid 0 0 /usr/local/lib /home/chroot/usr/local/lib nullfs ro,noatime,nosuid 0 0 /usr/local/libexec /home/chroot/usr/local/libexec nullfs ro,noatime,nosuid 0 0 /usr/local/share /home/chroot/usr/local/share nullfs ro,noatime,nosuid 0 0 /usr/local/perl6 /home/chroot/usr/local/perl6 nullfs ro,noatime,nosuid 0 0
# simple strace in perl use strict; use warnings; use FreeBSD::i386::Ptrace; use FreeBSD::i386::Ptrace::Syscall; die "$0 prog args ..." unless @ARGV; my $pid = fork(); die "fork failed:$!" if !defined($pid);
if ( $pid == 0 ) { # son pt_trace_me; exec @ARGV; } else { # mom wait; # for exec; my $count = 0; while ( pt_to_sce($pid) == 0 ) { last if wait == -1; my $call = pt_getcall($pid); pt_to_scx($pid); wait; my $retval = pt_getcall($pid); my $name = $SYS{$call} || 'unknown'; print "$name() = $retval\n"; $count++; } warn "$count system calls issued\n"; }
http://labs.cybozu.co.jp/blog/kazuho/
archives/2009/03/freebsd_ptrace.php
You can't stop the invocation but you can still poke the argument on the stack.
sub check_open { my $pid = shift; my $regs = pt_getregs($pid); my $st1 = pt_read( $pid, $regs->esp + 8 ); if ( $st1 & ( O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_TRUNC ) ) { if ( my $st0 = pt_read( $pid, $regs->esp + 4 ) ) { if ( my $path = pt_peekstr( $pid, $st0 ) ) { pt_write( $pid, $st0, 0 ); ptrace( PT_CONTINUE, $pid, 0, 9 ); die qq{XXX:open("$path",$st1)\n}; } } } }
You can't prevent [rv]?fork
but you still send them to the oblivion
pt_continue($pid, 0, 9);
both son & mom die w/ SEGV
But you can still run a program on SEGV via $SIG{SEGV}
$SIG{SEGV} = sub{ while(1){ warn "$$: don't kill me, please!"; sleep(1); } };
You can catch that, too.
sub check_sigaction { my ($pid) = @_; my $regs = pt_getregs($pid); my $st0 = pt_read( $pid, $regs->esp + 4 ); my $st1 = pt_read( $pid, $regs->esp + 8 ); my $st2 = pt_read( $pid, $regs->esp + 12 ); if ($st0 == 11 && $st1 != 0 && $sigactions++ >= $sigaction_ok){ pt_write( $pid, $st0, 0 ); pt_write( $pid, $st1, 0 ); pt_write( $pid, $st2, 0 ); ptrace( PT_CONTINUE, $pid, 0, 9 ); die qq{XXX:sigaction($st0, $st1, $st2)\n}; } }
Address 0x00000000 might not be the oblivion.
syscall(&SYS_mmap, 0,4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_ANON, -1,0,0);
You can catch that, too.
sub check_mmap { my ($pid) = @_; my $regs = pt_getregs($pid); my $st0 = pt_read( $pid, $regs->esp + 4 ); # addr my $st1 = pt_read( $pid, $regs->esp + 8 ); # len my $st2 = pt_read( $pid, $regs->esp + 12 ); # prot my $st3 = pt_read( $pid, $regs->esp + 16 ); # flags my $st4 = pt_read( $pid, $regs->esp + 20 ); # fd my $st5 = pt_read( $pid, $regs->esp + 24 ); # offset if ($st2 & 0x04 && $st3 & 0x10){ pt_write( $pid, $st0, 0 ); pt_write( $pid, $st1, 0 ); pt_write( $pid, $st2, 0 ); pt_write( $pid, $st3, 0 ); pt_write( $pid, $st4, 0 ); pt_write( $pid, $st5, 0 ); ptrace( PT_CONTINUE, $pid, 0, 9 ); die qq{XXX:mmap($st0, $st1, $st2, $st3, $st4, $st5)\n}; } }
Let children play free -- Just CATCH before they fall off from the cliff!
while(my $q = Questions->new){ $q->answer; }