From 80f9a1a2d4fb0216be219ca7eb68c4a8d9134298 Mon Sep 17 00:00:00 2001 From: criyle Date: Mon, 15 Apr 2019 18:29:08 -0400 Subject: [PATCH] add process_vm_readv for blocked read --- run_program/handle.go | 18 +++++----- run_program/main.go | 4 +++ tracer/context.go | 40 ++++++++++++++++----- tracer/context_amd64.go | 52 +++++++++++++++------------ tracer/context_helper.go | 76 ++++++++++++++++++++++++++++++++++++++++ tracer/tracer.go | 9 ++--- 6 files changed, 152 insertions(+), 47 deletions(-) create mode 100644 tracer/context_helper.go diff --git a/run_program/handle.go b/run_program/handle.go index 4ed1de8..05caf32 100644 --- a/run_program/handle.go +++ b/run_program/handle.go @@ -14,7 +14,7 @@ func softBanSyscall(ctx *tracer.Context) tracer.TraceAction { return tracer.TraceBan } -func getFileMode(flags uint64) string { +func getFileMode(flags uint) string { switch flags & syscall.O_ACCMODE { case syscall.O_RDONLY: return "r " @@ -49,8 +49,8 @@ func getHandle(t *tracer.Tracer, pType string, addRead []string, addWrite []stri return tracer.TraceKill } - checkOpen := func(ctx *tracer.Context, addr uint64, flags uint64) tracer.TraceAction { - fn := ctx.GetString(addr) + checkOpen := func(ctx *tracer.Context, addr uint, flags uint) tracer.TraceAction { + fn := ctx.GetString(uintptr(addr)) isReadOnly := (flags&syscall.O_ACCMODE == syscall.O_RDONLY) && (flags&syscall.O_CREAT == 0) && (flags&syscall.O_EXCL == 0) && @@ -69,8 +69,8 @@ func getHandle(t *tracer.Tracer, pType string, addRead []string, addWrite []stri return tracer.TraceAllow } - checkRead := func(ctx *tracer.Context, addr uint64) tracer.TraceAction { - fn := ctx.GetString(addr) + checkRead := func(ctx *tracer.Context, addr uint) tracer.TraceAction { + fn := ctx.GetString(uintptr(addr)) print("check read: ", fn) if !fs.isReadableFile(&fn) { return onDgsFileDetect(ctx, fn) @@ -78,8 +78,8 @@ func getHandle(t *tracer.Tracer, pType string, addRead []string, addWrite []stri return tracer.TraceAllow } - checkWrite := func(ctx *tracer.Context, addr uint64) tracer.TraceAction { - fn := ctx.GetString(addr) + checkWrite := func(ctx *tracer.Context, addr uint) tracer.TraceAction { + fn := ctx.GetString(uintptr(addr)) print("check write: ", fn) if !fs.isWritableFile(&fn) { return onDgsFileDetect(ctx, fn) @@ -87,8 +87,8 @@ func getHandle(t *tracer.Tracer, pType string, addRead []string, addWrite []stri return tracer.TraceAllow } - checkStat := func(ctx *tracer.Context, addr uint64) tracer.TraceAction { - fn := ctx.GetString(addr) + checkStat := func(ctx *tracer.Context, addr uint) tracer.TraceAction { + fn := ctx.GetString(uintptr(addr)) print("check stat: ", fn) if !fs.isStatableFile(&fn) { return onDgsFileDetect(ctx, fn) diff --git a/run_program/main.go b/run_program/main.go index 52dad82..c22991f 100644 --- a/run_program/main.go +++ b/run_program/main.go @@ -76,6 +76,10 @@ func main() { } rt, err := t.StartTrace() + if t.ShowDetails { + fmt.Fprintln(os.Stderr, "Used process_vm_readv: ", tracer.UseVMReadv) + } + if err != nil { c, ok := err.(tracer.TraceCode) if !ok { diff --git a/tracer/context.go b/tracer/context.go index 36d0be2..e19e72c 100644 --- a/tracer/context.go +++ b/tracer/context.go @@ -1,6 +1,9 @@ package tracer -import "syscall" +import ( + "os" + "syscall" +) // Context is the context for current syscall trap // used to retrive syscall number and arguments @@ -11,13 +14,15 @@ type Context struct { regs syscall.PtraceRegs } -func clen(b []byte) int { - for i := 0; i < len(b); i++ { - if b[i] == 0 { - return i - } - } - return len(b) + 1 +var ( + // UseVMReadv determine whether use ProcessVMReadv syscall to read str + // initial true and becomes false if tried and failed with ENOSYS + UseVMReadv = true + pageSize = 4 << 10 +) + +func init() { + pageSize = os.Getpagesize() } func getTrapContext(pid int) (*Context, error) { @@ -31,3 +36,22 @@ func getTrapContext(pid int) (*Context, error) { regs: regs, }, nil } + +// GetString get the string from process data segment +func (c *Context) GetString(addr uintptr) string { + buff := make([]byte, syscall.PathMax) + if UseVMReadv { + if err := vmReadStr(c.Pid, addr, buff); err != nil { + // if ENOSYS, then disable this function + if no, ok := err.(syscall.Errno); ok { + if no == syscall.ENOSYS { + UseVMReadv = false + } + } + } else { + return string(buff[:clen(buff)]) + } + } + syscall.PtracePeekData(c.Pid, addr, buff) + return string(buff[:clen(buff)]) +} diff --git a/tracer/context_amd64.go b/tracer/context_amd64.go index 7594c30..b2c6129 100644 --- a/tracer/context_amd64.go +++ b/tracer/context_amd64.go @@ -1,47 +1,44 @@ package tracer -import "syscall" +import ( + "syscall" + + unix "golang.org/x/sys/unix" +) // SyscallNo get current syscall no -func (c *Context) SyscallNo() int { - return int(c.regs.Orig_rax) +func (c *Context) SyscallNo() uint { + return uint(c.regs.Orig_rax) } // Arg0 gets the arg0 for the current syscall -func (c *Context) Arg0() uint64 { - return c.regs.Rdi +func (c *Context) Arg0() uint { + return uint(c.regs.Rdi) } // Arg1 gets the arg1 for the current syscall -func (c *Context) Arg1() uint64 { - return c.regs.Rsi +func (c *Context) Arg1() uint { + return uint(c.regs.Rsi) } // Arg2 gets the arg2 for the current syscall -func (c *Context) Arg2() uint64 { - return c.regs.Rdx +func (c *Context) Arg2() uint { + return uint(c.regs.Rdx) } // Arg3 gets the arg3 for the current syscall -func (c *Context) Arg3() uint64 { - return c.regs.R10 +func (c *Context) Arg3() uint { + return uint(c.regs.R10) } // Arg4 gets the arg4 for the current syscall -func (c *Context) Arg4() uint64 { - return c.regs.R8 +func (c *Context) Arg4() uint { + return uint(c.regs.R8) } // Arg5 gets the arg5 for the current syscall -func (c *Context) Arg5() uint64 { - return c.regs.R9 -} - -// GetString get the string from process data segment -func (c *Context) GetString(addr uint64) string { - buff := make([]byte, syscall.PathMax) - syscall.PtracePeekData(c.Pid, uintptr(addr), buff) - return string(buff[:clen(buff)]) +func (c *Context) Arg5() uint { + return uint(c.regs.R9) } // SetReturnValue set the return value if skip the syscall @@ -50,6 +47,15 @@ func (c *Context) SetReturnValue(retval int) { } func (c *Context) skipSyscall() error { - c.regs.Orig_rax = uint64(1<<64 - 1) //-1 + c.regs.Orig_rax = ^uint64(0) //-1 return syscall.PtraceSetRegs(c.Pid, &c.regs) } + +func getIovecs(base *byte, l int) []unix.Iovec { + return []unix.Iovec{ + unix.Iovec{ + Base: base, + Len: uint64(l), + }, + } +} diff --git a/tracer/context_helper.go b/tracer/context_helper.go new file mode 100644 index 0000000..12ed943 --- /dev/null +++ b/tracer/context_helper.go @@ -0,0 +1,76 @@ +package tracer + +import ( + "syscall" + "unsafe" + + unix "golang.org/x/sys/unix" +) + +// TODO: make this method not to call ptrace too much +func ptraceReadStr(pid int, addr uintptr, buff []byte) { + syscall.PtracePeekData(pid, addr, buff) +} + +func processVMReadv(pid int, localIov, remoteIov []unix.Iovec, + flags uintptr) (r1, r2 uintptr, err syscall.Errno) { + return syscall.Syscall6(unix.SYS_PROCESS_VM_READV, uintptr(pid), + uintptr(unsafe.Pointer(&localIov[0])), uintptr(len(localIov)), + uintptr(unsafe.Pointer(&remoteIov[0])), uintptr(len(remoteIov)), + flags) +} + +func vmRead(pid int, addr uintptr, buff []byte) (int, error) { + l := len(buff) + localIov := getIovecs(&buff[0], l) + remoteIov := getIovecs((*byte)(unsafe.Pointer(addr)), l) + n, _, err := processVMReadv(pid, localIov, remoteIov, uintptr(0)) + return int(n), err +} + +func vmReadStr(pid int, addr uintptr, buff []byte) error { + // Deal with unaligned addr + n := 0 + r := pageSize - int(addr%uintptr(pageSize)) + if r == 0 { + r = pageSize + } + + for len(buff) > 0 { + if l := len(buff); r < l { + r = l + } + + nn, err := vmRead(pid, addr+uintptr(n), buff[:r]) + if err != nil { + return err + } + + if hasNull(buff[:nn]) { + return nil + } + + n += nn + buff = buff[nn:] + r = pageSize + } + return nil +} + +func hasNull(buff []byte) bool { + for _, b := range buff { + if b == 0 { + return true + } + } + return false +} + +func clen(b []byte) int { + for i := 0; i < len(b); i++ { + if b[i] == 0 { + return i + } + } + return len(b) + 1 +} diff --git a/tracer/tracer.go b/tracer/tracer.go index 4ddb75d..eb58eaf 100644 --- a/tracer/tracer.go +++ b/tracer/tracer.go @@ -88,11 +88,6 @@ type TraceResult struct { // NewTracer return new Tracer with default setting func NewTracer() Tracer { - allow := make([]string, len(defaultAllows)) - copy(allow, defaultAllows) - - trace := make([]string, len(defaultTraces)) - copy(trace, defaultTraces) // default settings return Tracer{ TimeLimit: 1, @@ -101,7 +96,7 @@ func NewTracer() Tracer { OutputLimit: 64, StackLimit: 1024, - Allow: allow, - Trace: trace, + Allow: append([]string{}, defaultAllows...), + Trace: append([]string{}, defaultTraces...), } }