Image
Desempaquetando VMProtect 3.X

Desempaquetando VMProtect 3.X

¡Hola lectores!

Volvemos al blog con un nuevo artículo. Hoy vamos a hablaros sobre VMProtect, también abreviado VMP, un conocido software de protección para programas Windows. Este incluye gran cantidad de técnicas sofisticadas como mutación y virtualización de código, que dificultan tremendamente la tarea de realizar ingeniería inversa sobre un software que cuenta con esta protección.

Dadas las grandes ventajas que aporta, a lo largo de los años no solo ha atraído la atención de las compañías de desarrollo de software, sino que también se ha vuelto bastante popular su uso entre los desarrolladores de malware. No obstante, al tratarse de un software comercial, normalmente estos optan por utilizar versiones pirateadas y, debido también a su inexperiencia con el uso de la herramienta, no llegan a configurarla correctamente para aplicar las protecciones más avanzadas de mutación y/o virtualización, como en el caso del reciente ransomware Night Sky.

Independientemente de que se apliquen estas ofuscaciones, todos los binarios protegidos por VMProtect son protegidos por un empaquetador propio que, además, permite la opción de ofuscar las llamadas a la API declaradas en la tabla de importaciones del binario original.

En este artículo vamos a describir el procedimiento de desempaquetado para binarios protegidos mediante VMProtect sin centrarnos, por ahora, en la protección mediante virtualización y/o mutación, un proceso completamente diferente y mucho más costoso de superar.

Ventana de configuración de VMProtect

Empaquetar un binario consiste en comprimir y/o cifrar sus secciones. En el caso de VMProtect, las secciones originales son sustituidas y todo el fichero original es condensado en una sección que contiene en formato cifrado las secciones originales, las rutinas para su descifrado y otras informaciones de interés sobre las secciones. Esta nueva sección recibe el nombre de “.vmp1” por defecto.

Secciones de un binario “Hello World” antes y después de ser empaquetado con VMProtect

Tras cargar el binario empaquetado en el debugger se observa que, salvo la sección .data, ninguna tiene permisos de lectura. Por tanto, si el binario desea extraer la información original, deberá cambiar los permisos de estas para así poder escribir sobre ellas y sustituir su contenido.

Para conseguirlo, existen API de Windows como VirtualProtect. Sin embargo, VMProtect ha decidido hacer uso de una API similar pero de más bajo nivel y menos documentada, como lo es Zw Protect Virtual Memory.

Para comenzar el proceso de desempaquetado es necesario llegar al Entry Point del binario empaquetado, que es completamente diferente al del original. De hecho, esta rutina se encuentra virtualizada, por lo que resulta prácticamente imposible distinguir su funcionalidad observando los opcodes que identifique cualquier desensamblador.

Teniendo en cuenta que se van a realizar diversas llamadas a la API mencionada, se establecerá, a partir de aquí, un punto de interrupción vía hardware en dicha API.

Breakpoint de hardware en la API ZwProtectVirtualMemory

De esta forma, comienza el proceso de desempaquetado, durante el cual el binario necesita establecer con permisos de escritura cada una de las secciones, eliminar el permiso de copia y restaurar los permisos originales. Por tanto, se debe alcanzar el breakpoint de la API ZwProtectVirtualMemory todas estas veces hasta que los permisos sean los iniciales.

Estado de los permisos de las secciones del binario analizado

Una vez realizado este proceso, siempre y cuando no se hayan aplicado protecciones adicionales, el código original del programa puede ser encontrado en memoria dentro de la sección .text. Por lo tanto, a partir de aquí se podría volcar al disco el binario mediante cualquier herramienta que permita obtener un volcado, como por ejemplo Scylla. No obstante, aún queda una última tarea previa a este paso: encontrar la dirección del Entry Point original (OEP).

Dado que el código de la rutina de desempaquetado se encuentra protegido mediante virtualización, lo más sencillo para tratar de dar con el OEP es eliminar los puntos de interrupción anteriores y establecer, ahora, un punto de interrupción en la sección de memoria .text, dado que el OEP debería ser la primera instrucción ejecutada.

Entry Point del programa original vs OEP en el protegido

Adicionalmente al empaquetado, VMProtect incorpora una opción que permite ofuscar la tabla de funciones importadas por el programa.

Opción que permite proteger la tabla de importaciones del binario en VMProtect

Tras aplicar dicha protección, el binario generado sigue conservando las funciones importadas, quizás para evitar tener que importar las librerías vía API con LoadLibrary. Sin embargo, estas ya no son utilizadas directamente y no es posible seguir su uso en el programa de forma estática, dado que las direcciones reales ahora son calculadas en tiempo de ejecución.

Al activar la opción de protección, cada llamada a una API en la sección .text que debería tener la forma “call <API>” y ocupar 6 bytes, es sustituida por un “push + call” del mismo tamaño. Esta nueva función, que es llamada con la instrucción “call”, recibe mediante la instrucción “push” un valor aparentemente aleatorio y se utiliza una función diferente por cada API que haya sido sustituida.

Por tanto, para poder recuperar las direcciones que debería contener la tabla de importaciones es necesario realizar dichos cálculos en base al binario protegido. No obstante, este proceso ya ha sido automatizado mediante diferentes herramientas como por ejemplo VMPDump.

Proceso de reconstrucción de imports en el ransomware Night Sky protegido por VMProtect

Como se ha podido observar, pese a que VMProtect es un software de protección que destaca por características como la visualización y la mutación de código ensamblador, estas opciones deben ser configuradas explícitamente a la hora de proteger el binario y, en algunos casos, estas protecciones no son aplicadas, tratándose así de un simple empaquetador del que se puede llegar a extraer el código original.

Y aquí concluye este artículo sobre VMProtect. Espero que hayáis aprendido algo nuevo y que os sea de utilidad

¡Nos vemos!


Mariano Palomo