Introducción
Elastic Security Labs analiza diversos programas maliciosos que llegan a través de nuestros canales de búsqueda de amenazas y colas de telemetría. Recientemente nos topamos con una nueva familia de malware llamada DOUBLELOADER, vista junto con el ladrón de información RHADAMANTHYS. Un atributo interesante de DOUBLELOADER es que está protegido con un ofuscador de código abierto, ALCATRAZ, lanzado por primera vez en 2023. Si bien este proyecto tiene sus raíces en la comunidad de piratería de juegos, también se observó en el espacio del delito electrónico y se empleó en intrusiones específicas.
El objetivo de esta publicación es recorrer varias técnicas de ofuscación empleadas por ALCATRAZ, al tiempo que destaca los métodos para combatir estas técnicas como analistas de malware. Estas técnicas incluyen el aplanamiento del flujo de control, la mutación de instrucciones, el despliegue constante, el ocultamiento constante de LEA, trucos anti-desensamblaje y la ofuscación del punto de entrada.
Conclusiones clave
- El ofuscador de código abierto ALCATRAZ se vio dentro de un nuevo malware implementado junto con las infecciones de RHADAMANTHYS
- Las técnicas de ofuscación, como el aplanamiento del flujo de control, siguen siendo obstáculos para los analistas.
- Al comprender las técnicas de ofuscación y cómo contrarrestarlas, las organizaciones pueden mejorar su capacidad para clasificar y analizar eficazmente los binarios protegidos.
- Elastic Security Labs lanza herramientas para desofuscar los binarios protegidos de ALCATRAZ que se lanzan con esta publicación
CARGADOR DOBLE
A partir de diciembre pasado, nuestro equipo observó un malware de puerta trasera genérico acoplado con infecciones del ladrón RHADAMANTHYS . Según la ruta PDB, este malware se describe a sí mismo como DOUBLELOADER.
Este malware aprovecha llamadas al sistema como NtOpenProcess
, NtWriteVirtualMemory
, NtCreateThreadEx
para ejecutar código sin respaldo dentro del escritorio o administrador de archivos de Windows (explorer.exe
). El malware recopila información del host, aplicar una versión actualizada de sí mismo y comienza a enviar señales a una IP codificada (185.147.125.81
) almacenada dentro del binario.
Los ejemplos de DOUBLELOADER incluyen una sección no estándar (.0Dev
) con licencias ejecutables, esta es una marca de herramienta dejada en función del identificador del autor para la herramienta de ofuscación binaria, ALCATRAZ
.
Los ofuscadores como ALCATRAZ terminan aumentando la complejidad a la hora de clasificar el malware. Su objetivo principal es obstaculizar las herramientas de análisis binario y aumentar el tiempo del proceso de ingeniería inversa a través de diferentes técnicas; como ocultar el flujo de control o hacer que la descompilación sea difícil de seguir. A continuación se muestra un ejemplo de flujo de control ofuscado de una función dentro de DOUBLELOADER.
El resto del artículo se centrará en las diversas técnicas de ofuscación empleadas por ALCATRAZ. Emplearemos la primera etapa de DOUBLELOADER junto con ejemplos de código básicos para resaltar las características de ALCATRAZ.
ALCATRAZ
Descripción general de ALCATRAZ
Alcatraz es un ofuscador de código abierto lanzado inicialmente en enero de 2023. Si bien el proyecto es reconocido dentro de la comunidad de piratería de juegos como una herramienta fundamental para aprender técnicas de ofuscación, también se observó que grupos de delitos electrónicos y APT lo emplean de forma abusiva.
La base de código de Alcatraz contiene 5 características principales centradas en técnicas de ofuscación de código estándar junto con mejoras para ofuscar el punto de entrada. Su flujo de trabajo sigue un formato estándar bin2bin
, esto significa que el usuario proporciona un binario compilado y luego, luego de las transformaciones, recibirá un nuevo binario compilado. Este enfoque es especialmente atractivo para los hackers de juegos y desarrolladores de malware debido a su facilidad de uso, que requiere un esfuerzo mínimo y ninguna modificación a nivel del código fuente.
El desarrollador puede elegir ofuscar todas las funciones o funciones específicas, así como elegir qué técnicas de ofuscación aplicar a cada función. Luego de la compilación, el archivo se genera con la cadena (obf
) agregada al final del nombre del archivo.
Técnicas de ofuscación en ALCATRAZ
Las siguientes secciones repasarán las diversas técnicas de ofuscación implementadas por ALCATRAZ.
Ofuscación del punto de entrada
Lidiar con un punto de entrada oculto es como sufrir un pinchazo al comienzo de un viaje familiar por carretera. La idea se centra en confundir a los analistas y a las herramientas binarias donde no está directamente claro dónde comienza el programa, lo que genera confusión al comienzo del proceso de análisis.
La siguiente es la vista de un punto de entrada limpio (0x140001368
) desde un programa no ofuscado dentro de IDA Pro.
Al habilitar la ofuscación del punto de entrada, ALCATRAZ mueve el punto de entrada y luego incluye código adicional con un algoritmo para calcular el nuevo punto de entrada del programa. A continuación se muestra un fragmento de la vista descompilada del punto de entrada ofuscado.
Como ALCATRAZ es un ofuscador de código abierto, podemos encontrar el código de punto de entrada personalizado para ver cómo se realiza el cálculo o revertir nuestro propio ejemplo ofuscado. En nuestra descompilación, podemos ver que el algoritmo emplea algunos campos del encabezado PE, como Size of the Stack Commit
, Time Date Stamp
junto con los primeros cuatro bytes de la sección .0dev
. Estos campos se analizan y luego se emplean con operaciones bit a bit, como voltear a la derecha (ROR) y operación exclusiva (XOR), para calcular el punto de entrada.
A continuación se muestra un ejemplo de salida del script Python de IDA (Apéndice A) que analiza el PE y encuentra el punto de entrada verdadero, confirmando el punto de inicio original (0x140001368
) con la muestra no ofuscada.
Antidesmontaje
Los desarrolladores de malware y los ofuscadores emplean trucos anti-desensamblaje para confundir o romper los desensambladores con el fin de dificultar el análisis estático. Estas técnicas abusan de las debilidades durante los barridos lineales y el desensamblaje recursivo, impidiendo la reconstrucción limpia del código donde el analista se ve obligado a corregir manual o automáticamente las instrucciones subyacentes.
ALCATRAZ implementa una forma de esta técnica modificando cualquier instrucción que comience con el byte 0xFF
agregando una instrucción de salto corto ( 0xEB
) al frente. El byte 0xFF
puede representar el inicio de múltiples instrucciones válidas relacionadas con llamadas, saltos indirectos y empujones en la pila. Al agregar el salto corto 0xEB
al frente, esto efectivamente salta al siguiente byte 0xFF
. Si bien no es complejo, el daño se produce rompiendo el desmontaje y requiriendo algún tipo de intervención.
Para corregir esta técnica específica, se puede parchear el archivo reemplazando cada aparición del byte 0xEB
con NOP. Luego de aplicar el parche, el código se restaura a un estado más limpio, lo que permite desensamblar correctamente la siguiente instrucción call
.
Mutación de instrucciones
Una técnica común empleada por los ofuscadores es la mutación de instrucciones, donde las instrucciones se transforman de una manera que preserva su comportamiento original, pero hace que el código sea más difícil de entender. Marcos como Tigress o Perses son excelentes ejemplos de investigación sobre la ofuscación en torno a la mutación de instrucciones.
A continuación se muestra un ejemplo de esta técnica implementada por ALCATRAZ, donde se altera cualquier adición entre dos registros, pero su equivalencia semántica se mantiene intacta. La simple instrucción add
se transforma en 5 instrucciones diferentes (push
, not
, sub
, pop
, sub
).
Para corregir esto, podemos usar la coincidencia de patrones para encontrar estas instrucciones 5 juntas, desensamblar los bytes para encontrar qué registros están involucrados y luego usar un ensamblador como Keystone para generar los bytes correspondientes correctos.
Despliegue constante
Esta técnica de ofuscación prevalece en toda la muestra DOUBLELOADER y es un método ampliamente empleado en diversas formas de malware. El concepto aquí se centra en invertir el proceso de compilación; donde en lugar de optimizar los cálculos que se conocen en el momento de la compilación, el ofuscador “despliega” estas constantes haciendo que el desensamblaje y la descompilación sean complejos y confusos. A continuación se muestra un ejemplo simple de esta técnica donde la constante conocida (46
) se divide en dos operaciones matemáticas.
En DOUBLELOADER, encontramos que esta técnica se emplea cada vez que se mueven valores inmediatos a un registro. Estos valores inmediatos se reemplazan con múltiples operaciones bit a bit que enmascaran estos valores constantes, interrumpiendo así cualquier contexto y el flujo del analista. Por ejemplo, en el desmontaje a continuación, en el lado izquierdo, hay una instrucción de comparación del valor EAX en la dirección (0x18016CD93
). Al revisar las instrucciones anteriores, no resulta obvio ni claro cuál debería ser el valor de EAX debido a múltiples cálculos bit a bit poco claros. Si depuramos el programa, podemos ver que el valor EAX está establecido en 0
.
Para limpiar esta técnica de ofuscación, podemos confirmar su comportamiento con nuestro propio ejemplo donde podemos emplear el siguiente código fuente y ver cómo se aplica la transformación.
#include <iostream>
int add(int a, int b)
{
return a + b;
}
int main()
{
int c;
c = add(1, 2);
printf("Meow %d",c);
return 0;
}
Luego de compilar, podemos ver el desmontaje de la función main
en la versión limpia a la izquierda y ver estas dos constantes (2,1
) movidas al registro EDX y ECX. En el lado derecho, está la versión transformada, las dos constantes están ocultas entre las instrucciones recién agregadas.
Al emplear técnicas de coincidencia de patrones, podemos buscar estas secuencias de instrucciones, emular las instrucciones para realizar los distintos cálculos para recuperar los valores originales y luego parchear los bytes restantes con NOP para cerciorarnos de que el programa seguirá ejecutar.
Ofuscación de LEA
De manera similar a la técnica analizada anteriormente, la ofuscación LEA (dirección efectiva de carga) se centra en ocultar los valores inmediatos asociados con las instrucciones LEA. Un cálculo aritmético con resta seguirá directamente a la instrucción LEA para calcular el valor original previsto. Si bien esto puede parecer un cambio menor, puede tener un impacto significativo al romper las referencias cruzadas a cadenas y datos, que son esenciales para un análisis binario efectivo.
A continuación se muestra un ejemplo de esta técnica dentro de DOUBLELOADER donde el valor del registro RAX se disfraza a través de un patrón de carga de un valor inicial (0x1F4DFCF4F
) y luego se resta (0x74D983C7
) para darnos un nuevo valor calculado (0x180064B88
).
Si vamos a esa dirección dentro de nuestra muestra, seremos llevados a la sección de datos de solo lectura, donde podremos encontrar la cadena referenciada bad array new length
.
Para corregir esta técnica, podemos emplear la coincidencia de patrones para encontrar estas instrucciones específicas, realizar el cálculo y luego reconstruir una nueva instrucción LEA. En el modo de 64 bits, LEA emplea direccionamiento relativo a RIP, de modo que la dirección se calcula en función del puntero de instrucción (RIP) actual. Finalmente, obtenemos una nueva instrucción que se ve así: lea rax, [rip - 0xFF827]
.
A continuación se detallan los pasos para producir esta instrucción final:
Con esta información, podemos usar IDA Python para solucionar todos estos patrones; a continuación se muestra un ejemplo de una instrucción LEA fija.
Ofuscación del flujo de control
El aplanamiento del flujo de control es una poderosa técnica de ofuscación que altera la estructura tradicional del flujo de control de un programa al eliminar construcciones convencionales como ramas y bucles condicionales. En lugar de ello, reestructura la ejecución empleando un despachador centralizado, que determina el siguiente bloque básico a ejecutar en función de una variable de estado, lo que hace que el análisis y la descompilación sean significativamente más difíciles. A continuación se muestra un diagrama simple que representa las diferencias entre un flujo de control aplanado y no aplanado.
Nuestro equipo observó esta técnica en varios programas maliciosos como DOORME y no debería sorprender que en este caso el flujo de control aplanado sea una de las características principales del ofuscador ALCATRAZ. Para abordar el desaplanamiento, nos centramos en herramientas establecido mediante el uso del complemento IDA D810 escrito por el investigador de seguridad Boris Batteux.
Comenzaremos con nuestro programa de ejemplo anterior empleando la función común _security_init_cookie
empleada para detectar desbordamientos de búfer. A continuación se muestra el diagrama de flujo de control de la función de inicialización de cookies en forma no ofuscada. Basándonos en el gráfico, podemos ver que hay seis bloques básicos, dos ramas condicionales y podemos seguir fácilmente el flujo de ejecución.
Si tomamos la misma función y aplicamos la característica de aplanamiento del flujo de control de ALCATRAZ, el flujo de control del programa se ve muy diferente con 22 bloques básicos, 8 ramas condicionales y un nuevo despachador. En la siguiente figura, los bloques rellenos de color representan los bloques básicos anteriores de la versión no ofuscada, los bloques restantes en blanco representan el código ofuscador agregado que se emplea para despachar y controlar la ejecución.
Si echamos un vistazo a la descompilación, podemos ver que la función ahora está dividida en diferentes partes dentro de un bucle while
donde se emplea una nueva variable state
para guiar el programa junto con los restos de la ofuscación, incluidas las instrucciones popf/pushf
.
Para limpiar esta función, D810 aplica dos reglas diferentes (UnflattenerFakeJump
, FixPredecessorOfConditionalJumpBlock
) que aplican transformaciones de microcódigo para mejorar la descompilación.
2025-04-03 15:44:50,182 - D810 - INFO - Starting decompilation of function at 0x140025098
2025-04-03 15:44:50,334 - D810 - INFO - glbopt finished for function at 0x140025098
2025-04-03 15:44:50,334 - D810 - INFO - BlkRule 'UnflattenerFakeJump' has been used 1 times for a total of 3 patches
2025-04-03 15:44:50,334 - D810 - INFO - BlkRule 'FixPredecessorOfConditionalJumpBlock' has been used 1 times for a total of 2 patches
Cuando actualizamos el descompilador, se elimina el aplanamiento del flujo de control y se limpia el pseudocódigo.
Si bien este es un buen ejemplo, solucionar la ofuscación del flujo de control a menudo puede ser un proceso manual y oportuno que depende de la función. En la siguiente sección, reuniremos algunas de las técnicas que aprendimos y las aplicaremos a DOBLELOADER.
Limpieza de una función DOBLE CARGADOR
Uno de los desafíos a la hora de abordar la ofuscación en malware no son tanto las técnicas de ofuscación individuales, sino cuando las técnicas son estratificadas. Además, en el caso de DOUBLELOADER, grandes porciones de código se colocan en fragmentos de funciones con límites ambiguos, lo que dificulta su análisis. En esta sección, repasaremos un ejemplo práctico que muestra el proceso de limpieza de una función DOUBLELOADER protegida por ALCATRAZ.
Al iniciar la exportación Start
, una de las primeras llamadas va a loc_18016C6D9
. Esta parece ser una entrada a una función más grande, sin embargo, IDA no puede crear correctamente una función debido a instrucciones no definidas en 0x18016C8C1
.
Si nos desplazamos hasta esta dirección, podemos ver que la primera interrupción se debe a la técnica anti-desmontaje de salto corto que vimos anteriormente en la publicación del blog (EB FF
).
Luego de corregir 6 ocurrencias cercanas de esta misma técnica, podemos regresar a la dirección de inicio (0x18016C6D9
) y usar la función MakeFunction. Si bien la función se descompilará, seguirá estando muy ofuscada, lo que no es ideal para ningún análisis.
Volviendo al desmontaje, podemos ver la técnica de ofuscación LEA empleada en esta función a continuación, donde la constante de cadena ”Error”
ahora se recupera empleando la solución anterior.
Otro ejemplo a continuación muestra la transformación de un parámetro ofuscado para una llamada LoadIcon
donde el parámetro lpIconName
se limpia a 0x7f00
(IDI_APPLICATION
).
Ahora que la descompilación mejoró, podemos finalizar la limpieza eliminando la ofuscación del flujo de control con el complemento D810. A continuación se muestra una demostración que muestra los efectos antes y después.
En esta sección se cubrió un escenario real en el que se trabaja para limpiar una función ofuscada maliciosa protegida por ALCATRAZ. Si bien los reportes de análisis de malware suelen mostrar los resultados finales, a menudo se dedica una buena parte del tiempo previamente a trabajar para eliminar la ofuscación y reparar el binario para que luego pueda analizar adecuadamente.
Scripts de Python de IDA
Nuestro equipo está lanzando un serial de scripts de Python IDA de prueba de concepto que se emplean para manejar las técnicas de ofuscación predeterminadas impuestas por el ofuscador ALCATRAZ. Estos pretenden servir como ejemplos básicos a la hora de trabajar con estas técnicas y deben emplear con fines de investigación. Lamentablemente, no existe una solución milagrosa para lidiar con la ofuscación, pero tener algunos ejemplos y estrategias generales puede ser valioso para enfrentar desafíos similares en el futuro.
YARA
Elastic Security creó reglas YARA para identificar esta actividad.
Observaciones
En esta investigación se discutieron los siguientes observables.
Observable | Tipo | Nombre | Referencia |
---|---|---|---|
3050c464360ba7004d60f3ea7ebdf85d9a778d931fbf1041fa5867b930e1f7fd | SHA256 | DoubleLo.dll | CARGADOR DOBLE |
Referencias
A lo largo de la investigación anterior se hizo referencia a lo siguiente: