Heroes of Might and Magic IV: bug de taverne ou classique du patching

Cette histoire courte décrit l'un des projets réalisés par le projet Equilibris , un mod non officiel pour Heroes of Might et Magic IV. Du point de vue à la fois de la rétro-ingénierie et du patching, ce n'est pas d'un intérêt particulier - seule la fin s'est avérée un peu drôle.



image


Comme vous le savez, dans cette série de jeux dans chaque taverne, le joueur ne peut embaucher qu'un nouveau héros par semaine. Mais…



Description du bug: s'il n'y a pas eu de recrutement dans la taverne externe, à partir du 8ème jour, vous pouvez acheter deux héros en deux jours.



Le fichier désassemblé heroes4.exe du dernier addon officiel "Winds of War" est utilisé pour le travail. La procédure de fonctionnement de la taverne a été trouvée par l'équipe plus tôt et se trouve à l'adresse 4705E0. De l'ensemble de l'algorithme de son travail, je m'intéresse au lieu dans lequel il est déterminé s'il est possible d'embaucher un héros dans la taverne pour le moment, ou s'il faut attendre. Dans le jeu, cela se manifeste par la sortie du message correspondant:





D'un point de vue programmatique, il s'agit d'une nouvelle fenêtre créée dans le jeu à l'aide de la fonction NewWindowCreate (720C80) (les fonctions reconnues dans le désassembleur reçoivent leurs propres noms). Il y a plusieurs appels à cette fonction dans la procédure de la taverne, et le premier challenger est un appel à l'adresse 470823. Avec l'aide du débogueur, je m'assure qu'en fait, cet appel crée la boîte de dialogue souhaitée. Le code qui contrôle cet appel à NewWindowCreate est situé ci-dessus à 470645:



00470638                 call    HeroesPricesInTavern_Lost
0047063D                 mov   al, [ebp+48h]  // 0 –   ; 1 –     ( 7 ).
00470640                 add     esp, 8
00470643                 test    al, al
00470645                jz      loc_470866 //   ,      470823


J'achète dans la taverne du héros, puis je règle le "point d'arrêt" pour écrire dans la cellule adressée à [ebp + 48h], après quoi j'attends 7 jours de jeu. Lorsque la taverne est "vidée", le débogueur apparaît à l'adresse 470DFF. Voyons le code environnant:



00470DF0 TavernCountDays proc near               
00470DF0                 mov     dl, [ecx+48h] // ECX+48h –   :
DL=0 –   ;
DL=1 –     ( 7 )
00470DF3                 xor     eax, eax 
00470DF5                 cmp     dl, al
00470DF7                 jz      short loc_470E06
00470DF9                 cmp     dword ptr [ecx+4Ch], 7 //  [ECX+4Ch] -         .   7 – . 
00470DFD                 jl      short loc_470E06
00470DFF                mov     [ecx+48h], al  //   (AL=0)
00470E02                 mov     [ecx+4Ch], eax  //   
00470E05                 retn
00470E06
00470E06 loc_470E06:                             
00470E06                                         
00470E06                 inc     dword ptr [ecx+4Ch] //          
00470E09                 retn
00470E09 TavernCountDays endp


Cette petite procédure permet de vérifier le nombre de jours de fermeture de la taverne à la location. Notez qu'il est appelé pour chaque taverne sur la carte chaque jour de match. Qu'est-ce qui cause le bogue? Pour une raison quelconque, le programme continue de compter le nombre de jours pendant lesquels le héros n'a pas été embauché dans la taverne et après la semaine où la taverne a été fermée (voir le compteur à 470E06). En conséquence, nous obtenons l'image suivante. Que le premier recrutement du héros n'ait lieu que le huitième jour de match. A l'entrée de la procédure, la valeur du drapeau de disponibilité de la taverne à [ecx + 48h] sera égale à "1" (la taverne est fermée), et la valeur du compteur de jour à [ecx + 4Ch] sera égale à "8". Cependant, après la comparaison à 470DF9, le contrôle recevra un code à 470DFF, qui rouvre la taverne à la location! Cela réinitialisera le compteur du jour.et après avoir embauché le deuxième héros, l'algorithme fonctionnera déjà, comme les auteurs l'avaient prévu. Mais après deux semaines de jeu, tout le cycle se répétera.



Le moyen le plus simple de corriger le bogue est d'arrêter de compter les jours. Laissez le compteur fonctionner uniquement lorsque la taverne est fermée (ce qui est plus logique), et le reste du temps, nous le mettons à zéro. Ceci est réalisé très simplement - en modifiant la transition à l'adresse 00470DF7 vers la fin de la fonction:



00470DF5                 cmp     dl, al
00470DF7                 jz      short loc_470E09


Il ne reste plus qu'Ă  patcher le code existant. Pour ce faire, regardez l'original





et modifié





options.



Comme vous pouvez le voir, le résultat souhaité peut être obtenu en remplaçant 0D par 10 à l'adresse 470DF8. Un classique du genre: corrigez un bug en remplaçant un seul octet!



All Articles