sábado, 25 de agosto de 2012

Aula 10 - Packets

Tutorial - Packets

Oooi, quanto tempo não é? 
Sei que tinha bastante gente esperando mais aulas mas andei realmente muito ocupado com meu servidor (OverDestiny). E hoje resolvi vir aqui dar esta aulas a vocês. Vai ser uma aula bem legal pra quem quer realmente aprender sobre este assunto.


O que são packets?

Packets são como "conversas" entre duas "pessoas". Uma "pessoa" é você (no caso o jogador) e a outra é o servidor (o programa que controla tudo). Quando você, pessoa 1, se mexe no jogo ou então mexe algum item ou outra ação, o client manda um packet (a "fala") para a pessoa 2 dizendo o que você fez.

Seria assim:
Client -> Mexe item do slot 54 para o slot 55 (do inventário) -> Packet Enviado 
Servidor -> Recebe o packet -> Interpreta-o e verifica se é possível fazer esta ação -> Manda um packet de resposta (geralmente toda a pergunta tem uma resposta) 
Client -> Interpreta-o e faz você ver o que aconteceu.


Como ver um packet?

Para poder ver o packet você deve ter um packetcontrol na sua source. Um exemplo disso é:
for(int i=0;iSize;i++)
printf("%02X ", m_PacketBuffer[i]);
O que isso aí irá fazer?
Irá mostrar todos os bytes do packet recebido. Caso queira ver melhor, use:
printf("Recv ou Send -> Packet: 0x%X Size : %d ClientId: %d\n",packetHeader->PacketId,packetHeader->Size,packetHeader->ClientId);
printf("     ");
for(int i=0;iSize;i++)
{
printf("%02X ", m_PacketBuffer[i]);
if(i % 15 == 0 && i != 0)
printf("\n     ");
if(i >= packetHeader->Size)
printf("\n");
}


O que são os Bytes?

Os bytes são as informações que o packet carrega. Os primeiros 2 bytes (tipo short) é o tamanho do packet.

ATENÇÃO: Os bytes vão estar sempre em hexadecimal.
ATENÇÃO: Todo os bytes vem invertidos

Os bytes 2 e 3 são a Key e o Checksum. Não importantes agora para quem mexe com a TMSRV pois o SendPacket faz isso automaticamente.

Os bytes 4 e 5 é o ID do Packet, ou seja, sua identificação. Cada packet de ação tem seu próprio identificador. Alguns exemplos:
0x364 -> SendSpawn (envia um personagem para o client)
0x366 -> SendScore (atualiza alguns status do personagem)
0x333 -> SendSay (fala dos personagens)
0x399 -> ChangePK (quando personagem ativa/desativa o PK)
0x39F -> RequestDuel (pedido para duelo)

Esses aí são alguns. O WYD tem diversos packets que compõe o jogo. E você deve conhecer pelo menos os principais se quiser realmente programar!

Os bytes 6 e 7 é o ClientID. O ClientID é o identificar de quem está mandando o packet. 

Os bytes 8, 9, 10 e 11 é o TimeStamp, ou seja, o clock de quando foi enviado/recebido.

Todo o packet tem no mínimo 12 bytes (0~11 pois sua contagem começa de 0). Só existe um no jogo que tem apenas 4 bytes e não é encryptado que é o Hello Packet. Ele é enviado juntamente com o packet de login. 


O que é encryptação e decryptação?

Seus nomes são autoexplicativos. Encryptação nada mais é do que a forma em que foi encryptado o packet do jogo. Todo o packet, com exceção do HelloPacket, é encryptado. Antes de ser enviado para o servidor/client, o packet montado passa por uma função que faz ele ser encryptado (irreconhecível caso você não use um decryptador). Esse packet então é recebido pelo servidor ou client e então ele decrypta ele, neste caso ele usará o byte 2 e 3 para a decryptação pois mostrará como ele foi decryptado.


Exemplo de um packet

14 00 CB 3C 8B 02 01 00 F1 D4 62 05 99 10 00 00 01 00 00 00
Este é um exemplo de packet. Seguindo o que aprendemos sobre a ordem dos bytes temos:
0 e 1 = 14 00 -> 00 14 (invertidos, lembra?) = 20 = Size
2 = CB (apenas um byte) = 203 = Key
3 = 3C (apenas um byte) = 60 = CheckSum do client
4 e 5 = 8B 02 = 02 8B = 0x28B = PacketId
6 e 7 = 01 00 = 00 01 = 1 = ClientID
O resto não é importante agora.

Que packet é este? 
Este é o packet de quando você clica em algum NPC. É ele que iremos montar a struct como exemplo e para isto teremos que nos fazer algumas perguntas que eu faço:
O packet tem alguma relação com itens?
Tem alguma relação com boolean (true e false)?
Utiliza NPCs?

São perguntas bem simples mas que ajudam muito na hora de fazer a estrutura. Porque?
Quando você sabe que utiliza algum NPC, você já vai saber que lá pelo meio existirá o clientid deste mob em análise
Quando tem relação com um item, o ID dele ou o slot aparecerá no packet.
Quando tem uma relação com boolean você por exemplo aceitou o trade ou não ou clicou sim / não na janela de confirmação.


Informações de uma struct

Algumas coisas que deve saber é sobre o tamanho da variável. Ela influência TOTALMENTE na estrutura porque ela define quantos bytes você irá pegar. Os tipos básicos são:
BYTE = char = 1 Byte
WORD = short = 2 Bytes
DWORD = int = 4 Bytes

Acima disso não é usado em structs do WYD.  Caso defina algum tamanho errado, irá pegar provavelmente o valor errado (depende da struct).
A estrutura do PacketHeader (que é os 12 primeiros bytes que vimos anteriormente é assim):
typedef struct
{
WORD Size; // 2 bytes para o tamanho

BYTE Key; // 1 byte para a Key 
BYTE CheckSum; // 1 Byte para o CheckSum

WORD PacketId; // 2 Bytes para o PacketId (identificador do packet)
WORD ClientId; // 2 Bytes para o ClientID (quem manda o packet)

DWORD TimeStamp; // 4 Bytes para o clock ();
} PacketHeader;

Se o WORD Size fosse trocado para DWORD Size a estrutura retornaria valores totalmente errados. Então o que você deve fazer é prestar muito a atenção nas variáveis.

Downloads necessários: 

WydDecrypter -> usado para decryptar o packet caso não use o PacketControl - créditos: Petruka
WPEPro -> Pegar packets caso não use o PacketControl - site oficial

E claro, precisará de uma source. 

Atenção: não ensinarei a utilizar o WPEPro.

Pegando o packet para criar a estrutura

Bom, nosso primeiro packet será o 0x28B, que é quando você clica em algum NPC.
Ligue seu servidor normalmente, vá no jogo e pare no Mestre Arch (npc Skill BM).
Crie o item 1742 (Imortalidade) e ponha no slot dela
Abra o WPEPro, selecione o processo do WYD e aperte Play e clique no NPC e dê stop.
Um dos packets será do tipo Send. É ele que você deve procurar.

Após achar algum tipo Send, abra o WydDecrypter. Copie o packet e cole no console e aperte enter. Nele aparecerá o packet decryptado. Se você ver que os bytes 4 e 5 são 8B 02 está correto.

Copie-o para algum lugar (escrevendo manualmente) para efetuar a análise.

Análise do Packet

O que fizemos antes foi pegar o packet e o que faremos agora é analisá-lo. 
Os 12 primeiros bytes já sabemos o que é, não é mesmo? Então começaremos assim:
typedef struct {
PacketHeader Header;
} p28B;

PacketHeader já vem incluso na sua source, então basta adicioná-lo. 

É a partir disto que começaremos nossa analise. Os bytes que peguei foram:
14 00 CB 3C 8B 02 01 00 F1 D4 62 05 99 10 00 00 01 00 00 00
Até o 05 são 11 bytes, então até o 05 é o PacketHeader. É depois disso que iremos analisar.
Faremos o seguinte: iremos ver a partir dele como um byte, dois bytes e 4 bytes. Assim:
1 byte = 99hex = 153 decimal
2 bytes = 99 10 hex = 10 99 = 4249 decimal
4 bytes = 99 10 00 = 00 10 99  = 4249 decimal

Pensando assim... pelos conceitos básicos, o clientid de um personagem/npc varia de 1~30.000. E uma das perguntas que disse que deveria ser feita era:
Utiliza NPCs?
E aí? Utiliza? Ele está dentro do padrão de varição de clientids? Sim. 
Claro que em alguns packets não teremos certeza disso, então, isso apenas nossa estrutura teste, depois iremos testar ela e ver se está tudo ok.

Mas qual utilizar? O tamanho 2 bytes ou 4 bytes? 
O ClientID sempre será um valor 2 bytes porque nenhum client ultrapassará 2 bytes. Então temos uma variável tipo short, certo? 
Adicione na struct com o nome que quiser abaixo do PacketHeader, assim:
typedef struct {
PacketHeader Header;
short MobID;
} p28B;

Agora os 4 próximos bytes são: 00 00 01 00 
1 Byte = 00 hexadecimal = 0 decimal
2 Bytes = 00 00 hexadecimal = 0 decimal
4 Bytes = 00 00 01 00 = 65536 decimal

Pensando assim... o valor de 4 bytes é um valor muito alto. O de 2 bytes é igual a de 1 byte... Então tá aí o que não poderemos descobrir já. Esses bytes em que aparecem 0 geralmente colocamos como Unknow (desconhecido). Como são 2 bytes desconhecidos para ocuparmos menos linhas utilizaremos um short, ficando:
typedef struct {
PacketHeader Header;
short MobID;
short Unknow1;
} p28B;

Atenção: Nem todo o packet que retorna 00 00 ou 00 ou 00 00 00 00 será um Unknow (desconhecido). Tudo depende do que você está fazendo. Existem packets que sim, retornam 0 e que você deve identificar porque de alguma outra forma ele retornará 1, como no caso do próximo

Os 4 próximos bytes são  01 00 00 00 
1 Byte = 01 hexadecimal = 1 decimal
2 Bytes = 01 00 hexadecimal = 1 decimal
4 Bytes = 01 00 00 00 hexadecimal = 1 decimal

Esses valores todos retornam igual, mas você terá que identificar o que ele é. Lembra-se disso?
Tem alguma relação com boolean (true e false)?
Sim, ele tem. E ele é que você clica OK quando o packet é enviado. Se você clicar em um NPC sem a confirmação esse valor virá 0.

Para ter certeza, entre no jogo e faça o mesmo processo do WPE, clique no NPC que não tem confirmação e que não seja loja, faça o processo do WYDdecryptar e veja o resultado.

Mas qual utilizar? 2 Bytes ou 4 Bytes?
Nesse caso, é melhor usarmos o 2 bytes. Porque se os próximos valores vierem alterados ele modifica o valor todo. 

Então ficando:
typedef struct {
PacketHeader Header;
short MobID;
short Unknow1;
short Clicked;
} p28B;

Os últimos 2 bytes são: 00 00.
Você fez mais algo pra acontecer? Não? Está aí mais um Unknow. Apenas adicione-o como:
short Uknow2, ficando:
typedef struct {
PacketHeader Header;
short MobID;
short Unknow1;
short Clicked;
short Unkow2;
} p28B;

Uma estrutura simples. Para testar, coloque no seu PacketControl o case 0x28B (id do packet) puxando as informações do packet da seguinte forma:
NomeDaEstrutura *p = (NomeDaEstrutura*)m_PacketBuffer;

Acesse-o usando: p->

Finalizando
Bom, tentei explicar do jeito mais simples. Espero que gostem (;

Um comentário:

  1. Gostei muito!
    Parabéns e continue assim, compartilhando informações :)

    Att,
    Irmão do Gagoil

    ResponderExcluir