Análisis de malware

Invertir cargadores de malware: el caso Matsnu-A

7 de agosto de por Kyriakos Economou

La industria antivirus crece cada día junto con la industria clandestina que produce todo tipo de malware, desde simples infectados de archivos hasta tipos de troyanos más sofisticados que pueden recopilar y enviar información confidencial a los malos.

La lucha entre las empresas antivirus y los autores de malware es cada día más grande. Tanto los buenos como los malos dedican mucho tiempo a investigar e implementar formas de detectar y evitar la detección (dependiendo de qué lado estén estas personas).

¡Conviértete en un ingeniero inverso certificado!

Obtenga capacitación práctica en vivo sobre análisis de malware desde cualquier lugar y conviértase en un analista certificado de ingeniería inversa. Comienza a aprender

¡Conviértete en un ingeniero inverso certificado!

Obtenga capacitación práctica en vivo sobre análisis de malware desde cualquier lugar y conviértase en un analista certificado de ingeniería inversa. Comienza a aprender

La mayor parte de la investigación sobre malware suele concentrarse en los mecanismos de infección del malware. Otros puntos de interés incluyen técnicas utilizadas para que el malware se comunique con su creador y superar por completo las técnicas de evasión antivirus utilizadas por el malware en primer lugar.

Este artículo tiene como objetivo profundizar en el cargador utilizado por la familia de malware Matsnu para implementarse y evitar la detección por parte de productos antivirus. Afortunadamente, en este punto la variante ya es detectada por la mayoría de los proveedores de AV.

En mi trabajo como analista de malware, escucho muy a menudo que este tipo de técnica de evasión antivirus se describe como “empaquetador”. De una manera muy abstracta, esto podría ser cierto, pero desde un punto de vista técnico, en realidad no lo es.

Según mi experiencia con empaquetadores y desempaquetado manual, espero que un empaquetador incorpore algún algoritmo de compresión y muy probablemente un algoritmo de cifrado (personalizado o no). Además, el comportamiento de un empacador suele ser muy diferente. Un empaquetador normalmente descomprimirá y descifrará el código del ejecutable original y luego saltará a su punto de entrada original ( OEP ).

Por otro lado, prefiero llamar cargadores a estos “empaquetadores” utilizados por cada vez más autores de malware . Esto se debe a los detalles técnicos. Estos cargadores normalmente inician un proceso secundario en modo suspendido, sobrescriben su memoria con el código descifrado del malware y luego reanudan su hilo principal. Algunos de ellos podrían optar por asignar algo de memoria adicional al proceso hijo en lugar de sobrescribir su memoria e insertar allí el código viral descifrado. Además, este cargador podría inyectar un subproceso al proceso hijo con la dirección inicial al comienzo de la memoria asignada donde se coloca el código viral. Algunos otros podrían sobrescribirse a sí mismos a través de un código auxiliar escrito en una porción adicional de memoria asignada y luego regresar al espacio de direcciones de la imagen PE.

Además, muy a menudo los autores de malware optan por comprimir primero el código viral original utilizando un empaquetador común (como UPX, PECompact, etc.) y luego cifrarlo e incorporarlo dentro del cargador.

Desde un punto de vista técnico, es bastante justo distinguir estos dos tipos de mecanismos, e incluso si seguimos llamándolos a todos “empaquetadores” por simplicidad, es necesario comprender las diferencias entre ellos.

El objetivo final de este artículo es lograr aislar un ejecutable completamente funcional del malware original bajo las distintas capas de protección antivirus.

Etapa I de autodescifrado

Una gran parte del código del cargador se descifrará en tiempo de ejecución mediante un algoritmo de descifrado “lento” que realiza muchas operaciones en cada bucle, descifrando el código palabra por palabra.

El bucle exterior:

00401752 8B4D F0 MOV ECX, DWORD PTR SS:[EBP-10]

00401755 83C1 01 AÑADIR ECX, 1

00401758 894D F0 MOV DWORD PTR SS:[EBP-10], ECX

0040175B 817D F0 688E0 CMP DWORD PTR SS:[EBP-10], 28E68 ß comprobar contador

00401762 7D 5E JGE SHORT 004017C2 ß salir del bucle una vez terminado

…más código aquí

0040178F E8 D7040000 CALL 00401C6B ß llamada a la rutina de descifrado

…más código aquí

004017C0 EB 90 JMP CORTO 00401752 ß saltar al inicio del bucle

Dentro de la rutina de descifrado:

Aquí se llevan a cabo algunos bucles adicionales, pero la instrucción importante es la que realmente escribe cada vez que el resultado es una palabra dword almacenada en el registro ECX en la ubicación de memoria señalada por el registro EAX:

00401ED8 8908 MOV DWORD PTR DS:[EAX], ECX ß El valor inicial en EAX es 00408584 , se incrementa en una dword en cada iteración.

Etapa II de autodescifrado

Cuando el bucle exterior mencionado anteriormente ha terminado, se realiza otro unas pocas instrucciones más tarde.

004017DE 8B4D E0 MOV ECX, DWORD PTR SS:[EBP-/p>

004017E1 83C1 05 AÑADIR ECX, 5

004017E4 894D E0 MOV DWORD PTR SS:[EBP- ECX

004017E7 817D E0 DF0C0 CMP DWORD PTR SS:[EBP- 0CDF ß comprobar contador

004017EE 7D 77 JGE CORTO 00401867 ß salir del bucle

…más código aquí

00401862 E9 77FFFFFF JMP 004017DE ß saltar al inicio del bucle

Etapa III de autodescifrado

A continuación, habrá un bucle más durante la etapa de autodescifrado.

0040187E BA 01000000 MOV EDX, 1

00401883 85D2 PRUEBA EDX, EDX

00401885 0F84 D000 JE 0040195D

Las tres instrucciones anteriores crean una redirección de flujo de ejecución falsa. De hecho, dado que el valor 1 siempre se pasa al registro EDX, después de realizar la instrucción TEST en el mismo registro, el salto JE condicional que sigue nunca tendrá ningún efecto en el flujo de ejecución.

0040188B 817D F0 688E0 CMP DWORD PTR SS:[EBP-10], 28E68 ß comprobar contador

00401892 0F85 A1000000 JNZ 00401939 ß si no es igual, salte a incrementar_counter

A continuación se presenta algo más de código:

contador_incremento:

00401939 8B4D F0 MOV ECX, DWORD PTR SS:[EBP-10]

0040193C 83C1 01 AÑADIR ECX, 1

0040193F 894D F0 MOV DWORD PTR SS:[EBP-10], ECX

enter_next_decryprion_routine:

00401942 68 F7480700 EMPUJAR 748F7

00401947 68 18194F00 EMPUJAR 4F1918

0040194C 8B55 F4 MOV EDX, DWORD PTR SS:[EBP-C]

0040194F 52 EMPUJAR

00401950 E8 4A000000 CALL 0040199F ß rutina de descifrado de llamadas

00401955 83C4 0C AÑADIR ESP, 0C

00401958 E9 21FFFFFF JMP 0040187E ß saltar al inicio del bucle

Dentro de la rutina de descifrado:

Aquí se están realizando algunos bucles más, pero la instrucción importante es la que realmente escribe cada vez que el resultado es un dword almacenado en ECX y registrado en la ubicación de memoria señalada por el registro EAX:

00401B70 8908 MOV DWORD PTR DS:[EAX], ECX ß El valor inicial en EAX es 00408584 . Se incrementa en una palabra d en cada iteración.

Etapa IV de autodescifrado

Volviendo al bucle fuera de la función de descifrado, vimos la condición que normalmente señalaría el final del proceso de bucle. Es falso y debemos examinarlo más detenidamente para localizar el siguiente paso.

De hecho, cuando las condiciones sean correctas, la ejecución llegará a una instrucción CALL:

0040191D E8 8FF8FFFF LLAMADA 004011B1

La LLAMADA al inicio del código previamente cifrado se encuentra dentro de esta función:

004013B6 FF15 108B4000 CALL NEAR DWORD PTR DS:[408B10] ß el valor almacenado en esta dirección es 00408584

Una vez ingresamos a la función en la dirección 00408584 vemos lo siguiente:

00408584 E8 07000000 LLAMADA 00408590

00408589 75 3A JNZ CORTO 004085C5

Tenga en cuenta el truco de ofuscación en la primera instrucción que confunde al motor de desmontaje. De hecho, la instrucción CALL traerá la ejecución al final de la instrucción comenzando en la dirección 0040858B , lo que significa que todos los bytes intermedios son bytes basura en este caso.

0040858B 03A0 21D64F5B AÑADIR ESP, DWORD PTR DS:[EAX+5B4FD621]

00408591 81EB 05103A00 SUB EBX, 3A1005

00408597 8DB3 2E103A00 LEA ESI, DWORD PTR DS:[EBX+3A102E]

0040859D B9 8B00 MOV ECX, 28B

004085A2 66BF 7592 MOV DI, 9275

004085A6 66313E PTR DE PALABRA XOR DS:[ESI], DI

004085A9 6683C7 02 AÑADIR DI, 2

004085AD 83C6 02 AÑADIR ESI, 2

004085B0 E2 F4 BUCLE CORTO 004085A6

004085B2 FC CLD

004085B3 7E 2A JLE CORTO 004085DF

004085B5 1B95 CFF6215C SBB EDX, DWORD PTR SS:[EBP+5C21F6CF]

004085BB
8745 92 XCHG DWORD PTR SS:[EBP-6E], EAX

004085BE D7 XLAT BYTE PTR DS:[EBX+AL]

004085BF
1F POP DS

004085C0 30D5 XOR CH, DL

004085C2 94 XCHG EAX, ESP

Esto es lo que vemos una vez ejecutamos la instrucción CALL:

00408590 5B POP EBX

00408591 81EB 05103A00 SUB EBX, 3A1005

00408597 8DB3 2E103A00 LEA ESI, DWORD PTR DS:[EBX+3A102E] ß comienza desde la dirección 004085B2

0040859D B9 8B00 MOV ECX, 28B ß contador de bucle

004085A2 66BF 7592 MOV DI, clave de descifrado 9275 ß

004085A6 66313E XOR WORD PTR DS:[ESI], DI ß descifra mediante operación XOR con 9275 , una palabra en cada iteración.

004085A9 6683C7 02 AÑADIR DI, 2

004085AD 83C6 02 AÑADIR ESI, 2

004085B0 E2 F4 BUCLE CORTO 004085A6

El algoritmo de descifrado anterior descifrará una porción adicional de código a partir de la instrucción ubicada inmediatamente después del LOOPD.

Entonces, en este punto vimos los diversos pasos utilizados por este cargador para descifrar las siguientes partes del código. Ahora toca continuar con el resto de sus mecanismos.

Resolución de importaciones dinámicas y estructura de datos del cargador PEB

Normalmente, los autores de malware recuperan los VA de las API utilizando dos API de Windows, que son las API LoadLibrary y GetProcAddress . Estos se emplean para evitar la detección a través de las importaciones que normalmente figuran dentro de la tabla de importaciones. Sin embargo, en este caso, el autor del cargador ha decidido pasar por la estructura de datos del cargador PEB (Bloque de entorno de proceso): estructura PEB_LDR_DATA para recuperar la información necesaria, que es una forma más sigilosa de recuperar los VA de las API necesarias. .

El puntero a esta estructura se encuentra en PEB + 0x0C.

De regreso a donde nos detuvimos, inmediatamente después de finalizar el ciclo de descifrado ubicamos una CALL en la dirección 004085CD y al ingresar a esta función vemos otra CALL en la dirección 004086EF , y dentro de esa función es donde el cargador del malware accederá a la estructura PEB_LDR_DATA .

0040870E 64FF35 3000000 EMPUJAR DWORD PTR FS:[30]

00408715 58 POP EAX

En las dos instrucciones anteriores, notamos otro intento de ofuscación. De hecho, en lugar de insertar la dirección de PEB en la pila y luego devolver ese valor a EAX, podríamos simplemente hacer MOV EAX, DWORD PTR FS:[30].

00408716 8B40 0C MOV EAX, DWORD PTR DS:[EAX+C] ß mover a EAX el puntero a PEB_LDR_DATA

00408719 8B48 0C MOV ECX, DWORD PTR DS:[EAX+C] ß mover a ECX el puntero a la primera estructura LDR_MODULE del primer módulo cargado por el cargador de Windows

0040871C 8B11 MOV EDX, DWORD PTR DS:[ECX] ß guardar en EDX el puntero a la estructura LDR_MODULE del siguiente módulo cargado por el cargador de Windows

0040871E 8B41 30 MOV EAX, DWORD PTR DS:[ECX+30] ß mueva a EAX el puntero al nombre del primer módulo cargado por el cargador de Windows.

Luego sigue otra LLAMADA a la dirección 00408728 , a una función dedicada a calcular una palabra mágica a partir del nombre del módulo actualmente examinado. Si dword coincide con la constante predefinida, entonces el cargador sabe que encontró el módulo cargado necesario para continuar con sus mecanismos.

Algoritmo de cálculo:

00408797 8A10 MOV DL, BYTE PTR DS:[EAX] ß revisa todos los caracteres uno por uno

00408799 80CA 60 OR DL, 60 ß iniciar cálculo de dword

0040879C 01D3 AÑADIR EBX, EDX

0040879E D1E3 SHL EBX, cálculo de palabra d final 1 ß

004087A0 0345 10 AGREGAR EAX, DWORD PTR SS:[EBP+10] ß aumentar el puntero al nombre de la cadena en 2, porque está almacenado como Unicode

004087A3 8A08 MOV CL, BYTE PTR DS:[EAX] ß mueve el siguiente valor de carácter a CL

004087A5 84C9 TEST CL, CL ß comprobar si es cero, lo que significa que llegamos al final de la cadena

004087A7 E0 EE LOOPDNE SHORT 00408797 ß si no salta al bucle para el siguiente carácter

004087A9 31C0 XOR EAX, EAX ß puesta a cero EAX

004087AB 8B4D 0C MOV ECX, DWORD PTR SS:[EBP+C] ß pasar a ECX magic dword

004087AE 39CB CMP EBX, ECX ß comprobar si dword calculado = magic dword

004087B0 74 01 JE CORTO 004087B3 ß si lo es, módulo ubicado

004087B2 40 INC EAX

004087B3 5A POP EDX

004087B4 5B POP EBX

004087B5 59 POP ECX

004087B6 89EC MOV ESP, EBP

004087B8 5D POP EBP

004087B9 C2 0C00 RET 0C

La siguiente demuestra la condición en la que los dos valores coinciden al comprobar el módulo cargado kernel32.dll.

1: módulo Kernel32.dll ubicado

Una vez ubicado el módulo necesario, llegaremos a la siguiente parte del código que intentará encontrar los VA de funciones exportadas específicas desde kernel32.dll después de salir de la función anterior.

00408735 8B41 18 MOV EAX, DWORD PTR DS:[ECX+18] ß obtener la imagen base de kernel32.dll de la estructura LDR_MODULE

00408738 50 EMPUJE EAX

00408739 8B58 3C MOV EBX, DWORD PTR DS:[EAX+3C] ß obtiene el desplazamiento de su encabezado PE

0040873C 01D8 AÑADIR EAX, EBX

0040873E 8B58 78 MOV EBX, DWORD PTR DS:[EAX+78] ß obtener el RVA de su tabla de exportación

Una vez que el cargador del malware localice la tabla de exportación de kernerl32.dll, la utilizará para recuperar los VA de algunas API, cuatro en total, necesarias para continuar.

Aquí está la tabla que se crea en esta etapa:

00408AA5 760CBC8B kernel32.LoadLibraryExA

00408AA9 760D05F4 kernel32.VirtualAlloc

00408AAD 760C50AB kernel32.VirtualProtect

00408AB1 760D1837 kernel32.GetProcAddress

En la próxima entrega, comenzaré mostrando cómo localizar y aislar el ejecutable descifrado integrado.

¡Divertirse!

Agregue un comentario

Su dirección de correo no se hará público. Los campos requeridos están marcados *