Como investigador de malware , a menudo se enfrenta a muestras complejas y muy ofuscadas. Y GuLoader, el malware con el que nos enfrentamos hoy, es un ejemplo clásico.
Eche un vistazo a este pseudocódigo, producido al descompilar su código ensamblador: es feo e ilegible.
Ante un rompecabezas como este, es posible que te encuentres perdido. ¿Por dónde deberías empezar? ¿Cómo abordas el análisis de esta muestra? Vamos a desglosarlo.
En este artículo, entonces, exploraremos estrategias para desofuscar dicho código, usando GuLoader como referencia. Aprenderás sobre:
El artículo se basa en el análisis del malware GuLoader publicado anteriormente por ANY.RUN. Visite nuestro blog para encontrar la muestra que analizaremos, así como las instrucciones de desempaquetado y un script Ghidra que automatiza parcialmente gran parte de lo que vamos a cubrir a partir de este momento.
Este artículo se centra en el análisis estático. Pero si desea analizar una muestra de GuLoader dinámicamente, puede usar el entorno limitado de malware en la nube ANY.RUN .
Solicite una prueba gratuita de 14 días del plan Enterprise utilizando su correo electrónico comercial. Aproveche la vida útil prolongada de las instancias de VM, las tareas ilimitadas y las versiones de Windows 7 a 11.
El análisis dinámico le permite ver cómo funciona el malware en el mundo real al vincular su comportamiento con su configuración técnica. Es una forma de verificar sus acciones en diferentes configuraciones del sistema y recopilar IOC.
Sin demorar más, vamos a sumergirnos.
Después de desempaquetar la muestra, comenzamos con un análisis manual del shellcode y rápidamente nos damos cuenta de que está ofuscado. Estudiar el código nos permite agrupar las técnicas de ofuscación que emplea GuLoader en categorías:
Este es un buen momento para detenerse, analizar los métodos de ofuscación que se utilizan y desarrollar una estrategia de desobstrucción.
Por ejemplo, en nuestro caso, la mayoría de estas técnicas introducen código que no altera el resultado final de la ejecución. Por lo tanto, a menudo podemos "NOP" de forma segura para mejorar la legibilidad. Pero proceda con precaución: no todo el código ofuscado es irrelevante para el funcionamiento del programa, como pronto descubriremos.
Ahora, examinemos estas técnicas de ofuscación individualmente y veamos cómo derrotarlas.
El código está plagado de muchas instrucciones XMM. Estos aparecen desordenados y agregan complejidad al proceso de análisis. Los encontramos desde el primer byte del shellcode desempaquetado.
Tenga en cuenta que muchos motores de emulación tropiezan con ellos debido a la falta de soporte predeterminado. Pusimos a prueba los motores integrados de Angr, Triton y Ghidra; todos fallaron.
En el caso de GuLoader, las instrucciones XMM en realidad no afectan el comportamiento previsto del código. Encontrará métodos de ofuscación similares en una gran cantidad de malware. En consecuencia, podemos reemplazar con seguridad todas las instrucciones XMM con "NOP", como se muestra en la siguiente tabla:
Así es como se ve el resultado en el desensamblador Ghidra:
Las instrucciones JMP incondicionales segmentan el código en fragmentos más pequeños. Este método se usa con frecuencia para evitar la detección por parte del software antivirus y otras herramientas de seguridad. Además, puede hacer que el trabajo de los analistas consuma más tiempo y sea más frustrante, ya que deben saltar entre estos bloques, especialmente cuando se trata de una gran cantidad de código. GuLoader y otros programas maliciosos suelen emplear esta técnica.
Este método de ofuscación es bastante fácil de derrotar. El desensamblador en el código descompilado a menudo concatena con éxito estos bloques, mejorando la legibilidad del código, incluso con estos saltos incondicionales presentes. Como tal, podemos dejar los bloques pequeños como están sin necesidad de fusionarlos.
Las instrucciones de ensamblaje basura a menudo entran en juego como una capa adicional de ofuscación. Estas instrucciones no realizan ninguna función tangible, por lo general, dejan inalterados los valores de registro, el flujo de ejecución o la memoria.
También encontrará estos dentro de GuLoader.
Esté atento a las instrucciones que no realizan ninguna acción ("NOP", "FNOP") y aquellas que se desplazan o rotan por cero bits ("SHL reg, 0"; "ROL reg, 0"). También están presentes otras instrucciones sin impacto como "OR reg, 0", "XOR reg, 0", "CLD", "WAIT".
Abordar las instrucciones de comparación falsas es más desafiante que simplemente reemplazar las basura con "NOP". No podemos eliminar todas las instrucciones de comparación, ya que algunas son necesarias para que el código funcione correctamente. Una forma de abordar esto es "marcar" todas las instrucciones de comparación que encontramos. Si no se encuentran instrucciones que utilicen el resultado de la comparación, es seguro NOP. Si encontramos un salto condicional o similar, desmarcamos la comparación para evitar su eliminación.
La siguiente tabla muestra un ejemplo donde todas las instrucciones de comparación excepto "CMP EDX,0x0" fueron reemplazadas selectivamente por NOP:
GuLoader también emplea la táctica de ofuscación de usar instrucciones "PUSHAD" falsas, junto con una instrucción "POPAD" coincidente. Pueden modificar temporalmente valores de registro pero son anulados por el "POPAD" restaurando los valores de registro originales.
Nuestra investigación mostró que todas las instrucciones "PUSHAD" en GuLoader son extrañas. Entonces, abordamos esto reemplazando "PUSHAD", "POPAD" y las instrucciones intermedias con NOP:
Sin embargo, no todas las instrucciones "POPAD" en GuLoader son basura. Dejamos intactos aquellos sin un "PUSHAD" correspondiente.
Otra técnica de ofuscación similar a la anterior es el uso de instrucciones "PUSH" falsas. Estas instrucciones insertan un valor en la pila solo para sacarlo inmediatamente.
Un ejemplo es la inclusión de una instrucción "PUSH SS", posiblemente seguida de instrucciones que modifican un registro o ubicación de memoria. Sin embargo, el "POP SS" subsiguiente restaura el puntero de la pila a su valor inicial.
Derrotar las instrucciones PUSH falsas se parece al proceso de PUSHAD falso, pero es crucial dejar inalterados los registros no presionados.
Los predicados opacos son declaraciones condicionales que siempre devuelven verdadero o falso, pero son difíciles de analizar o predecir. Estos se encuentran en el código de GuLoader y complican la comprensión lógica.
Por ejemplo, un par de instrucciones como "MOV BL, 0xB6" y "CMP BL, 0xB6" podrían ir seguidas de un salto condicional como "JNZ ADDR". La comparación siempre devuelve falso ya que el valor comparado es igual al valor movido, lo que hace que el salto sea innecesario y desconcertante.
Superar los predicados opacos puede parecer un desafío debido al requisito de "predecir" la condición de salto. Sin embargo, nuestra situación es más sencilla ya que todos los predicados opacos se encuentran dentro de los bloques "PUSHAD" y "POPAD". Por lo tanto, simplemente reemplazamos todos los predicados entre estas instrucciones con NOP.
Las expresiones aritméticas ofuscadas se encuentran entre las técnicas más interesantes utilizadas por GuLoader. Hacen que sea más difícil comprender las operaciones reales realizadas. Estas expresiones incorporan acciones aritméticas como la suma o la resta. A veces, se mezclan con otras ofuscaciones como comparaciones falsas, predicados opacos e instrucciones basura.
Un ejemplo es mover un valor constante a un registro y ejecutar operaciones aritméticas:
Otro caso es insertar un valor constante en la pila y realizar cálculos en la memoria:
Para desofuscar las expresiones aritméticas de GuLoader, adoptamos un enfoque similar al manejo de comparaciones falsas. Marcamos todas las instrucciones "MOV" donde el segundo argumento es un valor escalar y todas las instrucciones "PUSH" donde el argumento es escalar. Al encontrar una operación aritmética, actualizamos el valor constante en la primera instrucción y reemplazamos la actual con NOP. De esta manera, la primera instrucción tiene el resultado y las instrucciones aritméticas posteriores se reemplazan por NOP.
A continuación se muestran ejemplos con operaciones "MOV" y "PUSH" optimizadas:
Tenga cuidado con los tamaños de los operandos. Preservar el tamaño correcto durante las operaciones es crucial.
En este artículo de los expertos de ANY.RUN , usamos GuLoader como un ejemplo del mundo real para resaltar las estrategias típicas de desofuscación. Comenzamos analizando el código, luego agrupamos tácticas de ofuscación similares y, finalmente, pensamos en formas únicas de abordar cada una.
Pero es crucial comprender que estas técnicas no son una solución única para todos. Cada muestra de malware puede presentar sus estrategias de ofuscación únicas que necesitan contramedidas únicas.
Nuestra discusión subraya la importancia de dividir una tarea compleja como la desofuscación en pasos claros y manejables. Recuerde que la desofuscación exitosa de cualquier malware implica un análisis detallado, la detección de patrones de ofuscación y la creación de estrategias optimizadas.