Análisis de malware

Avatar Rootkit: análisis más profundo del cuentagotas

marzo 22, por Souhail Hammou

En este segundo artículo sobre el cuentagotas, retomaremos nuestro análisis justo donde lo dejamos: el descifrado de la clave y los datos. Después del descifrado, se inicializan dos estructuras. El pseudocódigo equivalente se presenta a continuación. Además, observe que aquí se utiliza el fragmento de memoria » Mem » previamente asignado .

estructura typedef

{

DWORD Desconocido01;

LPVOID DllBase;

UINT tamaño Dll;

LPVOID NtdllBase;

DWORD Desconocido02;

Memoria LPVOID;

Tamaño de memoria UINT;

UINT Desconocido03;

} Avtr_strct;

/*[…]*/

Avtr_strct Avtr_Estructura;

Avtr_Structure.Unk01 = 0;

Avtr_Structure.DllBase = Ubicación_DLL_descomprimida;

Avtr_Structure.Dllsize = 0xA400;

Avtr_Structure.NtdllBase = dirección_ntdll;

Avtr_Structure.Unk02 = 1;

Avtr_Structure.Mem = Mem;

Avtr_Structure.Memsize = 0x1160;

Avtr_Structure.Unk03 = 0;

*(Mem+0x8) = Avtr_Decompressed_PEs; //Dónde residen los 3 archivos PE descifrados

*(Mem+0xC) = Avtr_Decompressed_PEs_size;

*(Mem+0x10) = datos_descifrados;

*(Mem+0x14) = tamaño_datos_descifrados; // == 0x319

*(Mem+0x1C) = decrypted_anti_vm; //Asignado pero aún vacío

*(Mem+0x4) = 0x80000000;

GetModuleFileNameW(hModule,(LPWSTR)(Mem+0x1260);

Una vez que el dropper termina con la inicialización de la estructura, comienza a generar cadenas aleatorias que son nombres para los mutex del malware, un objeto del kernel, una cadena aleatoria de 6 bytes y también dos valores aleatorios de 4 bytes. Esta aleatorización evita que el malware se detecte como lo haría normalmente cuando se utilizan nombres de eventos y mutex globales codificados.

Los marcadores de posición de nombre ya están presentes en la sección .rdata . La 1 muestra su formato antes de la aleatorización.

1

La rutina responsable de la aleatorización es Avtr_GenerateRandomNames. Esta rutina almacena punteros a los nombres generados y los dos valores aleatorios de palabras dobles en una estructura global que llamé rndm_v . Avtr_SetMutexNames se invoca posteriormente ( 2 ) para reemplazar primero las cadenas presentes en las secciones .rdata de los ejecutables descomprimidos y luego reemplazar todas las apariciones de 0xDA1EDA 0xDA1EDA1E por las dos palabras dobles aleatorias respectivamente en esos mismos PE.

2

La 3 muestra una aparición de 0xDA1EDAue será reemplazada por la primera palabra doble aleatoria en el controlador del rootkit.

3

A continuación, se invoca Avtr_GetObjectName para acceder al nombre de la matriz rndm_v con el parámetro 0x14. La rutina calculará a partir de este valor dónde está el objeto deseado y le devolverá un puntero. Luego, el nombre se utiliza para crear un evento ( 4 ).

4

La 4 también muestra una llamada a Avtr_RestorKiUserExceptionDispatcher que restaura el desplazamiento de llamada original de RtlDispatchException y una llamada a Avtr_DecryptAndSwitchToDLL. Lo que hace esta rutina es cargar la DLL que fue descomprimida por separado y luego invocar su DllEntryPoint ( 5 ).

5

Esta DLL es el dropper de segundo nivel y es el responsable de cargar el Rootkit. DllMain terminará llamando a Avtr_main_func de la DLL , que comienza verificando si el usuario actual es administrador (
6 ); Este paso es importante porque determina cómo se cargará el rootkit después.

6

La técnica utilizada en la función IsAdministrator es idéntica al código del ejemplo de MSDN CheckTokenMembership [1].

Después de eso, se invoca una función que determina qué versión de Windows está ejecutando el usuario. Luego, la ruta del módulo que se almacenó anteriormente en Mem+0x1e duplica en Mem+0x328. El siguiente bloque de código copia el primer y el segundo nombre de exclusión mutua, ambos precedidos por BaseNamedObjects, en Mem+0x Mem+0xA0 respectivamente (
7 ). Estos campos serán utilizados por el rootkit del kernel más adelante para la sincronización.

7

El código ejecutado posteriormente parcheará la llamada a RtlDispatchException en la rutina KiUserExceptionDispatcher como se hizo en el cuentagotas del primer nivel . Luego, Avtr_anti_VM comprobará si el malware se está ejecutando en una máquina virtual o no ( 8 ).

8

El código que realiza comprobaciones anti-VM está cifrado. Antes de descifrarlo, se copia la región de memoria previamente asignada en *( Mem+0x1C) (consulte el pseudocódigo al principio del artículo). Luego, se descifra utilizando un algoritmo XOR cíclico simple con la cadena «explorador» ( 9 ).

9

Antes de emitir una llamada al código independiente de la posición descifrada, Mem+0x18 se establece en 1. En la parte anterior, descubrimos que Mem+0x18 siempre se establece en 1 antes de realizar una verificación anti-depuración y no se reduce a cero hasta que todas las comprobaciones fallen al encontrar un depurador. Aquí se utiliza el mismo valor antes de probar una máquina virtual.

Para ver de cerca el código, se utilizó un script similar a los de la parte anterior para volcarlo. Se realizan un total de 5 comprobaciones anti-VM. Si una de ellas falla, el valor de Mem+0x18 no disminuye. En realidad, no hay nada nuevo en las técnicas utilizadas aquí y son bien conocidas. Sin embargo, eso no significa que no debamos fijarnos en algunos de ellos:

10

Esta primera técnica ( 10) compara el selector de segmento que apunta al TSS (Segmento de estado de tarea) de la tarea actual con 0x4000. Esta es una técnica anti-VMware ya que este valor es 0x4000 en VMWare [2].

11

Otra técnica anti-VMware utilizada es el famoso truco de instrucciones IN. Suponiendo que la muestra se ejecuta en VMware, la ejecución de la instrucción IN hará que EAX y EBX conserven la versión de VMware y ‘VMXh’ respectivamente. En casos normales, se generará una excepción ya que la instrucción IN tiene privilegios. Es por eso que, antes de ejecutar cualquier parte de este código, se conun controlador de excepciones. Este controlador recuperará inmediatamente el puntero de la pila anterior y devolverá la ejecución a la persona que llama con un «valor de retorno» exitoso (CF = 1).

Para evitar todas estas comprobaciones, simplemente se puede esperar a que el código descifrado regrese a Avtr_anti_VM y luego disminuir Mem+0x18.

Esto fue todo por esta parte; La próxima vez examinaremos las diferentes rutas de código que toma Avatar, dependiendo de las circunstancias, para cargar el controlador del rootkit en el kernel.

Referencias:

[1]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa376389(v=vs.85).aspx

[2]: https://www.aldeid.com/wiki/X86-assembly/Instructions/str