3

从 Go 调用程序时,我试图拦截系统调用,但是我遇到了两个问题。

孩子似乎挂起,这也挂起父进程。好像wait4(2)阻塞了,好像很奇怪,孩子最后不会叫exit(2)退出吗?

我得到的系统调用stdout不一致,有时最后一个系统调用是3,其他时候是6or 192。我的代码中有竞态条件吗?为什么会这样?

我尝试在父母身上收听信号,但我没有收到任何东西..

我已经替换了我通常运行的程序/bin/ls

package main

import (
  "syscall"
  "fmt"
  "os/signal"
  "os"
)

func main() {
  c := make(chan os.Signal, 1)
  signal.Notify(c, os.Interrupt, os.Kill)
  go SignalListener(c)

  attr := new(syscall.ProcAttr)
  attr.Sys = new(syscall.SysProcAttr)
  attr.Sys.Ptrace = true

  pid, err := syscall.ForkExec("/bin/ls", nil, attr)

  if err != nil {
    panic(err)
  }

  var wstat syscall.WaitStatus
  var regs syscall.PtraceRegs

  for {
    fmt.Println("Waiting..")
    _, err := syscall.Wait4(pid, &wstat, 0, nil)
    fmt.Printf("Exited: %d\n", wstat.Exited())

    if err != nil {
      fmt.Println(err)
      break
    }

    syscall.PtraceGetRegs(pid, &regs);
    fmt.Printf("syscall: %d\n", regs.Orig_eax)

    syscall.PtraceSyscall(pid, 0)
  }
}

func SignalListener(c <-chan os.Signal) {
  s := <-c

  fmt.Printf("Got signal %d\n", s)
}
4

1 回答 1

1

简短的回答是,用 Go 拦截系统调用将非常困难,任何 ptrace 都可能不起作用。

Go 有一个运行时,它将 go-routines 多路复用到 OS 线程上。系统调用是一个调度点,因此在系统调用返回后,您可能位于不同的线程上,而我认为 ptrace 遵循单个线程。

假设您正在执行 ptrace 的线程正在运行您的主 go-routine。然后你调用 fmt.Println(它执行 syscall.Write),所以 Go 运行时将你的 go-routine 从那个线程中取出,并在不同的 os 线程中运行系统调用(系统调用总是在不同的线程中运行)。当系统调用返回时,您的主 go-routine 将被放回可运行例程的调度程序列表中,并且它将在任何可用的 os 线程上继续,这可能不是您正在 ptrace 的那个。

这也是您无法使用gdb.

如果您只想执行一个外部程序(如 /bin/ls),您可以使用标准库中的os/exec

接近你想要做的程序可能是delve。我认为这会在每个单步的每个线程上设置一个断点,然后尝试根据 go-routine id 找到你的 go-routine 现在所在的线程。

于 2015-07-30T22:31:17.103 回答