Los virus de arranque (I)

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

Son los más fáciles de detectar y (generalmente) de eliminar. Los virus de ficheros pueden estar -al menos- en tantos sitios como programas ejecutables haya. Los de arranque, sin embargo, sólo pueden situarse en dos sectores concretos del disco (en uno si es un disquete). Y, paradójicamente, son los virus más extendidos, más populares y que más estragos causan. En las líneas que siguen serán estudiados en profundidad.

Esos dos sectores tan característicos (en seguida se verá el por qué de sus particularidades) son el de arranque, tanto de discos duros como de disquetes, y el MBR (Master Boot Record) de los discos duros. Puesto que en ellos se alojan los virus, lo primero que hace falta es el saber cómo verlos. Se utilizará, como siempre, el DEBUG.

El sector de arranque es el primer sector lógico tanto de un disquete como de una partición DOS. En un disquete, ese sector coincide con el primer sector físico, pero en un disco duro eso no tiene por qué ser así. Como es el primer sector que "entiende" el DOS, en ambos casos se puede leer haciendo tan sólo: -l7c00 0 0 1 (cargar en el desplazamiento de memoria 7C00h de la unidad 0 -primera unidad de disquete- el sector 0 -primer sector lógico del disco- y leer sólo un sector). Para un disco duro sólo hay que cambiar el número de unidad: -l7C00 2 0 1.

El MBR está fuera de la partición DOS y, por tanto, no se puede acceder a él directamente. Hay que hacerlo con la BIOS, concretamente con la interrupción que maneja unidades de disco: la 13h. La lectura ahora ya no es de sectores lógicos: empezando por el 0 y de uno en uno hasta llegar al final. Ahora se trabaja con sectores físicos: cilindro:cabeza:sector. El MBR está en la cabeza 0, cilindro 0, sector 1. El código para leerlo se dio ya en la anterior entrega, aunque se repite aquí por conveniencia:

-a
XXXX:100 mov ax,0201
XXXX:103 mov bx,7c00
XXXX:106 mov cx,1
XXXX:109 mov dx,80
XXXX:10C int 13
XXXX:10E int 20
XXXX:110
-g

Ahora el sector está en memoria en XXXX:7C00 y se puede ver, estudiar y desensamblar. Se aconseja consultar las referencias indicadas al final del artículo con el fin de familiarizarse con las diferentes funciones de la interrupción 13h. Aparte de la 02, ya explicada en el artículo anterior, que sirve para leer sectores, es de especial interés la 03, que realiza las funciones de escritura. Los restantes registros mantienen el mismo significado que para la lectura, así que este mismo fragmento de código se puede utilizar para escribir en el MBR los 512 bytes que se encuentran a partir de ES:7c00 con tal de cambiar la primera línea por MOV AX,0301. Con esta nueva función será con la que se restaure el MBR original.

Para poder entender la forma de actuación de estos virus y los cambios que realizan en los mencionados sectores es imprescindible tener unos conocimientos bastante precisos de cómo se realiza el arranque convencional del ordenador.

Se verá eso en primer lugar para después observar cómo un virus real opera en la práctica modificando estas estructuras.


EL PROCESO DE ARRANQUE

Desde el momento en que se aprieta el botón de encendido del ordenador hasta que el intérprete de comandos del sistema operativo elegido se encuentra a la espera de las órdenes pertinentes, hay toda una serie de complicadas acciones que a menudo pasan inadvertidas al usuario ordinario, pero que es imprescindible conocer para entender el funcionamiento de los virus de arranque.

Puesto que, como se explicó el mes pasado, estos virus toman el control del ordenador antes incluso de que llegue a cargarse el sistema operativo, es imprescindible que alteren el proceso de arranque habitual para cargarse ellos en memoria, y continúen después como si nada hubiera pasado para que el usuario no advierta comportamientos extraños. Se explicará primero el proceso de carga normal, tal y como lo pensaron en un principio los diseñadores del PC original de IBM, para ver a continuación el punto en que el virus tiene ocasión de modificarlo en su provecho y luego reconducirlo para que el cambio resulte transparente.

Al pulsar el interruptor de encendido, toda una avalancha de impulsos eléctricos empieza a recorrer el interior del ordenador. Haciendo abstracción de la parte circuital, pasado un tiempo (del orden de nanosegundos) los transistores del microprocesador han adquirido ya unos niveles de tensiones y corrientes adecuados, y la CPU se encuentra en condiciones de ejecutar instrucciones. En todos los PCs compatibles esas primeras instrucciones se encuentran en la misma posición de memoria: F000:0000, correspondiente a los chips de ROM que alojan la BIOS (Basic Input Output System) o "Sistema básico de entrada/salida". En esta zona se encuentran rutinas que realizan comprobaciones para asegurarse que los componentes necesarios para el buen funcionamiento del PC se hayan presentes y funcionan satisfactoriamente. Habitualmente esta parte del proceso de arranque recibe el nombre de POST (Power On Self Test, o "auto-test de encendido"): tras hacer una autocomprobación de la propia CPU, se envían señales por los buses al resto de la circuitería para comprobar que está presente y funcionando. Se comprueba la memoria de la tarjeta y se envían señales para controlar su funcionamiento. En este momento aparecen las primeras imágenes en la pantalla. Siguiendo con la ejecución del POST de la ROM-BIOS, se comprueba la RAM escribiendo datos en cada celda de memoria y leyéndolos a continuación para comparar que sean idénticos. Un contador de la cantidad de RAM que se lleva chequeada se muestra por pantalla.

A continuación se envían señales al teclado, comprobándose de paso si hay alguna tecla pulsada. Tras ello se envían más señales eléctricas a todas las unidades de disco, anotándose cuántas se hayan funcionando. En los PC AT y posteriores existe una memoria CMOS (conocida también como "el SETUP"), alimentada por baterías, en la que se guarda información relativa al hardware del equipo (tipo y número de discos duros, cantidad de memoria, etc...) que se mantiene aún cuando el ordenador está apagado. Por ello, si el equipo no es un XT, los resultados del POST son comparados con lo guardado en el SETUP, que es el resultado que cabía esperar. Si todo va bien se emite un único pitido, señal de que todos los tests han sido completados con éxito. En caso de haberse detectado problemas, la BIOS trata de informar de sus posibles causas.

Como es posible que los errores encontrados impidan el correcto funcionamiento de la pantalla, es muy común que manifieste los errores mediante combinaciones de pitidos largos y cortos. Con casi toda probabilidad el lector se habrá encontrado alguna vez en esta situación. Así un pitido largo seguido de otro corto indica problemas con la placa madre, dos pitidos cortos apuntan a fallos del monitor, etc.

Superado el POST de encendido, seguimos en ROM. Ahora se ejecuta el código que "traerá a la vida" al sistema operativo. En primer lugar, si el SETUP no dice lo contrario, se busca la presencia de un disco de arranque en una disquetera y, en caso de no encontrarse, se trata de arrancar con el disco duro. Como esto puede traer problemas de seguridad y, como se verá, infecciones con virus de arranque, se incluyó posteriormente la posibilidad de cambiar el orden de arranque en el SETUP para que primero trate de arrancar el disco duro y, sólo en caso de no lograrlo, la disquetera.

Hay una pequeña diferencia entre el caso de arrancar con disco duro o con disquete. En el primero la BIOS lee el "Master Boot Record" (MBR), también llamado habitualmente "Tabla de Particiones" en reconocimiento a la importancia de una estructura de datos contenida en el sector, y le transfiere el control, mientras que en el arranque desde disquete, al carecer estos de MBR, lo que se lee y ejecuta es el "Sector de Arranque". Dado que en un arranque desde disco duro también existen sectores de arranque, y finalmente se les pasa a ellos también el control, la descripción del proceso de arranque general se hará suponiendo que se ha arrancado desde disco duro, bastando olvidar todo lo mencionado hasta llegar al punto del sector de arranque en el caso de que se quiera conocer cómo es un arranque desde disquetera.

Como se ha dicho, la BIOS lee y carga en memoria el Master Boot Record, que está siempre en el primer sector del disco duro: cabeza 0, cilindro 0, sector 1. La posición de memoria en que la deposita es, así mismo, constante: 0000:7C00. La ROM-BIOS comprueba que los 2 últimos bytes del sector sean 55,AA, marca utilizada para asegurarse de que se trata de un MBR, e inmediatamente el control del ordenador deja de estar en la ROM y pasa a esa dirección. Lo primero que hace el MBR es desplazarse a sí mismo (sus 512 bytes) a la zona de memoria 0000:0600 (como siempre, y mientras no se diga lo contrario, la dirección se da en hexadecimal), para hacer sitio al sector de arranque que el propio MBR cargará y que, por convenio, debe hacerlo también en 0000:7C00.

TABLA 1: Estructura del sector de particiones (MBR) del disco duro.
Desplazamiento Longitud Contenido
000h Variable Código
1BEh 16 bytes Entrada de la Partición número 1
1CEh 16 bytes Entrada de la Partición número 2
1DEh 16 bytes Entrada de la Partición número 3
1EEh 16 bytes Entrada de la Partición número 4
1FEh 2 bytes Código de identificación: 55h, AAh.


La estructura del MBR se detalla en la tabla 1. El código suele ser bastante breve. Una primera parte se limita a comprobar que haya una única partición de arranque. Para ello se empieza a mirar a partir de la posición 0000:07BE (600 + desplazamiento 1BE dentro del sector) que, como indica la tabla 1, es donde empieza la información de cada partición. El objetivo es comprobar el primer byte de cada tabla: si es un 00, la partición no es de arranque; si tiene un 80h, lo es. Seguidamente, una vez identificada ésta de entre las 4 posibles (*), se mira en su entrada en la tabla correspondiente cuál es el número de cabeza, el número de cilindro y el número de sector en que comienza la partición (ver tabla 2). Se lee entonces el sector correspondiente a esa combinación de cabeza-cilindro-sector, y se guarda en la posición 0000:7C00.

Este sector debe ser el sector de arranque de la partición, encargado de cargar el núcleo del sistema operativo, y a él se salta para continuar con la carga.

TABLA 2: Estructura de una entrada en la Tabla de Particiones.
Desplazamineto Longitud Contenido
00h 1 byte Estado de la partición: 80h = Partición de arranque. 00h = Partición inactiva.
01h 1 byte Cabeza de comienzo de la partición.
02h 1 word Sector y cilindro de comienzo de la partición.
04h 1 byte Tipo de partición: 04h=DOS con FAT de 16 bits, 05h=DOS extendida, 06h=DOS > 32 Mb, 83h=Linux, 82h=Linux swap...
05h 1 byte Cabeza en que termina la partición.
06h 1 word Sector y cilindro en que termina la partición.
08h 1 dword Distancia, en sectores, desde el sector de arranque de la partición al MBR.
0Ch 1 dword Número de sectores en la partición.


Ya en el sector de arranque, y suponiendo que se trate de una partición DOS, que es el sistema del que se está haciendo el estudio de sus virus, se empezará actualizando una tabla justo al comienzo del sector (tras un JMP al resto del código) con información sobre el disco (bytes por sector, sectores por cluster, etc.), y después se mirará cuál es el sector en que empieza el fichero IO.SYS, que es lo que podríamos llamar el núcleo del DOS. A partir de ahí se leen los sectores correspondientes a ese fichero (llamado también algunas veces IBMBIOS.COM) y se ejecuta su código.

IO.SYS contiene extensiones de la ROM-BIOS que añade a la BIOS con la que ya se contaba desde el comienzo del arranque. Seguidamente se carga también el MSDOS.SYS (en algunos sistemas, IBMDOS.COM) que añade las llamadas del sistema operativo (interrupción 21h) con servicios para tratar con archivos (**): si hasta ahora el ordenador sólo entendía de cabezas y sectores, según se continúa con la ejecución el código será capaz de hacer abstracción y "comprender" el significado de ficheros y directorios. El MSDOS.SYS lee un fichero de configuración creado por el usuario, el CONFIG.SYS, con información sobre drivers de dispositivo y parámetros para el sistema operativo tales como el número de buffers. Para finalizar se carga el COMMAND.COM, el intérprete de comandos que "ejecuta" otro fichero de configuración con las primeras órdenes que se han de ejecutar en cada arranque: el AUTOEXEC.BAT.


MODIFICANDO EL PROCESO DE ARRANQUE: EL VIRUS SE INTERPONE

Tal y como se ha descrito, el proceso de arranque comienza ejecutando código que viene con el ordenador pregrabado en unos chips de ROM. Esto hace que sea inalterable por el virus que sólo tiene capacidad de actuar y modificar el discurrir habitual de los hechos cuando se abandona la ROM, es decir, cuando la BIOS pasa el control de la ejecución al MBR del disco duro o al sector de arranque del disquete.

El procedimiento, por tanto, para recibir el control tras el POST consiste en sustituir el código de ese sector por el del virus, que se acomoda así en memoria, carga otros sectores si ocupa más de 512 bytes, y continua después con el proceso normal de carga sin que note nada el usuario, pero teniendo ya el control del sistema.

Veamos cómo hace esto un virus real: el Anti-Telefónica, de "producción" española, que se aloja en el sector de arranque de los disquetes y en el MBR de los discos duros. Al llegar a los 333 encendidos del ordenador (otras versiones lo hacen a los 400) sobrescribe el disco duro y muestra por pantalla el mensaje: "Campaña Anti-TELEFONICA (Barcelona)", mensaje que guarda encriptado dentro del sector que sustituye al de arranque original. Se supondrá que ya se ha cargado el sector infectado en la posición que ocupará cuando lo ejecute la BIOS: es decir, en el desplazamiento 7C00. Lógicamente no se puede cargar en el segmento 0 con el debug, porque una vez cargado el sistema este segmento tiene las direcciones de los vectores de interrupción, variables de la BIOS, etc.

-u 7c00
0B91:7C00 EB1C 		JMP 7C1E
0B91:7C02 90 		NOP
0B91:7C03 4D 		DEC BP
0B91:7C04 53 		PUSH BX
0B91:7C05 44 		INC SP
0B91:7C06 4F 		DEC DI
0B91:7C07 53 		PUSH BX
0B91:7C08 332E3200 	XOR BP,[0032]
0B91:7C0C 0202 		ADD AL,[BP+SI]
0B91:7C0E 0100 		ADD [BX+SI],AX
0B91:7C10 027000 	ADD DH,[BX+SI+00]
0B91:7C13 D002 		ROL BYTE PTR [BP+SI],1
0B91:7C15 FD 		STD
0B91:7C16 0200 		ADD AL,[BX+SI]

El inicio del código, seguido de datos: obsérvese la falta de coherencia de las "instrucciones" que siguen al JMP. Al no tener el código fuente es importante saber distinguir lo que es código o lo que son datos, puesto que al pedirle al debug que desensamble una zona de memoria, lo hará aunque esa zona no sea de instrucciones. El no tiene forma de distinguir entre unos y otros: debe ser el analista el que lo haga.

-u 7c1e
0B91:7C1E BB007C 	MOV BX,7C00
0B91:7C21 31C0 		XOR AX,AX
0B91:7C23 FA 		CLI
0B91:7C24 8ED0 		MOV SS,AX
0B91:7C26 89DC 		MOV SP,BX
0B91:7C28 FB 		STI

Comienza con algunas inicializaciones preliminares de la pila (stack)...

0B91:7C29 8ED8 		MOV DS,AX
0B91:7C2B A11304 	MOV AX,[0413]
0B91:7C2E 48 		DEC AX
0B91:7C2F A31304 	MOV [0413],AX

Decrementa el número de Kbytes de memoria disponibles (variable localizada en la posición 0000:0413h) y así se reserva ese Kbyte para el virus.


ELIMINAR UN VIRUS DE ARRANQUE

Para terminar, se ofrece un resumen de los pasos, ordenados, que se sugieren para eliminar un virus de arranque:

Leer el sector original y colocarlo en su lugar, reescribiendo el virus. Re-arrancar.

Si todo va bien, el proceso ha terminado. De quedarse colgado el sistema, colocar de nuevo la copia que se hizo del sector vírico en su lugar, y tratar de identificar el problema. Téngase en cuenta que algunos virus, sobre todo cuando atacan a disquetes, no guardan copia del sector original. En tal caso la única solución consistirá en colocar un sector de arranque "genérico".

NOTAS

(*) El hecho de que sólo haya 4 posibles particiones primarias parece obedecer más a razones de convenio que a limitaciones impuestas por la arquitectura. De hecho, es bastante fácil rehacer el código del MBR de forma que acepte más particiones, aunque luego todos los programas que consulten la tabla (como el fdisk) se atendrán al convenio de empezar en el desplazamiento 1BEh y no "reconocerán" las otras...

(**) En todas las versiones del DOS el procedimiento de carga era el descrito. Sin embargo, con la llegada de Windows 95 ha cambiado ligeramente al integrarse todos los servicios en el IO.SYS y pasar el MSDOS.SYS a convertirse en fichero de configuración en modo texto, con datos tales como si se debe arrancar el entorno de ventanas desde un principio o se debe esperar a que se escriba "win" para cargarlo, o si se muestra o no el logotipo de Windows 95 al arrancar.

REFERENCIAS:

Dos referencias obligadas sobre interrupciones y funcionamiento de la BIOS y el DOS son :

  • "Guía del programador para el IBM PC y PS/2", de Peter Norton y Richard Wilton. Anaya.
  • "PC interno", de Michael Tischer. Marcombo.

Existen programas, muchas veces residentes en memoria, que facilitan esta misma información de forma interactiva, con menús. Uno de ellos es HelpPC, de David Jurgens. También en formato electrónico se puede encontrar la que, casi con toda seguridad, es la recopilación más exhaustiva de interrupciones del PC : la llevada a cabo por Ralf Brown. Todos ellos son fáciles de localizar en Internet.