#!/usr/bin/env perl ## jpg-recover-faster -- Copyright (c) 2006-2007, Nathan Arthur (truist-jpgrecover@rainskit.com) ## Program heavily based on jpg-recover, by Adam Glass (clarity.net@adam) ## Original copyright: jpg-recover -- Copyright (c) 2001-2002, Adam Glass ## ## jpg-recover scans through a file (probably a disk image) looking for ## JPEG headers, copying them to sequential filenames. ## ## NOTE! USE THIS PROGRAM AT YOUR OWN RISK; THIS PROGRAM COMES WITH ## NO WARRANTY WHATSOEVER. ## ## 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. ## ## Generally, the following variables are fine -- you may want to tune ## them if you know what you're doing, but you shouldn't have to. ## ## $minsize may be worth fiddling with, since the end marker ('FFD9') ## is not unique -- it can occur multiple times within JPEG data. ## If $minsize is set too low, you'll end up with truncated images (false ## matches on the end marker), but if $minsize is too long, jpg-recover ## will miss the end of the file -- and combine two images or simply have ## corruption at the end of the image. So you may need to try a number ## of different values. (The units are bytes; the default is to not ## believe the data in the file if it's specifying an image shorter ## than 102400 bytes -- 100k.) ## ## ------------------------------------------------------------------------ if ($ARGV[0] ne "") { $infile=$ARGV[0]; # use user-defined file if one is supplied } else { $infile="smcard.img"; # default input file to read (the disk image file) } ### CONFIGURE THESE VARIABLES $outtemp="lostpic{}.jpg"; # output file template: '{}' is replaced with file # $filenum=1; # output file numbering starts here $position=0; # position in input file, for restarting output $minsize=262144; # ignore apparent JPEG file end markers before here $maxsize=10485760; # give up after here $announce=102400; # announce position every 100k (0=disable announcemnts) $maxbufsize=$maxsize * 2; # maximum buffer size (must be greater than $maxsize) # adjust this if you are running out of memory ### END OF CONFIGURABLE SECTION ### $state=0; # current read state: 0=waiting for start marker, # 1=inside file, waiting for end $outlen=0; # output file length (for pretty output) $buffer=""; # scanning buffer (leave this alone) $freshread=""; # where characters get read in (leave this alone) $intro = "\xff\xd8\xff"; $ignorelen = length("\xe1\x1c\x45"); @ending = ( "\x45\x78\x69\x66", "\x4a\x46\x49\x46" ); $markersize=60; $end="\xff\xd9"; # JPEG end marker $starttime = time() - 1; select((select(STDOUT), $|=1)[0]); #auto-flush stdout open(INF,"< $infile") or die "couldn't open $infile"; binmode(INF); print "Scanning '$infile' looking for pictures...\n"; if ($position > 0) { seek(INF, 229309480, 0); } $bufferpos=$maxbufsize; while(!eof(INF)) { $position += read(INF, $freshread, $bufferpos); $buffer = substr($buffer, $bufferpos) . $freshread; $kbs = ($position / 1024) / (time() - $starttime); print "Passing byte $position of $infile ... $kbs kb/s\n"; $bufferpos = findfile(); } print "Finished\n"; close INF; exit 0; sub findfile { local $start = findstart(0, $maxsize); if ($start > -1) { local $endpos = findend($start); if ($endpos > $start) { local $secondstart = findstart($start + 1, $endpos); if ($secondstart > -1) { return $secondstart; } else { writefile($start, $endpos + length($end), false); return $endpos; } } else { return $start + 1; } } else { return $maxsize; } } sub findstart() { local ($start,$end) = @_; while (true) { $start = index($buffer, $intro, $start); if ($start > $end || $start < 0) { return -1; } else { local $subbuf = substr($buffer, $start + length($intro) + $ignorelen, length($ending[0])); if ($subbuf eq $ending[0] || $subbuf eq $ending[1]) { return $start; } else { $start = $start + 1; } } } } sub findend() { local $start = @_; local $subbuf = substr($buffer, $start, $maxsize); return index($subbuf, $end, $minsize) + $start; } sub writefile() { local ($start, $endpos, $addend) = @_; local $length = $endpos - $start; local $foundpos = $position + $start; print "Found file #$filenum at position $foundpos with $length bytes\n"; $outfile = seq_filename($outtemp, $filenum); open(OUTF, ">$outfile"); binmode(OUTF); local $contents = substr($buffer, $start, $length); if ($addend) { $contents = $contents . $end; } print OUTF $contents; close OUTF; $filenum += 1; } sub seq_filename { local ($template,$number) = @_; local $numstr = sprintf("%.4d",$number); $template =~ s/{}/$numstr/; return $template; }