You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解码Firefox与网站间的WebSocket二进制帧以实现客户端模拟?

解决WebSocket二进制帧解码问题的思路与方案

看起来你已经搞定了TLS解密的难题,卡在了WebSocket二进制帧的解码上——这确实是个容易踩坑的点,我来帮你梳理几个关键方向:

1. 先搞懂WebSocket帧的结构,别直接解码整个二进制数据

你之前的C#代码直接把所有十六进制转成字节处理,但忽略了WebSocket帧本身有固定的帧头结构,开头的字节不是payload内容,而是帧的控制信息(比如FIN位、Opcode、掩码标识、payload长度等)。举个例子:

  • 第一个字节:高1位是FIN(是否为最后一帧),低4位是Opcode(0x1是文本帧,0x2是二进制帧)
  • 第二个字节:高1位是掩码标识(客户端发的帧必须带掩码,服务器可选),低7位是payload长度(超过125会用后续2/8字节表示)
  • 如果带掩码,接下来4字节是掩码键,之后才是真正的payload数据

你给的十六进制串开头的000000000000000000大概率是帧头部分,直接转成字符串肯定得不到有效内容。

2. 工具层面的快速解决方案(不用自己写代码)

其实很多工具已经帮你处理了帧的解码,可能你之前没找对地方:

  • Chrome DevTools:打开Network面板,找到对应的WebSocket连接,切换到Frames标签页,这里会自动解析帧头、解掩码,直接展示文本帧的内容;如果是二进制帧,也能看到原始payload的十六进制或预览。
  • Wireshark:捕获到WebSocket流量后,右键选择「Follow > WebSocket Stream」,它会自动处理帧结构和解掩码,把真实的payload内容展示出来,不用手动解析。
  • MITM Proxy脚本扩展:用Python写个简单的脚本,在MITM Proxy中拦截WebSocket帧,直接拿到解码后的payload。比如:
    from mitmproxy import http, websocket
    
    def websocket_message(flow: websocket.WebSocketFlow):
        # 遍历所有收到的帧
        for message in flow.messages:
            if message.from_client:
                print(f"Client -> Server: {message.content}")
            else:
                print(f"Server -> Client: {message.content}")
    
    把这个脚本用mitmdump -s script.py运行,就能直接看到解码后的消息内容。

3. 修正你的C#代码:先解析帧头再处理payload

如果一定要自己写代码处理,得先完成帧头解析和解掩码步骤,示例代码如下:

using System;
using System.Linq;
using System.Text;

class WebSocketDecoder
{
    static void Main()
    {
        string sourceHex = "000000000000000000746a2a35e11296a3da439c0a31337fc6";
        byte[] frameData = Enumerable.Range(0, sourceHex.Length)
                                     .Where(x => x % 2 == 0)
                                     .Select(x => Convert.ToByte(sourceHex.Substring(x, 2), 16))
                                     .ToArray();

        int index = 0;
        // 解析帧头第一个字节:FIN和Opcode
        byte firstByte = frameData[index++];
        bool isFinalFrame = (firstByte & 0x80) != 0;
        byte opcode = (byte)(firstByte & 0x0F);

        // 解析帧头第二个字节:掩码标识和payload长度
        byte secondByte = frameData[index++];
        bool isMasked = (secondByte & 0x80) != 0;
        ulong payloadLength = (ulong)(secondByte & 0x7F);

        // 处理超长payload长度
        if (payloadLength == 126)
        {
            payloadLength = BitConverter.ToUInt16(frameData, index);
            index += 2;
        }
        else if (payloadLength == 127)
        {
            payloadLength = BitConverter.ToUInt64(frameData, index);
            index += 8;
        }

        // 处理掩码
        byte[] maskKey = null;
        if (isMasked)
        {
            maskKey = frameData.Skip(index).Take(4).ToArray();
            index += 4;
        }

        // 提取payload并解掩码
        byte[] payload = frameData.Skip(index).Take((int)payloadLength).ToArray();
        if (isMasked && maskKey != null)
        {
            for (int i = 0; i < payload.Length; i++)
            {
                payload[i] ^= maskKey[i % 4];
            }
        }

        // 根据Opcode处理内容
        if (opcode == 0x1) // 文本帧,用UTF8解码
        {
            string textMessage = Encoding.UTF8.GetString(payload);
            Console.WriteLine("解码后的文本消息:" + textMessage);
            // File.WriteAllText(@"string.txt", textMessage);
        }
        else if (opcode == 0x2) // 二进制帧,可能是自定义格式
        {
            Console.WriteLine("二进制帧,需要根据网站的序列化规则解析(比如Protobuf、MsgPack等)");
            // 可以先输出十六进制查看结构
            Console.WriteLine("Payload十六进制:" + BitConverter.ToString(payload).Replace("-", ""));
        }
    }
}

4. 如果是二进制帧的额外处理

如果解析后发现是Opcode为0x2的二进制帧,那大概率是网站用了自定义的序列化协议(比如Protobuf、MsgPack、或者自己定义的二进制格式)。这时候你需要:

  • 查看网站的前端代码,找到处理WebSocket二进制消息的逻辑(比如有没有引入protobuf.js之类的库)
  • 用十六进制工具分析payload的结构,比如有没有固定的标识位、长度字段等

内容的提问来源于stack exchange,提问作者C K

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 20:17:29