Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add arm64 compatibility #6

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 67 additions & 65 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"syscall"

"github.com/orivej/e"
s "github.com/orivej/fptrace/syscalls"
"golang.org/x/sys/unix"
"golang.org/x/text/collate"
"golang.org/x/text/language"
Expand Down Expand Up @@ -196,8 +197,8 @@ func mainLoop(sys *SysState, mainPID int, onExec func(*ProcState), onExit func(*
newpid := int(unewpid)
cloneFiles := false
if trapCause == syscall.PTRACE_EVENT_CLONE {
regs, ok := getRegs(pid)
cloneFiles = ok && regs.Rdx&syscall.CLONE_FILES != 0
regs, ok := getSysexitRegs(pstate, pid)
cloneFiles = ok && regs.arg2&syscall.CLONE_FILES != 0
}
pstates[newpid] = pstate.Clone(cloneFiles)
running[newpid] = true
Expand Down Expand Up @@ -273,80 +274,81 @@ func terminate(pid int, pstate *ProcState, onExit func(p *ProcState)) {
}

func sysenter(pid int, pstate *ProcState, sys *SysState) bool {
regs, ok := getRegs(pid)
regs, ok := getSysenterRegs(pstate, pid)
if !ok {
return false
}
pstate.Syscall = int(regs.Orig_rax)
pstate.Syscall = int(regs.syscall)
switch pstate.Syscall {
case syscall.SYS_EXECVE:
case s.SYS_EXECVE:
pstate.NextCmd = Cmd{
Path: pstate.Abs(readString(pid, regs.Rdi)),
Args: readStrings(pid, regs.Rsi),
Path: pstate.Abs(readString(pid, regs.arg0)),
Args: readStrings(pid, regs.arg1),
Dir: pstate.CurDir,
}
if *flEnv {
pstate.NextCmd.Env = readStrings(pid, regs.Rdx)
pstate.NextCmd.Env = readStrings(pid, regs.arg2)
}
fmt.Println(pid, "execve", pstate.NextCmd)
case unix.SYS_EXECVEAT:
case s.SYS_EXECVEAT:
pstate.NextCmd = Cmd{
Path: absAt(int32(regs.Rdi), readString(pid, regs.Rsi), pid, pstate, sys),
Args: readStrings(pid, regs.Rdx),
Path: absAt(int32(regs.arg0), readString(pid, regs.arg1), pid, pstate, sys),
Args: readStrings(pid, regs.arg2),
Dir: pstate.CurDir,
}
if *flEnv {
pstate.NextCmd.Env = readStrings(pid, regs.R10)
pstate.NextCmd.Env = readStrings(pid, regs.arg3)
}
fmt.Println(pid, "execveat", pstate.NextCmd)
case syscall.SYS_UNLINK, syscall.SYS_RMDIR:
case s.SYS_UNLINK, s.SYS_RMDIR:
if *flUndelete {
regs.Orig_rax = syscall.SYS_ACCESS
regs.Rsi = syscall.F_OK
err := syscall.PtraceSetRegs(pid, &regs)
regs.syscall = s.SYS_ACCESS
regs.arg1 = unix.F_OK
err := ptraceSetSysenterRegs(pstate, pid, regs)
e.Exit(err)
}
case syscall.SYS_UNLINKAT:
case s.SYS_UNLINKAT:
if *flUndelete {
regs.Orig_rax = syscall.SYS_FACCESSAT
regs.R10 = regs.Rdx
regs.Rdx = syscall.F_OK
err := syscall.PtraceSetRegs(pid, &regs)
regs.syscall = s.SYS_FACCESSAT
regs.arg3 = regs.arg2
regs.arg2 = unix.F_OK
err := ptraceSetSysenterRegs(pstate, pid, regs)
e.Exit(err)
}
}
return true
}

func sysexit(pid int, pstate *ProcState, sys *SysState) bool {
regs, ok := getRegs(pid)
regs, ok := getSysexitRegs(pstate, pid)
if !ok {
return false
}
ret := int(regs.Rax)
ret := int(regs.ret)
if ret < 0 {
return true
}
ret32 := int32(ret)
if pstate.Syscall == syscall.SYS_FCNTL {
switch regs.Rsi {
if pstate.Syscall == s.SYS_FCNTL {
switch regs.arg1 {
case syscall.F_DUPFD:
pstate.Syscall = syscall.SYS_DUP
pstate.Syscall = s.SYS_DUP
case syscall.F_DUPFD_CLOEXEC:
pstate.Syscall = syscall.SYS_DUP3
regs.Rdx = syscall.O_CLOEXEC
pstate.Syscall = s.SYS_DUP3
regs.arg2 = syscall.O_CLOEXEC
case syscall.F_SETFD:
b := regs.Rdx&syscall.FD_CLOEXEC != 0
pstate.FDCX[int32(regs.Rdi)] = b
fmt.Println(pid, "fcntl/setfd", regs.Rdi, b)
b := regs.arg2&syscall.FD_CLOEXEC != 0
pstate.FDCX[int32(regs.arg0)] = b
fmt.Println(pid, "fcntl/setfd", regs.arg0, b)
}
}
switch pstate.Syscall {
case syscall.SYS_OPEN, syscall.SYS_OPENAT:
call, at, name, flags := "open", int32(unix.AT_FDCWD), regs.Rdi, regs.Rsi
if pstate.Syscall == syscall.SYS_OPENAT {
call, at, name, flags = "openat", int32(regs.Rdi), regs.Rsi, regs.Rdx
case s.SYS_OPEN, s.SYS_OPENAT:
call, at, name, flags := "open", int32(unix.AT_FDCWD), regs.arg0, regs.arg1
if pstate.Syscall == s.SYS_OPENAT {
call, at, name, flags = "openat", int32(regs.arg0), regs.arg1, regs.arg2
}

var path string
switch {
default:
Expand Down Expand Up @@ -375,72 +377,72 @@ func sysexit(pid int, pstate *ProcState, sys *SysState) bool {
}
}
pstate.IOs.Map[write].Add(inode)
case syscall.SYS_CHDIR:
path := pstate.Abs(readString(pid, regs.Rdi))
case s.SYS_CHDIR:
path := pstate.Abs(readString(pid, regs.arg0))
pstate.CurDir = path
fmt.Println(pid, "chdir", path)
case syscall.SYS_FCHDIR:
path := sys.FS.Path(pstate.FDs[int32(regs.Rdi)])
case s.SYS_FCHDIR:
path := sys.FS.Path(pstate.FDs[int32(regs.arg0)])
pstate.CurDir = path
fmt.Println(pid, "fchdir", path)
case syscall.SYS_LINK:
oldpath := pstate.Abs(readString(pid, regs.Rdi))
newpath := pstate.Abs(readString(pid, regs.Rsi))
case s.SYS_LINK:
oldpath := pstate.Abs(readString(pid, regs.arg0))
newpath := pstate.Abs(readString(pid, regs.arg1))
oldnode := sys.FS.Inode(oldpath)
if !pstate.IOs.Map[W].Has[oldnode] {
pstate.IOs.Map[R].Add(oldnode)
}
pstate.IOs.Map[W].Add(sys.FS.Inode(newpath))
fmt.Println(pid, "link", oldpath, newpath)
case syscall.SYS_LINKAT:
oldpath := absAt(int32(regs.Rdi), readString(pid, regs.Rsi), pid, pstate, sys)
newpath := absAt(int32(regs.Rdx), readString(pid, regs.R10), pid, pstate, sys)
case s.SYS_LINKAT:
oldpath := absAt(int32(regs.arg0), readString(pid, regs.arg1), pid, pstate, sys)
newpath := absAt(int32(regs.arg2), readString(pid, regs.arg3), pid, pstate, sys)
oldnode := sys.FS.Inode(oldpath)
if !pstate.IOs.Map[W].Has[oldnode] {
pstate.IOs.Map[R].Add(oldnode)
}
pstate.IOs.Map[W].Add(sys.FS.Inode(newpath))
fmt.Println(pid, "linkat", oldpath, newpath)
case syscall.SYS_RENAME:
oldpath := pstate.Abs(readString(pid, regs.Rdi))
newpath := pstate.Abs(readString(pid, regs.Rsi))
case s.SYS_RENAME:
oldpath := pstate.Abs(readString(pid, regs.arg0))
newpath := pstate.Abs(readString(pid, regs.arg1))
sys.FS.Rename(oldpath, newpath)
fmt.Println(pid, "rename", oldpath, newpath)
case syscall.SYS_RENAMEAT, unix.SYS_RENAMEAT2:
oldpath := absAt(int32(regs.Rdi), readString(pid, regs.Rsi), pid, pstate, sys)
newpath := absAt(int32(regs.Rdx), readString(pid, regs.R10), pid, pstate, sys)
case s.SYS_RENAMEAT, s.SYS_RENAMEAT2:
oldpath := absAt(int32(regs.arg0), readString(pid, regs.arg1), pid, pstate, sys)
newpath := absAt(int32(regs.arg2), readString(pid, regs.arg3), pid, pstate, sys)
sys.FS.Rename(oldpath, newpath)
fmt.Println(pid, "renameat", oldpath, newpath)
case syscall.SYS_DUP, syscall.SYS_DUP2, syscall.SYS_DUP3:
pstate.FDs[ret32] = pstate.FDs[int32(regs.Rdi)]
if pstate.Syscall == syscall.SYS_DUP3 && regs.Rdx&syscall.O_CLOEXEC != 0 {
case s.SYS_DUP, s.SYS_DUP2, s.SYS_DUP3:
pstate.FDs[ret32] = pstate.FDs[int32(regs.arg0)]
if pstate.Syscall == s.SYS_DUP3 && regs.arg2&syscall.O_CLOEXEC != 0 {
pstate.FDCX[ret32] = true
}
fmt.Println(pid, "dup", regs.Rdi, ret32, pstate.FDCX[ret32])
case syscall.SYS_READ, syscall.SYS_PREAD64, syscall.SYS_READV, syscall.SYS_PREADV, unix.SYS_PREADV2:
inode := pstate.FDs[int32(regs.Rdi)]
fmt.Println(pid, "dup", regs.arg0, ret32, pstate.FDCX[ret32])
case s.SYS_READ, s.SYS_PREAD64, s.SYS_READV, s.SYS_PREADV, s.SYS_PREADV2:
inode := pstate.FDs[int32(regs.arg0)]
if inode != 0 && !pstate.IOs.Map[W].Has[inode] {
pstate.IOs.Map[R].Add(inode)
}
case syscall.SYS_WRITE, syscall.SYS_PWRITE64, syscall.SYS_WRITEV, syscall.SYS_PWRITEV, unix.SYS_PWRITEV2:
inode := pstate.FDs[int32(regs.Rdi)]
case s.SYS_WRITE, s.SYS_PWRITE64, s.SYS_WRITEV, s.SYS_PWRITEV, s.SYS_PWRITEV2:
inode := pstate.FDs[int32(regs.arg0)]
if inode != 0 {
pstate.IOs.Map[W].Add(inode)
}
case syscall.SYS_CLOSE:
n := int32(regs.Rdi)
case s.SYS_CLOSE:
n := int32(regs.arg0)
pstate.FDs[n] = 0
delete(pstate.FDCX, n)
fmt.Println(pid, "close", regs.Rdi)
case syscall.SYS_PIPE:
fmt.Println(pid, "close", regs.arg0)
case s.SYS_PIPE, s.SYS_PIPE2:
var buf [8]byte
_, err := syscall.PtracePeekData(pid, uintptr(regs.Rdi), buf[:])
_, err := syscall.PtracePeekData(pid, uintptr(regs.arg0), buf[:])
e.Exit(err)
readfd := int32(binary.LittleEndian.Uint32(buf[:4]))
writefd := int32(binary.LittleEndian.Uint32(buf[4:]))
inode := sys.FS.Pipe()
pstate.FDs[readfd], pstate.FDs[writefd] = inode, inode
if regs.Rsi&syscall.O_CLOEXEC != 0 {
if regs.arg1&syscall.O_CLOEXEC != 0 {
pstate.FDCX[readfd], pstate.FDCX[writefd] = true, true
}
fmt.Println(pid, "pipe", readfd, writefd, pstate.FDCX[readfd])
Expand Down
1 change: 1 addition & 0 deletions pstate.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type Cmd struct {
type ProcState struct {
SysEnter bool // true on enter to syscall
Syscall int // call number on exit from syscall
Arg0 uint64 // syscall arg0 on syscall exit
CurDir string // working directory
CurCmd *Cmd // current command
NextCmd Cmd // command after return from execve
Expand Down
25 changes: 25 additions & 0 deletions regs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package main

import "syscall"

type Regs struct {
syscall int64
arg0 uint64
arg1 uint64
arg2 uint64
arg3 uint64
arg4 uint64
arg5 uint64
ret uint64

platform syscall.PtraceRegs
}

func getPlatformRegs(pid int) (syscall.PtraceRegs, bool) {
var regs syscall.PtraceRegs
err := syscall.PtraceGetRegs(pid, &regs)
if err != nil && err.(syscall.Errno) == syscall.ESRCH {
return regs, false
}
return regs, true
}
58 changes: 58 additions & 0 deletions regs_amd64.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//go:build linux && amd64
// +build linux,amd64

package main

import (
"syscall"
)

func getSysenterRegs(pstate *ProcState, pid int) (regs Regs, ok bool) {
regs.platform, ok = getPlatformRegs(pid)
if !ok {
return regs, false
}

regs.syscall = int64(regs.platform.Orig_rax)
regs.arg0 = regs.platform.Rdi
regs.arg1 = regs.platform.Rsi
regs.arg2 = regs.platform.Rdx
regs.arg3 = regs.platform.R10
regs.arg4 = regs.platform.R8
regs.arg5 = regs.platform.R9

return regs, true
}

func getSysexitRegs(pstate *ProcState, pid int) (regs Regs, ok bool) {
regs.platform, ok = getPlatformRegs(pid)
if !ok {
return regs, false
}

regs.syscall = int64(regs.platform.Orig_rax)
regs.arg0 = regs.platform.Rdi
regs.arg1 = regs.platform.Rsi
regs.arg2 = regs.platform.Rdx
regs.arg3 = regs.platform.R10
regs.arg4 = regs.platform.R8
regs.arg5 = regs.platform.R9
regs.ret = regs.platform.Rax

return regs, true
}

func ptraceSetSysenterRegs(pstate *ProcState, pid int, regs Regs) error {
ptraceRegs := regs.platform

ptraceRegs.Orig_rax = uint64(regs.syscall)
ptraceRegs.Rdi = regs.arg0
ptraceRegs.Rsi = regs.arg1
ptraceRegs.Rdx = regs.arg2
ptraceRegs.R10 = regs.arg3
ptraceRegs.R8 = regs.arg4
ptraceRegs.R9 = regs.arg5
ptraceRegs.Rax = regs.ret

return syscall.PtraceSetRegs(pid, &ptraceRegs)
}
62 changes: 62 additions & 0 deletions regs_arm64.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//go:build linux && arm64
// +build linux,arm64

package main

import (
"syscall"
)

func getSysenterRegs(pstate *ProcState, pid int) (regs Regs, ok bool) {
regs.platform, ok = getPlatformRegs(pid)
if !ok {
return regs, false
}

regs.syscall = int64(regs.platform.Regs[8])
regs.arg0 = regs.platform.Regs[0]
regs.arg1 = regs.platform.Regs[1]
regs.arg2 = regs.platform.Regs[2]
regs.arg3 = regs.platform.Regs[3]
regs.arg4 = regs.platform.Regs[4]
regs.arg5 = regs.platform.Regs[5]

// on arm64 Linux overrides x0 with the sys return value, and doesn't provide
// a way to retrieve the original value like with orig_rax on x64
pstate.Arg0 = regs.arg0

return regs, true
}

func getSysexitRegs(pstate *ProcState, pid int) (regs Regs, ok bool) {
regs.platform, ok = getPlatformRegs(pid)
if !ok {
return regs, false
}

regs.syscall = int64(regs.platform.Regs[8])
regs.arg0 = pstate.Arg0
regs.arg1 = regs.platform.Regs[1]
regs.arg2 = regs.platform.Regs[2]
regs.arg3 = regs.platform.Regs[3]
regs.arg4 = regs.platform.Regs[4]
regs.arg5 = regs.platform.Regs[5]
regs.ret = regs.platform.Regs[0]

return regs, true
}

func ptraceSetSysenterRegs(pstate *ProcState, pid int, regs Regs) error {
pstate.Arg0 = regs.arg0

ptraceRegs := regs.platform
ptraceRegs.Regs[8] = uint64(regs.syscall)
ptraceRegs.Regs[0] = regs.arg0
ptraceRegs.Regs[1] = regs.arg1
ptraceRegs.Regs[2] = regs.arg2
ptraceRegs.Regs[3] = regs.arg3
ptraceRegs.Regs[4] = regs.arg4
ptraceRegs.Regs[5] = regs.arg5

return syscall.PtraceSetRegs(pid, &ptraceRegs)
}
Loading