Animal Crossing Entwicklermodus Reverse Engineering

Bestimmte des Codes auf einem echten GameCube

Letzten Sommer habe ich mit dem Reverse Engineering von Animal Crossing für den GameCube begonnen. Ich wollte die Möglichkeit untersuchen, Mods für dieses Spiel zu erstellen. Außerdem wollte ich den Prozess dokumentieren, um Tutorials für Leute zu erstellen, die sich für das Hacken von ROMs und Reverse Engineering interessieren. In diesem Beitrag werde ich über die Entwickler-Debugging-Funktionen sprechen, die im Spiel verblieben sind, und darüber, wie ich Cheat-Combos entdeckt habe, mit denen sie freigeschaltet werden können.

new_Debug_mode


Als ich die verbleibenden Debugging-Symbole studierte, bemerkte ich die Namen der Funktionen und Variablen, die das Wort "Debug" enthielten, und entschied, dass es interessant wäre zu sehen, ob noch Debugging-Funktionen im Spiel vorhanden waren. Wenn es mir gelingt, die Debugging- oder Entwicklungsfunktionen zu aktivieren, hilft mir dies beim Erstellen von Mods.

Die erste Funktion, die mir aufgefallen ist, war new_Debug_mode . Es wird von der entry aufgerufen, die unmittelbar nach dem Ende des Nintendo-Logo-Bildschirms startet. Sie platziert 0x1C94 die 0x1C94 und speichert einen Zeiger darauf.

Nachdem der entry in der gehosteten Struktur mit dem Offset 0xD4 unmittelbar vor dem Aufruf von mainproc Wert 0 gesetzt.


Um zu sehen, was passiert, wenn der Wert nicht Null ist, habe ich den li r0, 0 bei 80407C8C gepatcht und durch li r0, 1 . Die li r0, 0 des li r0, 0 sind 38 00 00 00 wobei der zugewiesene Wert am Ende des Befehls steht, sodass ich die Bytes einfach durch 38 00 00 01 ersetzen und li r0, 1 . Als zuverlässigere Methode zum Erstellen von Anweisungen können Sie so etwas wie kstool :

$ kstool ppc32be "li 0, 1"
li 0, 1 = [ 38 00 00 01 ]


Im Dolphin-Emulator kann dieser Patch angewendet werden, indem Sie in den Spieleigenschaften auf die Registerkarte "Patches" gehen und ihn wie folgt eingeben:


Nach dem Zuweisen des Werts 1 wurde am unteren Bildschirmrand ein interessantes Diagramm angezeigt:



Es sah aus wie ein Leistungsindikator: Die kleinen Balken am unteren Bildschirmrand nahmen zu oder ab. (Als ich mir später die Namen der Funktionen ansah, die dieses Diagramm zeichnen, stellte ich fest, dass sie tatsächlich Metriken für die CPU- und Speichernutzung anzeigen.)

Es war großartig, aber nicht besonders hilfreich. Nachdem ich den Wert 1 zugewiesen hatte, wurde meine Stadt nicht mehr geladen, sodass hier nichts anderes getan werden konnte.

Zuru-Modus


Ich suchte erneut nach anderen Verweisen auf Debugging-Funktionen und stieß mehrmals auf etwas, das als „Zuru-Modus“ bezeichnet wurde. Zweige von Codeblöcken mit Debugging-Funktionalität überprüften häufig die Variable zurumode_flag .

game_move_first Funktion

zzz_LotsOfDebug (der Name, den ich mir game_move_first habe) in der game_move_first gezeigten Funktion zurumode_flag nur aufgerufen, wenn zurumode_flag ungleich Null ist.

Auf der Suche nach Funktionen, die diesem Wert zugeordnet sind, habe ich Folgendes gefunden:

  • zurumode_init
  • zurumode_callback
  • zurumode_update
  • zurumode_cleanup

Auf den ersten Blick ist ihr Zweck mysteriös, sie jonglieren mit Bits in den Offsets einer Variablen namens osAppNMIBuffer .

So sah die Arbeit dieser Funktionen auf den ersten Blick aus:

zurumode_init


  • Setzt zurumode_flag auf 0
  • Überprüft mehrere Bits in osAppNMIBuffer
  • Speichert einen Zeiger auf die Funktion zurumode_callback in der padmgr Struktur
  • zurumode_update

zurumode_update


  • Überprüft mehrere Bits in osAppNMIBuffer
  • Abhängig vom Wert dieser Bits wird zurumode_flag aktualisiert
  • Druckt eine Formatzeichenfolge in die Betriebssystemkonsole.

Dies ist normalerweise nützlich, um dem Code einen Kontext zu geben, aber die Zeile enthielt viele nicht druckbare Zeichen. Der einzige erkennbare Text war "zurumode_flag" und "% d".

Zeichenfolge im Zuru-Modus

Angenommen, es könnte sich um japanischen Text mit Multibyte-Zeichencodierung handeln, habe ich die Zeichenfolge durch das Codierungserkennungswerkzeug geleitet und festgestellt, dass die Zeichenfolge mit Shift-JIS codiert wurde. In der Übersetzung bedeutete die Zeile einfach "Der Wert von zurumode_flag hat sich von% d auf% d geändert." Dies gibt uns nicht viele neue Informationen, aber jetzt wissen wir, dass Shift-JIS verwendet wird: In Binärdateien und Zeilentabellen gibt es viel mehr Zeilen in dieser Codierung.

zurumode_callback


  • zerumode_check_keycheck
  • Überprüft mehrere Bits in osAppNMIBuffer
  • Der zurumode_flag Wert wird zurumode_flag
  • zurumode_update

zerumode_check_keycheck bis wir uns wegen einer anderen Schreibweise getroffen haben ... was ist das?

zerumode_check_keycheck

Eine riesige komplexe Funktion, die viel mehr an Bits mit unbenannten Werten arbeitet.

Zu diesem Zeitpunkt habe ich mich entschlossen, einen Schritt zurückzutreten und andere Debugging-Funktionen und -Variablen zu untersuchen, da ich mir nicht sicher war, wie wichtig der Zuru-Modus ist. Außerdem habe ich nicht verstanden, was "Schlüsselüberprüfung" hier bedeutet. Ist es möglich, dass dies ein kryptografischer Schlüssel ist?

Zurück zum Debuggen


Ungefähr zu dieser Zeit bemerkte ich ein Problem beim Laden von Debugging-Symbolen in die IDA. Die Datei foresta.map auf der Festplatte des Spiels enthält viele Adressen und Namen von Funktionen und Variablen. Zuerst habe ich nicht gesehen, dass die Adressen für jeden Abschnitt von vorne beginnen, deshalb habe ich ein einfaches Skript geschrieben, das einen Namenseintrag für jede Zeile der Datei hinzufügt.

Ich habe neue IDA-Skripte geschrieben, um das Laden von Symboltabellen für verschiedene Abschnitte des Programms zu .text : .text , .rodata , .data und .bss . Der .text Abschnitt enthält alle Funktionen, daher habe ich ihn so gestaltet, dass das Skript diesmal beim .text des Namens die Funktionen an jeder Adresse automatisch erkennt.

In den m_debug.o erstellte er nun ein Segment für jedes m_debug.o (z. B. m_debug.o , das als Code für etwas namens m_debug kompiliert werden m_debug ) und legte den Platz und die Namen für jedes Datenelement fest.

Dies gab mir viel mehr Informationen, aber ich musste den Datentyp für jedes Datenelement manuell festlegen, da ich jedes Datenobjekt als einfaches Byte-Array definierte. (Rückblickend verstehe ich, dass es besser wäre anzunehmen, dass Fragmente von 4 Bytes 32-Bit-Ganzzahlen enthielten, da es viele und viele Adressen von Funktionen und Daten enthielt, die für die Erstellung von Querverweisen wichtig sind.)

Beim Studium des neuen .bss Segments für m_debug_mode.o ich mehrere Variablen der Form quest_draw_status und event_status . Dies ist interessant, weil ich wollte, dass nützliche Informationen, nicht nur ein Leistungsdiagramm, im Debug-Modus angezeigt werden. Glücklicherweise gab es aus diesen Datensätzen Querverweise auf einen riesigen Code, der debug_print_flg überprüfte.

Mit einem Debugger im Dolphin-Emulator habe ich einen Haltepunkt an der Stelle der Funktion festgelegt, an der debug_print_flg überprüft wurde (bei 8039816C ), um zu verstehen, wie diese Überprüfung funktioniert. Das Programm wurde jedoch nie an diesen Haltepunkt übergeben.

game_debug_draw_last wir game_debug_draw_last warum dies passiert: Diese Funktion wird von game_debug_draw_last . Ratet mal, welcher Wert vor seinem bedingten Aufruf überprüft wird? zurumode_flag ! Was zur Hölle ist los?

zurumode_flag check

Ich habe einen Haltepunkt für diese Prüfung festgelegt ( 80404E18 ) und es hat sofort funktioniert. Der Wert von zurumode_flag war Null, sodass das Programm bei normaler Ausführung den Aufruf dieser Funktion verpasst hätte. Ich habe stattdessen eine NOP-Verzweigungsanweisung eingefügt (durch eine Anweisung ersetzt, die nichts bewirkt), um zu überprüfen, was passiert, wenn die Funktion aufgerufen wird.

Im Dolphin-Debugger können Sie dies tun, indem Sie das Spiel anhalten, mit der rechten Maustaste auf die Anweisungen klicken und "Nop einfügen" auswählen:

Dolphin Debugger Nopping

Nichts ist passiert. Dann überprüfte ich, was in der Funktion vor sich ging, und entdeckte ein anderes Verzweigungskonstrukt, das alles Interessante umging, was bei 803981a8 passierte. Stattdessen habe ich auch NOP eingefügt, und der Buchstabe „D“ wurde in der oberen rechten Ecke des Bildschirms angezeigt.

Debug-Modus Buchstabe D.

In dieser Funktion bei 8039816C (ich habe sie zzz_DebugDrawPrint ) gibt es immer noch eine Menge interessanten Codes, der jedoch nicht aufgerufen wird. Wenn Sie diese Funktion in Form eines Diagramms betrachten, sehen Sie, dass es eine Reihe von Verzweigungsoperatoren gibt, die Codeblöcke in der gesamten Funktion überspringen:

Verzweigungen in zzz_DebugDrawPrint

Nachdem ich NOP anstelle mehrerer anderer Verzweigungskonstruktionen eingefügt hatte, sah ich verschiedene interessante Dinge auf dem Bildschirm:

Weitere Debug-Inhalte werden gemeldet

Die nächste Frage war, wie diese Debugging-Funktion aktiviert werden kann, ohne den Code zu ändern.

Außerdem tritt in einigen Verzweigungskonstrukten zurumode_flag in dieser Debug- zurumode_flag erneut auf. Ich habe einen weiteren Patch hinzugefügt, zurumode_update Flag in zurumode_update immer der Wert 2 zugewiesen wird, da es, wenn es nicht mit 0 verglichen wird, speziell mit dem Wert 2 verglichen wird.

Nach dem Neustart des Spiels sah ich in der oberen rechten Ecke des Bildschirms eine solche Meldung "msg. nein. "

Anzeige der nachrichtennummer

Die Nummer 687 ist die Datensatzkennung der zuletzt angezeigten Nachricht. Ich habe es mit dem Table Viewer-Programm überprüft, das ich zu Beginn der Analyse geschrieben habe. Sie können es aber auch mit dem String-Tabellen-Editor mit einer vollständigen GUI überprüfen, die ich zum Hacken von ROMs geschrieben habe. So sieht der Beitrag im Editor aus:

Nachricht 687 im String-Tabellen-Editor

Zu diesem Zeitpunkt wurde klar, dass das Studium des Zuru-Modus nicht mehr weggenommen wurde - es steht in direktem Zusammenhang mit den Debugging-Funktionen des Spiels.

Zurück zum Zuru-Modus


zurumode_init initialisiert verschiedene Dinge:

  • 0xC(padmgr_class) der Wert der zurumode_callback Adresse zurumode_callback
  • 0x10(padmgr_class) der Adresswert von padmgr_class selbst padmgr_class
  • 0x4(zuruKeyCheck) der Wert des letzten Bits in dem von 0x3C(osAppNMIBuffer) geladenen Wort 0x3C(osAppNMIBuffer) .

Ich habe herausgefunden, was padmgr , eine Abkürzung für "Gamepad Manager". Dies bedeutet, dass möglicherweise eine spezielle Kombination von Tasten (Schaltflächen) auf dem Gamepad eingegeben werden kann, um den Zuru-Modus zu aktivieren, oder eine Art Debugging-Gerät oder eine Funktion der Entwicklerkonsole, mit der ein Signal zum Aktivieren gesendet werden kann.

zurumode_init wird nur beim ersten Start des Spiels ausgeführt (wenn die Reset-Taste gedrückt wird, funktioniert es nicht).

Nachdem wir an der Adresse 8040efa4 einen Haltepunkt 8040efa4 , an dem der Wert 0x4(zuruKeyCheck) zugewiesen ist 0x4(zuruKeyCheck) , können wir sehen, dass beim Laden ohne Drücken von Tasten der Wert auf 0 gesetzt wird. Wenn Sie ihn durch 1 ersetzen, passiert etwas Interessantes:

Titelbildschirm mit Zuru-Modus

Der Buchstabe „D“ wird erneut in der oberen rechten Ecke angezeigt (diesmal grün, nicht gelb), und einige Montageinformationen werden ebenfalls angezeigt:

[CopyDate: 02/08/01 00:16:48 ]
[Date: 02-07-31 12:52:00]
[Creator:SRD@SRD036J]


Ein Patch, der zu Beginn immer 0x4(zuruKeyCheck) auf 1 setzt, sieht folgendermaßen aus:

8040ef9c 38c00001

Dies scheint der richtige Weg zu sein, um den Zuru-Modus zu initialisieren. Danach können verschiedene Aktionen erforderlich sein, um die Anzeige bestimmter Debugging-Informationen zu erreichen. Wenn Sie das Spiel starten, einen Spaziergang machen und mit einem Dorfbewohner sprechen, sehen wir keine der oben genannten Nachrichten (außer dem Buchstaben "D" in der Ecke).

Die wahrscheinlichsten Verdächtigen sind zurumode_update und zurumode_callback .

zurumode_update


zurumode_update zuerst in zurumode_init und dann ständig von zurumode_callback .

Es überprüft erneut das letzte Bit 0x3C(osAppNMIBuffer) und aktualisiert dann basierend auf diesem Wert zurumode_flag .

Wenn das Bit Null ist, wird das Flag auf Null gesetzt.

Wenn nicht, wird die folgende Anweisung ausgeführt, wobei der vollständige Wert 0x3c(osAppNMIBuffer) r5 :

extrwi r3, r5, 1, 28

Es extrahiert das 28. Bit aus r5 und speichert es in r3 .

Dann wird 1 zum Ergebnis hinzugefügt, dh das Endergebnis ist immer 1 oder 2.

Dann wird zurumode_flag mit dem vorherigen Ergebnis verglichen, je nachdem, wie viele der 28. und letzten Bits auf 0x3c(osAppNMIBuffer) : 0, 1 oder 2.

Dieser Wert wird in zurumode_flag . Wenn sich nichts ändert, wird die Funktion beendet und der aktuelle Flag-Wert zurückgegeben. Wenn sich der Wert ändert, wird eine viel komplexere Kette von Codeblöcken ausgeführt.

Auf Japanisch wird eine Meldung angezeigt: Der gleiche „zurumode_flag-Wert wurde von% d in% d geändert“, über den wir oben gesprochen haben.

Dann wird eine Reihe von Funktionen mit unterschiedlichen Argumenten aufgerufen, je nachdem, ob das Flag gleich Null geworden ist oder nicht. Der Assembler-Code dieses Teils ist eintönig, daher werde ich seinen Pseudocode zeigen:

 if (flag_changed_to_zero) { JC_JUTAssertion_changeDevice(2) JC_JUTDbPrint_setVisible(JC_JUTDbPrint_getManager(), 0) } else if (BIT(nmiBuffer, 25) || BIT(nmiBuffer, 31)) { JC_JUTAssertion_changeDevice(3) JC_JUTDbPrint_setVisible(JC_JUTDbPrint_getManager(), 1) } 

Beachten Sie, dass wenn das Flag Null ist, das Argument 0 an JC_JUTDbPrint_setVisible übergeben wird.

Wenn das Flag ungleich Null ist und Bit 25 oder Bit 31 auf 0x3C(osAppNMIBuffer) , wird setVisible an Argument 1 übergeben.

Dies ist der erste Schlüssel zum Aktivieren des Zuru-Modus: Das letzte Bit 0x3C(osAppNMIBuffer) muss auf 1 gesetzt werden, um Debugging-Informationen anzuzeigen, und zurumode_flag Wert ungleich Null setzen.

zurumode_callback


zurumode_callback befindet sich bei 8040ee74 und wird wahrscheinlich von einer Funktion aufgerufen, die sich auf das Gamepad bezieht. Nach dem Einfügen eines Haltepunkts in den Dolphin-Debugger zeigt der Aufrufstapel an, dass er tatsächlich von padmgr_HandleRetraceMsg .

Eine ihrer ersten Aktionen war die Ausführung von zerucheck_key_check . Diese Funktion ist komplex, scheint jedoch im Allgemeinen darauf ausgelegt zu sein, den Wert von zuruKeyCheck zu lesen und zu aktualisieren. Bevor ich zur Keycheck-Funktion überging, habe ich mich entschlossen zu überprüfen, wie dieser Wert im Rest der Rückruffunktion verwendet wird.

Dann wird erneut nach einigen Bits in 0x3c(osAppNMIBuffer) . Wenn Bit 26 gesetzt ist oder wenn Bit 25 gesetzt ist und padmgr_isConnectedController(1) einen Wert ungleich Null zurückgibt, wird das letzte Bit in 0x3c(osAppNMIBuffer) auf 1 gesetzt!

Wenn keines dieser Bits gesetzt ist oder Bit 25 gesetzt ist, aber padmgr_isConnectedController(1) 0 zurückgibt, prüft die Funktion, ob das Byte an der Adresse 0x4(zuruKeyCheck) gleich Null ist. Wenn gleich, wird das letzte Bit im ursprünglichen Wert zurückgesetzt und in 0x3c(osAppNMIBuffer) . Wenn nicht, wird das letzte Bit immer noch auf 1 gesetzt.

Im Pseudocode sieht es so aus:

 x = osAppNMIBuffer[0x3c] if (BIT(x, 26) || (BIT(x, 25) && isConnectedController(1)) || zuruKeyCheck[4] != 0) { osAppNMIBuffer[0x3c] = x | 1 // set last bit } else { osAppNMIBuffer[0x3c] = x & ~1 // clear last bit } 

Wenn danach Bit 26 nicht gesetzt ist, ruft die Funktion zurumode_update und wird beendet.

Wenn das Bit gesetzt ist und 0x4(zuruKeyCheck) ungleich Null ist, wird eine 0x4(zuruKeyCheck) , in der Folgendes angezeigt wird: „ZURU% d /% d“.

Um die Zwischensumme zusammenzufassen


Folgendes passiert:

padmgr_HandleRetraceMsg ruft zurumode_callback . Ich gehe davon aus, dass diese "Handle Retrace-Nachricht" bedeutet, dass einfach die Tastenanschläge des Controllers gescannt werden. Bei jedem Scan kann es zu einer Reihe unterschiedlicher Rückrufe kommen.

Wenn zurumode_callback ausgeführt wird zurumode_callback werden die aktuellen Tastenanschläge (Schaltflächen) überprüft. Es sieht so aus, als würde sie eine bestimmte Schaltfläche oder eine Kombination von Schaltflächen überprüfen.

Das letzte Bit im NMI-Puffer wird abhängig von den spezifischen Bits in seinem aktuellen Wert sowie vom Wert eines der zuruKeyCheck Bytes ( 0x4(zuruKeyCheck) ) 0x4(zuruKeyCheck) .

Dann wird zurumode_update und prüft dieses Bit. Wenn es 0 ist, wird das Zuru-Modus-Flag auf 0 gesetzt. Wenn es 1 ist, ändert sich das Modus-Flag auf 1 oder 2, je nachdem, ob Bit 28 gesetzt ist.

Es gibt drei Möglichkeiten, den Zuru-Modus zu aktivieren:

  1. Bit 26 wird auf 0x3C(osAppNMIBuffer)
  2. Bit 25 ist auf 0x3C(osAppNMIBuffer) und der Controller ist mit Port 2 verbunden
  3. 0x4(zuruKeyCheck) nicht Null

osAppNMIBuffer


Interessiert an der Bedeutung von osAppNMIBuffer suchte ich nach „NMI“ und fand im Nintendo-Kontext Links zu „nicht maskierbaren Interrupts“. Es stellt sich heraus, dass der Name dieser Variablen in der Entwicklerdokumentation für Nintendo 64 vollständig erwähnt wird:

osAppNMIBuffer ist ein 64-Byte-Puffer, der bei einem Kaltstart gelöscht wird. Wenn das System aufgrund von NMI neu gestartet wird, ändert sich der Status dieses Puffers nicht.

Tatsächlich ist dies ein kleines Stück Speicher, das während eines „sanften“ Neustarts (mit der Reset-Taste) gespeichert wird. Das Spiel kann diesen Puffer verwenden, um alle Daten zu speichern, während sich die Konsole im Netzwerk befindet. Das ursprüngliche Animal Crossing wurde auf Nintendo 64 veröffentlicht, daher ist es logisch, dass im Code etwas Ähnliches hätte erscheinen müssen.

Wenn wir zur binären Datei boot.dol gehen (alles, was oben gezeigt wurde, war in foresta.rel ), dann hat seine main viele Links zu osAppNMIBuffer . Ein kurzer Blick zeigt, dass es eine Reihe von Überprüfungen gibt, die dazu führen können, 0x3c(osAppNMIBuffer) Werte verschiedener Bits 0x3c(osAppNMIBuffer) mithilfe von ODER-Operationen festgelegt werden.

Die folgenden ODER-Operandenwerte können interessant sein:

  • Bit 31: 0x01
  • Bit 30: 0x02
  • Bit 29: 0x04
  • Bit 28: 0x08
  • Bit 27: 0x10
  • Bit 26: 0x20

Wir erinnern uns, dass die Bits 25, 26 und 28 besonders interessant sind: 25 und 26 bestimmen, ob der Zuru-Modus aktiviert ist, und Bit 28 bestimmt den Flag-Pegel (1 oder 2).
Bit 31 ist ebenfalls interessant, aber es sieht so aus, als würde es sich abhängig von den Werten der anderen ändern.

Bit 26

Zunächst einmal: An der Adresse 800062e0 befindet sich ein Befehl ori r0, r0, 0x20 mit einem Pufferwert von 0x3c . Es setzt Bit 26, das den Zuru-Modus immer einschaltet.

Bit 26 setzen

Damit das Bit gesetzt werden kann, muss das von DVDGetCurrentDiskID zurückgegebene achte Byte DVDGetCurrentDiskID sein. Diese Kennung befindet sich ganz am Anfang des Disk-Images des Spiels und wird unter 80000000 in den Speicher 80000000 . In einer regulären Einzelhandelsversion des Spiels sieht die ID folgendermaßen aus:

47 41 46 45 30 31 00 00 GAFE01..

Wenn Sie das letzte Byte des Bezeichners durch einen 0x99 für 0x99 , erhalten Sie zu Beginn des Spiels das folgende Bild:

Spielversions-ID 0x99

In der Betriebssystemkonsole wird Folgendes angezeigt:

06:43:404 HW\EXI_DeviceIPL.cpp:339 N[OSREPORT]: ZURUMODE2 ENABLE
08:00:288 HW\EXI_DeviceIPL.cpp:339 N[OSREPORT]: osAppNMIBuffer[15]=0x00000078


Alle anderen Patches können entfernt werden. Danach wird der Buchstabe D erneut in der oberen rechten Ecke des Bildschirms angezeigt, es werden jedoch keine Debug-Meldungen mehr aktiviert.

Bit 25

Bit 25 wird in Verbindung mit der Überprüfung von Controller-Port 2 verwendet. Was bewirkt, dass es eingeschaltet wird?

Bit 25 und 28

Es stellt sich heraus, dass er dieselbe Prüfung wie für Bit 28 verwenden sollte: Die Version muss größer oder gleich 0x90 . Wenn Bit 26 gesetzt ist (ID ist 0x99 ), werden auch diese beiden Bits gesetzt und der Zuru-Modus wird weiterhin aktiviert.

Wenn sich die Version jedoch im Bereich von 0x90 bis 0x98 , wird der Zuru-Modus nicht sofort 0x98 . Erinnern Sie sich an die in zurumode_callback durchgeführte zurumode_callback - der Modus wird nur aktiviert, wenn Bit 25 gesetzt ist und padmgr_isConnectedController(1) einen Wert ungleich Null zurückgibt.

Nachdem der Controller mit Port 2 verbunden ist (das Argument isConnectedController hat keine Indizierung), wird der Zuru-Modus aktiviert. Der Buchstabe D und Informationen zur Baugruppe werden auf dem Startbildschirm angezeigt, und wir ... können die Anzeige des Debuggens mit den Tasten des zweiten Controllers steuern!

Einige Schaltflächen führen Aktionen aus, die nicht nur die Anzeige ändern, sondern beispielsweise auch die Geschwindigkeit des Spiels erhöhen.

zerucheck_key_check


Das letzte Rätsel bleibt 0x4(zuruKeyCheck) . Es stellt sich heraus, dass dieser Wert mit einer riesigen komplexen Funktion aktualisiert wird, die ich oben gezeigt habe:

zerumode_check_keycheck

Mit dem Dolphin-Emulator-Debugger konnte ich feststellen, dass der von dieser Funktion überprüfte Wert ein Satz von Bits ist, die den Tastendrücken auf dem zweiten Controller entsprechen.

Die Verfolgung von 0x2(zuruKeyCheck) wird in einem 16-Bit-Wert bei 0x2(zuruKeyCheck) . Wenn die Steuerung nicht angeschlossen ist, lautet der Wert 0x7638 .

2 Bytes, die Flags der Controller-Tastendrücke enthalten, werden heruntergeladen und dann am Anfang von zerucheck_key_check aktualisiert. Der neue Wert wird mit dem Register r4 Funktion padmgr_HandleRetraceMsg , wenn die Rückruffunktion padmgr_HandleRetraceMsg .

Schlüsselprüfung ende

Gegen Ende von zerucheck_key_check gibt es einen weiteren Ort, an dem 0x4(zuruKeyCheck) aktualisiert wird 0x4(zuruKeyCheck) . , r3 , r3 , .

8040ed88 r4 0x4(zuruKeyCheck) . XOR- 1. — ( — ) 0 1. ( 0,
XOR 1 1. 1, 0. . XOR.)

Schlüsselprüfung Ende

Früher, als ich die Werte im Speicher studierte, habe ich dieses Verhalten nicht bemerkt, aber ich werde versuchen, diese Anweisung im Debugger zu unterbrechen, um zu verstehen, was passiert. Der ursprüngliche Wert wird um geladen 8040ed7c.

Ohne Berühren der Controller-Tasten komme ich auf dem Startbildschirm nicht zu diesem Haltepunkt. Um in diesen Codeblock zu gelangen, r5muss der Wert 0xbvor dem Verzweigungsbefehl vor dem Haltepunkt ( 8040ed74) gleich werden. Von den vielen verschiedenen Pfaden, die zu diesem Block führen, weist nur einer an der Adresse einen r5Wert 0xbdavor zu 8040ed68.

R5 auf 0xb setzen

Beachten Sie, dass der Wert unmittelbar davor gleich sein muss , um den Block zu erreichen, der den r5Wert zuweist . Wenn Sie den Blöcken entlang der Kette bis zum Beginn der Funktion folgen, sehen Sie alle Einschränkungen, die erforderlich sind, um diesen Block zu erreichen:0xBr00x1000

  • 8040ed74: Wert r5muss gleich sein0xB
  • 8040ed60: Wert r0muss gleich sein0x1000
  • 8040ebe8: Wert r5muss gleich sein0xA
  • 8040ebe4: Wert r5muss kleiner sein0x5B
  • 8040eba4: Wert r5muss größer sein0x7
  • 8040eb94: Wert r6muss 1 sein
  • 8040eb5c: Wert r0darf nicht 0 sein
  • 8040eb74: Die Werte der Port 2-Tasten sollten sich ändern

Verfolgung des Codepfads

Hier haben wir den Punkt erreicht, an dem die alten Schaltflächenwerte geladen und die neuen Werte gespeichert werden. Dann kommen einige Operationen, die auf die neuen und alten Werte angewendet werden: Die XOR-Operation markiert alle Bits, die sich zwischen den beiden Werten geändert haben. Dann maskiert die UND-Operation den neuen Eingang, um alle Bits zu setzen, die derzeit nicht auf den Zustand 0 gesetzt sind. Das Ergebnis ist ein Satz neuer Bits (Tastendruck) im neuen Wert. Wenn es nicht leer ist, sind wir auf dem richtigen Weg. Um einen Unterschied zu machen , muss sich die vierte der 16-Bit-Tracking-Tasten ändern. Nachdem ich nach der XOR / AND-Operation einen Haltepunkt eingefügt hatte, stellte ich fest, dass die START-Taste diesen Zustand auslöst. Die nächste Frage ist, wie man es anfänglich gleich macht

old_vals = old_vals XOR new_vals
old_vals = old_vals AND new_vals


r0

r00x1000

r50xA. r5und r6werden vom 0x0(zuruKeyCheck)Anfang der Schlüsseltestfunktion geladen und näher am Ende aktualisiert, wenn wir nicht in den Codeblock gelangen, der enthält 0x4(zuruKeyCheck).

Davor gibt es mehrere Stellen, an denen der r5Wert zugewiesen wird 0xA:

  • 8040ed50
  • 8040ed00
  • 8040ed38

8040ed38

  • 8040ed34: Der Wert r0muss gleich sein 0x4000(Taste B wird gedrückt)
  • 8040ebe0: Wert r5muss gleich sein0x5b
  • 8040eba4: Wert r5muss größer sein0x7
  • dann geht alles wie bisher ...

r5 sollte mit beginnen 0x5b

8040ed00

  • 8040ecfc: Wert r0muss gleich sein 0xC000(A und B werden gedrückt)
  • 8040ebf8: Wert r5muss> = 9 sein
  • 8040ebf0: Wert r5muss kleiner als 10 sein
  • 8040ebe4: Wert r5muss kleiner sein0x5b
  • 8040eba4: r5sollte mehr sein0x7
  • dann geht alles wie bisher ...

r5 sollte um 9 beginnen

8040ed50

  • 8040ed4c: Wert r0muss gleich sein 0x8000(Taste A gedrückt)
  • 8040ec04: Wert r5muss kleiner sein0x5d
  • 8040ebe4: Wert r5muss größer sein0x5b
  • 8040eba4: Wert r5muss größer sein0x7
  • dann geht alles wie bisher ...

r5sollte beginnen mit 0x5c

Es scheint, dass es zwischen den Tastenanschlägen eine Art Status gibt, nach dem Sie eine bestimmte Folge von Combos über die Tasten eingeben müssen, die mit dem Drücken von START enden. Es sieht so aus, als ob A und / oder B direkt vor dem Start gehen sollten.

Wenn Sie den Pfad des Codes verfolgen, der den r5Wert auf 9 setzt, entsteht ein Muster: r5- Es ist ein zunehmender Wert, der entweder zunehmen kann, wenn ein r0geeigneter Wert gefunden wird, oder Null. Die seltsamsten Fälle, in denen dies kein Wert im Bereich von 0x0bis ist0xBtreten auf, wenn Schritte mit mehreren Tasten verarbeitet werden, z. B. wenn A und B gleichzeitig gedrückt werden. Eine Person, die versucht, diese Kombination einzugeben, kann normalerweise nicht beide Tasten gleichzeitig drücken, während sie das Gamepad verfolgt. Sie müssen also die gedrückte Taste verarbeiten erster.

Wir untersuchen weiterhin verschiedene Codepfade:

  • r5nimmt den Wert 9 an, wenn an der Adresse RECHTS gedrückt wird 8040ece8.
  • r5nimmt den Wert 8 an, wenn die rechte Taste C an der Adresse gedrückt wird 8040eccc.
  • r5nimmt den Wert 7 an, wenn die linke Taste C an der Adresse gedrückt wird 8040ecb0.
  • r5nimmt den Wert 6 an, wenn LEFT an der Adresse gedrückt wird 8040ec98.
  • r5nimmt den Wert 5 an (und r6 nimmt den Wert 1 an), wenn an der Adresse DOWN gedrückt wird 8040ec7c.
  • r5 4, C 8040ec64 .
  • r5 3, C 8040ec48 .
  • r5 2, UP 8040ec30 .
  • r5 1 ( r6 1), Z 8040ec1c .

:

Z, UP, C-DOWN, C-UP, DOWN, LEFT, C-LEFT, C-RIGHT, RIGHT, A+B, START

Z : Z, 0x2030 : ( 0x10 0x20 ). , UP/DOWN/LEFT/RIGHT — D-pad, .

-


:

  1. Halten Sie die Stoßstangen L + R gedrückt und drücken Sie Z.
  2. D-up
  3. C-DOWN
  4. C-up
  5. D-down
  6. D-links
  7. C-links
  8. C-RECHTS
  9. D-RECHTS
  10. A + B.
  11. STARTEN

Es funktioniert! Schließen Sie den Controller an den zweiten Port an und geben Sie den Code ein. Danach werden die Debug-Informationen angezeigt. Danach können Sie die Tasten am zweiten (oder sogar dritten) Controller drücken, um verschiedene Aktionen auszuführen.

Diese Kombination funktioniert, ohne die Versionsnummer des Spiels zu patchen. Es kann sogar in einer regulären Einzelhandelsversion des Spiels ohne Cheat-Tools oder Konsolen-Mods verwendet werden. Durch erneutes Eingeben von Combos wird der Zuru-Modus deaktiviert.

Verwenden des Codes auf einem echten GameCube

«ZURU %d/%d» zurumode_callback , , ID 0x99 (, -). — , r5 . 1, , , r6 1.

, , , . , — . , - , .

, Z — , , , . fault_callback_scroll , . , NOP. , :

JUTConsole-Müllzeichen

Nachdem ich dies alles getan hatte, stellte ich fest, dass das Aufrufen des Debug-Modus durch Patchen der
Versions- ID 0x99bereits anderen Personen bekannt ist: https://tcrf.net/Animal_Crossing#Debug_Mode . (Außerdem enthält der Link gute Hinweise, die auf verschiedene Meldungen hinweisen und über andere Dinge sprechen, die mit dem Controller in Port 3 ausgeführt werden können.) Soweit ich weiß, hat jedoch noch niemand die Cheat-Kombination veröffentlicht.

Das ist alles Es gibt andere Entwicklerfunktionen, die ich untersuchen möchte, wie den Debug-Bildschirm der Karte und den NES-Emulator-Auswahlbildschirm und wie man sie ohne Verwendung von Patches aktiviert.

Kartenauswahlbildschirm


Darüber hinaus werde ich Artikel zum Reverse Engineering von Dialogsystemen, Ereignissen und Quests veröffentlichen, um Mods zu erstellen.

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


All Articles