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.

Sisääntulofunktion purkaminen

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:

$ kstool ppc32be "li 0, 1"li 0, 1 = 

Voit soveltaa tätä korjausta Dolphin-emulaattorissa menemällä pelin sproperties-välilehdelle ”Patches” ja syöttämällä sen näin:

Debug performance meter

Tämän arvon asettaminen arvoon 1 sai aikaan sen, että ruudun alareunaan ilmestyi mielenkiintoisen näköinen kuvaaja:

Debug performance meter

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.

game_move_first-funktio

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-funktioon padmgr-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”.

zuru mode format string

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?

zerumode_check_keycheck

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?

zurumode_flag check

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”:

Dolphinin debuggerin

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.

Debug-tilan 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:

Haaroja funktiossa zzz_DebugDrawPrint

NOP-poistamalla useampia näistä haarautumislausekkeista aloin nähdä, että näytölle tulostui erilaisia asioita:

More debug stuff getting printed

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.

viestin numeron näyttö

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:

Viesti 687 merkkijonotaulukkoeditorissa

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 osoitteeseen zurumode_callback
  • 0x10(padmgr_class) asetetaan osoitteeseen padmgr_class itse
  • 0x4(zuruKeyCheck) asetetaan osoitteeseen 0x3C(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:

Title screen with zuru mode

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:

extrwi r3, r5, 1, 28

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ä:

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)}

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:

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

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:

  1. Bitti 26 on asetettu kohdassa 0x3C(osAppNMIBuffer)
  2. Bitti 25 on asetettu kohdassa 0x3C(osAppNMIBuffer), ja porttiin 2 on kytketty ohjain
  3. 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 on ori r0, r0, 0x20-käsky puskurin arvosta 0x3c. Tämä asettaisi bitin 26, joka johtaa aina siihen, että zuru-tila on käytössä.

    Bitin 26 asettaminen

    Jotta bitti asettuisi, on DVDGetCurrentDiskID:n palauttaman kahdeksannen tavun oltava 0x99.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:

    Pelin versiotunniste 0x99

    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?

    Bit 25 ja 28

    Tässä osoittautuu olevan sama tarkistus kuin bitissä 28: version on oltava suurempi tai yhtä suuri kuin 0x90. Jos bitti 26 on asetettu (ID on 0x99), molemmat bitit asetetaan myös, ja zuru-tila otetaan joka tapauksessa käyttöön.

    Jos versio on välillä 0x90 ja 0x98, zuru-tila ei kuitenkaan oteta heti käyttöön.Muistutetaan kohdassa zurumode_callback tehdystä tarkistuksesta, että se otetaan käyttöön vain, jos bitti 25 asetetaan ja padmgr_isConnectedController(1) antaa tulokseksi arvon, joka on muu kuin nolla.Kun ohjain kytketään porttiin 2 (argumentti osoitteeseen isConnectedController 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:

    zerumode_check_keycheck

    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 on 0x7638.

    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 rekisterin r4 mukana padmgr_HandleRetraceMsg:n toimesta, kun se kutsuu takaisinkutsufunktiota.

    näppäintarkistus päättyy

    Alhaalla lähellä zerucheck_key_check:n loppua on itse asiassa toinenkin paikka, jossa 0x4(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 kirjoitetaan 0x4(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.)

    key check end

    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 oltava 0xb ennen sitä edeltävää haarautumiskäskyä (8040ed74). Monista eri poluista, jotka johtavat tuohon lohkoon, on yksi, joka asettaa r5:n arvoksi 0xb ennen sitä, kohdassa 8040ed68.

    Setting r5 to 0xb

    Huomaa, että päästäkseen lohkoon, joka asettaa r5:n arvoksi 0xB, r0:n on täytynytt olla yhtä suuri kuin 0x1000 juuri ennen sitä. Kun seuraamme lohkoja ketjussa ylöspäin funktion alkuun, näemme tämän lohkon saavuttamiseen tarvittavat rajoitukset:

    • 8040ed74: r5 on oltava 0xB
    • 8040ed60: r0 on oltava 0x1000
    • 8040ebe8: r5 on oltava 0xA
    • 8040ebe4: r5 on oltava pienempi kuin 0x5B
    • 8040eba4: r5 on oltava suurempi kuin 0x7
    • 8040eb94: r6 on oltava 1
    • 8040eb5c: r0 ei saa olla 0
    • 8040eb74: Port 2 button values must have changed

    Tracing the code path

    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 olisi 0x1000, 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 alkamaan 0xA:nä. r5 ja r6 ladataan 0x0(zuruKeyCheck):stä näppäintarkistustoiminnon alussa ja päivitetään lähellä loppua, kun emme anna koodilohkoa, joka vaihtaa 0x4(zuruKeyCheck). r0 on oltava 0x4000 (B-painiketta painettiin)

  • 8040ebe0: r5 on oltava 0x5b
  • 8040eba4: r5 täytyy olla suurempi kuin 0x7
  • sama kuin ennen tästä eteenpäin…

r5 täytyy alkaa 0x5b

8040ed00
  • 8040ecfc: r0 on oltava 0xC000 (A ja B painettuna)
  • 8040ebf8: r5 on oltava >= 9
  • 8040ebf0: r5 on oltava pienempi kuin 10
  • 8040ebe4: r5 on oltava pienempi kuin 0x5b
  • 8040eba4: r5 täytyy olla suurempi kuin 0x7
  • sama kuin ennenkin tästä eteenpäin…

r5 täytyy alkaa 9

8040ed50
  • 8040ed4c: r0 on oltava 0x8000 (A painettiin)
  • 8040ec04: r5 on oltava pienempi kuin 0x5d
  • 8040ebe4: r5 on oltava suurempi kuin 0x5b
  • 8040eba4: r5 must be greater than 0x7
  • 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 painetaan 8040ece8.
  • r5 asetetaan arvoon 8, kun C-tikkua oikealle painetaan 8040eccc.
  • r5 asetetaan arvoon 7, kun C-tikkua vasemmalle painetaan 8040ecb0.
  • r5 asetetaan arvoon 6, kun VASEMMALLE painetaan 8040ec98.
  • r5 asetetaan arvoon 5 (ja r6 arvoon 1), kun ALAS painetaan 8040ec7c.
  • r5 asetetaan arvoon 4, kun C-keppi ylös painetaan 8040ec64.
  • r5 asetetaan arvoon 3, kun C-keppi alas painetaan 8040ec48.
  • r5 asetetaan arvoon 2, kun YLÖS painetaan 8040ec30.
  • r5 asetetaan arvoon 1 (ja rr:n arvoon r6 arvoon 1), kun Z:n arvoa painetaan 8040ec1c.

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:

  1. Pidä L+R puskureita ja paina Z
  2. D-UP
  3. C-DOWN
  4. C-UP
  5. D-DOWN
  6. D-LEFT
  7. C-LEFT
  8. C-RIGHT
  9. D-RIGHT
  10. A+B
  11. 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ä.

Koodin käyttäminen oikealla GameCubella

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:

JUTConsole garbage characters

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öä.

Kartan valintaruutu

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ä.