Nmap 源码学习三 nmap_main主程序分析

主体程序位置在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, &currenths->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

Nmap 源码学习三 nmap_main主程序分析的相关文章

Nmap 源码学习三 软件简单使用

软件安装环境是win7.使用Zenmap, nmap6.49BETA2 扫描主机端口 nmap -T4 -A -v 192.168.0.207 输出结果: 扫描整个子网 nmap 192.168.1.1/24 扫描多个目标: nmap 192.168.1.2 192.168.1.5 从文件加载ip列表扫描 nmap -iL target.txt 查看扫描的主机列表 nmap -sL 192.168.1.1/24 扫描特定端口 nmap -p80,21,8080 192.168.0.207 半开扫

Nmap 源码学习二 整体架构

目录功能: docs :相关文档 libdnet-stripped :开源网络接口库 liblinear:开源大型线性分类库 liblua:开源Lua脚本语言库 libnetutil:基本的网络函数 libpcap:开源抓包库 libpcre:开源正则表达式库 macosx:xcode项目文件 mswin32:vs项目文件 nbase:Nmap封装的基础使用函数库 ncat:netcat网络工具,由Nmap实现 ndiff:比较Nmap扫描结果的实用命令 nmap-update:负责Nmap更新

[spring源码学习]三、IOC源码——自定义配置文件读取

一.环境准备 在文件读取的时候,第9步我们发现spring会根据标签的namespace来选择读取方式,联想spring里提供的各种标签,比如<aop:xxx>等应该会有不同的读取和解析方式,这一章我们来找一个其他文件,了解下spring自定义标签和配置的读取流程. 手边正好有一套dubbo的源码,因此为了区别与spring的原生读取,就使用它来进行分析. 首先spring的配置文件中我们需要加上标签的namespace <?xml version="1.0" enc

HashMap与HashTable源码学习及效率比较分析

一.个人学习后的见解: 首先表明学习源码后的个人见解,后续一次依次进行分析: 1.线程安全:HashMap是非线程安全的,HashTable是线程安全的(HashTable中使用了synchronized关键字进行控制),HashMap对应的线程安全的有concurrentHashMap,但如果不用concurrentHashMap的话,也可以只用Collections.synchronizedMap(Map)进行转换. 2.key值为null时的不同处理方式:HashMap允许key值为nul

Bottle 框架源码学习 三

def run(app=None, server='wsgiref', host='127.0.0.1', port=8080,         interval=1, reloader=False, quiet=False, plugins=None,         debug=None, **kargs): 今天要学习一下bottle里是怎样打印debug信息的 run函数的倒数第二个参数是debug,默认为None try:     if debug is not None: _debu

nmap 源码学习一 下载编译

简介 nmap是一款开源免费的扫描工具,学习其源码有助于加深对网络基础知识的理解.我对网络的基础知识一直是了解却不深入,以前也用c写过探测操作系统指纹的程序.现在为了深入学习网络安全知识,决定系统化学习一些工具及其原理.使用.nmap是首选的学习对象. SVN检出地址 官方编译文档 一开始我在windows下编译的,但win2012以上版本遇到很多问题.后来改为了Ubuntu下编译使用. Ubuntu下编译过程 1.下载安装包 我下载的是nmap-6.49BETA1.tar.bz2 2.解压 b

lua_gc 源码学习三

我们晓得,lua 对外的 API 中,统统个 gc 打交道的都经过lua_gc.C 说话构建体系时,普通不讲计划模式.但模式仍是存在的.若要按<计划模式>中的分类,这应当归于 Facade 形式.代码在 lapi.c 的 895 行: LUA_API int lua_gc (lua_State *L, int what, int data) { int res = 0; global_State *g; lua_lock(L); g = G(L); switch (what) { case L

Spring源码学习(三)默认标签的解析

默认标签的解析分为四种:import,alias,bean,beans,在下面函数中进行 1 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { 2 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { 3 importBeanDefinitionResource(ele); 4 } 5 else if (delegate.n

Java多线程之JUC包:ReentrantLock源码学习笔记

若有不正之处请多多谅解,并欢迎批评指正. 请尊重作者劳动成果,转载请标明原文链接: http://www.cnblogs.com/go2sea/p/5627539.html ReentrantLock是JUC包提供的一种可重入独占锁,它实现了Lock接口.与Semaphore类似,ReentrantLock也提供了两种工作模式:公平模式&非公平模式,也是通过自定义两种同步器FairSync&NonfairSync来实现的. 源代码: /* * ORACLE PROPRIETARY/CONF