如何解码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
相关产品推荐
相关产品推荐

