Recoder addCryptedHash() (HASH_FUNCTION)

Geuwp

Contributeur
Inscrit
16 Janvier 2015
Messages
37
Reactions
1
#1
Bonjour,

J'ai repris la création de bots sur Dofus 2.0 il y a maintenant une semaine, et je fais face à ma première difficulté : le hash chiffré envoyé avec certains paquets.

Pour expliquer le problème à tout le monde :
  1. Certains paquets (comme ExchangePlayerRequestMessage par exemple) incluent un hash chiffré avec les données du paquets
  2. Ce hash est vérifié (apparemment pas systématiquement, mais de manière aléatoire) par le serveur de jeu
  3. S'il est incorrect, alors le serveur nous déconnecte (voire nous bannis ?)

En examinant le code de ExchangePlayerRequestMessage (par exemple), on peut voir que c'est la fonction stockée dans NetworkMessage.HASH_FUNCTION qui s'occupe de générer ce hash :
JavaScript:
override public function pack(param1:ICustomDataOutput) : void
      {
         var _loc2_:ByteArray = new ByteArray();
         this.serialize(new CustomDataWrapper(_loc2_));
         if(HASH_FUNCTION != null)
         {
            HASH_FUNCTION(_loc2_);
         }
         writePacket(param1,this.getMessageId(),_loc2_);
      }
Mais quelle est cette fonction, et comment est-elle assignée à NetworkMessage.HASH_FUNCTION ?
J'a
  1. À la connexion, le serveur envoie un RawDataMessage qui est en fait un fichier SWF
  2. Le client exécute ce fichier SWF
  3. En analysant le code décompilé du SWF, on se rend compte que HASH_FUNCTION est assigné de cette manière :
    JavaScript:
    getDefinitionByName(§_a_-_---§.§_a_--_--§(-1820302786))["HASH_FUNCTION"] = this.addCryptedHash;
J'ai "nettoyé" la fonction addCryptedHash() et ça donne ceci :
JavaScript:
public function addCryptedHash(packetBytes:ByteArray) : void
{
    var authentificationManagerClass:Object = null;
    var gameServerTicketBytes:ByteArray = null;
   
    if(!this._hashKey)
    {
        gameServerTicketBytes = new ByteArray();
        gameServerTicketBytes.writeUTF(authentificationManagerClass.getInstance().getGameServerTicket());
        this._hashKey = HumanCheck.hash(gameServerTicketBytes);
    }
   
    var cryptedHash:ByteArray = new ByteArray();
    cryptedHash.writeBytes(HumanCheck.hash(packetBytes));
    cryptedHash.position = 0;
   
    var padder:_SEIWGELDMLL = new _SEIWGELDMLL();
    var cipher:_SEGGHWLLXDH = new _SEGGHWLLXDH(new _SEHLHWHWMOW(new _SOLLHGLEIG(this._hashKey), padder));
   
    padder.setBlockSize(cipher.getBlockSize());
    cipher.encrypt(cryptedHash);
   
    packetBytes.position = packetBytes.length;
    packetBytes.writeBytes(cryptedHash);
}
J'ai donc l'intention de recoder cette fonction dans mon MITM. Voyons les problèmes qui se posent :
  1. Obtenir le gameServerTicket : pas de problème, on le récupère dans le AuthenticationTicketMessage envoyé par le client
  2. Recoder HumanCheck.hash() : ça peut aller, la fonction n'est pas bien méchante
  3. Recoder les classes _SEIWGELDMLL, _SEGGHWLLXDH, _SEHLHWHWMOW et _SOLLHGLEIG : bon, là, ça commence à faire beaucoup...

D'où mes questions :
  1. Jusque-là, mon raisonnement est-il correct ? N'ai-je pas oublier quelque chose ?
  2. Est-ce que le code de ce RawDataMessage change fréquemment ?
  3. Est-ce que les classes ci-dessus ne sont pas en réalité des algorithmes connus, que je n'aie pas besoin de toutes les recoder ?

Merci à vous !
 

BlueDream

Administrateur
Membre du personnel
Inscrit
8 Decembre 2012
Messages
2 010
Reactions
149
#2
Hello @Geuwp ,

Ya un petit souci dans ton raisonnement, fut une époque ou l'on pouvait en effet se contenter de traduire la crypto d'ankama.
Désormais chaque fichier RawDataMessage est unique (ou en tout cas pour une certaine durée), c'est à dire que la crypto dépend de valeurs contenu dans le fichier qui changent (de longues chaines de bytes). A toi de trouver un moyen de contourner ce problème directement en c# mais franchement tu vas perdre du temps à mon avis.

Après faut aussi noter que le hash de ce genre de paquets n'est même plus vérifier par le serveur, j'ai des comptes qui tournent sans hash depuis plusieurs mois.
 

Geuwp

Contributeur
Inscrit
16 Janvier 2015
Messages
37
Reactions
1
#3
Merci pour ta réponse rapide !

Hello @Geuwp ,
Après faut aussi noter que le hash de ce genre de paquets n'est même plus vérifier par le serveur, j'ai des comptes qui tournent sans hash depuis plusieurs mois.
C'est à dire que je peux me contenter d'envoyer les données du paquet sans inclure de hash ? Comme si le if(HASH_FUNCTION != null) n'existait pas en somme ?
 

BlueDream

Administrateur
Membre du personnel
Inscrit
8 Decembre 2012
Messages
2 010
Reactions
149
#4
Exactement, j'ai entendu dire que le RawDataMessage prenait trop de place dans leur bdd.
 

Arth

Contributeur
Inscrit
28 Aout 2016
Messages
80
Reactions
3
#5
De ce que j'ai commencé à étudié du RDM, il semble être identique sûr au moins des périodes de plusieurs jours (il faudrait que je re-compares après un mois, mais je ne suis pas sous mon linux pour le faire pour le moment).

Sinon, c'est plutôt bien offusqué, je pensais faire un exemple/tuto de desobfuscation de source dans le future car il y a plusieurs méthode utilisé. Pour ce qui est de la construction de la clef AES,il faut regarder dans les fichier avec des noms du genre : a_-__--.as. C'est assez obfusqué, mais il y a beaucoup de code inutile à retirer (du dead code) et il y a finalement très peu de chose dedans : simplement quelque class hérité de Array (et autres) et un générateur de nombre pseudo aléatoire (RNG) qui les utilisent.

De mémoire (donc de manière incertaine), le ticket est utilisé comme seed pour le RNG, puis on génère avec ça la clef AES.

Le principe de cette RNG est très simple, on initialise un tableau de 256 cases qui contient toutes les valeurs de 0 à 255. on mélange ce tableau avec une seed. On a aussi 2 variable pour pointer des cases du tableau et on va jouer avec ces variables pour pointer et échanger les cases.
Pour sortir un nombre aléatoire, on calcul une nouvelle position dans le tableau en fonction des 2 variables, et on permute la case obtenu avec une autre, on utilise la valeur (et position ?) trouvé et la valeur des variables qui pointé dans le tableau. Les nombres sont généré par groupe de 128 ou 256 bit il me semble.

Désolé si cela manque de clarté, si tu veut plus de détail je pourrais t'en donner plus tard ^^.

Sinon comme le dit @BlueDream ... Tu fait sans, comme beaucoup de monde ;).
 

Geuwp

Contributeur
Inscrit
16 Janvier 2015
Messages
37
Reactions
1
#7
Exactement, j'ai entendu dire que le RawDataMessage prenait trop de place dans leur bdd.
Super, je vais essayer alors ! Merci beaucoup.

@Arth Merci de tes précisions. Pour l'instant je vais essayer sans et je reviendrai te demander des précisions si un jour je m'attaque au RawDataMessage , compte sur moi :p !
 
Haut Bas