Bonjour,
il y a de ça quelques temps j'ai connecté mon disque de sauvegarde (des centaines de giga d'images) à un PC... Et tout à coup le disque s'est mit à gratter curieusement, sans répit. Flairant le truc foireux, j'ai éteint tout. Trop tard. Le système avait été infecté par le virud Drefir K. Ce virus n'écrase pas les fichiers... il met la taille de TOUS les fichiers présents sur tous les disque à zéro octet. Si bien que les utilitaires de récupération de fichier ne marchent pas : ils voient des fichiers existants, mais d'une taille nulle.
Alors je me suis intéressé àune autre stratégie. L'idée a été de lire le support endommagé (le disque) comme un fichier simple (ce qu'on peut faire sous Unix donc Mac OS X) et le parcourir pour repérer des motifs caractéristiques de fichiers JPEG. En pariant sur le fait que la plupart des écritures se font dans des secteurs contigus, j'ai essayé, et récupéré la quasi totalité de mes images écrasées.
Le procédé a ses limites :
- il ne traite que les images JPEG
- les images écrites sur des secteurs fragmentés ne sont pas récupérées
- le programme détecte parfois à tort des début de séquence JPEG et récupère à tort des fichiers quine sont pas des images. Ce n'est pas bien grave, il suffit de les écraser.
Mon tulitaire est dérivé de JPEGRecover. Il s'appelle Scan4JPEG. C'est un programme PERL basique. Il tourne sur Mac OS X (il doit tourner pareil sur la plupart des systèmes Unix-like munis d'un interpréteur PERL). J'en posterai le source dans mon prochain commentaire.
Pour l'utiliser :
- copier le programme dans un fichier et l'appeler scan4jpeg (on va supposer que ce fichier est dans un répertoire de travail nommé 'recover')
- ouvrir un terminal, taper :
cd recover
chmod a+x scan4jpeg
Après, il faut connecter son disque endommagé à la machine et repérer le pseudo-fichier associé (c'est dans le répertoire /dev, par exemple /dev/rdisk1s1, ça va dépendre de votre config)
on lance l'appli en faisant :
sudo ./scan4jpeg /dev/rdisk1s1
Bonne chance
---------- Nouveau message ajouté à 18h57 ---------- Le message précédent a été envoyé à 18h56 ----------
#!/usr/bin/perl
## JPEG recovery utility
## F. Letellier 2008
## This utility scans a file (typically, an unmounted filesystem)
## and detects JPEG sequences in it. It then parses the sequences so to recover
## JPEG data - and dumps recovered data to new files.
## Initially developped to rfecover from a DREFIR-K virus attack
## (DREFIR sets to zero file lengths of all files in a file system ; because
## files are not deleted, undelete utilities fail. scan4jpeg is an alternate
## solution for recovering lost data...)
## Oh, by the way: and the utility has been successfully used to recover
## thousands of lost JPEG pictures

##
## NOTE! USE THIS PROGRAM AT YOUR OWN RISK; THIS PROGRAM COMES WITH
## NO WARRANTY WHATSOEVER.
##
## This utility is open source software.
## Permission to use, copy, modify, and distribute this software for any
## purpose with or without fee is hereby granted, provided that the above
## copyright notice and this permission notice appear in all copies.
$BEGIN = "\xff\xd8\xff" ;
$END = "\xff\xd9" ;
$STEP = 102400 ;
$TEMPLATE = "recovered%.6d.jpg" ;
$trace = 0 ; # If set, the program becomes verbose about JPEG tags
die "No filename specified" if ($ARGV[0] eq "") ;
$infile = $ARGV[0];
$outfilenb = 0 ;
die "Couldn't open $infile" unless open(INF,"<$infile") ;
binmode(INF);
print "Scanning '$infile' looking for JPEG pictures...\n";
$nbscanned = 0 ;
$lastmsg = 0 ;
$dumping = 0 ;
while ((!eof (INF)) || length ($buffer)) {
if (length ($buffer) < 256) {
die unless defined ($n = read (INF, $buffer, 8192, length ($buffer))) ;
$nbscanned += $n ;
}
#### if (($nbscanned - $lastmsg) > $STEP) {
#### $lastmsg = $nbscanned ;
#### print "Passing byte $nbscanned...\n" ;
#### }
if ($dumping) {
if ($taglen) { # On est en train de dumper un tag
if ($taglen > length ($buffer)) {
print OUTF $buffer ;
$dumplen += length ($buffer) ;
$taglen -= length ($buffer) ;
$buffer = "" ;
} else {
print OUTF substr ($buffer, 0, $taglen) ;
$dumplen += $taglen ;
$buffer = substr ($buffer, $taglen) ;
$taglen = 0 ;
}
} else {
$tag = substr ($buffer, 0, 2) ; # Lecture du tag
if ($tag eq "\xff\xd8") {
print "START " if $trace ;
print OUTF $tag ;
$dumplen += 2 ;
$buffer = substr ($buffer, 2) ;
$taglen = 0 ;
$eencodedlen = 0 ;
} elsif ($tag eq "\xff\xdd") {
if ($eencodedlen) {
print "ENTROPY($eencodedlen) " if $trace ;
$eencodedlen = 0 ;
}
print "RESTART " if $trace ;
print OUTF $tag ;
$dumplen += 2 ;
$buffer = substr ($buffer, 2) ;
$taglen = 0 ;
$eencodedlen = 0 ;
} elsif ($tag eq "\xff\x00") { # Escape sequence entropy encoded data
print OUTF $tag ;
$dumplen += 2 ;
$buffer = substr ($buffer, 2) ;
$taglen = 0 ;
$eencodedlen += 2 ;
} elsif ($tag eq "\xff\xd9") {
if ($eencodedlen) {
print "ENTROPY($eencodedlen) " if $trace ;
$eencodedlen = 0 ;
}
print "END\n\n" if $trace ;
print OUTF $tag ;
$dumplen += 2 ;
$buffer = substr ($buffer, 2) ;
die unless close OUTF ;
print "Closing file '$outfilename' ($dumplen bytes)...\n" ;
$dumping = 0 ;
$taglen = 0 ;
$eencodedlen = 0 ;
} elsif ($tag eq "\xff\xfe") {
if ($eencodedlen) {
print "ENTROPY($eencodedlen) " if $trace ;
$eencodedlen = 0 ;
}
print OUTF $tag ;
$dumplen += 2 ;
$buffer = substr ($buffer, 2) ;
$tag = substr ($buffer, 0, 2) ; # Lecture de la longueur
$taglen = (256 * ord (substr ($buffer, 0, 1))) + ord (substr ($buffer, 1, 1)) ;
print "COMMENT($taglen) " if $trace ;
$eencodedlen = 0 ;
} elsif (substr ($tag, 0, 1) eq "\xff") { # Tag inconnu - on suppose qu'il contient une longueur
if ($eencodedlen) {
print "ENTROPY($eencodedlen) " if $trace ;
$eencodedlen = 0 ;
}
print OUTF $tag ;
$tagcode = substr ($tag, 1, 1) ;
$dumplen += 2 ;
$buffer = substr ($buffer, 2) ;
$tag = substr ($buffer, 0, 2) ; # Lecture de la longueur
$taglen = (256 * ord (substr ($buffer, 0, 1))) + ord (substr ($buffer, 1, 1)) ;
printf "TAG-%2X(%d) ", ord ($tagcode), $taglen if $trace ;
$eencodedlen = 0 ;
} else { # On n'a pas trouve de tag... On est donc dans de l'entropy encoded data
$taglen = 0 ;
if (($pos = index ($buffer, "\xff")) >= 0) {
print OUTF substr ($buffer, 0, $pos) ;
$eencodedlen += $pos ;
$dumplen += $pos ;
$buffer = substr ($buffer, $pos) ;
} else {
print OUTF $buffer ;
$eencodedlen += length ($buffer) ;
$dumplen += length ($buffer) ;
$buffer = "" ;
}
}
}
} elsif (($pos = index ($buffer, $BEGIN)) >= 0) { # On a trouve un debut de fichier - on va le parser
$buffer = substr ($buffer, $pos) ;
while (1) {
$outfilename = sprintf($TEMPLATE, $outfilenb);
$outfilenb++ ;
last unless -f $outfilename ;
}
die unless open (OUTF,">$outfilename") ;
binmode (OUTF) ;
$dumping = 1 ;
print "Found JPEG start marker... Dumping file '$outfilename'...\n" ;
$dumplen = 0 ;
} else { # Ici on va conserver la fin du buffer pour eviter de rater un marker a cheval
$buffer = substr ($buffer, -3) ;
}
}
close INF ;
print "Finished scanning '$infile'.\n";