Analisis de virus: Avispa


En este numero vemos un virus argentino que causó problemas a mucha 
gente, el Avispa.

El virus avispa infecta solamente archivos .exe. En el momento de 
ejecutarse, si no se encontraba residente en memoria, intenta infectar 
los archivos c:\dos\xcopy.exe, c:\dos\mem.exe, c:\dos\setver.exe y 
c:\dos\emm386.exe, en el caso de que no estén previamente infectados. 
Obviamente lo hace para maximizar sus posibilidades de reproducción, ya 
que esos archivos suelen estar en el autoexec.bat o son muy usados. El 
virus queda residente en memoria como si fuera un residente común, no 
hace ningun esfuerzo para ocultarse en memoria, excepto que se copia al 
segmento del PSP del programa, para ocupar menos memoria una vez 
residente. Una vez residente va a infectar cada programa que empieze con 
los bytes MZ (identificador de .EXE) que se intente ejecutar. Está 
encriptado, y cada vez se encripta con una clave distinta, si bien 
siempre usa el mismo algoritmo. El virus funciona solamente en 386 y 
superiores, ya que usa instrucciones que sólo se encuentran en estos 
procesadores.

Tiene una rutina de daño muy interesante, de un tipo muy poco usado. En 
vez de modificar o destruir información en el disco, lo que hace es 
modificar el retorno de la interrupción 13h. Cuando se hace un pedido de 
leer uno o más sectores a partir del cilindro 10 en adelante con la 
interrupción 13h, y en ese momento el word más bajo del system timer 
está en 0, el virus llena los primeros 512 bytes del buffer de datos con 
el texto '$$ Virus AVISPA $$ Republica Argentina$$ Elijah Baley $$ 
Noviembre 10 de 1993 $$ This program is not an old virus variant, and it 
was written in Argentina by Elijah Baley. $$=64446$$', más los datos que 
se encuentren a continuación del virus en memoria, hasta llenar 512 
bytes. Por lo tanto, el virus no afecta a los datos del disco, pero 
afecta a los programas que leen datos del disco. En el caso de que lo 
que se lea con error sea un programa, probablemente se cuelgue la 
máquina. El word bajo del system timer está en cero cada poco más de 
media hora, así que en el funcionamiento normal de una máquina el efecto 
va a ser muy notable.

El virus contiene los siguientes textos, encriptados:

c:\dos\xcopy.exe
c:\dos\mem.exe
c:\dos\setver.exe
c:\dos\emm386.exe
__ Virus Avispa - Buenos Aires - Noviembre 1993 __


$$ Virus AVISPA $$ Republica Argentina$$ Elijah Baley $$ Noviembre 10 de 
1993 $$ This program is not an old virus variant, and it was written in 
Argentina by Elijah Baley. $$=64446$$

Podemos agregar que Elijah Baley es un personaje de la novela 'Bóvedas 
de Acero', de Isaac Asimov, y de las novelas que continuan su saga.
Notemos tambien que fue escrito en noviembre de 1993, y que en muy pocos 
meses se convirtió en epidemia. Estamos viviendo en Argentina una época 
donde, al contrario de lo normal, los virus que más infectan son los 
nuevos y no los viejos, y son originados en Argentina.

Funcionamiento

El Avispa, como ya dijimos, está encriptado, siempre con una clave 
distinta. Obviamente, lo primero que hace antes de nada es desencriptar 
su código. Para esto, hace un simple XOR de cada word del código con un 
valor. El loop de desencriptado es muy interesante porque en lugar de 
poner las constantes en forma directa, carga primero un valor en el 
registro y luego le suma otro valor para obtener el buscado. Esto es 
obviamente para dificultar su análisis automático con scaneadores 
heurísticos, por ejemplo. Podemos ver cómo lo hace con este pedazo de 
código, donde inicializa las constantes usadas por el desencriptor.

comienzo:
mov bx,11Eh
mov bh,bh
add bl,1Ch ; bx = 13Ah
mov ch,ch

Acá vemos que BX primero vale 11Eh y luego se le suma 1Ch para llegar al 
valor 13Ah, que es el comienzo del código a desencriptar. Las 
instrucciones mov bh, bh y mov ch, ch son instrucciones que no hacen 
nada, y son insertadas al azar por el virus en el desencriptor, para que 
sea difícil encontrar el virus con un string constante de scaneo.

El siguiente código es el desencriptor en sí, que sigue con el mismo 
criterio. Como veremos más adelante, el desencriptor es variable en más 
que en las instrucciones que no hacen nada insertadas.

desencriptar:
mov ax,cs:[bx] ; leer en ax para desencriptar
mov ch,ch
mov cx,13Bh
mov ch,ch
sub cl,29h ; cx = 164h (clave)
mov dh,dh
xor ax,cx ; ax es desencriptado
nop
mov dh,dh
mov cs:[bx],ax ; poner ax donde estaba
mov dh,dh
add bx,2 ; incrementar en 2 bx
mov al,al
mov ax,0FADBh
mov al,al
add ax,0DF1h ; ax = 8CCh
mov al,al
nop
cmp bx,ax ; bx es = 8CCh? (largo del código)
mov bl,bl
jc desencriptar ; Si no es asi, sigue desencriptando

El desencriptor es mucho más largo de lo que debería ser justamente por 
estas consideraciones polimórficas. El virus es casi polimórfico, porque 
hay pocas variantes de desencriptores que puede generar. El método de 
desencripción es extremadamente sencillo, hasta podríamos decir que es 
clásico, un XOR con un valor aleatorio. Cuando termina de desencriptar, 
salta por encima de una zona de variables y empieza el virus en sí. 
Primero intenta verificar si está residente en memoria, utilizando un 
servicio de la interrupción 21h redefinido por el virus, el 4BFFh. Si 
retorna 4BFEh significa que el virus estaba residente en memoria. Si es 
así, simplemente restaura el stack definido por el header original del 
programa y salta (mediante un ret far) al programa original.
Si no está residente en memoria, se copia al segmento del PSP (al que 
apunta el DS al cargarse el programa), y sigue ejecutándose en esa 
posición mediante un ret far hacia el nuevo segmento donde reside. Una 
vez copiado libera toda la memoria excepto la necesaria para si mismo. 
Guarda el valor actual del vector de la interrupción 21h y de la 13h en 
variables, y los redefine a su propio código, poniendo las rutinas de 
auto- detección e infección en la 21h y la de daño en la interrupción 
13h. Una vez que hace esto, busca en el segmento del environment el 
nombre del programa huesped del virus, para ejecutarlo. Si no lo 
encuentra, simplemente queda residente y no lo ejecuta. Una vez 
encontrado el nombre, llama a la interrupción 21h (mediante el vector 
que tenía salvado, para que no intente infectar al programa nuevamente) 
y utiliza la función 4B00h para ejecutarlo. Notemos que carga al 
programa nuevamente, por lo cual en discos lentos o programas largos 
puede notarse una demora en la carga del primer programa infectado. 
Luego de ejecutarlo, infecta los programas pre-definidos para infectar:
'c:\dos\xcopy.exe', 'c:\dos\mem.exe', y 'c:\dos\setver.exe'. Luego 
libera la memoria que antes había reservado, y queda residente usando la 
función 31h de la interrupción 21h.
Notemos que esta estrategia es muy interesante, ya que el virus, cuando 
ejecuta el programa, ya tiene el control de la interrupción 21h, por lo 
que infectaría a cualquier programa que se ejecute mientras dure la 
ejecución del huésped, y recién ejecuta a sus víctimas pre- 
seleccionadas cuando este programa termina, con lo cual hay menos demora 
en la carga del programa. Tambien notemos que intenta infectar a esos 
programas del DOS en el caso de que no se encontrara ya residente en 
memoria, porque considera que si estaba residente esos programas ya 
están infectados.

Interrupción 21h

El handler para esta interrupción es el que se ocupa de infectar. 
Primero verifica que la función llamada sea la 4BFFh, y si es así, 
devuelve 4BFEh en AX, y vuelve de la interrupción. Con esto se 
auto-detecta en memoria. Si la función llamada es la 4B00h se prepara 
para infectar. Si no es ninguna de las dos, sigue con la interrupción 
21h del DOS.
Para infectar, guarda el puntero hacia el nombre del programa que se 
intenta ejecutar en una variable, y llama a la subrutina de infección. 
Luego vuelve a la interrupción 21h normal.
La rutina de infección llama a otra rutina que chequea si el nombre del 
programa a ejecutar termina en AN, LD u OT. Obviamtente busca a los 
programas scAN.exe, f-prOT.exe y alguno que termina en LD y que no se me 
ocurre en este momento. Si descubre que se trata de uno de esos 
programas, devuelve 1 en AH, si no es así, devuelve 0. La rutina de 
infección verifica si devolvió 1, y en ese caso no intenta infectar el 
programa. En el caso de que decida infectarlo, abre el archivo, y lee 
sus primeros 127 bytes. Verifica si empieza con MZ, el identificador de 
.EXE, y si no es así, sale sin infectar. Si es un .EXE lo que leyó es su 
header. Verifica que el CS y el IP inicial, definidos en el header, sean 
distintos a 0, si por lo menos uno de ellos es distinto a 0 sigue 
infectandolo.
Abre el archivo, y si hay algún error en la apertura (quizá debido a que 
ese archivo no existe), no intenta seguir infectando. Si pudo abrirlo 
verifica que el archivo no sea más largo a lo declarado en el header del 
.EXE. Si es más largo considera que tiene overlays o algo así, y no lo 
infecta.
Si luego de todas estas pruebas lee los últimos 52 bytes del archivo, 
para la última prueba, verificar si el programa estaba infectado 
previamente. Estos últimos 52 bytes, en el caso de un programa 
infectado, contienen el texto encriptado
'__ Virus Avispa - Buenos Aires - Noviembre 1993 __'
precedido por la clave con la cual está encriptado. Se desencripta con 
un XOR de cada word del texto con la clave. Este método puede usarse 
para escribir un programa para detectar al virus, con un 100% de 
seguridad: se leen los últimos 52 bytes del archivo .EXE a verificar, se 
toma el primer word del mismo, y se hace un XOR de ese valor con cada 
uno de los words restantes. Si el texto desencriptado es el que ya 
mencionamos, el archivo está infectado. El virus, de todas formas, no 
hace exactamente esto. Lo que hace es desencriptar cada word y sumarlo 
en DX, y luego comparar si DX termina valiendo 7DDAh. Si es así, 
considera que el programa está previamente infectado. En definitiva, lo 
que hace es calcular un checksum del mensaje, en vez de compararlo con 
el texto real. En cuestión de código no se ahorra nada haciéndolo, 
hacerlo así es más largo que comparar con una instrucción de comparación 
del procesador.
Si el programa no está infectado, procede a hacerlo. Primero toma la 
interrupción 24h y la reemplaza por una que marca en una variable que 
fue llamada y luego vuelve, con lo cual puede saber si hubo error 
chequeando esa variable, y no alerta al usuario de posibles errores. 
Luego lee los atributos del archivo, y los guarda en una variable. Borra 
todo atributo del archivo, con lo cual puede escribir sobre él sin 
problemas. Vuelve a poner la interrupción 24h normal, y verificar que no 
hubieron errores durante su ejecución, mediante la variable que modifica 
el handler que instaló antes. Luego abre el archivo, y guarda la fecha y 
hora del mismo en una variable. Va al final del archivo, y empieza a 
rellenarlo de bytes 0 hasta que su largo queda divisible por 16. Luego 
procede a modificar el header del .EXE que tenía leído. Suma 4 al número 
de parrafos del .EXE, guarda el IP, CS, SS y SP originales en variables 
y los modifica por los adecuados para ejecutar el virus. Luego genera 
una zona de memoria para trabajo, reservando 90h párrafos de memoria y 
copiando el virus a esa zona. Eso lo usa de área de trabajo para 
encriptar el virus y generar el desenctriptor. Llama una subrutina que 
genera el desencriptor y luego el virus encriptado. Toma el byte más 
bajo del reloj de la máquina y se lo resta al offset del origen del 
código a desencriptar. Luego escribe en el desencriptor las operaciones 
necesarias como para volver a obtener el mismo valor. Genera una clave 
al azar a partir del reloj de la máquina, también dividiéndolo en dos 
operaciones, y generando el código para desencriptar con esa clave. 
Luego, al azar, genera el resto del desencriptor a partir de un número 
bastante corto de posibilidades. Básicamente, invierte algunas 
operaciones de orden y agrega algunos nops para rellenar en ciertas 
partes. Genera la condición de salida del loop del desencripor, 
comparando si llegó al offset 2252, o sea, a los 1996 bytes que quiere 
desencriptar, también con un número partido en dos partes aleatorias. 
Luego rellena los espacios vacíos que fue dejando de código con 
operaciones que no hacen nada al azar. A continuación encripta el virus 
y le agrega al final el texto que nombramos antes, encriptado.
Luego escribe el virus al final del archivo, sobreescribe el header del 
.EXE por el modificado, recupera la hora y día del archivo originales, 
recupera los atributos, y libera la memoria que había reservado para 
trabajar. El archivo creció 2048 bytes más lo necesario como para que su 
largo quede divisible por 16. Después de infectar, vuelve a la 
interrupción 21h normal.

Rutina de daño

La rutina que reemplaza a la interrupción 13h verifica si está 
llamándose a la función 02h, leer sectores. Si no es así, sigue con la 
interrupción 21h normal. Luego compara si el cilindro a leer es superior 
al 10. Si es así, se fija en el word más bajo del reloj del sistema. Si 
está en cero, llama a la interrupción 13h original con los parámetros 
con que la llamaron, y sobreescribe los primeros 512 bytes del buffer 
con el texto que mencionamos antes. Luego vuelve de la interrupción. Si 
no estaba en cero, continúa con la interrupción normal.

Conclusiones

Este virus es sencillo, tiene un método muy sencillo de polimorfismo, 
aunque es extremadamente fácil de detectar algorítmicamente (usando el 
método que ya describí). Es interesante el daño, ya que causa molestia, 
pero no tanta pérdida de información, por lo menos si la causa no es 
directamente sino indirectamente, por ejemplo si un programa copia de un 
lugar a otro un archivo, puede darse la casualidad que la copia destino 
quede destruida por el virus. Lo que no se sabe, como siempre, es cómo 
llegó a difundirse tanto por el país.

Fernando Bonsembiante es jefe de redacción de Virus Report y está 
estudiando los virus informáticos dese hace varios años. Tambien es 
miembro de la comisión directiva del Círculo Argentino de Ciencia 
Ficción, (CACyF) y participa como columnista de varias revistas sobre 
informática. También es asesor en seguridad informática y virus en 
varias empresas. Puede ser contactado por Fido en 4:901/303 o en 
Internet en ubik@ubik.to