- Der Kill-Screen-Absturz in NES Tetris kann nicht nur das Spiel beenden, sondern als Einstiegspunkt dienen, um auf unveränderter Hardware und mit unverändertem Modul neues Verhalten auszuführen
- Wenn die Punkteberechnung nach Level 155 nicht zwischen zwei Frames abgeschlossen wird, gerät der Kontrollfluss durcheinander und das Spiel liest unbeabsichtigt neue RAM-Adressen als Befehle, was zu arbitrary code execution führen kann
- Der Eingabe-RAM des Erweiterungs-Controller-Ports des Famicom überlappt mit der Position der Sprungroutine, sodass sich mit Tasten auf Controller 3 und 4 die Zieladresse nach dem Absturz manipulieren lässt
- Displaced Gamers hat einen Proof of Concept veröffentlicht, bei dem das Sprungziel auf die Highscore-Tabelle gesetzt wird, sodass Namens- und Punktedaten wie Opcodes der NES-CPU gelesen werden
- Wegen des eingeschränkten Zeichensatzes und des Fehlens eines Speichermediums ist der praktische Nutzen begrenzt, doch es könnte sich zu Absturzvermeidungs-Patches oder sogar voller Kontrolle über den RAM ausweiten
Wie ein Kill-Screen-Absturz zur Codeausführung wird
- Der Absturz in NES Tetris kann auftreten, wenn der Score-Handler zu lange braucht, um zwischen zwei Frames den neuen Punktestand zu berechnen
- Diese Situation kann ab Level 155 auftreten
- Kommt es zu der Verzögerung, wird ein Teil des Kontrollcodes von der Schreib-Routine des nächsten Frames unterbrochen
- Danach springt das Spiel zu einer unbeabsichtigten RAM-Adresse, um den nächsten Befehl zu finden
- Normalerweise führt dieser unerwartete Sprung dazu, dass Spieldatenmüll am Anfang des RAM als Code gelesen wird, was schnell in einem Absturz endet
- Ein aktuelles Video von Displaced Gamers zeigt das Verfahren, mit dem sich NES Tetris dazu bringen lässt, die Highscore-Tabelle wie Maschinenbefehle zu lesen
- Ähnliche Glitches mit arbitrary code execution sind zuvor bereits in Super Mario World, Paper Mario und The Legend of Zelda: Ocarina of Time bekannt geworden
- Ein grundlegender Weg, externen Code in NES Tetris einzuschleusen, wurde öffentlich mindestens seit 2021 theoretisch beschrieben; in der Community soll zudem schon lange bekannt gewesen sein, wie sich die vollständige Kontrolle über den RAM erreichen lässt
Kontrolle der Sprungposition über die Famicom-Eingabeverarbeitung
- Dieser Hack hängt nicht nur von der Absturzlogik von Tetris ab, sondern auch von der Eingabestruktur des Famicom
- Beim japanischen Famicom waren im Unterschied zur US-Version des NES zwei Controller fest mit der Konsole verbunden, während sich Controller von Drittherstellern über den Erweiterungsport an der Vorderseite anschließen ließen
- Der Tetris-Code liest die Eingaben dieses „zusätzlichen“ Controller-Ports
- Mit einem Adapter lassen sich zusätzlich zwei Standard-NES-Controller anschließen
- Es gab zwar eine separate Tetris-Version von Bullet-Proof Software für das Famicom, doch diese Eigenheit der Eingabeverarbeitung funktioniert im Code von NES Tetris
- Der RAM-Bereich zur Verarbeitung zusätzlicher Controller-Eingaben überlappt mit der Speicherposition der Sprungroutine, die beim Absturz verwendet wird
- Wenn der Absturz die Sprungroutine unterbricht, enthält dieser RAM Daten darüber, welche Tasten auf den Controllern gedrückt wurden
- Spieler können diese Werte nutzen, um präzise zu steuern, wohin der Spielcode nach dem Absturz springt
Die Highscore-Tabelle wie ausführbaren Code lesen
- Die Methode von Displaced Gamers zur Steuerung des Sprungs erfordert bestimmte Controller-Eingaben
- Auf Controller 3
updrücken - Auf Controller 4
right,leftunddowndrücken - Um
leftundrightgleichzeitig einzugeben, ist eine Modifikation des Controllers nötig
- Auf Controller 3
- Diese Eingaben schicken den Sprungcode in den RAM-Bereich, in dem Namen und Punktzahlen der Highscore-Liste gespeichert sind
- Wird an der Zielposition der B-Type-Highscore-Tabelle
(Geingetragen, springt das Spiel erneut in einen anderen Bereich der Highscore-Tabelle - Danach liest das Spiel Namen und Punktzahlen der Reihe nach und verarbeitet sie als Opcodes der NES-CPU, in einer Art, die Displaced Gamers als „bare metal“-Code bezeichnet
Möglichkeiten und Grenzen des Proof of Concept
- Im Eingabebereich für Highscore-Namen lassen sich nur 43 Symbole verwenden, und in Punktzahlen kommen nur 10 Ziffern vor
- Durch diese Beschränkung lässt sich nur ein Teil der auf dem NES möglichen Opcodes in die Highscore-Tabelle „codieren“
- Trotz der eingeschränkten Eingabe war kurzer Proof-of-Concept-Code möglich
- Zu den Beispieldaten gehören der Name
))"-P)und ein A-Type-Zweitplatz-Score von 8.575 - Diese Routine setzt die beiden höchsten Stellen des Spielstands auf 0
- Das verkürzt die Zeit für die Punkteverarbeitung und verringert damit die Absturzursache, doch beim Weiterspielen erreicht der Score wieder den „Gefahrenbereich“ für Abstürze
- Zu den Beispieldaten gehören der Name
- Das originale NES Tetris besitzt keinen batteriegepufferten Speicher, daher müssen die benötigten Highscores und komplizierten Namen nach jedem Einschalten erneut von Hand erstellt werden
- Auch der Platz in der Highscore-Tabelle ist begrenzt, weshalb sich dort nicht einfach komplexe Programme direkt über den eigentlichen Tetris-Code legen lassen
- HydrantDude schreibt, dass sich mit bestimmten Kombinationen aus Highscore-Namen und Zahlen ein Bootstrapper bauen lässt, der anschließend einen weiteren Bootstrapper erzeugt, um volle Kontrolle über den gesamten RAM zu erlangen
Potenzial zur Umprogrammierung weit über Absturzvermeidung hinaus
- Mit voller Kontrolle über den RAM könnten Spitzenspieler das Spiel sogar so umprogrammieren, dass der Absturz-Bug von NES Tetris gepatcht wird
- Das könnte besonders für Spieler hilfreich sein, die über Level 255 hinauskommen wollen
- Nach Level 255 springt das Spiel wieder auf Level 0 zurück
- Wird das Prinzip weiter ausgereizt, wären wie bei Speedrunnern in Super Mario World sogar Umbauten denkbar, die Tetris in etwas wie Flappy Bird verwandeln
1 Kommentare
Meinungen auf Hacker News
Bei solchen Exploits muss ich immer an die ultimative Endstufe des Hackens aus Stross’ Accelerando denken: „einen Timing-Channel-Angriff auf die rechnerische Superstruktur der Raumzeit selbst auszuführen, um zu irgendetwas darunter durchzubrechen“
Klingt nach einer sehr guten Methode, einen Vakuumzerfall auszulösen und das Universum mit einem Bluescreen abstürzen zu lassen.
Ich mag solche Leute wirklich.
Mir fehlt dieser Hacker-Mindset, Dinge nur aus Spaß auszuprobieren, die sehr wahrscheinlich nutzlos sind; das ist mir fast peinlich.
Der Rest von uns ist zu sehr damit beschäftigt, über die Runden zu kommen, um Zeit für solche Exploits zu haben – kein Wortspiel beabsichtigt.
Wenn man den ganzen Artikel nicht lesen will, sich aber fragt: „Ich dachte, NES-Cartridges laufen aus ROM?“: stimmt.
Dieser Exploit bringt die CPU allerdings dazu, in den RAM für die Highscore-Tabelle zu springen. Wirklich großartig.
Der Weg zu arbitrary code execution ist immer interessanter als das, was man danach damit macht.
Die Beharrlichkeit, das Spiel zu zerlegen, herauszufinden, wann und wo es sich so verhält, und zu finden, welche Stelle man manipulieren muss, um Befehle einzuschleusen, ist bewundernswert.
Pokemon Yellow: https://www.youtube.com/watch?v=Vjm8P8utT5g
Super Mario World: https://www.youtube.com/watch?v=hB6eY73sLV0
Arbitrary Code Execution wird dort tatsächlich als Shellcode ausgelöst. Buchstäblich, indem man im Spiel einen Koopa-Panzer manipuliert.
Wie lange dauert es wohl, bis jemand Doom in Tetris laufen lässt?
Das ist richtig gut verschwendete Zeit.
Ernst gemeinte Frage. Früher hielt ich auch unterhaltsame Dinge für Zeitverschwendung, aber mit zunehmendem Alter und nachdem ich den Preis für ein langes, stressreiches Leben gezahlt habe, denke ich anders darüber.
Ich möchte das mit Factorio ausprobieren.
Einen riesigen Computer aus Förderbändern in Factorio bauen und dann einen Segmentation Fault auslösen, der aus dem Spiel heraus ausbricht.
Arbitrary Code Execution in alten Spielen zu finden, ist wirklich faszinierend.
Als ich so etwas vor ein paar Jahren bei Super Mario World gesehen habe, war ich eine Zeit lang davon besessen, wie das möglich ist.
Ich meine das positiv und als Kompliment, auch wenn es etwas gemein klingen kann: Ich liebe es, wenn wirklich kluge Menschen enorm viel Zeit und Mühe in völlig nutzlose Dinge stecken.
Gibt es einen unmittelbaren Grund, Code in NES Tetris einzuschleusen? Wahrscheinlich nicht. Aber darum geht es nicht; es geht darum herauszufinden, was möglich ist und wozu man alten Code und primitive Computer zwingen kann.
Im klassischen Sinn ist das vielleicht nicht „nützlich“, aber das gilt genauso für Sudoku, Kreuzworträtsel oder überhaupt dafür, NES Tetris zu spielen.
Er ermöglicht es Spitzenspielern, Abstürze zu vermeiden und länger zu spielen, nachdem bestimmte Punkte erreicht sind, die sonst das Weiterspielen verhindern.
Für durchschnittliche Spieler ist das nicht praktisch relevant, aber für Menschen, die um Highscores konkurrieren wollen, beseitigt es eine Grenze und eröffnet neue Wettbewerbschancen.
Zahlentheorie galt ursprünglich auch als nutzlos, stützt heute aber praktisch jede Public-Key-Kryptografie.
Der Nutzen, den andere aus Dingen ziehen, die ich tue, obwohl ich sie nicht mag, ist ein glückliches Nebenprodukt – oder kommt mir indirekt zugute, etwa durch Geld oder Anerkennung.
Dinge, die man aus Liebe zur Sache tut, haben die direkteste Form von Nutzen für die wichtigste Person: einen selbst.
Ehrlich gesagt überrascht es mich, dass Tetris erst so spät geknackt wurde.
Das dürfte eine neue Ära von any%-Runs einläuten. Ziel ist es, die Endszene oder die Credits des Spiels so schnell wie möglich auszuführen.
Mein Lieblingsbeispiel ist Ocarina of Time; dort gibt es schon seit Jahren ACE-Exploits.
Das Spiel ist komplett kaputt: Wenn man den Speicher des Spiels manipuliert und bestimmte Eingang-Warps bearbeitet, kann man es in wenigen Minuten „durchspielen“.
Noch erstaunlicher ist, dass Menschen den Speicher von Hand bearbeiten, nur mit ein paar Buttons und einem Analogstick.
Ein Beispiel, bei dem die Credits nach 3 Minuten erscheinen: https://www.speedrun.com/oot/runs/z1l1627m
Nachdem ein Fehler in einem Warp-Tunnel gefunden wurde, löste er ein Out-of-bounds-Read aus; zuvor hatte man per Joystick-Manipulation direkt außerhalb des Buffers die Bytes geschrieben, die das gewünschte Verhalten auslösen sollten.
In einem der Pokemon-Spiele musste man den Speicher durch eine Menge Kauf-/Verkaufstransaktionen exakt vorbereiten und dann die Anzahl der Items überlaufen lassen, um den Jump auszulösen.
Wirklich fantastisch. Wenn uns eines Tages Aliens angreifen, werden uns solche Spielereien retten.
Diese ACE scheint erst zu funktionieren, wenn man den Kill Screen erreicht.
Stell dir vor, ACE in Ocarina of Time würde erst nach den Ending Credits ausgelöst; dann passiert es erst, nachdem man bereits erfolgreich fertig ist, und kann den Rekord nicht verkürzen.
Ich liebe Arbeiten dieser Art wirklich.
Das erinnert mich daran, wie jemand mithilfe eines Glitches Flappy Bird in Super Mario World programmiert hat. Völlig irre.
https://www.youtube.com/watch?v=hB6eY73sLV0