Jump to content

Recommended Posts

  • Active Members

Author(s)

Joe Stewart

Latest Version

0.1

Description

Foregone is a forensic file recovery tool written in Perl. It was inspired by the Air Force Office of Special Investigations' forensic tool known as "Foremost", which uses defined headers and footers of certain file types to search a raw disk image and extract files with those characteristics. Foregone is a Perl implementation of the same technique with some added features:

  • Only searches for headers starting on a block boundary, for a speed increase
  • Uses compression to reject interleaved blocks of dissimilar files

Foregone should not be considered an investigative-level forensic tool, but merely a utility that may help you recover files from a corrupted filesystem.

#!/usr/bin/perl

use Compress::Zlib;

## foregone.pl - recover files of a given type from a raw disk image even if
## interleaved with other blocks (blocks must be in correct order)
##
## (c)2003-2006 Joe Stewart <jstewart@lurhq.com>
##
## Foregone is released under the GNU General Public License.

## examples ##

## PDF ##
## my $header = "\x25\x50\x44\x46\x2D";
## my $footer = "\x25\x25\x45\x4F\x46";
## my $extension = "pdf";
## my $mustfind = "";
## my $mustfind2 = "";

## GNUCASH ##
## my $header = "<?xml version=\"1.0\"?>\n<gnc-";
## my $footer = "<!-- End: -->";
## my $extension = "xml";
## my $mustfind = "<act";
## my $mustfind2 = "<trn";

## JPEG ##
my $header = "\xff\xd8\xff\xe0";
my $footer = "\xff\xd9";
my $extension = "jpg";
my $mustfind = "";
my $mustfind2 = "";

## Blocksize of filesystem being read ##
my $blocksize = 1024;

## Threshold to reject blocks based on compressed size
## compares initial block size when compressed with subsequent block
## sizes when compressed ... good at rejecting dissimliar types of data
## More than this zthreshold percent deviation will result in rejection
## Adjust as needed depending on how many bad blocks you get in output
my $zthreshold = 50;

## maximum size of any output file ##
my $maxbytes = 3000000;

## directory to write recovered files. Will be created if it doesn't exist ##
my $recoverdir = "foregone-recover";

my $image = $ARGV[0];
my $size = (stat($image))[7];
my $buf = "";
my $buflength = length($header);
my $found = 0;
my $outbytes = 0;
my $pos = 0;

die "Usage: $0 <disk image>\n" unless $image;

open (IN, $image) or die "Could not open $image : $!\n";

unless (-e "$recoverdir") {
mkdir("$recoverdir", 0755) or die "Could not create $recoverdir : $1\n";
}

while ($pos < $size) {
my $blockpos = $pos;
read(IN,$buf,$buflength) or last;
$percent = ($pos / $size) * 100;
printf("Looking at first %s bytes of block at offset $pos (%.2f%% done)\r", $buflength, $percent);
if ($buf eq $header) {
print "\nHeader match at $pos\n";
print "Extracting file...\n";
&extract_file;
}
$pos += $blocksize;
seek(IN,$pos, 0) or die "\ncould not seek to $pos";
}

print "\nFinished.\n";
close IN;

sub extract_file {
$found++;
my $filename = sprintf("recovered.%05d.%s", $found, $extension);
open(OUT, ">$recoverdir/$filename") or die "Couldn't write output file!\n";
print OUT $buf;
my $tmpblocksize = $blocksize - $buflength;
while ($pos < $size) {
read(IN,$block,$tmpblocksize) or last;
$tmpblocksize = $blocksize;

my $zblock = compress($block);
my $zblocklength = length($zblock);
if (!$zfirstblock) {
$zfirstblock = $zblocklength;
$lowerlimit = ((100 - $zthreshold) / 100) * $zfirstblock;
$upperlimit = ((100 + $zthreshold) / 100) * $zfirstblock;
}
else {
if ($block !~ /$footer/) {
# compressed block size is too different. reject it.
if ($zblocklength > $upperlimit || $zblocklength < $lowerlimit)
{
print "Rejected block: compressed size = $zblocklength : $lowerlimit < $zfirstblock > $upperlimit\n";
next;
}
}
}

if ($mustfind) {
next unless $block =~ /$mustfind|$mustfind2/;
}
if ($block =~ /$footer/ || $outbytes + $blocksize > $maxbytes ) {
my ($blockt, $garbage) = split(/$footer/, $block);
print OUT $blockt . $footer;
$outbytes += length($blockt) + length($footer);
if (length($blockt) + length($footer) == $blocksize) { print "Failed to truncate last block\n" }
last;
}
$outbytes += $blocksize;
print OUT $block;
}
print "Wrote $recoverdir/$filename ($outbytes bytes)\n";
$outbytes = 0;
close OUT;
}

Source

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...