github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/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 }