Tricherie sur Game Boy
Série 5

Introduction

Le but de cette série d'exercices est de trouver comment tricher à deux jeux Game Boy célèbres — Super Mario Land 2 et Tetris.

L'idée de la tricherie est simple et consiste à trouver où, en mémoire, sont stockées certaines valeurs importantes — p.ex. le nombre de vies du joueur, ou alors son score. Une fois leur adresse identifiée, il est possible de les modifier à notre guise en écrivant simplement la valeur désirée à cette adresse.

La question est de savoir comment déterminer à quelle adresse se trouvent ces valeurs importantes. La technique que nous utiliserons consiste à obtenir le contenu de la mémoire du système à différents moments du jeu, et à utiliser ce que l'on sait sur l'état du jeu à ces moments-là pour déterminer la position des valeurs intéressantes.

Nous vous fournissons donc une archive Zip contenant 6 fichiers, 3 par jeu. Chacun de ces fichiers contient la valeur de la totalité des octets de l'espace d'adressage du Game Boy à un instant donné. Ils contiennent donc tous exactement 65 536 octets, même si leur taille réelle est plus petite car ils ont été compressés avec gzip. Le premier octet du fichier est celui de l'adresse 0, le second de l'adresse 1, et ainsi de suite jusqu'à l'adresse 65 535 (FFFF16). Ces fichiers, que l'on nomme memory dumps en anglais, ont été obtenus au moyen d'une version modifiée du corrigé du projet Gameboj.

Pour débuter cette série, créez un nouveau projet Eclipse et importez-y le contenu de cette archive. Créez ensuite une classe nommée p.ex. GameBoyCheater dans laquelle vous écrirez tout votre code, sous forme de méthodes statiques.

Exercice 1 : Super Mario Land 2

Dans Super Mario Land 2, le joueur dispose d'un certain nombre de vies. Pour tricher à ce jeu, il est donc intéressant de trouver où ce nombre de vies est stocké en mémoire, afin de pouvoir l'augmenter artificiellement.

sml.png

Figure 1 : Super Mario Land 2 avec 5 vies disponibles

Dans l'archive que nous vous avons fournie se trouvent trois fichiers nommés sml_5.bin.gz, sml_4.bin.gz et sml_3.bin.gz. Ils contiennent la totalité des octets lus depuis le bus lorsque le joueur avait respectivement 5, 4 et 3 vies.

Pour déterminer l'adresse mémoire à laquelle est stocké le nombre de vies, l'idée est de trouver une (ou plusieurs) adresses contenant la valeur 5 dans le premier fichier, la valeur 4 dans le second, et la valeur 3 dans le troisième.

Pour ce faire, écrivez une première méthode permettant de lire le contenu d'un fichier du type de ceux fournis, et le retournant sous la forme d'un tableau de type byte[] d'une taille de 65 536 éléments. Prenez garde au fait que les fichiers sont compressés avec gzip et qu'il faut donc les décompresser à la volée en utilisant un flot filtrant de type GZIPInputStream.

Pour lire le contenu des fichiers, vous pouvez soit utiliser la méthode read retournant les octets un à un (plus simple mais moins efficace) ou celle les retournant par paquets, et les plaçant dans un tableau (plus compliqué mais plus efficace). Dans ce second cas, notez que vous devrez faire plusieurs appels à la méthode read pour obtenir la totalité des octets !

Une fois cette méthode écrite, écrivez-en une deuxième qui, étant donné un tableau retourné par la première méthode et un entier compris entre 0 et 255 retourne l'ensemble des adresses à laquelle cet octet apparaît. Bien entendu, cet ensemble sera représenté par un objet du type Set<Integer>.

Finalement, utilisez ces deux méthodes pour en écrire une qui lise les trois fichiers et calcule l'intersection des ensembles d'adresses contenant 5 dans le fichier sml_5.bin.gz, 4 dans le fichier sml_4.bin.gz et 3 dans le fichier sml_3.bin.gz. Cet ensemble ne doit pas être vide, mais peut éventuellement contenir plus d'une adresse…

Si vous ne vous souvenez plus de la méthode à utiliser pour déterminer l'intersection de deux ensembles, cherchez-la dans la documentation de la classe Set.

Lorsque vous pensez avoir trouvé la ou les adresses contenant le nombre de vies, vous pouvez vérifier votre réponse en essayant d'ouvrir l'adresse suivante dans votre navigateur Web :

https://cs108.epfl.ch/+/????.mov

dans laquelle vous aurez remplacé les points d'interrogation (????) par la ou les adresses trouvées, ordonnées en ordre croissant et représentées chacune par 4 chiffres hexadécimaux, avec les lettres en majuscules. Par exemple, si vous pensez que les adresses sont 12ab16 et 012316, alors remplacez les ???? par 012312AB.

Si vous a découvrez une courte vidéo à cette adresse, regardez bien ce qui se passe dans le coin bas-gauche de l'écran.

Exercice 2 : Tetris

Dans Tetris, le joueur ne possède pas de vie, mais un score qui augmente avec le nombre de lignes qu'il réussit à compléter. Pour tricher à ce jeu, il est donc intéressant de trouver où ce score est stocké en mémoire afin de pouvoir l'augmenter à volonté.

tetris_799.png

Figure 2 : Tetris avec un score de 799 points

Au premier abord, ce problème semble identique à celui de l'exercice précédent, mais ce n'est pas tout à fait le cas. En effet, contrairement au nombre de vies de Super Mario Land 2, le score de Tetris peut être bien plus élevé que 255, la valeur maximale représentable au moyen d'un seul octet.

Dans un premier temps, on peut faire l'hypothèse que ce score est représenté au moyen de 2 octets. Pour trouver où ces deux octets se trouvent en mémoire, il faut encore deviner comment ils sont représentés. Deux questions se posent alors :

  • étant donné un score, quelle est la valeur des deux octets le représentant ?
  • étant donnés ces deux octets, dans quel ordre apparaissent-ils en mémoire ?

Etant donné ce que vous savez sur le Game Boy, essayez de trouver la réponse à ces questions et, cela fait, écrivez une nouvelle méthode dans votre programme permettant de calculer l'ensemble des adresses auxquelles se trouve une séquence de plusieurs octets — ici 2. Utilisez-la ensuite pour essayer de trouver où se trouve le score dans la mémoire de Tetris, sachant que :

  • le fichier tetris_11.bin.gz a été obtenu lorsque le score valait 11,
  • le fichier tetris_799.bin.gz a été obtenu lorsque le score valait 799,
  • le fichier tetris_3979.bin.gz a été obtenu lorsque le score valait 3979.

Si vous n'arrivez pas à trouver le score dans la mémoire, relisez la §2.7 de l'étape 2, et la §1.4.3 de l'étape 3, elles pourraient vous aider…

Une fois que vous pensez avoir trouvé la solution, vous pouvez la vérifier au moyen de la même technique que pour l'exercice 1.