【问题标题】:ptrace'ing of parent processptrace'ing 父进程
【发布时间】:2011-01-14 00:49:46
【问题描述】:

子进程能否使用ptrace系统调用来跟踪其父进程?

操作系统是 linux 2.6

谢谢。

更新1: 我想从“自身”追踪 process1。这是不可能的,所以我分叉并尝试从子进程中执行ptrace(process1_pid, PTRACE_ATTACH)。但我不能,有一个奇怪的错误,比如内核禁止子跟踪他们的父进程

UPD2:安全策略可以禁止此类跟踪。哪些政策会这样做?内核中的检查代码在哪里?

UPD3:在我的嵌入式 linux 上,PEEKDATA 没有错误,但 GETREGS 没有:

child: getregs parent: -1
errno is 1, strerror is Operation not permitted 

errno = EPERM

【问题讨论】:

  • 您看到奇怪的错误后出现什么错误 (errno)?
  • osgx,下面我的回答是否正确地测试了您正在尝试的父进程的跟踪?
  • 既然你反正是在分叉,那你为什么不做相反的事情,即从父母那里追踪孩子?
  • 我需要检查完整状态。在 fork 之后,一些 文件描述符和 mmap(例如使用 MAP_PRIVATE 或 MADV_DONTFORK )将被清除。

标签: linux fork trace ptrace


【解决方案1】:

这个问题让我很感兴趣。所以我写了一些代码来尝试一下。

首先请记住,在跟踪进程时,跟踪进程在大多数情况下都会成为父进程,但名称除外(即getppid())。首先,手册中PTRACE_ATTACH 部分的 sn-p 很有帮助:

   PTRACE_ATTACH
          Attaches to the process specified in pid,  making  it  a  traced
          "child"  of the calling process; the behavior of the child is as
          if it had done a PTRACE_TRACEME.  The calling  process  actually
          becomes the parent of the child process for most purposes (e.g.,
          it will receive notification of  child  events  and  appears  in
          ps(1)  output  as  the  child's parent), but a getppid(2) by the
          child will still return the PID of  the  original  parent.   The
          child  is  sent a SIGSTOP, but will not necessarily have stopped
          by the completion of this call; use  wait(2)  to  wait  for  the
          child to stop.  (addr and data are ignored.)

现在这是我编写的代码,用于测试并验证您实际上可以ptrace() 您的父母(您可以通过将其转储到名为blah.c 的文件中并运行make blah 来构建它:

#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/ptrace.h>

int main()
{
    pid_t pid = fork();
    assert(pid != -1);
    int status;
    long readme = 0;
    if (pid)
    {
        readme = 42;
        printf("parent: child pid is %d\n", pid);
        assert(pid == wait(&status));
        printf("parent: child terminated?\n");
        assert(0 == status);
    }
    else
    {
        pid_t tracee = getppid();
        printf("child: parent pid is %d\n", tracee);
        sleep(1); // give parent time to set readme
        assert(0 == ptrace(PTRACE_ATTACH, tracee));
        assert(tracee == waitpid(tracee, &status, 0));
        printf("child: parent should be stopped\n");
        printf("child: peeking at parent: %ld\n", ptrace(PTRACE_PEEKDATA, tracee, &readme));
    }
    return 0;
}

请注意,我正在利用父虚拟地址空间的复制来了解查找位置。另请注意,当孩子随后终止时,我怀疑存在必须允许父母继续的隐式分离,我没有进一步调查。

【讨论】:

  • 是的,确实如此。也许您正在运行一个带有一些默认进程安全策略的发行版,我认为 Fedora 就是这样做的。它对你有用吗?您应该看到孩子偷看,并打印来自父母的值。
  • 这段代码似乎可以工作...在 linux/x86 和我的 linux(嵌入式)上
  • 自然...我确实对其进行了测试:P 玩这些东西非常有趣。您可能会遇到我在早期测试时遇到的“失效”流程问题。您必须仔细管理跟踪子状态。它需要在停止时发送 SIGCONT,并且您必须在附加完成时等待初始 SIGSTOP..
  • 马特,请看这个q:stackoverflow.com/questions/958369/…你怎么看?
  • 在我的嵌入式 linux 上禁用了 GETREGS... :( 请参阅此问题的更新。
【解决方案2】:

是的,有可能... 甚至 GETREGS 也有效。 在 x86 上检查 (基于 Matt Joiner 代码,谢谢他)

#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/user.h>

int main()
{
    pid_t pid = fork();
//    assert(pid != -1);
    int status;
    long readme = 0;
    struct user_regs_struct regs;
    if (pid)
    {
        readme = 42;
        printf("parent: child pid is %d\n", pid);
        assert(pid == wait(&status));
        printf("parent: child terminated?\n");
        assert(0 == status);
    }
    else
    {
        pid_t tracee = getppid();
        printf("child: parent pid is %d\n", tracee);
        sleep(1); // give parent time to set readme
        assert(0 == ptrace(PTRACE_ATTACH, tracee));
        assert(tracee == waitpid(tracee, &status, 0));
        printf("child: parent should be stopped\n");
        printf("child: peeking at parent: %ld\n", ptrace(PTRACE_PEEKDATA, tracee, &readme, NULL));
        printf("Regs was %p, %p, %p, %p; &status is %p \n", regs.eax, regs.ebx, regs.ecx, regs.edx, &status);
        printf("child: getregs parent: %ld\n", ptrace(PTRACE_GETREGS, tracee, NULL, &regs));
        printf("Regs is %p, %p, %p, %p; &status is %p \n", regs.eax, regs.ebx, regs.ecx, regs.edx, &status);
    }
    return 0;
}

结果:

child: parent pid is 1188
parent: child pid is 1189
child: parent should be stopped
child: peeking at parent: 42
Regs was (nil), (nil), (nil), (nil); &status is 0xbfffea50
child: getregs parent: 0
Regs is 0xfffffe00, 0xffffffff, 0xbfffea50, (nil); &status is 0xbfffea50
parent: child terminated?

【讨论】:

  • 是的,但是添加了 GETREGS。我有问题。
  • osgx,尝试为 getregs 问题创建另一个问题
  • 抱歉,这个嵌入式系统使用了特定且不那么广泛的分布式内核。所以我会自己检查内核。我认为在所有 linux 内核上都不允许跟踪父级,但仅在此修改后的内核中存在问题,并且仅在 GETREGS 中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-08-09
  • 1970-01-01
  • 2013-08-11
  • 2011-04-20
  • 1970-01-01
  • 1970-01-01
  • 2017-04-15
相关资源
最近更新 更多