Découper un packet

Inscrit
19 Juillet 2016
Messages
14
Reactions
0
#1
Bonjour,

En JAVA j'essaie de décode les packets dofus afin d'obtenir leur ID mais je rencontre des problèmes afin de connaître la taille des headers.

J'ai remarqué que mes headers de packet systématiquement ont pour longueur 54 (ip+eth+tcp/udp+(arp)+payload)

j'extrais alors les 54 premiers bytes de mon packet : le reste correspond au payload, par exemple :

Pour le packet 226 - MapComplementaryInformationsDataMessage

24 FD 52 AC 49 59 2C AE 2B B8 BC FF 08 00 45 00 01 D4 39 27 40 00 34 06 CB 4E D5 F8 7E 3C C0 A8 2B D1 15 B3 F2 1E 2B 44 B2 E8 6F 34 5E 5D 50 18 00 ED 6E 00 00 00 03 8A 01 8A BA 03 41 A2 5C 08 00 00 00 00 00 00 00 04 00 24 42 62 64 5D 60 19 A0 00 01 00 02 51 D4 10 00 05 01 F5 B9 5F 02 EB 73 13 03 FF FF E1 04 DC 3A 12 05 DC 3A 12 00 01 8C 01 00 00 00 3C 00 16 02 00 0A 43 61 6C 65 2D 53 65 6B 69 6E 00 9D 00 08 00 01 00 00 05 E9 85 D7 00 00 00 42 62 64 5D 60 1A 60 00 00 A0 C0 D3 89 40 00 00 00 00 B1 19 00 00 00 00 00 00 00 00 00 3C 00 23 07 00 00 8C 00 00 10 0E 02 00 01 00 00 10 0E 03 B1 19 00 00 00 00 00 00 00 00 42 76 1D DD 49 0C E0 00 00 1B 77 40 FF FF 00 A0 C0 D3 88 C0 00 00 00 00 AF 19 00 00 00 00 00 00 00 00 00 3C 00 2F 03 00 00 8C 00 00 10 0D 02 00 00 42 76 1D DD 4C DB 80 00 00 1B 77 40 FF FF 00 A0 C0 D3 89 00 00 00 00 00 B0 19 00 00 00 00 00 00 00 00 00 3C 00 14 07 00 00 8C 00 00 10 0C 01 00 02 00 00 10 0E 02 B1 19 00 00 00 00 00 00 00 00 00 00 10 0E 03 B1 19 00 00 00 00 00 00 00 00 42 76 1D DD 4E 1F 20 00 00 1B 77 40 FF FF 00 02 00 50 00 07 77 B0 FF FF FF FF 00 01 00 DB B8 01 02 55 2F 26 00 00 01 01 8E 00 07 78 E9 00 00 00 FE 00 01 00 DB 44 02 55 2F 27 00 00 01 00 01 00 01 00 07 78 E9 DE 01 00 01 00 00 00 00 01 00 0C 86 01 94 01 95 01 A2 01 A3 01 B1 01 B2 01 BF 01 C0 01 CE 01 CF 01 DC 01 00 0C 19 1A 28 29 36 42 45 50 5E 5F 6C 6D 64 D9 0B 00 01 CD 81 AC 97 B2 12 00 01 00 02 A9 0B 42 62 64 5D 60 19 A0 00 00 08 00 02 C0

03 8A 01 8A BA 03 41 A2 5C 08 00 00 00 00 00 00 00 04 00 24 42 62 64 5D 60 19 A0 00 01 00 02 51 D4 10 00 05 01 F5 B9 5F 02 EB 73 13 03 FF FF E1 04 DC 3A 12 05 DC 3A 12 00 01 8C 01 00 00 00 3C 00 16 02 00 0A 43 61 6C 65 2D 53 65 6B 69 6E 00 9D 00 08 00 01 00 00 05 E9 85 D7 00 00 00 42 62 64 5D 60 1A 60 00 00 A0 C0 D3 89 40 00 00 00 00 B1 19 00 00 00 00 00 00 00 00 00 3C 00 23 07 00 00 8C 00 00 10 0E 02 00 01 00 00 10 0E 03 B1 19 00 00 00 00 00 00 00 00 42 76 1D DD 49 0C E0 00 00 1B 77 40 FF FF 00 A0 C0 D3 88 C0 00 00 00 00 AF 19 00 00 00 00 00 00 00 00 00 3C 00 2F 03 00 00 8C 00 00 10 0D 02 00 00 42 76 1D DD 4C DB 80 00 00 1B 77 40 FF FF 00 A0 C0 D3 89 00 00 00 00 00 B0 19 00 00 00 00 00 00 00 00 00 3C 00 14 07 00 00 8C 00 00 10 0C 01 00 02 00 00 10 0E 02 B1 19 00 00 00 00 00 00 00 00 00 00 10 0E 03 B1 19 00 00 00 00 00 00 00 00 42 76 1D DD 4E 1F 20 00 00 1B 77 40 FF FF 00 02 00 50 00 07 77 B0 FF FF FF FF 00 01 00 DB B8 01 02 55 2F 26 00 00 01 01 8E 00 07 78 E9 00 00 00 FE 00 01 00 DB 44 02 55 2F 27 00 00 01 00 01 00 01 00 07 78 E9 DE 01 00 01 00 00 00 00 01 00 0C 86 01 94 01 95 01 A2 01 A3 01 B1 01 B2 01 BF 01 C0 01 CE 01 CF 01 DC 01 00 0C 19 1A 28 29 36 42 45 50 5E 5F 6C 6D 64 D9 0B 00 01 CD 81 AC 97 B2 12 00 01 00 02 A9 0B 42 62 64 5D 60 19 A0 00 00 08 00 02 C0 00 00 00 00

Sauf problème j'ai remarqué que je ne reçois pas tous les packets du jeu... beaucoup portent l'identifiant "6362" je ne sais pas a quoi correspond ce dernier.
Je récupère par exemple le 226 comme vu ci-dessus lorsque je change de map, je récupère le 951 (GameMapMovementMessage) lorsque je déplace le personnage sur la map, le 500 (CharacterStatsListMessage) si je modifie une caractéristique, 5654 (JobExperienceUpdateMessage), 881 (ChatServerMessage),

Je pense que le packet "6362" je trouve cet identifiant car j'enlève pas la bonne taille de header (54) car j'ai remarqué que quand je modifie la taille (58 par exemple soit 1 byte de plus ou bien 62) je reçois certains packet que je ne recevais pas avant (par exemple si j'envoie un message privé je reçois plusieurs packet "6362").

Quelqu'un peut-il m'aiguiller sur la décomposition des packets plus dans le détail ? Par exemple, le packet ci-dessous me donne 6362 en ID:

24 FD 52 AC 49 59 2C AE 2B B8 BC FF 08 00 45 00 00 53 3B 31 40 00 34 06 CA C5 D5 F8 7E 3C C0 A8 2B D1 15 B3 F2 1E 2B 44 FC 2A 6F 34 6A 2C 50 18 00 ED 21 3F 00 00 0D C9 28 09 00 01 6C 5A 97 0C 4F 00 08 6C 31 68 69 33 71 32 6B CD 81 84 CC 88 19 00 0E 49 6E 66 65 72 6E 6F 2D 61 6E 74 6F 6E 79

Voici la manière dont je décode le packet :


puis

Code:
byte[] bytes = packet.getByteArray(0, packet.size());
                 
                    /*
                     * Obtenir le payload
                     */
                    ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length - headerSize); // headerSize = cumule des header [54]
                    for(int i = headerSize ; i < bytes.length ; i++){ // Suppression des 54 premiers byte pour ne garder que la partie payload
                        byteBuffer.put(bytes[i]);
                    }
                    byte[] payload = byteBuffer.array();
                 
                    try {
                        String code1 = String.format("%02X", payload[0]);
                        String code2 = String.format("%02X",  payload[1]);
                     
                        Conversion conversion = new Conversion(code1 + code2);
                        String bin = conversion.toBin();
                        String binId = bin.substring(0, bin.length() - 2); // suppression des 2 derniers caractères
                        int id = conversion.binToDec(binId);
                        if(id>0){
                            System.out.println("sizeHeader[" + headerSize + "] id : " + id + " - " + PacketIdentificator.get(id));
                        }
                    } catch (Exception e) {
                        // e.printStackTrace();
                    }

Code:
public class Conversion {
 
    private String _hex;
 
    public Conversion(String hex){
        this._hex = hex;
    }
 
    /**
     * Obtenir la valeur décimal
     * @return
     */
    public int toDec(){
        return binToDec(binUpdate(toBin()));
    }
 
    /**
     * Obtenir la valeur binnaire
     * @return
     */
    public String toBin() {
        String hex = _hex;
        String bin = "";
        String binFragment = "";
        int iHex;
        hex = hex.trim();
        hex = hex.replaceFirst("0x", "");

        for (int i = 0; i < hex.length(); i++) {
            iHex = Integer.parseInt("" + hex.charAt(i), 16);
            binFragment = Integer.toBinaryString(iHex);

            while (binFragment.length() < 4) {
                binFragment = "0" + binFragment;
            }
            bin += binFragment;
        }
        return bin;
    }

    public String binUpdate(String bin) {
        return bin.substring(0, bin.length());
    }

    public int binToDec(String bin) {
        return Integer.parseInt(bin, 2);
    }
}
Ainsi pour chaque packet arrivant j'en extrait l'ID, la fonction de découpe vous parait-elle bonne ?
Est-ce que c'est bien normal que mon 'headerSize' est systématiquement 54

J'ai entendu parlé de plusieurs packet en 1 seul, est-ce que ça pourrait expliquer que j'en loupe certains ?
J'ai aussi entendu parlé de plusieurs packet de données qu'il faut cumuler pour en extraire toutes les données ?

Merci de votre aide
 
Dernière édition:

DevChris

Membre Actif
Inscrit
12 Avril 2017
Messages
138
Reactions
24
#2
Hello, en C# du moins, on peut faire un truc comme ça :
C#:
namespace YourProject.YourForlder
{
    using System;
    using YourProject.Abstractions.Interfaces;
  
    public static class MessageParser
    {
        public static bool TryParse(IReader reader, out int messageId, out int dataLength, out byte[] data)
        {
            messageId = 0;
            dataLength = 0;
            data = null;

            if (reader.BytesAvailable < 2)
                return false;

            var header = reader.ReadValue<short>();
            messageId = header >> 2;

            var dataLengthBytesCount = header & 0x3;

            if (reader.BytesAvailable >= dataLengthBytesCount)
            {
                if (dataLengthBytesCount < 0 || dataLengthBytesCount > 3)
                    throw new ArgumentOutOfRangeException(nameof(dataLengthBytesCount));

                for (var i = dataLengthBytesCount - 1; i >= 0; i--)
                    dataLength |= reader.ReadValue<byte>() << (i * 8);
            }

            if (dataLength <= 0) return false;
            if (dataLength > reader.BytesAvailable)
                return false;

            if (data == null && dataLength >= 0)
            {
                if (dataLength == 0)
                    data = new byte[0];

                // enough bytes in the buffer to build a complete message
                if (reader.BytesAvailable >= dataLength)
                {
                    data = reader.ReadArray<byte>(dataLength);
                }
                // not enough bytes, so we read what we can
                else if (dataLength > reader.BytesAvailable)
                {
                    data = reader.ReadArray<byte>(reader.BytesAvailable);
                }
            }
            //second case : the message was split and it missed some bytes
            if (data != null && dataLength != 0 && data.Length < dataLength)
            {
                var bytesToRead = 0;

                // still miss some bytes ...
                if (data.Length + reader.BytesAvailable < dataLength)
                    bytesToRead = reader.BytesAvailable;

                // there is enough bytes in the buffer to complete the message :)
                else if (data.Length + reader.BytesAvailable >= dataLength)
                    bytesToRead = dataLength - data.Length;

                if (bytesToRead != 0)
                {
                    var oldLength = data.Length;
                    Array.Resize(ref data, (data.Length + bytesToRead));
                    Array.Copy(reader.ReadArray<byte>(bytesToRead), 0, data, oldLength, bytesToRead);
                }
            }

            return true;
        }
    }
}
Que tu peux utiliser comme ça :
C#:
var isValid = MessageParser.TryParse(readerWithData, out int messageId, out int dataLength, out byte[] data);
MyManager.Deserialize(data, dataLength, messageId);
ou comme ça :
C#:
while(MessageParser.TryParse(readerWithData, out int messageId, out int dataLength, out byte[] data))
{
     MyManager.Deserialize(data, dataLength, messageId);
}
Ce qui te permet de recuperer id, longueurs des données, et données de chaque message à la reception des données dans ta socket.
 
Inscrit
31 Mars 2016
Messages
33
Reactions
0
#3
J'ai aussi entendu parlé de plusieurs packet de données qu'il faut cumuler pour en extraire toutes les données ?
La manière dont tu récupères les packets peut différer selon 2 points :
- Combien de temps tu attends avant de lire sur la socket
- Combien de bytes tu lis sur la socket

En fonction de ça, si tu lis avec un buffer trop petits (genre tu lis 2048 bytes par 2048 bytes) et qu'un paquet complet est plus grand que le buffer (exemple : le paquet qui contient les données des maps) tu vas lire un paquet entier en deux buffers.

Après je ne sais pas si lire trop vite sur la socket (sans delay) peut permettre au client de récupérer un paquet incomplet que le server n'aurais pas envoyé complétement.
 
Inscrit
25 Novembre 2015
Messages
169
Reactions
20
#4
Hello, en C# du moins, on peut faire un truc comme ça :
C#:
namespace YourProject.YourForlder
{
    using System;
    using YourProject.Abstractions.Interfaces;
 
    public static class MessageParser
    {
        public static bool TryParse(IReader reader, out int messageId, out int dataLength, out byte[] data)
        {
            messageId = 0;
            dataLength = 0;
            data = null;

            if (reader.BytesAvailable < 2)
                return false;

            var header = reader.ReadValue<short>();
            messageId = header >> 2;

            var dataLengthBytesCount = header & 0x3;

            if (reader.BytesAvailable >= dataLengthBytesCount)
            {
                if (dataLengthBytesCount < 0 || dataLengthBytesCount > 3)
                    throw new ArgumentOutOfRangeException(nameof(dataLengthBytesCount));

                for (var i = dataLengthBytesCount - 1; i >= 0; i--)
                    dataLength |= reader.ReadValue<byte>() << (i * 8);
            }

            if (dataLength <= 0) return false;
            if (dataLength > reader.BytesAvailable)
                return false;

            if (data == null && dataLength >= 0)
            {
                if (dataLength == 0)
                    data = new byte[0];

                // enough bytes in the buffer to build a complete message
                if (reader.BytesAvailable >= dataLength)
                {
                    data = reader.ReadArray<byte>(dataLength);
                }
                // not enough bytes, so we read what we can
                else if (dataLength > reader.BytesAvailable)
                {
                    data = reader.ReadArray<byte>(reader.BytesAvailable);
                }
            }
            //second case : the message was split and it missed some bytes
            if (data != null && dataLength != 0 && data.Length < dataLength)
            {
                var bytesToRead = 0;

                // still miss some bytes ...
                if (data.Length + reader.BytesAvailable < dataLength)
                    bytesToRead = reader.BytesAvailable;

                // there is enough bytes in the buffer to complete the message :)
                else if (data.Length + reader.BytesAvailable >= dataLength)
                    bytesToRead = dataLength - data.Length;

                if (bytesToRead != 0)
                {
                    var oldLength = data.Length;
                    Array.Resize(ref data, (data.Length + bytesToRead));
                    Array.Copy(reader.ReadArray<byte>(bytesToRead), 0, data, oldLength, bytesToRead);
                }
            }

            return true;
        }
    }
}
Que tu peux utiliser comme ça :
C#:
var isValid = MessageParser.TryParse(readerWithData, out int messageId, out int dataLength, out byte[] data);
MyManager.Deserialize(data, dataLength, messageId);
ou comme ça :
C#:
while(MessageParser.TryParse(readerWithData, out int messageId, out int dataLength, out byte[] data))
{
     MyManager.Deserialize(data, dataLength, messageId);
}
Ce qui te permet de recuperer id, longueurs des données, et données de chaque message à la reception des données dans ta socket.

cf. https://gist.github.com/thenameless314159/75eb9a91aa07e53192a73ed256adbca6
Merci à toi
 
Haut Bas