当前位置:首页 > 嵌入式 > 嵌入式微处理器
[导读]大家好,我是轩辕。前几天,我在读者群里提了一个问题:这一下,大家总算停止了灌水(这群人都不用上班的,天天划水摸鱼),开始讨论起这个问题来了。有人说,通过User-Agent可以看,我直接给了一个狗头。然后发现不对劲,改口说,可以通过HTTP响应的Server字段看,比如看到像这种...

大家好,我是轩辕。前几天,我在读者群里提了一个问题:

这一下,大家总算停止了灌水(这群人都不用上班的,天天划水摸鱼),开始讨论起这个问题来了。

有人说,通过User-Agent可以看,我直接给了一个狗头。

然后发现不对劲,改口说,可以通过HTTP响应的Server字段看,比如看到像这种的,那肯定Windows无疑了:

HTTP/1.1 200 OK
Content-Type: text/html
Last-Modified: Fri, 23 Aug 2019 01:02:08 GMT
Accept-Ranges: bytes
ETag: "e65855634e59d51:0"
Server: Microsoft-IIS/8.0
X-Powered-By: ASP.NET
Date: Fri, 23 Jul 2021 06:02:38 GMT
Content-Length: 1375
还有人说,可以通过URL路径来判断,如果大小写敏感就是Linux,不敏感就是Windows。

于是,我进一步提高了难度,如果连Web服务也没有,只有一个TCP Server呢?

这时又有人说,可以通过ping这个IP,查看ICMP报文中的TTL值,如果是xxx就是xx系统,如果是yyy就是yy系统···(不过,有些情况下也不是太准确)

从TCP重传说起

今天,想跟大家探讨的是另外一种方法。这个方法的思路来源于前几天被删掉的那篇文章。就是日本网络环境下访问不了极客时间的问题,当时抓包看到的情况是这样的:

看到了服务器后面在不断的尝试重发了吗?当时我就想到了一个问题:

服务器到底会重传好多次呢?

众所周知,TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议。

其中,可靠性的一个重要体现就是它的超时重传机制。

TCP的通信中有一个确认机制,我发给你了数据,你得告诉我你收到没,这样双方才能继续通信下去,而这个确认机制是通过序列号SEQ和确认号ACK来实现的。

简单来说,当发送方给接收方发送了一个报文,而接收方在规定的时间内没有给出应答,那么发送方将认为有必要重发。

那具体最多重发多少次呢?关于这一点,RFC中关于TCP的文档并未明确规定出来,只是给了一些在总超时时间上的参考,这就导致不同的操作系统在实现这一机制的时候可能会有一些差异。

于是,我进一步想到了另一个问题:

会不会不同操作系统重传次数不一样,这样就能通过这一点来判断操作系统了呢?

然后,我翻看了《TCP/IP详解·卷1》,试图在里面寻找答案。果然,这本神书从来没有让我失望过:

上面这段大意是说RFC标准中建议有两个参数R1和R2来控制重传的次数,Linux中,这俩参数可以这样看:

cat /proc/sys/net/ipv4/tcp_retries1
cat /proc/sys/net/ipv4/tcp_retries2
tcp_retries1默认值是3,tcp_retries2默认值是15。

但需要特别注意的是,并不是最多重传3次或者15次,Linux内部有一套算法,这两个值是算法中非常重要的参数,而不是重传次数本身。具体的重传次数还与RTO有关系,具体的算法,有兴趣的朋友可以看看这篇文章:聊一聊重传次数(http://perthcharles.github.io/2015/09/07/wiki-tcp-retries/)

总体来说,在Linux上重传的次数不是一个固定值,而是不同的连接根据tcp_retries2RTO计算出来的一个动态值,不固定。

而在Windows上,也有一个变量来控制重传次数,可以在注册表中设定它:

键值路径:
HKLM\System\CurrentControlSet\Services\Tcpip\Parameters

键值名:
TcpMaxDataRetransmissions

默认值:5
我手里有一份Windows XP的源码,在实现协议栈的驱动tcpip.sys的部分中,也印证了这个信息:

从注册表中读取键值
没有读到的默认值
不过,就目前的信息来看,由于Linux的重传次数是不固定的,还没法用这个重传次数来判断操作系统。

TCP之SYN ACK的重传

就在我想要放弃的时候,我再一次品读了《TCP/IP详解·卷1》中的那段话,发现另一个信息:TCP的重传在建立连接阶段和数据传输阶段是不一样的!

上面说到的重传次数限制,针对的是TCP连接已经建立完成,在数据传输过程中发生超时重传后的重传次数情况描述。

而在TCP建立连接的过程中,也就是三次握手的过程中,发生超时重传,它的次数限定是有另外一套约定的。

Linux:

在Linux中,另外还有两个参数来限定建立连接阶段的重传次数:

cat /proc/sys/net/ipv4/tcp_syn_retries
cat /proc/sys/net/ipv4/tcp_synack_retries
tcp_syn_retries限定作为客户端的时候发起TCP连接,最多重传SYN的次数,Linux3.10中默认是6,Linux2.6中是5。

tcp_synack_retries限定作为服务端的时候收到SYN后,最多重传SYN ACK的次数,默认是5。

重点来关注这个tcp_synack_retries,它指的就是TCP的三次握手中,服务端回复了第二次握手包,但客户端一直没发来第三次握手包时,服务端会重发的次数。

我们知道,在正常情况下,TCP的三次握手是这个样子的:

但是,如果客户端不给服务端发起第三个包,那么服务端就会重发它的第二次握手包,情况就会变成下面这样:

所以,这个tcp_synack_retries实际上规定的就是上面这种情况下,服务端会重传SYN ACK的次数。

为了进一步验证,我使用Python写了一段代码,用来手动发送TCP报文,里面使用的发包库是scapy。

下面的这段代码,我向目标IP的指定端口只发送了一个SYN包:

def tcp_syn_test(ip, port):

    # 第一次握手,发送SYN包
    # 请求端口和初始序列号随机生成
    # 使用sr1发送而不用send发送,因为sr1会接收返回的内容
    ans = sr1(IP(dst=ip) / TCP(dport=port, sport=RandShort(), seq=RandInt(), flags='S'), verbose=False)
用上面这段代码,向一台Linux的服务器发送,抓包来看一下:

实际验证,服务器确实重传了5次SYN ACK报文。

一台服务器说明不了问题,我又多找了几个,结果都是5次。

再来看一下Linux的源码中关于这个次数的定义:

接下来,看一下Windows上的情况。

Windows

前面说过,在注册表HKLM\System\CurrentControlSet\Services\Tcpip\Parameters目录下有一个叫TcpMaxDataRetransmissions的参数,可以用来控制数据重传次数。不过,那是限定的数据传输阶段的重传次数。

根据MSDN上的介绍,除了这个参数,还有另一个参数用来限制上面SYN ACK重传的次数,它就是TcpMaxConnectResponseRetransmissions

而且有趣的是,和Linux上的默认值不一样,Windows上的默认值是2。

这就有意思了,通过这一点,就能把Windows和Linux区分开来。

我赶紧用虚拟机中的XP上跑了一个nginx,测试了一下:

果然是2次,随后我又换了一个Windows Server 2008,依旧是2次。

为了进一步验证,我通过注册表把这个值设定成了4:

再来试一下:

重传次数果然变成了4次了。

接下来,在手中的Windows XP源码中去印证这个信息:

果然,不管是从实验还是从源码中都得到了同一个结论:

Linux上,SYN ACK默认重传5次。

Windows上,SYN ACK默认重传2次。

总结

如果一个IP开启了基于TCP的服务,不管是不是HTTP服务,都可以通过向其发送SYN包,观察其回应来判断对方是一个Linux操作系统还是一个Windows操作系统。

当然,这种方法的局限性还是挺大的。

首先,本文只介绍了一些默认的情况,但TCP的重传次数是可以更改的,如果网络管理员更改了这个数值,判断的结果就不准确了。

其次,对于有些网络服务器开启了防DDoS功能,测试发现,其根本不会重传SYN ACK包,比如我用百度的IP测试就得到了这样的结果。

最后,没有测试其他操作系统上的情况,比如Unix和MAC OSX,为什么呢?

因此,文中介绍的这种方法只能作为一种辅助手段,仅供参考,大家能顺便了解一些关于TCP重传的知识也是很有意义的。

好了,以上就是今天的分享了,写作不易,大家看完给个三连支持呀~


END
作者:轩辕之风O来源:编程技术宇宙版权归原作者所有,如有侵权,请联系删除。
嵌入式ARM

扫描二维码,关注更多精彩内容

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭