| 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;
}