【问题标题】:Ptrace catching many traps for execvePtrace 为 execve 捕获了许多陷阱
【发布时间】:2018-04-10 21:54:17
【问题描述】:

我正在使用 ptrace 来拦截系统调用。除了我拦截了 16 个对 execve 的调用(8 个用于系统前调用,8 个用于系统调用后)之外,一切似乎都运行良好。

我见过没有它的工作示例,但我正在尝试使用标志 PTRACE_O_TRACESYSGOOD

其他answers to ptrace problems 表示我应该只看到一个前/后+一个信号,但他们没有使用PTRACE_O_TRACESYSGOOD

我的输出如下:

Intercepted rt_sigprocmask[14]
Syscall returned with value 0
Intercepted execve[59]
Syscall returned with value -2
Intercepted execve[59]
Syscall returned with value -2
Intercepted execve[59]
Syscall returned with value -2
Intercepted execve[59]
Syscall returned with value -2
Intercepted execve[59]
Syscall returned with value -2
Intercepted execve[59]
Syscall returned with value -2
Intercepted execve[59]
Syscall returned with value -2
Intercepted execve[59]
Syscall returned with value 0
Tracer: Received signal: 5
Intercepted brk[12]
...

输出的其余部分与strace 的输出相匹配。

每个“拦截”和“返回的系统调用”对应一个 waitid() 调用。重现此的极简示例代码:

#include <sys/types.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/user.h>
#include <sys/vfs.h>
#include <sys/ptrace.h>
#include <sys/reg.h>     /* For constants ORIG_EAX, etc */
#include <string.h>
#include <sys/wait.h>
#include <sys/syscall.h>    /* For SYS_write, etc */
#include <unistd.h>
#include <stdio.h>

int main(){
  pid_t pid = fork();

  // Child.
  if(pid == 0){
    ptrace(PTRACE_TRACEME, 0, NULL, NULL);

    // Wait for parent to be ready.
    raise(SIGSTOP);
    execlp("pwd", "pwd", NULL);
    return 0;
  }
  // Tracer.
  else{
    struct user_regs_struct regs;
    bool isPre = true;
    int status;
    // Wait for child to stop itself.
    waitpid(pid, &status, 0);
    ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACESYSGOOD);

    while(true){
      ptrace(PTRACE_SYSCALL, pid, 0, 0);
      pid  = waitpid(pid, &status, 0);

      // Check if tracee has exited.
      if (WIFEXITED(status)){
    return 0;
      }

      // This is a stop caused by a system call exit-pre/exit-post.
      if(WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP |0x80) ){
        ptrace(PTRACE_GETREGS, pid, NULL, &regs);

        if(isPre){
          printf("Intercepted syscall: %llu\n", regs.orig_rax);
          isPre = ! isPre;
        }else{
          printf("Done with system call!\n");
          isPre = ! isPre;
        }
      }else{
        printf("Tracer: Received signal: %d\n", WSTOPSIG(status));
      }
    }
  }

  return 0;
}

我担心我误解了execvePTRACE_O_TRACESYSGOOD。 我在内核版本为 4.10.0-37-generic 的 Lubuntu 16.04 上运行它。

编辑:修复了系统调用的返回值。

【问题讨论】:

    标签: c system-calls ptrace


    【解决方案1】:

    没有错。对execlp 的一次调用通常会导致对execve 的多次调用,每个调用(最后一个除外)都返回ENOENT 作为错误代码。

    尽管execlpexecvp 经常记录在Unix 和Linux 手册的第2 部分(系统调用)中,但它们是作为用户空间函数实现的。他们查看$PATH 并在每个$PATH 组件和可执行文件名称的连接上调用execve,直到一个execve 成功或全部失败。

    以下是来自musl 的一些来源,说明了正在发生的事情:

    if (strchr(file, '/'))
        return execve(file, argv, envp);
    
    if (!path) path = "/usr/local/bin:/bin:/usr/bin";
    ...
    for(p=path; ; p=z) {
        char b[l+k+1];
        z = strchr(p, ':');
        if (!z) z = p+strlen(p);
        if (z-p >= l) {
            if (!*z++) break;
            continue;
        }
        memcpy(b, p, z-p);
        b[z-p] = '/';
        memcpy(b+(z-p)+(z>p), file, k+1);
        execve(b, argv, envp);
        if (errno == EACCES) seen_eacces = 1;
        else if (errno != ENOENT) return -1;
        if (!*z++) break;
    }
    if (seen_eacces) errno = EACCES;
    return -1;
    

    【讨论】:

    • 完美,这正是我想要的!由于像 strace 这样的工具只显示了一次对 execve 的调用,我认为我有一个错误。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多