########################################### 
package WriteTracer; 
use strict; 
use POSIX; 
use Inline "C"; 
use Fcntl; 

use Sys::Ptrace qw(ptrace 
            PTRACE_SYSCALL PTRACE_TRACEME); 

########################################### 
sub run { 
########################################### 
  my($prg, @params) = @_; 

  my @files = (); 
  my %files = (); 

  if((my $pid = fork()) < 0) { 
      die "fork failed"; 

  } elsif($pid == 0) { 
      # child 
    ptrace(PTRACE_TRACEME, $$, 0, 0); 
    exec($prg, @params); 

  } else { 
      # parent 
    { 
      my $rc = waitpid($pid, 0); 
      last if $rc < 0; 

      if( WIFSTOPPED($?) ) { 
        my($eax, $orig_eax, $ebx, $ecx, 
           $edx) = ptrace_getregs($pid); 

        if($eax == -ENOSYS()) { 
          if($orig_eax == 5 and 
             $ecx & O_WRONLY) { 
            my $str = ptrace_string_read( 
                               $pid, $ebx); 
            push @files, $str 
                 unless $files{$str}++; 
          } 
        } 

        ptrace(PTRACE_SYSCALL, $pid, 
               undef, undef); 
        redo; 
      } 
    } 
  } 
  return @files; 
} 

1; 

__DATA__ 
__C__ 
#include <sys/ptrace.h> 
#include <asm/user.h> 

#define IVPUSH(x) Inline_Stack_Push( \ 
                   sv_2mortal(newSViv(x))); 

/* ------------------------------------- */ 
void ptrace_getregs(int pid) { 
  int rc; 
  struct user_regs_struct registers; 
  Inline_Stack_Vars; 

  rc = ptrace(PTRACE_GETREGS, pid, 
                          0, &registers); 
  if(rc == -1) { 
      return; 
  } 

  if( registers.eax == -ENOSYS ) { 
      Inline_Stack_Reset; 
      IVPUSH(registers.eax); 
      IVPUSH(registers.orig_eax); 
      IVPUSH(registers.ebx); 
      IVPUSH(registers.ecx); 
      IVPUSH(registers.edx); 
      Inline_Stack_Done; 
  } 
} 

/* ------------------------------------- */ 
int ptrace_aligned_word_read_c(int pid, 
         void *addr, char *buf, int *len) { 
  char *aligned_addr; 
  long  word; 
  void *ptr; 

  aligned_addr = (char *) ( 
       (long)addr & ~ (sizeof(long) - 1) ); 

  word = ptrace(PTRACE_PEEKDATA, pid, 
                aligned_addr, NULL); 

  if(word == -1) { 
      return -1; 
  } 

  *len = sizeof(long) - ( (long) addr - 
                     (long) aligned_addr ); 
  ptr = &word; 
  ptr += (sizeof(long) - *len); 
  memcpy(buf, ptr, *len); 

  return 0; 
} 

/* ------------------------------------- */ 
void ptrace_string_read(int pid, 
                        void *addr) { 
  char  word_buf[ sizeof(long) ]; 
  int   word_len; 
  SV   *pv; 
  int   rc; 
  int   i; 
  Inline_Stack_Vars; 

  pv = newSVpv((const char *)"", 0); 

  while(1) { 
    rc = ptrace_aligned_word_read_c(pid, 
                addr, word_buf, &word_len); 
    if(rc < 0) { 
        return; 
    } 

    for(i=0; i<word_len; i++) { 
      if(word_buf[i] == '\0') { 
        goto FINISH; 
      } 
      sv_catpvn(pv, (const char *) 
                    &word_buf[i], 1); 
    } 
    addr += word_len; 
  } 

  FINISH: 
  Inline_Stack_Reset; 
  Inline_Stack_Push(sv_2mortal(pv)); 
  Inline_Stack_Done; 
}
