Nmap源码分析(基本框架)
发布日期:2021-05-15 08:34:46 浏览次数:20 分类:原创文章

本文共 21761 字,大约阅读时间需要 72 分钟。

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


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


源码下载地址:


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扫描结果之间差异。


 





[plain]



  1. Nmap/  

  2. ├─docs(Nmap相关文档,包括License、usage说明及XMLschema文件等)  

  3. │  ├─licenses     

  4. │  └─man-xlate  

  5. ├─libdnet-stripped(libdnet:简单的网络接口开源库)  

  6. │  ├─config  

  7. │  ├─include  

  8. │  └─src  

  9. ├─liblinear(LIBLINEAR:负责大型线性分类的开源库)  

  10. │  └─blas  

  11. ├─liblua(Lua脚本语言源码库)  

  12. ├─libnetutil(Nmap实现的基本的网络实用函数)  

  13. ├─libpcap(开源的抓包代码库libpcap)  

  14. │  ├─bpf  

  15. │  ├─ChmodBPF  

  16. │  ├─lbl  

  17. │  ├─missing  

  18. │  ├─msdos  

  19. │  ├─NMAP_MODIFICATIONS  

  20. │  ├─packaging  

  21. │  ├─pcap  

  22. │  ├─SUNOS4  

  23. │  ├─tests  

  24. │  └─Win32  

  25. ├─libpcre(Perl兼容的正则表达式开源库libpcre)  

  26. ├─macosx(该目录负责支持苹果的操作系统MACOS X)  

  27. │  └─nmap.pmdoc  

  28. ├─mswin32(该目录负责支持Windows操作系统)  

  29. │  ├─lib  

  30. │  ├─license-format  

  31. │  ├─NET  

  32. │  ├─NETINET  

  33. │  ├─nsis  

  34. │  ├─OpenSSL  

  35. │  ├─pcap-include  

  36. │  ├─RPC  

  37. │  └─winpcap  

  38. ├─nbase(Nmap封装的基础使用程序库,包括string/path/random等)  

  39. ├─ncat(Ncat是Nmap项目组实现的新版的netcat:强大的网络工具)  

  40. │  ├─certs  

  41. │  ├─docs  

  42. │  └─test  

  43. ├─ndiff(Ndiff是用于比较Nmap扫描结果的实用命令)  

  44. │  ├─docs  

  45. │  └─test-scans  

  46. ├─nmap-update(负责Nmap更新相关操作)  

  47. ├─nping(Nping是Nmap项目组实现的新版的Hping:网络探测与构建packet)  

  48. │  └─docs  

  49. ├─nselib(Nmap使用Lua语言编写的常用的脚本库)  

  50. │  └─data  

  51. ├─nsock(Nmap实现的并行的SocketEvent处理库)  

  52. │  ├─include  

  53. │  └─src  

  54. ├─scripts(Nmap提供常用的扫描检查的lua脚本)  

  55. ├─todo(介绍Nmap项目将来开发的具体任务)  

  56. └─zenmap(Nmap的官方的图形界面程序,由python语言编写)  

  57.    ├─install_scripts  

  58.    ├─radialnet  

  59.    ├─share  

  60.    ├─test  

  61.    ├─zenmapCore  

  62.    └─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的其他模块。





[cpp]



  1. int nmap_main(int argc, char *argv[]) {  

  2.   int i;  

  3.   vector<Target *> Targets;  

  4.   time_t now;  

  5.   struct hostent *target = NULL;  

  6.   time_t timep;  

  7.   char mytime[128];  

  8.   addrset exclude_group;  

  9.   #ifndef NOLUA  

  10.   /* Only NSE scripts can add targets */  

  11.   NewTargets *new_targets = NULL;///NewTargets为Singleton模式,产生单个实例  

  12.   /* Pre-Scan and Post-Scan script results datastructure */  

  13.   ScriptResults *script_scan_results = NULL;  

  14.   #endif  

  15.   char **host_exp_group;    

  16.   int num_host_exp_groups;  

  17.   HostGroupState *hstate = NULL;  

  18.   unsigned int ideal_scan_group_sz = 0;  

  19.   Target *currenths;  

  20.   char *host_spec = NULL;  

  21.   char myname[MAXHOSTNAMELEN + 1];  

  22.   int sourceaddrwarning = 0; /* Have we warned them yet about unguessable 

  23.                                 source addresses? */  

  24.   unsigned int targetno;  

  25.   char hostname[MAXHOSTNAMELEN + 1] = "";  

  26.   struct sockaddr_storage ss;  

  27.   size_t sslen;  

  28.   char **fakeargv = NULL;  

  29.   

  30.   now = time(NULL);  

  31.   local_time = localtime(&now);  

  32.   ///设置错误log输出函数  

  33.   if(o.debugging)  

  34.     nbase_set_log(fatal,error);  

  35.   else  

  36.     nbase_set_log(fatal,NULL);  

  37.   

  38.   if (argc < 2 ) printusage(-1);  

  39.   

  40.   /* argv faking silliness */  

  41.   fakeargv = (char **) safe_malloc(sizeof(char *) * (argc + 1));  

  42.   for(i=0; i < argc; i++) {  

  43.     fakeargv[i] = strdup(argv[i]);  

  44.   }  

  45.   fakeargv[argc] = NULL;  

  46.   

  47.   Targets.reserve(100);  

  48. #ifdef WIN32  

  49.   win_pre_init();  

  50. #endif  

  51. ///调用parse_options进行命令参数的解析  

  52.   parse_options(argc, fakeargv);      

  53. ///在Linux下设置终端为只读非阻塞方式,在Windows平台为空函数。  

  54.   tty_init(); // Put the keyboard in raw mode  

  55. ///将解析命令时需要延迟执行的操作在此处处理  

  56.   apply_delayed_options();  

  57.   

  58. #ifdef WIN32  

  59. ///调用WSAStartup启动Winsock DLL,后续网络解析等需要用到。  

  60.   win_init();      

  61. #endif  

  62.   

  63. ///如果用户使用了参数--iflist,那么会在此处打印网卡和路由表信息,然后退出。  

  64. ///该选项对于显示指定发送网卡非常有帮助,可以提供基本的网络设备信息。  

  65.   if (delayed_options.iflist) {  

  66.     print_iflist();  

  67.     exit(0);  

  68.   }  

  69.   

  70. ///quashargv部分用于修改命令行参数,将程序名字更改为FAKE_ARGV(默认为“pine”),  

  71. ///并将剩余的各个参数都清空。  

  72. ///在命令中加入-q可实现quashargv功能。这最初是为了逃避ps等程序名称显示,便于隐蔽Nmap。  

  73. ///不过在Windows系统上并无实效。  

  74.   /* more fakeargv junk, BTW malloc'ing extra space in argv[0] doesn't work */  

  75.   if (o.quashargv) {  

  76.     size_t fakeargvlen = strlen(FAKE_ARGV), argvlen = strlen(argv[0]);  

  77.     if (argvlen < fakeargvlen)  

  78.       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");  

  79.     strncpy(argv[0], FAKE_ARGV, fakeargvlen);  

  80.     memset(&argv[0][fakeargvlen], '\0', strlen(&argv[0][fakeargvlen]));  

  81.     for(i=1; i < argc; i++)  

  82.       memset(argv[i], '\0', strlen(argv[i]));  

  83.   }  

  84. ///如果使用FTP bounce scan的扫描方式,那么需要首先保证该FTP网站是可以访问到的。  

  85. ///关于FTP bounce scan更多介绍,请参考:http://nmap.org/nmap_doc.html#bounce  

  86.   /* If he wants to bounce off of an FTP site, that site better damn well be reachable! */  

  87.   if (o.bouncescan) {  

  88.     if (!inet_pton(AF_INET, ftp.server_name, &ftp.server)) {  

  89.       if ((target = gethostbyname(ftp.server_name)))  

  90.         memcpy(&ftp.server, target->h_addr_list[0], 4);  

  91.       else {  

  92.         fatal("Failed to resolve FTP bounce proxy hostname/IP: %s",  

  93.               ftp.server_name);  

  94.       }  

  95.     } else if (o.verbose) {  

  96.       log_write(LOG_STDOUT, "Resolved FTP bounce attack proxy to %s (%s).\n",  

  97.                 ftp.server_name, inet_ntoa(ftp.server));  

  98.     }  

  99.   }  

  100. ///<Start--------------扫描信息输出-----------------Start>    

  101.   fflush(stdout);  

  102.   fflush(stderr);  

  103.   

  104.   timep = time(NULL);  

  105. ///准备将基本的扫描输出到文件与控制台中  

  106.   /* Brief info in case they forget what was scanned */  

  107.   Strncpy(mytime, ctime(&timep), sizeof(mytime));  

  108.   chomp(mytime);  ///去掉字符串末尾换行符  

  109.   char *xslfname = o.XSLStyleSheet();///XML样式表  

  110.   xml_start_document();  

  111.   if (xslfname) {  

  112.     xml_open_pi("xml-stylesheet");  

  113.     xml_attribute("href""%s", xslfname);  

  114.     xml_attribute("type""text/xsl");  

  115.     xml_close_pi();  

  116.     xml_newline();  

  117.   }  

  118.   

  119.   std::string command;  

  120.   if (argc > 0)  

  121.     command += fakeargv[0];  

  122.   for (i = 1; i < argc; i++) {  

  123.     command += " ";  

  124.     command += fakeargv[i];  

  125.   }  

  126.   

  127.   xml_start_comment();  

  128.   xml_write_escaped(" %s %s scan initiated %s as: %s ", NMAP_NAME, NMAP_VERSION, mytime, join_quoted(fakeargv, argc).c_str());  

  129.   xml_end_comment();  

  130.   xml_newline();  

  131.   

  132.   log_write(LOG_NORMAL|LOG_MACHINE, "# ");  

  133.   log_write(LOG_NORMAL|LOG_MACHINE, "%s %s scan initiated %s as: ", NMAP_NAME, NMAP_VERSION, mytime);  

  134.   log_write(LOG_NORMAL|LOG_MACHINE, "%s", command.c_str());  

  135.   log_write(LOG_NORMAL|LOG_MACHINE, "\n");  

  136.   

  137.   xml_open_start_tag("nmaprun");  

  138.   xml_attribute("scanner""nmap");  

  139.   xml_attribute("args""%s", join_quoted(fakeargv, argc).c_str());  

  140.   xml_attribute("start""%lu", (unsigned long) timep);  

  141.   xml_attribute("startstr""%s", mytime);  

  142.   xml_attribute("version""%s", NMAP_VERSION);  

  143.   xml_attribute("xmloutputversion", NMAP_XMLOUTPUTVERSION);  

  144.   xml_close_start_tag();  

  145.   xml_newline();  

  146.   

  147.   output_xml_scaninfo_records(&ports);  

  148.   

  149.   xml_open_start_tag("verbose");  

  150.   xml_attribute("level""%d", o.verbose);  

  151.   xml_close_empty_tag();  

  152.   xml_newline();  

  153.   xml_open_start_tag("debugging");  

  154.   xml_attribute("level""%d", o.debugging);  

  155.   xml_close_empty_tag();  

  156.   xml_newline();  

  157.   

  158.   /* Before we randomize the ports scanned, lets output them to machine 

  159.      parseable output */  

  160.   if (o.verbose) ///输出机器可以解析端口信息(grepable格式)  

  161.     output_ports_to_machine_parseable_output(&ports);  

  162.   

  163. #if defined(HAVE_SIGNAL) && defined(SIGPIPE)  

  164.   ///注册信号处理函数,这里只是直接忽略SIGPIPE信号。其具体实现为#define SIG_IGN       (void (*)(int))1  

  165.   signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE so our program doesn't crash because 

  166.                                of it, but we really shouldn't get an unexpected 

  167.                                SIGPIPE */  

  168. #endif  

  169.   ///检查配置的最大并发度是否在系统最大的套接字数量范围之内  

  170.   if (o.max_parallelism && (i = max_sd()) && i < o.max_parallelism) {  

  171.     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);  

  172.   }  

  173.   

  174.   if (o.debugging > 1) log_write(LOG_STDOUT, "The max # of sockets we are using is: %d\n", o.max_parallelism);  

  175.   

  176.   // At this point we should fully know our timing parameters  

  177.   if (o.debugging) {  

  178.     log_write(LOG_PLAIN, "--------------- Timing report ---------------\n");  

  179.     log_write(LOG_PLAIN, "  hostgroups: min %d, max %d\n", o.minHostGroupSz(), o.maxHostGroupSz());  

  180.     log_write(LOG_PLAIN, "  rtt-timeouts: init %d, min %d, max %d\n", o.initialRttTimeout(), o.minRttTimeout(), o.maxRttTimeout());  

  181.     log_write(LOG_PLAIN, "  max-scan-delay: TCP %d, UDP %d, SCTP %d\n", o.maxTCPScanDelay(), o.maxUDPScanDelay(), o.maxSCTPScanDelay());  

  182.     log_write(LOG_PLAIN, "  parallelism: min %d, max %d\n", o.min_parallelism, o.max_parallelism);  

  183.     log_write(LOG_PLAIN, "  max-retries: %d, host-timeout: %ld\n", o.getMaxRetransmissions(), o.host_timeout);  

  184.     log_write(LOG_PLAIN, "  min-rate: %g, max-rate: %g\n", o.min_packet_send_rate, o.max_packet_send_rate);  

  185.     log_write(LOG_PLAIN, "---------------------------------------------\n");  

  186.   }  

  187. ///<End--------------扫描信息输出-----------------End>  

  188.   

  189. ///<Start-------------端口与地址初始化-------------Start>  

  190.   /* Before we randomize the ports scanned, we must initialize PortList class. */  

  191.   if (o.ipprotscan)  

  192.     PortList::initializePortMap(IPPROTO_IP,  ports.prots, ports.prot_count);  

  193.   if (o.TCPScan())  

  194.     PortList::initializePortMap(IPPROTO_TCP, ports.tcp_ports, ports.tcp_count);  

  195.   if (o.UDPScan())  

  196.     PortList::initializePortMap(IPPROTO_UDP, ports.udp_ports, ports.udp_count);  

  197.   if (o.SCTPScan())  

  198.     PortList::initializePortMap(IPPROTO_SCTP, ports.sctp_ports, ports.sctp_count);  

  199.   

  200.   if (o.randomize_ports) {  

  201.     if (ports.tcp_count) {  

  202.       ///将端口进行随机打乱操作  

  203.       shortfry(ports.tcp_ports, ports.tcp_count);  

  204.       // move a few more common ports closer to the beginning to speed scan  

  205.       ///将常见的端口移动到前面,以便最快地发现有效的端口  

  206.       random_port_cheat(ports.tcp_ports, ports.tcp_count);  

  207.     }  

  208.     if (ports.udp_count)  

  209.       shortfry(ports.udp_ports, ports.udp_count);  

  210.     if (ports.sctp_count)  

  211.       shortfry(ports.sctp_ports, ports.sctp_count);  

  212.     if (ports.prot_count)  

  213.       shortfry(ports.prots, ports.prot_count);  

  214.   }  

  215.   ///exclude_group记录的是排除地址,如命令行nmap 192.168.1.1/24 --exclude 192.168.1.0-10  

  216.   ///扫描C类地址192.168.1.x,并排除其中192.168.1.0-192.168.1.10地址。  

  217.   ///addrset_init()将初始化排除地址组的链表头指针为NULL。  

  218.   addrset_init(&exclude_group);  

  219.   

  220.   /* lets load our exclude list */  

  221.   if (o.excludefd != NULL) {
    ///文件指定的排除地址  

  222.     load_exclude_file(&exclude_group, o.excludefd);  

  223.     fclose(o.excludefd);  

  224.   }  

  225.   if (o.exclude_spec != NULL) {
    ///命令行直接指定的排除地址  

  226.     load_exclude_string(&exclude_group, o.exclude_spec);  

  227.   }  

  228.   

  229.   if (o.debugging > 3)  ///若调试级别大于3,打印出排除地址信息  

  230.     dumpExclude(&exclude_group);  

  231. ///<End-------------端口与地址初始化-------------End>  

  232.   

  233.   

  234. ///<Start------NSE环境准备并执行pre-scripts--------Start>  

  235. #ifndef NOLUA  

  236.   if (o.scriptupdatedb) {  

  237.     o.max_ips_to_scan = o.numhosts_scanned; // disable warnings?  

  238.   }  

  239.   if (o.servicescan)  ///当配置了版本扫描时,会默认启动版本扫描脚本,位于NSE中version类别中  

  240.     o.scriptversion = 1;  

  241.   if (o.scriptversion || o.script || o.scriptupdatedb)  

  242.     open_nse();  ///开启NSE环境  

  243.   

  244.   /* Run the script pre-scanning phase */  

  245.   if (o.script) {  

  246.     new_targets = NewTargets::get();  ///分配实例或返回已有实例(Singleton模式)  

  247.     script_scan_results = get_script_scan_results_obj();  

  248.     script_scan(Targets, SCRIPT_PRE_SCAN);  

  249.     printscriptresults(script_scan_results, SCRIPT_PRE_SCAN);  

  250.     script_scan_results->clear();  

  251.   }  

  252. #endif  

  253. ///<End------NSE环境准备并执行pre-scripts--------End>  

  254.   

  255.   

  256. ///<Start------创建主机组状态,进入主循环--------Start>  

  257.   /* Time to create a hostgroup state object filled with all the requested 

  258.      machines. The list is initially empty. It is refilled inside the loop 

  259.      whenever it is empty. */  

  260.   ///分配字符串数组,用以保存各个主机表达式字符串的地址  

  261.   host_exp_group = (char **) safe_malloc(o.ping_group_sz * sizeof(char *));  

  262.   num_host_exp_groups = 0;  

  263.   

  264.   hstate = new HostGroupState(o.ping_group_sz, o.randomize_hosts,  

  265.                   host_exp_group, num_host_exp_groups);  

  266.   

  267.   do {  

  268.     ///确定最佳的host group的大小,该大小取决于扫描方式与网络速度。  

  269.     ideal_scan_group_sz = determineScanGroupSize(o.numhosts_scanned, &ports);  

  270.     ///<Start---------对host group进行主机发现----------Start>  

  271.     ///以下的while()将依次进行主机发现,确定主机是否在线。  

  272.     ///若该主机在线加入该host group,用于后续的操作。当数量达到最佳大小时,退出循环。  

  273.     while(Targets.size() < ideal_scan_group_sz) {  

  274.       o.current_scantype = HOST_DISCOVERY;  ///设置扫描状态:HOST_DICOVERY  

  275.       currenths = nexthost(hstate, &exclude_group, &ports, o.pingtype); ///主机发现的核心函数  

  276.       ///如果当前主机发现无法找到有效主机,那么会做以下尝试:  

  277.       ///1)更换主机表达式(host expressions)  

  278.       ///例如:nmap 192.168.1.1/24 10.10.30.55-100,192.168.1.x不能再发现主机时候,切换为10.30.55-100  

  279.       ///2)将执行脚本扫描时发现的主机,加入主机表达式组host_exp_group  

  280.       ///3) 建立新的主机组状态,并做最后的主机发现尝试  

  281.       if (!currenths) {  

  282.         /* Try to refill with any remaining expressions */  

  283.         /* First free the old ones */  

  284.         for(i=0; i < num_host_exp_groups; i++)  

  285.           free(host_exp_group[i]);  

  286.         num_host_exp_groups = 0;  

  287.         /* Now grab any new expressions */  

  288.         while(num_host_exp_groups < o.ping_group_sz &&   

  289.           (!o.max_ips_to_scan || o.max_ips_to_scan > o.numhosts_scanned + (int) Targets.size() + num_host_exp_groups) &&  

  290.           (host_spec = grab_next_host_spec(o.inputfd, o.generate_random_ips, argc, fakeargv))) {  

  291.             // For purposes of random scan  

  292.             host_exp_group[num_host_exp_groups++] = strdup(host_spec);  

  293.         }  

  294. #ifndef NOLUA  

  295.         /* Add the new NSE discovered targets to the scan queue */  

  296.         if (o.script) {  

  297.           if (new_targets != NULL) {  

  298.             while (new_targets->get_queued() > 0 && num_host_exp_groups < o.ping_group_sz) {  

  299.               std::string target_spec = new_targets->read();  

  300.               if (target_spec.length())  

  301.                 host_exp_group[num_host_exp_groups++] = strdup(target_spec.c_str());  

  302.             }  

  303.   

  304.             if (o.debugging > 3)  

  305.               log_write(LOG_PLAIN,  

  306.                   "New targets in the scanned cache: %ld, pending ones: %ld.\n",  

  307.                   new_targets->get_scanned(), new_targets->get_queued());  

  308.           }  

  309.         }  

  310. #endif  

  311.         if (num_host_exp_groups == 0)  ///当没有其他的主机表达式时,退出整个主机发现循环  

  312.           break;  

  313.         delete hstate;  

  314.         hstate = new HostGroupState(o.ping_group_sz, o.randomize_hosts,host_exp_group,  

  315.                         num_host_exp_groups);  

  316.         

  317.         /* Try one last time -- with new expressions */  

  318.         currenths = nexthost(hstate, &exclude_group, &ports, o.pingtype);  

  319.         if (!currenths)  

  320.           break;  

  321.       }  

  322.       

  323.       if (currenths->flags & HOST_UP && !o.listscan)   

  324.         o.numhosts_up++;  

  325.       

  326.       if ((o.noportscan && !o.traceroute  

  327. #ifndef NOLUA  

  328.       && !o.script  

  329. #endif  

  330.           ) || o.listscan) {  

  331.         ///当不进行端口扫描(-sn)并且没有指定traceroute和脚本的话,那么扫描就到此处就结束。  

  332.         ///或当进行列表扫描(-sL,只列举出主机IP,并不真正扫描)时,扫描也到此结束。        

  333.         /* We're done with the hosts */  

  334.         if (currenths->flags & HOST_UP || o.verbose) {  

  335.           xml_start_tag("host");  

  336.           write_host_header(currenths);  

  337.           printmacinfo(currenths);  

  338.           //  if (currenths->flags & HOST_UP)  

  339.           //  log_write(LOG_PLAIN,"\n");  

  340.           printtimes(currenths);  

  341.           xml_end_tag();  

  342.           xml_newline();  

  343.           log_flush_all();  

  344.         }  

  345.         delete currenths;  

  346.         o.numhosts_scanned++;  

  347.         continue;  

  348.       }  

  349.       ///若配置要伪造源IP地址(-S ip),将命令行中传入的地址写入当前主机源地址  

  350.       if (o.spoofsource) {  

  351.         o.SourceSockAddr(&ss, &sslen);  

  352.         currenths->setSourceSockAddr(&ss, sslen);  

  353.       }  

  354.         

  355.       ///如果主机状态为HOST_DOWN,那么需要根据配置考虑是否输出其状态  

  356.       ///输出条件:verbose级别大于0,并且没有指定openonly或已确定有开放端口。  

  357.       ///疑问:如果有open Ports,为什么此主机状态会是HOST_DOWN呢?  

  358.       /* I used to check that !currenths->weird_responses, but in some 

  359.          rare cases, such IPs CAN be port successfully scanned and even 

  360.          connected to */  

  361.       if (!(currenths->flags & HOST_UP)) {  

  362.         if (o.verbose && (!o.openOnly() || currenths->ports.hasOpenPorts())) {  

  363.           xml_start_tag("host");  

  364.           write_host_header(currenths);  

  365.           xml_end_tag();  

  366.           xml_newline();  

  367.         }  

  368.         delete currenths;  

  369.         o.numhosts_scanned++;  

  370.         continue;  

  371.       }  

  372.       ///如果是RawScan(即涉及到构建原始的packet的扫描方式,如SYN/FIN/ARP等等),  

  373.       ///需要设置套接字源IP地址  

  374.       if (o.RawScan()) {  

  375.         if (currenths->SourceSockAddr(NULL, NULL) != 0) {  

  376.           if (o.SourceSockAddr(&ss, &sslen) == 0) {  

  377.             ///若全局变量o中已有源IP地址,直接赋值给当前目标机  

  378.             currenths->setSourceSockAddr(&ss, sslen);  

  379.           } else {  

  380.             ///否则,需要重新查询、解析主机来获取源地址  

  381.             if (gethostname(myname, MAXHOSTNAMELEN) ||  

  382.                 resolve(myname, 0, 0, &ss, &sslen, o.af()) == 0)  

  383.               fatal("Cannot get hostname!  Try using -S <my_IP_address> or -e <interface to scan through>\n");   

  384.           

  385.             o.setSourceSockAddr(&ss, sslen);  

  386.             currenths->setSourceSockAddr(&ss, sslen);  

  387.             if (! sourceaddrwarning) {  

  388.               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>.",  

  389.                   inet_socktop(&ss));  

  390.                 sourceaddrwarning = 1;  

  391.             }  

  392.           }  

  393.         }  

  394.   

  395.         if (!currenths->deviceName())///网卡名字,在主机发现函数nexthost()中设置  

  396.           fatal("Do not have appropriate device name for target");  

  397.           

  398.         ///如果新发现的主机与该主机组类型不大相同,那么考虑将此主机放入新的主机组内。  

  399.         ///因为对主机分组是为了加快扫描速度,所以尽可能特征相似的主机组合在一起。  

  400.         ///流水线工作模式的扫描思想。  

  401.         /* Hosts in a group need to be somewhat homogeneous. Put this host in 

  402.            the next group if necessary. See target_needs_new_hostgroup for the 

  403.            details of when we need to split. */  

  404.         if (target_needs_new_hostgroup(Targets, currenths)) {  

  405.           returnhost(hstate);  

  406.           o.numhosts_up--;  

  407.           break;  

  408.         }  

  409.         ///设置IP诱骗时,将当前主机真实IP放入decoyturn位置。  

  410.         ///其他的诱骗IP地址在parse options时已经确定。  

  411.         o.decoys[o.decoyturn] = currenths->v4source();      

  412.       }  

  413.       ///将新发现的主机加入Targets向量  

  414.       Targets.push_back(currenths);          

  415.     }///一次分组的主机发现在此处结束,接下来执行端口扫描、服务侦测、OS侦测、脚本扫描等。  

  416.     ///<End---------对host group进行主机发现----------End>  

  417.       

  418.     if (Targets.size() == 0)///主机发现没有找到任何目标机时,退出主循环  

  419.       break/* Couldn't find any more targets */  

  420.       

  421.     // Set the variable for status printing  

  422.     o.numhosts_scanning = Targets.size();  

  423.       

  424.     // Our source must be set in decoy list because nexthost() call can  

  425.     // change it (that issue really should be fixed when possible)  

  426.     if (o.af() == AF_INET && o.RawScan())  

  427.       o.decoys[o.decoyturn] = Targets[0]->v4source();  

  428.       

  429.     /* I now have the group for scanning in the Targets vector */  

  430.   

  431.     if (!o.noportscan) {  

  432.       ///<Start---------端口扫描----------Start>  

  433.       ///针对用户指定的不同扫描方式,分别使用不同参数调用ultra_scan()  

  434.       ///ultra_scan()设计精巧,用统一的接口处理大多数的端口扫描  

  435.       // Ultra_scan sets o.scantype for us so we don't have to worry  

  436.       if (o.synscan)  

  437.         ultra_scan(Targets, &ports, SYN_SCAN);  

  438.         

  439.       if (o.ackscan)  

  440.         ultra_scan(Targets, &ports, ACK_SCAN);  

  441.         

  442.       if (o.windowscan)  

  443.         ultra_scan(Targets, &ports, WINDOW_SCAN);  

  444.         

  445.       if (o.finscan)  

  446.         ultra_scan(Targets, &ports, FIN_SCAN);  

  447.         

  448.       if (o.xmasscan)  

  449.         ultra_scan(Targets, &ports, XMAS_SCAN);  

  450.         

  451.       if (o.nullscan)  

  452.         ultra_scan(Targets, &ports, NULL_SCAN);  

  453.         

  454.       if (o.maimonscan)  

  455.         ultra_scan(Targets, &ports, MAIMON_SCAN);  

  456.         

  457.       if (o.udpscan)  

  458.         ultra_scan(Targets, &ports, UDP_SCAN);  

  459.         

  460.       if (o.connectscan)  

  461.         ultra_scan(Targets, &ports, CONNECT_SCAN);  

  462.         

  463.       if (o.sctpinitscan)  

  464.         ultra_scan(Targets, &ports, SCTP_INIT_SCAN);  

  465.         

  466.       if (o.sctpcookieechoscan)  

  467.         ultra_scan(Targets, &ports, SCTP_COOKIE_ECHO_SCAN);  

  468.         

  469.       if (o.ipprotscan)  

  470.         ultra_scan(Targets, &ports, IPPROT_SCAN);  

  471.         

  472.       /* These lame functions can only handle one target at a time */  

  473.       if (o.idlescan) {  

  474.         for(targetno = 0; targetno < Targets.size(); targetno++) {  

  475.            o.current_scantype = IDLE_SCAN;  

  476.            keyWasPressed(); // Check if a status message should be printed  

  477.            idle_scan(Targets[targetno], ports.tcp_ports,  

  478.                                   ports.tcp_count, o.idleProxy, &ports);  

  479.         }  

  480.       }  

  481.       if (o.bouncescan) {  

  482.         for(targetno = 0; targetno < Targets.size(); targetno++) {  

  483.            o.current_scantype = BOUNCE_SCAN;  

  484.            keyWasPressed(); // Check if a status message should be printed  

  485.           if (ftp.sd <= 0) ftp_anon_connect(&ftp);  

  486.           if (ftp.sd > 0) bounce_scan(Targets[targetno], ports.tcp_ports,  

  487.                                       ports.tcp_count, &ftp);  

  488.         }  

  489.       }  

  490.       ///<End---------端口扫描----------End>  

  491.       ///<Start------服务与版本扫描--------Start>  

  492.       if (o.servicescan) {  

  493.         o.current_scantype = SERVICE_SCAN;   

  494.   

  495.         service_scan(Targets);  

  496.       }  

  497.   

  498.       if (o.servicescan) {  

  499.         /* This scantype must be after any TCP or UDP scans since it 

  500.          * get's it's port scan list from the open port list of the current 

  501.          * host rather than port list the user specified. 

  502.          */  

  503.         for(targetno = 0; targetno < Targets.size(); targetno++)  

  504.           pos_scan(Targets[targetno], NULL, 0, RPC_SCAN);  

  505.       }  

  506.       ///<End------服务与版本扫描--------End>  

  507.     }  

  508.     ///操作系统扫描  

  509.     if (o.osscan){  

  510.       OSScan os_engine;  

  511.       os_engine.os_scan(Targets);  

  512.     }  

  513.     ///若需要路径追踪,在此处调用traceroute获取路径  

  514.     if (o.traceroute)  

  515.       traceroute(Targets);  

  516.     ///脚本扫描  

  517. #ifndef NOLUA  

  518.     if(o.script || o.scriptversion) {  

  519.       script_scan(Targets, SCRIPT_SCAN);  

  520.     }  

  521. #endif  

  522.   

  523.     ///<Start------输出扫描结果--------Start>  

  524.     for(targetno = 0; targetno < Targets.size(); targetno++) {  

  525.       currenths = Targets[targetno];  

  526.       /* Now I can do the output and such for each host */  

  527.       if (currenths->timedOut(NULL)) {  

  528.         xml_open_start_tag("host");  

  529.         xml_attribute("starttime""%lu", (unsigned long) currenths->StartTime());  

  530.         xml_attribute("endtime""%lu", (unsigned long) currenths->EndTime());  

  531.         xml_close_start_tag();  

  532.         write_host_header(currenths);  

  533.         xml_end_tag(); /* host */  

  534.         xml_newline();  

  535.         log_write(LOG_PLAIN,"Skipping host %s due to host timeout\n",  

  536.             currenths->NameIP(hostname, sizeof(hostname)));  

  537.         log_write(LOG_MACHINE,"Host: %s (%s)\tStatus: Timeout",   

  538.             currenths->targetipstr(), currenths->HostName());  

  539.       } else {  

  540.         /* --open means don't show any hosts without open ports. */  

  541.         if (o.openOnly() && !currenths->ports.hasOpenPorts())  

  542.           continue;  

  543.   

  544.         xml_open_start_tag("host");  

  545.         xml_attribute("starttime""%lu", (unsigned long) currenths->StartTime());  

  546.         xml_attribute("endtime""%lu", (unsigned long) currenths->EndTime());  

  547.         xml_close_start_tag();  

  548.         write_host_header(currenths);  

  549.         printportoutput(currenths, ¤ths->ports);  

  550.         printmacinfo(currenths);  

  551.         printosscanoutput(currenths);  

  552.         printserviceinfooutput(currenths);  

  553. #ifndef NOLUA  

  554.         printhostscriptresults(currenths);  

  555. #endif  

  556.         if (o.traceroute)  

  557.           printtraceroute(currenths);  

  558.         printtimes(currenths);  

  559.         log_write(LOG_PLAIN|LOG_MACHINE,"\n");  

  560.         xml_end_tag(); /* host */  

  561.         xml_newline();  

  562.       }  

  563.     }  

  564.     log_flush_all();  

  565.     ///<End------输出扫描结果--------End>  

  566.     o.numhosts_scanned += Targets.size();  

  567.     

  568.     /* Free all of the Targets */  

  569.     while(!Targets.empty()) {  

  570.       currenths = Targets.back();  

  571.       delete currenths;  

  572.       Targets.pop_back();  

  573.     }  

  574.     o.numhosts_scanning = 0;  

  575.   } while(!o.max_ips_to_scan || o.max_ips_to_scan > o.numhosts_scanned);  

  576.   ///当指定的扫描数量没有达到已经扫描数量,继续循环  

  577.   ///<End------创建主机组状态,进入主循环--------End>  

  578.     

  579.   ///执行post-script,释放分配的资源  

  580. #ifndef NOLUA  

  581.   if (o.script) {     

  582.     script_scan(Targets, SCRIPT_POST_SCAN);  

  583.     printscriptresults(script_scan_results, SCRIPT_POST_SCAN);  

  584.     script_scan_results->clear();  

  585.     delete new_targets;  

  586.     new_targets = NULL;  

  587.   }  

  588. #endif  

  589.   

  590.   delete hstate;  

  591.     

  592.   addrset_free(&exclude_group);  

  593.   hstate = NULL;  

  594.   

  595.   /* Free host expressions */  

  596.   for(i=0; i < num_host_exp_groups; i++)  

  597.     free(host_exp_group[i]);  

  598.   num_host_exp_groups = 0;  

  599.   free(host_exp_group);  

  600.   

  601.   if (o.inputfd != NULL)  

  602.     fclose(o.inputfd);  

  603.   

  604.   printdatafilepaths();  

  605.   

  606.   printfinaloutput();  

  607.   

  608.   free_scan_lists(&ports);  

  609.   

  610.   eth_close_cached();  

  611.   

  612.   if (o.release_memory) {  

  613.     /* Free fake argv */  

  614.     for(i=0; i < argc; i++)  

  615.       free(fakeargv[i]);  

  616.     free(fakeargv);  

  617.   

  618.     nmap_free_mem();  

  619.   }  

  620.   return 0;  




上一篇:关于metasploit的一些架构目录
下一篇:总结一下关于postgresql数据库的使用心得

发表评论

最新留言

初次前来,多多关照!
[***.217.46.12]2025年04月22日 11时12分45秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章