Cómo crear un "proxy" de DLLs en .NET
En este artículo se verá cómo crear un proxy de DLLs entre una aplicación y una DLL original desarrolladas en .NET. Dicho de otro modo, se introducirá un código intermedio entre una aplicación y sus librerías pudiendo interceptar y modificar tanto las llamadas que realiza la aplicación a las DDLs así como las respuestas que estas retornan a la aplicación.
Figura 1: Arquitectura de la aplicación "proxy dll"
Para llevar a cabo esta prueba de concepto, crearemos una DLL muy sencilla con dos métodos que nos devolverán el valor del atributo ‘nombre’, en este caso ‘DLL ORIGINAL’.
Figura 2: Código fuente de la DLL original
A continuación se muestra el código de la aplicación que hará uso de dicha DLL, el cual únicamente hará dos llamadas a los métodos de la DLL y los mostrará por pantalla.
Figura 3: Código fuente de la aplicación que llama a la DLL
De este modo, el resultado de la ejecución normal de la aplicación sería la siguiente:
Figura 4: Resultado tras una ejecución normal
Ahora que tenemos el objetivo a atacar compilado (Aplicación + DLL), procederemos a realizar un análisis a modo de caja negra de la DLL, ya que para introducir nuestra DLL (Proxy) entre la aplicación y la DLL original, tendremos que implementar todos los métodos y atributos que tenga la original.
Para ello utilizaremos la aplicación Reflector, el cual muestra la estructura de un ensamblado .NET. En la siguiente captura se ve marcado de color verde los métodos públicos existentes - los privados tienen un candado como icono -, los cuales son los únicos que nos interesan, puesto que son los únicos que serían accesibles por la aplicación, y sobre los cuales habría que llevar a cabo la intercepción de las llamadas.
Figura 5: Apertura de la DLL con Reflector
Una vez tenemos el objetivo fijado, deberemos modificar su ‘NameSpace’ (dlloriginal), ya que si no habría conflictos de acceso con la DLL que estableceremos como proxy, debido a que tendría el mismo ‘NameSpace’ que la original. Esta modificación podemos realizarla a través de un editor hexadecimal, remplazando la cadena ‘dllOriginal’ por ‘dllOrigina2’, y guardando el fichero como ‘dllOrigina2.dll’.
Figura 6: Modificación del namespace
De este modo, el estado actual de los ficheros serían, la aplicación junto a la DLL original, a la cual le hemos modificado el ‘NameSpace’.
Ahora es el momento de desarrollar el código de la DLL que funcionará como proxy, la cual deberá implementar los métodos que se obtuvieron mediante ‘Reflector’ y deberá tener una referencia a ‘dllOrigina2’, como se ve en la siguiente captura:
Figura 7: Métodos a implementar
Los métodos que esta debe implementar deben ser todos los que disponga la DLL original, y el código de cada uno de ellos ya depende de la funcionalidad que se le quiera dar, desde ideas beneficiosas como la mejora de determinadas funciones, como la curiosidad de conocer los datos internos de cada llamada, o hasta la inclusión de una puerta trasera que se active al recibir determinado valor a través de los atributos de una llamada a una función.
Figura 8: Código fuente de la nueva DLL
Ya solo es necesario copiar la DLL que actúa como proxy en el mismo directorio que la aplicación, y con el nombre ‘dllOriginal.dll’.
Figura 9: Ejecución con intercepción de llamadas a DLLs
Autor: Manu "The_Sur"
Figura 1: Arquitectura de la aplicación "proxy dll"
Para llevar a cabo esta prueba de concepto, crearemos una DLL muy sencilla con dos métodos que nos devolverán el valor del atributo ‘nombre’, en este caso ‘DLL ORIGINAL’.
Figura 2: Código fuente de la DLL original
A continuación se muestra el código de la aplicación que hará uso de dicha DLL, el cual únicamente hará dos llamadas a los métodos de la DLL y los mostrará por pantalla.
Figura 3: Código fuente de la aplicación que llama a la DLL
De este modo, el resultado de la ejecución normal de la aplicación sería la siguiente:
Figura 4: Resultado tras una ejecución normal
Ahora que tenemos el objetivo a atacar compilado (Aplicación + DLL), procederemos a realizar un análisis a modo de caja negra de la DLL, ya que para introducir nuestra DLL (Proxy) entre la aplicación y la DLL original, tendremos que implementar todos los métodos y atributos que tenga la original.
Para ello utilizaremos la aplicación Reflector, el cual muestra la estructura de un ensamblado .NET. En la siguiente captura se ve marcado de color verde los métodos públicos existentes - los privados tienen un candado como icono -, los cuales son los únicos que nos interesan, puesto que son los únicos que serían accesibles por la aplicación, y sobre los cuales habría que llevar a cabo la intercepción de las llamadas.
Figura 5: Apertura de la DLL con Reflector
Una vez tenemos el objetivo fijado, deberemos modificar su ‘NameSpace’ (dlloriginal), ya que si no habría conflictos de acceso con la DLL que estableceremos como proxy, debido a que tendría el mismo ‘NameSpace’ que la original. Esta modificación podemos realizarla a través de un editor hexadecimal, remplazando la cadena ‘dllOriginal’ por ‘dllOrigina2’, y guardando el fichero como ‘dllOrigina2.dll’.
Figura 6: Modificación del namespace
De este modo, el estado actual de los ficheros serían, la aplicación junto a la DLL original, a la cual le hemos modificado el ‘NameSpace’.
Ahora es el momento de desarrollar el código de la DLL que funcionará como proxy, la cual deberá implementar los métodos que se obtuvieron mediante ‘Reflector’ y deberá tener una referencia a ‘dllOrigina2’, como se ve en la siguiente captura:
Figura 7: Métodos a implementar
Los métodos que esta debe implementar deben ser todos los que disponga la DLL original, y el código de cada uno de ellos ya depende de la funcionalidad que se le quiera dar, desde ideas beneficiosas como la mejora de determinadas funciones, como la curiosidad de conocer los datos internos de cada llamada, o hasta la inclusión de una puerta trasera que se active al recibir determinado valor a través de los atributos de una llamada a una función.
Figura 8: Código fuente de la nueva DLL
Ya solo es necesario copiar la DLL que actúa como proxy en el mismo directorio que la aplicación, y con el nombre ‘dllOriginal.dll’.
Figura 9: Ejecución con intercepción de llamadas a DLLs
Autor: Manu "The_Sur"
5 comentarios:
Mola, muy útil para separar capas a la hora de aplicar políticas (por ejemplo).
¡Vaya! Buena entrada, me recuerda a la exportación de apis a otro fichero, para salteo de las heurísticas antivirus.
Saludos!
Yo lo veo mucho mas util para la ocultacion de malware o rootkits
@Gg Bueno, si firmas la DLL (y hoy por hoy es lo más común) tendrás que hacer algo más que un proxy para meter malware...
Lo del proxy es una buena idea pero creo que hasta resulta más fácil "desensamblar" el código de la DLL original y obtener el código MSIL resultante (que también se puede obtener con reflector). Modificarlo a nuestro gusto y volver a compilarlo obteniendo una nueva DLL que es exactamente como la original pero con nuestro código añadido.
Y lo de firmar los ensamblados realmente es una "tonteria". La firma de ensamblados solo garantiza que el ensamblado es de quien dice ser pero no garantiza que no se pueda modificar. De hecho, antes de volver a compilar el MSIL se puede quitar la firma y compilarlo sin problemas.
Publicar un comentario