| 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129. 130. 131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143. 144. 145. 146. 147. 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158. 159. 160. 161. 162. 163. 164. 165. 166. 167. 168. 169. 170. 171. 172. 173. 174. 175. 176. 177. 178. 179. 180. 181. 182. 183. 184. 185. 186. 187. 188. 189. 190. 191. 192. 193. 194. 195. 196. 197. 198. 199. 200. 201. 202. 203. 204. 205. 206. 207. 208. 209. 210. 211. 212. 213. 214. 215. 216. 217. 218. 219. 220. 221. 222. 223. 224. 225. 226. 227. 228. 229. 230. 231. 232. 233. 234. 235. 236. 237. 238. 239. 240. 241. 242. 243. 244. 245. 246. | #!/usr/bin/perl
#
# nmap_spawn.pl
#
#####################################################################
# JoMo-Kun Parallel NMAP Scanner
#
#####################################################################
#
#
# This program will read in a list of IPs from a text file (or specified
# range) and then spawn X number of parallel nmap processes to scan
# those addresses.
#
use Getopt::Long;
use NetAddr::IP;
use POSIX ":sys_wait_h";
my $VERSION = "0.9.8.2";
my $TSTART, $TSTOP;
print "\nJoMo-Kun Parallel NMAP Scanner\n\n";
GetOptions (
'threads=i' => \$PARALLEL,
'iplist=s' => \$inFile,
'start_index=s' => \$startIndex,
'stop_index=s' => \$stopIndex,
'iprange=s' => \$ipRange,
'output=s' => \$outFile,
'sS' => sub { $NMAP_OPTIONS = "-sS "; $ChkRoot = 1; },
'sT' => sub { $NMAP_OPTIONS = $NMAP_OPTIONS . "-sT "; $ChkRoot = 1; },
'sP' => sub { $NMAP_OPTIONS = $NMAP_OPTIONS . "-sP "; $ChkRoot = 1; },
'O' => sub { $NMAP_OPTIONS = $NMAP_OPTIONS . "-O "; $ChkRoot = 1; },
'sV' => sub { $NMAP_OPTIONS = $NMAP_OPTIONS . "-sV "; },
'p=s' => sub { $NMAP_OPTIONS = $NMAP_OPTIONS . "-@_ "; },
'P0' => sub { $NMAP_OPTIONS = $NMAP_OPTIONS . "-P0 "; },
'T=s' => sub { $NMAP_OPTIONS = $NMAP_OPTIONS . "-@_ "; },
);
if ( !defined($PARALLEL) || !(defined($inFile) xor defined($ipRange)) || !defined($outFile) ) {
print "NMAP SPAWN V. $VERSION\n";
print "Usage:\n";
print " $0\n";
print " --threads <number of threads>\n";
print " --iplist <ip list file>\n";
print " --iprange <ip range>\n";
print " --output <output file>\n";
print " --start_index <list start index> [OPTIONAL, 0 = FIRST]\n";
print " --stop_index <list stop index> [OPTIONAL]\n";
print " NMAP Options (-sS, -sT, -sP, -O, -p, -P0, -T)\n";
exit(1);
}
elsif ( ($ChkRoot) && ($>) ) {
print "\n** NMAP FINGERPRINTING REQUIRES ROOT PRIVILEGES **\n\n";
exit(1);
}
#####################################################################
# MISC VARIABLES:
#
# $pidMap{} is keyed on the pid of the child process in charge
# of executing the nmap commands.
#
$PROCCOUNT = 0;
$p_read = 'PREAD00';
$p_write = 'PWRITE00';
$myPid = $$;
$NMAP = "/usr/bin/nmap";
$SIG{'CHLD'} = \&reaper;
$SIG{'INT'} = \&doSummary;
#$SIG{'QUIT'} = \&doSummary;
# read in ip address list
#
my @ip_list;
if (defined($inFile)) {
open(FILE, $inFile) || die("Unable to open: $inFile $!");
my @ip_list_tmp=<FILE>;
foreach(@ip_list_tmp) {
if (/\//) { # range
my $ip = new NetAddr::IP "$_";
push @ip_list, $ip->hostenum;
}
else { push @ip_list, $_; }
foreach (@ip_list) { s/\/\d+//; }
}
close(FILE);
} else {
my $ip = new NetAddr::IP "$ipRange";
#push @ip_list, $ip->network;
push @ip_list, $ip->hostenum;
#push @ip_list, $ip->broadcast;
foreach (@ip_list) { s/\/\d+//; }
}
#####################################################################
# MAIN:
#
my $total_addr = $#ip_list + 1;
my $current_addr = 0;
$TSTART = time();
if (!defined($startIndex)) { $startIndex = 0; }
if (!defined($stopIndex)) { $stopIndex = $#ip_list; }
for ($i = $startIndex; $i <= $stopIndex; $i++) {
$address = $ip_list[$i];
chomp $address;
$current_addr++;
ADD: {
($p_name ,$pid) = &addChild($address);
if ($pid == 0) {
sleep 1;
#print STDERR "Redoing add\n";
redo ADD;
} elsif ($pi>d == -1) {
print "PARENT [$$] failed $address.\n";
} else {
# Success
# print "PARENT [$$] added child $status.\n";
$pidMap{$pid}{'PIPE'} = $p_name;
}
} # ADD
}
while ($PROCCOUNT > 0) { sleep 1; }
$TSTOP = time();
doSummary();
exit(0);
#####################################################################
# Subroutines
# Add a child, return the pid of the child process if successful, -1 if the
# fork failed, 0 if there are no available slots.
#
sub addChild($) {
my $address = $_[0];
my $pid;
chomp $address;
return 0 if ($PROCCOUNT >= $PARALLEL);
#print STDERR "PROCCOUNT is $PROCCOUNT, forking a child\n";
pipe($p_read, $p_write) || die "Can't open a pair of pipes: $!";
FORK: {
if ($pid = fork()) {
# ********* Parent Process **********
$PROCCOUNT++;
$pidMap{$pid} = $address;
close($p_write);
$hold = $p_read;
$p_read++;
$p_write++;
return($hold, $pid);
# ********* Parent Process **********
} elsif (defined($pid)) {
# ********** Child Process **********
close($p_read);
my $temp_current_addr = $current_addr + $startIndex;
print STDERR "Starting NMAP: $address\t($temp_current_addr of $total_addr)\n";
$data = `$NMAP $NMAP_OPTIONS $address`;
print $p_write $data;
close($p_write);
print STDERR "Stopping NMAP: $address\n";
exit(0);
# ********** Child Process **********
} elsif ($! =~ /no more process/i) {
# recoverable fork error?
sleep 5;
redo FORK;
} else {
# can't fork
return -1;
}
} # FORK
} # addChild
# handle the exit of a child process. return the output of the child's
# nmap scan.
#
sub reaper() {
my $pid;
while (($pid = waitpid(-1, &WNOHANG)) > 0) {
# If this pid is not in the pid map, then we are harvesting one of
# our grandchildren, ignore it.
if (! defined ($pidMap{$pid})) {
return;
}
$PROCCOUNT--;
local($pipe) = $pidMap{$pid}{'PIPE'};
@{ $res{ $pidMap{$pid} } } = <$pipe>;
print "\n--> FINISHED: $pidMap{$pid} <--\n @{ $res{ $pidMap{$pid} } }\n\n";
close($pipe);
delete $pidMap{$pid} if $pidMap{$pid}; # Keep track of only what is running.
}
} # reaper
# print the results from the scans
#
sub doSummary {
# display results for parent only
if ($$ == $myPid) {
open (OUT, ">$outFile") || die("Unable to open: $outFile: $!");
foreach $ip (sort byIP keys %res) {
next if !defined( @{ $res{$ip} } );
foreach (@{ $res{$ip} }) {
s/^ //;
next if /Starting nmap /;
next if /Nmap run completed --/;
if (/Note: Host seems down/) { print OUT "Host seems down: $ip\n\n"; }
else { print OUT; }
}
}
$sTSTART = scalar localtime($TSTART);
$sTSTOP = scalar localtime($TSTOP);
$mDiff = int(($TSTOP - $TSTART) / 60);
$sDiff = sprintf("%02d", ($TSTOP - $TSTART) - 60 * $mDiff);
print "(STATS) START: $sTSTART STOP: $sTSTOP DIFF: $mDiff\:$sDiff\n";
print OUT "(STATS) START: $sTSTART STOP: $sTSTOP DIFF: $mDiff\:$sDiff\n";
close(OUT);
}
#exit(0);
} #doSummary
# sort by IP address
#
sub byIP {
my @oct_a = split /\./, $a;
my @oct_b = split /\./, $b;
$oct_a[0] <=> $oct_b[0] or
$oct_a[1] <=> $oct_b[1] or
$oct_a[2] <=> $oct_b[2] or
$oct_a[3] <=> $oct_b[3] or
$a cmp $b
} # byIP
|