实现 Xray 不能正常使用的 V2Ray 入站

Author Avatar
秋のかえで 9月 11, 2021
  • 在其它设备中阅读本文章

TL;DR: 我不想解释我为什么写这篇文章。
本文旨在通过简单地修改或是完全不修改 V2Ray 的源代码,实现 Xray 不能正常使用的 V2Ray 入站(即服务端)。

WebSocket

强制使用 early data

这里涉及到多个不同的特性。

  • V2Ray 使用 Base 64 Encoding with URL and Filename Safe Alphabet 编码 early data,而 Xray 使用 Base 64 编码。
  • V2Ray 可使用 path 或是 header 承载 early data,Xray 只能使用 Sec-WebSocket-Protocol 承载 early data。
  • 在 write 的数据长度超过 early data 最大长度的数据时,V2Ray 会发送与最大长度相等的 early data,而 Xray 则不发送 early data。
diff --git a/transport/internet/websocket/hub.go b/transport/internet/websocket/hub.go
index 5a3ccd7d..50773428 100644
--- a/transport/internet/websocket/hub.go
+++ b/transport/internet/websocket/hub.go
@@ -53,10 +53,18 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
             return
         }
         earlyDataStr := request.Header.Get(h.earlyDataHeaderName)
+        if len(earlyDataStr) == 0 {
+            writer.WriteHeader(http.StatusForbidden)
+            return
+        }
         earlyData = base64.NewDecoder(base64.RawURLEncoding, bytes.NewReader([]byte(earlyDataStr)))
     } else {
         if strings.HasPrefix(request.URL.RequestURI(), h.path) {
             earlyDataStr := request.URL.RequestURI()[len(h.path):]
+            if len(earlyDataStr) == 0 {
+                writer.WriteHeader(http.StatusForbidden)
+                return
+            }
             earlyData = base64.NewDecoder(base64.RawURLEncoding, bytes.NewReader([]byte(earlyDataStr)))
         } else {
             writer.WriteHeader(http.StatusNotFound)

XUDP

Xray 在 v1.3.0 中引入了一个特性,通过修改 Mux.Cool 协议的 Keep 结构实现在单条连接上传输多个目标地址不同的 UDP 数据包。与此同时,目标地址会被设置成 v1.mux.cool,端口则为 666,通过简单地修改代码即可屏蔽 VMess 和 VLESS 中来自 Xray 的 XUDP 连接,patch 如下:

diff --git a/proxy/vless/encoding/encoding.go b/proxy/vless/encoding/encoding.go
index 7ec515bb..40d572ea 100644
--- a/proxy/vless/encoding/encoding.go
+++ b/proxy/vless/encoding/encoding.go
@@ -109,12 +109,16 @@ func DecodeRequestHeader(isfb bool, first *buf.Buffer, reader io.Reader, validat
         }
 
         request.Command = protocol.RequestCommand(buffer.Byte(0))
+        addr, port, err := addrParser.ReadAddressPort(&buffer, reader)
         switch request.Command {
         case protocol.RequestCommandMux:
+            if port == 666 {
+                return nil, nil, false, newError("block XUDP")
+            }
             request.Address = net.DomainAddress("v1.mux.cool")
             request.Port = 0
         case protocol.RequestCommandTCP, protocol.RequestCommandUDP:
-            if addr, port, err := addrParser.ReadAddressPort(&buffer, reader); err == nil {
+            if err == nil {
                 request.Address = addr
                 request.Port = port
             }
diff --git a/proxy/vmess/encoding/server.go b/proxy/vmess/encoding/server.go
index 202de2a9..da96f205 100644
--- a/proxy/vmess/encoding/server.go
+++ b/proxy/vmess/encoding/server.go
@@ -239,13 +239,16 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request
     // 1 bytes reserved
     request.Command = protocol.RequestCommand(buffer.Byte(37))
 
+    addr, port, err := addrParser.ReadAddressPort(buffer, decryptor)
     switch request.Command {
     case protocol.RequestCommandMux:
+        if port == 666 {
+            return nil, newError("block XUDP")
+        }
         request.Address = net.DomainAddress("v1.mux.cool")
         request.Port = 0
-
     case protocol.RequestCommandTCP, protocol.RequestCommandUDP:
-        if addr, port, err := addrParser.ReadAddressPort(buffer, decryptor); err == nil {
+        if err == nil {
             request.Address = addr
             request.Port = port
         }

VMess

V2Ray 在 v4.41.0 中引入了 VMess AEAD Authenticated Length 的实验性特性,而 Xray 无此特性,修改配置即可实现。

{
    "id": "27848739-7e62-4138-9fd3-098a63964b6b",
    "alterId": 0,
    "experiments": "AuthenticatedLength"
}

最后,本文中所提供的 patch 均使用 MIT License 授权。

本文使用 CC BY-NC-SA 4.0 授权
本文链接:https://blog.akinokae.de/archives/xray-incompatible-v2ray-inbounds/