Ptrace - 与子进程的沟通

时间:2016-04-17 14:04:37

标签: c linux ptrace

我想以下列方式使用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

  3. 我认为我们可以假设在使用pthread之前不会引发SIGSTOP。

2 个答案:

答案 0 :(得分:1)

<德尔> 我可能会误解它,但是你有什么特别的理由想要使用`ptrace`看起来像IPC(进程间通信)吗? Linux上的`ptrace`通常不适合用于IPC,并且您不应该真正使用它来修改子进程中的数据。 <德尔> 如果您希望您的子进程与父进程通信,则有多种不同的方法可以实现所述任务(即Unix域套接字,管道,信号量,共享内存),我建议您在尝试使用IPC之前先查看它们`ptrace`。


修改

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

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

asm volatile ("int3;")

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

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

答案 1 :(得分:0)

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

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

现有代码中的断点

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

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),但尝试修改数据可能没什么意义。

伪造系统调用

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

  • 使用unused system callNR_tuxcall?)

  • 使用未使用的系统电话号码(但可能会在某个时候使用);

  • 或蹲下现有的。

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

制作一个tuxcall:

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