主体程序位置在nmap.cc line:1640
学习要点:
程序在1650行,新建一个主机的单例对象,
#ifndef NOLUA
/* Only NSE scripts can add targets */
NewTargets *new_targets = NULL;
/* Pre-Scan and Post-Scan script results datastructure */
ScriptResults *script_scan_results = NULL;
#endif
从1680行开始主体程序。
int nmap_main(int argc, char *argv[]) {
int i;
std::vector<Target *> Targets;
time_t now;
struct hostent *target = NULL;
time_t timep;
char mytime[128];
addrset exclude_group;
#ifndef NOLUA
/* Only NSE scripts can add targets */
NewTargets *new_targets = NULL;
/* Pre-Scan and Post-Scan script results datastructure */
ScriptResults *script_scan_results = NULL;
#endif
unsigned int ideal_scan_group_sz = 0;
Target *currenths;
char myname[MAXHOSTNAMELEN + 1];
int sourceaddrwarning = 0; /* Have we warned them yet about unguessable
source addresses? */
unsigned int targetno;
char hostname[MAXHOSTNAMELEN + 1] = "";
struct sockaddr_storage ss;
size_t sslen;
now = time(NULL);
local_time = localtime(&now);
if (o.debugging)
nbase_set_log(fatal, error);
else
nbase_set_log(fatal, NULL);
if (argc < 2)
printusage(-1);
Targets.reserve(100);
#ifdef WIN32
win_pre_init();
#endif
//命令行参数解析
parse_options(argc, argv);
//linux平台设置只读非阻塞
tty_init(); // Put the keyboard in raw mode
//延迟处理的操作
apply_delayed_options();
#ifdef WIN32
win_init();
#endif
/*
这里用到的变量route_dst_hosts是由参数 --route-dst debugging模式定义的目标列表。定义如下:
static std::vector<std::string> route_dst_hosts;
前面命令行解析后会对其赋值。
*/
for (unsigned int i = 0; i < route_dst_hosts.size(); i++) {
const char *dst;
struct sockaddr_storage ss;
struct route_nfo rnfo;
size_t sslen;
int rc;
dst = route_dst_hosts[i].c_str();
//解析目标
rc = resolve(dst, 0, &ss, &sslen, o.af());
if (rc != 0)
fatal("Can‘t resolve %s: %s.", dst, gai_strerror(rc));
printf("%s\n", inet_ntop_ez(&ss, sslen));
if (!route_dst(&ss, &rnfo, o.device, o.SourceSockAddr())) {
printf("Can‘t route %s (%s).", dst, inet_ntop_ez(&ss, sslen));
} else {
printf("%s %s", rnfo.ii.devname, rnfo.ii.devfullname);
printf(" srcaddr %s", inet_ntop_ez(&rnfo.srcaddr, sizeof(rnfo.srcaddr)));
if (rnfo.direct_connect)
printf(" direct");
else
printf(" nexthop %s", inet_ntop_ez(&rnfo.nexthop, sizeof(rnfo.nexthop)));
}
printf("\n");
}
route_dst_hosts.clear();
if (delayed_options.iflist) {
print_iflist();
exit(0);
}
/* If he wants to bounce off of an FTP site, that site better damn well be reachable! */
// FTP bounce scan模式,nmap -b参数定义
if (o.bouncescan) {
if (!inet_pton(AF_INET, ftp.server_name, &ftp.server)) {
if ((target = gethostbyname(ftp.server_name)))
memcpy(&ftp.server, target->h_addr_list[0], 4);
else {
fatal("Failed to resolve FTP bounce proxy hostname/IP: %s",
ftp.server_name);
}
} else if (o.verbose) {
log_write(LOG_STDOUT, "Resolved FTP bounce attack proxy to %s (%s).\n",
ftp.server_name, inet_ntoa(ftp.server));
}
}
fflush(stdout);
fflush(stderr);
timep = time(NULL);
/* Brief info in case they forget what was scanned */
//扫描的简要信息 记录到xml
Strncpy(mytime, ctime(&timep), sizeof(mytime));
chomp(mytime);
char *xslfname = o.XSLStyleSheet();
xml_start_document("nmaprun");
if (xslfname) {
xml_open_pi("xml-stylesheet");
xml_attribute("href", "%s", xslfname);
xml_attribute("type", "text/xsl");
xml_close_pi();
xml_newline();
}
std::string command;
if (argc > 0)
command += argv[0];
for (i = 1; i < argc; i++) {
command += " ";
command += argv[i];
}
xml_start_comment();
xml_write_escaped(" %s %s scan initiated %s as: %s ", NMAP_NAME, NMAP_VERSION, mytime, join_quoted(argv, argc).c_str());
xml_end_comment();
xml_newline();
// 记录扫描日志
log_write(LOG_NORMAL | LOG_MACHINE, "# ");
log_write(LOG_NORMAL | LOG_MACHINE, "%s %s scan initiated %s as: ", NMAP_NAME, NMAP_VERSION, mytime);
log_write(LOG_NORMAL | LOG_MACHINE, "%s", command.c_str());
log_write(LOG_NORMAL | LOG_MACHINE, "\n");
xml_open_start_tag("nmaprun");
xml_attribute("scanner", "nmap");
xml_attribute("args", "%s", join_quoted(argv, argc).c_str());
xml_attribute("start", "%lu", (unsigned long) timep);
xml_attribute("startstr", "%s", mytime);
xml_attribute("version", "%s", NMAP_VERSION);
xml_attribute("xmloutputversion", NMAP_XMLOUTPUTVERSION);
xml_close_start_tag();
xml_newline();
output_xml_scaninfo_records(&ports);
xml_open_start_tag("verbose");
xml_attribute("level", "%d", o.verbose);
xml_close_empty_tag();
xml_newline();
xml_open_start_tag("debugging");
xml_attribute("level", "%d", o.debugging);
xml_close_empty_tag();
xml_newline();
/* Before we randomize the ports scanned, lets output them to machine
parseable output */
// 在随机端口扫描前,把可以解析的端口输出机器
if (o.verbose)
output_ports_to_machine_parseable_output(&ports);
#if defined(HAVE_SIGNAL) && defined(SIGPIPE)
signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE so our program doesn‘t crash because
of it, but we really shouldn‘t get an unexpected
SIGPIPE */
#endif
if (o.max_parallelism && (i = max_sd()) && i < o.max_parallelism) {
error("WARNING: Your specified max_parallel_sockets of %d, but your system says it might only give us %d. Trying anyway", o.max_parallelism, i);
}
// 端口号是否溢出
if (o.debugging > 1)
log_write(LOG_STDOUT, "The max # of sockets we are using is: %d\n", o.max_parallelism);
// At this point we should fully know our timing parameters
if (o.debugging) {
log_write(LOG_PLAIN, "--------------- Timing report ---------------\n");
log_write(LOG_PLAIN, " hostgroups: min %d, max %d\n", o.minHostGroupSz(), o.maxHostGroupSz());
log_write(LOG_PLAIN, " rtt-timeouts: init %d, min %d, max %d\n", o.initialRttTimeout(), o.minRttTimeout(), o.maxRttTimeout());
log_write(LOG_PLAIN, " max-scan-delay: TCP %d, UDP %d, SCTP %d\n", o.maxTCPScanDelay(), o.maxUDPScanDelay(), o.maxSCTPScanDelay());
log_write(LOG_PLAIN, " parallelism: min %d, max %d\n", o.min_parallelism, o.max_parallelism);
log_write(LOG_PLAIN, " max-retries: %d, host-timeout: %ld\n", o.getMaxRetransmissions(), o.host_timeout);
log_write(LOG_PLAIN, " min-rate: %g, max-rate: %g\n", o.min_packet_send_rate, o.max_packet_send_rate);
log_write(LOG_PLAIN, "---------------------------------------------\n");
}
//端口与地址初始化
if (o.ipprotscan)
PortList::initializePortMap(IPPROTO_IP, ports.prots, ports.prot_count);
if (o.TCPScan())
PortList::initializePortMap(IPPROTO_TCP, ports.tcp_ports, ports.tcp_count);
if (o.UDPScan())
PortList::initializePortMap(IPPROTO_UDP, ports.udp_ports, ports.udp_count);
if (o.SCTPScan())
PortList::initializePortMap(IPPROTO_SCTP, ports.sctp_ports, ports.sctp_count);
// 打乱端口顺序
if (o.randomize_ports) {
if (ports.tcp_count) {
shortfry(ports.tcp_ports, ports.tcp_count);
// move a few more common ports closer to the beginning to speed scan
//常见端口往前放
random_port_cheat(ports.tcp_ports, ports.tcp_count);
}
if (ports.udp_count)
shortfry(ports.udp_ports, ports.udp_count);
if (ports.sctp_count)
shortfry(ports.sctp_ports, ports.sctp_count);
if (ports.prot_count)
shortfry(ports.prots, ports.prot_count);
}
//--exclude_group 命令行参数的排除地址处理
addrset_init(&exclude_group);
/* lets load our exclude list */
if (o.excludefd != NULL) {
load_exclude_file(&exclude_group, o.excludefd);
fclose(o.excludefd);
}
if (o.exclude_spec != NULL) {
load_exclude_string(&exclude_group, o.exclude_spec);
}
if (o.debugging > 3)
dumpExclude(&exclude_group);
//NES 环境
#ifndef NOLUA
if (o.scriptupdatedb) {
o.max_ips_to_scan = o.numhosts_scanned; // disable warnings?
}
// 版本扫描
if (o.servicescan)
o.scriptversion = 1;
if (o.scriptversion || o.script || o.scriptupdatedb)
open_nse();
/* Run the script pre-scanning phase */
// 预分析脚本
if (o.script) {
new_targets = NewTargets::get();
script_scan_results = get_script_scan_results_obj();
script_scan(Targets, SCRIPT_PRE_SCAN);
printscriptresults(script_scan_results, SCRIPT_PRE_SCAN);
while (!script_scan_results->empty()) {
script_scan_results->front().clear();
script_scan_results->pop_front();
}
}
#endif
// hstate 是一个list,初始为空,循环执行后保存各主机表达式字符串地址
HostGroupState hstate(o.ping_group_sz, o.randomize_hosts, argc, (const char **) argv);
// 主程序循环
do {
// 计算host group大小
ideal_scan_group_sz = determineScanGroupSize(o.numhosts_scanned, &ports);
// 主机发现成功,同加入到host group,再后续处理
while (Targets.size() < ideal_scan_group_sz) {
o.current_scantype = HOST_DISCOVERY;
// 主机发现
currenths = nexthost(&hstate, &exclude_group, &ports, o.pingtype);
//如果没有发现主机,就进行下一次循环
if (!currenths)
break;
if (currenths->flags & HOST_UP && !o.listscan)
o.numhosts_up++;
if ((o.noportscan && !o.traceroute
#ifndef NOLUA
&& !o.script
#endif
) || o.listscan) {
/* We‘re done with the hosts */
// 如果 命令行参数-sn(不进行端口扫描) 且没有指定traceroute和脚本的话,扫描结束
// 如果 -sL(只列出ip),扫描也结束。
if (currenths->flags & HOST_UP || (o.verbose && !o.openOnly())) {
xml_start_tag("host");
write_host_header(currenths);
printmacinfo(currenths);
// if (currenths->flags & HOST_UP)
// log_write(LOG_PLAIN,"\n");
printtimes(currenths);
xml_end_tag();
xml_newline();
log_flush_all();
}
delete currenths;
o.numhosts_scanned++;
continue;
}
// -S ip (配置要伪造的IP地址)
if (o.spoofsource) {
o.SourceSockAddr(&ss, &sslen);
currenths->setSourceSockAddr(&ss, sslen);
}
/* I used to check that !currenths->weird_responses, but in some
rare cases, such IPs CAN be port successfully scanned and even
connected to */
// 一些情况下,主机有返回状态,全状态为HOST_DOWN
if (!(currenths->flags & HOST_UP)) {
if (o.verbose && (!o.openOnly() || currenths->ports.hasOpenPorts())) {
xml_start_tag("host");
write_host_header(currenths);
xml_end_tag();
xml_newline();
}
delete currenths;
o.numhosts_scanned++;
continue;
}
//RawScan ,如SYN/FIN/ARP
if (o.RawScan()) {
if (currenths->SourceSockAddr(NULL, NULL) != 0) {
if (o.SourceSockAddr(&ss, &sslen) == 0) {
//直接设置IP
currenths->setSourceSockAddr(&ss, sslen);
} else {
//解析主机名
if (gethostname(myname, MAXHOSTNAMELEN) ||
resolve(myname, 0, &ss, &sslen, o.af()) != 0)
fatal("Cannot get hostname! Try using -S <my_IP_address> or -e <interface to scan through>\n");
o.setSourceSockAddr(&ss, sslen);
currenths->setSourceSockAddr(&ss, sslen);
if (! sourceaddrwarning) {
error("WARNING: We could not determine for sure which interface to use, so we are guessing %s . If this is wrong, use -S <my_IP_address>.",
inet_socktop(&ss));
sourceaddrwarning = 1;
}
}
}
// 网络设备(网卡)名称
if (!currenths->deviceName())
fatal("Do not have appropriate device name for target");
/* Hosts in a group need to be somewhat homogeneous. Put this host in
the next group if necessary. See target_needs_new_hostgroup for the
details of when we need to split. */
//同一个组内主机要是同性质的,这里判断目标是否加到list列表内
if (target_needs_new_hostgroup(Targets, currenths)) {
returnhost(&hstate);
o.numhosts_up--;
break;
}
o.decoys[o.decoyturn] = currenths->v4source();
}
Targets.push_back(currenths);
}
//没有发现主机
if (Targets.size() == 0)
break; /* Couldn‘t find any more targets */
// Set the variable for status printing
o.numhosts_scanning = Targets.size();
// Our source must be set in decoy list because nexthost() call can
// change it (that issue really should be fixed when possible)
if (o.af() == AF_INET && o.RawScan())
o.decoys[o.decoyturn] = Targets[0]->v4source();
/* I now have the group for scanning in the Targets vector */
// 定义了端口扫描,进入扫描的主体
if (!o.noportscan) {
// Ultra_scan sets o.scantype for us so we don‘t have to worry
if (o.synscan)
ultra_scan(Targets, &ports, SYN_SCAN);
if (o.ackscan)
ultra_scan(Targets, &ports, ACK_SCAN);
if (o.windowscan)
ultra_scan(Targets, &ports, WINDOW_SCAN);
if (o.finscan)
ultra_scan(Targets, &ports, FIN_SCAN);
if (o.xmasscan)
ultra_scan(Targets, &ports, XMAS_SCAN);
if (o.nullscan)
ultra_scan(Targets, &ports, NULL_SCAN);
if (o.maimonscan)
ultra_scan(Targets, &ports, MAIMON_SCAN);
if (o.udpscan)
ultra_scan(Targets, &ports, UDP_SCAN);
if (o.connectscan)
ultra_scan(Targets, &ports, CONNECT_SCAN);
if (o.sctpinitscan)
ultra_scan(Targets, &ports, SCTP_INIT_SCAN);
if (o.sctpcookieechoscan)
ultra_scan(Targets, &ports, SCTP_COOKIE_ECHO_SCAN);
if (o.ipprotscan)
ultra_scan(Targets, &ports, IPPROT_SCAN);
/* These lame functions can only handle one target at a time */
if (o.idlescan) {
for (targetno = 0; targetno < Targets.size(); targetno++) {
o.current_scantype = IDLE_SCAN;
keyWasPressed(); // Check if a status message should be printed
idle_scan(Targets[targetno], ports.tcp_ports,
ports.tcp_count, o.idleProxy, &ports);
}
}
if (o.bouncescan) {
for (targetno = 0; targetno < Targets.size(); targetno++) {
o.current_scantype = BOUNCE_SCAN;
keyWasPressed(); // Check if a status message should be printed
if (ftp.sd <= 0)
ftp_anon_connect(&ftp);
if (ftp.sd > 0)
bounce_scan(Targets[targetno], ports.tcp_ports, ports.tcp_count, &ftp);
}
}
if (o.servicescan) {
o.current_scantype = SERVICE_SCAN;
service_scan(Targets);
}
}
if (o.osscan) {
OSScan os_engine;
os_engine.os_scan(Targets);
}
if (o.traceroute)
traceroute(Targets);
#ifndef NOLUA
if (o.script || o.scriptversion) {
script_scan(Targets, SCRIPT_SCAN);
}
#endif
//输出扫描结果
for (targetno = 0; targetno < Targets.size(); targetno++) {
currenths = Targets[targetno];
/* Now I can do the output and such for each host */
if (currenths->timedOut(NULL)) {
xml_open_start_tag("host");
xml_attribute("starttime", "%lu", (unsigned long) currenths->StartTime());
xml_attribute("endtime", "%lu", (unsigned long) currenths->EndTime());
xml_close_start_tag();
write_host_header(currenths);
xml_end_tag(); /* host */
xml_newline();
log_write(LOG_PLAIN, "Skipping host %s due to host timeout\n",
currenths->NameIP(hostname, sizeof(hostname)));
log_write(LOG_MACHINE, "Host: %s (%s)\tStatus: Timeout\n",
currenths->targetipstr(), currenths->HostName());
} else {
/* --open means don‘t show any hosts without open ports. */
if (o.openOnly() && !currenths->ports.hasOpenPorts())
continue;
xml_open_start_tag("host");
xml_attribute("starttime", "%lu", (unsigned long) currenths->StartTime());
xml_attribute("endtime", "%lu", (unsigned long) currenths->EndTime());
xml_close_start_tag();
write_host_header(currenths);
printportoutput(currenths, ¤ths->ports);
printmacinfo(currenths);
printosscanoutput(currenths);
printserviceinfooutput(currenths);
#ifndef NOLUA
printhostscriptresults(currenths);
#endif
if (o.traceroute)
printtraceroute(currenths);
printtimes(currenths);
log_write(LOG_PLAIN | LOG_MACHINE, "\n");
xml_end_tag(); /* host */
xml_newline();
}
}
log_flush_all();
o.numhosts_scanned += Targets.size();
/* Free all of the Targets */
while (!Targets.empty()) {
currenths = Targets.back();
delete currenths;
Targets.pop_back();
}
o.numhosts_scanning = 0;
} while (!o.max_ips_to_scan || o.max_ips_to_scan > o.numhosts_scanned); // 循环条件
#ifndef NOLUA
if (o.script) {
script_scan(Targets, SCRIPT_POST_SCAN);
printscriptresults(script_scan_results, SCRIPT_POST_SCAN);
while (!script_scan_results->empty()) {
script_scan_results->front().clear();
script_scan_results->pop_front();
}
delete new_targets;
new_targets = NULL;
}
#endif
addrset_free(&exclude_group);
if (o.inputfd != NULL)
fclose(o.inputfd);
printdatafilepaths();
printfinaloutput();
free_scan_lists(&ports);
eth_close_cached();
if (o.release_memory) {
nmap_free_mem();
}
return 0;
}
其它关联信息
Target类
target.cc定义的是主机的类,扫描信息也是保存在target对象。nmap_main创建target时,使用了单例模式。
时间: 2024-10-14 17:21:39