Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Principal > API de Windows
Registrarse FAQ Miembros Calendario Guía de estilo Buscar Temas de Hoy Marcar Foros Como Leídos

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 01-05-2008
rolandoj rolandoj is offline
Miembro
 
Registrado: abr 2007
Posts: 395
Poder: 17
rolandoj Va por buen camino
Smile Como inyectar código directamente a un proceso externo

Hola,

Ante una de petición de Khronos, he creído util crear un hilo para analizar el método de inyección de código dentro de un proceso directamente desde nuestro programa, como contraposición al método de inyectarlo vía un DLL.

La idea surge por un problema que me he encontrado y describo en este hilo :

http://www.clubdelphi.com/foros/showthread.php?t=55785

Voy a describir el proceso tal y como yo lo entiendo, advirtiendo que hay puntos en los que tengo dudas, o pudiera estar errado, así que espero que haya quienes puedan aclararlas.

Empiezo diciendo que este método tiene fuertes restricciones; pero es muy eficiente. Su uso solo se justifica cuando necesitamos acceder a alguna información del proceso remoto que solo está disponible vía rutinas de Windows que no aceptan especificarles un proceso sino que actuan sobre
el proceso dentro del cual se ejecutan (Hay varias que trabajan así)

Programáticamente, el orden de acciones para el tema sería :

1. Definición de los datos necesarios para pasar a nuestra rutina
2. Escritura de la rutina
3. Obtención del manejador del proceso externo al que deseamos inyectar nuestro código
4. Preparación de los datos a enviarle desde nuestro programa
5. Obtención de la memoria, en el proceso externo, que usaremos en nuestro aplicativo
6. Ejecución del proceso
7. Obtención de resultados
8. Liberación de recursos

Pero, conceptualmente, creo mejor empezar discutiendo la ejecución del proceso en sí porque el entender esto abre la puerta para los demás pasos. Veamos:

Ejecutar una rutina en un proceso externo significa básicamente que una rutina compilada dentro del espacio de direcciones de nuestra aplicación vá a ejecutarse en el espacio de direcciones de un proceso que usualmente no hemos escrito nosotros y del cual ni siquiera tenemos los fuentes.

Si consideamos que cuando llamamos rutinas dentro de nuestro programa estamos referenciando a direcciones de memoria en el espacio de direcciones de nuestra aplicación, la primera dificultad que nos salta a la vista es : Y como se localizan esas direcciones desde la rutina que vamos a inyectar si esta estará ejecutandose en un espacio de direcciones diferente ?. Y de ahí se deriva otra : Si la rutina a inyectar tiene su dirección de memoria en nuestro espacio, como puede el otro proceso referenciarla y ejecutarla en su propio espacio ?

Bueno, para responder lo anterior digamos que existe una rutina de Windows llamada CreateRemoteThread que se encarga de ejecutar la nuestra en el proceso externo. Como su nombre lo indica, lo que hace es ejecutar un hilo dentro del proceso remoto, y ese hilo consiste en ejecutar la rutina que inyectamos. Veamos la definición de CreateRemoteThread en Delphi:

Código Delphi [-]
function CreateRemoteThread(hProcess: THandle; 
   lpThreadAttributes: Pointer;
   dwStackSize: DWORD; 
   lpStartAddress: TFNThreadStartRoutine; 
   lpParameter: Pointer;
   dwCreationFlags: DWORD; 
   var lpThreadId: DWORD): THandle; stdcall;

No es mi intención duplicar la explicación que puede encontrarse en Windows, sino comentar los puntos importantes :

hProcess es el manejador del proceso al cual le queremos inyectar la rutina
lpThreadAttributes es un apuntador a una estructura de atributos de seguridad para la ejecución del hilo bajo el cual correrá nuestra rutina. Si se usa Nil, el sistema usará uno por default. A efectos prácticos, a mi me ha funcionado bien con Nil; pero son bienvenidos mayores detalles

dwStackSize es el tamaño de la pila usada por el hilo y para simplicidad, usemos 0 lo que hace que el sistema adopte el mismo tamaño de la pila principal del proceso remoto

lpStartAddress es la dirección inicial de la rutina que queremos inyectar; pero, la dirección inicial que ella tendrá dentro del espacio de direcciones del proceso remoto. En otras palabras, si nuestra rutina se llamara MiRutinaRemota, este parámetro no es @MiRutinaRemota , ya que el @
nos da la dirección; pero dentro de nuestro propio espacio. Queda claro entonces que para pasar este parámetro primero tenemos que averiguar su valor, lo que realmente se hace en el paso 5 que habiamos mencionado. Por otra parte, no puede ser cualquier rutina, tiene que seguir ciertas
reglas que veremos después

lpParameter : Este parámetro representa el único parámetro que podemos pasar desde nuestro programa al proceso externo y no solo es quizás el punto más importante sino también el peor documentado en la ayuda de Windows.

En teoría, puede usarse este valor para pasar un entero a la rutina, o no pasarle nada e indicarlo usando 0; pero, para que la rutina tenga utilidad práctica, el parámetro debe ser un apuntador a una estructura de datos tipo Record que contenga los parámetros de entrada-salida que enviemos a la rutina, y eso no es todo.

La rutina recibirá este parámetro como un apuntador; pero la rutina se ejecuta en otro proceso por tanto, los datos a que apunte este valor deben estar en el espacio de direcciones de ese proceso externo, no en el nuestro. Dicho en otras palabra, si por ejemplo nosotros definimos lo
siguiente:

Código Delphi [-]
Type
    TRemoto = Record
      MIVALOR:     Integer;
      MIRESULTADO: Boolean;
    End;
Var
    MisDatos: TRemoto,

Al momento de llamar a CreateRemoteThread no podemos pasar @MisDatos como lpParameter, por el mismo razonamiento de @MiRutinaRemota. Necesitamos primero que esos datos queden en el espacio de direcciones de nuestro proceso remoto y obtener la dirección que tengan en él; lo que al igual que con la rutina, se hace en el paso 5 mencionado.

Acabo diciendo que dentro de TRemoto lo normal será colocar ciertos valores auxiliares para la lógica de nuestro problema; es decir, no se trata simplemente de colocar ahí solo los parámetros que usualmente tendría nuestra rutina, hay que incluír cierta información adicional que explicaré
cuando describa las reglas que deberá cumplir nuestra rutina

dwCreationFlags brinda facilidades para manipular el hilo. En mi caso, y creo que será lo normal, solo paso 0 porque con eso el hilo se ejecuta enseguida.

lpThreadId es una variable de salida que devuelve el identificador del hilo creado (no muy útil desde mi punto de vista).

La rutina en sí devuelve el manejador del hilo creado; lo que puede usarse desde nuestro programa para determinar cuando se termina el hilo. Igualmente, puede decirnos si hubo errores, ya que en tal caso el valor devuelto es Nil y deberíamos usar GetLastError para obtener más información del error.

Bien entendido, CreateRemoteThread está es ejecutando un hilo y, aún cuando la ejecución empiece enseguida, el control puede devolverse a nuestro programa sin que el hilo, o la rutina remota, hayan terminado; por esto, en nuestro aplicativo deberíamos controlar el fin del hilo usando el
manejador devuelto por la rutina.

Así las cosas, la llamada a CreateRemoteThread sería algo como :

Código Delphi [-]
Var
   AProcess:         THandle;
   AExtFuncAddr:     Pointer;
   AExtDataAddr:     Pointer;
   lpThreadId:       DWORD;
   hThread:          THandle;
Begin
     ...
     ..
     hThread := CreateRemoteThread(AProcess,nil,0,AExtFuncAddr,AExtDataAddr,0,lpThreadId);
     .....
     ...
End;

Bueno, creo que hasta aquí está bien de la primera parte que ya de por sí ha sido muy larga. Voy a esperar a ver si hay quienes se interesan, o puedan aportar, antes de seguír con el resto.
Responder Con Cita
  #2  
Antiguo 08-05-2008
rolandoj rolandoj is offline
Miembro
 
Registrado: abr 2007
Posts: 395
Poder: 17
rolandoj Va por buen camino
Por falta de interés, no seguirá la explicación

Hola,

A todos los que han visitado este hilo les agradezco al menos haberlo hecho y les comentó lo siguiente :

Ha pasado casi una semana y aunque ha habido un número decente de visitas, nadie ha manifestado interes por el tema, en consecuencia no continuaré con la explicación de los restantes puntos.

En lo personal, creo que el tema es técnicamente muy interesante y esta pobremente documentado; pero reconozco que el uso de esta técnica es para situaciones especiales. Como igualmente supongo que la mayoría, al igual que yo, son personas muy ocupadas, entiendo que no le den mayor peso al tema, y en esas circunstancias no se justifica dedicarle tiempo para detallarlo paso a paso
Responder Con Cita
  #3  
Antiguo 11-05-2008
xjre xjre is offline
Miembro
 
Registrado: feb 2008
Posts: 13
Poder: 0
xjre Va por buen camino
Hola, qué tal?

Llevo solo un par de meses programando en delphi y solo un par mas programando en si. Estaba viendo los threads que había en API Windows para ver si en una de esas , alguno servía.
Este me llamó poderosamente la atención por una razón: Lo que se esta haciendo con este procedimiento y el explicado en el link que pusiste, ¿no es "hackear" otra aplicación circundante o estoy confundiendo términos? Me refiero a que según entendí lo que se esta haciendo es meter código nuevo a una aplicación que ya esta corriendo.

Saludos
Responder Con Cita
  #4  
Antiguo 22-11-2008
rolandoj rolandoj is offline
Miembro
 
Registrado: abr 2007
Posts: 395
Poder: 17
rolandoj Va por buen camino
Smile Escritura de la rutina a inyectar

Hola,

En vista de que nadie se interesó, había abandonado este hilo; pero, he recibido una petición personal para continuarlo, así quea quí hay más detalles:

Ya hemos visto la idea central y analizado un poco el punto de arranque, o sea la definición de los datos. pasemos al punto 2, la escritura de nuestra rutina:

También sabemos que la rutina a escribir tiene un solo parámetro que es un apuntador a una estructura tipo Record. Nuestro proceso luce entonces algo como:

Código Delphi [-]
Function ProcesoExterno(dwEntryPoint: PRemoteStruct): longword; stdcall;

Ahora bien, lo crítico aquí es entender que como esta rutina se ejecuta en el espacio de direcciones de otro proceso, cualquier dato no local (o sea, difererente a variables locales de la rutina, al parámetro, u otras rutinas a llamar) tiene que estar en el espacio de direcciones del otro proceso.

Así pués, que pasa si por ejemplo quisieramos llamar a la rutina GetCommandLine de Windows ?

Si intentáramos algo como esto:

Código Delphi [-]
function ProcesoExterno(dwEntryPoint: PRemoteStruct): longword; stdcall;
Var
   TheLine:     String; 
Begin
     ....
     ..
     TheLine := StrPas(GetCommandLine);
     ...
End;

El resultado sería un total desastre. Con lo dicho, es claro que al compilar la dirección de StrPas queda con respecto a nuestro espacio de direcciones, no el del otro proceso, y lo mismo aplica para GetCommandLine.

Y entonces como se hace ?.

La manera de proceder es pasar, como campos de la estructura RemoteStruct, las direcciones, dentro del espacio del otro proceso, de las rutinas que necesitemos llamar. Esto, de todas formas es muy restringido porque solo aplica para ciertas rutinas de Windows, como explicaremos más adelante. Por ahora, concentremonos en como quedaría la rutina;

Código Delphi [-]
function ProcesoExterno(dwEntryPoint: PRemoteStruct): longword; stdcall;
Var
   TheLine:     PChar; 
Begin
     ....
     ..
     TheLine := dwEntryPoint^.DGetCommandLine;
     ...
End;

El cambio de String a PChar es porque, según lo expuesto, no podemos llamar a StrPas, tenemos por tanto que limitarnos al uso de Windows, o sea al PChar.

Observese que se está llamando una Variable, correspondiente a un campo de dwEntryPoint, que debe ser del tipo Function. Por tanto, la definición de RemoteStruct debe tener en cuenta eso. Quedaría algo así:

Código Delphi [-]
Type
    TFncSendMessage = Function(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
    TFncGetCommandLine = Function: PChar; stdcall;
 
    PRemoteStruct = ^TRemoteStruct;
    TRemoteStruct = Record
       ....
       ..
       DSendMessage:           TFncSendMessage;  
       DGetCommandLine:        TFncGetCommandLine;
       ..
       . 
    End;

Para ser más claro, he incluído otra función de Windows, SendMessage, resaltando que se debe definir el prototipo completo de la función, incluyendo los parámetros de la misma

Según esta lógica, es claro que se debe llenar DGetCommandLine en nuestro proceso con la dirección que dicha rutina tenga en el otro proceso. Como obtemo esa dirección ? Lo veremos en la próxima entrega.
Responder Con Cita
  #5  
Antiguo 24-11-2008
alquimista alquimista is offline
Miembro
 
Registrado: ene 2008
Posts: 203
Poder: 17
alquimista Va por buen camino
Talking

creo que la información que estas posteando es muy interesante y valiosa.
Creo que si la gente no contesta es porque no es una cosa muy usual lo que explicas. Y no todo el mundo puede seguir estos temas (incluido yo mismo), aunque tengo interes en este tema.

Por lo que te animo a continuar e incluso pienso que este tema es para hacer un tutorial extenso.

Gracias por ser generoso y compartir tus conocimientos.

Un saludo....
Responder Con Cita
  #6  
Antiguo 30-11-2008
rolandoj rolandoj is offline
Miembro
 
Registrado: abr 2007
Posts: 395
Poder: 17
rolandoj Va por buen camino
Cita:
Empezado por alquimista Ver Mensaje
creo que la información que estas posteando es muy interesante y valiosa.
Creo que si la gente no contesta es porque no es una cosa muy usual lo que explicas. Y no todo el mundo puede seguir estos temas (incluido yo mismo), aunque tengo interes en este tema.

Por lo que te animo a continuar e incluso pienso que este tema es para hacer un tutorial extenso.

Gracias por ser generoso y compartir tus conocimientos.

Un saludo....
Hola,

Gracias por el comentario. Ciertamente, comparto tús apreciaciones.

Quiero disculparme con los que estén interesados en el tema. Sí tengo la intención de terminarlo; pero, un exceso de trabajo me ha impedido elaborar el siguiente punto. Espero hacerlo a mediados de la semana de Diciembre
Responder Con Cita
Respuesta


Herramientas Buscar en Tema
Buscar en Tema:

Búsqueda Avanzada
Desplegado

Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro

Temas Similares
Tema Autor Foro Respuestas Último mensaje
Thread y servidor DCOM externo al proceso ( EXE ) Aldo OOP 1 15-09-2006 18:39:47
Como utilizar un componente externo? Sergei OOP 2 24-01-2006 20:12:24
Inyectar proceso conde API de Windows 4 10-09-2005 16:52:17
Como se puede programar directamente??? Antuan Varios 10 04-08-2005 09:04:38
Como correr un archivo externo? fayala Firebird e Interbase 3 07-04-2005 04:56:00


La franja horaria es GMT +2. Ahora son las 20:53:15.


Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi
Copyright 1996-2007 Club Delphi