低开销方式访问跟踪进程的内存空间?

时间:2009-06-05 22:05:44

标签: linux ptrace

我正在寻找一种有效的方法来访问(对于读取和写入操作)我的ptraced子进程的内存空间。正在访问的块的大小可能从几个字节到几兆字节不等,因此使用带有PTRACE_PEEKDATAPTRACE_POKEDATA的ptrace调用,一次只读取一个字,并且每次都切换上下文重新调用似乎是一种毫无意义的浪费资源。但是,我能找到的唯一一个替代解决方案是/proc/<pid>/mem文件,但它早已被翻译成只读。

还有其他(相对简单的)方法吗?理想的解决方案是以某种方式与我的父进程共享我的子进程的地址空间,然后使用简单的memcpy调用来复制我需要的两个方向的数据,但我没有线索如何做到这一点以及从哪里开始。

有什么想法吗?

6 个答案:

答案 0 :(得分:4)

如果这是Linux(标签表明它是),您可以使用带有CLONE_VM标志的clone()与父级共享整个孩子的地址空间。由于这两个进程共享相同的VM空间,因此所有修改将在两者之间立即可见,基本上没有开销。

这意味着你不能在孩子身上exec();因为它将取代两个进程的VM空间。

答案 1 :(得分:1)

您是否掌控了子进程及其源代码?如果是这样,您可以考虑使用Shared memory

答案 2 :(得分:1)

考虑将一些调试函数注入ptraced进程并通过ptrace_setregs调用它。类似于gdb如何运行ptraced进程的任何功能的方式。

此外,您可以尝试通过LD_PRELOAD将一些代码注入进程。您甚至可以尝试使用信号在没有ptrace的情况下进行工作。

upd1: Gdb注入或“次要函数调用”相当复杂。请参阅文件gdb-6.6.50.20070809> gdb> infcall.c中的函数call_function_by_hand:http://sources.debian.net/src/gdb/7.6.2-1/gdb/infcall.c?hl=462#L462

/* All this stuff with a dummy frame may seem unnecessarily complicated
   (why not just save registers in GDB?).  The purpose of pushing a dummy
   frame which looks just like a real frame is so that if you call a
   function and then hit a breakpoint (get a signal, etc), "backtrace"
   will look right.  Whether the backtrace needs to actually show the
   stack at the time the inferior function was called is debatable, but
   it certainly needs to not display garbage.  So if you are contemplating
   making dummy frames be different from normal frames, consider that.  */

/* Perform a function call in the inferior.
   ARGS is a vector of values of arguments (NARGS of them).
   FUNCTION is a value, the function to be called.
   Returns a value representing what the function returned.
   May fail to return, if a breakpoint or signal is hit
   during the execution of the function.

   ARGS is modified to contain coerced values.  */

struct value *
call_function_by_hand (struct value *function, int nargs, struct value **args)
{
...
  frame = get_current_frame ();
  gdbarch = get_frame_arch (frame);

  if (!gdbarch_push_dummy_call_p (gdbarch))
    error (_("This target does not support function calls."));

  /* A cleanup for the inferior status.
     This is only needed while we're preparing the inferior function call.  */
  inf_status = save_infcall_control_state ();
  inf_status_cleanup
    = make_cleanup_restore_infcall_control_state (inf_status);

  /* Save the caller's registers and other state associated with the
     inferior itself so that they can be restored once the
     callee returns.  To allow nested calls the registers are (further
     down) pushed onto a dummy frame stack.  Include a cleanup (which
     is tossed once the regcache has been pushed).  */
  caller_state = save_infcall_suspend_state ();
  make_cleanup_restore_infcall_suspend_state (caller_state);
...
    sp = push_dummy_code (gdbarch, sp, funaddr, args, nargs,
                  target_values_type, &real_pc, &bp_addr,
                  get_current_regcache ());
... pass args ...
  /* Create the dummy stack frame.  Pass in the call dummy address as,
     presumably, the ABI code knows where, in the call dummy, the
     return address should be pointed.  */
  sp = gdbarch_push_dummy_call (gdbarch, function, get_current_regcache (),
                bp_addr, nargs, args,
                sp, struct_return, struct_addr);
...
  /* Everything's ready, push all the info needed to restore the
     caller (and identify the dummy-frame) onto the dummy-frame
     stack.  */
  dummy_frame_push (caller_state, &dummy_id);
... 
    /* Run the inferior until it stops.  */

    e = run_inferior_call (tp, real_pc);
  }

答案 3 :(得分:0)

如果您控制子进程,也许您可​​以添加一个允许您写入相关内存的调试接口?

答案 4 :(得分:0)

对于阅读,最好的办法是解析/proc/<pid>/maps文件,查找感兴趣的内存区域的虚拟地址。

然后,您可以通过打开/proc/<pid>/mem来阅读这些内容,并在感兴趣的区域使用大缓冲区执行read()调用。

对于写作,我还没有找到一种简单的方法来编写整个块,我相信这与子进程的锁定和稳定性有关,通过ptrace()的调用可以保证这一点,但可以直接访问另一个过程'记忆不能。我通常会在ptrace(PTRACE_POKEDATA, ...)周围编写一个包装来镜像Windows WriteProcessMemory()

答案 5 :(得分:0)

clone或mmap是您正在寻找的。 mmap两个进程之间的临时文件,并使用该内存空间来回传递数据。