端口扫描是Nmap的核心功能,用于确定目标机的端口状态(开放、关闭、过滤等),也为Nmap的服务与版本扫描、OS扫描、脚本扫描提供基本的指引信息。所以,深入理解端口扫描的实现对分析其他的扫描方式很有帮助。

1     简单回顾

  首先简要回顾一下端口扫描的原理以及命令行选项。

1.1    扫描原理

  Nmap提供的10多种类型的端口扫描方法,如TCP SYN/ACK/FIN/Xmas/NULL/ Windows/Connect,FTP Bounce, Idle scan, UDP port unreachable/ UDP recv_from, IP protocol,SCTPINIT/SCTP COOKIE ECHO等扫描方式。原理是基于网络数据包的特征或者网络编程API来判定端口的状态。例如,以TCP SYN方式(Nmap的默认的TCP扫描方式)为例来简单地回顾端口扫描原理。

TCP SYN探测到端口关闭:

TCP SYN探测到端口开放:

  更多扫描原理的细节,请参考电子书:Secrets of NetworkCartography

1.2    端口扫描用法

扫描方式指定选项

-sS/sT/sA/sW/sM:指定使用 TCPSYN/Connect()/ACK/Window/Maimon scans的方式来对目标主机进行扫描。-sU: 指定使用UDP扫描方式确定目标主机的UDP端口状况。 -sN/sF/sX: 指定使用TCP Null, FIN, and Xmas scans秘密扫描方式来协助探测对方的TCP端口状态。--scanflags <flags>: 定制TCP包的flags。-sI <zombiehost[:probeport]>: 指定使用idle scan方式来扫描目标主机(前提需要找到合适的zombie host)-sY/sZ: 使用SCTP INIT/COOKIE-ECHO来扫描SCTP协议端口的开放的情况。-sO: 使用IP protocol 扫描确定目标机支持的协议类型。-b <FTP relay host>: 使用FTP bounce scan扫描方式</code></pre>端口指定选项<pre><code class="language-diff">-p <port ranges>: 扫描指定的端口实例:-p22; -p1-65535; -p U:53,111,137,T:21-25,80,139,8080,S:9(其中T代表TCP协议、U代表UDP协议、S代表SCTP协议)-F: Fast mode – 快速模式,仅扫描TOP 100的端口-r: 不进行端口随机打乱的操作(如无该参数,nmap会将要扫描的端口以随机顺序方式扫描,以让nmap的扫描不易被对方防火墙检测到)。--top-ports <number>:扫描开放概率最高的number个端口(nmap的作者曾经做过大规模地互联网扫描,以此统计出网络上各种端口可能开放的概率。以此排列出最有可能开放端口的列表,具体可以参见文件:nmap-services。默认情况下,nmap会扫描最有可能的1000个TCP端口)--port-ratio <ratio>: 扫描指定频率以上的端口。与上述--top-ports类似,这里以概率作为参数,让概率大于--port-ratio的端口才被扫描。显然参数必须在在0到1之间,具体范围概率情况可以查看nmap-services文件。

详细端口扫描用法介绍

官方手册:http://nmap.org/book/man-port-scanning-techniques.html

2     实现框架

  下面我们从端口扫描部分的文件组织、核心类介绍与代码流程框架三个角度来整体把握其要点。

2.1    文件组织

  scan_engine.cc/scan_engine.h

  Nmap的端口扫描功能主要在scan_engine.h/scan_engine.cc中实现,其中scan_engine.cc为C++文件,共有6000多行代码(目前版本Nmap6.0)。实现端口扫描所需的基础类及调用接口。

  nmap-services

  端口扫描功能会用到nmap-services数据库文件,此文件描述互联网上常见的注册端口对应的服务名称,以及该端口开放的频率和注释信息。端口开放的频率是Nmap项目组对互联网上大量的计算机进行扫描,统计出的每个端口开放的概率值,所以根据此概率可以很方便指定扫描覆盖的范围。例如,使用--top-ports 3000,可以让Nmap扫描概率排名前3000的端口;使用--port-ratio 0.001,可以让Nmap扫描开放概率在0.001以上的所有端口。

2.2    核心类分析

  Nmap端口扫描部分涉及到多个Class,这些类将端口扫描过程中涉及到各种数据及接口有效地管理起来。

2.2.1   UltraScanInfo

  UltraScanInfo是统一管理端口扫描过程信息的类。该类的对象主要用在ultra_scan()函数中。下面简要列举UltraScanInfo类中的关键点:

  1. 执行的扫描类型详细信息(如TCP SYN/UDP/ARP扫描)
  2. massping状态信息(massping用于主机发现阶段对大多数情况的目标PING操作)
  3. now时间:记录当前时间值
  4. 主机组扫描状态信息(GroupScanStats *gstats),记录扫描的一组主机的整体扫描状态。
  5. 性能状态
  6. 未完成主机列表:放置还没有扫描完成的主机
  7. 已完成主机列表:放置已经扫描完毕的主机
  8. 扫描进度与发包频率度量:用于记录扫描进度和发包频率
  9. 接口函数:对主机列表进行操作;获取扫描状态信息(完成百分数、未完成主机数等)。

  上述的未完成主机列表与已完成主机列表是核心部分,因为其他的操作都围绕此两张列表进行操作。

  /* Any function whichmesses with (removes elements from)incompleteHosts mayhave to manipulate nextI */list<HostScanStats*> incompleteHosts;/* Hosts are moved fromincompleteHosts to completedHosts as they arecompleted. We keepthem around because sometimes responses come back verylate, after weconsider a host completed. */list<HostScanStats*> completedHosts;

2.2.2   GroupScanStats

  GroupScanStats用于管理端口扫描过程中一组主机的整体统计状态。通常情况下,为了加快执行速度,Nmap是将一组主机(根据扫描类型,可能由64或128等等不同数量主机组成)统一进行端口扫描的。所以,此处就会用到GroupScanStats来对整个主机组进行状态的管理。

  下面简单介绍该类的核心作用:

  1. 记录超时值(timeout):等到何时未完成就放弃此次扫描
  2. 检查是否允许发送:sendOK(when),判断在when的时刻是否允许发送数据包
  3. 统计所有主机的活动的探测包:还没有完成的探测过程,例如该探测还没有收到回复包,还在持续等待。
  4. 时序及超时信息:拥塞窗口、阀值、期待回复包数量等信息。
  5. 扫描的主机数量与探测包数量:包括完成主机与未完成主机;探测包数量为每个主机扫描预计的探测包数量(每个端口一个探测包,探测包数量也就等于端口数量)。
  6. 扫描频率控制:send_no_earlier_than与send_no_later_than,控制扫描发送包的频率。对应到用户输入的命令行选项:--max-rate和--min-rate。
  7. 全局的ping host记录:在主机发现过程进行全局PING时候,对该主机进行PING操作。该主机为最新发现的在线主机。

2.2.3   HostScanStats

  HostScanStats类管理单个的目标主机的扫描统计状态。在上述的UltraScanInfo类中的两个列表:未完成列表与已完成列表中存放的都是HostScanStats指针类型数据,列表中每一项都指向HostScanStats对象,因此可从列表快速获取到单个主机的详细信息。

  该类中包含单个主机的以下的信息:

  1. 指向目标主机(Target对象)的指针:用于获取目标主机基本控制信息。
  2. 剩余端口数量:记录扫描主机还剩余多少主机没有被扫描。
  3. massping状态:记录发送探测包类型及各种探测方式对应的下一个端口的索引值
  4. 超时时间和过期时间:探测包多长时间算超时;完全放弃一个探测包的时间。
  5. 获取下一个最早超时的探测包:该主机可能发送大量的探测包,其中多个探测包都可能超时,在此处可以返回其中最早超时的探测包。
  6. 销毁未完成的探测包:销毁单个包或销毁全部为完成的探测包(OutstandingProbes)。
  7. 未完成的探测包列表:list<UltraProbe*> probes_outstanding;该列表记录当前处于活动状态但没有完成的探测包。
  8. 探测包工作台:vector<probespec>probe_bench;该向量用于存放达到try_no(尝试次数)允许最大的尝试次数并在等待继续尝试的探测包。若允许继续增加尝试次数,那么在探测包工作台中的Probe被移动重试栈区(retry_stack)中。
  9. 重试栈区:vector<probespec>retry_stack;该向量用于保存从探测包工作台上移动出来的探测包。
  10. 判断扫描是否结束:接口函数bool completed();
  11. 允许的尝试次数:allowedTryno()函数返回主机允许最大尝试次数。
  12. 完成的端口数量:已经完成探测的端口数量
  13. 扫描延时:包括扫描延时信息、频率限制检测、增加扫描延时等内容。

  其中,未完成探测包列表、探测包工作台、重试栈区是该类的核心部分,针对目标主机进行所有的探测包由以上三个列表或向量来统一管理。

2.2.4   UltraProbe

  UltraProbe类是用于管理每一个探测包的信息。

  该类主要包含以下几类关键信息:

  1. 设置探测包类型:比如setIP(),setConnect(),setARP()等。
  2. 源端口与目的端口:根据探测包类型,返回对应端口配置。
  3. 获取协议相关信息:例如IPID,TCP seqnumber, SCTPvtag等信息。
  4. 重试次数、超时信息、重传等信息:与探测包进行过程紧密相关的属性。

2.3    代码流程

  Nmap的端口扫描主要从nmap_main()函数中的ultra_scan()函数进入,根据配置的不同扫描类型,ultra_scan()中进行不同的处理。两个特殊扫描方式idle scan和FTP bounce scan是单独实现的处理函数,不借用ultra_scan()函数。

这里,我们主要分析ultra_scan()函数方式进行的扫描,因为这是最通用最有代表性的。

2.3.1   代码流程图

2.3.2   流程解析

  以TCPSYN为例,在nmap_main()中调用ultra_scan(Targets, &ports,SYN_SCAN),此处传入参数目标主机Targets(是vector容器保存的);ports是struct scan_list类型的指针,指向解析出来的端口列表;SYN_SCAN是预先定义的枚举值,让ultra_scan能够辨别出扫描类型。

  进入ultra_scan()后,第一个重要步骤是加载UDP扫描需要的负载,即UDP探测方式需要发送的包的内容(该内容从数据库文件nmap-payloads中读取出来)。此步骤在init_payloads()中完成。

  判断是否是在Windows平台扫描环回接口(loopback),若是,则打印出提示信息:Windows平台无法支持扫描自己的环回接口。

  创建UltraScanInfo对象,使用Targets,ports,scantype初始化。

  开始嗅探(begin_sniffer),启动libpcap库对网络数据包进行嗅探,以便能够接收到目标机的回复包。主要调用libpcap的API:pcap_open_live打开实时嗅探,然后再设置libpcap抓包的过滤器(Filter),最终调用pcap_setfilter()具体设置。

  进入端口扫描的主循环,只要UltraScanInfo中的未完成列表不为空,都将继续执行循环结构体。

  1)       首先进行PING探测操作,发送必要的探测包到目标机特定端口。

  2)       然后重传所有未完成的探测包。处于outstanding状态,并允许重传的探测包将在这里进行重传。

  3)       重传retry_stack中探测包。retry_stack中探测包是从探测包工作台(probe bench)中移动出来的,是重新获得重传机会的探测包。所以在此处检查retry_stack中是否有探测包等待重传。

  4)       检查是否需要传输新的探测包。Nmap扫描时候,是对一批主机扫描同一个端口,然后推进到下一个端口进行扫描。所以,在此处检查是否有主机需要进行新的端口的探测。

  5)       获取时间,并打印出端口扫描状态。

  6)       等待接收回复包。根据不同类型的探测方式,等待接收不同类型的回复包。这里是通过libpcap的API:pcap_next()函数来读取到回复包的。

  7)       获取时间,并对接收到的数据进行处理。

  8)       检测是否按键,若有允许的按键按下(v增加verbose,V减少verbose;d增加debugginglevel,D减少debugginglevel;p是打开packet trace,P是关闭packet trace),则执行相应功能,否则仅仅打印出扫描进度信息。

  退出循环后,首先停止发送频率度量,USI->send_rate_meter.stop(&USI->now),因为此时真正的扫描已经结束,所以此处可以停止度量。

  保存计算出的超时信息,并将扫描过程的详细信息与调试信息打印出来。

  删除UltraScanInfo对象,该对象仅仅用于每一个ultra_scan()函数调用。

3     代码注释

/* 3rd generation Nmap scanning function. Handles most Nmap port scan types.The parameter to gives group timing information, and if it is not NULL,changed timing information will be stored in it when the function returns. Itexists so timing can be shared across invocations of this function. If to isNULL (its default value), a default timeout_info will be used. */
void ultra_scan(vector<Target *> &Targets, struct scan_lists *ports, stype scantype, struct timeout_info *to) {UltraScanInfo *USI = NULL;///扫描信息控制类o.current_scantype = scantype;///标记当前扫描类型,用于输出init_payloads(); /* Load up _all_ payloads into a mapped table */if (Targets.size() == 0) {return;}#ifdef WIN32if (scantype != CONNECT_SCAN && Targets[0]->ifType() == devt_loopback) {log_write(LOG_STDOUT, "Skipping %s against %s because Windows does not support scanning your own machine (localhost) this way.\n", scantype2str(scantype), Targets[0]->NameIP());return;}
#endif// Set the variable for status printingo.numhosts_scanning = Targets.size();startTimeOutClocks(Targets);USI = new UltraScanInfo(Targets, ports, scantype);/* Use the requested timeouts. */if (to != NULL)USI->gstats->to = *to;if (o.verbose) {char targetstr[128];bool plural = (Targets.size() != 1);if (!plural) {(*(Targets.begin()))->NameIP(targetstr, sizeof(targetstr));} else Snprintf(targetstr, sizeof(targetstr), "%d hosts", (int) Targets.size());log_write(LOG_STDOUT, "Scanning %s [%d port%s%s]\n", targetstr, USI->gstats->numprobes, (USI->gstats->numprobes != 1)? "s" : "", plural? "/host" : "");}///begin_sniffer()开启libpcap并设置pcap filter,以便接收目标主机返回的数据包begin_sniffer(USI, Targets);while(!USI->incompleteHostsEmpty()) { ///向目标机发送探测包(probe)doAnyPings(USI); ///重传未完成探测过程的数据包doAnyOutstandingRetransmits(USI); // Retransmits from probes_outstanding/* Retransmits from retry_stack -- goes after OutstandingRetransmits formemory 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()中接收libpcap中抓取到的数据包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 getanomalies (rates are too low) from having just done a potentiallylong waitForResponses without sending any packets. */USI->log_current_rates(LOG_STDOUT, false);}log_flush(LOG_STDOUT);}}USI->send_rate_meter.stop(&USI->now);/* Save the computed timeouts. */if (to != NULL)*to = USI->gstats->to;///输出详细信息与调试信息if (o.verbose) {char additional_info[128];if (USI->gstats->num_hosts_timedout == 0)if (USI->ping_scan) {Snprintf(additional_info, sizeof(additional_info), "%lu total hosts",(unsigned long) Targets.size());} else {Snprintf(additional_info, sizeof(additional_info), "%lu total ports",(unsigned long) USI->gstats->numprobes * Targets.size());}else Snprintf(additional_info, sizeof(additional_info), "%d %s timed out",USI->gstats->num_hosts_timedout, (USI->gstats->num_hosts_timedout == 1)? "host" : "hosts");USI->SPM->endTask(NULL, additional_info);}if (o.debugging)USI->log_overall_rates(LOG_STDOUT);if (o.debugging > 2 && USI->pd != NULL)pcap_print_stats(LOG_PLAIN, USI->pd);delete USI;USI = NULL;
}

Nmap源码分析(端口扫描)相关推荐

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

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

  2. Nmap源码分析(基本框架)

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

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

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

  4. Nmap源码分析(主机发现)

    ​Nmap在进行真正的端口扫描之前,通常需要确定目标主机是否在线(主机发现过程),以免发送大量探测包到不在线的主机.主机发现作为Nmap的基本功能之一,用户也可以单独运用.例如,仅仅需要确定局域网内哪 ...

  5. Nmap源码分析(整体架构)

    整体架构 功能目录 docs :相关文档 libdnet-stripped :开源网络接口库 liblinear:开源大型线性分类库 liblua:开源Lua脚本语言库 libnetutil:基本的网 ...

  6. Nmap源码分析(操作系统扫描)

    Nmap第四个核心功能是操作系统侦测,包括识别出操作系统类型.版本号.目标机硬件平台类型及附加信息(如TCP序号产生方式.IPID产生方式.启动时间等).目前Nmap 拥有丰富的系统指纹数据库 (nm ...

  7. Android wpa_supplicant源码分析--bss扫描结果

    1 扫描方式 手机扫描结果的获取有两种方式:被动和主动 1,AP隔固定时间会发送Beacon帧,Beacon帧中有AP的SSID BSSID等基本信息,手机接收到Beacon帧就认为搜索到该AP创建的 ...

  8. Netty源码分析系列之服务端Channel的端口绑定

    扫描下方二维码或者微信搜索公众号菜鸟飞呀飞,即可关注微信公众号,Spring源码分析和Java并发编程文章. 微信公众号 问题 本文内容是接着前两篇文章写的,有兴趣的朋友可以先去阅读下两篇文章: Ne ...

  9. Wifi模块—源码分析Wifi热点扫描2(Android P)

    一 前言 这次接着讲Wifi工程流程中的Wifi热点扫描过程部分的获取扫描结果的过程,也是Wifi扫描过程的延续,可以先看前面Wifi扫描的分析过程. Wifi模块-源码分析Wifi热点扫描(Andr ...

最新文章

  1. C#条件判断-根据条件判断要走的路-if结构
  2. ARP协议全面实战手册1.1.2设置过滤器
  3. 一蹴而就的解释是什么_健身会让我们成为什么样的人?
  4. 在项目中增加task定时任务
  5. C# 连接 Oracle 的几种方式
  6. 很好的理解遗传算法的样例
  7. python sorted下标_【面试划重点】-- Python常见知识点
  8. HTML5触摸事件演化tap事件
  9. CV算法助理 | 华为外包招聘
  10. lambert(兰伯特)投影 应用工具_全息投影技术,在哪些场地可以用到
  11. 再见了SpringMVC!这个框架有点厉害,甚至干掉了Servlet!
  12. 深度学习入门资料整理
  13. 学计算机应用的必懂知识,学习计算机应用基础心得体会
  14. Cartopy理解变换和投影关键字
  15. Android反编译工具的使用-Android Killer
  16. react 中子路由(route)或二级路由如何配置?
  17. 半小时教你做大转盘游戏(QT篇)
  18. Rancher 添加主机失败的解决方法
  19. 流利说懂你英语结班学习总结
  20. 热带雨林的昆虫繁殖题解

热门文章

  1. taro 打包微信小程序运行失败(一)
  2. jquery ajax(实现单独提交某个form)
  3. 《我是一只IT小小鸟》读后感
  4. C/C++ 框架,类库,资源集合
  5. 请问如何更改dedecms“文件保存目录”的字符限制 ?
  6. 不能创建Outlook邮件的解决办法
  7. php正则的练习(持续跟新)
  8. Setting Up YARN High Availability
  9. mac 安装 RabbitMQ
  10. java.lang.IllegalArgumentException和org.apache.catalina.LifecycleException