1.29 [Journal de Bord] Analyser ses premiers paquets

Inscrit
22 Février 2020
Messages
26
Reactions
18
#1
Bien le bonjour,

J'ouvre un petit journal de bord inclusif pour partager mon apprentissage avec les petits et éventuellement recevoir un petit coup de pouce des plus grands qui passeraient par là. Mon objectif à terme est d'exploiter les données transitant entre le client et le serveur pour assister mon bot pixel (c'est mon bébé chéri que j'ai mis 10 jours à coder, sisi :inlove:) et le rendre plus puissant. Pour ça, il va falloir comprendre le protocole de Dofus 1: c'est le sujet de ce journal de bord.
Comme nous sommes quand même dans la partie Questions/Réponses, pourquoi ne pas commencer avec notre 1er problème et tenter d'y répondre ? Prêt ? C'est parti !

Comment détecter et décoder les paquets nous indiquant qu'une ressource (ici, un arbre) est en train d'être récoltée par un autre ?

1. Dabord toi chercher, après toi parler
Après plusieurs heures de recherche dans les tréfonds de Cadernis, les vénérables tutos de Labo et Bouh2 ou réponses d'internautes nous indiquent qu'il nous faut un sniffer comme Wireshark auquel on appliquera un filtre de capture tcp port 5555 and len > 66 pour sniffer de manière sélective les paquets qui nous intéressent. Ensuite on se connecte en jeu, on trouve une petite map tranquille histoire de pas être inondée de paquets par des bots qui traversent la map, j'ai donc pour ma part choisi la Merveilleuse Forêt d'Astrub worst idea ever:p On lance la capture, un petit click sur un arbre et hop dès qu'il est récolté j'arrête la capture de paquets. Facile non ? Sauf que...

En regardant le contenu des paquets, je me rends compte que c'est du bordel de chinois ce protocole ! o_O
GA0;1;90128766; Kéza koiii ?! Mais dis donc bouh2, tu m'avais vendu du rêve dans ton tuto en me faisant croire que c'était aussi facile que de lire "Bonjour|123":'(
Première réaction : je me persuade que j'ai du louper une étape de décodage, j'essaie des conversions en tout genre entre décimal/hexa/ascii sans rien comprendre jusqu'à me rendre à l'évidence que... faut fouiller dans le code source mon coco, comme le dit si bien tonton Labo dans ses tutos.

2. L'expérimentation/observation/déduction est ta meilleure amie
Du coup après plusieurs heures de recherche et d'incompréhension, le seul mot clé que j'arrive à capter est "GA" pour Game Action, mais je ne trouve ni la correspondance des id des actions, ni rien d'autre de très utile. Du coup, une seule solution: on va faire chauffer wireshark hell yeah, baby !
Je load les sources partagées de Maxoubot et trouve des fichiers .txt contenant les id des map et des ressources pour m'aider à trouver une correspondance. Je me reco sur notre map de la forêt d'astrub sur une map tranquille et test des actions simples comme se déplacer sur la map, couper un chataigner, puis un frêne et ainsi de suite plusieurs fois en analysant systématiquement les paquets pour identifier une éventuelle redondance. Bingo, ça fonctionne ! Bah forcément coco, c'est du chinois, mais du chinois logique !


Voilà ce que j'arrive à déduire :
-lorsque je clique sur une ressource et que mon perso court pour la chopper, mon client envoyait un paquet composé de GA500cell_id;
-lorsque mon perso arrive à commencer à couper un arbre, je recevais un paquet dont le data est composé de GA1;501;player_id;cell_id;harvest_delay;
-suivi d'une donnée indiquant que la ressource n'est plus dispo sous la forme GDF|cell_id;2;0
-le chiffre 2 est changé selon le moment d'indisponibilité de la ressource: 3 lorsque je viens de terminer de couper l'arbre, et 4 lorsque la ressource était déjà indisponible, lors d'un changement de map par exemple.


Sympa, non ? Pour ma part, ces infos devraient me suffire à intégrer la détection que je souhaite à mon bot pixel mais devoir déduire tous les types de paquets "à la main" en cherchant désespérément un mot clé correspondant dans le code quelque part me semble suicidaire. Dites moi M'sieur l'Professeur Blue Dream, z'auriez pas un p'tit filon pour trouver les correspondances des id plus facilement ? C'est que DataProcessor.as m'voyez... c'est un peu le boxon. Y'aurait pas un fichier magique qui nous dit tout un peu comme map.txt de Maxoubot ?

C'est fini pour aujourd'hui, j'espère que ça aura aiguillé les plus perdus d'entre nous ou fait rire nos vieux singes du forum :teeth:
La suite au prochain épisode, stay tuned !
 
Dernière édition:

tazman59

Contributeur
Inscrit
20 Decembre 2012
Messages
148
Reactions
25
#2
Plop !
Alors déjà merci de partager avec nous tes retours d'expérience.
Et non, malheureusement il n'y a pas de fichier CreateYourBot.as dans les sources :p
L'observation et la déduction sont des points clé du développement d'un bot Dofus.
Si tout était si simple, Cadernis n'existerait sûrement pas. :vicieux:

À force de persévérance, plus logique à tes yeux, tout deviendra et un vieux singe, tu deviendras. OK c'était nul.

A+
 
Inscrit
3 Février 2020
Messages
4
Reactions
0
#3
je ne comprend pas c'est un bot pixel mais qui annalyse les paquet?
car normalement un pixel bot va plutot jouer sur la couleur pour analyser l’état d'une resource
 
Inscrit
22 Février 2020
Messages
26
Reactions
18
#4
Bonjour les codeurs fous,

Mon week end prolongé de deux jours m’a permis de creuser un peu plus le sujet en profondeur et je dois vous avouer que j’ai du mal à me contenter des quelques paquets présentés jusqu'ici... c'est à dire que ce protocole et le code source de Dofus 1.29 me font un peu le même effet que Light Yagami après avoir ramassé le Death Note, voyez-vous ?

Aujourd’hui on va apprendre à chercher dans le code le secret de ces fameux paquets, parce que déduire avec wireshark c’est mignon mais on peut faire mieux ! Avant de rentrer dans le vif du sujet, place au
tazman59 Merci d’avoir pris le temps de répondre, ça me touche ! Quand j’ai écrit ce premier post, j’avais parcouru les sources pendant des heures sans trouver la logique des paquets, donc ça me semblait fou de réaliser un sniffer. Maintenant je crois avoir compris la méthodologie que je vais essayer de présenter aujourd'hui. Ceci dit je suis surpris qu’il n’y ait pas une sorte de « fichier communautaire » où tout le monde peut apporter sa petite brique et répertorier le décodage d’un paquet. Ou alors vous créez des algorithmes qui retrouvent et traduisent automatiquement les sources décodant ces paquets ? Mais ça me semble un peu trop fumé pour être vrai :vicieux:

Deathhat Mon bot est pour l’instant 100% « pixel », c’est à dire qu’il utilise le template matching de la librairie opencv pour reconnaître les ressources, les soleils, le pnj de la banque, etc. (+quelques clics de flemmard à des positions préenregistrées parce que je voulais avoir vite un résultat pour me motiver). En réalité, c’est une grosse faiblesse de fonctionner comme ça, et l’écoute des paquets envoyé par le serveur me permettrait d’obtenir des infos difficiles à obtenir avec du template matching. Ici mon soucis c’est la réactivité du bot à changer de cible quand un autre choppe la ressource avant lui. En réalité, je me rends compte qu'une fois qu'on sait sniffer et décoder ces paquets, la détection pixel est quasi obsolète... vu qu'on peut obtenir toutes les positions des objets en convertissant les cell_id reçues en positions (x, y) selon la résolution de l'ordi. Mais p'tetre qu'en pratique c'est plus tricky que ça, à voir dans la suite de ce jdb donc !

3. Comment chercher dans le code source la logique de chaque paquet ?
Après avoir fait joujou avec Wireshark, il est temps maintenant de chercher une manière un peu moins chronophage de décoder ce paquets. En regardant le destinataire et l'expéditeur des paquets, on remarque que la majorité de ceux-ci sont envoyés depuis le serveur vers le client. Qu'est-ce que ça implique ? Que le client doit pouvoir les décoder, pardis ! et qui dit client, dit... code source à éplucher :cool:
Mes premières recherches m'indiquaient qu'on devrait regarder du coté de "DataProcessor"... ça semble plus logique maintenant, non ?
Pour commencer, on va s'exercer avec un paquet pas trop compliqué. Retour à ma forêt préférée, un petit click sur un chataigner (ce sont les plus beaux), je récupère le paquet OQ80662292|18 et on va voir dans notre fichier DataProcessor.as
Voyons voir ce qu'on y trouve...

WARNING: J'ai jamais codé en actionscript ni C#... J'utilise exclusivement Python, j'avais codé un bot en autoIT et bricolé des petits jeux en C++ quand j'étais ado

Code:
_loc1.postProcess = function (sType, sAction, bError, sData)
{
        switch (sType)
{
           case "H":    
{
               switch (sAction)        
{
                    case "C":

  // (. . .) ouais y en a beaucoup des 'case', faut y aller franco sur la mollette...

           case "O":   // Oh putaing c'est lui qu'on cherche, yeah !
{
               switch (sAction)
{
// (. . .)
                    case "Q": //Bingo, trouvé !
{
                        this.aks.Items.onQuantity(sData.substr(2));
                        break;
}
// (. . .)
}
Et là on peut voir que pour le paquet de type OQ il envoie le contenu après OQ dans la fonction onQuantity du fichier Item de Aks. C'est une piste intéressante non ? Partons à l'exploration de ce fichier sans plus attendre !

Code:
    _loc1.onQuantity = function (sExtraData)
    {
        var _loc3 = sExtraData.split("|");
        var _loc4 = Number(_loc3[0]);
        var _loc5 = Number(_loc3[1]);
        this.api.datacenter.Player.updateItemQuantity(_loc4, _loc5);
    };
Là on voit comment il split les données, et les renvoie dans une autre fonction. C'est important de comprendre comment se fait le split, on trouvera sûrement à quoi correspondent c'est différents split dans le code de la fonction updateItemQuantity. Prêt ? Allez on s'arrête pas en si bon chemin ! Le nouveau fichier qu'on doit parcourir doit certainement s'appeler Player.as dans le dossier datacenter... non ?
Eh bien non, le fichier s'appeller LocalPlayer.as, really -_- Décidément je comprends vraiment rien à l'actionscript mais c'est pas grave, y a des redondances synthaxiques entre les langages de haut niveau, ça me suffit pour survoler ce code et en tirer quelque chose.
Code:
    _loc1.updateItemQuantity = function (nItemNum, nQuantity)
    {
        var _loc4 = this.Inventory.findFirstItem("ID", nItemNum);
        var _loc5 = _loc4.item;
        _loc5.Quantity = nQuantity;
        this.Inventory.updateItem(_loc4.index, _loc5);
    };
Et voilà on a toutes les infos qu'il nous faut et on peut résumer notre paquet comme OQ item_id|inventory_quantity, cool non ? :cool:

4. El Famoso paquet GDM, my lovely nightmare
Décoder ce protocole, c'est comme un haribo -oui oui les petits ourson làà- : une fois qu'on commence, on ne sait plus s'arrêter ! Sauf que le paquet GDM, c'est du gros gibier :'( Les quelques sujets du forum qui en parlent m'ont pas mal aiguillé sauf qu'il y a une info que j'arrive pas à trouver. C'est là que je suis contraint d'exécuter la danse de la pluie la danse d'invocation des grands singes pour me venir en aide:

Une fois que MapData est déchiffrée avec la clé du paquet grâce à prepareKey et decypherData, les données sont envoyées dans api.gfx.buildMap que je ne retrouve pas... Où est-ce que je retrouver le traitement de ces données ? Comment je peux extraire les infos ? Qu'est-ce qui y est contenu de juteux ? Le temps de respawn des ressources ? Les données pour chaque cell_id I guess ? En tout cas, j'ai analysé tous les autres paquets (enfin ceux de la map quand je suis seul) et il manque clairement des données qui doivent se trouver dans cette fameuse MapData...
EDIT: j'ai déniché les infos, elles se trouvent dans le fichier Maps.vb et Maphandler.vb des sources de Maxoubot… sacré gaillard ce Maxence, une vraie petite d'or qu'il nous a légué celui là. Je lui tire mon chapeau ! On décortiquera tout ça ensemble bientôt :)


Voilà c'est tout pour aujourd'hui, n'hésitez pas à me dire quand j'écris de la merde comme ça j'edit et évite de propager des infos erronées. Une fois la MapData élucidée, je vais mettre en pratique toute cette théorie dans mon bot avec pyshark. Bien sûr on fera ça ensemble, comme ça vous pourrez me taper sur les doigts à chaque fois que je code comme un cochon, c'est-à-dire tout le temps :p
Stay tuned !
 
Dernière édition:

tazman59

Contributeur
Inscrit
20 Decembre 2012
Messages
148
Reactions
25
#5
Plop !
C'est remoa.

J'ai oublié de préciser quelques petites informations dans mon dernier message.

Il existe un MINI fichier "CreateYourBot" disponible sur Cadernis :cool:,
le lien : https://cadernis.fr/index.php?attachments/diagramme_des_packets_dofus-png.35/
C'est un diagramme représentant la succession de packets reçus et envoyés de la connexion à l'entrée en jeu visiblement.

De plus, il existe des bots Open Source disponibles sur le web qui pourraient répondre à certaines de tes questions (du style comment parser une map etc.)

J'aimerais pouvoir t'aiguiller encore plus mais je n'ai pas énormément de temps pour moi en ce moment, et étant donné que je n'ai pas touché à la 1.29 depuis quelques années, je pense que d'autres le feront bien mieux que moi !

A+
 
Inscrit
22 Février 2020
Messages
26
Reactions
18
#6
Okay les bambini aujourd'hui on va faire du très lourd : démystifier les secrets les plus deep de cette fameuse MapData.
Si si, grâce à Maxou, cet homme invisible que je ne connaîtrai probablement jamais de mon vivant, c'est possible ! Par contre mon cerveau ne le remercie pas, maintenant il ressemble à une mignonette Côte D'or (au Lait, bien sûr) laissée au soleil, merci maxou quoi… ^-^
Tazman59 Yep j'avais vu ce diagramme, mais il me semblait incomplet voire pas toujours très clair… ceci dit c'est vrai qu'y être retourné jeter un œil après tant de recherche et d'apprentissage, je dois avouer que je le trouve de plus en plus sensé ce p'tit diagramme, thx ! Comme quoi c'est bien en forgeant qu'o… bon ok j'arrête la philo ça me va pas :p

Pas de soucis c'est déjà cool de passer ici, et puis j'oserais jamais demander qu'on cherche à ma place (ce n'est même pas souhaitable pour moi). Par exemple l'idée c'était que si un dev qui travaille le parsing des map 1.29 passait par là, il m'aurait dit "tu trouveras ce que tu cherches dans le fichier Maphandler.vb de maxoubot".

5. Comment parser une map 1.29 ?
Vous voyez le dicton qui dit "Vaut mieux avoir une petite qui frétille ?" Eh ben là c'est le parfait contre-exemple, parce que notre question elle paraît petite mais en réalité elle est aussi grosse que celle de Rocco.

Commençons en douceur : en suivant notre méthodologie, DataProcessor.as nous a délicatement invité à chercher dans le fichier Game.as (du dossier Aks) duquel nous trouverons le code de cette fameuse fonction "onMapData". Rien de bien sorcier, elle split le paquet et envoie ça dans une nouvelle fonction.
Actionscript:
                this.api.kernel.MapsServersManager.loadMap(_loc4, _loc5, _loc6);
Du coup, on retrouve ce fichier MapsServerManager pour trouver le code de loadMap. Il prépare la clef du paquet et l'envoie dans loadData…
Actionscript:
        this.loadData(sID + "_" + sDate + (this._aKeys[Number(sID)] != undefined ? ("X") : ("")) + ".swf");
loadData ? Kézako ? Impossible de trouver le code de ce loadData… Et merde. Un cul de sac ! Bon, ma petite curiosité me force à scroll un peu tout de même et je tombe sur le code sympa de parseMap (dans le même fichier donc) qui, après déchiffrage de mapData à l'aide de decypherData, nous renvoie dans
Actionscript:
        this.api.gfx.buildMap(_loc3, _loc9, _loc10, _loc11, _loc12, _loc13, _loc21);
où _loc13 sont nos datas déchiffrées tandis que les autres sont essentiellement des données non chiffrées issus du fichier .swf de la map. Hélas je n'arrive pas à retrouver buildMap et je me retrouve dans un autre cul de sac.
Au moins on aura pu comprendre jusqu'ici la structure du paquet GDM à savoir un map_id, une date et une clef pour déchiffrer les données du fichier .swf de la map. On a pas tout tout compris mais pas grave non ? Ah si ? Quoi, vous me dites qu'un vrai dev ne se satisferait pas aussi vite ? rip

Pas le choix faut changer de stratégie : éplucher maxoubot. Et là, à force de chercher, bingo !
Alors je vais pas vous copy paste le code mais plutôt tenter de résumer la structure de ces fameuses mapData et comment elles sont traitées. Une fois la mapData déchiffrée, la fonction uncompressMap de Maphandler.vb (dossier Modules) nous indique qu'elle suit une organisation en multiple de 10 caractères où chaque multiple correspond aux données d'une cell_id.
mapData = "cell_data1" + "celldata2" + ... + "celldata_n"
où cell_data_n.length() = 10

Ensuite, la fonction uncompressCell du même fichier nous explique comment sont traitées chacune de ces cell_data et là ça se corse.
La chaîne de 10 caractères est décodée par la fonction hashCodes faisant correspondre un entier à chaque caractère. Plutôt drôle manière de faire mais why not. Ensuite on tombe sur ce genre de synthaxe:
Actionscript:
                _loc5.layerObject2Num = ((_loc8(0) And 2) << 12) + ((_loc8(7) And 1) << 12) + (_loc8(8) << 6) + _loc8(9)
Really ? =.='
Je vais pas vous cacher avoir mis le temps que les poules mettent à avoir des dents pour comprendre ce que sont les opérations bitwise/bitshift (cf tuto Microsoft). Je sais pas quel l'individu a été aussi fou chez Ankama pour obscurcir à ce point la représentation du contenu d'une cellule mais bon j'imagine que ça fait le café.

En regardant l'utilisation de ce layerObject2Num dans la fonction loadRessources de Maps.vb (dossier Fichier), on peut comprendre qu'en fait les ressources "non fauchées" se retrouvent dans le mapData avec leur layerObject2Num associé à leur cellule. Ce qui semble plutôt logique vu qu'aucun paquet ne me permettait de connaître les cell_id des ressources déjà dispo sur la map.
D'après maxoubot, ressource fauchée = pas de layerObject2Num de la ressource, ce que je trouve plutôt étrange vu qu'il y a un layerObject2Interactive mais bon… why not ? A tester in situ donc!

Nous pouvons compléter le raisonnement en regardant comment sont traités les paquets GDF. Si une ressource venait à être fauchée après que la map ait été chargée, le serveur envoie un paquet GDF qui sera traité avec:
Actionscript:
this.api.gfx.setObject2Interactive(_loc6, _loc9, 2); //si ressource de nouveau dispo
// ou alors
this.api.gfx.setObject2Frame(_loc6, _loc7); //si la ressource n'est pas dispo
Même si je ne sais toujours pas comment trouver le code de toutes ces fonctions api.gfx (je suppose que Maxou a du y arriver lui), on peut facilement déduire par son appellation que ça mettra à jour le mapData.
Pour conclure, mapData contient le layerObject2Num des ressources à leur cell_id correspondant et sont par défaut "non fauchées". Lors de chaque changement de map, le client reçoit du serveur le paquet GDF avec toutes les ressources fauchées pour pouvoir être updated. Voilà, la boucle est bouclée ! Stylé hein ? :cool:

Il reste encore des petits mystères à résoudre comme le .movement propre à chaque cell mais les infos qu'on a déjà là nous permettrons de nous passer complètement de la détection pixel de nos arbres préférés.
Bien entendu, ce journal de bord manquerait de saveur si je terminais ce chapitre sans une danse invocatrice petite question à l'égard des vieux singes du forum en quête de divertissement:

Please dites moi où je peux trouver api.gfx ça me démange le kiki jpp :'(:'(:'(

Voilà c'est fini pour aujourd'hui. Comme d'habitude n'hésitez pas à venir lâcher un com et surtout me rectifier si je dis de la merde parce que y a de fortes chances que j'interprète trop vite ce que je démystifie. Si c'est pas clair dites le moi aussi comme ça j'améliore mes skills de synthèse et de communication. Prochain épisode: mise en pratique de toute cette théorie et adaptation des algorithmes de parsing en python, hell yeah baby!
 
Dernière édition:
Inscrit
9 Avril 2018
Messages
4
Reactions
2
#7
Ou se trouve la suite de cette magnifique aventure :(
 
Haut Bas