Nmap是一款非常强大的开源扫描工具。自己在使用过程中忍不住想仔细阅读一下它的源码。源码里面汇集了众多安全专家的精巧设计与优雅写法,读起来令人心旷神怡而又受益匪浅。

这里我们以阅读nmap6.0的代码作为主线来分析Nmap源码的实现框架。

源码下载地址:http://nmap.org/dist/nmap-6.01.tar.bz2

SVN检出:svn co https://svn.nmap.org/nmap

1 文件组织

我们先从总体上了解Nmap的文件组织方式及分析源码需要关注的重点。

1.1    目录结构

解压Nmap的源码包后,可以看到根目录下有许多的子目录和文件。

文件分析

放在Nmap/根目录下包括几种类型文件:

1)       Nmap核心功能的源码(如nmap.cc/ scan_engine.cc/ service_scan.cc/osscan2.cc/ nse_main.lua等)。

2)       Nmap的核心数据库文件(nmap-os-db/ nmap-service-probes/ nmap-rpc/nmap-protocols等)。

3)       编译链接相关的Makefile或CONFIG文件。

4)       其他杂项文件(如安装提示:README-WIN32)

目录分析

使用Windows的tree命令列举出Nmap的目录结构.

为了避免目录树过长,这里只显示了3个级别的目录,目录的主要作用在名字后面简略说明。

可以看到Nmap工具也使用到很多其他开源项目的成果,例如libdnet/ liblinear/ liblua/libpcap/ libpcre等;另外Nmap自身也实现很多有用的程序库,如nsock/ libnetutil/;Nmap项目包括附带几个小工具:1)ncat,这是根据TCPIP协议栈瑞士军刀netcat(该工具已停止维护更新)开发扩展出来的工具。2)nping,类似于Hping的工具,用于进行主机探测和发包收包。3)ndiff,用于比较两次nmap扫描结果之间差异。

Nmap/
├─docs(Nmap相关文档,包括License、usage说明及XMLschema文件等)
│  ├─licenses
│  └─man-xlate
├─libdnet-stripped(libdnet:简单的网络接口开源库)
│  ├─config
│  ├─include
│  └─src
├─liblinear(LIBLINEAR:负责大型线性分类的开源库)
│  └─blas
├─liblua(Lua脚本语言源码库)
├─libnetutil(Nmap实现的基本的网络实用函数)
├─libpcap(开源的抓包代码库libpcap)
│  ├─bpf
│  ├─ChmodBPF
│  ├─lbl
│  ├─missing
│  ├─msdos
│  ├─NMAP_MODIFICATIONS
│  ├─packaging
│  ├─pcap
│  ├─SUNOS4
│  ├─tests
│  └─Win32
├─libpcre(Perl兼容的正则表达式开源库libpcre)
├─macosx(该目录负责支持苹果的操作系统MACOS X)
│  └─nmap.pmdoc
├─mswin32(该目录负责支持Windows操作系统)
│  ├─lib
│  ├─license-format
│  ├─NET
│  ├─NETINET
│  ├─nsis
│  ├─OpenSSL
│  ├─pcap-include
│  ├─RPC
│  └─winpcap
├─nbase(Nmap封装的基础使用程序库,包括string/path/random等)
├─ncat(Ncat是Nmap项目组实现的新版的netcat:强大的网络工具)
│  ├─certs
│  ├─docs
│  └─test
├─ndiff(Ndiff是用于比较Nmap扫描结果的实用命令)
│  ├─docs
│  └─test-scans
├─nmap-update(负责Nmap更新相关操作)
├─nping(Nping是Nmap项目组实现的新版的Hping:网络探测与构建packet)
│  └─docs
├─nselib(Nmap使用Lua语言编写的常用的脚本库)
│  └─data
├─nsock(Nmap实现的并行的SocketEvent处理库)
│  ├─include
│  └─src
├─scripts(Nmap提供常用的扫描检查的lua脚本)
├─todo(介绍Nmap项目将来开发的具体任务)
└─zenmap(Nmap的官方的图形界面程序,由python语言编写)  ├─install_scripts  ├─radialnet  ├─share  ├─test  ├─zenmapCore  └─zenmapGUI  

1.2    文件类型

下面从源码类型的角度上浏览一下Nmap项目的特点:

Nmap6.0工程内总共包括1300多个文件。

1)       C和C++文件有600多个,主要实现Nmap最核心的功能:主机发现、端口扫描、服务侦测、OS侦测及搭建脚本引擎框架;也包括其他开源项目如libpcap的源码。

2)       Python文件有100多个,主要实现Zenmap图形界面,Zenmap会调用到Nmap基本命令,也实现一些新的功能:例如确定网络拓扑结构、Profile的管理(常用的命令保存为Profile)等。

3)       Lua与NSE文件400多个,负责构建Nmap脚本引擎及提供常用的扫描脚本。其中NSE格式为Nmap定制的Lua文件,方便用户自行编写脚本进行功能扩展。

4)       XML文件数十个,用于辅助描述Nmap的内容或Zenmap的测试等工作。

5)       其他文件,其他辅助工具操作的文件。

2      源码分析

2.1    执行流程框架

Nmap的执行流程简单清晰,主要的工作在nmap.cc文件中完成,而main.cc负责简单地包装nmap_main()函数。

nmap_main()函数是执行流程的核心。

  • 准备阶段:在其中会执行参数解析、资源分配、基本扫描信息的输出、端口与地址列表的初始化、NSE环境准备及pre-scripts的运行等基本的准备操作。
  • 工作阶段:然后进入主循环,每次循环对一组目标地址进行主机发现、端口扫描、服务与版本侦测、OS侦测及脚本扫描等操作,直到所有的目标地址都被扫描完毕才推出主循环。
  • 善后阶段:在完成所有的扫描操作后,调用post-script完成相应处理,然后打印出扫描的最终结果,并释放掉分配的资源。

2.2    nmap_main()函数

以下代码是对nmap_main()函数基本的分析。

其中以///开头是新添加的注释。而以类似于///<Start------创建主机组状态,进入主循环--------Start>的形式出现的注释用于标注一个比较大的功能代码段。

Nmap的所有的功能都在此nmap_main()设有入口,以此为基础可以更深入地分析Nmap的其他模块。

int nmap_main(int argc, char *argv[]) {int i;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;///NewTargets为Singleton模式,产生单个实例/* Pre-Scan and Post-Scan script results datastructure */ScriptResults *script_scan_results = NULL;#endifchar **host_exp_group;  int num_host_exp_groups;HostGroupState *hstate = NULL;unsigned int ideal_scan_group_sz = 0;Target *currenths;char *host_spec = NULL;char myname[MAXHOSTNAMELEN + 1];int sourceaddrwarning = 0; /* Have we warned them yet about unguessablesource addresses? */unsigned int targetno;char hostname[MAXHOSTNAMELEN + 1] = "";struct sockaddr_storage ss;size_t sslen;char **fakeargv = NULL;now = time(NULL);local_time = localtime(&now);///设置错误log输出函数if(o.debugging)nbase_set_log(fatal,error);elsenbase_set_log(fatal,NULL);if (argc < 2 ) printusage(-1);/* argv faking silliness */fakeargv = (char **) safe_malloc(sizeof(char *) * (argc + 1));for(i=0; i < argc; i++) {fakeargv[i] = strdup(argv[i]);}fakeargv[argc] = NULL;Targets.reserve(100);
#ifdef WIN32win_pre_init();
#endif
///调用parse_options进行命令参数的解析parse_options(argc, fakeargv);
///在Linux下设置终端为只读非阻塞方式,在Windows平台为空函数。tty_init(); // Put the keyboard in raw mode
///将解析命令时需要延迟执行的操作在此处处理apply_delayed_options();#ifdef WIN32
///调用WSAStartup启动Winsock DLL,后续网络解析等需要用到。win_init();
#endif///如果用户使用了参数--iflist,那么会在此处打印网卡和路由表信息,然后退出。
///该选项对于显示指定发送网卡非常有帮助,可以提供基本的网络设备信息。if (delayed_options.iflist) {print_iflist();exit(0);}///quashargv部分用于修改命令行参数,将程序名字更改为FAKE_ARGV(默认为“pine”),
///并将剩余的各个参数都清空。
///在命令中加入-q可实现quashargv功能。这最初是为了逃避ps等程序名称显示,便于隐蔽Nmap。
///不过在Windows系统上并无实效。/* more fakeargv junk, BTW malloc'ing extra space in argv[0] doesn't work */if (o.quashargv) {size_t fakeargvlen = strlen(FAKE_ARGV), argvlen = strlen(argv[0]);if (argvlen < fakeargvlen)fatal("If you want me to fake your argv, you need to call the program with a longer name.  Try the full pathname, or rename it fyodorssuperdedouperportscanner");strncpy(argv[0], FAKE_ARGV, fakeargvlen);memset(&argv[0][fakeargvlen], '\0', strlen(&argv[0][fakeargvlen]));for(i=1; i < argc; i++)memset(argv[i], '\0', strlen(argv[i]));}
///如果使用FTP bounce scan的扫描方式,那么需要首先保证该FTP网站是可以访问到的。
///关于FTP bounce scan更多介绍,请参考:http://nmap.org/nmap_doc.html#bounce/* If he wants to bounce off of an FTP site, that site better damn well be reachable! */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));}}
///<Start--------------扫描信息输出-----------------Start>  fflush(stdout);fflush(stderr);timep = time(NULL);
///准备将基本的扫描输出到文件与控制台中/* Brief info in case they forget what was scanned */Strncpy(mytime, ctime(&timep), sizeof(mytime));chomp(mytime);  ///去掉字符串末尾换行符char *xslfname = o.XSLStyleSheet();///XML样式表xml_start_document();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 += fakeargv[0];for (i = 1; i < argc; i++) {command += " ";command += fakeargv[i];}xml_start_comment();xml_write_escaped(" %s %s scan initiated %s as: %s ", NMAP_NAME, NMAP_VERSION, mytime, join_quoted(fakeargv, 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(fakeargv, 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 machineparseable output */if (o.verbose) ///输出机器可以解析端口信息(grepable格式)output_ports_to_machine_parseable_output(&ports);#if defined(HAVE_SIGNAL) && defined(SIGPIPE)///注册信号处理函数,这里只是直接忽略SIGPIPE信号。其具体实现为#define SIG_IGN       (void (*)(int))1signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE so our program doesn't crash becauseof it, but we really shouldn't get an unexpectedSIGPIPE */
#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 parametersif (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");}
///<End--------------扫描信息输出-----------------End>///<Start-------------端口与地址初始化-------------Start>/* Before we randomize the ports scanned, we must initialize PortList class. */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记录的是排除地址,如命令行nmap 192.168.1.1/24 --exclude 192.168.1.0-10///扫描C类地址192.168.1.x,并排除其中192.168.1.0-192.168.1.10地址。///addrset_init()将初始化排除地址组的链表头指针为NULL。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)  ///若调试级别大于3,打印出排除地址信息dumpExclude(&exclude_group);
///<End-------------端口与地址初始化-------------End>///<Start------NSE环境准备并执行pre-scripts--------Start>
#ifndef NOLUAif (o.scriptupdatedb) {o.max_ips_to_scan = o.numhosts_scanned; // disable warnings?}if (o.servicescan)  ///当配置了版本扫描时,会默认启动版本扫描脚本,位于NSE中version类别中o.scriptversion = 1;if (o.scriptversion || o.script || o.scriptupdatedb)open_nse();  ///开启NSE环境/* Run the script pre-scanning phase */if (o.script) {new_targets = NewTargets::get();  ///分配实例或返回已有实例(Singleton模式)script_scan_results = get_script_scan_results_obj();script_scan(Targets, SCRIPT_PRE_SCAN);printscriptresults(script_scan_results, SCRIPT_PRE_SCAN);script_scan_results->clear();}
#endif
///<End------NSE环境准备并执行pre-scripts--------End>///<Start------创建主机组状态,进入主循环--------Start>/* Time to create a hostgroup state object filled with all the requestedmachines. The list is initially empty. It is refilled inside the loopwhenever it is empty. *////分配字符串数组,用以保存各个主机表达式字符串的地址host_exp_group = (char **) safe_malloc(o.ping_group_sz * sizeof(char *));num_host_exp_groups = 0;hstate = new HostGroupState(o.ping_group_sz, o.randomize_hosts,host_exp_group, num_host_exp_groups);do {///确定最佳的host group的大小,该大小取决于扫描方式与网络速度。ideal_scan_group_sz = determineScanGroupSize(o.numhosts_scanned, &ports);///<Start---------对host group进行主机发现----------Start>///以下的while()将依次进行主机发现,确定主机是否在线。///若该主机在线加入该host group,用于后续的操作。当数量达到最佳大小时,退出循环。while(Targets.size() < ideal_scan_group_sz) {o.current_scantype = HOST_DISCOVERY;  ///设置扫描状态:HOST_DICOVERYcurrenths = nexthost(hstate, &exclude_group, &ports, o.pingtype); ///主机发现的核心函数///如果当前主机发现无法找到有效主机,那么会做以下尝试:///1)更换主机表达式(host expressions)///例如:nmap 192.168.1.1/24 10.10.30.55-100,192.168.1.x不能再发现主机时候,切换为10.30.55-100///2)将执行脚本扫描时发现的主机,加入主机表达式组host_exp_group///3) 建立新的主机组状态,并做最后的主机发现尝试if (!currenths) {/* Try to refill with any remaining expressions *//* First free the old ones */for(i=0; i < num_host_exp_groups; i++)free(host_exp_group[i]);num_host_exp_groups = 0;/* Now grab any new expressions */while(num_host_exp_groups < o.ping_group_sz && (!o.max_ips_to_scan || o.max_ips_to_scan > o.numhosts_scanned + (int) Targets.size() + num_host_exp_groups) &&(host_spec = grab_next_host_spec(o.inputfd, o.generate_random_ips, argc, fakeargv))) {// For purposes of random scanhost_exp_group[num_host_exp_groups++] = strdup(host_spec);}
#ifndef NOLUA/* Add the new NSE discovered targets to the scan queue */if (o.script) {if (new_targets != NULL) {while (new_targets->get_queued() > 0 && num_host_exp_groups < o.ping_group_sz) {std::string target_spec = new_targets->read();if (target_spec.length())host_exp_group[num_host_exp_groups++] = strdup(target_spec.c_str());}if (o.debugging > 3)log_write(LOG_PLAIN,"New targets in the scanned cache: %ld, pending ones: %ld.\n",new_targets->get_scanned(), new_targets->get_queued());}}
#endifif (num_host_exp_groups == 0)  ///当没有其他的主机表达式时,退出整个主机发现循环break;delete hstate;hstate = new HostGroupState(o.ping_group_sz, o.randomize_hosts,host_exp_group,num_host_exp_groups);/* Try one last time -- with new expressions */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) {///当不进行端口扫描(-sn)并且没有指定traceroute和脚本的话,那么扫描就到此处就结束。///或当进行列表扫描(-sL,只列举出主机IP,并不真正扫描)时,扫描也到此结束。        /* We're done with the hosts */if (currenths->flags & HOST_UP || o.verbose) {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;}///若配置要伪造源IP地址(-S ip),将命令行中传入的地址写入当前主机源地址if (o.spoofsource) {o.SourceSockAddr(&ss, &sslen);currenths->setSourceSockAddr(&ss, sslen);}///如果主机状态为HOST_DOWN,那么需要根据配置考虑是否输出其状态///输出条件:verbose级别大于0,并且没有指定openonly或已确定有开放端口。///疑问:如果有open Ports,为什么此主机状态会是HOST_DOWN呢?/* I used to check that !currenths->weird_responses, but in somerare cases, such IPs CAN be port successfully scanned and evenconnected to */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(即涉及到构建原始的packet的扫描方式,如SYN/FIN/ARP等等),///需要设置套接字源IP地址if (o.RawScan()) {if (currenths->SourceSockAddr(NULL, NULL) != 0) {if (o.SourceSockAddr(&ss, &sslen) == 0) {///若全局变量o中已有源IP地址,直接赋值给当前目标机currenths->setSourceSockAddr(&ss, sslen);} else {///否则,需要重新查询、解析主机来获取源地址if (gethostname(myname, MAXHOSTNAMELEN) ||resolve(myname, 0, 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())///网卡名字,在主机发现函数nexthost()中设置fatal("Do not have appropriate device name for target");///如果新发现的主机与该主机组类型不大相同,那么考虑将此主机放入新的主机组内。///因为对主机分组是为了加快扫描速度,所以尽可能特征相似的主机组合在一起。///流水线工作模式的扫描思想。/* Hosts in a group need to be somewhat homogeneous. Put this host inthe next group if necessary. See target_needs_new_hostgroup for thedetails of when we need to split. */if (target_needs_new_hostgroup(Targets, currenths)) {returnhost(hstate);o.numhosts_up--;break;}///设置IP诱骗时,将当前主机真实IP放入decoyturn位置。///其他的诱骗IP地址在parse options时已经确定。o.decoys[o.decoyturn] = currenths->v4source();    }///将新发现的主机加入Targets向量Targets.push_back(currenths);        }///一次分组的主机发现在此处结束,接下来执行端口扫描、服务侦测、OS侦测、脚本扫描等。///<End---------对host group进行主机发现----------End>if (Targets.size() == 0)///主机发现没有找到任何目标机时,退出主循环break; /* Couldn't find any more targets */// Set the variable for status printingo.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) {///<Start---------端口扫描----------Start>///针对用户指定的不同扫描方式,分别使用不同参数调用ultra_scan()///ultra_scan()设计精巧,用统一的接口处理大多数的端口扫描// Ultra_scan sets o.scantype for us so we don't have to worryif (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 printedidle_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 printedif (ftp.sd <= 0) ftp_anon_connect(&ftp);if (ftp.sd > 0) bounce_scan(Targets[targetno], ports.tcp_ports,ports.tcp_count, &ftp);}}///<End---------端口扫描----------End>///<Start------服务与版本扫描--------Start>if (o.servicescan) {o.current_scantype = SERVICE_SCAN; service_scan(Targets);}if (o.servicescan) {/* This scantype must be after any TCP or UDP scans since it* get's it's port scan list from the open port list of the current* host rather than port list the user specified.*/for(targetno = 0; targetno < Targets.size(); targetno++)pos_scan(Targets[targetno], NULL, 0, RPC_SCAN);}///<End------服务与版本扫描--------End>}///操作系统扫描if (o.osscan){OSScan os_engine;os_engine.os_scan(Targets);}///若需要路径追踪,在此处调用traceroute获取路径if (o.traceroute)traceroute(Targets);///脚本扫描
#ifndef NOLUAif(o.script || o.scriptversion) {script_scan(Targets, SCRIPT_SCAN);}
#endif///<Start------输出扫描结果--------Start>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", 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 NOLUAprinthostscriptresults(currenths);
#endifif (o.traceroute)printtraceroute(currenths);printtimes(currenths);log_write(LOG_PLAIN|LOG_MACHINE,"\n");xml_end_tag(); /* host */xml_newline();}}log_flush_all();///<End------输出扫描结果--------End>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);///当指定的扫描数量没有达到已经扫描数量,继续循环///<End------创建主机组状态,进入主循环--------End>///执行post-script,释放分配的资源
#ifndef NOLUAif (o.script) {   script_scan(Targets, SCRIPT_POST_SCAN);printscriptresults(script_scan_results, SCRIPT_POST_SCAN);script_scan_results->clear();delete new_targets;new_targets = NULL;}
#endifdelete hstate;addrset_free(&exclude_group);hstate = NULL;/* Free host expressions */for(i=0; i < num_host_exp_groups; i++)free(host_exp_group[i]);num_host_exp_groups = 0;free(host_exp_group);if (o.inputfd != NULL)fclose(o.inputfd);printdatafilepaths();printfinaloutput();free_scan_lists(&ports);eth_close_cached();if (o.release_memory) {/* Free fake argv */for(i=0; i < argc; i++)free(fakeargv[i]);free(fakeargv);nmap_free_mem();}return 0;
}

Nmap源码分析(基本框架)相关推荐

  1. 【OkHttp】OkHttp 源码分析 ( 网络框架封装 | OkHttp 4 迁移 | OkHttp 建造者模式 )

    OkHttp 系列文章目录 [OkHttp]OkHttp 简介 ( OkHttp 框架特性 | Http 版本简介 ) [OkHttp]Android 项目导入 OkHttp ( 配置依赖 | 配置 ...

  2. SpringMVC源码分析_框架原理图

                                                                                 SpringMVC源码分析_框架原理图     ...

  3. Activiti源码分析(框架、核心类。。。)

    Activiti源码分析(框架.核心类...) 目录 概 述 activiti源码分析(一)设计模式 总结: 相关工具如下: 分析: 小结: 参考资料和推荐阅读 LD is tigger foreve ...

  4. Nmap源码分析(脚本引擎)

    Nmap提供了强大的脚本引擎(NSE),以支持通过Lua编程来扩展Nmap的功能.目前脚本库已经包含300多个常用的Lua脚本,辅助完成Nmap的主机发现.端口扫描.服务侦测.操作系统侦测四个基本功能 ...

  5. [Abp vNext 源码分析] - 1. 框架启动流程分析

    一.简要说明 本篇文章主要剖析与讲解 Abp vNext 在 Web API 项目下的启动流程,让大家了解整个 Abp vNext 框架是如何运作的.总的来说 ,Abp vNext 比起 ABP 框架 ...

  6. Scrapy-redis 源码分析 及 框架使用

    From:https://blog.csdn.net/weixin_37947156/article/details/75044971 From:https://cuiqingcai.com/6058 ...

  7. ThinkPHP5.1.x 框架源码分析之框架的灵魂

    一.类的自动加载初始 框架的灵魂,类的自动加载 为什么说是框架灵魂呢,一般框架都会有类的自动加载,当引入文件很多的时候,就会需要用到.这一个也是很多人想去阅读源码时卡住的点 源码阅读 打开到入口文件 ...

  8. android animatorset 监听,Android源码分析--动画框架AnimatorSet

    这一节,我要介绍的是AnimatorSet. 一提到android的动画,一定会涉及到AnimatorSet,因为他太好用了.它所封装的接口让很多复杂的动画叠加变得容易. AnimatorSet是继承 ...

  9. Nmap源码分析(服务与版本扫描)

    在进行端口扫描后,Nmap可以进一步探测出运行在端口上的服务类型及应用程序的版本.目前Nmap可以识别几千种服务程序的签名(Signature),覆盖了180多种应用协议.比如,端口扫描检测到80端口 ...

最新文章

  1. 分布式系统的面试题14
  2. python自然语言处理.词性标注
  3. ADO.NET 快速入门(十五):ADO 应用转换为 ADO.NET
  4. 利用 async amp; await 的异步编程
  5. 十荟团关停全国业务 社区电商开启“降本增效”大突围
  6. android自定义进度条百分比跟着走,Android studio圆形进度条 百分数跟随变化
  7. JVM笔记1:Java内存模型及内存溢出
  8. nyoj--32--组合数
  9. 【转】一个小妙招能让你在服装上省下好多rmb
  10. linux那条命令可以装中文,Linux下的中文显示和支持常见问题解答
  11. VS2008的绿色精简版,只有VC2008部分
  12. 计算机找不到u 盘,u盘做系统找不到引导分区-U 盘装系统怎么分区?
  13. 日语开发java自我介绍,用日语自我介绍,这些你一定会用到
  14. 我和同事相处的心得。
  15. ddns-go搭建ddns(动态域名解析)
  16. 认知服务调用如何使用图片的DataURL
  17. 积极主动 个人愿景的原则_IT专家的愿景:您个人需要了解的内容
  18. VC6.0补丁Vs6sp6安装方法
  19. Minecraft 1.12.2模组开发(四十三) 自定义盾牌(Shield)
  20. 以Docker方式安装和配置Kong网关和Konga控制台

热门文章

  1. YTKNetwork使用application json方式传递参数
  2. Ogre 2011-11-29
  3. 2017-2018-2 20179209《网络攻防》第六周作业
  4. A 元素[HTML 4.01]
  5. 不能创建Outlook邮件的解决办法
  6. 财会小白的办公室自救指南
  7. js-new、object.create、bind的模拟实现【转载备忘】
  8. LNMP环境搭建(centos6.9+mysql5.7+php7.1+nginx1.10)
  9. HTML5实现手势屏幕解锁
  10. 时代亿信 认证墙-SID强身份认证产品