8

I'm looking for an efficient way to access(for both read and write operations) the memory space of my ptraced child process. The size of blocks being accessed may vary from several bytes up to several megabytes in size, so using the ptrace call with PTRACE_PEEKDATA and PTRACE_POKEDATA which read only one word at a time and switch context every time they're called seems like a pointless waste of resources. The only one alternative solution I could find, though, was the /proc/<pid>/mem file, but it has long since been made read only.

Is there any other (relatively simple) way to do that job? The ideal solution would be to somehow share the address space of my child process with its parent and then use the simple memcpy call to copy data I need in both directions, but I have no clues how to do it and where to begin.

Any ideas?

vovick
  • 298
  • 1
  • 8
  • you want to look at any memory information or at a particular piece of data that you want to share ? I mean, you want to acess the whole memory space ? – LB40 Jun 06 '09 at 02:00
  • I want to access particular pieces of data whose location, length and number vary every time I launch the program. But for that I think that access to the whole memory space of the traced process is the most natural and easy way to read and modify them. – vovick Jun 06 '09 at 07:00
  • 1
    ah i had this very same question about 3 years ago! =) – Matt Joiner Feb 08 '10 at 15:42

6 Answers6

5

If this is Linux (which the tags suggest it is), you can share the entirety of the child's address space with the parent by using clone() with the CLONE_VM flag. As the two processes share the same VM space, all modifications will be immediately visible between the two, with essentially zero overhead.

This does mean you can't then exec() in the child; as it will replace the VM space of both processes.

DaveR
  • 9,540
  • 3
  • 39
  • 58
  • won't this require access to the child's source code to invoke said clone(), or are you clone()ing from the parent, and then calling ptrace() from the new "parent thread"? – Matt Joiner Feb 08 '10 at 15:45
  • 1
    this seems like the best answer, but i'm not sure it's possible to "intrude" on the target process without being able to access it's source, or interposing its calls, as osgx suggests – Matt Joiner Feb 08 '10 at 15:49
2

Are you in control of the child process and its sourcecode? If so, you could consider using Shared memory.

Kasper
  • 2,451
  • 2
  • 17
  • 19
1

Consider injecting of some debug function into ptraced process and calling it via ptrace_setregs. Something like the way how gdb can run any function of ptraced process.

Also you can try to inject some code into process via LD_PRELOAD. You can even try to make in work without ptrace, using signals.

upd1: Gdb injection or "inferior function call" is rather complicated. See function call_function_by_hand in file gdb-6.6.50.20070809›gdb›infcall.c here: 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);
  }
osgx
  • 90,338
  • 53
  • 357
  • 513
1

For reading, your best bet is to parse the /proc/<pid>/maps file for the virtual addresses of the memory regions of interest.

You can then read these by opening /proc/<pid>/mem and perform read() call with a large buffer on areas of interest.

For writing, I've yet to find an easy way to write entire blocks, I believe this has to do with locking and stability for the child process, calls through ptrace() can guarantee this, but direct access to another process' memory cannot. I typically write a wrapper around ptrace(PTRACE_POKEDATA, ...) to mirror Windows' WriteProcessMemory().

Matt Joiner
  • 112,946
  • 110
  • 377
  • 526
0

If you're in control of the child process, maybe you could add a debug interface that allows you to write to the memory in question?

Douglas Leeder
  • 52,368
  • 9
  • 94
  • 137
0

clone or mmap are what you are looking for. mmap a temp file between the two processes and use that memory space for passing data back and forth.

nategoose
  • 12,054
  • 27
  • 42