add process_vm_readv for blocked read

This commit is contained in:
criyle 2019-04-15 18:29:08 -04:00
parent 600acacbd5
commit 80f9a1a2d4
6 changed files with 152 additions and 47 deletions

View File

@ -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)

View File

@ -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 {

View File

@ -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)])
}

View File

@ -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),
},
}
}

76
tracer/context_helper.go Normal file
View File

@ -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
}

View File

@ -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...),
}
}