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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 14:25:30