func (t *tracerImpl) decodeArg(typ interface{}, value regParam, argValue *ArgValue) { if reflect.TypeOf(typ).Kind() == reflect.Ptr && value == 0 { argValue.Str = "NULL" argValue.Value = nil return } switch typ.(type) { case StringC: argValue.Str = t.decodeArgStringC(value) argValue.Value = argValue.Str case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64: argValue.Value = value argValue.Str = fmt.Sprintf("%d", argValue.Value) case *uint64: var out []byte = make([]byte, 8) count, err := syscall.PtracePeekData(t.cmd.Process.Pid, uintptr(value), out) if err != nil { log.Printf("Error while reading syscall arg: %s", err) } if count != 8 { log.Printf("Error while reading syscall arg: count = %d (should be 8)", count) } argValue.Value = binary.LittleEndian.Uint64(out) argValue.Str = fmt.Sprintf("%d", argValue.Value) default: argValue.Value = value argValue.Str = fmt.Sprintf("%v", value) + "(NOTIMPL=" + reflect.TypeOf(typ).String() + ")" } }
func (t *thread) getSocketAddress(ptr uintptr) (addr net.IP, port uint16, err error) { var ( buf = make([]byte, syscall.SizeofSockaddrAny) read int ) if ptr == 0 { err = fmt.Errorf("Null ptr") return } read, err = syscall.PtracePeekData(t.tid, ptr, buf) if read != syscall.SizeofSockaddrAny || err != nil { return } var sockaddr4 = (*syscall.RawSockaddrInet4)(unsafe.Pointer(&buf[0])) if sockaddr4.Family != syscall.AF_INET { return } addr = net.IP(sockaddr4.Addr[0:]) port = ntohl(sockaddr4.Port) return }
// getString returns the C string stored at the process' memory address addr. func getString(pid int, addr uintptr) string { var buffer [4096]byte if _, err := syscall.PtracePeekData(pid, addr, buffer[:]); err == nil { if i := bytes.IndexByte(buffer[:], 0); i >= 0 && i < len(buffer) { return string(buffer[:i]) } } return "" }
func (t *tracerImpl) decodeArgBuffer(value regParam, size uint64) (buffer []byte, str string) { if size < 0 { return nil, "" } if size == 0 { return []byte{}, "" } bufferSize := size extra := false if bufferSize > t.maxBufferSize { extra = true bufferSize = t.maxBufferSize } buffer = make([]byte, bufferSize) count, err := syscall.PtracePeekData(t.cmd.Process.Pid, uintptr(value), buffer) if err != nil { str = fmt.Sprintf("Error while reading syscall arg: %s", err) return } if uint64(count) != bufferSize { str = fmt.Sprintf("Error while reading syscall arg: count = %d (should be %d)", count, bufferSize) return } strBuffer := make([]byte, 0, bufferSize+2) strBuffer = append(strBuffer, '"') for _, b := range buffer { switch { case b == '\n': strBuffer = append(strBuffer, '\\', 'n') case b == '\r': strBuffer = append(strBuffer, '\\', 'r') case b == '\t': strBuffer = append(strBuffer, '\\', 't') case b >= ' ' && b <= '~': strBuffer = append(strBuffer, b) default: strBuffer = append(strBuffer, []byte(fmt.Sprintf("\\%d", b))...) } } strBuffer = append(strBuffer, '"') if extra { strBuffer = append(strBuffer, "..."...) } str = string(strBuffer) return }
func (t *tracerImpl) decodeArgStringC(value regParam) string { out := []byte{0} str := make([]byte, 0, 10) i := uint64(0) extra := false for { count, err := syscall.PtracePeekData(t.cmd.Process.Pid, uintptr(value+regParam(i)), out) if out[0] == 0 { break } if i > t.maxStringSize { extra = true break } if err != nil { log.Printf("Error while reading syscall arg: %s", err) } if count != 1 { log.Printf("Error while reading syscall arg: count = %d (should be 1)", count) } switch { case out[0] == '\n': str = append(str, '\\', 'n') case out[0] == '\r': str = append(str, '\\', 'r') case out[0] == '\t': str = append(str, '\\', 't') case out[0] >= ' ' && out[0] <= '~': str = append(str, out[0]) default: str = append(str, []byte(fmt.Sprintf("\\%d", out[0]))...) } i++ } result := "\"" + string(str) + "\"" if extra { result += "..." } return result }
func main() { runtime.LockOSThread() flag.Parse() args := flag.Args() if *verbose { log.Printf("Starting program %v", args) } cmd, wait_syscall := read_ptrace_events(args) var buf [1024]byte var filemap = map[uintptr]*File{} for { regs := wait_syscall() if regs == nil { break } call, args := syscall_params(regs) callname := strings.ToLower(syscall_name(call)[4:]) //log.Printf("call %d", call) _ = callname // Return of syscall regs = wait_syscall() if regs == nil { break } retval := syscall_retval(regs) interesting := true sw: switch call { case syscall.SYS_OPEN: count, err := syscall.PtracePeekData(cmd.Process.Pid, uintptr(args[0]), buf[:]) if err != nil { panic(err) } i := bytes.IndexByte(buf[:count], 0) path := string(buf[:i]) errno := syscall.Errno(-retval) if retval < 0 { errno = 0 args[1] = 99999 } // For the moment hide failed opens if retval >= 0 { _ = errno if *verbose { log.Printf("open(path=%v, flags=0x%x, mode=0x%x) -> fd=%v e=%v", path, args[1], args[2], retval, errno) } } else { interesting = false } if retval >= 0 { fd := uintptr(retval) fileinfo, err := os.Stat(path) if err != nil { panic(err) } filemap[fd] = &File{path, fd, 0, fileinfo, make(map[int64]int64), []int64{}, 0} } case syscall.SYS_READ: if *verbose { log.Printf("read(fd=%v, buf=0x%x, bufsize=%v) -> ret=%v", args[0], args[1], args[2], retval) } if retval < 0 { interesting = false break sw } fd := uintptr(args[0]) if _, ok := filemap[fd]; !ok { // stdin? break sw //log.Panic("fd not in file map: ", fd) } f := filemap[fd] rfp := &f.read_file_pieces pos := f.pos if size, present := (*rfp)[pos]; present { if retval > size { // this read is at the same place but larger, so it over-rides // the existing read (*rfp)[pos] = retval } } else { (*rfp)[pos] = retval } // Only keep the last N reads f.decay = append(f.decay, pos) if len(f.decay) > 1 { if _, ok := (*rfp)[f.decay[0]]; ok { delete(*rfp, f.decay[0]) } f.decay = f.decay[1:] } f.pos += retval f.Show() case syscall.SYS_READV: if *verbose { log.Printf("readv(fd=%v, buf=0x%x, bufsize=%v) -> ret=%v", args[0], args[1], args[2], retval) } fd := uintptr(args[0]) filemap[fd].pos += retval case syscall.SYS_PREAD64: log.Printf("pread64(fd=%v, buf=0x%x, bufsize=%v, offset=%v) -> ret=%v", args[0], args[1], args[2], args[3], retval) fd := uintptr(args[0]) filemap[fd].pos += retval case syscall.SYS_LSEEK: if *verbose { log.Printf("lseek(fd=%v, offset=%v, whence=%v) -> ret=%v", args[0], args[1], args[2], retval) } fd := uintptr(args[0]) offset := int64(args[1]) whence := int(args[2]) switch whence { case os.SEEK_SET: filemap[fd].pos = offset case os.SEEK_CUR: filemap[fd].pos += offset case os.SEEK_END: // panic("Not implemented") } case syscall.SYS_CLOSE: if *verbose { log.Printf("close(fd=%x) = %v", args[0], retval) } fd := uintptr(args[0]) if retval >= 0 && fd > 2 { delete(filemap, fd) } default: interesting = false } if interesting { time.Sleep(*delay) } } }
func (t *Tracer) PeekData(addr uintptr, out []byte) (int, error) { return syscall.PtracePeekData(t.Process.Pid, addr, out) }
func initSyscalls() SyscallTable { read_or_write := func(name string, regs syscall.PtraceRegs) func(syscall.PtraceRegs) { var addr = regs.Rsi var count = int32(regs.Rdx) fd := regs.Rdi fmt.Printf(`%s(%d, `, name, fd) return func(regs syscall.PtraceRegs) { result := int32(regs.Rax) var buf []byte = []byte{} if result > 0 { buf = make([]byte, result+4) for i := 0; i <= int(result); i += 4 { syscall.PtracePeekData(pid, uintptr(addr), buf[i:i+4]) addr += 4 } fmt.Printf("%s, %d) = %d\n", strconv.Quote(string(buf[0:result])), count, result) } else { fmt.Printf("\"\", %d) = %d\n", count, result) } } } fork_or_vfork := func(name string, regs syscall.PtraceRegs) func(regs syscall.PtraceRegs) { fmt.Printf("%s()", name) return func(regs syscall.PtraceRegs) { var code = int32(regs.Rax) fmt.Printf(" = %d", code) } } parseSockAddr := func(bs []byte) (error, *syscall.RawSockaddr) { if len(bs) < 2 { return errors.New("ETOOSHORT"), nil } var family uint16 = uint16(bs[1])<<8 | uint16(bs[0]) log.Printf("FAMILY IS %d\n", family) switch family { case syscall.AF_LOCAL: var rsau *syscall.RawSockaddrUnix rsau = (*syscall.RawSockaddrUnix)(unsafe.Pointer(&bs)) log.Printf("%#v", rsau) } return errors.New("LUL"), nil } readBytes := func(addr uintptr, length int) []byte { var buf = make([]byte, length+4) log.Printf("READBYTES addr: %v length: %d\n", addr, length) for i := 0; i < length; i += 4 { syscall.PtracePeekData(pid, addr, buf[i:i+4]) addr += 4 } return buf } readString := func(addr uintptr, length int) string { buf := readBytes(addr, length) return strconv.Quote(string(buf)) } m := SyscallTable{ SysRead: func(regs syscall.PtraceRegs) func(syscall.PtraceRegs) { return read_or_write("read", regs) }, SysWrite: func(regs syscall.PtraceRegs) func(syscall.PtraceRegs) { return read_or_write("write", regs) }, SysClose: func(regs syscall.PtraceRegs) func(syscall.PtraceRegs) { fd := regs.Rdi fmt.Printf("close(%d)", fd) return func(regs syscall.PtraceRegs) { var ret = int32(regs.Rax) fmt.Printf(" = %d\n", ret) } }, SysOpen: func(regs syscall.PtraceRegs) func(syscall.PtraceRegs) { var fnameptr = regs.Rdi var flags = regs.Rsi var mode = regs.Rdx var buf = bytes.NewBuffer([]byte{}) var mbuf = make([]byte, 4) var nullFound = false for { syscall.PtracePeekData(pid, uintptr(fnameptr), mbuf[0:4]) j := 0 for _, v := range mbuf { if v == '\x00' { nullFound = true break } j++ } buf.Write(mbuf[0:j]) if nullFound { break } fnameptr += 4 } fmt.Printf("open(%s, %x, %x)", strconv.Quote(string(buf.Bytes())), flags, mode) return func(regs syscall.PtraceRegs) { ret := int32(regs.Rax) fmt.Printf(" = %d\n", ret) } }, SysSocket: func(regs syscall.PtraceRegs) func(regs syscall.PtraceRegs) { family := regs.Rdi typ := regs.Rsi proto := regs.Rdx fmt.Printf("socket(%s, %s, %d)", families[family], types[typ&0xf], proto) return func(regs syscall.PtraceRegs) { ret := int32(regs.Rax) fmt.Printf(" = %d\n", ret) } }, SysConnect: func(regs syscall.PtraceRegs) func(regs syscall.PtraceRegs) { fd := regs.Rdi sockaddr := regs.Rsi addrlen := regs.Rdx if sockaddr != 0 && addrlen != 0 { buf := readBytes(uintptr(sockaddr), int(addrlen)) parseSockAddr(buf) } fmt.Printf("connect(%d, %x, %x)", fd, sockaddr, addrlen) return func(regs syscall.PtraceRegs) { ret := int32(regs.Rax) fmt.Printf(" = %d\n", ret) } }, SysSendto: func(regs syscall.PtraceRegs) func(regs syscall.PtraceRegs) { fd := regs.Rdi bufptr := regs.Rsi length := regs.Rdx flags := regs.Rcx sockaddr := regs.R8 addrlen := regs.R9 buf := readString(uintptr(bufptr), int(length)) fmt.Printf("sendto(%d, %s, %d, %d, %v, %d)", fd, buf, length, flags, sockaddr, addrlen) return func(regs syscall.PtraceRegs) { ret := int32(regs.Rax) fmt.Printf("= %d\n", ret) } }, SysSendmsg: func(regs syscall.PtraceRegs) func(regs syscall.PtraceRegs) { fd := regs.Rdi msg := regs.Rsi flags := regs.Rdx fmt.Printf("sendmsg(%d, %v, %v)", fd, msg, flags) return func(regs syscall.PtraceRegs) { ret := int32(regs.Rax) fmt.Printf("= %d\n", ret) } }, SysRecvmsg: func(regs syscall.PtraceRegs) func(regs syscall.PtraceRegs) { fd := regs.Rdi msg := regs.Rsi flags := regs.Rdx fmt.Printf("recvmsg(%d, ", fd) return func(regs syscall.PtraceRegs) { ret := int32(regs.Rax) if ret >= 0 { fmt.Printf("\"%v\", %s) = %d\n", msg, flags, ret) } else { fmt.Printf("\"\", %s) = %d\n", flags, ret) } } }, SysSyslog: func(regs syscall.PtraceRegs) func(regs syscall.PtraceRegs) { typ := regs.Rdi msgptr := regs.Rsi length := regs.Rdx msg := readString(uintptr(msgptr), int(length)) fmt.Printf("syslog(%d, %s, %d)", typ, msg, length) return func(regs syscall.PtraceRegs) { ret := int32(regs.Rax) fmt.Printf(" = %d\n", ret) } }, SysGetuid: func(regs syscall.PtraceRegs) func(regs syscall.PtraceRegs) { fmt.Printf("getuid()") return func(regs syscall.PtraceRegs) { ret := int32(regs.Rax) fmt.Printf(" = %d\n", ret) } }, SysGetgid: func(regs syscall.PtraceRegs) func(regs syscall.PtraceRegs) { fmt.Printf("getgid()") return func(regs syscall.PtraceRegs) { ret := int32(regs.Rax) fmt.Printf(" = %d\n", ret) } }, SysSetuid: func(regs syscall.PtraceRegs) func(regs syscall.PtraceRegs) { uid := int32(regs.Rdi) fmt.Printf("setuid(%d)", uid) return func(regs syscall.PtraceRegs) { ret := int32(regs.Rax) fmt.Printf(" = %d\n", ret) } }, SysExit: func(regs syscall.PtraceRegs) func(regs syscall.PtraceRegs) { var code = regs.Rdi fmt.Printf("exit_group(%d)\n", code) return func(regs syscall.PtraceRegs) {} }, SysFork: func(regs syscall.PtraceRegs) func(regs syscall.PtraceRegs) { return fork_or_vfork("fork", regs) }, SysVfork: func(regs syscall.PtraceRegs) func(regs syscall.PtraceRegs) { return fork_or_vfork("vfork", regs) }, SysRestart: func(regs syscall.PtraceRegs) func(regs syscall.PtraceRegs) { fmt.Printf("<restarting syscall>") return func(regs syscall.PtraceRegs) { } }, SysClone: func(regs syscall.PtraceRegs) func(regs syscall.PtraceRegs) { var flags = regs.Rdi var sp = regs.Rsi var envp = regs.Rdx fmt.Printf("clone(%x, %x, %x)", flags, sp, envp) return func(regs syscall.PtraceRegs) { var code = int32(regs.Rax) fmt.Printf(" = %d", code) } }, } return m }