ARC4解密SMSG_AUTH_RESPONSE成功,解密SMSG_CHAR_ENUM失败求排查
AzerothCore机器人SMSG_CHAR_ENUM头部解密失败排查与修复
我正在为私人AzerothCore服务器开发Python玩家机器人,已完成SRP6认证及初期世界服务器数据包处理:
- 能正确解密
SMSG_AUTH_RESPONSE数据包,获取到操作码0x1EE - 处理
SMSG_CHAR_ENUM时头部解密失败,但数据包中能看到用户名,说明原始数据包是正确的 - 已尝试推送最多2024字节数据排查同步问题、用相同会话密钥重新初始化ARC4密码器,均未解决
相关代码
数据包读取与头部解密函数
def data_received(self, data): self.remaining = -1 if len(data) < self.HEADER_SIZE_INCOMING: self.logger.debug(f"Received partial header: {data}") # Not enough data for a header return header = data[:self.HEADER_SIZE_INCOMING] data = data[self.HEADER_SIZE_INCOMING:] if self.arc4_decryptor: header = self.arc4_decryptor.decrypt(header) self.remaining = int.from_bytes(header[:2], "big") size = self.remaining data_size = size - self.OPCODE_SIZE_INCOMING # Check if we have enough data for the remaining packet if self.remaining > 0 and len(data) >= data_size: opcode = int.from_bytes(header[2:], "little") if opcode in gameopcodes.values(): self.logger.debug(f"Received {opcode_names[opcode]}") return data elif self.remaining != 0: self.logger.debug(f"couldn't decrypt: {header}")
ARC4实现代码
from Crypto.Cipher import ARC4 from Crypto.Hash import HMAC, SHA1 class Arc4: ENCRYPTION_KEY = bytes.fromhex("C2B3723CC6AED9B5343C53EE2F4367CE") DECRYPTION_KEY = bytes.fromhex("CC98AE04E897EACA12DDC09342915357") def __init__(self, key): self.key = key self.enc_key = self.hash_key(self.ENCRYPTION_KEY, key) self.dec_key = self.hash_key(self.DECRYPTION_KEY, key) self.enc_cipher = ARC4.new(self.enc_key) self.dec_cipher = ARC4.new(self.dec_key) # Arc4-drop1024 synchronization sync_data = bytes([0] * 1024) self.enc_cipher.encrypt(sync_data) self.dec_cipher.encrypt(sync_data) def encrypt(self, data): return self.enc_cipher.encrypt(data) def decrypt(self, data): return self.dec_cipher.decrypt(data) @staticmethod def hash_key(key, data_key): return HMAC.new(key, data_key, SHA1).digest()
期望实现:匹配SMSG_CHAR_ENUM的操作码0x03B时返回数据包数据。
问题排查与修复方案
1. ARC4状态同步问题(核心原因)
ARC4是流密码,解密状态会随数据处理持续推进,不能中途重置或重新初始化实例。如果处理SMSG_AUTH_RESPONSE后重新创建Arc4实例,会导致后续数据包的解密状态与服务器不同步,直接引发头部解密失败。
- 确保整个会话周期内使用同一个Arc4实例,不要在处理不同数据包时重新初始化。
- 禁止手动推送额外字节重置状态,这会破坏原本的同步链。
2. 数据包累积逻辑缺失
当前data_received函数未处理部分数据包的累积:如果一次收到的数据不足以覆盖整个数据包(比如头部完整但数据体不全,或头部都不完整),剩余数据会被丢弃,导致后续数据包的ARC4状态错位。
修改函数添加剩余数据缓存:
def __init__(self): # 新增缓存变量,存储未处理完的剩余数据 self.buffer = b"" def data_received(self, data): self.buffer += data self.remaining = -1 while len(self.buffer) >= self.HEADER_SIZE_INCOMING: # 从缓存中取出头部 header = self.buffer[:self.HEADER_SIZE_INCOMING] self.buffer = self.buffer[self.HEADER_SIZE_INCOMING:] if self.arc4_decryptor: header = self.arc4_decryptor.decrypt(header) # 解析数据包总大小(大端字节序) packet_total_size = int.from_bytes(header[:2], "big") # 数据包体大小 = 总大小 - 操作码长度 body_size = packet_total_size - self.OPCODE_SIZE_INCOMING # 检查缓存中是否有足够的数据包体 if len(self.buffer) >= body_size: opcode = int.from_bytes(header[2:], "little") if opcode in gameopcodes.values(): self.logger.debug(f"Received {opcode_names[opcode]}") # 取出数据包体,剩余数据留在缓存中 body = self.buffer[:body_size] self.buffer = self.buffer[body_size:] return body else: self.logger.debug(f"Unknown opcode: {hex(opcode)}") # 未知操作码,丢弃当前数据包 self.buffer = self.buffer[body_size:] else: # 数据体不足,把头部放回缓存,等待下一次数据 self.buffer = header + self.buffer self.logger.debug(f"Waiting for more data, need {body_size - len(self.buffer)} bytes") break
3. 头部与操作码的字节序验证
确认HEADER_SIZE_INCOMING和OPCODE_SIZE_INCOMING的取值:
- AzerothCore的世界数据包头部通常是4字节:2字节大端的总长度,2字节小端的操作码
- 确保
HEADER_SIZE_INCOMING = 4,OPCODE_SIZE_INCOMING = 2,否则会导致头部解析错位。
4. 操作码版本匹配
不同版本的AzerothCore操作码可能不同,确认gameopcodes字典中SMSG_CHAR_ENUM对应的操作码是否为0x03B,可通过AzerothCore源码中的Opcode.h文件核对目标版本的操作码。
内容的提问来源于stack exchange,提问作者Dave Zitney
相关产品推荐
相关产品推荐

