github.com/hugelgupf/u-root@v0.0.0-20191023214958-4807c632154c/pkg/strace/tracer.go (about)

     1  // Copyright 2018 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package strace supports tracing programs.
     6  // The basic control of tracing is via a Tracer, which returns raw
     7  // TraceRecords via a chan. The easiest way to create a Tracer is via
     8  // RunTracerFromCommand, which uses a filled out exec.Cmd to start a
     9  // process and produce trace records.
    10  // Forking is not yet supported.
    11  package strace
    12  
    13  import (
    14  	"encoding/binary"
    15  	"fmt"
    16  	"io"
    17  	"os/exec"
    18  	"runtime"
    19  	"syscall"
    20  	"time"
    21  
    22  	"golang.org/x/sys/unix"
    23  )
    24  
    25  // Debug is a do-nothing function which can be replaced by, e.g., log.Printf
    26  var Debug = func(string, ...interface{}) {}
    27  
    28  // TraceRecord has information about a ptrace event.
    29  type TraceRecord struct {
    30  	EX     EventType
    31  	Regs   unix.PtraceRegs
    32  	Serial int
    33  	Pid    int
    34  	Err    error
    35  	Errno  int
    36  	Args   SyscallArguments
    37  	Ret    [2]SyscallArgument
    38  	Sysno  int
    39  	Time   time.Duration
    40  	Out    string
    41  }
    42  
    43  // Tracer has information to trace one process. It can be created by
    44  // starting a command, or attaching. Attaching is not supported yet.
    45  type Tracer struct {
    46  	Pid     int
    47  	EX      EventType
    48  	Records chan *TraceRecord
    49  	Count   int
    50  	Raw     bool // Set by the user, it disables pretty printing
    51  	Name    string
    52  	Printer func(t *Tracer, r *TraceRecord)
    53  	Last    *TraceRecord
    54  	// We save the output from the previous Enter so Exit handling
    55  	// can both use and adjust it.
    56  	output []string
    57  }
    58  
    59  // New returns a new Tracer.
    60  func New() (*Tracer, error) {
    61  	return &Tracer{Pid: -1, Records: make(chan *TraceRecord, 1), Printer: SysCall}, nil
    62  }
    63  
    64  // RunTracerFromCommand runs a Tracer given an exec.Cmd.
    65  // It locks itself down with LockOSThread and will unlock itself
    66  // when it returns, after the command and all its children exit.
    67  func (t *Tracer) RunTracerFromCmd(c *exec.Cmd) {
    68  	defer close(t.Records)
    69  	if c.SysProcAttr == nil {
    70  		c.SysProcAttr = &syscall.SysProcAttr{}
    71  	}
    72  	c.SysProcAttr.Ptrace = true
    73  	// Because the go runtime forks traced processes with PTRACE_TRACEME
    74  	// we need to maintain the parent-child relationship for ptrace to work.
    75  	// We've learned this the hard way. So we lock down this thread to
    76  	// this proc, and start the command here.
    77  	// Note this function will block; if you want it to be nonblocking you
    78  	// need to use go etc.
    79  	runtime.LockOSThread()
    80  	defer runtime.UnlockOSThread()
    81  	if err := c.Start(); err != nil {
    82  		Debug("Start gets err %v", err)
    83  		t.Records <- &TraceRecord{Err: err}
    84  		return
    85  	}
    86  	Debug("Start gets pid %v", c.Process.Pid)
    87  	if err := c.Wait(); err != nil {
    88  		fmt.Printf("Wait returned: %v\n", err)
    89  		t.Records <- &TraceRecord{Err: err}
    90  	}
    91  	t.Pid = c.Process.Pid
    92  	t.Name = fmt.Sprintf("%s(%d)", c.Args[0], t.Pid)
    93  	t.EX = Exit
    94  	Run(t)
    95  }
    96  
    97  // NewTracerChild creates a tracer from a tracer.
    98  func NewTracerChild(pid int) (*Tracer, error) {
    99  	nt, err := New()
   100  	if err != nil {
   101  		return nil, err
   102  	}
   103  	nt.Pid = pid
   104  	nt.Name = fmt.Sprintf("%d", pid)
   105  	nt.EX = Exit
   106  	return nt, nil
   107  }
   108  
   109  // Step steps a Tracer by issuing a PtraceSyscall to it and then doing a Wait.
   110  // Note that Step waits for any child to return, not just the one we are stepping.
   111  func (t *Tracer) Step(e EventType) (int, error) {
   112  	Debug("Step %d", t.Pid)
   113  	if err := unix.PtraceSyscall(t.Pid, 0); err != nil {
   114  		r := &TraceRecord{Serial: t.Count, EX: e, Pid: t.Pid}
   115  		Debug("ptracesyscall for %d gets %v", t.Pid, err)
   116  		r.Err = fmt.Errorf("unix.PtraceSyscall: %d: %s: %v", t.Pid, t.Name, err)
   117  		t.Records <- r
   118  		return -1, r.Err
   119  	}
   120  	Debug("Stepped %d, now Wait", t.Pid)
   121  	pid, w, err := Wait(-1)
   122  	Debug("Wait returns (%d, %v, %v)", pid, w, err)
   123  	if err != nil {
   124  		r := &TraceRecord{Serial: t.Count, EX: e, Pid: t.Pid}
   125  		r.Err = fmt.Errorf("unix.Wait: %d: %s: %v, %v", t.Pid, t.Name, w, err)
   126  		Debug("wait4 for %d gets %v, %v", t.Pid, w, err)
   127  		t.Records <- r
   128  		return -1, r.Err
   129  	}
   130  	Debug("Step %d: back from wait", pid)
   131  	return pid, nil
   132  }
   133  
   134  // Run runs a set of processes as defined by a Tracer. Because of Unix restrictions
   135  // around which processes which can trace other processes, Run gets a tad involved.
   136  // It is implemented as a simple loop, driving events via ptrace commands to processes;
   137  // and responding to events returned by a Wait.
   138  // It has to handle a few events specially:
   139  // o if a wait fails, the process has exited, and must no longer be commanded
   140  //   this is indicated by a wait followed by an error on PtraceGetRegs
   141  // o if a process forks successfully, we must add it to our set of traced processes.
   142  //   We attach that process, wait for it, then issue a ptrace system call command
   143  //   to it. We don't use the Linux SEIZE command as we can do this in a more generic
   144  //   Unix way.
   145  // We create a map of our traced processes and run until it is empty.
   146  // The initial value of the map is just the one process we start with.
   147  func Run(root *Tracer) error {
   148  	var nextEX EventType
   149  	var procs = map[int]*Tracer{
   150  		root.Pid: root,
   151  	}
   152  	Debug("procs %v", procs)
   153  	var tm time.Time
   154  	var a SyscallArguments
   155  	var sysno = syscall.SYS_EXECVE
   156  	Debug("Run %v", root.Pid)
   157  	pid := root.Pid
   158  	var err error
   159  	var count int
   160  	for len(procs) > 0 {
   161  		t := procs[pid]
   162  		t.Count++
   163  		count++
   164  		Debug("Get regs for %d", pid)
   165  		x := &TraceRecord{Serial: t.Count, EX: t.EX, Pid: pid, Args: a}
   166  		if err := unix.PtraceGetRegs(pid, &x.Regs); err != nil {
   167  			Debug("ptracegetregs for %d gets %v", pid, err)
   168  			x.Err = fmt.Errorf("ptracegetregs for %d gets %v", pid, err)
   169  			t.Records <- x
   170  			delete(procs, pid)
   171  			pid, _, _ = Wait(-1)
   172  			continue
   173  		}
   174  		Debug("GOT regs for %d", pid)
   175  		x.FillArgs()
   176  		if t.EX == Exit {
   177  			x.FillRet()
   178  			x.Sysno = sysno
   179  			x.Time = time.Since(tm)
   180  			nextEX = Enter
   181  		} else {
   182  			tm = time.Now()
   183  			x.FillArgs()
   184  			a = x.Args
   185  			sysno = x.Sysno
   186  			t.Last = x
   187  			nextEX = Exit
   188  		}
   189  
   190  		if !t.Raw {
   191  			SysCall(t, x)
   192  		}
   193  		Debug("Push %v", x)
   194  		t.Records <- x
   195  		// Was there a clone? Capture the child. Don't forget the child has an exit
   196  		// record for the clone too, so don't get confused.
   197  		p := int(x.Ret[0].Int())
   198  		Debug("Check for new pid: tracer pid %d, ret %d", t.Pid, p)
   199  		if x.Sysno == unix.SYS_CLONE && x.EX == Exit && p > 0 && p != t.Pid {
   200  			nt, err := NewTracerChild(int(x.Ret[0].Int()))
   201  			if err != nil {
   202  				Debug("Setting up child: %v", err)
   203  			} else {
   204  				nt.Records = t.Records
   205  				Debug("New child: %v", nt)
   206  				// The result of the attach gets picked up by the wait()
   207  				if err := unix.PtraceAttach(nt.Pid); err != nil {
   208  					r := &TraceRecord{Serial: nt.Count, EX: Enter, Pid: nt.Pid}
   209  					Debug("RunTracerChild: attach for %d gets %v", nt.Pid, err)
   210  					nt.Records <- r
   211  				}
   212  				pid, w, err := Wait(nt.Pid)
   213  				Debug("Wait returns (%d, %v, %v)", pid, w, err)
   214  				if err != nil || pid != nt.Pid {
   215  					r := &TraceRecord{Serial: nt.Count, EX: Exit, Pid: nt.Pid}
   216  					r.Err = fmt.Errorf("unix.Wait: %d: %s: %v, %v", nt.Pid, nt.Name, w, err)
   217  					Debug("wait4 for %d gets %v, %v", nt.Pid, w, err)
   218  					t.Records <- r
   219  				}
   220  				Debug("Step %d", nt.Pid)
   221  				if err := unix.PtraceSyscall(nt.Pid, 0); err != nil {
   222  					r := &TraceRecord{Serial: nt.Count, EX: Exit, Pid: nt.Pid}
   223  					Debug("ptracesyscall for %d gets %v", nt.Pid, err)
   224  					r.Err = fmt.Errorf("unix.PtraceSyscall: %d: %s: %v", nt.Pid, nt.Name, err)
   225  					t.Records <- r
   226  				}
   227  				procs[nt.Pid] = nt
   228  			}
   229  		}
   230  
   231  		Debug("Step after exit")
   232  		t.EX = nextEX
   233  		if pid, err = t.Step(t.EX); err != nil {
   234  			return err
   235  		}
   236  	}
   237  
   238  	Debug("Pushed %d records", count)
   239  	return nil
   240  }
   241  
   242  // EventType describes whether a record is system call Entry or Exit
   243  type EventType string
   244  
   245  const (
   246  	Enter EventType = "E"
   247  	Exit  EventType = "X"
   248  )
   249  
   250  // String is a stringer for TraceRecords
   251  // TODO: stringer for Regs.
   252  func (t *TraceRecord) String() string {
   253  	pre := fmt.Sprintf("%s %d#%d:", t.EX, t.Pid, t.Serial)
   254  	if t.Err != nil {
   255  		return fmt.Sprintf("%s(%v)", pre, t.Err)
   256  	}
   257  	return fmt.Sprintf("%s %v", pre, t.Regs)
   258  }
   259  
   260  // A ProcIO is used to implement io.Reader and io.Writer.
   261  // it contains a pid, which is unchanging; and an
   262  // addr and byte count which change as IO proceeds.
   263  type ProcIO struct {
   264  	pid   int
   265  	addr  uintptr
   266  	bytes int
   267  }
   268  
   269  // NewProcReader returns an io.Reader for a ProcIO.
   270  func NewProcReader(pid int, addr uintptr) io.Reader {
   271  	return &ProcIO{pid: pid, addr: addr}
   272  }
   273  
   274  // Read implements io.Read for a ProcIO.
   275  func (p *ProcIO) Read(b []byte) (int, error) {
   276  	n, err := unix.PtracePeekData(p.pid, p.addr, b)
   277  	if err != nil {
   278  		return n, err
   279  	}
   280  	p.addr += uintptr(n)
   281  	p.bytes += n
   282  	return n, nil
   283  }
   284  
   285  // NewProcWriter returns an io.Writer for a ProcIO.
   286  func NewProcWriter(pid int, addr uintptr) io.Writer {
   287  	return &ProcIO{pid: pid, addr: addr}
   288  }
   289  
   290  // Write implements io.Write for a ProcIO.
   291  func (p *ProcIO) Write(b []byte) (int, error) {
   292  	n, err := unix.PtracePokeData(p.pid, p.addr, b)
   293  	if err != nil {
   294  		return n, err
   295  	}
   296  	p.addr += uintptr(n)
   297  	p.bytes += n
   298  	return n, nil
   299  }
   300  
   301  // Read reads from the process at Addr to the interface{}
   302  // and returns a byte count and error.
   303  func (t *Tracer) Read(addr Addr, v interface{}) (int, error) {
   304  	p := NewProcReader(t.Pid, uintptr(addr))
   305  	err := binary.Read(p, binary.LittleEndian, v)
   306  	return p.(*ProcIO).bytes, err
   307  }
   308  
   309  // ReadString reads a null-terminated string from the process
   310  // at Addr and any errors.
   311  func (t *Tracer) ReadString(addr Addr, max int) (string, error) {
   312  	if addr == 0 {
   313  		return "<nil>", nil
   314  	}
   315  	var s string
   316  	var b [1]byte
   317  	for len(s) < max {
   318  		if _, err := t.Read(addr, b[:]); err != nil {
   319  			return "", err
   320  		}
   321  		if b[0] == 0 {
   322  			break
   323  		}
   324  		s = s + string(b[:])
   325  		addr++
   326  	}
   327  	return s, nil
   328  }
   329  
   330  // ReadStringVector takes an address, max string size, and max number of string to read,
   331  // and returns a string slice or error.
   332  func (t *Tracer) ReadStringVector(addr Addr, maxsize, maxno int) ([]string, error) {
   333  	var v []Addr
   334  	if addr == 0 {
   335  		return []string{}, nil
   336  	}
   337  
   338  	fmt.Printf("read vec at %#x", addr)
   339  	// Read in a maximum of maxno addresses
   340  	for len(v) < maxno {
   341  		var a uint64
   342  		n, err := t.Read(addr, &a)
   343  		if err != nil {
   344  			fmt.Printf("Could not read vec elemtn at %v", addr)
   345  			return nil, err
   346  		}
   347  		if a == 0 {
   348  			break
   349  		}
   350  		addr += Addr(n)
   351  		v = append(v, Addr(a))
   352  	}
   353  	fmt.Printf("Read %v", v)
   354  	var vs []string
   355  	for _, a := range v {
   356  		s, err := t.ReadString(a, maxsize)
   357  		if err != nil {
   358  			fmt.Printf("Could not read string at %v", a)
   359  			return vs, err
   360  		}
   361  		vs = append(vs, s)
   362  	}
   363  	return vs, nil
   364  }
   365  
   366  // Write writes to the process address sapce and returns a count and error.
   367  func (t *Tracer) Write(addr Addr, v interface{}) (int, error) {
   368  	p := NewProcWriter(t.Pid, uintptr(addr))
   369  	err := binary.Write(p, binary.LittleEndian, v)
   370  	return p.(*ProcIO).bytes, err
   371  }
   372  
   373  // CaptureAddress pulls a socket address from the process as a byte slice.
   374  // It returns any errors.
   375  func CaptureAddress(t *Tracer, addr Addr, addrlen uint32) ([]byte, error) {
   376  	b := make([]byte, addrlen)
   377  	if _, err := t.Read(addr, b); err != nil {
   378  		return nil, err
   379  	}
   380  	return b, nil
   381  }