El siguiente programa sirve para recuperar imágenes jpeg borradas. Se basa en la suposición de que la imagen que se quiere recuperar esta guardada en clusteres consecutivos dentro del disco duro. Esta suposición no siempre tiene que ser cierta, pero si lo es en la mayoría de los casos. Así que si hay suerte, podremos recuperar la imagen.
El proceso es sencillo, examinamos cluster a cluster, buscando uno cuyos tres primeros bytes coincidan con los tres primeros bytes de un archivo jpeg. Estos bytes, para cualquier archivo jpeg, son FFD8FF. Una vez que encontramos esa coincidencia intentamos reconstruir la imagen a partir de ese cluster y los siguientes. Una vez finalizada la reconstrucción, tanto si tuvo éxito como si no, regresamos hasta el cluster siguiente y continuamos la búsqueda.
El programa necesita que se le pasen al menos dos parámetros, el archivo de entrada y la plantilla que sirve para dar nombre a las imágenes recuperadas. También se puede incluir un tercer parámetro que indica el tamaño del cluster a utilizar.
El archivo de entrada puede ser la imagen de un disco (una imagen .iso, o una imagen de un disco duro), o puede ser directamente el propio disco. Para este ultimo caso utilizaremos el siguiente parámetro \\.\F: donde F es la letra que identifica el disco.
La plantilla tiene que incluir "%d", esta variable sera sustituida por un valor que se va incrementando. Por ejemplo, si queremos guardar las imágenes en C:\Temp utilizaremos C:\Temp\%d.jpg
El ultimo parámetro sirve para determinar el tamaño del cluster. El tamaño que se utilizara es igual al valor del parámetro multiplicado por 512. El valor mínimo es 1, en algunas memorias usb deberías de usar 2, en los discos duros un valor de 8 podría ser el adecuado. Cuanto mayor es el tamaño mas rápida es la búsqueda, pero si indicamos un tamaño demasiado grande el programa no funciona. Así que lo mejor es averiguar el tamaño del cluster del disco duro a examinar, o utilizar 1 que siempre funciona. Si no se usa este parámetro el valor por defecto es 1.
El código es el siguiente (es una aplicación de consola):
program recover; {$APPTYPE CONSOLE} uses Windows, SysUtils, Classes; procedure RecuperarJpeg(Origen: TStream; FormatStr: String; var Index: Integer); var Destino: TMemoryStream; Buffer: array[0..$FFFF] of Byte; Header: array[1..4] of Byte; Size: Integer; begin Destino:= TMemoryStream.Create; try // En este bucle vamos copiando segmento a segmento while TRUE do begin Origen.ReadBuffer(Header,4); Size:= ((Header[3] shl 8) + Header[4]) - 2; case Header[2] of $01,$D0..$D8: begin // Estos segmentos solo miden dos bytes, // asi que escribimos 2 bytes y Destino.WriteBuffer(Header,2); // retrocedemos otros dos hacia atras. Origen.Seek(-2,soFromCurrent); end; $D9: begin // Este indica que llegamos al final del archivo jpeg Destino.WriteBuffer(Header,2); // Guardamos el archivo usando la plantilla Destino.SaveToFile(Format(FormatStr,[Index])); Writeln('Recuperada: ' + Format(FormatStr,[Index])); // Incrementamos el indice inc(Index); // Salimos del procedure Exit; end; $DA: begin // Este segmento no tiene tamaño Destino.WriteBuffer(Header,2); Origen.Seek(-2,soFromCurrent); // Buscamos el final byte a byte while TRUE do begin Origen.ReadBuffer(Buffer,1); // Posible comienzo de otro segmento if Buffer[0] = $FF then begin Origen.ReadBuffer(Buffer,1); // $FF00 = Secuencia de escape // $FFD0 .. $FFD7 = Se deben de ignorar if (Buffer[0] <> 0) and not (Buffer[0] in [$D0..$D7]) then begin // Si encontramos el comienzo de otro segmento, // retrocedemos dos bytes, y volvemos al bucle principal Origen.Seek(-2,soFromCurrent); break; end else begin // Si no es el comienzo de otro segmento, escribimos // y continuamos Header[1]:= $FF; Header[2]:= Buffer[0]; Destino.WriteBuffer(Header,2); end; end else Destino.WriteBuffer(Buffer,1); end; end else begin // Cualquier otro segmento lo copiamos directamente Origen.ReadBuffer(Buffer,Size); Destino.WriteBuffer(Header,4); Destino.WriteBuffer(Buffer,Size); end; end; end; finally Destino.Free; end; end; procedure Scan(Stream: TStream; ClusterSize: Integer; FormatStr: String); var Buffer: PChar; Indice: Integer; Leidos: Integer; MemStream: TMemoryStream; Posicion: int64; begin Indice:= 0; // Reservamos memoria GetMem(Buffer,ClusterSize); try // Leemos un cluster completo Leidos:= Stream.Read(Buffer^,ClusterSize); while Leidos > 0 do begin // Comprobamos si los primeros bytes del cluter coinciden con // el inicio de un archivo jpeg. if CompareMem(Buffer,PChar(#$FF#$D8#$FF),3) then begin // Guardamos la posicion Posicion:= Stream.Position; try // Cargamos en un stream este cluster y los siguientes 8 Megas MemStream:= TMemoryStream.Create; try MemStream.Write(Buffer^,Leidos); MemStream.CopyFrom(Stream,8*1024*1024); MemStream.Position:= 0; Writeln('Encontrada una posible imagen.'); // Intentamos recuperar la imagen RecuperarJpeg(MemStream,FormatStr,Indice); finally MemStream.Free; end; except // Si se produce un error lo mostramos, pero no salimos del bucle On E: Exception do Writeln('Error: ' + E.Message); end; // Nos movemos hasta el siguiente cluster Stream.Position:= Posicion + ClusterSize; end; Leidos:= Stream.Read(Buffer^,ClusterSize); end; finally FreeMem(Buffer); end; end; var TickCount: Cardinal; Stream: TFileStream; begin try // Necesitamos al menos 2 parametros if ParamCount > 1 then begin // Comprobamos que se pueden crear archivos en la ruta indicada TFileStream.Create(Format(ParamStr(2),[0]),fmCreate).Free; // Borramos el archivo de prueba DeleteFile(Format(ParamStr(2),[0])); // Abrimos el archivo de origen Stream:= TFileStream.Create(ParamStr(1),fmOpenRead or fmShareDenyNone); try // Si llegamos hasta aqui podemos leer y escribir TickCount:= GetTickCount; // Iniciamos la busqueda Writeln('Comenzando la busqueda ...'); Scan(Stream,StrToIntDef(ParamStr(3),1) * 512,ParamStr(2)); // Mostramos el tiempo que hemos tardado Writeln(Format('Tiempo transcurrido: %d ms',[GetTickCount - TickCount])); finally Stream.Free; end; end else // Si no nos pasan dos parametros, imprimimos una pequeña ayuda Writeln('Help: ' + ExtractFilename(ParamStr(0)) + ' <File> <Format> [ClusterSize]'); except On E: Exception do // Si algo sale mal, escribimos la descripcion del error Writeln('Error: ' + E.Message); end; end.
Por ejemplo, para buscar imágenes en el disco F, guardarlas en C:\Temp y usar un tamaño de cluster de 1024 bytes, usaríamos el siguiente comando:
recover \\.\F: C:\Temp\%d.jpg 2
El código completo y programa, los puedes bajar de aquí
Comentarios
Gracias por este estupendo
Gracias por este estupendo programa, me ha servido y mucho....
Saludos
Muy bueno !!!
Muy bueno !!!