Engenharia reversa · ProjectG.exe

Login & Message Server

Os dois servidores auxiliares do PangYa. Cada um tem espaço de opcode próprio começando do zero — por isso o mesmo número significa coisas diferentes dependendo do socket. Aqui estão os dois, separados.

Confirmado no IDA 10 server→client 7 client→server

O primeiro servidor que o cliente toca. Valida usuário e senha, mostra a lista de Game Servers e devolve a AuthKey — a credencial que o cliente apresenta depois no Game Server. Também cuida de nickname e da criação do personagem inicial.

Login Server→AuthKey→Game Server +Message Server
10Server → Client
7Client → Server
16Vistos no IDA
0x01–0x11Faixa de opcodes

Tabela de pacotes

A coluna Payload vem do código do servidor, então é exata. A coluna Cliente vem do IDA e serve pra provar que o cliente realmente trata aquele opcode.

0 de 0 pacotes
Opcode Dir. Nome Payload (servidor) Cliente (IDA) Descrição
carregando mais pacotes…
Role dentro do painel — clique numa linha para expandir a descrição.

Como o cliente trata esses dois protocolos

O cliente não tem uma classe dedicada por servidor. Os mesmos dispatchers do Game Server atendem também esses sockets — o que muda é só a faixa de opcode que cada um reconhece.

Recepção: quem trata o quê

O bloco baixo do CLobbyTask (0x01–0x11) é o Login Server — faz sentido, é ele que desenha a tela de seleção de servidor. O MSN cai no handler comum sub_9C20A0.

A prova que fecha o caso — o case 0x03:

servidor
pacote003 escreve u32 status + str AuthKey
cliente
lê u32, testa se é 0, lê a string e guarda em [dword_E47E2C+0x11254]

Bate byte a byte, e o destino é justamente o global da chave que ele reapresenta no Game Server. No MSN, o 0x2F (servidor manda u8+u32, cliente lê u8 u32) e o 0x30 (sub-opcode u16 primeiro dos dois lados) fecham igual.

Envio: resolvido com breakpoint

O cliente escolhe o socket como constante: monta o pacote e chama sub_A4DAB0(connId), que indexa um array de 4 conexões na classe WNetworkSystem. Os nomes vieram do RTTI:

0 = GameUnit 1 = MSNUnit 3 = LoginUnit 4 = RankUnit

A busca estática não achava nenhum envio de Login. O motivo apareceu no breakpoint: existe uma segunda cópia do construtor (sub_A4E8A0, idêntica à sub_A4E800 — mesmo vtable, mesmo impl) e é ela que Login, MSN e Rank usam. Eu tinha enumerado só uma das duas.

Com as duas cópias, os 66 call sites fecham 100%: 7 opcodes pro LoginUnit, 23 pro MSNUnit, 2 pro RankUnit. E os 7 do Login são exatamente os 7 da tabela addPacketCall do teu Login Server — conjunto idêntico.
Confirmado rodando o jogo. Com um breakpoint de hardware em sub_A4DAB0 capturando o endereço de retorno e o argumento, os disparos reais bateram com a análise estática: o 0x03 do LoginUnit saiu na hora de escolher o servidor, e o MSNUnit disparou ao abrir a lista de amigos. O RankUnit não disparou nenhuma vez.
Cuidado ao usar o sniffer: opcode sozinho não identifica um pacote. O 0x12 é CLIENT_SHOT no Game Server e LOGIN no Message Server. A única exceção é o 0x40, que é chat nos dois.

O que ficou em aberto

  • 5 opcodes do MSN que o teu servidor não trata: 0x21, 0x22, 0x26, 0x27 e 0x16B. O cliente monta e manda os cinco pelo socket do MSN — no SuperSS eles caem como "tipo desconhecido" no log. Implementar esses é ganho direto.
  • Os opcodes 0x13 e 0x29 chegam no servidor mas ninguém sabe o que pedem — o SuperSS só loga o hex.
  • O RankUnit nunca conecta porque o servidor não existe. Os dois opcodes estão no binário (sub_B292A0 e sub_B293E0), mas o payload deles só dá pra confirmar com o socket de pé — daria pra subir um servidor falso na porta do rank só pra ver o que chega.
  • Os opcodes 0x2A–0x2D o SuperSS trata mas o cliente nunca manda: são servidor↔servidor, vindos do Game Server pro Message Server.