Eine Hacking-Geschichte eines klassischen Dendy- oder Contra-Spiels mit Spreadgan am Anfang

Seit meinem letzten Artikel hat Sie zu meiner großen Überraschung interessiert. Ich beschloss, das Ergebnis, eine gehackte Version des Spiels "Contra (J) [T + Rus_Chronix]", mit ein wenig Funktionalität zu ergänzen und gleichzeitig "Code Injection" auf NES anzuzeigen. Dieses Mal werde ich die Spieler dazu bringen, das Spiel mit der gepumpten Spreadgun zu starten. Um es ins Spiel zu bringen, musst du das Symbol "S" gefolgt von "R" auswählen.



Alle Interessierten willkommen unter Katze.


Traditionell:


langweilige und langwierige Videoaufzeichnung des Prozesses

Und wir planen traditionell die Abfolge der Aktionen.


  1. Adressen finden
  2. Finden Sie den Wert der gepumpten Spreadgun heraus
  3. Finden Sie zu Beginn des Spiels heraus, was er an diese Adressen schreibt
  4. ROM neu schreiben
    • Easyway - Ändern Sie den Wert der Basiswaffe in den gepumpten Spreadgan
    • Hardway - Verwenden Sie eine vollständige Code-Injektion, wenn der einfache Weg fehlschlägt
  5. Speichern Sie das Ergebnis in einer neuen Datei

Um nach Adressen zu suchen, verwenden wir die zuvor beschriebene Methode. Wenn wir uns jedoch daran erinnern, dass sich Leben in benachbarten Adressen befanden, werden wir nur nach Waffen des ersten Spielers suchen, in der Hoffnung, dass der zweite Spieler in der Nähe ist. Wenn wir das Fenster "Ram Watch" nach dem Start des ersten Levels öffnen, suchen wir nach einem unbekannten Wert. Ich glaube, die Basiswaffe ist auf 0 gesetzt, aber ich weiß es nicht genau.
Wir rennen entlang des Levels, ohne voraus zu rennen, schießen in alle Richtungen und beseitigen die sich ändernden Werte. Die Waffe hat sich noch nicht geändert.


Verwenden wir eine andere Fensteroption, oder besser gesagt, im Feld "Vergleichen mit / nach", wobei die Funkfrequenz "Anzahl der Änderungen" in Feld 0 hervorgehoben wird. Die Art des Vergleichs ist natürlich "Gleich". Die Waffe änderte sich immer noch nicht.


Wenn Sie also von der Option "gleich dem vorherigen Wert" zur Option "Die Anzahl der Änderungen ist 0" springen, können Sie ungefähr 10.000 Adressen erreichen. Wenn weitere Überprüfungen die Adressliste zu geringfügig oder gar nicht reduzieren, kann man weit genug vorwärts gehen, um die erste Waffe auszuschalten.


Nachdem wir es aufgenommen haben, verwenden wir sofort die Suchmethode "ungleich dem vorherigen Wert" und reduzieren die Liste weiter, indem wir nach "Die Anzahl der Änderungen ist 1" suchen. Die Waffe wurde genau einmal geändert.


An der Stelle, an der wir unsere erste Waffe abholen, erscheint auch ein Waffenverstärker. Nachdem Sie es ausgewählt und aufgenommen haben, können Sie die Anzahl der Adressen auf 1 reduzieren. Wenn es jedoch nicht funktioniert, töten Sie einfach Ihren Charakter und wechseln Sie die Waffe erneut. (Vergessen Sie nicht die Suche nach jeder Änderung).


Bei einem Waffenverstärker besteht das Risiko, dass sich nicht der Wert der Waffe selbst ändert, sondern die Flagge an einer anderen Stelle im Speicher. Hoffen wir jedoch, dass die Autoren des Spiels Speicher und Anweisungen gespeichert haben. Und ich hatte Glück, dass der Bonus "R" den Wert der Waffe geändert hat und die Adresse in Koordinate AA 16 war . (Ich könnte mich irren, aber ich habe die Waffe des Helden in verschiedenen Versionen des Contra-Spiels wiederholt überwacht, wie überall dort, wo diese Adresse AA 16 war. )


Mir ist auch aufgefallen, dass der Bonus "R" den Wert in der Adresse um 16 10 oder 10 16 erhöht hat, dh um 1 die erste Ziffer der Hexadezimalzahl erhöht hat.


Nach dem Neustart im Fenster „Ram Watch“ ist zu sehen, dass der Basiswert tatsächlich 00 16 ist , der Bonus „M“ die zweite Ziffer um 1 und der Bonus „R“ die erste erhöht.


Sie können sich für einen Spreadgan entscheiden, er wird sich auf diesem Level definitiv treffen, oder Sie können den Wert der Adresse ändern, um zu sehen, welche Zahlen, welche Waffen sie herstellen. Empirisch fand ich heraus, dass 01 16 "Maschinengewehr", 02 16 "Feuer", 03 16 "Spreadgun" und 04 16 "Laser" ist. Wenn Sie andere Werte in die zweite Ziffer eingeben, treten verschiedene Störungen auf.


Nachdem Sie das Spiel zurückgesetzt und den Wert 1x 16 eingegeben haben (wobei "x" eine der akzeptablen Optionen ist), bevor Sie den "Schnell" -Bonus auswählen, können Sie feststellen, dass die erneute Auswahl des Bonus nichts ändert.


Jetzt können Sie das Spiel neu starten, das Spiel für zwei Personen starten und versuchen, die Adressen neben AA 16 zu ändern. (Es gibt zwei davon, die Suche wird nicht lange dauern.) Nachdem ich den zweiten Spieler erschossen hatte, stellte ich sehr schnell fest, dass die Waffen des zweiten Spielers wirklich in der Nähe unter der Adresse AB 16 aufbewahrt werden . Und jetzt kennen wir die Adressen, die für uns von Interesse sind, und den Wert, der dort platziert werden sollte. Es ist Zeit herauszufinden, was er an diese Adressen schreibt.


Als ich einen Haltepunkt auf die Aufzeichnung dieser Adresse setzte, stellte ich fest, dass die Aufzeichnung dort mehrmals und eine davon nach dem Begrüßungsbildschirm erfolgt. Der folgende Code macht diesen Eintrag:


AdresseOpcodeMnemonikArgumenteA.X.
C307A2 28LDX# $ 28????
C309A9 00Lda# $ 00??28 oder 29 oder ... oder F0
C30B95 00STA$ 00, X.0028 oder 29 oder ... oder F0
C30dE8Inx0028 oder 29 oder ... oder F0
C30eE0 F0CPX# $ F00029 oder 30 oder ... oder F0
C310D0 F9Bne$ C30B0029 oder 30 oder ... oder F0

Wenn Sie das Spiel hier sorgfältig lesen, wird der Adressbereich von 0028 16 bis 00F0 16 auf Null gesetzt, offensichtlich beide Adressen, die für uns im Bereich von Interesse sind. Es wird also keinen einfachen Weg geben. Ich muss "Code Injection" und die einfachste Lösung verwenden, die ich sehe, um herauszufinden, woher wir kommen, die Ausführung an einen Ort umleiten, der frei von Code und Daten im Speicher ist, dort meine Version der Schleife schreiben, die den gesamten Bereich mit Ausnahme der Adressen 00AA 16 und 00AB 16 belegt, und den Wagen zurückgeben Ausführung zurück. Dies ist übrigens die klassischste Version der Injektion. Sie können auch davon ausgehen, dass wir hier von der JSR-Anweisung (Jump to SubRoutine) kommen. Dies ist mit dem Stack leicht zu überprüfen.


Wie funktioniert der Stack?

In 6502-Prozessoren befindet sich der Stapel für alle Computer, die auf diesem Prozessor basieren, immer im Adressbereich 0100 16 - 01FF 16 und wächst von einer größeren Adresse zu einer kleineren. Es gibt ein separates Register, das auf die Oberseite des Stapels zeigt. Anfangs entspricht es FF 16, da sich ein signifikanteres Byte nie ändert. Das Register selbst zeigt immer das höchste Byte an, das nicht mit nützlichen Daten belegt ist.


Der Emulator-Debugger zeigt nicht den Wert des Registers "Stapelzeiger" an, sondern die Adresse, auf die sich das Register bezieht, und im Moment ist es 01F2 16 , einfache Berechnungen zeigen, dass die letzten Daten auf dem Stapel C3 16 und C2 16 sind , was führen könnte Ich denke an die Adresse C3C2 16, aber 6502 ist ein Prozessor vom Typ "Little Endian", und daher wird beim Ausführen von Anweisungen und Speichern der Adresse im Speicher oder auf dem Stapel zuerst das weniger signifikante Byte geschrieben. Und wenn sich die Adresse wirklich oben auf dem Stapel befindet, ist dies die Adresse C2C3 16 . Und dies ist die Adresse des letzten Arguments der JSR-Anweisung, wenn es sich wiederum überhaupt um eine Adresse handelt. Es ist sehr einfach zu überprüfen, schauen Sie sich einfach an, was zwei Bytes über der Adresse C2C3 16 geschrieben ist .


AdresseOpcodeMnemonikArgumente
C2C120 07 C3Jsr$ C307

Wie Sie sehen können, ist dies eine JSR-Anweisung an C307 16 , was bedeutet, dass die Annahme des Unterprogramms korrekt ist.


Jetzt müssen Sie den Injektionscode korrekt schreiben, einen geeigneten Ort dafür finden, an diesen Ort schreiben und den JSR-Befehl auf diese Injektion umleiten.


Hilfeseiten

Opcodes , Anweisungen , viele Informationen zur 6502-Baugruppe .


Dafür ist es sehr praktisch, einen Notizblock zu verwenden, ich habe Visual Studio Code dafür. Jeder hat seinen eigenen Schreibstil. Ich persönlich bin der erste, der eine JSR-Anweisung mit ihrer Adresse schreibt, um zu wissen, wo Änderungen vorgenommen werden müssen, und einen vollständigen Opcode, um zu wissen, was geändert werden muss.


Nach ein paar Einrückungen dupliziere ich den Schleifencode. Er kann bereits ohne Adressen verwendet werden. Es ist jedoch äußerst nützlich, Mnemonics mit anderen Argumenten als Opcodes anzuzeigen, und es ist nützlich, die Anweisung nach dieser Schleife zusammen mit der Adresse abzurufen, um zu wissen, wohin von der Injektion zurückgekehrt werden soll.


C2C1:20 07 C3 JSR $C307 A2 28 LDX #$28 A9 00 LDA #$00 95 00 STA $00,X E8 INX E0 F0 CPX #$F0 D0 F9 BNE $C30B C312:A2 07 LDX #$07 

Ein paar Einrückungen weiter unten können Sie den Code der Injektion selbst schreiben. Tatsächlich handelt es sich um ein Duplikat des Zyklus selbst mit einigen Ergänzungen.


 C312:A2 07 LDX #$07 A2 28 LDX #$28 A9 00 LDA #$00 95 00 STA $00,X E8 INX E0 AA CPX #$AA D0 F9 BNE -7 A9 13 LDA #$13 95 00 STA $00,X E8 INX E0 AC CPX #$AC D0 F9 BNE -7 A9 00 LDA #$00 95 00 STA $00,X E8 INX E0 F0 CPX #$F0 D0 F9 BNE -7 

Aus Gründen der Übersichtlichkeit habe ich den Einzug anstelle der Sprungortadresse angegeben.


Wenn Sie diesen Code nicht lesen könnten

Hier ist der gleiche Zyklus, aber in drei Teile unterteilt. Im ersten Zyklus vergleichen wir das Register X mit dem Wert AA 16 , da wir alle Adressen auf die Adresse 00AA 16 setzen müssen , nachdem wir 13 16 in das Register A, den Wert der gepumpten Spreizpistole, eingetragen und den zweiten Zyklus geschrieben haben es an die Adresse 00AC 16, ab der der Rest des Bereichs wieder auf Null gesetzt werden soll. Wir kehren zum Register A Null zurück und Null den Rest des Bereichs.


Es ist unbedingt erforderlich, die Anweisungen zur Rückführung der Einspritzung am Ende zu vervollständigen.


 E0 F0 CPX #$F0 D0 F9 BNE -7 4C 12 C3 JMP $C312 

Der Einfachheit halber ziehe ich es vor, die folgenden Opcodes in die Datei zu schreiben.


 4C 12 C3 JMP $C312 A2 28 A9 00 95 00 E8 E0 AA D0 F9 A9 13 95 00 E8 E0 AC D0 F9 A9 00 95 00 E8 E0 F0 D0 F9 4C 12 C3 

Und nachdem Sie die Opcodes gezählt haben, können Sie leicht feststellen, dass es 32 davon gibt. Daher müssen Sie 20 16 nicht belegte Adressen im ROM finden. In der Regel sind nicht belegte Adressen große Räume mit denselben Werten, meistens Nullen oder FF 16. Es muss nur ein so großes Stück gefunden werden, dass es mindestens 35 10 Adressen sein muss, damit ein gewisser Spielraum besteht.


Im "Hex Editor" habe ich einen solchen Bereich in B29E 16 -BFFF 16 gefunden . Die Verwendung des Anfangs solcher freien Stellen für Injektionen kann gefährlich sein. Ich empfehle Ihnen daher, am Ende einen Injektionscode zu schreiben. Die bequemste Adresse zum Starten der Injektion ist BFE0 16 , aber dies ist die Adresse im Speicher der Konsole. Um herauszufinden, wo sie sich in der ROM-Datei befindet, klicken Sie mit der rechten Maustaste darauf und wählen Sie "Hierher in die ROM-Datei".


Jetzt können Sie hier den gesamten Opcode kopieren und einfügen (32 Werte). Anweisungen zum Ändern der letzten Berührung


 C2C1:20 07 C3 JSR $C307 

Um zu der Injektionsadresse zu springen, die ich habe, ist BFE0 16 .


 C2C1:20 E0 BF JSR $BFE0 

Natürlich den wahren Ort der Anweisung im ROM finden.


Die Antwort auf die Frage unaufmerksamer Profis.

Die Adresse des JSR-Befehls wird auf dem Stapel abgelegt, daher ist unabhängig vom Ort des Sprungs dieselbe Rücksprungadresse vorhanden, und von der Injektion kehren wir mit dem JMP-Befehl zum Code zurück, der den Stapel in keiner Weise beeinflusst. Somit arbeitet der RTS-Befehl an derselben Stelle wie ohne Injektion und kehrt an dieselbe Stelle wie ohne Injektion zurück. Infolgedessen bricht diese Injektion den Stapel nicht.


PS Für immer müssen Sie immer noch sicherstellen, dass die Charaktere nach dem Tod mit einer gepumpten Ausbreitung wiedergeboren werden, aber ich bin sicher, dass Sie mit dem erworbenen Wissen bewaffnet sind, mit dem Sie selbst umgehen können. Ich werde meine Augen auf etwas anderes richten. Unity-Engine zum Beispiel.

Source: https://habr.com/ru/post/de447990/


All Articles