主机发现(一)

局域网使用arp以及syn扫描

1.操作流程

sudo ./nmap 192.168.10.100

Nmap done: 1 IP address (1 host up) scanned in 0.28 seconds

2.源码分析

main() ->
nmap_main()->
...
//命令行解析
-->parse_options(argc, argv)
...
//应用延时选项
-->apply_delayed_options() ->
...
-->validate_scan_lists(ports, o);
...
//参数和权限校验
//选择的扫描方式等是否有权限执行以及某些选项之间是否冲突
-->o.ValidateOptions();
...
-->gettoppts()
//1. 加载端口/服务/速率 信息 (nmap-services) 到 list中
//2. 扫描配置(默认/快速/指定端口数) 选择扫描的最大端口数
//3. 根据扫描类型(udp/tcp/sctp) 从list中选择端口(最多2.获取到的), 写入到对应的数组中(ports->tcp_ports/ports->udp_ports/ports->sctp_count)
// 有排除端口,则排除
//4. 然后将端口排序(升序)
....
//初始化port class
//实际上是将需要扫描的端口 映射到 静态全局变量 port_map 和 port_map_rev
....
//将端口随机化, o.randomize_ports默认为true, -r 关闭随机化
// 然后将随机化后的TCP受欢迎的端口放在开头 u16 pop_ports[] = {80, 23, 443, 21, 22, 25, 3389, 110, 445, 139,143, 53, 135, 3306, 8080, 1723, 111, 995, 993, 5900,1025, 587, 8888, 199, 1720,113, 554, 256};
....
....
// 开始主机发现

//如果没有设置扫描类型,则默认设置ipv4/ipv6
validate_scan_lists(ports, o)
//初始值为PINGTYPE_UNKNOWN
if (o.pingtype == PINGTYPE_UNKNOWN) {
if (o.isr00t) { //必须有root权限
if (o.pf() == PF_INET) {//ipv4
o.pingtype = DEFAULT_IPV4_PING_TYPES;//PINGTYPE_ICMP_PING|PINGTYPE_TCP|PINGTYPE_TCP_USE_ACK|PINGTYPE_TCP_USE_SYN|PINGTYPE_ICMP_TS
} else {//ipv6
o.pingtype = DEFAULT_IPV6_PING_TYPES;//PINGTYPE_ICMP_PING|PINGTYPE_TCP|PINGTYPE_TCP_USE_ACK|PINGTYPE_TCP_USE_SYN
}
//将端口写入对应的链表中(数组)
//80
getpts_simple(DEFAULT_PING_ACK_PORT_SPEC, SCAN_TCP_PORT,
&ports.ack_ping_ports, &ports.ack_ping_count);
//443
getpts_simple(DEFAULT_PING_SYN_PORT_SPEC, SCAN_TCP_PORT,
&ports.syn_ping_ports, &ports.syn_ping_count);
} else {
o.pingtype = PINGTYPE_TCP; // if nonr00t
getpts_simple(DEFAULT_PING_CONNECT_PORT_SPEC, SCAN_TCP_PORT,
&ports.syn_ping_ports, &ports.syn_ping_count);
}
}

//如果未设置扫描方式,则默认设置syn扫描/connect扫描
//检验各个参数是否可用以及冲突
o.ValidateOptions();
...
//因为什么扫描方式都未指定,这默认使用了syn扫描
if (!noportscan && !(TCPScan() || UDPScan() || SCTPScan() || ipprotscan)) {
if (isr00t)
synscan++;
else connectscan++;
// if (verbose) error("No TCP, UDP, SCTP or ICMP scantype specified, assuming %s scan. Use -sn if you really don't want to portscan (and just want to see what hosts are up).", synscan? "SYN Stealth" : "vanilla tcp connect()");
}
....
//参数和权限校验
//选择的扫描方式等是否有权限执行以及某些选项之间是否冲突

apply_delayed_options()
...
//o.ipprotscan 默认值为0; -sO , ip protocol scan, 指定此选项将置1
if (o.ipprotscan) {
if (o.portlist)
getpts(o.portlist, &ports);
else
getpts((char *) (o.fastscan ? "[P:0-]" : "0-"), &ports); // Default protocols to scan

//o.noportscan 默认值为false
// -sn Ping Scan - disable port scan; 
//-sL, List Scan - simply list targets to scan;
//设置这两个某一个选项后,o.noportscan置为true
} else if (!o.noportscan) { 
  //o.topportlevel 默认值为-1
  //      可通过--top-ports [1-] 或者 --port-ratio [0,1) 设置
  //o.portlist 默认为空
  //      可通过 -p port1,port2,U:port,T:port,S:port 设置
  gettoppts(o.topportlevel, o.portlist, &ports, o.exclude_portlist);
}

gettoppts() ->
...
//加载端口服务信息
nmap_services_init()
...
//将信息放入list中 std::list<service_node> services_by_ratio;
struct service_node sn;

      sn.s_name = cp_strdup(servicename);
      sn.s_port = portno;
     sn.s_proto = cp_strdup(proto);
     sn.s_aliases = NULL;
     sn.ratio = ratio;

     service_table[ps] = sn;

     services_by_ratio.push_back(sn);
     ...     

//获取扫描端口数
gettoppts()
...
//选择最大扫描端口数
if (level == -1) { // 默认为-1
if (portlist){
getpts(portlist, ports);
return;
}
//o.fastscan 默认fase
// 可通过 -F 或者 --servicedb filename 设置
if (o.fastscan) level = 100;
else level = 1000; //默认扫描1000个端口
}

//加载端口
gettoppts()
...
//指定速率的
if (level < 1) {
...
//和下面的逻辑一致
//只是根据速率来获取端口,而不是直接获取端口

//指定最大端口数/默认端口数    
  } else if (level >= 1) { 
    if (level > 65536)
      fatal("Level argument to gettoppts (%g) is too large", level);

    //numtcpports, numudpports, numsctpports 为全局变量,在加载nmap-services时计算的
    //分配空间
    if (o.TCPScan()) {
      ports->tcp_count = MIN((int) level, numtcpports);
      ports->tcp_ports = (unsigned short *)safe_zalloc(ports->tcp_count * sizeof(unsigned short));
    }
    if (o.UDPScan()) {
      ports->udp_count = MIN((int) level, numudpports);
      ports->udp_ports = (unsigned short *)safe_zalloc(ports->udp_count * sizeof(unsigned short));
    }
    if (o.SCTPScan()) {
      ports->sctp_count = MIN((int) level, numsctpports);
      ports->sctp_ports = (unsigned short *)safe_zalloc(ports->sctp_count * sizeof(unsigned short));
    }

    ports->prots = NULL;

    //填充端口
    for (i = services_by_ratio.begin(); i != services_by_ratio.end(); i++) {
      current = &(*i);
      if (ptsdata_initialized && !is_port_member(&ptsdata, current))
        continue;
      if (o.TCPScan() && strcmp(current->s_proto, "tcp") == 0 && ti < ports->tcp_count)
        ports->tcp_ports[ti++] = current->s_port;
      else if (o.UDPScan() && strcmp(current->s_proto, "udp") == 0 && ui < ports->udp_count)
        ports->udp_ports[ui++] = current->s_port;
      else if (o.SCTPScan() && strcmp(current->s_proto, "sctp") == 0 && si < ports->sctp_count)
        ports->sctp_ports[si++] = current->s_port;
    }

    //重置实际端口数
    if (ti < ports->tcp_count) ports->tcp_count = ti;
    if (ui < ports->udp_count) ports->udp_count = ui;
    if (si < ports->sctp_count) ports->sctp_count = si;
  } else
    fatal("Argument to gettoppts (%g) should be a positive ratio below 1 or an integer of 1 or higher", level);

    //排序
    if (ports->tcp_count > 1)
    qsort(ports->tcp_ports, ports->tcp_count, sizeof(unsigned short), &port_compare);

  if (ports->udp_count > 1)
    qsort(ports->udp_ports, ports->udp_count, sizeof(unsigned short), &port_compare);

  if (ports->sctp_count > 1)
    qsort(ports->sctp_ports, ports->sctp_count, sizeof(unsigned short), &port_compare);

//初始化端口映射
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);
}

//开始扫描
do {
ideal_scan_group_sz = determineScanGroupSize(o.numhosts_scanned, &ports);

while (Targets.size() < ideal_scan_group_sz) {
    //主机发现
  o.current_scantype = HOST_DISCOVERY;
  currenths = nexthost(&hstate, exclude_group, &ports, o.pingtype);
  if (!currenths)
    break;
    ...

    //各种扫描
    ...

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

//获取扫描目标地址
const char *HostGroupState::next_expression() ->
grab_next_host_spec()
...
//1.随机生成
get_random_unique_u32()
//2.从命令行参数获取
(optind < argc)? argv[optind++] : NULL);
//3.从文件中获取
read_host_from_file()

//解析目标地址
//将IP分成四段并设置成bitmap
int TargetGroup::parse_expr(const char *target_expr, int af)
...
this->netblock = NetBlock::parse_expr(target_expr, af);

//去掉需要排除的目标
hostInExclude((struct sockaddr *) &ss, sslen, exclude_group)

//建立扫描目标,开始扫描
setup_target(hs, &ss, sslen, pingtype)
...
//如果需要域名解析,进行域名解析
if (hs->current_group.is_resolved_address(ss)) {
if (hs->current_group.get_namedhost())
t->setTargetName(hs->current_group.get_resolved_name());
t->unscanned_addrs = hs->current_group.get_unscanned_addrs();
}
...

 if (o.RawScan()) {
   //1. 寻找路由
   //2.设置mac,mtu,以及接口等信息
  }

//查询路由
nmap_route_dst(ss, &rnfo) ->
...
route_dst(dst, rnfo, o.device, NULL) ->
...
route_dst_netlink(dst, rnfo, device, spoofss)
//通过netlinkAPI 获取路由信息
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);

        memset(&snl, 0, sizeof(snl));
        snl.nl_family = AF_NETLINK;
        rc = bind(fd, (struct sockaddr *) &snl, sizeof(snl));

        memset(buf, 0, sizeof(buf));
        nlmsg = (struct nlmsghdr *) buf;

        nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(*rtmsg));
        assert(nlmsg->nlmsg_len <= sizeof(buf));
        nlmsg->nlmsg_flags = NLM_F_REQUEST;
        nlmsg->nlmsg_type = RTM_GETROUTE;

        rtmsg = (struct rtmsg *) (nlmsg + 1);
        rtmsg->rtm_family = dst->ss_family;

//设置相关参数

//是否为直连
 if (rnfo.direct_connect) {
  t->setDirectlyConnected(true);
 } else {
  t->setDirectlyConnected(false);
  //不是直连,则设置下一跳地址
  t->setNextHop(&rnfo.nexthop, sizeof(rnfo.nexthop));
}
//设置网卡类型
t->setIfType(rnfo.ii.device_type);
//如果是以太网,设置源 mac
if (rnfo.ii.device_type == devt_ethernet) {
  if (o.spoofMACAddress())
    t->setSrcMACAddress(o.spoofMACAddress());
  else
    t->setSrcMACAddress(rnfo.ii.mac);
}

//设置源地址
t->setSourceSockAddr(&rnfo.srcaddr, sizeof(rnfo.srcaddr));
if (hs->current_batch_sz == 0) /* Because later ones can have different src addy and be cut off group */
  o.decoys[o.decoyturn] = t->source();
//设置接口名字  
t->setDeviceNames(rnfo.ii.devname, rnfo.ii.devfullname);
//设置mtu
t->setMTU(rnfo.ii.mtu);

//对于直连设备 发送arp
if (hs->hostbatch[0]->ifType() == devt_ethernet &&
hs->hostbatch[0]->af() == AF_INET &&
hs->hostbatch[0]->directlyConnected() &&
o.sendpref != PACKET_SEND_IP_STRONG &&
o.implicitARPPing) {
arpping(hs->hostbatch, hs->current_batch_sz);
arpping_done = true;
}

arpping()
...
for (targetno = 0; targetno < num_hosts; targetno++) {
//初始化超时信息
initialize_timeout_info(&hostbatch[targetno]->to);
...
//将目标加入到集合中
targets.push_back(hostbatch[targetno]);
}
//开始扫描
if (!targets.empty()) {
if (targets[0]->af() == AF_INET)
//ipv4 进行arp扫描
ultra_scan(targets, NULL, PING_SCAN_ARP);
else
ultra_scan(targets, NULL, PING_SCAN_ND);
}

ultra_scan()
...
increment_base_port()
...
//加载udp协议相关的payload数据(nmap-payloads)
//将数据放入到static std::map<struct proto_dport, struct payload> payloads;
init_payloads()

...
//使用待扫描的目标以及端口,扫描类型初始化扫描类
UltraScanInfo USI(Targets, ports, scantype);

//调用构造函数
UltraScanInfo(std::vector<Target > &Targets, struct scan_lists pts, stype scantype) {
Init(Targets, pts, scantype);
}

//构造函数中调用Init进行初始化
void UltraScanInfo::Init(std::vector<Target > &Targets, struct scan_lists pts, stype scantp) {
...
//获取当前时间
gettimeofday(&now, NULL);
...
//生成随机数
seqmask = get_random_u32();
...
//构建扫描进度对象
SPM = new ScanProgressMeter(scantype2str(scantype));
send_rate_meter.start(&now);
//初始化扫描类型
tcp_scan = udp_scan = sctp_scan = prot_scan = false;
ping_scan = noresp_open_scan = ping_scan_arp = ping_scan_nd = false;
...

set_default_port_state(Targets, scantype);

perf.init();

/ Keep a completed host around for a standard TCP MSL (2 min) /
completedHostLifetime = 120000;
memset(&lastCompletedHostRemoval, 0, sizeof(lastCompletedHostRemoval));

// 将待扫描的目标放入 集合incompleteHosts中

for (targetno = 0; targetno < Targets.size(); targetno++) {
if (Targets[targetno]->timedOut(&now)) {
num_timedout++;
continue;
}

hss = new HostScanStats(Targets[targetno], this);
incompleteHosts.insert(hss);

}
numInitialTargets = Targets.size();
nextI = incompleteHosts.begin();

gstats = new GroupScanStats(this); / Peeks at several elements in USI - careful of order /
gstats->num_hosts_timedout += num_timedout;

pd = NULL;
rawsd = -1;
ethsd = NULL;

/ See if we need an ethernet handle or raw socket. Basically, it's if we
aren't doing a TCP connect scan, or if we're doing a ping scan that
requires it.
/
//如果是原始扫描,将打开raw socket
if (isRawScan()) {
if (ping_scan_arp || (ping_scan_nd && o.sendpref != PACKET_SEND_IP_STRONG) || ((o.sendpref & PACKET_SEND_ETH) &&
(Targets[0]->ifType() == devt_ethernet))) {
/ We'll send ethernet packets with dnet /
ethsd = eth_open_cached(Targets[0]->deviceName());
...
} else {
rawsd = nmap_raw_socket();
...
unblock_socket(rawsd);*/
ethsd = NULL;
}
}
}

Target::Target() {
Initialize();
}

void Target::Initialize() {
...
//pingprobe.type = 0
memset(&pingprobe, 0, sizeof(pingprobe));
pingprobe_state = PORT_UNKNOWN;
}

//循环未扫描完成的目标
while (!USI.incompleteHostsEmpty()) {
doAnyPings(&USI);
doAnyOutstandingRetransmits(&USI); // Retransmits from probes_outstanding
/ Retransmits from retry_stack -- goes after OutstandingRetransmits for
memory consumption reasons
/
doAnyRetryStackRetransmits(&USI);
doAnyNewProbes(&USI);
gettimeofday(&USI.now, NULL);
// printf("TRACE: Finished doAnyNewProbes() at %.4fs\n", o.TimeSinceStartMS(&USI.now) / 1000.0);
printAnyStats(&USI);
waitForResponses(&USI);
gettimeofday(&USI.now, NULL);
// printf("TRACE: Finished waitForResponses() at %.4fs\n", o.TimeSinceStartMS(&USI.now) / 1000.0);
processData(&USI);

if (keyWasPressed()) {
  // This prints something like
  // SYN Stealth Scan Timing: About 1.14% done; ETC: 15:01 (0:43:23 remaining);
  USI.SPM->printStats(USI.getCompletionFraction(), NULL);
  if (o.debugging) {
    /* Don't update when getting the current rates, otherwise we can get
       anomalies (rates are too low) from having just done a potentially
       long waitForResponses without sending any packets. */
    USI.log_current_rates(LOG_STDOUT, false);
  }

  log_flush(LOG_STDOUT);
}

}

doAnyNewProbes() ->
sendNextScanProbe() ->
sendArpScanProbe()

上一篇:树莓派4b安装ubuntu18.04并安装ros


下一篇:k8s创建deployment时出现错误ValidationError