File Coverage

blib/lib/Metabrik/Forensic/Volatility.pm
Criterion Covered Total %
statement 9 197 4.5
branch 0 84 0.0
condition 0 75 0.0
subroutine 3 17 17.6
pod 1 14 7.1
total 13 387 3.3


line stmt bran cond sub pod time code
1             #
2             # $Id$
3             #
4             # forensic::Volatility Brik
5             #
6             package Metabrik::Forensic::Volatility;
7 1     1   779 use strict;
  1         2  
  1         31  
8 1     1   5 use warnings;
  1         2  
  1         29  
9              
10 1     1   5 use base qw(Metabrik::Shell::Command Metabrik::System::Package);
  1         1  
  1         2444  
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             profile => [ qw(profile) ],
22             input => [ qw(file) ],
23             capture_mode => [ qw(0|1) ],
24             },
25             attributes_default => {
26             profile => 'Win7SP1x64',
27             capture_mode => 1,
28             },
29             commands => {
30             install => [ ], # Inherited
31             imageinfo => [ qw(file|OPTIONAL) ],
32             command => [ qw(command file|OPTIONAL profile|OPTIONAL) ],
33             envars => [ qw(file|OPTIONAL profile|OPTIONAL) ],
34             pstree => [ qw(file|OPTIONAL profile|OPTIONAL) ],
35             pslist => [ qw(file|OPTIONAL profile|OPTIONAL) ],
36             netscan => [ qw(file|OPTIONAL profile|OPTIONAL) ],
37             hashdump => [ qw(file|OPTIONAL profile|OPTIONAL) ],
38             psxview => [ qw(file|OPTIONAL profile|OPTIONAL) ],
39             hivelist => [ qw(file|OPTIONAL profile|OPTIONAL) ],
40             hivedump => [ qw(offset file|OPTIONAL profile|OPTIONAL) ],
41             filescan => [ qw(file|OPTIONAL profile|OPTIONAL) ],
42             consoles => [ qw(file|OPTIONAL profile|OPTIONAL) ],
43             memdump => [ qw(pid file|OPTIONAL profile|OPTIONAL) ],
44             },
45             require_modules => {
46             'Metabrik::System::File' => [ ],
47             },
48             require_binaries => {
49             'volatility' => [ ],
50             },
51             need_packages => {
52             ubuntu => [ qw(volatility) ],
53             debian => [ qw(volatility) ],
54             kali => [ qw(volatility) ],
55             },
56             };
57             }
58              
59             sub imageinfo {
60 0     0 0   my $self = shift;
61 0           my ($file) = @_;
62              
63 0   0       $file ||= $self->input;
64 0           my $datadir = $self->datadir;
65 0 0         $self->brik_help_run_undef_arg('imageinfo', $file) or return;
66 0 0         $self->brik_help_run_file_not_found('imageinfo', $file) or return;
67              
68 0           my $cmd = "volatility imageinfo -f \"$file\"";
69              
70 0           $self->log->info("imageinfo: running...");
71 0           my $data = $self->capture($cmd);
72 0           $self->log->info("imageinfo: running...done");
73              
74 0           my @profiles = ();
75 0           for my $line (@$data) {
76 0 0         if ($line =~ m{suggested profile}i) {
77 0           my @toks = split(/\s+/, $line);
78 0           @profiles = @toks[4..$#toks];
79 0           for (@profiles) {
80 0           s/,$//g;
81             }
82             }
83             }
84              
85 0           return \@profiles;
86             }
87              
88             sub command {
89 0     0 0   my $self = shift;
90 0           my ($command, $file, $profile) = @_;
91              
92 0   0       $file ||= $self->input;
93 0   0       $profile ||= $self->profile;
94 0 0         $self->brik_help_run_undef_arg('command', $command) or return;
95 0 0         $self->brik_help_run_undef_arg('command', $file) or return;
96 0 0         $self->brik_help_run_undef_arg('command', $profile) or return;
97              
98 0           my $cmd = "volatility --profile $profile $command -f \"$file\"";
99              
100 0           return $self->execute($cmd);
101             }
102              
103             sub envars {
104 0     0 0   my $self = shift;
105 0           my ($file, $profile) = @_;
106              
107 0   0       $file ||= $self->input;
108 0   0       $profile ||= $self->profile;
109 0 0         $self->brik_help_run_undef_arg('envars', $file) or return;
110 0 0         $self->brik_help_run_undef_arg('envars', $profile) or return;
111              
112 0           my $cmd = "volatility --profile $profile envars -f $file";
113              
114 0           return $self->execute($cmd);
115             }
116              
117             sub pstree {
118 0     0 0   my $self = shift;
119 0           my ($file, $profile) = @_;
120              
121 0   0       $file ||= $self->input;
122 0   0       $profile ||= $self->profile;
123 0 0         $self->brik_help_run_undef_arg('pstree', $file) or return;
124 0 0         $self->brik_help_run_undef_arg('pstree', $profile) or return;
125              
126 0           my $cmd = "volatility --profile $profile pstree -v -f $file";
127              
128 0           return $self->execute($cmd);
129             }
130              
131             sub pslist {
132 0     0 0   my $self = shift;
133 0           my ($file, $profile) = @_;
134              
135 0   0       $file ||= $self->input;
136 0   0       $profile ||= $self->profile;
137 0 0         $self->brik_help_run_undef_arg('pslist', $file) or return;
138 0 0         $self->brik_help_run_undef_arg('pslist', $profile) or return;
139              
140 0           my $cmd = "volatility --profile $profile pslist -v -f $file";
141              
142 0           $self->capture_stderr(0);
143 0 0         my $lines = $self->execute($cmd) or return;
144 0           $self->capture_stderr(1);
145              
146             # Offset(V)|Name|PID|PPID|Thds|Hnds|Sess|Wow64|Start Exit
147 0           my $skip = 3;
148 0           my @info = ();
149 0           for my $line (@$lines) {
150 0 0         if ($skip != 0) {
151 0           $skip--;
152 0           next;
153             }
154 0           my @t = split(/\s+/, $line, 9);
155 0           my $offset = $t[0];
156 0           my $name = $t[1];
157 0           my $pid = $t[2];
158 0           my $ppid = $t[3];
159 0           my $thds = $t[4];
160 0           my $hhds = $t[5];
161 0           my $sess = $t[6];
162 0           my $wow64 = $t[7];
163 0           my $start_exit = $t[8];
164              
165             # "2016-06-04 16:23:13 UTC+0000"
166             # "2016-06-04 16:26:04 UTC+0000 2016-06-04 16:26:06 UTC+0000"
167 0           $start_exit =~ s{\s*$}{};
168 0           my ($start, $exit) = $start_exit =~ m{^(\S+ \S+ \S+)(?:\s+(\S+ \S+ \S+))?$};
169              
170 0           push @info, {
171             offset => $offset,
172             name => $name,
173             pid => $pid,
174             ppid => $ppid,
175             thds => $thds,
176             hhds => $hhds,
177             sess => $sess,
178             wow64 => $wow64,
179             start => $start,
180             exit => $exit,
181             #start_exit => $start_exit,
182             };
183             }
184              
185 0           return \@info;
186             }
187              
188             sub netscan {
189 0     0 0   my $self = shift;
190 0           my ($file, $profile) = @_;
191              
192 0   0       $file ||= $self->input;
193 0   0       $profile ||= $self->profile;
194 0 0         $self->brik_help_run_undef_arg('netscan', $file) or return;
195 0 0         $self->brik_help_run_undef_arg('netscan', $profile) or return;
196              
197 0           my $cmd = "volatility --profile $profile netscan -v -f $file";
198              
199 0           $self->capture_stderr(0);
200 0 0         my $lines = $self->execute($cmd) or return;
201 0           $self->capture_stderr(1);
202              
203             # "Offset(P) Proto Local Address Foreign Address State Pid Owner Created",
204 0           my $skip = 1;
205 0           my @info = ();
206 0           my @raw = ();
207 0           for my $line (@$lines) {
208 0 0         if ($skip != 0) {
209 0           $skip--;
210 0           next;
211             }
212              
213             # "0x41b9520 TCPv6 -:0 7808:2401:80fa:ffff:7808:2401:80fa:ffff:0 CLOSED 1820 avgmfapx.exe"
214 0 0         if ($line =~ m{\s+TCPv(4|6)\s+}) {
    0          
215 0           my @t = split(/\s+/, $line, 7);
216 0           my $offset = $t[0];
217 0           my $proto = $t[1];
218 0           my $local_address = $t[2];
219 0           my $foreign_address = $t[3];
220 0           my $state = $t[4];
221 0           my $pid = $t[5];
222 0           my $owner = $t[6];
223              
224 0           push @info, {
225             offset => $offset,
226             proto => $proto,
227             local_address => $local_address,
228             foreign_address => $foreign_address,
229             state => $state,
230             pid => $pid,
231             owner => $owner,
232             created => 'undef',
233             };
234             }
235             # "0x171e1360 UDPv4 10.0.3.15:138 *:* 4 System 2016-10-15 14:58:46 UTC+0000",
236             elsif ($line =~ m{\s+UDPv(4|6)\s+}) {
237 0           my @t = split(/\s+/, $line, 7);
238 0           my $offset = $t[0];
239 0           my $proto = $t[1];
240 0           my $local_address = $t[2];
241 0           my $foreign_address = $t[3];
242 0           my $pid = $t[4];
243 0           my $owner = $t[5];
244 0           my $created = $t[6];
245              
246 0           push @info, {
247             offset => $offset,
248             proto => $proto,
249             local_address => $local_address,
250             foreign_address => $foreign_address,
251             state => 'undef',
252             pid => $pid,
253             owner => $owner,
254             created => $created,
255             };
256             }
257             else {
258 0           $self->log->warning("netscan: don't know what to do with line [$line]");
259             }
260             }
261              
262 0           return \@info;
263             }
264              
265             sub memdump {
266 0     0 0   my $self = shift;
267 0           my ($pid, $file, $profile) = @_;
268              
269 0   0       $file ||= $self->input;
270 0   0       $profile ||= $self->profile;
271 0 0         $self->brik_help_run_undef_arg('memdump', $pid) or return;
272 0 0         $self->brik_help_run_undef_arg('memdump', $file) or return;
273 0 0         $self->brik_help_run_undef_arg('memdump', $profile) or return;
274              
275 0 0         my $sf = Metabrik::System::File->new_from_brik_init($self) or return;
276 0 0         $sf->mkdir($pid) or return;
277              
278 0           my $cmd = "volatility --profile $profile memdump -p $pid --dump-dir $pid/ -f $file";
279 0 0         $self->execute($cmd) or return;
280              
281 0           return "$pid/$pid.dmp";
282             }
283              
284             sub hashdump {
285 0     0 0   my $self = shift;
286 0           my ($file, $profile) = @_;
287              
288 0   0       $file ||= $self->input;
289 0   0       $profile ||= $self->profile;
290 0 0         $self->brik_help_run_undef_arg('hashdump', $file) or return;
291 0 0         $self->brik_help_run_undef_arg('hashdump', $profile) or return;
292              
293 0           my $cmd = "volatility --profile $profile hashdump -f $file";
294              
295 0           return $self->execute($cmd);
296             }
297              
298             sub psxview {
299 0     0 0   my $self = shift;
300 0           my ($file, $profile) = @_;
301              
302 0   0       $file ||= $self->input;
303 0   0       $profile ||= $self->profile;
304 0 0         $self->brik_help_run_undef_arg('psxview', $file) or return;
305 0 0         $self->brik_help_run_undef_arg('psxview', $profile) or return;
306              
307 0           my $cmd = "volatility --profile $profile psxview -f $file";
308              
309 0           return $self->execute($cmd);
310             }
311              
312             sub hivelist {
313 0     0 0   my $self = shift;
314 0           my ($file, $profile) = @_;
315              
316 0   0       $file ||= $self->input;
317 0   0       $profile ||= $self->profile;
318 0 0         $self->brik_help_run_undef_arg('hivelist', $file) or return;
319 0 0         $self->brik_help_run_undef_arg('hivelist', $profile) or return;
320              
321 0           my $cmd = "volatility --profile $profile hivelist -f $file";
322              
323 0           $self->capture_stderr(0);
324 0 0         my $lines = $self->execute($cmd) or return;
325 0           $self->capture_stderr(1);
326              
327             # "Virtual Physical Name"
328 0           my $skip = 2;
329 0           my @info = ();
330 0           for my $line (@$lines) {
331 0 0         if ($skip != 0) {
332 0           $skip--;
333 0           next;
334             }
335 0           my @t = split(/\s+/, $line, 3);
336 0           my $virtual = $t[0];
337 0           my $physical = $t[1];
338 0           my $name = $t[2];
339              
340 0           push @info, {
341             virtual => $virtual,
342             physical => $physical,
343             name => $name,
344             };
345             }
346              
347 0           return \@info;
348             }
349              
350             sub hivedump {
351 0     0 0   my $self = shift;
352 0           my ($offset, $file, $profile) = @_;
353              
354 0   0       $file ||= $self->input;
355 0   0       $profile ||= $self->profile;
356 0 0         $self->brik_help_run_undef_arg('hivedump', $offset) or return;
357 0 0         $self->brik_help_run_undef_arg('hivedump', $file) or return;
358 0 0         $self->brik_help_run_undef_arg('hivedump', $profile) or return;
359              
360 0           my $cmd = "volatility --profile $profile hivedump --hive-offset $offset -f $file";
361              
362 0           return $self->execute($cmd);
363             }
364              
365             sub filescan {
366 0     0 0   my $self = shift;
367 0           my ($offset, $file, $profile) = @_;
368              
369 0   0       $file ||= $self->input;
370 0   0       $profile ||= $self->profile;
371 0 0         $self->brik_help_run_undef_arg('filescan', $offset) or return;
372 0 0         $self->brik_help_run_undef_arg('filescan', $file) or return;
373 0 0         $self->brik_help_run_undef_arg('filescan', $profile) or return;
374              
375 0           my $cmd = "volatility --profile $profile filescan -f $file";
376              
377 0           return $self->execute($cmd);
378             }
379              
380             sub consoles {
381 0     0 0   my $self = shift;
382 0           my ($file, $profile) = @_;
383              
384 0   0       $file ||= $self->input;
385 0   0       $profile ||= $self->profile;
386 0 0         $self->brik_help_run_undef_arg('consoles', $file) or return;
387 0 0         $self->brik_help_run_undef_arg('consoles', $profile) or return;
388              
389 0           my $cmd = "volatility --profile $profile consoles -f $file";
390              
391 0           return $self->execute($cmd);
392             }
393              
394             1;
395              
396             __END__