[Présentation]
Bonjour à tous,
C'est mon tout pemier message sur ce forum donc je vais faire une petite présentation de moi même x)
Je joue à Dofus depuis 2005 (mes 7 ans), et cela par période.
J'adore ce jeu et j'avais toujours "rêvé" de faire un bot, à défaut d'en avoir déjà utilisé plusieurs il y a de cela quelques années :p
Je suis particuliérement un touche à tout dans la vie, très autodidacte et indépendant dans mes projets bien que j'ai souvent du mal à les finir suite à une perte de motivation certainement.
Côté programmation / électronique, j'ai fait du C++ / Python / Arduino / Création de cartes électroniques / Php / SQL / Flutter / ...
Pleins de petits programmes sympas sur le moment mais inutiles en général, sauf avec Flutter / Arduino où j'avais créé un arrosage automatique contrôlé par l'Arduino pilotant des électrovannes en suivant des calendriers d'arrosages, et tout cela modifiable depuis le téléphone x)
Et actuellement je finis la réparation d'une trancheuse à pain en re-créant une carte électronique + pilotage avec l'Arduino.
Concernant Dofus, mon projet est le suivant, créer un bot mitm pour commencer sur Dofus Retro [1.29].
J'ai prototypé une interface avec adobe XD puis commencé à la développer avec Flutter.
J'attendais mon pote d'école d'ingé qui a fait option cyber-sécurité et qui est très débrouillard comme moi pour qu'il s'occupe de la partie réseau mais bon il est v'la occupé en ce moment et j'ai envie d'avancer.
Cela fait plusieurs jours que j'épluche le forum, et internet pour avoir une vision globale du sujet.
Si je viens demander de l'aide, c'est qu'actuellement je suis bloqué et que ça devient viscérale xD.
[Configuration]
Concernant ma configuration :
- Mac OS Monterey 12.2
- Anaconda + Python pour les programmes tests rapides
- Sublime Text
- Wireshark pour lire les paquets entrants et sortants
- Source de Maxoubots (trouvé sur le forum)
- Code Source Dofus (trouvé sur le forum)
Question 1 : Quelqu'un connaitrait un décompilateur swf pour Mac ?
(Tous ceux que j'ai essayé ne sont plus compatibles avec ma version Mac)
[Objectif Actuel]
Mon objectif actuel est d'arriver à coder / décoder toutes les trames qui me seront utiles pour mon bot.
Cela signifie que la partie envoie / réception des sockets en mode automatique je ne l'ai pas encore abordée et la garderai pour quand je serai en mesure d'être sûr d'avoir bien coder / décoder les trames utiles.
J'ai décidé de changer de technique de travail et d'évite de m'éparpiller, je me connais sinon je fais Interface / Sockets / IA / ... / tout en même temps, v'la le foutoir :o
[Procédure de décodage des trames]
- Sur Wireshark, je filtre selon tcp / l'ip / la longueur des trames
- Lancement de la capture Wireshark
- Sur le client dofus je fais l'action désirée
- J'arrête la capture Wireshark
- J'analyse le(s) paquet(s) Client envoyé(s)
- J'analyse le(s) paquet(s) Serveur reçu(s)
- Je recherche dans les sources Dofus pour en comprendre les tenants et aboutissants
- Je recherche sur le forum / internet si je bégaye
- Eventuellement chez Maxoubot si jamais j'ai vraiment du mal
[Exemple pour mise à l'appui]
Dans le cas d'un déplacement (d'une case à une autre), voilà les trames dans l'ordre :
Envoyée : GA001biTaiWhh6
Reçue : GA0;1;xxx;aihbiTaiWhh6
Envoyée: GKK0
Reçue : BN
Analyse de la première trame envoyée :
Lors de l'analyse la première trame envoyée "GA001biTaiWhh6" :
Je recherche dans l'intégralité de la source Dofus "GA" et l'unique occurence se situe dans le fichier GameActions dans la fonction suivante :
Code
Cliquez pour révéler
Cliquez pour masquer
_loc1.sendActions = function (nActionType, aParams)
{
var _loc4 = new String();
this.aks.send("GA" + new ank.utils.ExtendedString(nActionType).addLeftChar("0", 3) + aParams.join(";"));
};
D'après la fonction addLeftChar trouvée dans le fichier ExtendedString, elle complète le nActionType de "0" sur la gauche afin que la chaine de caractère finale fasse 3 caractères.
On en déduit donc que se déplacer est une nActionType = 1.
Et que l'envoie d'un déplacement sur Dofus se fait de cette manière : GA001[Déplacement]
Il ne reste plus qu'à trouver à quoi correspond "biTaiWhh6".
En analysant les déplacements une case par une case sur une map très grande, on reçoit le déplacement sous forme de 3 caractères.
On s'aperçoit rapidement que le premier caractère est compris entre [a:h].
Ce qui correspond à la direction empruntée par l'utilisateur lors du déplacement :
a = Est
b = Sud-Est
...
g = Nord
h = Nord-Est
Et que les 2 derniers caractères représentent la cellId sur laquelle on va aller.
On remarque que la base de ces caractères est la base 64 = [a,b,x...x,A,B,x...x,0,1,x...x,9,-,_]
d'où :
aa = 0
a_ = 63
ba = 64
ca = 128
c_ = 191
(Par exemple)
Si l'on revient à "biTaiWhh6", alors on s'aperçoit qu'en découpant la chaine de caractères tous les 3 caractères, on obtient 3 déplacements indépendants :)
Cela nous donnerait la combinaison de déplacements suivante :
Sud-Est - 557
Est - 560
Nord-Ouest - 506
Analyse de la première trame reçue :
Lors de l'analyse de la première trame reçue "GA0;1;xxx;aihbiTaiWhh6" :
En suivant la même procédure, on en déduit que la "commande" (GA0) est sur 3 caractères, avec un possible addRightChar(0,3) (par déduction).
On retrouve l'identifiant de la GA (1).
On trouve l'identifiant du personnage.
Ainsi que le déplacement qui lui est associé.
Trame reçue pour un déplacement de personnage : GA0;1;[playerId];[Deplacement]
Pour conclure ce mini apparté, maintenant je sais donc "coder" / "décoder" une suite de déplacements version Dofus xD
Bon et maintenant, passons à la suite !!!
[Décodage d'une map]
La vérité de la vérité, je me suis dit, bon ben c'est fastosh en fait !
Mais le vrai visage de cette fausse vérité fantasmée s'est dévoilée à moi petit à petit mdr
Voici un exemple de trame lors du changement de map :
GDM|6689|0706131721|28694150315578304b226f26515f6a3c5d2e3947363d777e226c572532423e503362602a3c4c59365477466d4c707854592333567d785b7b6e2e3a627c204361663821682264322a2d46764032234b41453e47692a2f4b7b3d6a5f6723673a4931415926583e435e39725d39754b74594048477b583e3673273f324e347e5a41633a645635522f5971422a6a715c414166743139447a622532355a665b684c30365d74385a6a21702532354f4d22296b662e722f39764a66436e4057232e643e2a75616266795d4c345b69337c2c7c3154575669473a74624a4e5738383b4628432d4cBT1665489037521fC0
Analyse de la trame
1) On retrouve "GDM" dans DataProcessor et on arrive sur la fonction suivante : this.aks.Game.onMapData(sData.substr(4));
2) Dans onMapData() on arrive sur la fonction suivante this.api.kernel.MapsServersManager.loadMap(_loc4, _loc5, _loc6);
_loc4 = 6689
_loc5 = 0706131721
_loc6 = 28694150315578304b226f26515f6a3c5d2e3947363d777e226c572532423e503362602a3c4c59365477466d4c707854592333567d785b7b6e2e3a627c204361663821682264322a2d46764032234b41453e47692a2f4b7b3d6a5f6723673a4931415926583e435e39725d39754b74594048477b583e3673273f324e347e5a41633a645635522f5971422a6a715c414166743139447a622532355a665b684c30365d74385a6a21702532354f4d22296b662e722f39764a66436e4057232e643e2a75616266795d4c345b69337c2c7c3154575669473a74624a4e5738383b4628432d4cBT1665489037521fC0
3) En regardant la définition de loadMap(sID, sDate, sKey) on en déduit que la trame est sous cette forme : GDM|mapId|mapDate|mapKey
4) Dans loadMap(..) on arrive sur la fonction suivante this.loadData(sID + "_" + sDate + (this._aKeys[Number(sID)] != undefined ? ("X") : ("")) + ".swf");
A partir d'ici je suis bloqué car je ne trouve pas la fonction loadData(..).
La fonction loadMap(...)
Cliquez pour révéler
Cliquez pour masquer
_loc1.loadMap = function (sID, sDate, sKey)
{
this._lastLoadedMap = undefined;
if (!_global.isNaN(Number(sID)))
{
if (sKey != undefined && sKey.length > 0)
{
this._aKeys[Number(sID)] = dofus.aks.Aks.prepareKey(sKey);
}
else
{
delete this._aKeys[Number(sID)];
} // end if
} // end else if
this.loadData(sID + "_" + sDate + (this._aKeys[Number(sID)] != undefined ? ("X") : ("")) + ".swf");
};
En analysant le code de la fonction loadMap, il semble envoyer la Key dans le tableau aKeys[] (Certainement pour accéder localement à la variable).
Pour this.loadData(sID + "_" + sDate + (this._aKeys[Number(sID)] != undefined ? ("X") : ("")) + ".swf");
J'en ai déduit qu'à ce moment là, le client chargeait la map désirée en rechercheant le swf correspondant à la map.
La notion "X" : "", je ne vois pas à quoi elle sert pour le moment car si la Key de l'Id existe, alors rajouter "X", sinon ne rien rajouter.... Moueh mdrr
Question 2 : Comment trouver ce foutu fichier avec la fonction loadData(..) à l'intérieur ?
[Parser la map]
J'ai l'identifiant de la map, j'ai la date de la map, j'ai la clef de la map, j'ai les fichiers swf de la map grâce au client Dofus.
En fouillant dans le code, j'ai trouver parseMap dans MapsServerManager :
parseMap
Cliquez pour révéler
Cliquez pour masquer
_loc1.parseMap = function (oData)
{
if (this.api.network.Game.isBusy)
{
this.addToQueue({object: this, method: this.parseMap, params: [oData]});
return;
} // end if
var _loc3 = Number(oData.id);
if (this.api.config.isStreaming && this.api.config.streamingMethod == "compact")
{
var _loc4 = this.api.lang.getConfigText("INCARNAM_CLASS_MAP");
var _loc5 = false;
var _loc6 = 0;
while (++_loc6, _loc6 < _loc4.length && !_loc5)
{
if (_loc4[_loc6] == _loc3)
{
_loc5 = true;
} // end if
} // end while
if (_loc5)
{
var _loc7 = [dofus.Constants.GFX_ROOT_PATH + "g" + this.api.datacenter.Player.Guild + ".swf", dofus.Constants.GFX_ROOT_PATH + "o" + this.api.datacenter.Player.Guild + ".swf"];
}
else
{
_loc7 = [dofus.Constants.GFX_ROOT_PATH + "g0.swf", dofus.Constants.GFX_ROOT_PATH + "o0.swf"];
} // end else if
if (!this.api.gfx.loadManager.areRegister(_loc7))
{
this.api.gfx.loadManager.loadFiles(_loc7);
this.api.gfx.bCustomFileLoaded = false;
} // end if
if (this.api.gfx.loadManager.areLoaded(_loc7))
{
this.api.gfx.setCustomGfxFile(_loc7[0], _loc7[1]);
} // end if
if (!this.api.gfx.bCustomFileLoaded || !this.api.gfx.loadManager.areLoaded(_loc7))
{
var _loc8 = this.api.ui.getUIComponent("CenterTextMap");
if (!_loc8)
{
_loc8 = this.api.ui.loadUIComponent("CenterText", "CenterTextMap", {text: this.api.lang.getText("LOADING_MAP"), timer: 40000}, {bForceLoad: true});
} // end if
this.api.ui.getUIComponent("CenterTextMap").updateProgressBar("Downloading", this.api.gfx.loadManager.getProgressions(_loc7), 100);
this.addToQueue({object: this, method: this.parseMap, params: [oData]});
return;
} // end if
if (_loc5 && !this._bPreloadCall)
{
this._bPreloadCall = true;
this.api.gfx.loadManager.loadFiles([dofus.Constants.CLIPS_PERSOS_PATH + (this.api.datacenter.Player.Guild * 10 + this.api.datacenter.Player.Sex) + ".swf", dofus.Constants.CLIPS_PERSOS_PATH + "9059.swf", dofus.Constants.CLIPS_PERSOS_PATH + "9091.swf", dofus.Constants.CLIPS_PERSOS_PATH + "1219.swf", dofus.Constants.CLIPS_PERSOS_PATH + "101.swf", dofus.Constants.GFX_ROOT_PATH + "g0.swf", dofus.Constants.GFX_ROOT_PATH + "o0.swf"]);
} // end if
} // end if
this._bCustomFileCall = false;
if (this.api.network.Game.nLastMapIdReceived != _loc3 && (this.api.network.Game.nLastMapIdReceived != -1 && this.api.lang.getConfigText("CHECK_MAP_FILE_ID")))
{
this.api.gfx.onMapLoaded();
return;
} // end if
this._bBuildingMap = true;
this._lastLoadedMap = oData;
var _loc9 = this.getMapName(_loc3);
var _loc10 = Number(oData.width);
var _loc11 = Number(oData.height);
var _loc12 = Number(oData.backgroundNum);
var _loc13 = this._aKeys[_loc3] != undefined ? (dofus.aks.Aks.decypherData(oData.mapData, this._aKeys[_loc3], _global.parseInt(dofus.aks.Aks.checksum(this._aKeys[_loc3]), 16) * 2)) : (oData.mapData);
var _loc14 = oData.ambianceId;
var _loc15 = oData.musicId;
var _loc16 = oData.bOutdoor == 1 ? (true) : (false);
var _loc17 = (oData.capabilities & 1) == 0;
var _loc18 = (oData.capabilities >> 1 & 1) == 0;
var _loc19 = (oData.capabilities >> 2 & 1) == 0;
var _loc20 = (oData.capabilities >> 3 & 1) == 0;
this.api.datacenter.Basics.aks_current_map_id = _loc3;
this.api.kernel.TipsManager.onNewMap(_loc3);
this.api.kernel.StreamingDisplayManager.onNewMap(_loc3);
var _loc21 = new dofus.datacenter.DofusMap(_loc3);
_loc21.bCanChallenge = _loc17;
_loc21.bCanAttack = _loc18;
_loc21.bSaveTeleport = _loc19;
_loc21.bUseTeleport = _loc20;
_loc21.bOutdoor = _loc16;
_loc21.ambianceID = _loc14;
_loc21.musicID = _loc15;
this.api.gfx.buildMap(_loc3, _loc9, _loc10, _loc11, _loc12, _loc13, _loc21);
this._bBuildingMap = false;
};
Utilisée par onComplete(..)
Qui est utilisée par blabla, bref je n'ai pas l'impression que cette piste soit concluante bien que dans parseMap, la partie commençant à _loc9 semble intéressante.
J'ai trouvé sur les forum des algorithmes permettant de déchiffre la MapData comme ici https://cadernis.com/d/1790-système-de-décryptage-mapdata-dofus-1-29x (j'ai d'autres liens similaires) mais je n'ai pas trouvé / compris comment récupérer les MapData...
J'ai pourtant retrouvé les fonctions prepareKey et decypherData dans la source dofus mais aucune trace des mapData.
En décompilant les maps.swf ?
Question 3 : Où trouver les MapData ?
[Conclusion]
Hum...
Je vais m'arrêter là pour les questions, cela est suffisant selon moi pour l'instant.
Si quelqu'un arrive à me débloquer sur la question 3, je pourrai re-avancer sans le moindre doute x)
Récapitulatif des questions :
Question 1 : Quelqu'un connaitrait un décompilateur swf pour Mac ?
Question 2 : Comment trouver ce foutu fichier avec la fonction loadData(..) à l'intérieur ?
Question 3 : Où trouver les MapData ?
Si j'ai fait "long" avec des exemples, c'est principalement pour vous montrer que je ne viens pas les mains dans les poches, en espérant que cela compte pour vous ;)
Merci par avance !
Poloy