Últimamente se ha puesto de moda entre el "malware" utilizar discos extraíbles (memorias usb, cámaras de fotos, reproductores de mp3, etc ...) para propagarse de un ordenador a otro. Para conseguirlo crean en el directorio raíz del disco un archivo "AUTORUN.INF" que permite ejecutar el software malicioso cada vez que el disco es conectado a un ordenador. La pequeña aplicación que muestro a continuación permite "vacunar" este archivo de tal forma que resulte mucho más difícil (aunque no imposible) modificarlo, impidiendo de este modo que nuestro disco sea infectado cuando lo usemos en otros ordenadores.
Para vacunar nuestro disco lo primero que tenemos que hacer es crear en el directorio raíz del disco un fichero llamado "AUTORUN.INF" (el archivo puede estar vacío)
Luego ejecutamos la aplicación como administradores, asegurándonos antes de que ningún programa esta usando el disco.
Despues de aceptar la clausula de exclusión de responsabilidad, se nos mostrará una pantalla como esta:
[0] Salir [1] \\.\C: [2] \\.\D: [3] \\.\E: [4] \\.\F: [5] \\.\G: Escoge:
Escogemos el número que corresponde a la unidad que queremos vacunar y pulsamos [Enter].
Se mostrará entonces una serie de información referente al disco, y si todo va bien, al final aparecerá el siguiente mensaje:
AUTORUN.INF encontrado Activando vacuna ... Proceso terminado
Ahora el fichero "AUTORUN.INF" que habíamos creado previamente, aparece oculto y "marcado" de tal manera que es inaccesible para cualquier programa.
No lo podremos ni abrir, ni modificar, ni siquiera borrarlo impidiendo de esta manera que ningún software malicioso lo reemplace por uno infectado. La única manera de eliminarlo es quitando la vacuna con este mismo programa, o formateando el disco. Aunque como ya dije mas arriba el método no es infalible, los fabricantes de malware también conocen esta técnica y podrían desmarcar el fichero para luego eliminarlo, pero la verdad es que la mayoría no se molestan en hacerlo, ademas como se necesitan permisos de administrador llamarían mucho la atención. La realidad es que algunas marcas de antivirus conocidas que también "vacunan" discos extraibles usan esta técnica, y si a ellos les parece segura, a mi también.
Para quitar la vacuna solo hay que volver a repetir los pasos anteriores, pero en este caso, al estar el disco ya vacunado, se mostrará el siguiente mensaje:
AUTORUN.INF encontrado Eliminando vacuna ... Proceso terminado
El código fuente y el programa ya compilado se pueden bajar de aquí
El código fuente es el siguiente (es una aplicación de consola)
unit umain; interface uses Windows, Sysutils, Classes; procedure main; implementation type PDWordArray = ^TDWordArray; TDWordArray = array[0..$0FFFFFFF] of DWord; TPartition = record Stream: TFileStream; PartBegin: DWord; BytsPerSec: Word; SecPerClus: Byte; BytsPerClus: Word; RsvdSecCnt: Word; NumFATs: Byte; FATSz32: Word; ClusCount: DWord; ClusBegin: DWord; RootClus: DWord; FAT: PDWordArray; end; function Min(i,j: Integer): Integer; begin if i < j then Result:= i else Result:= j; end; // Muestra el contenido del buffer en hexadecimal y como texto procedure WriteHex(Buffer: PAnsiChar; Count: Integer); var i,j: Integer; begin j:= 0; while Count > 0 do begin Write(IntToHex(j,8) + ':' + #32#32); for i:= 0 to Min(Count,8) - 1 do Write(IntToHex(Byte(Buffer[i]),2) + #32); Write(#32); for i:= 8 to Min(Count,16) - 1 do Write(IntToHex(Byte(Buffer[i]),2) + #32); for i:= Min(Count,16) to 15 do Write(#32#32#32); Write(#32 + '|'); for i:= 0 to Min(Count,16) - 1 do if Char(Buffer[i]) in ['A'..'Z','a'..'z','0'..'9'] then Write(Buffer[i]) else Write('.'); Writeln('|'); Dec(Count,16); inc(Buffer,16); inc(j,16); end; end; // Lee el primer sector de la particion procedure ReadBootSector(var Partition: TPartition); var Buffer: PByteArray; begin // Reservamos memoria para guardar el sector de arranque GetMem(Buffer,$0200); try with Partition do begin // Leemos el sector de arranque Stream.ReadBuffer(Buffer^,$0200); // Volcamos en pantalla el contenido del sector de arranque Writeln('=== Boot Sector ==='); WriteHex(PAnsiChar(Buffer), $0200); Writeln; // Comprobamos la firma del sector de arranque if PWORD(@Buffer[$01FE])^ <> $AA55 then raise Exception.Create('Invalid Boot Sector signature = ' + IntToHex(Swap(PWORD(@Buffer[$01FE])^),2)); // Comprobamos que es FAT32 if Trim(Copy(PAnsiChar(@Buffer[$0052]),1,8)) <> 'FAT32' then raise Exception.Create('Invalid System ID = ' + Trim(Copy(AnsiString(PAnsiChar(@Buffer[$0052])),1,8))); // Mostramos la informacion de la particion por pantalla Writeln('Signature: ' + IntToHex(Swap(PWORD(@Buffer[$01FE])^),2)); Writeln('System ID: ' + Trim(Copy(PAnsiChar(@Buffer[$0052]),1,8))); Writeln('OEM ID: ' + Trim(Copy(PAnsiChar(@Buffer[$0003]),1,8))); Writeln('Volume label: ' + Trim(Copy(PAnsiChar(@Buffer[$0047]),1,11))); Writeln('Volume serial: ' + IntToHex(PDWORD(@Buffer[$0043])^,8)); BytsPerSec:= PWORD(@Buffer[$000B])^; Writeln('Bytes per sector: ' + IntToStr(BytsPerSec)); SecPerClus:= PByte(@Buffer[$000D])^; Writeln('Sectors per cluster: ' + IntToStr(SecPerClus)); BytsPerClus:= BytsPerSec * SecPerClus; Writeln('Bytes per cluster: ' + IntToStr(BytsPerClus)); RsvdSecCnt:= PWORD(@Buffer[$000E])^; Writeln('Reserved sectors: ' + IntToStr(RsvdSecCnt)); NumFATs:= PByte(@Buffer[$0010])^; Writeln('Number of FATs: ' + IntToStr(NumFATs)); FATSz32:= PWORD(@Buffer[$0024])^; Writeln('Sectors per FAT: ' + IntToStr(FATSz32)); ClusCount:= ((FATSz32 * BytsPerSec) div SizeOf(DWord)); Writeln('Cluster count: ' + IntToStr(ClusCount)); ClusBegin:= RsvdSecCnt + (NumFATs * FATSz32); RootClus:= PDWORD(@Buffer[$002C])^; Writeln('Root cluster: ' + IntToStr(RootClus)); Writeln; end; finally // Liberamos la memoria FreeMem(Buffer); end; end; // Lee la FAT procedure ReadFAT(Partition: TPartition); var i: DWord; FreeClus: int64; ResClus: int64; BadClus: int64; begin FreeClus:= 0; ResClus:= 0; BadClus:= 0; with Partition do begin // Nos colocamos al principio de la FAT Stream.Position:= (PartBegin + RsvdSecCnt) * BytsPerSec; // La cargamos en memoria Stream.ReadBuffer(FAT^, FATSz32 * BytsPerSec); Writeln('=== Clusters ==='); // La recorremos registro a registro for i:= 0 to ClusCount - 1 do begin // y contamos el estado de cada cluster case FAT[i] of 0: inc(FreeClus); $FFFFFF7: inc(BadClus); else inc(ResClus); end; end; // Mostramos los contadores Writeln('Free: ' + IntToStr(FreeClus)); Writeln('Used: ' + IntToStr(ResClus)); Writeln('Bad: ' + IntToStr(BadClus)); Writeln; end; end; // Lee un cluster procedure ReadClus(Partition: TPartition; Cluster: DWord; Buffer: PByte); begin with Partition do begin Stream.Position:= (PartBegin + ClusBegin + (Cluster - 2) * SecPerClus) * BytsPerSec; Stream.ReadBuffer(Buffer^, BytsPerClus); end; end; // Escribe un cluster procedure WriteClus(Partition: TPartition; Cluster: DWord; Buffer: PByte); begin with Partition do begin Stream.Position:= (PartBegin + ClusBegin + (Cluster - 2) * SecPerClus) * BytsPerSec; Stream.WriteBuffer(Buffer^, BytsPerClus); end; end; // Lee los datos del directorio a partir de su primer cluster procedure ReadData(Partition: TPartition; Cluster: DWord; Stream: TStream); var Buffer: PByte; begin // Reservamos memoria para guardar los datos de un cluster GetMem(Buffer,Partition.SecPerClus * Partition.BytsPerSec); try // Mientras no se llegue al final del fichero while Cluster < Partition.ClusCount do begin // Leemos el cluster ReadClus(Partition, Cluster, Buffer); // Lo grabamos en el stream Stream.WriteBuffer(Buffer^, Partition.SecPerClus * Partition.BytsPerSec); // Buscamos en la FAT el siguiente cluster Cluster:= Partition.FAT[Cluster]; end; finally FreeMem(Buffer); end; end; // Escribe los datos del directorio a partir de su primer cluster procedure WriteData(Partition: TPartition; Cluster: DWord; Stream: TStream); var Buffer: PByte; begin // Reservamos memoria para guardar los datos de un cluster GetMem(Buffer,Partition.SecPerClus * Partition.BytsPerSec); try // Mientras no se llegue al final del fichero while Cluster < Partition.ClusCount do begin // Leemos el cluster del stream Stream.ReadBuffer(Buffer^, Partition.SecPerClus * Partition.BytsPerSec); // Escribimos el cluster en el disco WriteClus(Partition, Cluster, Buffer); // Buscamos en la FAT el siguiente cluster Cluster:= Partition.FAT[Cluster]; end; finally FreeMem(Buffer); end; end; // Vacuna el archivo "AUTORUN.INF" function Vacunar(Partition: TPartition; Cluster: DWord): Boolean; var Tmp: TMemoryStream; Entry: PAnsiChar; Name: String; Attr: Byte; begin Result:= FALSE; // Creamos un stream temporal Tmp:= TMemoryStream.Create; // Reservamos memoria para guardar cada entrada del directorio GetMem(Entry, 32); try // Leemos todas las entradas del directorio ReadData(Partition,Cluster,Tmp); Tmp.Position:= 0; // Procesamos cada una de las entradas del directorio while Tmp.Read(Entry^, 32) = 32 do begin // Si el primer byte es cero, significa que es la ultima if Entry[0] = #0 then break; // Si el primer bytes es alguno de estos, ignoramos la entrada if Entry[0] in [#$00,#$05,#$2E,#$E5] then continue; Attr:= Byte(Entry[$0B]); // Ignoramos los nombres largos if (Attr and $0F) = $0F then continue; // Obtenemos la extension Name:= Trim(Copy(Entry,9,3)); if Name <> EmptyStr then Name:= '.' + Name; // y se la sumamos al nombre Name:= Trim(Copy(Entry,1,8)) + Name; // Si encontramos el archivo "AUTORUN.INF" lo vacunamos if Uppercase(Name) = 'AUTORUN.INF' then begin Result:= TRUE; Writeln('AUTORUN.INF encontrado'); // Si no esta vacunado if Entry[$0B] <> #$42 then begin // Lo vacunamos Writeln('Activando vacuna ...'); Entry[$0B]:= #$42; end else begin // y si lo esta eliminamos la vacuna Writeln('Eliminando vacuna ...'); Entry[$0B]:= #0; end; // Cambiamos la entrada Tmp.Position:= Tmp.Position - 32; Tmp.WriteBuffer(Entry^,32); Tmp.Position:= 0; // Y volvemos a escribir los datos en el disco WriteData(Partition,Cluster,Tmp); Writeln('Proceso terminado'); break; end; end; finally // Liberamos la memoria y el stream FreeMem(Entry); Tmp.Free; end; end; // Esta es la funcion principal procedure main; var C: Char; i, H: Integer; Str: String; Stream: TFileStream; Partition: TPartition; begin Writeln; Writeln('Creado por Domingo Seoane - <a href="http://delphi.jmrds.com'">http://delphi.jmrds.com'</a>); Writeln; Writeln('ANTES DE CONTINUAR, RECUERDA QUE ESTE PROGRAMA ES SOLO UN EJEMPLO:'); Writeln; Writeln('--> NO DEBE UTILIZARSE EN DISCOS QUE CONTENGAN DATOS IMPORTANTES.'); Writeln; Writeln('--> EN NINGUN CASO ME HARE RESPONSABLE DE LOS DA' + #165 + 'OS QUE PUEDA SUFRIR EL DISCO.'); Writeln; Writeln('--> TAMPOCO DE LOS DA' + #165 + 'OS QUE PUEDAN SUFRIR LOS DATOS CONTENIDOS EN EL.'); Writeln; Writeln('--> USA ESTE PROGRAMA CON PRECAUCION Y BAJO TU PROPIA RESPONSABILIDAD.'); Writeln; while TRUE do begin Write('ERES CONSCIENTE DEL RIESGO Y QUIERES CONTINUAR? (S/N)'); Readln(Str); if Uppercase(Str) = 'S' then break; if Uppercase(Str) = 'N' then halt; end; Writeln; try // Creamos la lista de opciones with TStringList.Create do try Add(EmptyStr); Writeln('[' + IntToStr(Count-1) + '] Salir'); // Buscamos todas las unidades for C:= 'A' to 'Z' do begin Str:= '\\.\' + C + ':'; H:= FileOpen(PAnsiChar(Str),fmOpenRead or fmShareDenyNone); // Si se puede abrir lo añadimos a la lista if H > 0 then begin CloseHandle(H); Add(Str); Writeln('[' + IntToStr(Count-1) + '] ' + Str); end; end; Writeln; i:= -1; while i < 0 do begin Write('Escoge: '); Readln(Str); i:= StrToIntDef(Str,-1); end; Writeln; if i > 0 then Stream:= TFileStream.Create(Strings[i],fmOpenReadWrite or fmShareExclusive) else Exit; finally Free; end; if Stream = nil then Halt; try // Borramos todas las variables Fillchar(Partition,Sizeof(Partition),#0); Partition.Stream:= Stream; // Leemos el sector de arranque de la particion ReadBootSector(Partition); // Reservamos espacio en memoria para la FAT with Partition do GetMem(FAT,FATSz32 * BytsPerSec); try // Leemos la FAT y la guardamos en memoria ReadFAT(Partition); // Creamos o eliminamos la vacuna if not Vacunar(Partition, Partition.RootClus) then Writeln('No puedo encontrar el fichero "AUTORUN.INF"'); finally // Liberamos la memoria reservada para la FAT FreeMem(Partition.FAT); end; finally // Liberamos el stream Stream.Free; end; except // Si ocurre un error On E: Exception do begin // Lo mostramos Writeln('Exception: ' + E.Message); Writeln('LastError: ' + SysErrorMessage(GetLastError)); Writeln('ParamStr(1): ' + ParamStr(1)); end; end; Writeln; Writeln('Pulsa [ENTER] para cerrar el programa ...'); Readln; end; end.
En el código se puede ver como el programa accede directamente a los datos de la partición, busca el fichero "autorun.inf" y lo marca con un flag especial que lo vuelve "intocable".
Para saber mas revisa estos enlaces con documentación sobre la FAT32:
http://www.pjrc.com/tech/8051/ide/fat32.html
http://en.wikipedia.org/wiki/File_Allocation_Table
http://en.wikipedia.org/wiki/Master_boot_record
http://technet.microsoft.com/en-us/library/cc977221.aspx
http://delphi.jmrds.com/node/74
Comentarios
Estimado Seoane:
Estimado Seoane:
Existe un truco más simple y yo diría que más "perfeccionado".
El truco no es nada más que crear una simple carpeta llamada autorun.inf en el raiz.
No hace falta ocultarla ni cambiarle los permisos, aunque también se puede hacer.
No se puede crear o sobreescribir un archivo autorun.inf porque la carpeta lo evita, y borrar una carpeta (para un programa/virus) es más "complejo" que un archivo por lo que no lo suelen poder hacer.
A mi me ha funcionado muy bien durante años.
Un saludo.
Borrar una carpeta no es nada
Borrar una carpeta no es nada complicado, es algo trivial, aunque no dudo de que habrá muchos virus que ni se molesten. Todo depende de lo difícil que se lo quieras poner, si a ti te llega con crear una carpeta estupendo.
Saludos