Nmap分析

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


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

如上图对网关进行操作系统扫描,可以看到探测出的结果:由MAC地址推断网关是TP-LINK公司产品,设备类型是WAP(无线接入点,即无线路由器)或打印机,运行的是Wind River公司的VxWorks操作系统,OS CPE描述为cpe:/o:windriver:vxworks,网络距离是一跳(1 hop)。

1   简单引入

下面从OS扫描原理、命令行选项角度进行简单回顾。

1.1  扫描原理

Nmap使用TCP/IP协议栈指纹来识别不同的操作系统和设备。在RFC规范中,有些地方对TCP/IP的实现并没有强制规定,由此不同的TCP/IP方案中可能都有自己的特殊的处理方式。Nmap主要是根据这些细节上的差异来判断操作系统的类型的。

具体实现方式如下:

  • Nmap内部包含了2600多已知系统的指纹特征(在文件nmap-os-db文件中)。将此指纹数据库作为进行指纹对比的样本库。
  • 分别挑选一个open和closed的端口,向其发送经过精心设计的TCP/UDP/ICMP数据包,根据返回的数据包生成一份系统指纹。
  • 将探测生成的指纹与nmap-os-db中指纹进行对比,查找匹配的系统。如果无法匹配,以概率形式列举出可能的系统。

1.2  命令行选项

OS侦测的用法简单,Nmap提供的命令比较少。

[plain] view plaincopy

  1. -O: 指定Nmap进行OS侦测。 
  2. --osscan-limit: 限制Nmap只对确定的主机的进行OS探测(至少需确知该主机分别有一个open和closed的端口)。 
  3. --osscan-guess: 大胆猜测对方的主机的系统类型。由此准确性会下降不少,但会尽可能多为用户提供潜在的操作系统。 

2   实现框架

下面主要从文件组织、核心类、代码流程的角度来分析操作系统侦测的实现框架。

2.1  文件组织

2.1.1流程文件

操作系统侦测功能主要在os_scan2.h/os_scan2.cc文件中实现(os_scan.h/ os_scan.cc是第一代操作系统侦测的代码,目前默认不使用其流程);IPv6的操作系统探测过程主要FPengine.h/FPengine.cc文件中实现。

2.1.2数据库文件

Nmap操作系统指纹数据库文件nmap-os-db,里面包含了2600多种操作系统与设备的指纹。所谓的指纹,即由特定的回复包提取出的数据特征。

下面摘取其中片段,简单了解其结构。

#Windows 7 Professional Version 6.1 Build 7600

Fingerprint MicrosoftWindows 7 Professional

ClassMicrosoft | Windows | 7 | general purpose

CPEcpe:/o:microsoft:windows_7::professional

SEQ(SP=FC-106%GCD=1-6%ISR=108-112%TI=I%II=I%SS=S%TS=7)

OPS(O1=M5B4NW8ST11%O2=M5B4NW8ST11%O3=M5B4NW8NNT11%O4=M5B4NW8ST11%O5=M5B4NW8ST11%O6=M5B4ST11)

WIN(W1=2000%W2=2000%W3=2000%W4=2000%W5=2000%W6=2000)

ECN(R=Y%DF=Y%T=7B-85%TG=80%W=2000%O=M5B4NW8NNS%CC=N%Q=)

T1(R=Y%DF=Y%T=7B-85%TG=80%S=O%A=S+%F=AS%RD=0%Q=)

T2(R=N)

T3(R=N)

T4(R=N)

T5(R=Y%DF=Y%T=7B-85%TG=80%W=2000%S=Z%A=S+%F=AR%O=%RD=0%Q=)

T6(R=N)

T7(R=N)

U1(DF=N%T=7B-85%TG=80%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)

IE(DFI=N%T=7B-85%TG=80%CD=Z)

以上是Windows 7 professional版本的指纹特征。

第一行为注释行,说明此指纹对应的操作系统与版本。

Fingerprint关键字定义一个新的指纹,紧随其后的是指纹名字Microsoft Windows 7Professional。

Class行用于指定该指纹所属的类别,依次指定该系统的vendor(生产厂家), OS family(系统类别), OS generation(第几代操作系统), and device type(设备类型),如此处Vendor为Microsoft, OS family为Windows,OS generation为7,设备类型为通用设备(普通PC或服务器)。

接下来是CPE行,此行非常重要,使用CPE(Common Platform Enumeration,通用平台枚举)格式描述该系统的信息。以标准的CPE格式来描述操作系统类型,便于Nmap与外界信息的交换,比如可以很快从网上开源数据库查找到CPE描述的操作系统具体信息。

关于CPE标准介绍:http://cpe.mitre.org/

此处作为指纹描述字段的CPE格式如下:

cpe:/::::::

接下来从SEQ到IE的13行都是具体指纹数据描述行,在对比指纹时,就是对比这13行里面的具体数据,如果匹配则目标机为指纹所描述的系统类型。

SEQ描述顺序产生方式;OPS描述TCP包中可选字段的值;WIN描述TCP包的初始窗口大小;ECN(Explicit Congestion Notification)描述TCP明确指定拥塞通知时的特征;T1-T7描述TCP回复包的字段特征;U1描述向关闭的UDP发包产生的回复的特征;IE描述向目标机发送ICMP包产生的特征。

2.2  核心类

下面简要介绍操作系统扫描部分涉及到的Class。

2.2.1OSScan

OSScan是管理操作系统扫描过程的类,将IPv4和IPv6的操作系统扫描过程封装起来,为Nmap主程序提供统一的调用接口os_scan()。

以下是该类主要的内容:

  1. 提供操作系统扫描接口os_scan()
  2. 提供重置函数接口(初始化必要的变量)。
  3. 保存ip的协议版本
  4. 执行分块与扫描过程,确定并发执行的数量
  5. 针对IPv4进行操作系统扫描
  6. 针对IPv6进行操作系统扫描

[cpp] view plaincopy

  1. /** This is the class that performs OS detection (both IPv4 and IPv6).
  2.   * Using it is simple, just call os_scan() passing a list of targets.
  3.   * The results of the detection will be stored inside the supplied
  4.   * target objects. */
  5. class OSScan { 
  6. private: 
  7. int ip_ver;             /* IP version for the OS Scan (4 or 6) */
  8. int chunk_and_do_scan(std::vector &Targets, int family); 
  9. int os_scan_ipv4(std::vector &Targets); 
  10. int os_scan_ipv6(std::vector &Targets); 
  11. public: 
  12.    OSScan(); 
  13.    ~OSScan(); 
  14. void reset(); 
  15. int os_scan(std::vector &Targets); 
  16. }; 
2.2.2OsScanInfo

OsScanInfo类整体管理全部主机的扫描过程,其中维护操作系统扫描未完成列表。

下面是其中主要的内容:

  1. 未完成扫描列表std::listincompleteHosts。
  2. 扫描起始时间starttime.
  3. 未完成列表访问接口,读取总数、获取下一个、 查找主机、重置迭代器。
  4. 移除已经完成的主机。
2.2.3HostOsScanInfo

HostOsScanInfo管理单个主机的操作系统扫描的信息。

主要包含以下具体内容:

  1. 对应的目标机Target *target.
  2. 被包含的OsScanInfo对象地址。
  3. 当前主机产生的指纹信息FingerPrint **FPs
  4. 记录是否超时、是否完成
  5. 单个OS扫描每一轮(one round)的统计信息HostOsScanStats *hss。(同一个主机可能被扫描多个回合,所以这里使用该对象来管理每一轮扫描的信息)
  6. 指纹扫描结果与匹配情况。

[cpp] view plaincopy

  1. /* The overall os scan information of a host:
  2. *  - Fingerprints gotten from every scan round;
  3. *  - Maching results of these fingerprints.
  4. *  - Is it timeout/completed?
  5. *  - ... */
  6. class HostOsScanInfo { 
  7. public: 
  8.   HostOsScanInfo(Target *t, OsScanInfo *OSI); 
  9.   ~HostOsScanInfo(); 
  10.   Target *target;       /* The target                                  */
  11.   FingerPrintResultsIPv4 *FPR; 
  12.   OsScanInfo *OSI;      /* The OSI which contains this HostOsScanInfo  */
  13.   FingerPrint **FPs;    /* Fingerprints of the host                    */
  14.   FingerPrintResultsIPv4 *FP_matches; /* Fingerprint-matching results      */
  15. bool timedOut;        /* Did it time out?                            */
  16. bool isCompleted;     /* Has the OS detection been completed?        */
  17.   HostOsScanStats *hss; /* Scan status of the host in one scan round   */
  18. }; 
2.2.4HostOsScanStats

HostOsScanStats管理每个主机每一轮OS扫描的统计信息。

内容概括起来如下:

  1. 扫描探测包的管理
  2. 以太网信息管理
  3. 指纹信息的管理
  4. TCP序号、IPID、启动时间等信息管理
  5. 其他杂项信息
2.2.5OFProbe

OFProbe管理OS扫描过程需要的探测包信息,该对象中本身只包含用于构建探测包的关键属性,而不包含探测包本身。另外该对象也包含时序信息。

该对象主要在HostOsScanStats中使用。

2.3  代码流程

在nmap.cc中的nmap_main()函数如果检测用户配置了OS扫描选项(或-A选项),那么将启动扫描系统扫描功能。Nmap将会创建OSScan对象,并调用OSScan::os_scan()进入详细的探测过程。

2.3.1代码流程图

2.3.2流程解析

OSScan::os_scan()的执行流程非常简单,只有短短30行代码。

首先将传入的Targets参数依据地址类型划分两个小组,IPv4和IPv6。因为对于两类的地址扫描的方式不同。

随后调用os_scan_ipv4()做IPv4的操作系统扫描过程。

然后调用os_scan_ipv6()做IPv6的操作系统扫描过程。

判断ipv4和ipv6两类操作系统扫描执行的结果,返回最终结果。

因为os_scan_ipv4()才是完成ipv4类的操作系统扫描真正的地方,这里我们也简要描述其过程。

首先,定义未匹配主机管理列表listunMatchedHosts,用于管理超时未匹配的主机。

随后,初始化扫描性能变量scan_perforamance_vars,以便对整个操作系统扫描过程时序与性能进行控制。

创建OsScanInfo对象、并初始化必要的时间值。

begin_sniffer()打开libpcap,设置相应filter,进行回复包的监听。

随后进入主循环过程,直到所有的主机都完成扫描,才退出循环。下面是主要循环步骤:

A.      根据进行的扫描次数,适当休眠

B.       准备该轮扫描的所需的环境,清理垃圾数据并初始化必要的变量

C.       做顺序产生测试(Sequence generationtests),提取指纹的SEQ/OPS/WIN/T1几行数据。

D.      做TCP/UDP/ICMP综合探测,提取指纹数据。

E.       处理此轮探测的结果,匹配相应的系统指纹,移除已完成。

F.       移除超时不匹配的主机到unMatchedHosts列表中。

退出循环后,将unMatchedHosts列表中主机移动到未完成列表,然后统一对其进行最接近指纹匹配。

返回扫描执行结果。

3   代码注释

[cpp] view plaincopy

  1. /* This function performs the OS detection. It processes the supplied list of
  2. * targets and classifies it into two groups: IPv4 and IPv6 targets. Then,
  3. * OS detection is carried out for those two separate groups. It returns
  4. * OP_SUCCESS on success or OP_FAILURE in case of error. */
  5. int OSScan::os_scan(vector &Targets) { 
  6.   vector ip4_targets; ///IPv4类型地址的目标机
  7.   vector ip6_targets; ///IPv6类型地址的目标机
  8. int res4 = OP_SUCCESS, res6 = OP_SUCCESS; 
  9. /* Make sure we have at least one target */
  10. if (Targets.size() <= 0) 
  11. return OP_FAILURE; 
  12. /* Classify targets into two groups: IPv4 and IPv6 */
  13. ///先根据地址将目标机划分到不同向量里,因为两类目标机扫描过程不同
  14. for (size_t i = 0; i < Targets.size(); i++) { 
  15. if (Targets[i]->af() == AF_INET6) 
  16.           ip6_targets.push_back(Targets[i]); 
  17. else
  18.           ip4_targets.push_back(Targets[i]); 
  19.   } 
  20. /* Do IPv4 OS Detection */
  21. ///在os_scan_ipv4()函数中具体实现IPv4的操作系统探测的过程
  22. if (ip4_targets.size() > 0) 
  23.       res4 = this->os_scan_ipv4(ip4_targets); 
  24. /* Do IPv6 OS Detection */
  25. ///在os_scan_ipv6()函数中具体实现IPv6的操作系统探测的过程
  26. if (ip6_targets.size() > 0) 
  27.       res6 = this->os_scan_ipv6(ip6_targets); 
  28. /* If both scans were succesful, return OK */
  29. if (res4 == OP_SUCCESS && res6 == OP_SUCCESS) 
  30. return OP_SUCCESS; 
  31. else
  32. return OP_FAILURE; 
  33. /* Performs the OS detection for IPv4 hosts. This method should not be called
  34. * directly. os_scan() should be used instead, as it handles chunking so
  35. * you don't do too many targets in parallel */
  36. ///IPv4的操作系统探测的实现函数,由os_scan()来调用。
  37. int OSScan::os_scan_ipv4(vector &Targets) { 
  38. int itry = 0; 
  39. /* Hosts which haven't matched and have been removed from incompleteHosts because
  40.    * they have exceeded the number of retransmissions the host is allowed. */
  41.   list unMatchedHosts; ///记录超时或超过最大重传而未匹配的主机扫描信息
  42. /* Check we have at least one target*/
  43. if (Targets.size() == 0) { 
  44. return OP_FAILURE; 
  45.   } 
  46.   perf.init();///初始化扫描性能变量
  47. ///操作系统扫描的管理对象,维护未完成扫描列表std::list incompleteHosts;
  48.   OsScanInfo OSI(Targets); 
  49. if (OSI.numIncompleteHosts() == 0) { 
  50. /* no one will be scanned */
  51. return OP_FAILURE; 
  52.   } 
  53. ///设置起始时间与超时
  54.   OSI.starttime = o.TimeSinceStart(); 
  55.   startTimeOutClocks(&OSI); 
  56. ///创建HOS对象,负责管理单个主机的具体扫描过程
  57.   HostOsScan HOS(Targets[0]); 
  58. /* Initialize the pcap session handler in HOS */
  59. ///打开libpcap,设置对应的BPF filter,以便接收目标的回复包
  60.   begin_sniffer(&HOS, Targets); 
  61. while (OSI.numIncompleteHosts() != 0) { 
  62. if (itry > 0) 
  63.       sleep(1); 
  64. if (itry == 3) 
  65.       usleep(1500000); /* Try waiting a little longer just in case it matters */
  66. if (o.verbose) { 
  67. char targetstr[128]; 
  68. bool plural = (OSI.numIncompleteHosts() != 1); 
  69. if (!plural) { 
  70.     (*(OSI.incompleteHosts.begin()))->target->NameIP(targetstr, sizeof(targetstr)); 
  71.       } else Snprintf(targetstr, sizeof(targetstr), "%d hosts", (int) OSI.numIncompleteHosts()); 
  72.       log_write(LOG_STDOUT, "%s OS detection (try #%d) against %s\n", (itry == 0)? "Initiating" : "Retrying", itry + 1, targetstr); 
  73.       log_flush_all(); 
  74.     } 
  75. ///准备第itry轮的OS探测:删除陈旧信息、初始化必要变量
  76.     startRound(&OSI, &HOS, itry); 
  77. ///执行顺序产生测试(发送6个TCP探测包,每隔100ms一个)
  78.     doSeqTests(&OSI, &HOS); 
  79. ///执行TCP/UDP/ICMP探测包测试
  80.     doTUITests(&OSI, &HOS); 
  81. ///对该轮探测的结果做指纹对比,获取OS扫描信息
  82.     endRound(&OSI, &HOS, itry); 
  83. ///将超时未匹配的主机移动到unMatchedHosts列表中
  84.     expireUnmatchedHosts(&OSI, &unMatchedHosts); 
  85.     itry++; 
  86.   } 
  87. /* Now move the unMatchedHosts array back to IncompleteHosts */
  88. ///对没有找到匹配的主机,将之移动的未完成列表,并查找出最接近的指纹(以概率形式展现给用户)
  89. if (!unMatchedHosts.empty()) 
  90.     OSI.incompleteHosts.splice(OSI.incompleteHosts.begin(), unMatchedHosts); 
  91. if (OSI.numIncompleteHosts()) { 
  92. /* For hosts that don't have a perfect match, find the closest fingerprint
  93.      * in the DB and, if we are in debugging mode, print them. */
  94.     findBestFPs(&OSI); 
  95. if (o.debugging > 1) 
  96.       printFP(&OSI); 
  97.   } 
  98. return OP_SUCCESS; 
  99. }  

你可能感兴趣的:(工具使用)