繁体   English   中英

ptrace - 与子进程通信

[英]Ptrace - communication with child process

我想以下列方式使用ptrace (伪代码):

孩子:

    foo();
    now that foo is done parent should use ptrace to change things
    parent did what he wanted to do
    bar();  

家长:

pid = fork();
if (pid == 0)
    //child
    exec(child_program)
else
    //parent
    attach ptrace
    let child run
    use ptrace to modify it's data
    let child continue
  1. 孩子应该如何与父母沟通它已完成foo并准备好进行修改? raise(SIGSTOP)也许?

  2. 父母应该如何等待孩子运行foo

我认为我们可以假设在使用pthread之前没有引发 SIGSTOP。

我可能会误解它,但是您是否有任何特定原因想要将 `ptrace` 用于看起来像 IPC(进程间通信)的内容? Linux 上的`ptrace` *通常不太适合* IPC,您不应该真正使用它来修改子进程中的数据。 如果您希望您的子进程与父进程通信,则有多种不同的方法可以实现上述任务(即 Unix 域套接字、管道、信号量、共享内存),我建议您在尝试使用 IPC 之前先查看它们`ptrace`。


编辑:

您可以使用信号量让父sem_overview等待子sem_overview (请参阅 Linux 手册页中的sem_overview )并执行您需要执行的操作。 您可以使用sem_open创建一个命名信号量,并让子sem_open在父sem_open等待它,让子sem_open在完成上述任务后通知信号量。

或者,让被跟踪的子进程使用断点指令,该指令将通过SIGTRAP停止它,允许您wait它,然后执行您需要执行的操作。 我相信 GDB 使用类似的方法进行调试(修补指令)。 如果您使用的是 x86,以下代码应该适用于在您的代码中发出断点指令:

asm volatile ("int3;")

我还可以建议使用process_vm_writev而不是ptrace函数来写入进程内存( PTRACE_POKETEXT ),因为它们可以对进程内存进行批量读取/写入。

为了进一步参考,我认为debuggers_part2_code是一个很好的例子,说明如何推出自己的调试工具。

你说你想在执行的某个时刻修改他跟踪进程的寄存器。 您可能应该尝试澄清您的问题,因为它并不清楚您真正想要实现的目标:首先为什么要修改寄存器。 您希望在寄存器中找到什么? 为什么要更改这些值?

您确定不想与套接字和/或共享内存通信吗? 您可能应该提供一个更详细的示例来解释您要做什么。

现有代码中的断点

您在跟踪的过程中有此代码:

foo();
// You want to modify something there.
bar();

foo()bar() ,真的不清楚寄存器中的内容。 假设我们使用的是 x86_64。

如果在foo返回时中断:

  • EAX 包含foo的返回值(如果有),无论如何它在调用者中被忽略(因此修改它没有多大意义);

  • 被调用者保存的寄存器可能包含来自调用者的一些值,但您必须弄乱 DWARF 信息以试图从中获得一些意义;

  • 调用者保存的寄存器将不包含任何有用的内容(但您可以使用 DWARF 展开信息来查找其他一些在调用者中有意义的数据)。

bar的调用位置(在调用者中或在bar的开头)中断对您来说可能更有趣,因为您可以访问bar的参数。 您可以在跟踪器进程中修改它们,如果需要,您甚至可以强制使用一个值进行返回调用。

提高信号

另一种解决方案是发出信号:

foo();
raise(SIGTRAP);
bar();

和以前一样,不清楚寄存器中的内容,您可能必须使用 DWARF 来尝试定位感兴趣的数据(可能有效也可能无效)。

一个(可能)更干净的解决方案是通过指令引发异常:

int     $3

问题是如果你的程序没有在跟踪器下运行,它就会死掉。

为追踪器添加一个钩子

一个更简洁的解决方案是在foobar之间添加另一个函数:

foo();
int res = delegate_to_tracer(x, y, z);
bar();

其中delegate_to_tracer可以存根为:

int delegate_to_tracer(int x, int y, int z)
{
  // No-op implementation used when there is no tracer:
  return 0;
}

您现在可以在此函数的开头添加一个断点,以便在跟踪器中处理它的功能:

  • 您可以访问参数;

  • 你可以修改它们;

  • 您可以使用给定的返回值强制返回。

另一个类似的解决方案是使用静态跟踪点( SDTUST ),但尝试修改它们的数据可能没有多大意义。

伪造系统调用

您可以使用系统调用来与跟踪器通信:

  • 要么使用未使用的系统调用NR_tuxcall ?)

  • 要么使用一个未使用的系统调用号(但它可能会在某个时候被使用);

  • 或者通过蹲一个现有的。

这个想法是,如果它不在您的跟踪器下运行,系统调用将因SIGSYS (或其他)而失败。 但是,在您的跟踪器下,您将拦截系统调用并自行处理。

进行 tuxcall:

movq    $184, %rax # tuxcall
movq    $42,  %edi # param1
syscall

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM