source: repository/lib/Metabrik/Forensic/Scalpel.pm

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