Desmitificación de las técnicas de ransomware mediante ensamblajes .Net: Ensamblados EXE vs. DLL
En la primera parte de este serial, examinamos algunas técnicas empleadas por el malware, específicamente el ransomware . Como vimos, estas técnicas individuales, como descargadores, droppers y cargadores, así como la codificación y el cifrado, son capacidades legítimas y programables ofrecidas por el marco de software .Net (dot net) y muchos otros marcos de programación y lenguajes de código. A continuación se muestra un collage de algunas de las técnicas discutidas en el artículo anterior.

En este segundo artículo, procederemos a examinar los fundamentos de los ensamblados a través del marco de Microsoft .Net. Profundizaremos en las diferencias entre los ensamblados (EXE vs. DLL) y sus relaciones, lo que permite cómo estas capacidades se ejecutan finalmente desde un código inicial de alto nivel como el código de programación de C#. Usaremos el código presentado en el artículo anterior para explorar estas diferencias y relaciones.
¿Qué es Microsoft .Net?
Microsoft .Net es un marco de desarrollo de software diseñado para admitir varios lenguajes de programación y dirigir a diferentes sistemas operativos. Los lenguajes de programación admitidos como C# (pronunciado C sharp) se compilan y ejecutan como lo que se conoce como código gestionado (a diferencia del código nativo o no gestionado). Para lograr esto, .Net ejecuta su código en una máquina virtual dedicada en lugar de directamente en la plataforma de destino. Esta máquina virtual se conoce como Common Language Runtime (CLR) de .Net . Se puede considerar como el intermediario común que finalmente ejecuta el código compilado o ensamblado de todos los diferentes lenguajes de programación, como C#, VB.Net y F#, que admite .Net . En este ejemplo siguiente se muestra el código del lenguaje de programación C# del artículo anterior.

El código gestionado significa que el código del lenguaje de programación C# de alto nivel anterior y otros como F# y VB.Net se compilan primero en un lenguaje intermedio (IL). El código de alto nivel de C# que se muestra arriba se compila según las instrucciones del lenguaje intermedio que se muestran en la imagen siguiente. Este código se asemeja a la sintaxis de programación de ensambladores de bajo nivel.

Este lenguaje intermedio (IL) se compila en código nativo o de máquina dirigido a la plataforma de máquina correspondiente. Esta compilación la realiza otro componente de .Net denominado compilador Just-in-Time (JIT).
El código nativo o de máquina es el conjunto de instrucciones (ceros y unos) que entiende el procesador (CPU) de una computadora en individua. Este último paso lo gestiona Common Language Runtime (CLR), que también contiene el JIT. CLR es el entorno de tiempo de ejecución de .Net o la máquina virtual. Java es otro marco de software que emplea el concepto de tiempos de ejecución intermedios. Al igual que la máquina virtual Java, es una parte principal de lo que hace que la plataforma .Net sea independiente. El código .Net se denomina código gestionado porque el código de programación lo gestiona el CLR intermediario y no lo ejecuta directamente la CPU del equipo.
Un beneficio del código gestionado en .Net es la administración automática de memoria y la recolección de elementos no empleados. Esto significa que el desarrollador no necesita preocupar por asignar y desasignar la memoria de la computadora en su código para ahorrar recursos del sistema como en el caso de, por ejemplo, el código C o C ++. En .Net, existe el recolector de elementos no empleados que se ejecuta periódicamente para controlar la memoria desaasignada. También puede ser llamado por el programador cuando sea necesario. En el diagrama siguiente se muestra la arquitectura de una aplicación .Net .

Por el contrario, los compiladores que no son.Net como VB6, C y C++ compilan su código de alto nivel directamente en el código de máquina de la plataforma de destino (sistema operativo y CPU). Por lo tanto, el ejecutable o ensamblado de código resultante está vinculado a la plataforma de máquina de destino del compilador. Esto también se conoce como código nativo o no gestionado. Aunque arquitectónicamente diferente, es posible usar código de ensamblados, especialmente archivos DLL desarrollados en código nativo en un archivo . Aplicacióngestionada en red mediante una capacidad conocida como Interop Marshalling (Platform Invoke). Ejemplos de esto serán el uso de archivos DLL nativos del sistema operativo Windows o bibliotecas externas, como el código escrito en C++ al que se hace referencia en una aplicación .Net gestionada para habilitar algunas funciones del sistema operativo de bajo nivel. En este caso, .Net en sí mismo se puede considerar como un envoltorio seguro alrededor de las DLL nativas en las que se basa el sistema operativo Windows y gran parte de las cuales están escritas en C ++.
¿Qué es un ensamblado .Net?
Microsoft describe los ensamblados .Net como una sola unidad de implementación. Lo que esto significa es que un ensamblado es una colección de varios tipos de código y archivos asociados que se compilaron (ensamblado) en alguna forma que se puede ejecutar en cualquier plataforma de destino . Net compatible. La ejecución se realiza mediante . Tiempo de ejecución de lenguaje común de Net. Algunos ejemplos de ensamblados en el sistema operativo Windows son los archivos ejecutables (.exe) y los archivos de biblioteca de clases o biblioteca de vínculos dinámicos (.dll).
Al profundizar en la imagen de código de ejemplo siguiente, se muestra el ensamblado ejecutable de C# a la izquierda y otro código de ensamblado DLL de C# (también conocido como biblioteca de clases) a la derecha. El código ejecutable hace referencia al archivo DLL y, a continuación, llama a un método específico (función) desde el código DLL durante la ejecución. Estas referencias y llamadas se destacaron en la imagen a continuación. Explicaremos los detalles de ambos fragmentos de código más adelante en este artículo. También mostraremos cómo se puede emplear esta combinación para fines maliciosos en este serial.

En el ejemplo siguiente, se hace referencia manualmente al archivo DLL en el código ejecutable. Esto significa que se hace referencia al archivo DLL y a la información relacionada sobre sus metadatos, así como al código (formado por módulos, clases y métodos) durante el tiempo de compilación del código ejecutable.

Como biblioteca compartida, el código DLL no se puede ejecutar por sí solo directamente. Desde el punto de vista del código, esto se debe a que los archivos DLL no tienen una función de punto de entrada principal desde la que ejecutar y, por lo tanto, no se pueden ejecutar como código independiente de la forma en que se configura un código ejecutable (.exe). Como ejemplo, el mensaje de error siguiente muestra las consecuencias de intentar ejecutar una biblioteca de clases o un archivo DLL directamente desde un compilador.

El código ejecutable, por otro lado, tendrá una función o método de punto de entrada principal donde comienza la ejecución, pero un archivo DLL realmente no necesita una función de punto de entrada principal, ya que es principalmente una biblioteca de bloques de código a los que hacen referencia otros ensamblados.
Una vez referenciado, se puede llamar para su ejecución al código específico del archivo DLL que es de interés. Como se muestra en el artículo anterior, los ejemplos de código (EXE y DLL) siguientes reiteran este punto.

La aplicación ejecutable se ejecuta y llama al código desde el archivo DLL al que hace referencia para generar la salida que se muestra en la imagen siguiente.

Este sencillo programa muestra cómo se pueden usar juntos los ensamblados .Net , como EXE y DLL.
El código DLL al que se hace referencia anteriormente tiene un método (función) que toma dos parámetros por entrada (un nombre y una edad) y, a continuación, muestra un mensaje de saludo con esta información. El código ejecutable, por otro lado, ejecuta código que acepta los detalles de entrada del usuario de nombre y antigüedad de la línea de comandos y, a continuación, pasa esa información al método DLL como argumentos o entradas. A continuación, el mensaje del código DLL se muestra de nuevo en la pantalla de la consola con la información que la aplicación EXE recopiló del usuario.
Análisis de ensamblados .Net
Al realizar un análisis estático en el archivo ejecutable, se muestran las distintas referencias de archivos DLL y otros componentes importados para su ejecución. Además de nuestro propio archivo DLL personalizado, el ensamblado ejecutable también importa archivos DLL adicionales asociados con .Net , como mscorlib , que es un archivo DLL que contiene código base (clases, tipos, etc.) y es algo que nuestro programa necesita para funcionar sin problemas.

En nuestro entorno de desarrollo de código Visual Studio, podemos confirmar el uso de mscorlib rastreando sus orígenes en uno de los tipos de datos (en este caso, cadena de System.String en .Net). Esto revela el ensamblado . Net integrado donde se origina ese tipo, que es mscorlib , como se muestra a continuación.

String es un tipo de datos en términos de programación donde se almacena el texto que ingresa el usuario y luego se muestra. También podemos ver en nuestro análisis estático la DLL llamada "DLL_dontNet_Assembly". Este es nuestro archivo DLL personalizado que contiene el método "DisplayMsgMethod" que muestra al usuario un mensaje luego de escribir sus datos.
En nuestro ejemplo, hicimos referencia y cargamos nuestro archivo DLL personalizado manualmente durante la compilación de todo nuestro código antes de que el programa comenzara a ejecutar. También es posible hacer referencia a un archivo DLL durante la ejecución de un ejecutable. Esto puede ser especialmente útil en los casos en los que es posible que no tengamos acceso a la DLL deseada durante la compilación de nuestro código. Este proceso se conoce como reflexión, y permite la capacidad de examinar un ensamblado .Net (metadatos y atributos) y también de usar código (módulos, clases, métodos y propiedades) contenido en él durante el tiempo de ejecución de nuestro programa. Esta técnica también se puede modificar para intenciones maliciosas en lo que se conoce como ataques de inyección de DLL reflexiva.
Los ensamblados de .Net (ejecutables y bibliotecas de clases) también constan de un archivo de manifiesto que contiene metadatos sobre el ensamblado y el código del lenguaje intermedio (IL) que, en conjunto, permiten que Common Language Runtime ejecute el ensamblado en cualquier plataforma compatible que pueda ejecutar .Net. En la imagen siguiente se muestran las instrucciones de ensamblado de IL y la estructura del manifiesto de los dos ensamblados: EXE y DLL. El archivo de manifiesto contiene los metadatos sobre el ensamblado .Net , como el número de versión, la descripción, etc.

Ahora deberíamos tener una comprensión fundamental del marco de software .Net , sus ensamblajes asociados y cómo pueden interactuar entre sí.
En el próximo artículo, pondremos las técnicas y capacidades que discutimos y aprendido hasta ahora en un solo ejecutable de ransomware malicioso.
Obtenga más información sobre cómo Illumio Zero Trust Segmentation puede ayudarlo a contener las infracciones de ransomware.