在SSL/TLS中,消息作为记录的一部分发送。应该预料的是,客户端首先发送一条ClientHello消息,该消息本身包含在一个或多个记录中。
记录格式为:
record type: 1 byte (0x16 for "records contains some handshake message data")
protocol version: 2 bytes (0x03 0x00 for SSL 3.0, 0x03 0x01 for TLS 1.0, and so on)
record length: 2 bytes (big endian)
then the record data...
对于第一条记录(从客户端到服务器),客户端将首先发送一个ClientHello消息,这是一种握手消息,因此封装在如上所示的记录中(记录的第一个字节将是 0x16)。理论上,客户端可能会发送ClientHello拆分为几条记录,并且可能以一条或几条空记录开头,但这不太可能。ClientHello消息本身以它自己的四字节标头开始,其中一个字节表示消息类型(0x01 表示)ClientHello,然后是超过三个字节的消息长度(又是大端)。
一旦客户端发送了它的ClientHello,它就会期待来自服务器的响应,所以ClientHello它的记录中将是单独的。
所以你可以期待一个以以下 9 个字节开头的有效负载:
0x16 0x03 X Y Z 0x01 A B C
和:
X将是 0, 1, 2, 3...或更多,具体取决于客户端用于第一条消息的协议版本。目前,定义的 SSL/TLS 版本是SSL 3.0、TLS 1.0、TLS 1.1和TLS 1.2。未来可能会定义其他版本。他们可能会使用3.X编号方案,因此您可以期望第二个标头字节保持为 0x03,但您不应任意限制第三个字节。
YZ是记录长度的编码;ABC是ClientHello消息长度的编码。由于ClientHello消息以 4 字节标头(不包括其长度)开头,并且应该在其记录中单独存在,因此您应该具有:A = 0和256*X+Y = 256*B+C+4。
如果您看到 9 个这样的字节来验证这些条件,那么很可能这是ClientHello来自 SSL 客户端。
一些非最新的 SSL 客户端也可能支持较旧的协议版本,称为 SSL 2.0。这些客户端将发出一个ClientHello遵循 SSL 2.0 规则的消息和记录,其中消息和记录以某种方式合并。该 SSL 2.0ClientHello消息将声明客户端也知道 SSL 3.0 或更新版本,但它不会以上面解释的 9 字节序列开头。
SSL 2.0ClientHello结构在附录 E.2 或 RFC 5246中进行了解释。尽管这样的客户端很少(有一个关于完全禁止 SSL 2.0 支持的RFC),但仍然有很多部署在那里。
您的代码有几个问题:
- 它没有检测到 SSL 2.0
ClientHello消息。
- 它检查第三个头字节(我上面的描述中的X)是否等于 0、1 或 2,这排除了 TLS 1.2。这太局限了。
- 它假设整体
ClientHello将在单个记录中(这是一个合理的假设),并且这ClientHello将被编码在单个数据包中(这是一个不太合理的假设)。
- 它不会尝试查看握手消息中的长度并用记录长度来证实它。
相应地,逃避检测将很容易(通过使用 SSL 2.0 ClientHello,通过使用标记有 TLS 1.2 版本的记录,通过发出ClientHello不适合单个数据包的大消息......方法很多);并且一些现有部署的客户端将不会被检测到:不仅可以故意避免检测,但也有可能不情愿。