jamchambin blogi
Viime kesänä aloin tehdä reverse engineeringiä GameCubelle julkaistavaan Animal Crossing -peliin tutkiakseni mahdollisuuksia luoda modeja peliin. Halusin myös dokumentoida prosessin luodakseni tutoriaaleja ihmisille, jotka ovat kiinnostuneita ROM-hakkeroinnista ja reverse engineeringistä.Tässä postauksessa tutkin kehittäjän debuggausominaisuuksia, jotka ovat vielä jäljellä pelissä, ja miten löysin huijauskombon, jota voidaan käyttää niiden avaamiseen.
new_Debug_mode
Katsellessani jäljelle jääneitä debug-symboleita,huomasin funktioita ja muuttujien nimiä, jotka sisälsivät sanan ”debug”, ja ajattelin, että olisi mielenkiintoista nähdä, mitä debug-toimintoja pelissä voisi olla jäljellä. Jos siellä olisi debuggaus- tai kehittäjäominaisuuksia, jotka voisin aktivoida, se voisi auttaa myös modien luomisessa.
Ensimmäinen funktio, jota vilkaisin, oli new_Debug_mode
.Sitä kutsuu entry
-funktio, joka suoritetaan heti Nintendotrademark-näytön päätyttyä. Kaikki mitä se tekee on allokoida 0x1C94
tavun rakenne ja tallettaa sen osoitin.
Kun sitä kutsutaan entry
:ssa, arvo 0 asetetaan offsetiin 0xD4
allokoidussa rakenteessa,juuri ennen kuin mainproc
kutsutaan.
Katsoakseni, mitä tapahtuu, kun arvo ei ole nolla, korjasin li r0, 0
-käskyn kohdasta 80407C8C
kohtaan li r0, 1
. Ohjeen li r0, 0
raa’at tavut ovat 38 00 00 00
,jossa määritetty arvo on ohjeen lopussa, joten voit vain vaihtaa sen 38 00 00 01
:ksi saadaksesi li r0, 1
. Jos haluat luotettavamman tavan koota käskyjä,voit käyttää esimerkiksi kstool
:
Voit soveltaa tätä korjausta Dolphin-emulaattorissa menemällä pelin sproperties-välilehdelle ”Patches” ja syöttämällä sen näin:
Tämän arvon asettaminen arvoon 1 sai aikaan sen, että ruudun alareunaan ilmestyi mielenkiintoisen näköinen kuvaaja:
Se näytti suorituskykymittarilta, jossa ruudun alareunassa olevat pikku pylväätkasvoivat ja kutistuivat. (Myöhemmin, kun selvitin kuvaajan piirtävien funktioiden nimiä, huomasin, että ne todellakin näyttävät suorittimen ja muistin käytön mittarit.) Tämä oli siistiä, mutta ei erityisen hyödyllistä. Arvon asettaminen yli 1 pysäytti itse asiassa mytownin latautumisen, joten ei näyttänyt siltä, että tällä olisi ollut paljon muuta tekemistä.
Zuru-tila
Aloin katsella ympärilleni muita viittauksia debug-aiheisiin asioihin, ja näin jotain nimeltä ”zuru-tila” putkahtavan esiin muutaman kerran. Haarautumiset koodilohkoihin, joissa oli debug-toiminnallisuutta, tarkistivat usein muuttujan nimeltä zurumode_flag
.
Yllä olevassa game_move_first
-funktiossa zzz_LotsOfDebug
(keksimäni nimi)kutsutaan vain, jos zurumode_flag
ei ole nolla.
Etsimällä tähän arvoon liittyviä funktioita saadaan seuraavat:
zurumode_init
zurumode_callback
zurumode_update
zurumode_cleanup
Ensimmäisellä silmäyksellä ne ovat kaikki hieman hämäräperäisiä, ja ne näpyttelevät erinäisiä bittejä muuttujan nimeltä osAppNMIBuffer
offseteissä.Tässä on ensimmäinen katsaus siihen, mitä nämä funktiot tekevät:
zurumode_init
- Säädä
zurumode_flag
arvoksi 0 - Tarkista joitakin bittejä
osAppNMIBuffer
:ssä - Tallenna osoitin
zurumode_callback
-funktioonpadmgr
-rakenteeseen - Kutsu
zurumode_update
.
zurumode_update
- Tarkista jotkin bitit
osAppNMIBuffer
:ssä - Päivitä
zurumode_flag
ehdollisesti näiden bittien perusteella - Tulosta muotoilujono käyttöjärjestelmän konsoliin.
Tällainen on yleensä hyödyllistä koodin kontekstin antamiseksi, mutta merkkijonossa oli joukko tulostamattomia merkkejä. Ainoa tunnistettava teksti oli ”zurumode_flag” ja ”%d”.
Ajattelin, että kyseessä saattaisi olla japanilaisteksti, jossa käytetään monen tavun merkkikoodausta, ja ajoin merkkijonon merkkikoodauksen tunnistustyökalun läpi ja sain selville, että merkkijono oli Shift-JIS-koodattu. Käännetty merkkijono tarkoittaa vain ”zurumode_flag hasbeen changed from %d to %d”. Tämä ei anna paljon uutta tietoa, mutta tieto Shift-JIS:n käytöstä antaa, koska binääritiedostoissa ja merkkijonotaulukoissa on paljon muitakin merkkijonoja, jotka käyttävät tätä koodausta.
zurumode_callback
- Kutsut
zerumode_check_keycheck
- Tarkistaa joitain bittejä
osAppNMIBuffer
- Tulostaa
zurumode_flag
:n arvon jonnekin - Kutsut
zurumode_update
zerumode_check_keycheck
eivät näkyneet aiemmin erilaisen kirjoitusasun vuoksi.. mikä se on?
Jättimäinen monimutkainen funktio, joka tekee paljon muuta bittien vääntelyä arvoille, joilla ei ole nimeä.Tässä vaiheessa päätin perääntyä ja etsiä muita debuggaukseen liittyviä funktioita ja muuttujia, sillä en ollut edes varma, mikä zuru-tilan merkitys oli. En myöskään ollut varma, mitä ”key check” tarkoitti tässä. Voisiko se olla kryptografinen avain?
Paluu debuggaukseen
Tänään huomasin, että IDA:han ladattujen debug-symbolien lataamisessa oli ongelma. Pelilevyllä oleva foresta.map
-tiedosto sisältää joukon funktioiden ja muuttujien osoitteita ja nimiä. En ollut alun perin huomannut, että jokaisen osion osoitteet alkoivat nollasta, joten perustin yksinkertaisen skriptin, joka lisäsi nimimerkinnän jokaiselle riville tiedostossa.
Asensin uusia IDA:n skriptejä korjatakseni symbolikartan lataamisen ohjelman eri osioille: .text
, .rodata
, .data
ja .bss
. Osiossa .text
ovat kaikki funktiot,joten asetin skriptin havaitsemaan automaattisesti funktiot jokaisessa osoitteessa, kun nimeä asetetaan tällä kertaa.
Dataosioille asetin skriptin luomaan segmentin jokaiselle binääriobjektiin kuuluvalle objektille (kuten m_debug.o
,joka olisi käännettyä koodia jollekin m_debug
-nimiselle objektille) ja määrittelemään tilaa ja nimiä kullekin datalle.Näin sain paljon enemmän tietoa kuin aiemmin, vaikka minun piti nyt määritellä manuaalisesti tietotyyppi kullekin datapalalle, koska asetin jokaisen dataobjektin yksinkertaiseksi tavujoukoksi. (Jälkikäteen ajateltuna olisi ollut parempi ainakin olettaa, että kaikki tiedot, joiden koko on 4 tavun kerrannainen, sisälsivät 32-bittisiä kokonaislukuja, koska niitä on niin paljon ja monet niistä sisältävät osoitteita funktioihin ja tietoihin, jotka ovat tärkeitä ristiviittausten rakentamisen kannalta.)
Katsellessani uutta .bss
-segmenttiä m_debug_mode.o
:n kohdalla huomasin joitain muuttujia, kuten quest_draw_status
ja event_status
. Nämä ovat mielenkiintoisia, koska haluan saada debug-tilan näyttämään jotain hyödyllisempää kuin suorituskykykaavio. Onneksi näistä tietomerkinnöistä oli ristiviittauksia valtavaan koodinpätkään, joka tarkistaa debug_print_flg
.
Käyttämällä Dolphinin debuggeria asetin katkaisupisteen funktiolle, jossa debug_print_flg
tarkistetaan (kohdassa 8039816C
) nähdäkseni, miten tarkistus toimii. Pysäytyspiste ei koskaan osunut.
Katsotaanpa miksi: tätä funktiota kutsutaan game_debug_draw_last
. Arvaa, mikä arvo tarkistetaan ennen ehdollista kutsumista? zurumode_flag
. Mikä hitto se on?
Asetin breakpointin tuohon checkiin (80404E18
) ja se katkesi heti. zurumode_flag
:n arvo oli nolla, joten se normaalisti ohittaisi tämän funktion kutsumisen. Otin haarautumiskäskyn pois (korvasin sen käskyllä, joka ei tee mitään) nähdäkseni, mitä tapahtuisi, kun funktiota kutsutaan.
Dolphinin debuggerissa voit tehdä tämän pysäyttämällä pelin, napsauttamalla hiiren kakkospainikkeella käskyä ja napsauttamalla sitten ”Insert nop”:
Mitään ei tapahtunut. Sitten tarkistin, mitä funktion sisällä on tapahtunut, ja löysin toisen haarautumislausekkeen, joka voisikiertää kaiken mielenkiintoisen ohi kohdassa 803981a8
. NOPpasin senkin pois, ja näytön oikeaan yläkulmaan ilmestyi D-kirjain.
Tässä funktiossa oli joukko mielenkiintoiselta näyttävää koodia kohdassa 8039816C
(kutsuin sitä zzz_DebugDrawPrint
),mutta mitään siitä ei kutsuttu. Jos katsot tämän funktion graafinäkymää, näet, että siinä on sarja haarautumislauseita, jotka hyppäävät lohkojen yli koko funktion ajan:
NOP-poistamalla useampia näistä haarautumislausekkeista aloin nähdä, että näytölle tulostui erilaisia asioita:
Jatkokysymyksenä on nyt se, miten nämä debug-toiminnot saadaan aktivoitua ilman, että joudutaan muokkaamaan koodia.Lisäksi zurumode_flag
ilmestyy jälleen joidenkin haarautumislausekkeiden kohdalla, jotka on tehty tässä debug draw -funktiossa. lisäsin toisen korjauksen niin, että zurumode_flag
asetetaan aina 2:ksi zurumode_update
:ssä, koska sitä verrataan epäilyttävästi nimenomaan 2:een, kun sitä ei verrata 0:aan.Käynnistettyäni pelin uudelleen, näin tämän ”msg. no” -viestin näkyvän ruudun oikeassa yläkulmassa.
Numero 687 on viimeksi näytetyn viestin kirjautumistunnus. Tarkistin tämän käyttämällä yksinkertaista taulukkokatseluohjelmaa, jonka tein alkuvaiheessa, mutta voit tarkistaa sen myös täydellä GUI-merkkijonotaulukkoeditorilla, jonka tein ROM-hakkerointia varten. Tältä viesti näyttää editorissa:
Tässä vaiheessa oli selvää, että zuru-tilan selvittäminen ei ollut enää vältettävissä; se tarttui suoraan pelin debuggausominaisuuksiin.
Zuru-tila revisited
Palatessamme zurumode_init
:iin, se alustaa muutaman asian:
-
0xC(padmgr_class)
asetetaan osoitteeseenzurumode_callback
-
0x10(padmgr_class)
asetetaan osoitteeseenpadmgr_class
itse -
0x4(zuruKeyCheck)
asetetaan osoitteeseen0x3C(osAppNMIBuffer)
:stä ladatun sanan viimeinen bitti.
Katsoin mitä padmgr
tarkoittaa, ja se on lyhenne sanoista ”gamepad manager”. Tämä viittaa siihen, että voisi ollaerityinen näppäinyhdistelmä (painike), joka on syötettävä gamepadilla zuru-tilan aktivoimiseksi, tai mahdollisesti jokin erityinen debuggauslaite tai kehityskonsolin ominaisuus, jota voitaisiin käyttää signaalin lähettämiseen sen aktivoimiseksi.
zurumode_init
toimii vain, kun peli ladataan ensimmäisen kerran (reset-painikkeen painaminen ei käynnistä sitä).
Asettamalla breakpointin kohtaan 8040efa4
, jossa 0x4(zuruKeyCheck)
on asetettu, näemme, että boottauksen aikana pitämättä mitään näppäimiä alhaalla, se asetetaan arvoon 0. Tämän korvaaminen 1:llä saa aikaan mielenkiintoisen asian:
Kirjain ”D” näkyy taas oikeassa yläkulmassa (tällä kertaa vihreänä keltaisen sijasta),ja siellä on myös build-info:
Parannus, jolla 0x4(zuruKeyCheck)
asetetaan aina 1:ksi käynnistyksen yhteydessä:
8040ef9c 38c00001
Näyttää siltä, että tämä on oikea tapa saada zuru-tila alustettua. Sen jälkeen voi olla erilaisia toimenpiteitä, jotka meidän on tehtävä, jotta saamme tietyt debug-tiedot näkyviin. Pelin käynnistäminen ja käveleminen ympäriinsä ja puhuminen kyläläisen kanssa ei näyttänyt mitään aiemmin mainituista näytöistä (nurkassa olevan D-kirjaimen lisäksi).
Todennäköiset epäillyt ovat zurumode_update
ja zurumode_callback
.
zurumode_update
zurumode_update
kutsutaan ensin zurumode_init
:sta, ja sen jälkeen sitä kutsutaan toistuvasti zurumode_callback
:stä.
Se tarkistaa uudelleen 0x3C(osAppNMIBuffer)
:n viimeisen bitin ja päivittää sitten zurumode_flag
:n sen arvon perusteella.
Jos bitti on nolla, lippu asetetaan nollaan.
Jos ei ole, suoritetaan seuraava käsky, jossa r5
on 0x3c(osAppNMIBuffer)
:n täysi arvo:
Tämä poimii r5
:n 28. bitin ja tallentaa sen r3
:ään.Sitten tulokseen lisätään 1, joten lopputulos on aina 1 tai 2.
zurumode_flag
Verrataan sitten edelliseen tulokseen sen mukaan, kuinka monta 28. ja viimeistä bittiä on asetettu 0x3c(osAppNMIBuffer)
:ssä: 0, 1 tai 2.
Tämä arvo kirjoitetaan zurumode_flag
:een. Jos se ei muuttanut mitään, funktio päättyy ja palauttaa lipun nykyisen arvon. Jos se muuttaa arvoa,suoritetaan paljon monimutkaisempi koodilohkojen ketju.
Tulostetaan japaninkielinen viesti: tämä on aiemmin mainittu ”zurumode_flag on muutettu %d:stä %d:ksi”-viesti.
Sitten kutsutaan sarjaa funktioita, joilla on eri argumentit riippuen siitä, oliko lippu muutettu nollaksi vai ei. Tämän osan assembly on työläs, joten sen pseudokoodi näyttää tältä:
Huomaa, että jos lippu on nolla, JC_JUTDbPrint_setVisible
-funktiolle annetaan argumenttina 0. Jos lippu ei ole nolla JA bitti 25 tai bitti 31 on asetettu 0x3C(osAppNMIBuffer)
:ssä, setVisible
-funktiolle annetaan argumenttina 1.
Tämä on ensimmäinen avain zuru-tilan aktivoimiseen: 0x3C(osAppNMIBuffer)
:n viimeinen bitti on asetettava 1:ksi, jotta debug-näytöt saadaan näkyviin ja zurumode_flag
asetetaan muuhun kuin nolla-arvoon.
zurumode_callback
zurumode_callback
on osoitteessa 8040ee74
, ja sitä kutsuu luultavasti jokin gamepadiin liittyvä funktio. Asettamalla pysäytyspisteen siihen Dolphinin debuggerissa, callstack näyttää, että sitä todellakin kutsutaan padmgr_HandleRetraceMsg
:stä.
Yksi ensimmäisistä asioista, joita se tekee, on zerucheck_key_check
. Se on monimutkainen, mutta kaiken kaikkiaan se näyttää lukevan ja sitten päivittää zuruKeyCheck
:n arvon. Päätin katsoa, miten tuota arvoa käytetään muussa takaisinkutsufunktiossa, ennen kuin menen syvemmälle näppäintarkistusfunktioon.
Seuraavaksi se tarkistaa jälleen joitakin bittejä 0x3c(osAppNMIBuffer)
:ssä. Jos bitti 26 on asetettu, tai jos bitti 25 on asetettu ja padmgr_isConnectedController(1)
palauttaa nollasta poikkeavan tuloksen, viimeinen bitti 0x3c(osAppNMIBuffer)
:ssä asetetaan arvoon 1!
Jos kumpikaan näistä biteistä ei ole asetettu, tai jos bitti 25 on vähintään asetettu, mutta padmgr_isConnectedController(1)
palauttaa arvon 0, se tarkistaa, onko tavu osoitteessa 0x4(zuruKeyCheck)
arvossa 0. Jos se on,se nollaa alkuperäisen arvon viimeisen bitin ja kirjoittaa sen takaisin 0x3c(osAppNMIBuffer)
:ään.Jos ei, niin se silti asettaa viimeisen bitin arvoksi 1.
Pseudokoodissa tämä näyttää seuraavalta:
Jos bitti 26 ei ole asetettu, se lyhentää kutsuun zurumode_update
ja lopettaa sen jälkeen.
Jos bitti 26 on asetettu, niin jos 0x4(zuruKeyCheck)
ei ole nolla, niin se lataa muotoilujonon, jossa näyttäisi siltä, että se aikoo tulostaa: ”ZURU %d/%d”.
Recap
Tässä tapahtuu:
padmgr_HandleRetraceMsg
kutsuu zurumode_callback
.Arvelen, että ”handle retrace message” tarkoittaa sitä, että se on juuri skannannut näppäinpainalluksiaohjaimeen. Joka kerta kun se skannaa, se saattaa kutsua sarjan eri callbackeja.
Kun zurumode_callback
suoritetaan, se tarkistaa nykyiset näppäimen (painikkeen) painallukset.Tämä näyttää tarkistavan tietyn näppäimen tai näppäinyhdistelmän.
NMI-puskurin viimeinen bitti päivitetään sen currentvaluen tiettyjen bittien sekä yhden zuruKeyCheck
tavun (0x4(zuruKeyCheck)
) arvon perusteella.
Silloin zurumode_update
suoritetaan ja tarkistetaan kyseinen bitti. Jos se on 0, zuru-tilan lippu asettuu arvoon 0. Jos se on 1, tilan lippu päivitetään arvoon 1 tai 2 sen mukaan, onko bitti 28 asetettu.
Kolme tapaa aktivoida zuru-tila ovat:
- Bitti 26 on asetettu kohdassa
0x3C(osAppNMIBuffer)
- Bitti 25 on asetettu kohdassa
0x3C(osAppNMIBuffer)
, ja porttiin 2 on kytketty ohjain -
0x4(zuruKeyCheck)
ei ole nolla
osAppNMIBuffer
Mitähän osAppNMIBuffer
oli, Aloitin etsimällä sanaa ”NMI”, ja löysin viittauksia ”non-maskable interrupt” Nintendon yhteydessä. Kävi ilmi, että koko muuttujanimi esiintyy myös Nintendo 64:n kehittäjädokumentaatiossa:
osAppNMIBuffer on 64 tavun puskuri, joka tyhjennetään kylmässä nollauksessa. Jos järjestelmä käynnistyy uudelleen NMI:n takia, tämä puskuri ei muutu.
Periaatteessa tämä on pieni pala muistia, joka säilyy pehmeiden uudelleenkäynnistysten aikana. Peli voi käyttää tätä puskuria tallentaakseen mitä tahansa, kunhan konsoli on päällä.Alkuperäinen Animal Crossing -peli julkaistiin itse asiassa Nintendo 64:llä, joten on järkevää, että jotain tällaista näkyisi koodissa.
Vaihtaessamme boot.dol
-binääriin (kaikki yllä oleva on foresta.rel
:stä),main
-funktiossa on paljon viittauksia osAppNMIBuffer
:ään. Nopea tarkastelu osoittaa, että on olemassa sarja tarkistuksia, jotka voivat johtaa siihen, että 0x3c(osAppNMIBuffer)
:n eri bitit asettuvat OR-operaatioilla.
Interenkiintoisia OR-operaattorin arvoja, joita kannattaa varoa, olisivat:
- Bit 31: 0x01
- Bit 30: 0x02
- Bit 29: 0x04
- Bit 28: 0x08
- Bit 27: Muista, että bitit 25, 26 ja 28 ovat erityisen mielenkiintoisia: 25 ja 26 määrittävät, onko zuru-tila käytössä, ja bitti 28 määrittää lipun tason (1 tai 2).Bitti 31 on myös mielenkiintoinen, mutta näyttää lähinnä päivittyvän muiden arvojen perusteella.
Bitti 26
Ensiksi: kohdassa
800062e0
onori r0, r0, 0x20
-käsky puskurin arvosta0x3c
. Tämä asettaisi bitin 26, joka johtaa aina siihen, että zuru-tila on käytössä.Jotta bitti asettuisi, on
DVDGetCurrentDiskID
:n palauttaman kahdeksannen tavun oltava0x99
.Tämä tunnus sijaitsee aivan pelilevyn kuvan alussa, ja se ladataan muistiin osoitteessa80000000
. Pelin tavallisessa vähittäismyyntiversiossa ID näyttää tältä:47 41 46 45 30 31 00 00 GAFE01..
Tunnuksen viimeisen tavun muuttaminen
0x99
:ksi saa aikaan sen, että peliä käynnistettäessä tapahtuu seuraavaa:Ohjelmiston käyttöjärjestelmän konsoliin tulostuu seuraava:
06:43:404 HW\EXI_DeviceIPL.cpp:339 N: ZURUMODE2 ENABLE08:00:288 HW\EXI_DeviceIPL.cpp:339 N: osAppNMIBuffer=0x00000078
Kaikki muut patchit voidaan poistaa, ja myös D-kirjain ilmestyy jälleen ruudun oikeaan yläkulmaan, mutta mikään muu debug-näyttö ei aktivoidu.
Bit 25
Bit 25:tä käytetään portin 2 ohjaimen tarkistuksen suorittamisen yhteydessä. Mikä saa sen aktivoitumaan?
Tässä osoittautuu olevan sama tarkistus kuin bitissä 28: version on oltava suurempi tai yhtä suuri kuin
0x90
. Jos bitti 26 on asetettu (ID on0x99
), molemmat bitit asetetaan myös, ja zuru-tila otetaan joka tapauksessa käyttöön.Jos versio on välillä
0x90
ja0x98
, zuru-tila ei kuitenkaan oteta heti käyttöön.Muistutetaan kohdassazurumode_callback
tehdystä tarkistuksesta, että se otetaan käyttöön vain, jos bitti 25 asetetaan japadmgr_isConnectedController(1)
antaa tulokseksi arvon, joka on muu kuin nolla.Kun ohjain kytketään porttiin 2 (argumentti osoitteeseenisConnectedController
on nolla-indeksoitua),zuru-tila otetaan käyttöön. D-kirjain ja build-info näkyvät otsikkoruudulla, ja…toisen ohjaimen painikkeiden painaminen ohjaa debug-näyttöjä!Jotkut painikkeet tekevät myös muita asioita kuin muuttavat näyttöä, kuten lisäävät pelin nopeutta.
zerucheck_key_check
Viimeinen arvoitus on
0x4(zuruKeyCheck)
. Kävi ilmi, että tätä arvoa päivittää aiemmin esitetty jättimäinen monimutkainen funktio:Käyttämällä Dolphinin debuggeria sain selville, että tämän funktion tarkistama arvo on joukko bittejä, jotka vastaavat toisen ohjaimen napinpainalluksia. Painikkeen painalluksen jälki on tallennettu 16-bittiseen arvoon osoitteessa
0x2(zuruKeyCheck)
. Kun ohjainta ei ole kytketty, arvo on0x7638
.Kaksi tavua, jotka sisältävät ohjaimen 2 napinpainallusten liput, ladataan ja päivitetään lähellä
zerucheck_key_check
:n alkua. Uusi arvo välitetään rekisterinr4
mukanapadmgr_HandleRetraceMsg
:n toimesta, kun se kutsuu takaisinkutsufunktiota.Alhaalla lähellä
zerucheck_key_check
:n loppua on itse asiassa toinenkin paikka, jossa0x4(zuruKeyCheck)
päivitetään. Se ei näkynyt ristiviittausluettelossa, koska se käyttäär3
:tä perusosoitteena, ja voimme selvittää, mikär3
on, vain katsomalla, mihin se asetetaan aina, kun tätä funktiota ollaan kutsumassa.Kohdassa
8040ed88
r4
:n arvo kirjoitetaan0x4(zuruKeyCheck)
:ään. Se ladataan samasta paikasta ja sitten XORdataan 1:n kanssa juuri ennen sitä. Tämän pitäisi vaihtaa tavun (oikeastaan vain viimeisen bitin) arvoa 0:n ja 1:n välillä (jos se on 0, sen ja 1:n välisen XOR:n tulos on 1). Jos se on 1, tulos on 0. Katso XOR:n totuustaulukkoa nähdäksesi tämän.)En huomannut tätä käyttäytymistä tarkkaillessani muistin arvoja aiemmin, mutta yritän murtaa tämän käskyn debuggerissa nähdäkseni, mitä tapahtuu. Alkuperäinen arvo ladataan osoitteeseen
8040ed7c
.Koskettamatta mitään ohjainten painikkeita en osu tähän katkaisupisteeseen otsikkoruudun aikana. Jotta pääsen tähän koodilohkoon,
r5
:n arvon on oltava0xb
ennen sitä edeltävää haarautumiskäskyä (8040ed74
). Monista eri poluista, jotka johtavat tuohon lohkoon, on yksi, joka asettaar5
:n arvoksi0xb
ennen sitä, kohdassa8040ed68
.Huomaa, että päästäkseen lohkoon, joka asettaa
r5
:n arvoksi0xB
,r0
:n on täytynytt olla yhtä suuri kuin0x1000
juuri ennen sitä. Kun seuraamme lohkoja ketjussa ylöspäin funktion alkuun, näemme tämän lohkon saavuttamiseen tarvittavat rajoitukset:- 8040ed74:
r5
on oltava0xB
- 8040ed60:
r0
on oltava0x1000
- 8040ebe8:
r5
on oltava0xA
- 8040ebe4:
r5
on oltava pienempi kuin0x5B
- 8040eba4:
r5
on oltava suurempi kuin0x7
- 8040eb94:
r6
on oltava 1 - 8040eb5c:
r0
ei saa olla 0 - 8040eb74: Port 2 button values must have changed
Here we reach the point where the old button values are loaded and the new valuesare stored. Tämän jälkeen uusiin ja vanhoihin arvoihin sovelletaan pari operaatiota:
old_vals = old_vals XOR new_valsold_vals = old_vals AND new_vals
XOR-operaatio merkitsee kaikki bitit, jotka ovat muuttuneet kahden arvon välillä. AND-operaatio peittää sitten uuden tulon peruuttaakseen kaikki bitit, jotka eivät ole tällä hetkellä asetettu. Tulos
r0
on uusien bittien (painikkeiden painallusten) joukko uudessa arvossa. Jos se ei ole tyhjä, olemme oikealla tiellä.Jotta
r0
olisi0x1000
, neljännen bitin 16:sta painikejäljitysbitistä on täytynyt juuri muuttua.Asettamalla pysäytyspisteen XOR/AND-operaation jälkeen voin selvittää, mikä painikkeen painallus aiheuttaa tämän: se on START-painike.Seuraavaksi kysyn, miten saada
r5
alkamaan0xA
:nä.r5
jar6
ladataan0x0(zuruKeyCheck)
:stä näppäintarkistustoiminnon alussa ja päivitetään lähellä loppua, kun emme anna koodilohkoa, joka vaihtaa0x4(zuruKeyCheck)
.r0
on oltava0x4000
(B-painiketta painettiin) - 8040ed74:
-
8040ebe0
:r5
on oltava0x5b
-
8040eba4
:r5
täytyy olla suurempi kuin0x7
- sama kuin ennen tästä eteenpäin…
r5
täytyy alkaa 0x5b
8040ed00
-
8040ecfc
:r0
on oltava0xC000
(A ja B painettuna) -
8040ebf8
:r5
on oltava >= 9 -
8040ebf0
:r5
on oltava pienempi kuin 10 -
8040ebe4
:r5
on oltava pienempi kuin0x5b
-
8040eba4
:r5
täytyy olla suurempi kuin0x7
- sama kuin ennenkin tästä eteenpäin…
r5
täytyy alkaa 9
8040ed50
-
8040ed4c
:r0
on oltava0x8000
(A painettiin) -
8040ec04
:r5
on oltava pienempi kuin0x5d
-
8040ebe4
:r5
on oltava suurempi kuin0x5b
-
8040eba4
:r5
must be greater than0x7
- sama kuin ennenkin tästä eteenpäin…
r5
must start at 0x5c
Näyttää siltä, että nappien painallusten välissä on jonkinlainen tila, jonka jälkeen on syötettävä tietty nappikombojen sarja, joka päättyy STARTtiin. Vaikuttaa siltä, että A ja/tai B tulevat juuri ennen STARTia.
Seuraamalla koodipolkua, joka asettaa r5
:n arvoksi 9, syntyy kuvio: r5
on inkrementoiva arvo, jota voidaan joko kasvattaa, kun oikea painikkeen painallusarvo löytyy r0
:sta,tai nollata arvoon 0. Oudommat tapaukset, joissa se ei ole arvo 0x0
:n ja 0xB
:n välillä, esiintyvät, kun käsitellään usean painikkeen vaiheita, kuten A:n ja B:n painamista samaan aikaan. Henkilö, joka yrittää syöttää tätä yhdistelmää, ei yleensä paina molempia painikkeita täsmälleen samaan aikaan, kun pad-jälki tapahtuu, joten sen on käsiteltävä jommankumman painikkeen painaminen ennen toista.
Jatketaan eri koodipolkujen käsittelyä:
-
r5
asetetaan arvoon 9, kun oikealle painetaan8040ece8
. -
r5
asetetaan arvoon 8, kun C-tikkua oikealle painetaan8040eccc
. -
r5
asetetaan arvoon 7, kun C-tikkua vasemmalle painetaan8040ecb0
. -
r5
asetetaan arvoon 6, kun VASEMMALLE painetaan8040ec98
. -
r5
asetetaan arvoon 5 (ja r6 arvoon 1), kun ALAS painetaan8040ec7c
. -
r5
asetetaan arvoon 4, kun C-keppi ylös painetaan8040ec64
. -
r5
asetetaan arvoon 3, kun C-keppi alas painetaan8040ec48
. -
r5
asetetaan arvoon 2, kun YLÖS painetaan8040ec30
. -
r5
asetetaan arvoon 1 (ja rr:n arvoonr6
arvoon 1), kun Z:n arvoa painetaan8040ec1c
.
Tämänhetkinen sekvenssi on:
Z, UP, C-DOWN, C-UP, DOWN, LEFT, C-LEFT, C-RIGHT, RIGHT, A+B, START
Ensimmäinenkin ehto tarkistetaan ennen Z-tarkistusta: samalla kun äsken painetun nappulan on oltava Z, tämänhetkisten lippujen on oltava 0x2030
: vasemmanpuoleisen ja oikeanpuoleisen törmäyspuskurinkin on oltavajatkuvasti alaspainettuna (niiden arvot ovat 0x10
ja 0x20
). Lisäksi UP/DOWN/LEFT/RIGHT ovatD-pad-painikkeita, eivät analogitikkuja.
Huijauskoodi
Koko combo on:
- Pidä L+R puskureita ja paina Z
- D-UP
- C-DOWN
- C-UP
- D-DOWN
- D-LEFT
- C-LEFT
- C-RIGHT
- D-RIGHT
- A+B
- START
Se toimii! Liitä ohjain toiseen porttiin ja syötä koodi, ja debug-näytöt tulevat näkyviin. Sen jälkeen voit alkaa painaa toisen (tai jopa kolmannen) ohjaimen nappeja aloittaaksesi toiminnan.
Tämä combo toimii ilman pelin versionumeron paikkaamista.voit käyttää tätä jopa pelin tavallisella vähittäismyyntikopiolla ilman huijaustyökaluja tai konsolimodia. Combon syöttäminen toisen kerran kytkee zuru-tilan takaisin pois päältä.
Kohdassa zurumode_callback
olevaa ”ZURU %d/%d”-viestiä käytetään tulostamaan tämän combon tila, jos syötät sen,kun levyn ID on jo 0x99
(oletettavasti itse huijauskoodin debuggausta varten). Ensimmäinen numero on nykyinen sijaintisi sarjassa, joka vastaa r5
. Toinen asetetaan 1:ksi, kun tiettyjä painikkeita sekvenssissä pidetään alhaalla, nämä saattavat vastata sitä, kun r6
on asetettu 1:ksi.
Useimmat näytöt eivät selitä, mitä ne ovat ruudulla, joten selvittääksesi, mitä ne tekevät, sinun on löydettävä funktiot, jotka käsittelevät niitä. Esimerkiksi näytön yläreunassa näkyvä pitkä rivi sinisiä ja punasävyisiä rasterikylttejä ovat paikanvartijoita, joilla näytetään eri tehtävien tila. Kun tehtävä on aktiivinen, sinne ilmestyy numeroita, jotka kertovat tehtävän tilan.
Musta ruutu, joka ilmestyy, kun painat Z-näppäintä, on konsoli, joka tulostaa virheenkorjausviestejä, mutta nimenomaan matalan tason asioita, kuten muistin allokointi- ja kasaamisvirheitä tai muita huonoja poikkeuksia varten. fault_callback_scroll
:n käyttäytyminen viittaa siihen, että se saattaa olla näiden virheiden näyttämistä ennen järjestelmän uudelleenkäynnistystä. En laukaissut mitään näistä virheistä, mutta sain sen tulostamaan pari roskamerkkiä joidenkin NOP-merkkien avulla. Uskon, että tämä olisi todella hyödyllinen tulostettaessa mukautettuja virheenkorjausviestejä myöhemmin:
Tämän kaiken jälkeen sain selville, että virheenkorjaustilan saaminen versiotunnuksen paikkaamisella 0x99
:ksi on jo tiedossa: https://tcrf.net/Animal_Crossing#Debug_Mode. (Heillä on myös hyviä muistiinpanoja siitä, mitä eri näytöt ovat, ja lisää asioita, joita voi tehdä portin 3 ohjaimella.) Tietääkseni huijausyhdistelmää ei ole kuitenkaan vielä julkaistu.
Tässä postauksessa riittää. On vielä joitain muita kehittäjäominaisuuksia, joita haluaisin tutkia,kuten debug-karttaruutu ja NES-emulaattorin valintaruutu, ja miten ne voi aktivoida ilman patchien käyttöä.
Postaan myös kirjoituksia dialogi-, tapahtuma- ja quest-järjestelmien käänteisestä muuttamisesta modien tekemistä varten.
Päivitys: Tästä pitämäni esitelmän dioja löydät täältä.