Python如何解析Minecraft基岩版登录包Chain与Skin数据
问题分析
你现有代码仅完成了登录包前5字节的解析(1字节包ID + 4字节协议版本),未适配基岩版协议的变长编码规则,因此无法提取后续的Chain data与Skin data字段。
基岩版Login包在协议版本字段之后的固定结构顺序如下:
- payload总长度:无符号VarInt类型,标记后续所有登录载荷的总字节数,一般用于包完整性校验
- Chain data长度:无符号VarInt类型,标记Chain数据的字节长度
- Chain data内容:UTF-8编码的JSON字符串,内容为Xbox Live认证链
- Skin data长度:无符号VarInt类型,标记Skin数据的字节长度
- Skin data内容:UTF-8编码的JWT字符串,包含玩家用户名、皮肤、客户端信息等数据
其中VarInt是基岩版协议通用的变长整数编码,每个字节最高位标记是否存在后续字节,低7位存储实际数值,无法用固定长度的struct模块直接解析。
实现代码
1. 扩展Packet基类,添加通用解析方法
首先修改packet/packet.py,增加读取偏移指针和VarInt、变长字符串的通用解析方法,后续所有包解析都可以复用:
class Packet(object): def __init__(self, original_bytes: bytes): self.original_bytes = original_bytes self.id = original_bytes[0:1] self.data = original_bytes[1:] # 读取偏移指针,初始位置指向包ID后第一个字节 self.offset = 0 def read_unsigned_varint(self) -> int: """读取无符号VarInt,读取完成后自动移动偏移指针""" result = 0 bit_offset = 0 while True: if self.offset >= len(self.data): raise ValueError("Packet truncated when reading VarInt") current_byte = self.data[self.offset] self.offset += 1 result |= (current_byte & 0x7F) << bit_offset if not (current_byte & 0x80): break bit_offset += 7 if bit_offset >= 35: raise ValueError("VarInt exceeds maximum allowed length") return result def read_string(self) -> str: """读取变长字符串(VarInt长度前缀 + UTF-8内容),读取完成后自动移动偏移指针""" str_length = self.read_unsigned_varint() content = self.data[self.offset : self.offset + str_length] self.offset += str_length return content.decode("utf-8") class IncorrectPacketID(Exception): pass
2. 完善Login_Packet解析逻辑
修改packet/login.py,修正原有协议版本解析的字节序问题,补充Chain data和Skin data的解析逻辑:
from packet import * import struct class Login_Packet(Packet): """客户端发送的登录包""" def __init__(self, original_bytes: bytes): super().__init__(original_bytes) if self.id != b'\x01': raise IncorrectPacketID(f"Expected packet ID 0x01, got {self.id.hex()}") # 解析固定字段 self.protocol_version = self._parse_protocol_version() # 读取并跳过载荷总长度字段 self.read_unsigned_varint() self.chain_data = self.read_string() self.skin_data = self.read_string() def _parse_protocol_version(self) -> int: # 协议版本为4字节大端有符号整数 version = struct.unpack(">i", self.data[self.offset : self.offset + 4])[0] self.offset += 4 return version def get_protocol_version(self) -> int: return self.protocol_version def get_chain_data(self) -> str: return self.chain_data def get_skin_data(self) -> str: return self.skin_data
注意事项
- 你当前提供的测试字节串是不完整的登录包,实际客户端发送的登录包长度远大于该示例,测试时请使用完整抓包数据,否则会触发包截断错误。
- 解析得到的Chain data是JSON格式,可直接用
json.loads加载,其中chain字段为三段JWT认证令牌;Skin data是单个JWT令牌,base64解码中段载荷即可获取皮肤、玩家ID等信息。 - 后续解析其他基岩版协议包时,可直接复用Packet基类的VarInt、字符串读取方法,按照协议文档补充其他类型(如小端整数、浮点数、有符号VarInt)的读取逻辑即可。
内容的提问来源于stack exchange,提问作者UCYT5040
相关产品推荐
相关产品推荐

