32 votes

Déconstruire les pépins de Pokémon?

(Je m'excuse si c'est le mauvais endroit pour demander cela. Je pense que c'est vraiment une programmation connexe, mais si cela appartient à un autre site s'il vous plaît laissez-moi savoir)

J'ai grandi en jouant à Pokémon Rouge et Bleu, les jeux qui ont été très amusant, mais sont un peu tristement célèbre pour avoir de nombreux exploitable pépins (voir, par exemple, cette ridicule speedrun du jeu qui utilise de corruption de la mémoire à son tour l'élément de l'écran dans un éditeur hexadécimal).

Récemment, j'ai trouvé un intéressant speedrun du jeu qui utilise un glitch appelé le "ZZAZZ glitch" de corrompre important emplacements de mémoire et de laisser le lecteur presque immédiatement gagner le jeu. Selon l'auteur, la description de la speedrun, le ZZAZZ glitch fonctionne comme suit:

Pour démarrer un Entraîneur de bataille, le jeu doit charger un grand nombre de données, tels que [...] l'argent qu'il va céder en cas de défaite. Lors du chargement de l'argent est là que les choses peuvent vraiment moche. Pour des raisons qui sont hors de moi, de l'argent est stockée dans une manière complètement différente, le jeu utilise une structure de données de trois octets et au lieu de convertir la valeur en binaire, il les stocke dans "l'homme" de la représentation. Par exemple, $123456 est stockée en tant que 0x123456 au lieu de 0x01E240, la conversion correcte.

[Certaines entrées non valides dans le Formateur table] point d'emplacement non valide de l'argent de données. Quand le jeu tente d'effectuer des opérations avec ces données dans ladite structure, il va de noix et commence à écraser une énorme partie de la mémoire RAM. Plus précisément, pour chaque bloc de trois octets, deux d'entre eux contiennent 0x9999 (le montant maximum d'argent qu'un formateur peut donner). Ce schéma se répète de nombreuses fois par le biais de RAM. Pour voir ce mieux, je recommande la mise en pause de la vidéo sur l'émulateur après la ZZAZZ formateur est confronté et de définir VBA mémoire du spectateur à 0xD070.

Cette analyse du sens, mais comme je suis programmeur, je ne peux pas aider mais se demander comment sur la terre que les programmeurs écrit le code qui permettrait de rendre cela possible. Aucune approche que je peux penser pour écrire une fonction qui convertit en hexadécimal codé nombre décimal à virgule serait jamais démarrer le remplissage aléatoire de blocs de mémoire avec 0x9999 si l'entrée n'était pas une hexadécimal valide codé nombre décimal.

Ma question est, sans précisément, la conception de l'algorithme à l'échec de cette façon, est-il une simple mise en œuvre d'une conversion de l'hexadécimal codé en décimal à virgule que pourrait entraîner ce genre de corruption de la mémoire lorsque la fed à une valeur non valide?

Encore une fois, si c'est hors-sujet, toutes mes excuses. Mes pensées sont que d'autres programmeurs sur ce site peuvent avoir grandi en jouant à ce jeu, et ça sonne comme un exercice intéressant dans la rétro-ingénierie pour essayer de comprendre comment un glitch comme cela pourrait être possible.

13voto

Michael Dorgan Points 7849

Honnêtement, je pense que c'est juste un stupide, méchant glitch par quelqu'un écrit un de leurs premiers jeux sur la cible. Pokemon Rouge/Bleu ont été le premier de la série, et en avait bien d'autres problèmes que Nintendo aurait normalement un coup de pied hors du lot-test, que je me demande comment il est arrivé par le biais de. Le défilement de l'écran de maj question, c'est celle qui me fait. De toute façon, qui sait ce qu'ils werer de la pensée. Peut-être que cette zone a été écrit via le script et donc stockés les choses différemment. Peut-être le motif de bits 0x0101 a été utilisé pour montrer mémoire a été libérée et que le code accidentellement est devenue dingue étranges endroits. J'ai pu verser sur le Z80 code et de revivre ma propre jeu de temps de développement sur cette plate-forme, mais meh. Trop de travail pour essayer de déchiffrer ce que les flammes qu'ils avaient en tête.

C'est sûr qu'il fait une tonne d'argent, même si...

Edit 1:

Ok, vous bountied il. J'ai passé un peu plus de temps de verser sur mon mémoire et trouvé une friandise pour vous. Le GBC/DMG a un opcode appelé la DAA. Décimal Ajuster Accumulateur (A). Ce qu'il fait est de convertir une valeur dans l'accumulateur en format BCD. Les zones de mémoire que vous voyez sont déjà en format BCD: http://en.wikipedia.org/wiki/Binary-coded_decimal

Maintenant, je peux vous dire, en 4 ans, ou alors quand j'ai été le codage à la main assembleur Z80 pour les jeux, je n'ai jamais eu une fois une nécessité pour cet opcode, et ne voit-il utilisé une fois dans un match de baseball, nous avons fait de l'affichage de certains scores. Alors que c'est un 1er cycle de l'arithmétique de l'instruction, je ne pouvais pas vraiment trouver un bon usage normal de codage. Hmm. Je acutally ont encore le DMG tech docs de la part de Nintendo. Allez comprendre ;) de toute façon, rien d'excitant là non plus, sauf qu'il mess avec un certain nombre de drapeaux funky façons.

Ma conjecture est que le tableau est supposé être en format BCD. Le changer pour quelque chose en dehors de ce format provoque l'interne. mathématiques d'aller très haywire - Porter et Zéro drapeaux ensemble quand ils ne sont pas censés l'être. Cela provoque le débordement d'une colonne à l'autre, causant de très grands nombres à calculer. Sans regarder directement le opcodes en question que de lire ce domaine, je ne peux pas l'affirmer, mais ma conjecture est, il est un fourre-tout vérifier ici qui dit que si effectuer est toujours à l'achèvement de la BCD de mathématiques, définissez une valeur max au lieu de stocker un négatif ou en dehors des limites de la valeur. L'une ou l'instruction DAA, lors de la réception de déchets de données est de retour 0x99 pour elle valeur de retour, même si je suis moins sûr que la.

Espérons que cela aide...

9voto

Mehrdad Points 70493

Je peux penser à un algorithme (bien que je suis désolé pour celui qui aurait l'ai écrit):

  • Supposons que l'entrée est un 32 bits chiffre décimal en hexadécimal, little endian (par exemple, 0x56 0x34 0x00 0x12).

  • Maintenant la boucle à travers chaque octet, alors que vous n'avez pas encore atteint zéro octet. (Cela ne devrait jamais arriver, si 0x999999 est en effet garanti pour être le max... mais hélas, il ne l'est pas.)

  • Sur chaque boucle, calculer la réelle valeur et écrire les données en arrière dans l'entier (ou dans un autre tampon, où vous faire une "boucle-tout" plutôt que quelque chose comme "pour i = 0 à 4").

Vous pouvez voir comment vous pouvez obtenir un pépin, si votre valeur n'a pas de 0x00 à la fin (c'est à dire la version 32 bits de "virgule" entier est plus grand que 0x999999).

Bien sûr, cela est plutôt une manière obscure de calcul de la valeur, mais je pense que c'est tout à fait possible que quelqu'un fait un tout à l'/-lors de la boucle plutôt qu'un délimitée for boucle pour cela.

Edit 1:

Au début je pensais que cela aurait l'avantage" de permettre à la chaîne directement à l'utilisateur (car il serait terminée par null), mais bien sûr, cela ne fonctionne pas avec little-endian. Ils auraient pu faire quelque chose de similaire avec big endian, mais cela nécessiterait un rétro boucle de trop-plein, que je trouve être une moins susceptibles erreur pour quelqu'un de faire.

Edit 2:

C'était peut-être une optimisation du compilateur en raison d'un comportement indéfini (qui le programmeur était pas au courant, comme un pointeur non valide fonte ou une aliasing question)?

7voto

templatetypedef Points 129554

Mystère résolu! Il ressemble à l'utilisateur TheZZAZZGlitch compris quelles sont les causes de cette.

Le glitch est déclenché lorsque le jeu cherche à calculer un très grand nombre entier. En interne, le jeu a une routine qui à plusieurs reprises ajoute les valeurs de simuler une multiplication. Il semble écrire des octets comme il va, déplacement sur une sortie de position d'écriture. Le code est conçu pour couper toute valeur qui dépasse 0x009999, afin que le joueur ne gagne pas plus de $9999 à partir d'un formateur de bataille (les valeurs sont stockées dans hexadecimally codé en décimal). Cependant, le jeu oublie pour réinitialiser le pointeur de sortie lorsque cela se produit, de sorte que si un très grand nombre est généré, le jeu à plusieurs reprises écrire le modèle 0x009999 à travers la RAM par le passage de l'écriture pointeur au-dessus et de l'écriture 0x99 à deux sur trois octets.

Espérons que cette aide!

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X