Код:
     #!/usr/bin/perl
#
#  Runs a number of concurrent nmap processes and stores the results in
#  a specified directory in xml, human and machine formats. Optionally runs
#  amap using the nmap output as input.  Writes the results to an HTML file 
#  in the results directory.
#  Make sure you have the latest nmap and amap (www.thc.org) and that they play
#  nicely together.  Tested with nmap 3.27 and amap 4.2. 
#
#  NOTE: Change the values below to match your environment and requirements!
#
#  by 
#    Stephen de Vries - stephen at twisteddelight dot org
#    www.twisteddelight.org/security
#
#############################################################################
use strict;

my $NMAP_COMMAND="/usr/local/bin/nmap";
my $NMAP_OPTIONS="--max_rtt_timeout 2000"; # -oX -oN -oM are set automatically

my $AMAP_COMMAND="/usr/local/bin/amap";
my $AMAP_OPTIONS="-b -1"; # -o -m are set automatically

my $BAN_WIDTH=50;

#############################################################################

my @hosts, my $wdir, my $hostFile, my $procs, my $run_amap;
my @pids; # Store the currently running pids
my %activehosts; # hosts currently being scanned, indexed according to pid
my %banners; # store banner info from amap
my @HTML; # temporary storage for the output


use POSIX qw(:signal_h :errno_h :sys_wait_h);
use Getopt::Std;
 
$SIG{CHLD} = \&REAPER;
sub REAPER {
    my $pid;
    $pid = waitpid(-1, &WNOHANG);
    
    if ($pid == -1) {
        # no child waiting.  Ignore it.
    } elsif (WIFEXITED($?)) {
	my $found=0;
	my $i=0;
	# Check to see if the pid that exited belongs to one of the nmaps
	while ((!$found) && ($i <= $#pids)) {
    if ($pid == $pids[$i]) {
    	splice(@pids,$i,1);
    	$found=1;
    	wlog(0,"Nmap completed on: $activehosts{$pid}");
    	if ($run_amap) {
        launch_amap($activehosts{$pid});
    	}
    	delete $activehosts{$pid};
    }
    $i++;
	}	
    } else {
        print "False alarm on $pid.\n";
    }
    $SIG{CHLD} = \&REAPER;          # in case of unreliable signals
}


sub getDate {
	my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
	return "$year-$mon-$mday $hour:$min:$sec";
}

sub wlog {
	my ($code, @msg) = @_;
	if ($code ==0) {
    # Print to screen and log file
    print "@msg\n";
    my $date = getDate();;
    print LOG "$date|@msg\n";
	} elsif ($code ==1) {
    # Just print to stdout
    print "@msg\n";
	} elsif ($code ==2) {
    # Just print to the log file
    print "@msg\n";
	} else {
    print "Unknown error code: $code\n";
    print "@msg\n";
	}
}

sub launch {
	my ($host) = @_;
	my $OUTPUT="-oX $wdir/xml/$host.nmap.xml -oN $wdir/human/$host.nmap.human -oM $wdir/machine/$host.nmap.machine";
	my $RUNSTR="$NMAP_COMMAND $NMAP_OPTIONS $OUTPUT $host";
	wlog(0, "Launching: $RUNSTR");
	exec($RUNSTR);
}

sub launch_amap {
	my ($host) = @_;
	my $OUTPUT="";
	my $RUNSTR="$AMAP_COMMAND $AMAP_OPTIONS -m -i $wdir/machine/$host.nmap.machine -o $wdir/amap/$host.amap";
	wlog(0, "Launching: $RUNSTR");
	if (!fork()) {
    exec($RUNSTR);
	}
}

sub confirmSettings {
        print "\nLaunching with the following options:\n";
        print "\nReading hosts from:\t\t$hostFile";
        print "\nConcurrent nmap processes:\t$procs";
        print "\nResults directory:\t\t$wdir";
        open (D, $wdir); # Just so we can use the -X operators
        if (-d D) {print " already exists, CLOBBERING existing data!";}
        close D;
        print "\nLaunching nmap:\t\t\t$NMAP_COMMAND $NMAP_OPTIONS";
        if (defined($run_amap)) {
                print "\nLaunching amap:\t\t\t$AMAP_COMMAND $AMAP_OPTIONS";
        }
        print "\n\nConfirm (y,n)?\n";
        my $conf = getc;
        if ($conf ne "y") {
                print "Aborted.\n";
                exit 1;
        }
}

sub init {
	$wdir =~ s/\/$//g;
	my $line;
	wlog (0,"Creating directories in $wdir");
        if (mkdir($wdir,0750) == 1) {
    wlog(2,"Directory $wdir created");
	} else {
    wlog(2,"Error creating directory $wdir: $!");
	}
	if (mkdir("$wdir/xml") == 1) {
    wlog(2,"Created xml directory");
	}
	if (mkdir("$wdir/machine") == 1) {
    wlog(2,"Created machine readable directory");
	}
	if (mkdir("$wdir/human") == 1) {
    wlog(2,"Created human readable directory");
	}
	if (mkdir("$wdir/amap") == 1) {
    wlog(2,"Created amap directory");
	}
	open LOG, ">$wdir/multiscan.log" || die "Fatal Error: could not open log file $wdir/multiscan.log";
	open F, $hostFile or die "Fatal error opening file: $hostFile\n";
	while ($line=<F>) {
    chomp $line;
    wlog(0, "Adding host [$line] to scan list");
    push @hosts, $line;
	}
	close F;
}


sub getbanners {
	my ($host) = @_;
	my $line, my @vals;
	open AMAPFILE, "$wdir/amap/$host.amap" || wlog(0, "Error: Can't open file $wdir/amap/$host.amap for reading");
	while ($line = <AMAPFILE>) {
    if ($line =~ /^$host:([0-9]+)([a-z0-9A-Z\-_]*:){5}(.*)/) {
    	my $port = $1; my $ban = $3;
    	$ban =~ s/:$/No banner found/;
    	$banners{$port} = $ban;
    	print ">> $port = $ban\n";
    }
	}
	close AMAPFILE;
}


sub convertToHTML {
	my $dirname = "$wdir/machine/";
	my $line, my $file;
	my $ip, my $hostname, my $os, my $seq, my @ports, my $port, my $tcpseq, my $portslist;
	opendir(DIR, $dirname) or die "can't open dir $dirname: $!";
	open OUTFILE, ">$wdir/machine/allresults.machine.nmap" || die "Can't open output file: $wdir/machine/allresults.machine.nmap";	
	while (defined($file = readdir(DIR))) {
    if (($file =~ /^\./) || ($file =~ /^allresults/)) {next;}
    open INFILE, "$wdir/machine/$file" || wlog(0,"Error: Can't open results file: $wdir/machine/$file\n");
    my @lines = <INFILE>;
    close INFILE;
    print OUTFILE @lines;
	}
	close OUTFILE;
	closedir(DIR);

	#### Put a header on the html file
	push @HTML, "<HTML><HEAD></HEAD><BODY>\n<H3>Nmap results</H3>\n";

	#### Process the file
	open FILE, "$wdir/machine/allresults.machine.nmap" || die "Error opening $wdir/machine/allresults.nmap";
	my @lines = <FILE>;
	close FILE;
	foreach $line (@lines) {
    if ($line =~ /^Host:\s+([0-9\.]+)\s+\((\S*)\)\s+Ports:\s+(.*)\s+(?=Ignored)Ignored\ State\:(.*)/) {
    	my ($ip, $hostname, $portslist,$rest) = ($1,$2,$3,$4);
    	getbanners($ip);
    	my ($os, $seq) = ("Unknown", "Unknown");
    	push @HTML,"\n<table width=\"80%\" border=1><tr>\n";
    	push @HTML,"   <td width=120><b>IP Address</b></td><td>$ip</td></tr>\n";
    	if (length($hostname > 1)) {
    	      push @HTML,"   <tr><td><b>Hostname</b></b></td><td>$hostname</td></tr>\n";
    	}
    	if ($rest =~ /(.*)(?=OS)OS\:(.*)(?=IPID)IPID\s+Seq\:(.*)/) {
        ($rest,$os, $seq) = ($1,$2,$3);
        push @HTML,"   <tr><td><b>OS Guess</b></td><td>$os</td></tr>\n";
        #push @HTML,"   <tr><td><b>TCP Sequence</b></td><td>$tcpseq</td></tr>\n";
        push @HTML,"   <tr><td><b>IPID Sequence</b></td><td>$seq</td></tr>\n";
    	}
    	push @HTML,"   <tr><td valign=top><b>Ports</b></td><td><table width=\"100%\" border=1>\n";
    	push @HTML,"   <tr><td><b>Num</b></td><td><b>Status</b></td><td><b>Proto</b></td><td><b>Name</b></td>";
    	if ($run_amap) {
        push @HTML, "<td><b>Banner</b></td>";
    	}
    	push @HTML, "</tr>\n";
    	my @ports = split(/\,/, $portslist);
    	foreach $port (@ports) {
        $port =~ s/^\s+//;
        my @list = split(/\//, $port);
    	        push @HTML,"       <tr><td valign=top>$list[0]</td>\n";
    	        push @HTML,"       <td valign=top>$list[1]</td>\n";
    	        push @HTML,"       <td valign=top>$list[2]</td>\n";
    	        push @HTML,"       <td valign=top>$list[4]</td>\n";
        if ($run_amap) {
        	my $banner = $banners{$list[0]};
        	$banner =~ s/\</\&lt;/g;
        	$banner =~ s/\>/\&gt;/g;
        	$banner =~ s/\\n/\\n<br>/g;
    	        	push @HTML,"       <td valign=top>$banner</td>";
        }
        push @HTML, "</tr>\n";
    	}
    	push @HTML,"   </table></td></tr></table>\n<br><br>";
    }
	}
	#### Put a footer on the HTML
	push @HTML, "</BODY></HTML>\n";
	open OUT, ">$wdir/multimap.html";
	print OUT @HTML;
	close OUT;
}

sub usage {
        print STDERR << "EOF";

Usage: $0 -f file -d dir -p processes [-n "options"] [-a] [-h]

        -f file         : text file containing IP addresses to scan
        -d dir          : directory to store output files
        -p processes    : the number of concurrent nmap scans to run

        -n "options"    : additional nmap options
        -a              : run amap
        -h              : this help

Notes:
        Change the values of $NMAP_OPTIONS and $AMAP_OPTIONS in the code to save from
        typing your favourite options on the cmd line.

EOF
}

###############################################################################
# Start main program

my %options;
getopts("f:d:p:n:ah", \%options);
$hostFile = $options{"f"};
$wdir = $options{"d"};
$procs = $options{"p"};
my $temp = $options{"n"};
$run_amap = $options{"a"};
$temp =~ s/\"//g;
$NMAP_OPTIONS = $NMAP_OPTIONS . " " . $temp;

my $error=0;
if (length($hostFile) == 0) {
        print "Error: Must specify a host file with -f file\n";
        $error=1;
}
unless (open(F, $hostFile)) {
        print "Error: can't open file $hostFile for reading\n";
}
close F;
if (length($wdir) == 0) {
        print "Error: Must specify a working directory with -d dir\n";
        $error=1;
}
if (length($procs) == 0){
        print "Error: Must specify the number of processes to run with -p processes\n";
        $error=1;
}
if ($error == 1) {
        usage();
        exit(1);
}

confirmSettings();
init();
my $i=0, my $host, my $currentpid;
while ($#hosts+1 > 0) {
	if ($i < $procs ) {
    $i++;
    $host = pop @hosts;
    $currentpid = fork();
    push @pids, $currentpid;
    if ($currentpid == 0) {
    	launch($host);
    	exit(0);
    } else {
    	$activehosts{$currentpid}=$host;
    }
	}
	my $total=0;
	for (my $n=0;$n <= $#pids;$n++) {
    if ($pids[$n] != 0) {$total++;}
	}
	$i = $total;
	sleep 1;
}
wait;
convertToHTML;
close LOG;