Низкоуровневый способ доступа к пространству памяти отслеживаемого процесса? - PullRequest
7 голосов
/ 06 июня 2009

Я ищу эффективный способ доступа (как для операций чтения, так и для записи) к пространству памяти моего дочернего процесса ptraced. Размер блоков, к которым осуществляется доступ, может варьироваться от нескольких байтов до нескольких мегабайт, поэтому использование вызова ptrace с PTRACE_PEEKDATA и PTRACE_POKEDATA, который читает только одно слово за раз и переключает контекст при каждом вызове, выглядит как бессмысленная трата ресурсов. Единственное альтернативное решение, которое я смог найти, это файл /proc/<pid>/mem, но он уже давно доступен только для чтения.

Есть ли другой (относительно простой) способ сделать эту работу? Идеальным решением было бы как-то разделить адресное пространство моего дочернего процесса с его родителем, а затем использовать простой вызов memcpy для копирования нужных мне данных в обоих направлениях, но я не знаю, как это сделать и с чего начать.

Есть идеи?

Ответы [ 6 ]

4 голосов
/ 11 августа 2009

Если это Linux (что, как предполагают теги), вы можете поделиться родительским адресным пространством целиком с помощью clone() с флагом CLONE_VM. Поскольку эти два процесса совместно используют одно и то же пространство виртуальных машин, все модификации будут сразу же видны между ними, с практически нулевыми издержками.

Это значит, что вы не можете exec() у ребенка; поскольку это заменит пространство VM обоих процессов.

1 голос
/ 07 февраля 2010

Рассмотрим введение какой-либо функции отладки в процесс ptraced и вызов ее через ptrace_setregs. Что-то вроде того, как GDB может запускать любую функцию процесса ptraced.

Также вы можете попытаться внедрить некоторый код в процесс через LD_PRELOAD. Вы даже можете попытаться сделать в работе без ptrace, используя сигналы.

upd1: Внедрение GDB или «вызов функции низкого уровня» довольно сложно. Смотрите функцию call_function_by_hand в файле gdb-6.6.50.20070809 ›gdb› infcall.c здесь: 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);
  }
1 голос
/ 10 августа 2009

Контролируете ли вы дочерний процесс и его исходный код? Если это так, вы можете рассмотреть возможность использования Shared memory .

0 голосов
/ 27 марта 2010

клон или mmap - это то, что вы ищете. Отобразите временный файл между двумя процессами и используйте это пространство памяти для передачи данных туда и обратно.

0 голосов
/ 14 февраля 2010

Для чтения лучше всего проанализировать файл /proc/<pid>/maps для виртуальных адресов областей памяти, представляющих интерес.

Затем вы можете прочитать их, открыв /proc/<pid>/mem и выполнить read() вызов с большим буфером в областях, представляющих интерес.

Что касается записи, мне еще предстоит найти простой способ написания целых блоков, я думаю, что это связано с блокировкой и стабильностью дочернего процесса, вызовы через ptrace() могут гарантировать это, но прямой доступ к памяти другого процесса не может. Я обычно пишу обертку вокруг ptrace(PTRACE_POKEDATA, ...), чтобы отразить Windows * WriteProcessMemory().

0 голосов
/ 26 сентября 2009

Если вы управляете дочерним процессом, может быть, вы могли бы добавить интерфейс отладки, который позволит вам записывать данные в память?

...