En los momentos en que afrontamos el análisis de un código malicioso, siempre existen una serie de pasos que debemos realizar. Uno de los principales, o más comunes, a la hora de arrancar con una nueva tarea de reversing es visualizar cuáles son las funciones que importa el malware que vamos a analizar. Sin embargo, en numerosas ocasiones, la información que vemos al principio puede no estar completa, por ello, en el post de hoy, vamos a ver un ejemplo de cómo un código malicioso carga diferentes funciones directamente en tiempo de ejecución.
La muestra que vamos a analizar corresponde a la familia de códigos maliciosos detectados por los productos de ESET como Win32/LockScreen, una familia de ransomware que ha causado más de un problema a los usuarios. Este tipo de amenazas, ocultan, cifran o bloquean el acceso al sistema o la información contenida en él solicitando al usuario algún tipo de rescate.
Como mencionamos anteriormente, uno de los primeros pasos, antes de ejecutar el código en un entorno controlado, es ver qué funciones intenta importar. Dicha información se puede encontrar en la Import Table del archivo ejecutable, y es posible acceder a ella a través de un gran número de herramientas disponibles en Internet. En este caso, podemos ver una imagen correspondiente a algunos de los imports del ejecutable:
En el listado de los módulos (librerías) y funciones que se cargan al iniciar el sistema, si bien no es completo, podríamos darnos una idea de lo que este código malicioso intenta hacer. Sin embargo, si nos quedamos solo con esta imagen, nos estaríamos perdiendo una parte importante de lo que va a pasar. Los cibercriminales, como un método para engañar a los analistas de malware, o para hacer más complejo el análisis de sus amenazas, suelen optar por la utilización de rutinas de código que cargan de manera dinámica y en tiempo de ejecución algunas librerías del sistema o funciones particulares.
En estos casos, lo que podemos hacer es utilizar algún debugger para ejecutar el código malicioso y buscar en qué secciones de su código se realiza esta tarea. Tengamos en cuenta que este tipo de acciones, pueden resultar un poco tediosas, pero sin embargo, nos van a permitir entender bien en detalle, qué esta ocultando la amenaza y al mismo tiempo aprender las técnicas que los mismos desarrolladores de malware implementan, para ocultar sus actividades.
Para este análisis, yo me decidí por utilizar Immunity Debugger, para seguir los pasos del código malicioso, utilizando además, algunas de las funciones para saltear las protecciones de anti-debugging y/o anti-vm. Esto es más una elección personal que otra cosa, cada uno puede optar por la herramienta que quiera. Entonces, una vez abierto y parcheadas las protecciones estamos en condiciones de realizar nuestro análisis:
Hasta aquí, nada del otro mundo, pero a medida que nos fuimos adentrando en el análisis nos encontramos con una sección de código que valía la pena mirar un poco más en detalle y que además nos ayudaría a entender algunas acciones que el código malicioso intentará en el sistema. En la siguiente captura podemos ver cómo se llama reiteradas veces a una función alojada en la dirección de memoria 409A7E, a la que le pasa dos parámetros:
A partir de este momento, se realizarán un total de once llamadas a la misma función con diferentes valores. En estos momentos nos podríamos preguntar para qué sirven tales hashes, y como se podrán imaginar, identificarán a las funciones que debe buscar. El bloque de código que se ejecuta en cada llamada a esa función es el siguiente:
A la función se le pasa la dirección base de una librería y va a iterar por cada una de las funciones calculando el hash y comparando con el que está buscando. Mientras no lo encuentre va a continuar comparando cada uno de los valores, para en luego retornar la dirección correcta. El hash se calcula en el "CALL girl-fuc.00409A68" y las instrucciones que ejecuta son:
Analizando el valor del registro ESI, es posible conocer cuantas veces tuvo que iterar por las diferentes funciones hasta encontrar la que está buscando:
Finalmente se observa cómo el valor de retorno de la función alojado en EAX, apunta a la función que se buscó. En este caso podemos ver que el hash correspondiente al valor "1FC0EAEE" corresponde a la funcíon GetProcAddress de la librería Kernel32.
Una vez que se llame a la función las once veces el código malicioso habrá cargado todas las funciones que estará llamando. El listado final es:
- Kernel32.GetProcAddress
- Kernel32.VirtualProtect
- Kernel32.LoadLibraryExA
- Kernel32.GetModuleHandleA
- Kernel32.VirtualAlloc
- Kernel32.GetModuleFileNameA
- Kernel32.CreateFileA
- Kernel32.SetFilePointer
- Kernel32.ReadFile
- Kernel32.VirtualFree
- Kernel32.CloseHandle
Una vez que terminó con la carga de las funciones y conoce sus direcciones de memoria el código malicioso las puede llamar cuando las necesite. De esta manera el cibercriminal logra ocultar parte del comportamiento de la amenaza y hacer que su análisis sea más complejo. Este no es la única manera de hacerlo y existen otras, las iremos compartiendo con ustedes en el blog.
¡Atentos!
Pablo Ramos
Security Researcher