File Coverage

blib/lib/Metabrik/Forensic/Scalpel.pm
Criterion Covered Total %
statement 9 74 12.1
branch 0 38 0.0
condition 0 17 0.0
subroutine 3 6 50.0
pod 1 3 33.3
total 13 138 9.4


line stmt bran cond sub pod time code
1             #
2             # $Id$
3             #
4             # forensic::scalpel Brik
5             #
6             package Metabrik::Forensic::Scalpel;
7 1     1   996 use strict;
  1         3  
  1         29  
8 1     1   5 use warnings;
  1         2  
  1         30  
9              
10 1     1   5 use base qw(Metabrik::Shell::Command Metabrik::System::Package);
  1         21  
  1         1713  
11              
12             # Default attribute values put here will BE inherited by subclasses
13             sub brik_properties {
14             return {
15 0     0 1   revision => '$Revision$',
16             tags => [ qw(unstable carving carve file filecarve filecarving) ],
17             author => 'GomoR ',
18             license => 'http://opensource.org/licenses/BSD-3-Clause',
19             attributes => {
20             datadir => [ qw(datadir) ],
21             extensions => [ qw($extensions_list) ],
22             conf => [ qw(file) ],
23             },
24             attributes_default => {
25             extensions => [ qw(doc pdf jpg png zip odt) ],
26             conf => 'scalpel.conf',
27             },
28             commands => {
29             install => [ ], # Inherited
30             generate_conf => [ qw($extensions_list|OPTIONAL file|OPTIONAL) ],
31             scan => [ qw(file output|OPTIONAL conf|OPTIONAL) ],
32             },
33             require_modules => {
34             'Metabrik::File::Find' => [ ],
35             'Metabrik::File::Text' => [ ],
36             'Metabrik::File::Type' => [ ],
37             'Metabrik::System::File' => [ ],
38             },
39             require_binaries => {
40             'scalpel' => [ ],
41             },
42             need_packages => {
43             ubuntu => [ qw(scalpel) ],
44             debian => [ qw(scalpel) ],
45             kali => [ qw(scalpel) ],
46             },
47             };
48             }
49              
50             sub generate_conf {
51 0     0 0   my $self = shift;
52 0           my ($extensions, $file) = @_;
53              
54 0           my $datadir = $self->datadir;
55 0   0       $extensions ||= $self->extensions;
56 0   0       $file ||= $datadir.'/'.$self->conf;
57 0 0         $self->brik_help_run_undef_arg('generate_conf', $extensions) or return;
58 0 0         $self->brik_help_run_invalid_arg('generate_conf', $extensions, 'ARRAY') or return;
59 0 0         $self->brik_help_run_undef_arg('generate_conf', $file) or return;
60              
61 0 0         my $sf = Metabrik::System::File->new_from_brik_init($self) or return;
62 0 0         $sf->remove($file) or return;
63              
64 0           my $ext = [
65             { case => "y", ext => "art", footer => "\\xcf\\xc7\\xcb", header => "\\x4a\\x47\\x04\\x0e", size => 150000, },
66             { case => "y", ext => "art", footer => "\\xd0\\xcb\\x00\\x00", header => "\\x4a\\x47\\x03\\x0e", size => 150000, },
67             { case => "y", ext => "gif", footer => "\\x00\\x3b", header => "\\x47\\x49\\x46\\x38\\x37\\x61", size => 5000000, },
68             { case => "y", ext => "gif", footer => "\\x00\\x3b", header => "\\x47\\x49\\x46\\x38\\x39\\x61", size => 5000000, },
69             { case => "y", ext => "jpg", footer => "\\xff\\xd9", header => "\\xff\\xd8\\xff\\xe0\\x00\\x10", size => 200000000, },
70             { case => "y", ext => "png", footer => "\\xff\\xfc\\xfd\\xfe", header => "\\x50\\x4e\\x47?", size => 20000000, },
71             { case => "y", ext => "bmp", footer => undef, header => "BM??\\x00\\x00\\x00", size => 100000, },
72             { case => "y", ext => "tif", footer => undef, header => "\\x49\\x49\\x2a\\x00", size => 200000000, },
73             { case => "y", ext => "tif", footer => undef, header => "\\x4D\\x4D\\x00\\x2A", size => 200000000, },
74             { case => "y", ext => "avi", footer => undef, header => "RIFF????AVI", size => 50000000, },
75             { case => "y", ext => "mov", footer => undef, header => "????moov", size => 10000000, },
76             { case => "y", ext => "mov", footer => undef, header => "????mdat", size => 10000000, },
77             { case => "y", ext => "mov", footer => undef, header => "????widev", size => 10000000, },
78             { case => "y", ext => "mov", footer => undef, header => "????skip", size => 10000000, },
79             { case => "y", ext => "mov", footer => undef, header => "????free", size => 10000000, },
80             { case => "y", ext => "mov", footer => undef, header => "????idsc", size => 10000000, },
81             { case => "y", ext => "mov", footer => undef, header => "????pckg", size => 10000000, },
82             { case => "y", ext => "mpg", footer => "\\x00\\x00\\x01\\xb9", header => "\\x00\\x00\\x01\\xba", size => 50000000, },
83             { case => "y", ext => "mpg", footer => "\\x00\\x00\\x01\\xb7", header => "\\x00\\x00\\x01\\xb3", size => 50000000, },
84             { case => "y", ext => "fws", footer => undef, header => "FWS", size => 4000000 },
85             { case => "y", ext => "doc", footer => "\\xd0\\xcf\\x11\\xe0\\xa1\\xb1\\x1a\\xe1\\x00\\x00", header => "\\xd0\\xcf\\x11\\xe0\\xa1\\xb1\\x1a\\xe1\\x00\\x00", size => 10000000, },
86             { case => "y", ext => "doc", footer => undef, header => "\\xd0\\xcf\\x11\\xe0\\xa1\\xb1", size => 10000000, },
87             { case => "y", ext => "pst", footer => undef, header => "\\x21\\x42\\x4e\\xa5\\x6f\\xb5\\xa6", size => 500000000, },
88             { case => "y", ext => "ost", footer => undef, header => "\\x21\\x42\\x44\\x4e", size => 500000000, },
89             { case => "y", ext => "dbx", footer => undef, header => "\\xcf\\xad\\x12\\xfe\\xc5\\xfd\\x74\\x6f", size => 10000000, },
90             { case => "y", ext => "idx", footer => undef, header => "\\x4a\\x4d\\x46\\x39", size => 10000000, },
91             { case => "y", ext => "mbx", footer => undef, header => "\\x4a\\x4d\\x46\\x36", size => 10000000, },
92             { case => "y", ext => "wpc", footer => undef, header => "?WPC", size => 1000000 },
93             { case => "n", ext => "htm", footer => "", header => " 50000, },
94             { case => "y", ext => "pdf", footer => "%EOF\\x0d", header => "%PDF", size => 5000000, },
95             { case => "y", ext => "pdf", footer => "%EOF\\x0a", header => "%PDF", size => 5000000, },
96             { case => "y", ext => "mail", footer => undef, header => "\\x41\\x4f\\x4c\\x56\\x4d", size => 500000, },
97             { case => "y", ext => "pgd", footer => undef, header => "\\x50\\x47\\x50\\x64\\x4d\\x41\\x49\\x4e\\x60\\x01", size => 500000, },
98             { case => "y", ext => "pgp", footer => undef, header => "\\x99\\x00", size => 100000, },
99             { case => "y", ext => "pgp", footer => undef, header => "\\x95\\x01", size => 100000, },
100             { case => "y", ext => "pgp", footer => undef, header => "\\x95\\x00", size => 100000, },
101             { case => "y", ext => "pgp", footer => undef, header => "\\xa6\\x00", size => 100000, },
102             { case => "y", ext => "txt", footer => undef, header => "-----BEGIN\\040PGP", size => 100000, },
103             { case => "y", ext => "rpm", footer => undef, header => "\\xed\\xab", size => 1000000, },
104             { case => "y", ext => "wav", footer => undef, header => "RIFF????WAVE", size => 200000, },
105             { case => "y", ext => "ra", footer => undef, header => "\\x2e\\x72\\x61\\xfd", size => 1000000, },
106             { case => "y", ext => "ra", footer => undef, header => ".RMF", size => 1000000 },
107             { case => "y", ext => "dat", footer => undef, header => "regf", size => 4000000 },
108             { case => "y", ext => "dat", footer => undef, header => "CREG", size => 4000000 },
109             { case => "y", ext => "zip", footer => "\\x3c\\xac", header => "PK\\x03\\x04", size => 10000000, },
110             { case => "y", ext => "java", footer => undef, header => "\\xca\\xfe\\xba\\xbe", size => 1000000, },
111             { case => "y", ext => "max", footer => "\\x00\\x00\\x05\\x80\\x00\\x00", header => "\\x56\\x69\\x47\\x46\\x6b\\x1a\\x00\\x00\\x00\\x00", size => 1000000, },
112             { case => "y", ext => "pins", footer => undef, header => "\\x50\\x49\\x4e\\x53\\x20\\x34\\x2e\\x32\\x30\\x0d", size => 8000, },
113             { ext => "odt", case => "y", size => 20000000, header => "PK????????????????????????????mimetypeapplication/vnd.oasis.opendocument.textPK", footer => "META-INF/manifest.xmlPK????????????????????" },
114             { ext => "ods", case => "y", size => 10000000, header => "PK????????????????????????????mimetypeapplication/vnd.oasis.opendocument.spreadsheetPK", footer => "META-INF/manifest.xmlPK????????????????????" },
115             { ext => "odp", case => "y", size => 10000000, header => "PK????????????????????????????mimetypeapplication/vnd.oasis.opendocument.presentationPK", footer => "META-INF/manifest.xmlPK????????????????????" },
116             { ext => "odg", case => "y", size => 10000000, header => "PK????????????????????????????mimetypeapplication/vnd.oasis.opendocument.graphicsPK", footer => "META-INF/manifest.xmlPK????????????????????" },
117             { ext => "odc", case => "y", size => 10000000, header => "PK????????????????????????????mimetypeapplication/vnd.oasis.opendocument.chartPK", footer => "META-INF/manifest.xmlPK????????????????????" },
118             { ext => "odf", case => "y", size => 10000000, header => "PK????????????????????????????mimetypeapplication/vnd.oasis.opendocument.formulaPK", footer => "META-INF/manifest.xmlPK????????????????????" },
119             { ext => "odi", case => "y", size => 10000000, header => "PK????????????????????????????mimetypeapplication/vnd.oasis.opendocument.imagePK", footer => "META-INF/manifest.xmlPK????????????????????" },
120             { ext => "odm", case => "y", size => 10000000, header => "PK????????????????????????????mimetypeapplication/vnd.oasis.opendocument.text-masterPK", footer => "META-INF/manifest.xmlPK????????????????????" },
121             { ext => "sxw", case => "y", size => 10000000, header => "PK????????????????????????????mimetypeapplication/vnd.sun.xml.writerPK", footer => "META-INF/manifest.xmlPK????????????????????" },
122             ];
123              
124 0           my @lines = ();
125 0           push @lines, '# To redefine the wildcard character, change the setting below and all';
126 0           push @lines, '# occurences in the formost.conf file.';
127 0           push @lines, '#wildcard ?';
128              
129 0           my $wanted = { map { $_ => 1 } @$extensions };
  0            
130 0           for my $this (@$ext) {
131 0           my $ext = $this->{ext};
132 0 0         next unless exists($wanted->{$ext});
133 0           my $case = $this->{case};
134 0           my $size = $this->{size};
135 0           my $header = $this->{header};
136 0   0       my $footer = $this->{footer} || '';
137 0           my $line = " $ext $case $size $header $footer";
138 0           push @lines, $line;
139             }
140              
141 0 0         my $ft = Metabrik::File::Text->new_from_brik_init($self) or return;
142 0 0         $ft->write(\@lines, $file) or return;
143 0           $ft->close;
144              
145 0           return $file;
146             }
147              
148             sub scan {
149 0     0 0   my $self = shift;
150 0           my ($file, $output, $conf) = @_;
151              
152 0 0         $self->brik_help_run_undef_arg("scan", $file) or return;
153              
154 0           my $datadir = $self->datadir;
155 0           my ($base) = $file =~ m{^.*/(.*)$};
156 0   0       $base ||= $file;
157 0   0       $output ||= $datadir.'/'.$base.'.scalp';
158 0   0       $conf ||= $datadir.'/'.$self->conf;
159 0 0         $self->brik_help_run_file_not_found('scan', $file) or return;
160 0 0         $self->brik_help_run_file_not_found('scan', $conf) or return;
161              
162 0 0         if (! -d $output) {
163 0           $self->log->info("scan: never launched scalpel on this file, starting...");
164 0           my $cmd = "scalpel -c $conf -o $output $file";
165 0 0         $self->system($cmd) or return;
166             }
167             else {
168 0           $self->log->info("scan: already launched scalpel, skipping new scan");
169             }
170              
171 0 0         my $ff = Metabrik::File::Find->new_from_brik_init($self) or return;
172 0 0         my $files = $ff->files($output) or return;
173              
174 0           my $ext = {
175             'txt' => 'text/plain',
176             'doc' => 'application/msword',
177             'jpg' => 'image/jpeg',
178             'pdf' => 'application/pdf',
179             'png' => 'image/png',
180             'zip' => 'application/zip',
181             'odt' => 'application/vnd.oasis.opendocument.text',
182             };
183              
184 0 0         my $ft = Metabrik::File::Type->new_from_brik_init($self) or return;
185              
186             # If we know the supposed MIME-type of a file, we correlate with it
187 0           my @verified = ();
188 0           my @unverified = ();
189 0           for my $file (@$files) {
190 0           my ($this) = $file =~ m{\.(\w+)$};
191 0 0         if (exists($ext->{$this})) {
192 0 0         my $check = $ft->get_mime_type($file) or next;
193 0 0         if ($check eq $ext->{$this}) {
194 0           push @verified, $file;
195             }
196             else {
197 0           push @unverified, $file;
198             }
199             }
200             else {
201 0           push @unverified, $file;
202             }
203             }
204              
205             # We remove the audit.txt file which is generated by Scalpel itself
206 0           @verified = grep {!/audit.txt$/} @verified;
  0            
207 0           @unverified = grep {!/audit.txt$/} @unverified;
  0            
208              
209 0           return { verified => \@verified, unverified => \@unverified };
210             }
211              
212             1;
213              
214             __END__