0x00 原理
- 绝大多数代理不会转发tcp头,转发包的时候会直接重组tcp头部;
- websocket存在接口bufferedAmount,可以查看缓存区残余数量;
基于以上两个特点我们可以修改tcp数据包,利用特殊的tcp头部是否生效来检测服务器和客户端中间是否存在代理;实现的步骤主要如下:
- 在客户端利用js与服务端建立websocket连接;
- 客户端和服务端建立连接后,服务端返回给客户端的包中将tcp中的Windows size设置为0;
- 客户端持续向服务端发包;
- 客户端js通过bufferedAmount接口判断缓冲区数据大小,若tcp头部生效,则所有的数据会被累积在缓冲区,则可以判断未挂代理;
- 反之,若tcp头部未生效,则缓冲区数据会继续被发送直到清空,可以判断未使用代理。
0x01 实现
实现的主要难点在于对tcp包的修改,tcp包的封装是交给系统内核完成的,可以利用gopacket封装了api对数据包进行修改;之后工作其实就是三次握手后建立http连接,通过http协议完成websocket:握手,在握手完成后开始发window 0的tcp包;
websocket捂手过程:
主要涉及到一个HTTP头部:Sec-WebSocket的计算。客户端发包:
GET / HTTP/1.1
Connection: Upgrade
Upgrade: websocket
Host: example.com
Origin: null
Sec-WebSocket-Key: <base64的个key>
Sec-WebSocket-Version: 13
服务端返回包:
HTTP / 1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: <计算结果的base64>
Sec-WebSocket-origin: null
Sec-WebSocket-Location: ws://example.com/
其中Sec-VebSocket--Accept就是在客户端的Sec-WebSocket-.Key字符串后添加一个magic string后再取一个SHA-1的hash。这个magic string在RFC6456里规定了,一定为:258EAFA5-E914-47DA-95CA-C5AB0DC85B11。