AberSheeran
Aber Sheeran
I know nothing except the fact of my ignorance.

使用WebSocket进行网络穿透

起笔自
所属文集: Hack
共计 2669 个字符
落笔于

网络穿透的本质就是代理,而想要稳定的翻墙,必须要把代理伪装成一个正常的网络请求,而这一点上,在拜读了Shadowsocks的源码之后,我觉得它还不够,因为Shadowsocks的连接只能让GFW知道这个是个未知的协议。虽然SSR的混淆做的比较好,然而Breakwa11都删库了,他那个神仙代码我实在是没法维护,还是自己写吧。

在之前的一篇绕开校园网计费里,我写了一个Sock5代理,但单纯的Socks5代理是无法翻墙的,因为GFW能轻易的分析出你是一个代理,从而封掉你的海外IP。

在研究完了Shadowsocks的混淆代码之后,我把目光盯上了WebSocket。

WebSocket

WebSocket是一种基于TCP的长连接,它会使用一次HTTP的报文握手,在那之后便是WebSocket自身规定的方式进行通讯了。它是个在各种网站都可能被用到的协议。既然它常见,那么就安全

为什么要使用WebSocket

使用WebSocket的原因除了它常见以外,还有就是可以利用各种CDN服务,将你的WebSocket进行代理,而让真实的服务器IP隐藏在重重CDN之后。哪怕真实的服务器IP被封锁了,你的代理也不会失效。

并且标准Websocket的协议设计,使得客户端发送到服务端的信息都有一次性的掩码进行混淆,使得我们不需要做二次处理,只需要稍微处理一下服务器发送到客户端的数据即可。

自定义协议

当一个WebSocket连接建立起来之后,我们需要自定义协议,来让服务端知道需要干什么。

握手

握手阶段使用标准的JSON格式能让我们减少很多工作。此时我们使用的应当是Websocket标准中规定的文本帧。

客户端请求

为了验证身份,我们首先需要一个握手包来让服务端知道,这个连接来自可靠的客户端。

{
    "VERSION": 1,
    "USERNAME": "用户名",
    "PASSWORD": "密码"
}
  • VERSION: 协议版本号,此处为'01'

服务端响应

当服务端验证账户密码正确时,需要返回本机支持的一些功能,这能让客户端避免一些无效的操作。

{
    "VERSION": 1,
    "IPV4": true,
    "IPV6": true,
    "TCP": true,
    "UDP": true
}
  • VERSION: 协议版本号,此处为'01'
  • IPV4: 是否支持IPV4的转发
  • UDP: 是否支持UDP的转发
  • IPV6: 是否支持IPV6的转发
  • TCP: 是否支持TCP的转发

如果验证失败,我们就假定这是一个恶意攻击者,回复

{
    "VERSION": 1,
    "MESSAGE": "用户名或密码错误",
    "STATUS": "ERROR"
}

请求代理

当握手完成后,客户端根据服务端返回的配置,进行本地的配置(例如本地Socks5代理服务器支持的方法等)。当有数据流需要走代理时,应做如下的请求。

客户端请求

客户端发送一个如下的数据包给服务端,用以约定此连接是代理TCP还是UDP。当约定为UDP代理时,DST.ADDR与DST.PORT为任意值均可,服务端无需解析此部分。

{
    "VERSION": 1,
    "CMD": "TCP" | "UDP",
    "ADDR": "目标的地址",
    "PORT": "目标的端口"
}
  • VERSION: 协议版本号,此处为'01'
  • CMD: 指定代理方式
    1. "TCP"
    2. "UDP"
  • ADDR: 目标的地址
  • PORT: 目标的端口(number类型)

服务端响应

服务端根据客户端请求进行响应——如果是TCP方法,则尝试连接客户端指定的地址和端口;如果是UDP方法,则视自身情况而定是否同意UDP转发。
当服务端对此次请求处理完毕后,视情况而定回复一个数据包。

{
    "VERSION": 1,
    "STATUS": "SUCCESS"
}
  • STATUS: 为SUCCESS时,客户端可进行下一步。否则,客户端认为服务器无法做到此次请求的要求,立刻关闭连接。

数据传输

当进行完以上两步后,可进行正式的数据传输。数据传输阶段使用二进制帧。

TCP

对于TCP方法,客户端直接将数据发送到此WebSocket连接即可,而服务端也只需要单纯的转发。

UDP

对于UDP方法,客户端需要将UDP数据进行封装

+------+----------+----------+----------+
| ATYP | DST.ADDR | DST.PORT |   DATA   |
+------+----------+----------+----------+
|  1   | Variable |    2     | Variable |
+------+----------+----------+----------+
  • ATYP: 指定DST.ADDR的类型

    • IPV4: X'01'
    • 域名: X'03'
    • IPV6: X'04'
  • DST.ADDR: 该数据包渴望到达的目标地址

  • DST.PORT: 该数据包渴望到达的目标端口
  • DATA: 实际要传输的数据

而服务端进行解包转发后,所接收到的UDP包也解析为同样格式,再发给客户端。

题外话

本协议实现的开始,只是我想突破学校的网络封锁。后来想着,用了别人的软件那么久,也该自己写一个,以防不测了。于是就有了此文。

代码实现在websocks

新协议

由于从学校出来之后,网络环境大改。不再需要代理UDP,且Websocket代理UDP的速度的确慢,于是设计了新协议仅用于搜索学术问题。

使用WebSocket进行网络穿透(续)

如果你觉得本文值得,不妨赏杯茶
没有上一篇
使用WebSocket进行网络穿透(续)