作者:轩辕之风O

来源: 编程技术宇宙

前言

大家好,我是轩辕。

前几天,我在读者群里提了一个问题:

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

有的说通过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重传的知识也是很有意义的。

哈哈!TCP泄露了操作系统信息···相关推荐

  1. linux 查询内存和核心数,Linux下查看操作系统信息、内存情况及cpu信息:cpu个数、核心数、线程数...

    1.查看物理CPU的个数 [root@MysqlCluster01 ~]# cat /proc/cpuinfo |grep "physical id"|sort |uniq|wc ...

  2. 通过System.Management获取操作系统信息

    引用System.Management.dll 我们能轻松获取系统信息.看如下代码: 1: ObjectQuery oq = new ObjectQuery("SELECT * FROM W ...

  3. OPK修改操作系统信息 --oobe.xml

    OPK修改操作系统信息<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /& ...

  4. java知识点8——垃圾回收原理和算法、通用的分代垃圾回收机制、 JVM调优和Full GC、开发中容易造成内存泄露的操作

    垃圾回收原理和算法 内存管理 Java的内存管理很大程度指的就是对象的管理,其中包括对象空间的分配和释放. 对象空间的分配:使用new关键字创建对象即可 对象空间的释放:将对象赋值null即可 垃圾回 ...

  5. 【Qt】modbus之TCP模式写操作

    00. 目录 文章目录 00. 目录 01. 概述 02. 开发环境 03. 读Coils程序示例 04. 读HoldingRegisters程序示例 07. 综合示例 08. 程序下载 09. 附录 ...

  6. 【Qt】modbus之TCP模式读操作

    00. 目录 文章目录 00. 目录 01. 概述 02. 开发环境 03. 读Coils程序示例 04. 读DiscreteInputs程序示例 05. 读InputRegisters程序示例 06 ...

  7. 使用Sigar包获取操作系统信息

    项目中的一个需求是获取操作系统的相关信息,  在网上找了相关的资料,发现了一个好的玩意,就是Sigar,它是通过java api的方式来调用程序,基本上能够获取操作系统的全部信息,感觉挺强大的.Sig ...

  8. springboot查看请求ip、ip归属地、设备信息、本地MAC地址、操作系统信息

    springboot查看请求ip.ip归属地.设备信息.操作系统信息 1.在pom添加依赖 <dependency><groupId>eu.bitwalker</grou ...

  9. C#获得计算机硬件信息和操作系统信息

    获取电脑的一些硬件信息 前言 一.准备 二.获取硬件信息 1.获取CPU序列号 2.获取主板序列号 3.获取硬盘序列号 4.获取网卡地址 总结 常见Key值 参考引用 前言 项目代码中有关于获取Fir ...

最新文章

  1. 使用Fiddler解析WCF RIA Service传输的数据
  2. POJ - 3764 The xor-longest Path(字典树性质)
  3. 组织来了!特斯拉中国车友俱乐部开启官方认证
  4. android之menu,一起学Android之Menu
  5. 开源软件 Nagios 曝11个漏洞,可使IT 基础设施遭接管引发供应链攻击
  6. require() 源码解读
  7. java设计模式-可复用面向对象软件的基础(一)
  8. Jenkins实现持续集成 上传远程服务器war 并执行sh脚本重启
  9. STM32CubeIDE 下载及安装教程
  10. 电脑围棋领域的研究概述
  11. JAVA类似ABP框架_【Net】ABP框架学习之它并不那么好用
  12. c++语言常量,C++常量(constant)
  13. 意大利西西里百年历史Asaro家族橄榄油
  14. mysql报08S01的错误- SQL Error: 0, SQLState: 08S01 偶发
  15. 入职脉脉是一种什么体验?附上我的脉脉Java后端开发面经,本人已于上周成功入职!
  16. 半夜撸 flap bird
  17. java nio MappedByteBuffer 文件映射
  18. @Validated和@Valid 解决list校验问题
  19. 字符串排序算法:低位优先排序(LSD)
  20. 【读书笔记】理工科思维解读《万万没想到》

热门文章

  1. 如何给mysql表添加百万条数据_给mysql一百万条数据的表添加索引
  2. linux 父子进程 资源_linux 父子进程 资源_实验4 Linux父子进程同步
  3. 该计算机没有运行windows无线服务器,老司机示范win7系统诊断提示此计算机上没有运行的windows无线服务的恢复方法...
  4. 俺是如何在3个月内写出博士论文的?
  5. 当年赫赫有名的“四大工学院”,如今都发展得怎样?依然牛气冲天
  6. 教授呼吁:应当让博士生先回学校
  7. Nature150岁生日:盘点史上十大重磅论文,中国13篇文章登上封面!
  8. 改变世界的十位算法大师
  9. mysql命令行导入url_Mysql 导入数据,推荐Source命令,太快了
  10. HTML字体小于12谷歌不兼容,Chrome谷歌浏览器下不支持css字体小于12px的解决办法...