目录
今天突然想起之前在一个评论中看到的tcpwin=0 的控制信息 + WebSocket.bufferedAmount 属性检测浏览器代理,之前不能工作的测试页面今天发现可以访问了。简单记录下。
抄下原理
由于大部分 socks5 代理只转发数据,不转发 TCP 头,因此后端返回数据时附带一个特殊的 TCP 头,然后前端验证该头是否生效,即可推断否是存在代理。
例如后端下发 tcpwin=0 的控制信息,正常情况下前端收到后不会再往外发包,数据累计在协议栈缓冲区里。该特征可通过 WebSocket.bufferAmount 属性检测;而有代理的情况下,数据被累积在了代理服务的缓冲区,前端仍能往代理服务发包,缓冲区不会有累积。
后端实现也很简单,NodeJS 实现 ws 服务,通过 setsockopt 给 socket 设置 mark,结合 tc 改包。
const {setsockopt} = require('sockopt')
const {WebSocketServer} = require('ws')
const wss = new WebSocketServer({port: 8080})
wss.on('connection', (ws, req) => {
console.log(req.socket.remoteAddress + ':' + req.socket.remotePort)
setsockopt(req.socket, 1 , 36 , 1 )
ws.send('')
setTimeout(() => ws.terminate(), 2000)
})
tc qdisc del dev eth0 root
tc qdisc replace dev eth0 root handle 1: htb
tc filter add dev eth0 parent 1: u32 \
match mark 1 0xffffffff \
action pedit munge offset 34 u16 set 0 pipe \
csum ip and tcp
npm install ws sockopt
node test.js
一些测试
-
win10 + chrome + wifi
符合预期
-
iphone 15.5 + Sarfari/Edge/WXBrower/chrome + wifi/4G
均返回 true
-
win10 + chrome + devtools(4g/3g) + proxy
均返回 false
-
win10 + chrome + proxy(限速3.5Mbps/s)
符合预期
-
Android
自己的 红米k30U 正常,朋友的未知型号都返回true#
-
win10 firefox
均返回 true
canIuse
记录于2023年3月21日,即便在大多数的浏览器上都支持的属性,但实际这个案例的测试并没有想象的那么美好。
小结
- 网速影响
这点比较好解释,网速慢发包慢,缓冲区来不及情况就会得到测试3的情况
- iphone 和部分安卓不支持
这点不好确定,可能时控制命令被无视了,可能设备自带缓冲区但不共享信息给浏览器,这里就只是瞎比比了
异想天开的部分操作
通过chatGPT,我得知了php控制TCP_Win的方法,然后我就萌生了用php直接复刻一个的想法。毕竟用到顺手的workerman在搭ws还是挺简单的。
stream_set_write_buffer($connection->getSocket(), 0);
$packet = pack('nnNnN', 0x1234, 0x0000, 0x00000000, 0x0000, 0x0000);
首先我尝试了ws,发现能正常工作,但已经没有浏览器支持了。而后,我尝试直接使用php 搭建wss,直接说结论,失败了。同样的控制TCP_Win内容,在加上TLS后,前端wss会获得以下错误。抓包发现TCP窗口未被正确设置,应该是被当作TLS的应用数据了,而不是一个TCP控制命令,所以依然得用tc改包才行。
One or more reserved bits are on: reserved1 = 0, reserved2 = 1, reserved3 = 1