Hace algún tiempo estuvimos analizando en este blog un archivo malicioso que contaba con ciertas técnicas de anti-VM para ocultar sus verdaderas intenciones al ser ejecutado en entornos virtualizados. Hoy veremos otras técnicas no tan comunes pero igualmente efectivas, basadas en instrucciones especiales de la arquitectura x86.
De manera sencilla, un entorno virtualizado es posible gracias a la existencia de un programa monitor de la VM. En general, aquellas instrucciones privilegiadas en modo kernel que deseen ejecutarse en la VM no serán realmente ejecutadas por el procesador físico, sino que serán emuladas por el programa monitor. Por su parte, las instrucciones en modo usuario sí son ejecutadas directamente en el procesador, generando interrupciones que son atendidas por el monitor de la VM. De este modo, la virtualización debería ser transparente para las aplicaciones de usuario.
Sin embargo, existen ciertas instrucciones provistas en la arquitectura x86 que acceden a tal información de hardware que, para ser correctamente virtualizadas, requerirían de la emulación de todo el set de instrucciones por el monitor de la VM, lo cual sería demasiado costoso en términos de performance. Para complicar las cosas, estas instrucciones son accesibles desde el espacio de usuario, con lo cual VMware (por ejemplo) simplemente ha decidido no virtualizarlas correctamente. Como podrán imaginar, la utilización de estas instrucciones generará resultados distintos en un entorno nativo versus uno virtualizado, hecho que puede ser aprovechado por las aplicaciones maliciosas.
Así, encontraremos algunas estructuras en memoria cuya ubicación y tamaño son almacenados en registros únicos y leídos por instrucciones particulares. La estructura interrupt descriptor table (IDT) almacena información sobre cómo deben ser manejadas las interrupciones; su dirección es leída del registro pertinente mediante la instrucción sidt. Además, existen dos tablas con información del mapeo de los diferentes segmentos en memoria, global descriptor table (GDT) y local descriptor table (LDT), accedidas con las instrucciones sgdt y sldt, respectivamente.
Para ilustrar lo expuesto, a continuación veremos cómo se utiliza la instrucción sidt para leer el registro con la información de la tabla IDT (registro IDTR), de tal modo de detectar la presencia de VMware:
En el código principal se invoca a la rutina read_idtr, que es la encargada de ejecutar la instrucción sidt; vemos que en realidad almacena el valor en [IDTR+2]. Luego, al valor leído se le aplica una operación de corrimiento de bits y, si el valor resultante es 0xFF, se ha detectado la presencia de VMware. Para ser más específicos, en general el quinto byte de IDTR posee el valor típico de 0xFF en VMware; No ocurre así en sistemas nativos. Luego, para contrarrestar esta técnica, el analista puede remplazar la instrucción sidt por un NOP. Adicionalmente, la ejecución en un entorno virtualizado multiprocesador no garantiza el funcionamiento de esta técnica de anti-VM, dado que no se puede asegurar que cada procesador tendrá el quinto byte a 0xFF.
Otra técnica que pueden utilizar los cibercriminales consiste en la lectura de la tabla LDT: esta estructura suele no ser utilizada en Windows, con lo cual la ejecución de sldt devuelve cero como resultado. En VMware, sin embargo, el resultado será distinto de cero. Existen además otras instrucciones y otras técnicas para evitar la ejecución en máquinas virtuales. En un próximo post analizaremos una aplicación que realiza todas estas comprobaciones y evaluaremos la creación de firmas para detectar la presencia de estas técnicas en una cantidad grande de archivos.