Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Principal > API de Windows
Registrarse FAQ Miembros Calendario Guía de estilo Temas de Hoy

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 30-11-2008
rolandoj rolandoj is offline
Miembro
 
Registrado: abr 2007
Posts: 395
Poder: 18
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
  #2  
Antiguo 01-12-2008
Avatar de escafandra
[escafandra] escafandra is offline
Miembro Premium
 
Registrado: nov 2007
Posts: 2.197
Poder: 20
escafandra Tiene un aura espectacularescafandra Tiene un aura espectacular
Bueno, cuando alguien se propone publicar tutoriales o explicaciones sobre temas complejos o poco comunes, siempre espera un poco de apoyo. No puedo leer tu hilo sin demostrarte explícitamente mi interés.

Aunque yo programo en C fundamentalmente y apenas domino el delphi, te sigo perfectamente. Veo que te centras, de momento, en la inyección con la API CreateRemoteThread. ¿Has pensado en otras técnicas?. Digo esto porque numerosos antivirus y cortafuegos tienen un Hook a esa API y bloquean la inyección. Y no es que el objetivo sea burlar un antivirus, que de hecho muchos de ellos son burlados inyectando así, sino por curiosidad técnica... Que conste que no pretendo desviar tu exposición.

El tema de inyectar sin dll es más complejo que si lo hacemos con dll recibe mi apoyo por atreverte decidirte en abordar este tema.

Saludos.

Última edición por escafandra fecha: 01-12-2008 a las 00:24:03.
Responder Con Cita
  #3  
Antiguo 01-12-2008
Avatar de Al González
[Al González] Al González is offline
In .pas since 1991
 
Registrado: may 2003
Posts: 5.604
Poder: 30
Al González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en bruto
Thumbs up

Hola amigos.

Rolando, acabo de enterarme de la existencia de este interesante hilo. Y pensar que vienes planteando esto desde mayo.

He leído por completo el planteamiento hecho aquí y en el otro hilo que refieres al principio. Francamente desconocía que existiese algo como la función CreateRemoteThread, aunque ahora que lo pienso, quizá ella es una de las claves de cómo funcionan los depuradores.

Ya leí la ayuda de ésta en el archivo de oro Win32.hlp que viene con todo Delphi. Y también encontré esto en Google Code Search. Es un ejemplo muy bien explicado, del cual me llamó la atención esta parte:

Cita:
HMODULE pfnLoadLibrary =
GetProcAddress(LoadLibrary("kernel32.dll"), "LoadLibraryA" );

hThread = ::CreateRemoteThread(hProcessForHooking,
NULL,
0,
pfnLoadLibrary,
"C:\\HookTool.dll",
0,
NULL);

By using GetProcAddress() API we get the address of the LoadLibrary() API.
The dodgy thing here is that Kernel32.DLL is mapped always to the same
address space of each process, thus the address of LoadLibrary()
function has the same value in address space of any running process.
This ensures that we pass a valid pointer (i.e. pfnLoadLibrary)
as parameter of CreateRemoteThread()
.
Sé que buscas inyectar código máquina que no forma parte de una DLL. Pero puse esa referencia porque me llama mucho la atención lo que marqué con negritas.

Por un lado pienso, "ah, pues entonces sólo con DLLs nativas se puede", pero la pregunta que inmediatamente me salta es: ¿Cómo le hace Windows cuando llamamos a una función de su API que recibe una rutina de retrollamada (call back) como parámetro, puesto que solemos usar un simple "@Rutina"?

Y mientras terminaba de escribir esta tonta pregunta ya me respondía a mí mismo: Ah, pues ahí no hay problema porque cuando llamamos a esa función de la API, su DLL fue previamente cargada en el espacio de nuestro propio proceso, así que la dirección dada por el arroba es perfectamente válida.

Pero entonces me queda la inquietud: ¿realmente puede instalarse una rutina de un proceso en ejecución dentro del espacio y contexto de otro proceso? ¿O en la práctica esto de la inyección de código máquina sólo es posible definiendo la rutina en una DLL y haciendo que un LoadLibrary inyectado con CreateRemoteThread cargue esa DLL en el proceso ajeno?

Que interesante tema, en verdad. Estaré pendiente de los avances, esperemos llegar a un feliz desenlace.

Y bueno, me despido por unas horas, porque si no mi chica me enviará a un lugar muy muy remoto.

Un abrazo inyectado.

Al González.

P.D. Una duda más: ¿cómo es posible que el proceso remoto pueda usar el parámetro "C:\\HookTool.dll", si al compilarse el programa esa constante quedaría en el espacio de memoria del proceso inyector?
Responder Con Cita
  #4  
Antiguo 01-12-2008
Avatar de escafandra
[escafandra] escafandra is offline
Miembro Premium
 
Registrado: nov 2007
Posts: 2.197
Poder: 20
escafandra Tiene un aura espectacularescafandra Tiene un aura espectacular
Pues creo que la intención de rolandoj es injectar código sin dll. Si que se puede...
Para poder colocar "algo" en el espacio de direcciones ajeno, tenemos varias API que lo permiten:

OpenProcess // Abre un proceso;
VirtualAllocEx // Reserva memoria en un proceso
WriteProcessMemory // Escribe en la memoria de un proceso reservada por VirtualAllocEx
VirtualFree // Libera la memoria reservada por VirtualAllocEx

Las DLL nativas se cargan siempre en el mismo espacio relativo de cada proceso, no así las demás dlls.

Saludos.
Responder Con Cita
  #5  
Antiguo 01-12-2008
Avatar de Al González
[Al González] Al González is offline
In .pas since 1991
 
Registrado: may 2003
Posts: 5.604
Poder: 30
Al González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en bruto
Smile

Hola de nuevo.

Indagando encontré respuestas a las dudas que me surgieron en el mensaje anterior.

1. El código que cité arriba más bien era seudocódigo, el real conlleva complejidades adicionales pero por lo visto superables.

2. Efectivamente, el parámetro lpParameter dado a la función CreateRemoteThread, debe ser una dirección de memoria válida para el proceso remoto. El truco para esto es obtener ésta con la función VirtualAllocEx y escribir en ella el argumento con la función WriteProcessMemory. ¡Por lo visto podemos escribir datos directamente en la memoria de otros procesos!

3. El ejemplo de Rezmond que enlacé arriba es sólo el segundo de al menos dos maneras que él plantea para lograr la inyección. El método 1 no necesita que sea cargada ninguna DLL (aunque por otras razones sí emplea DLLs en el ejemplo que expone).

4. Sí es posible inyectar una rutina de nuestro programa en el espacio de memoria de otro proceso. Como puede apreciarse en el ejemplo del método 1, el truco es, también, utilizar las funciones VirtualAllocEx y WriteProcessMemory para copiar el código máquina de nuestra rutina en la memoria del proceso remoto, y luego usar CreateRemoteThread para crear un hilo dentro de tal proceso, empezando su ejecución en la dirección base del bloque de memoria donde fue copiado el código.

5. En ambos casos, Rezmond ejecuta inicialmente otra función de su autoría, llamada GetDebugPrivs, la cual llama a otro par de funciones de la API de Windows para darle al programa algo así como permisos de depuración. Desconozco en qué punto de sus ejemplos son necesarios tales permisos, pero parece ser algo importante para que aquellos funcionen.

Ahora sólo me quedaría la pregunta, ¿cómo saber cuántos bytes ocupa el código máquina de una función? En uno de los ejemplos vemos que se utilizó un valor de 1000 bytes (cómo diciendo más vale que sobre a que falte), pero ¿podría, por ejemplo, servirnos la ventana CPU del depurador (Ctrl+Alt+C) para calcular el tamaño de una rutina?

Espero con esto haber contribuido en algo para que Rolando encuentre solución al problema que plantea. No quiere decir que me despido del tema, solo que esperaré sus comentarios para ver por dónde podemos seguir ayudando.

Saludos.

Al González.

P.D. Ahora veo que Escafandra hizo un adelanto sobre esto que comenté. Interesante el uso de esas funciones.

Última edición por Al González fecha: 01-12-2008 a las 09:18:28.
Responder Con Cita
  #6  
Antiguo 01-12-2008
Avatar de escafandra
[escafandra] escafandra is offline
Miembro Premium
 
Registrado: nov 2007
Posts: 2.197
Poder: 20
escafandra Tiene un aura espectacularescafandra Tiene un aura espectacular
Cita:
Empezado por Al González Ver Mensaje
Ahora sólo me quedaría la pregunta, ¿cómo saber cuántos bytes ocupa el código máquina de una función?
Pues en C++ es fácil, Si Func1 es la función cuyo tamaño queremos calcular y Func2 es la siguiente en el orden del IDE, sólo tenemos que restar (BYTE*)Func2-(BYTE*) Func1 y el resultado es el tamaño de Func 1 en nº de Bytes una vez compilada.

El problema aparece cuando Func1 llama a su vez a otras funciones que no sean APIs, pues ese tamaño habrá que calcularlo para reservar memoria y "subirlas" a su vez, con lo que en realidad lo que interesa es "subir" no una función sino todo el código que se valla a ejecutar en el nuevo espacio de direcciones. En el caso de hacerlo con dll es el S.O. el que se encarga de todo, pero en nuestro caso debe hacerse manualmente.

Saludos.
Responder Con Cita
  #7  
Antiguo 01-12-2008
rolandoj rolandoj is offline
Miembro
 
Registrado: abr 2007
Posts: 395
Poder: 18
rolandoj Va por buen camino
Smile Gracias por el interés

Hola a todos,

Gracias por el interés. Veo que ahora si se está moviendo el hilo. Dos comentarios rápidos:

1. Una gran disculpa. Como dije antes, infortunadamente, debido a exceso de trabajo, no puedo dedicarme ahora a completar el proceso. Solo a mediados de semana, tendré tiempo libre.

2. Mi problema original si lo resolví. El código que estoy explicando es el que usé y funciona. Las razones para publicarlo son dos: Lo pobre de la documentación disponible, y el hecho de existir un problema en la limpieza final del mismo, el cual, aunque para mi caso no me afecta, si me intriga
Responder Con Cita
  #8  
Antiguo 06-12-2008
rolandoj rolandoj is offline
Miembro
 
Registrado: abr 2007
Posts: 395
Poder: 18
rolandoj Va por buen camino
Smile Preparando los datos a enviar

Hola,

Bueno, al fin tengo algo de tiempo para proseguir con este tema.

Empiezo contestando la pregunta de escafandra:

Si, he usado la técnica de Hook; aunque ya hace bastante tiempo. Lo que ocurre es que cada técnica tiene su ámbito de aplicación. En el caso de los Hook, ellos sirven para monitorear los mensajes de Windows; por ello, pueden enlentecer el sistema, lo que hace que solo se recomiende instalarlos cuando sea necesario. Lo importante es que dependen de que se ejecute el mensaje que se esté monitoreando.

En mi caso, la necesidad era totalmente diferente. Se trataba de que una aplicación debía solicitar a otras determinada información; es decir era un servicio bajo pedido. La lógica seguida era por tanto más adecuada a la inyección de código, donde se llama a la rutina en la aplicación remota obteniendo enseguida la respuesta.

Pasando al tema en si, Veo que ya se ha agregado parte de la información que les traigo; pero creo que de todas maneras les será util.

Habíamos quedado en como obtener las direcciones de las funciones Windows que necesitamos pasar dentro de nuestra estructura TRemoteStruct. Para hacerlo, se procede así:

Definamos una variable global que sea nuestra estructura. Por ejemplo:

Código Delphi [-]
Var
   DataInterProc:       TRemoteStruct;

Esa variable la inicializamos (típicamente en el OnCreate de un formulario), con un código similar a:

Código Delphi [-]
procedure TfrmWPPrincipal.FormCreate(Sender: TObject);
Var
   MUser:               HMODULE;
   MKernel:             HMODULE;
   ::::
   :::
   :: 
begin
     ::::
     :::
     THE_REMOTE_MSG := RegisterWindowMessage(MY_REMOTE_MSG);
     MUser          := GetModuleHandle('user32');
     MKernel        := GetModuleHandle('kernel32');
     DataInterProc.DRegisterWindowMessage := GetProcAddress(MUser,'RegisterWindowMessageA');
     DataInterProc.DGlobalAddAtom         := GetProcAddress(MKernel,'GlobalAddAtomA');
     DataInterProc.DSendMessage           := GetProcAddress(MUser,'SendMessageA');
     DataInterProc.DGetCommandLine        := GetProcAddress(MKernel,'GetCommandLineA');
     StrCopy(DataInterProc.IdRemoteMsg,MY_REMOTE_MSG);
     :::
     ::
end;

Analicemos lo correspondiente a GetModuleHandle y GetProcAddress. La parte clave del asunto es la ya comentada por Al, de su busqueda en Google, que él coloca en negritas.

Debido a que las librerías user32 y kernel32 se cargan en la mismo lugar del espacio de direcciones de cualquier proceso que se ejecute, las direcciones relativas de sus rutinas son las mismas en cualquier proceso. Por lo anterior, es posible obtenerlas en nuestro proceso, para luego pasarlas e invocarlas desde el otro proceso, tal y como se ha explicado en la segunda nota que hice de este tema.

GetModuleHandle, como su nombre lo indica, devuelve el manejador del módulo que le pasamos como parámetro; así pués, lo primero que hacemos es obtener los manejadores de User32 y Kernel32. Luego es solo cuestión de obtener la dirección de cada rutina que nos interese; para lo cual empleamos GetProcAddress. El primer parámetro es obviamente el manejador del módulo; pero el segundo requiere aclaración.

Observarán, por ejemplo, que aparece SendMessageA y no SendMessage. Por qué la A al final ?. Sin detallar mayormente el tema, lo que ocurre es las librerías de Windows a menudo tienen más de una implementación para una función. El nombre que debemos usar es el de la versión adecuada a nosotros. Donde hallamos esa información ?. En nuestro clásico archivo Windows.Pas. Para el caso de la función SendMessage, la línea que aparece es:

Código Delphi [-]
function SendMessage; external user32 name 'SendMessageA';

Lo que nos indica que el nombre requerido para SendMessage es SendMessageA.

Para finalizar esta parte, solo queda mencionar a RegisterWindowMessage.

Esta parte es un mecanismo interesante para que nuestro código inyectado nos devuelva valores, con un mecanismo diferente al de la estructura TRemoteStruct.

La idea es que la información nos sea devuelta por medio de un mensaje de Windows definido por nosotros mismos. Este mecanismo es una alternativa más comoda de manejar cuando hay que devolver varios string de longitud variable e impronosticable, ya que en nuestro esquema, hacer eso por medio del parámetro que pasamos sería complicado.

RegisterWindowMessage devuelve un identificador único a todo Windows para un string que represente un mensaje. La lógica seguída es que si el string que se registra ya existe en Windows, la función devuelve el identificador existente para el mismo. Así pués, lo que hacemos es obtener el identificador en nuestro programa y luego, en la rutina a inyectar, se vuelve a llamar RegisterWindowMessage para obtenerlo de nuevo; pero guardandolo dentro de una variable válida en ese proceso remoto (típicamente, local a nuestra rutina "ProcesoExterno"). Una vez hecho eso ya podemos comunicar ambas aplicaciones vía mensajes propios.

Por lo anterior, si requerimos esa técnica, debemos incluir RegisterWindowMessage dentro de las funciones que deben hacerse disponibles a nuestro "ProcesoExterno".

Quedarían dos puntos por anotar en esta parte. Uno es la definición de MY_REMOTE_MSG y THE_REMOTE_MSG.

El primero es una constante string que debemos asegurar que sea única. Se definiría más o menos así :

Código Delphi [-]
Const
     MY_REMOTE_MSG = 'Mi servicio es AB12456V';

Es decir, un texto que nos indique lo que hace y algunos números y letras que garanticen su unicidad.

El segundo es simplemente una variable del tipo UINT.

El último punto es la llamada :

Código Delphi [-]
StrCopy(DataInterProc.IdRemoteMsg,MY_REMOTE_MSG);

Aquí es la misma lógica ya explicada. MY_REMOTE_MSG está en nuestro espacio de direcciones; como necesitamos usarlo en nuestar rutina remota, debemos incluirlo dentro de los datos a enviarle a ella; por eso, se usa una variable, del tipo arreglo de caracteres, incluida dentro de nuestro registro TRemoteStruct; lógicamente con suficiente espacio para contener a MY_REMOTE_MSG. El Strcopy simplemente se encarga de poner a MY_REMOTE_MSG dentro de nuestro registro.

Bueno, esto ya resultó muy largo; pero espero que les sea util.

En la próxima entrega veremos el proceso de copiado de nuestra rutina.
Responder Con Cita
Respuesta



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 17:39:47
Como utilizar un componente externo? Sergei OOP 2 24-01-2006 19:12:24
Inyectar proceso conde API de Windows 4 10-09-2005 15:52:17
Como se puede programar directamente??? Antuan Varios 10 04-08-2005 08:04:38
Como correr un archivo externo? fayala Firebird e Interbase 3 07-04-2005 03:56:00


La franja horaria es GMT +2. Ahora son las 15:57:06.


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