2009-06-05 5 views
7

Estoy buscando una manera eficiente de acceder (tanto para las operaciones de lectura como de escritura) al espacio de memoria de mi proceso secundario dirigido. El tamaño de los bloques a los que se accede puede variar desde varios bytes hasta varios megabytes de tamaño, por lo que usar la llamada ptrace con PTRACE_PEEKDATA y PTRACE_POKEDATA que solo lee una palabra a la vez y cambia el contexto cada vez que se llaman parece una pérdida inútil de recursos. La única solución alternativa que pude encontrar, sin embargo, fue el archivo /proc/<pid>/mem, pero desde hace mucho tiempo se ha hecho de solo lectura.¿Manera de bajo costo para acceder al espacio de memoria de un proceso trazado?

¿Hay alguna otra forma (relativamente simple) de hacer ese trabajo? La solución ideal sería compartir de alguna manera el espacio de direcciones de mi proceso hijo con su padre y luego usar la llamada memcpy simple para copiar los datos que necesito en ambas direcciones, pero no tengo ninguna pista sobre cómo hacerlo y dónde comenzar.

¿Alguna idea?

+0

¿desea ver cualquier información de memoria o en un dato en particular que quiera compartir? Quiero decir, ¿quieres acceder a todo el espacio de la memoria? – LB40

+0

Quiero acceder a determinados datos cuya ubicación, longitud y número varían cada vez que inicio el programa. Pero para eso creo que el acceso a todo el espacio de memoria del proceso trazado es la manera más natural y fácil de leer y modificarlos. – vovick

+1

¡ah tengo esta misma pregunta hace 3 años! =) –

Respuesta

1

¿Tiene usted el control del proceso hijo y su código fuente? De ser así, podría considerar usar Shared memory.

4

Si se trata de Linux (que las etiquetas sugieren que es), puede compartir la totalidad del espacio de direcciones del niño con el elemento principal utilizando clone() con el indicador CLONE_VM. Como los dos procesos comparten el mismo espacio de VM, todas las modificaciones serán inmediatamente visibles entre los dos, con esencialmente cero sobrecarga.

Esto significa que no puede entonces exec() en el niño; ya que reemplazará el espacio VM de ambos procesos.

+0

¿no requerirá esto el acceso al código fuente del niño para invocar dicho clon(), o está clonando() ing desde el padre, y luego llamando a ptrace() desde el nuevo "hilo padre"? –

+1

esto parece ser la mejor respuesta, pero no estoy seguro de que sea posible "entrometerse" en el proceso de destino sin poder acceder a su fuente o interponer sus llamadas, como sugiere osgx –

0

Si tiene el control del proceso secundario, ¿tal vez podría agregar una interfaz de depuración que le permita escribir en la memoria en cuestión?

1

Considere la posibilidad de inyectar alguna función de depuración en el proceso ptraced y llamarla a través de ptrace_setregs. Algo así como la forma en que gdb puede ejecutar cualquier función del proceso ptraced.

También puede intentar inyectar código mediante LD_PRELOAD. Incluso puede intentar hacer el trabajo sin ptrace, usando señales.

upd1: La inyección de Gdb o "llamada de función inferior" es bastante complicada. Ver call_function_by_hand función en el archivo GDB-6.6.50.20070809> GDB> infcall.c aquí: 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); 
    } 
0

Para la lectura, la mejor opción es para analizar el archivo /proc/<pid>/maps para las direcciones virtuales de las regiones de memoria de interés.

A continuación, puede leer estos abriendo /proc/<pid>/mem y realizar la llamada read() con un gran búfer en las áreas de interés.

Para escribir, todavía tengo que encontrar una manera fácil de escribir bloques enteros, creo que esto tiene que ver con el bloqueo y la estabilidad para el proceso secundario, las llamadas a través de ptrace() pueden garantizar esto, pero el acceso directo a otro proceso ' la memoria no puede. Normalmente escribo un contenedor alrededor de ptrace(PTRACE_POKEDATA, ...) para reflejar el 'WriteProcessMemory() de Windows'.

0

clone o mmap son lo que estás buscando. mmap un archivo temporal entre los dos procesos y usa ese espacio de memoria para pasar datos de un lado a otro.

Cuestiones relacionadas