mirror of
https://github.com/criyle/go-sandbox.git
synced 2025-09-26 23:19:11 +08:00
91 lines
2.3 KiB
Go
91 lines
2.3 KiB
Go
package ptracer
|
|
|
|
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))
|
|
if err == 0 {
|
|
return int(n), nil
|
|
}
|
|
return int(n), err
|
|
}
|
|
|
|
func getIovecs(base *byte, l int) []unix.Iovec {
|
|
return []unix.Iovec{getIovec(base, l)}
|
|
}
|
|
|
|
func vmReadStr(pid int, addr uintptr, buff []byte) error {
|
|
// Handle unaligned address: calculate remaining bytes to page boundary
|
|
totalRead := 0 // Total bytes read so far
|
|
// Calculate distance to next page boundary, nextRead is the number of bytes to read
|
|
nextRead := pageSize - int(addr%uintptr(pageSize))
|
|
if nextRead == 0 {
|
|
nextRead = pageSize // If exactly at page boundary, use full page size
|
|
}
|
|
|
|
// Read in a loop until buffer is full or termination condition is met
|
|
for len(buff) > 0 {
|
|
// If remaining buffer is smaller than planned read size, reduce read size
|
|
if restToRead := len(buff); restToRead < nextRead {
|
|
nextRead = restToRead
|
|
}
|
|
|
|
// Read data from current position
|
|
curRead, err := vmRead(pid, addr+uintptr(totalRead), buff[:nextRead])
|
|
if err != nil {
|
|
return err // Read error
|
|
}
|
|
if curRead == 0 {
|
|
break // No more data to read
|
|
}
|
|
if hasNull(buff[:curRead]) {
|
|
break // Found string terminator
|
|
}
|
|
|
|
// Update counters and buffer
|
|
totalRead += curRead // Update total bytes read
|
|
buff = buff[curRead:] // Move buffer pointer
|
|
nextRead = pageSize // Reset to full page size
|
|
}
|
|
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
|
|
}
|