package Malware::Parser::NormanSandBoxLive;

=head1 NAME

Malware::Parser::NormanSandBox - Perl extension for parsing norman sandbox email digests

=head1 SYNOPSIS
  use Data::Dumper;
  use Malware::Parser::NormanSandBox;
  my $f = 'digest.zip';

  my $n = Malware::Parser::NormanSandBoxLive->new( -zip => $f, -cleanup => 1);

  my (@http,@bn);
  foreach my $m (@{$n->malware()}){
  	if(my @a = $m->returnConnectionsByLayer_array(-type => 'url', -layer => 7, -protocol => 'http')){
  		push(@http,@a) if(@a);
  	}
  	if(my @a = $m->returnConnectionsByLayer_array(-type => 'dns', -layer => 7, -protocol => 'irc')){
  		push(@bn,@a) if(@a);
  	}
  }

  warn Dumper(@http);
  warn Dumper(@bn);

=head1 DESCRIPTION

This module is a parser for NormanSandBox email digests (does the web too...but email is better if you can get it). From here you can take the malware behavior and use it to create detection rules etc...

=cut

use 5.008007;
use strict;
use warnings;
use Malware;
use Archive::Extract;
use Net::Protocol::Simple;
use Net::Connection::Simple;
use Class::ParmList qw(parse_parms);

use constant AUTO => 1; # auto extract and parse?
use constant CLEAN => 1; # auto unlink() files?

use Data::Dumper;

our $VERSION = '1.00';

=head1 OBJECT METHODS

=head2 new()

  my $n = Malware::Parser::NormanSandBox->new(
  	-auto => 1, # default, do we want to auto extract the zip and parse? (yes, rgc, i am that lazy ;-)
  	-zip => $PATH_TO_ZIPFILE,
  	-path => $PATH_TO_SANDBOX_FILE # use this if you dont have the zip, filename must start with 'sandbox_'
  	-cleanup => 1, # clean up the zip file and its extractions after?
  }

=cut

sub new {
	my ($class,%parms) = @_;
	my $self = {};
	bless($self,$class);
	$self->init(%parms);
	return $self;
}

sub init {
	my ($self,%parms) = @_;
	$parms{-auto} = AUTO() if(!$parms{-auto});
	$self->zip(	$parms{-zip});
	$self->path(	$parms{-path});
	$self->cleanup(	$parms{-cleanup});
	$self->auto(	$parms{-auto});

	if($self->auto()){
		$self->extractFromZip() if($self->zip());
		if($self->path()){
			return $self->parse_txtFile();
		}
		else { die 'No Text file found...'."\n";}
	}
}

=head2 extractFromZip()

Uses Archive::Extract to open up the zip file (used internally if you -auto on new()) and sets the path of the sandbox file.

  $n->zip($zipFilePath);
  $n->extractFromZip();

Returns 1 on sucess, dies() on error

=cut

sub extractFromZip {
	my $self = shift;
	my $fz = Archive::Extract->new( archive => $self->zip() );
	my $ok = $fz->extract() || die $fz->error();
	foreach my $x (@{$fz->files()}){
		if((uc($x)) =~ /SANDBOX_/){
			$self->path($x);
			next;
		}
		unlink($x) if(!defined($self->cleanup()) || $self->cleanup() == 1);
	}
	unlink($self->zip()) if(!defined($self->cleanup()) || $self->cleanup() == 1);
	return 1;
}

=head2 parse_txtFile()

Parses the main text file and pushes each malware object it creates into $self->malware()

Returns 1 on success, dies() if the file can't be opened

=cut

sub parse_txtFile {
	my $self = shift;
	my ($ct,$s) = (0,0); # count,start
	my $hr = {};
	my (@a,$m,$tmpSec); # return array, tmp_maleware_placeholder, tmp_section

	open(F, $self->path()) || die 'can not open file: '.$self->path();
	while(<F>){
		chomp();
		next unless length();
		next if(/    \* \*\*IMPORTANT: PLEASE SEND THE SCANNED FILE TO/);
		if($s == 0){
			if(/^Detection:\s.*$/){ $s=1; }
			else { next; }
		}
		if(/^Detection:\s(.*)$/){
			if($ct > 0){
				$m->rawReport($hr->{$ct});
				$self->parser($m);
				$self->malware($m);
			}
			$ct++;
			my $platform = undef;
			for(uc($1)){
				if(/^W32/){
					$platform = 'Win32';
					last;
				}
				$self->unknown($1);
			}
			$m = Malware->new(
				-source		=> 'NormanSandBox',
				-sourceLoc	=> 'NormanEmailDigest',
				-classification	=> $platform,
				-platform	=> $platform,
			);
			next;
		}
		if(/\[\s(.*)\s\]/){
			$tmpSec = $1;
			next;
		}
		if(/\*\s(.*)\./){
			push(@{$hr->{$ct}->{$tmpSec}},$1);
			next;
		}
	}
	close(F);
	unlink($self->path()) if(!defined($self->cleanup()) || $self->cleanup() == 1);
	return 1;
}

# ACCESSORS / MODIFIERS

=head1 ACCESSORS / MODIFIERS

=head2 zip()

Sets and Returns the zipFile path

=cut

sub zip {
	my ($self,$v) = @_;
	$self->{_zip} = $v if(defined($v));
	return $self->{_zip};
}

=head2 path()

Sets and Returns the sandbox_ file path

=cut

sub path {
	my ($self,$v) = @_;
	$self->{_path} = $v if(defined($v));
	return $self->{_path};
}

=head2 auto()

Sets and Returns auto property (auto parse)

=cut

sub auto {
	my ($self,$v) = @_;
	$self->{_auto} = $v if(defined($v));
	return $self->{_auto};
}

=head2 cleanup()

Sets and Returns the cleanup property (do we unlink() files on parse?)

=cut

sub cleanup {
	my ($self,$v) = @_;
	$self->{_cleanup} = $v if(defined($v));
	return $self->{_cleanup};
}

=head2 malware()

Stores an arrayref of malware objects.

Accepts Malware Objects or return ($errstr,undef) 

Returns arrayref of malware objects

=cut

sub malware {
	my ($self,$v) = @_;
	if(defined($v)){
		return ('expecting malware object', undef) if(!(ref($v) =~ /^Malware/));
		push(@{$self->{_malware}},$v);
	}
	return $self->{_malware};
}

=head2 unknown()

Stores an array of strings that hold unknown classifications

Accepts strings

Returns arrayref of strings

=cut

sub unknown {
	my ($self,$v) = @_;
	push(@{$self->{_unknown}},$v) if(defined($v));
	return $self->{_unknown};
}

# Internal functions

sub parser {
	my ($self,$m) = @_;
	my $hr = $m->rawReport();
	foreach my $x (keys %$hr) {
		for($x){
			if(/^classification$/){
				$m->classification($hr->{$x});
				next;
			}
			if(/^Network services$/){
				foreach my $y (@{$hr->{$x}}) {
					for($y){
						if(/^Connects to "(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b)" on port (\d+) \((\S+)\)/){
							my $c = Net::Connection::Simple->new();

							my $p = Net::Protocol::Simple->new(protocol => 'ip', layer => 3);
							$p->{dip} = $1;
							$c->protocols($p);

							$p = Net::Protocol::Simple->new(protocol => $3, layer => 4);
							$p->{dport} = $2;
							$c->protocols($p);

							$p = Net::Protocol::Simple->new(protocol => 'irc', layer => 7);
							$c->protocols($p);

							$m->connections($c);
							last;
						}
						if(/^Connects to "(\S+\.\S+\.\S+)" on port (\d+) \((\S+)\)/){
							my $c = Net::Connection::Simple->new();
							my $p;

							$p = Net::Protocol::Simple->new(protocol => 'ip', layer => 3);
							$c->protocols($p);

							$p = Net::Protocol::Simple->new(protocol => $3, layer => 4);
							$p->{_dport} = $2;
							$c->protocols($p);

							$p = Net::Protocol::Simple->new(protocol => 'irc', layer => 7);
							$p->{_dns} = $1;
							$c->protocols($p);

							$m->connections($c);
							last;
						}
						if(/^IRC: Joins channel (\S+) with password (\S+)/){
							last;
						}
						if(/^Downloads file from (.*) as.*$/){
							my $c = Net::Connection::Simple->new();
							my $p;

							$p = Net::Protocol::Simple->new(protocol => 'ip', layer => 3);
							$c->protocols($p);

							$p = Net::Protocol::Simple->new(protocol => 'tcp', layer => 4);
							$p->{_dport} = 80;
							$c->protocols($p);

							$p = Net::Protocol::Simple->new(protocol => 'HTTP', layer => 7);
							$p->{_url} = $1;
							$c->protocols($p);

							$m->connections($c);
							last;
						}
					}
				}
				last;
			}
			if(/^General information$/){
				foreach my $y (@{$hr->{$x}}) {
					if($y =~ /^MD5 hash:\s(.*)$/){
						$m->md5sum($1);
						next;
					}
					if($y =~ /^Anti debug\/emulation code present$/){
						$m->antiEmulation(1);
						next;
					}
					if($y =~ /^File length:\s+(\d+)\s(\S+)$/){
						$m->filesize($1);
						$m->filesizeUnits($2);
					}
				}
				last;
			}
			if(/^Changes to registry$/){
				foreach my $y (@{$hr->{$x}}) {
					$m->registry($y);
				}
				last;
			}
			if(/^Changes to filesystem$/){
				foreach my $y (@{$hr->{$x}}) {
					$m->filesystem($y);
				}
				last;
			}
			if(/^Security issues$/){
				foreach my $y (@{$hr->{$x}}) {
					if($y =~ /^Possible backdoor functionality \S+ port (\d+)$/){
						my $p = Net::Protocol::Simple->new(protocol => 'tcp', layer => 4);
						$p->{_sport} = $1;
						$m->backdoors($p);
						next;
					}
					$m->securityIssues($y);
				}
				last;
			}
			if(/^Process\/window information$/){
				foreach my $y (@{$hr->{$x}}) {
					$m->processInfo($y);
				}
				last;
			}
			if(/^Signature Scanning$/){
				my $hr2;
				foreach my $y (@{$hr->{$x}}) {
					if($y =~ /^(.*)\s\(.*\)\s:\s(.*)$/){
						$hr2->{$1} = $2;
					}
				}
				$m->malwareFiles($hr2);
				last;
			}
		}
	}
}
1;
__END__

=head1 SEE ALSO

Malware,Net::Connection::Simple,Net::Connection::Simple,Time::Timestamp

=head1 AUTHOR

Wes Young, E<lt>saxguard9-cpan@yahoo.comE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2006 by Wes Young

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.7 or,
at your option, any later version of Perl 5 you may have available.


=cut