Speedran Legend of Zelda dengan memanipulasi memori game
Bagian pertama dari Legend of Zelda adalah klasik abadi. Seorang pemain biasa membutuhkan beberapa hari untuk menyelesaikannya, tetapi untuk speedrunners yang paling berpengalaman ini adalah pertanyaan setengah jam. Namun, bug yang sangat membingungkan dan kompleks yang dibuka oleh Sockfolder memungkinkan pengguna untuk mengeksekusi kode arbitrer langsung dari gim untuk menyelesaikan gim dalam waktu kurang dari tiga menit.Secara singkat, ini terjadi sebagai berikut:- Masukkan kode pada layar entri nama.
- Kami memasuki ruang bawah tanah kedua, mengambil peluit.
- Kami melewati pemakaman, kami memanggil sepuluh hantu.
- Kami menunggu kondisi yang diperlukan, menghentikan permainan saat makhluk berada di tempat tertentu.
- Beristirahatlah, tekan A dan B pada saat bersamaan, dan hanya itu!
Ya, ini luar biasa. Sekarang mari kita melihat lebih dekat apa yang terjadi dalam game dan bagaimana bug Legend of Zelda yang menakjubkan ini dieksekusi.
Cara menyebabkan kesalahan
Jadi, pertama kita masukkan kode pada layar entri nama. Nama file disimpan dalam memori, dan setiap karakter sesuai dengan byte tertentu dalam memori. Kami memberikan nama-nama aneh ke file, karena sebenarnya kami menulis byte yang diperlukan ke dalam memori, yang kemudian akan menjadi kode assembler. Hanya ada dua batasan:- kita dapat bekerja dengan hanya tiga file, yaitu, program hanya dapat berisi 3 * 8 karakter = 24 byte;
- kita harus memasukkan lima karakter pertama dari salah satu file ZELDA jika kita ingin memulai dengan pencarian kedua.
Kita harus mulai dengan pencarian kedua karena suatu alasan yang akan saya jelaskan nanti. Kami memanggil file ZELDA pertama karena nama ini paling cocok. Sekarang mari kita mulai permainan. Sebelum kita sampai di kuburan, perilaku kita praktis tidak berbeda dengan permainan biasa. Hal utama yang perlu kita lakukan adalah berusaha untuk tidak mati dua kali. Yang dimaksud dengan "mati" adalah kematian itu sendiri dan transisi yang sering ke menu. Anda akan mengerti mengapa demikian, dari penjelasan lebih lanjut.
Setelah kami mendengar peluit, kami pergi ke layar pemakaman ini untuk menyelesaikan bug. Kesalahan dipicu di sini, karena ketika menggunakan peluit, sebuah objek dibuat dari batu nisan, yang membuka tangga tersembunyi. Ini diperlukan untuk terjadinya bug. Dan semua ini disebabkan oleh keterbatasan sprite. Di Legend of Zelda, layar dapat memiliki hingga 11 sprite sekaligus. Jika Anda mencoba membuat sprite ke-12, gim ini tidak akan memungkinkan Anda untuk melakukan ini. Untuk mereproduksi kesalahan, kami mematahkan batas overflow dan membuat sprite ke-12.
Saat Anda membuat objek peluit, game lupa memeriksa berapa banyak sprite yang ada di layar sebelumnya. Oleh karena itu, ketika kami membuat jumlah sprite maksimum, sprite dibuat di luar tabel sprite, memori ditimpa, dan terjadi kondisi yang tidak terduga.Ada fragmen kecil dalam memori mulai dari offset 350 yang menyimpan pengidentifikasi sebelas sprite. Sprite dibuat ketika layar dimuat, mulai dari posisi dengan offset terkecil dari sprite. Ini berarti bahwa sprite mencari posisi dengan offset minimum, mulai dari offset 350 dan seterusnya. Saat sprite akan dibuat, gim mencari nilai kosong di tabel sprite untuk menggantinya dengan sprite identifier, sehingga membuatnya. Tidak seperti membuat sprite saat memuat layar, saat mencoba membuat sprite, permainan mencari posisi dengan offset maksimum untuk sprite. Ini berarti bahwa pertama dia memeriksa apakah mungkin untuk membuat sprite pada posisi 10 (0A). Jika tidak, ia memeriksa posisi 9 (09), mis. mengimbangi 359, dan seterusnya. Jika semua posisi sprite ditempati, maka game "menyerah" dan tidak membuat sprite.
Namun, pengembang lupa untuk melakukan pemeriksaan perbatasan sehingga permainan bisa "menyerah" ketika mencoba membuat objek "peluit", dan game terus mencari byte dengan nilai 00 di tempat di luar tabel sprite untuk menuliskan pengenal objek di sana. Tapi di mana dia mencari arti ini dulu? Bahkan, tabel ini adalah bagian dari array yang lebih besar yang menyimpan informasi tentang sprite. Ketika peluit menemukan tempat untuk membuat di awal tabel, ia mulai mencari posisi dengan nilai 00 dari akhir array.Pada layar kuburan, fragmen array ini berisi informasi tentang keadaan aksi hantu saat ini. Sekarang mari kita bicara tentang keadaan aksi hantu. Hantu-hantu yang dibuat di kuburan bergerak secara acak. Ini karena tindakan mereka dikendalikan oleh perilaku yang ditentukan oleh keadaan tindakan mereka. Status tindakan ini dapat memiliki nilai mulai dari 0 (00) hingga 5 (05), tergantung pada tindakan hantu. Mereka ditulis di akhir array yang berisi informasi tentang sprite.Secara umum, semua status tindakan ini sesuai dengan posisi di tabel fungsi yang menentukan tindakan hantu. Karena negara hanya dapat memiliki 6 nilai, tabel memiliki ukuran yang diperlukan untuk menyimpan semua enam tindakan. Di sini, keadaan aksi 0 (00) sesuai dengan percepatan hantu. Ini penting karena ketika peluit mulai mencari tempat untuk membuat sprite-nya, ia akan mengisi posisi pertama yang ditemukan dalam memori dengan nilai 00. Akselerasi hantu berhubungan dengan aksi 0, direkam dalam kode dengan nilai 00, sehingga permainan menganggapnya sebagai posisi kosong dan menulis pengenal untuk itu. peluit 5E.
Kemudian gim mencoba mengeksekusi kode pada posisi 5E dari tabel, tetapi karena tabel hanya berisi nilai hingga 05, gim mengeksekusi data "sampah" sebagai kode yang jauh melampaui tabel. Jika Anda melakukan segalanya dengan benar, data "sampah" akan membawa kita ke kode yang kita rekam dengan simbol file, permainan akan menuju ke Zelda, dan kita akan melewati permainan.Namun, agar bug berfungsi, kita perlu merusak status aksi ketiga dalam tabel. Jadi hantu ketiga yang dibuat harus dipercepat, dan semua hantu berikutnya harus melakukan tindakan lain (yang kodenya tidak sama dengan 00). Ini informasi penting. Sekarang mari kita lihat apa yang terjadi ketika kita menjalankan bug.Eksekusi bug
Jika kami melakukan segalanya dengan benar, permainan akan mencoba mengeksekusi data di posisi 5E dari tabel. Itu mulai dibaca sebagai data kode mulai dari offset 602. Informasi yang terkait dengan keadaan tindakan Tautan disimpan dalam memori pada offset 602 dan 603, sehingga kita dapat mengendalikannya. Ketika Link berdiri, nilai pada offset 602 dan 603 akan menjadi 00. Tetapi ketika kita menekan tombol B untuk menggunakan peluit, nilai pada 602 akan menjadi 10, dan ketika kita menekan A untuk menggunakan pedang, nilai pada 603 akan menjadi 01.
Oleh karena itu, ketika kami menekan kedua tombol ini secara bersamaan, data tetangga adalah 10 01. Permainan menafsirkan data ini sebagai perintah cabang BPL untuk mengimbangi 605 dan mengeksekusi data di dalamnya sebagai kode. Nilai 605 dan 606 akan menjadi 00 jika kesehatan kita tidak terlalu rendah, dan 40 jika kesehatan kita rendah. Untuk lulus permainan dengan cepat, nilai-nilai ini harus sama dengan 00, jadi Anda harus mencoba menjaga kesehatan sampai bug selesai. Nilai 00 sesuai dengan instruksi (istirahat) BRK. Karena tidak ada artinya di sini, kita perlu nilainya menjadi 00 dan permainan untuk melanjutkan mengeksekusi kode.
Karena kami menggunakan pedang, permainan akan terus menjalankan instruksi di byte aneh berikutnya. Instruksi berikutnya, yang bukan BRK, berada di offset 08, tetapi karena kami menggunakan pedang, permainan melompati dan mengeksekusi kode pada 09 dan 0A. Nilai dalam byte 09 selalu 10, yang berarti bahwa kita kembali berurusan dengan instruksi cabang BPL. Bytes pada offset 623 (62E) berhubungan dengan musik. Dia mencoba meningkatkan dalam proses memainkan musik, tetapi kadang-kadang melompat ke nilai yang lebih rendah.Ini berarti bahwa untuk menyelesaikan bug kita perlu menerapkannya pada musik tertentu. Jika nilainya kecil, kami memiliki peluang untuk melompat ke memori ke area dengan nilai yang berubah secara signifikan. Mereka berubah secara acak sehingga kami tidak bisa mengendalikan mereka secara real time untuk menerima instruksi yang harus diikuti oleh permainan. Oleh karena itu, transisi di sini cenderung mengarah pada permainan.
Namun, setelah data kacau ini, ada area data yang aman. Karena itu, kami akan mencoba melompat ke sana. Kami membutuhkan lebih banyak data dalam 60A untuk secara akurat melewati area yang tidak aman dan mendapatkan data permanen dan aman. Jika kita pergi ke sana, maka semuanya beres. Tetapi pada offset 630, ada penghitung kematian Link. Jika Tautan mati dua kali, nilai ini adalah 02, yang membentuk instruksi dan menghentikan permainan. Inilah sebabnya mengapa penting agar Anda tidak mati dua kali. Jadi, sebagai hasilnya, kita beralih ke offset 638, yang merupakan awal dari tabel yang menyimpan nama file. Jika kita sampai di sini, game akan mengeksekusi kode yang kita masukkan pada layar file. Dan di sini dimulai ...Kode
Nama file disimpan dalam memori secara berurutan. Ini berarti bahwa byte pertama yang kita jalankan adalah file bernama ZELDA. Untungnya, kode yang dieksekusi oleh byte ini aman, sehingga kita bisa beralih ke yang lain. Karena nama ZELDA tidak penting, mari kita lihat apa yang dilakukan oleh kode lainnya. Pertama, kita menjalankan tiga perintah PLP (pull from stack, pull from stack). Ketika kita memanggil suatu fungsi, ia menulis dua nilai ke stack. Nilai-nilai ini sesuai dengan di mana Anda berada dalam kode ketika Anda menjalankan fungsi. Sehingga game bisa mengetahui kemana harus kembali ke sana setelah melakukan fungsinya.Kami mulai dengan mengekstraksi tiga nilai dari tumpukan, dan kemudian kami melakukan fungsi kembali. Kami melakukan ini untuk kembali ke tempat kami berada sebelum eksekusi bug. Jika tidak, game tidak akan selesai mengeksekusi data sebagai kode dan sebagai hasilnya akan membeku. Tetapi sebelum kita sampai pada kode yang merusak memori dan mengakhiri permainan, kita perlu berbicara tentang beberapa nilai lagi.Nilai pada memory offset 10 sesuai dengan jumlah dunia tempat kita berada. Jika kita berada di dunia di atas tanah, nilainya adalah 00. Untuk penjara bawah tanah pertama, nilainya 01, untuk yang kedua - 02, dan seterusnya. Karena Zelda ada di ruang bawah tanah kesembilan, kita perlu nilai ini menjadi 09. Namun, kita melakukan bug di dunia di atas tanah dan nilainya adalah 00. Jadi kita perlu menemukan cara untuk menyamakannya dengan 09. Nilai pada offset 11 sesuai dengan beberapa data tentang kondisi permainan. Saat gim dimuat, nilainya 00, saat gim tidak memuat - 01. Kita perlu memuat dungeon kesembilan, jadi nilai ini harus 00.Nilai pada offset 12 sesuai dengan beberapa bagian negara. Saat layar dimuat, nilainya biasanya 02, ketika tidak dimuat, biasanya 05. Karena kita ingin memuat dungeon, nilai ini harus 02.Nilai terakhir yang ingin kita ubah sesuai dengan pencarian yang kita jalani. Nilai pada offset 62D sesuai dengan pencarian pertama atau kedua, dan memiliki nilai 00 atau 01. Kami mulai dari pencarian kedua, tetapi ingin mengakhiri dalam pencarian pertama, karena ini adalah bagaimana kami membingungkan permainan. Karena itu, kita perlu mengubah nilai ini menjadi 00. Ini akan menempatkan kita dalam hibrida aneh dari pencarian pertama dan kedua. Kita memerlukan keadaan hybrid ini untuk mengeksekusi kode untuk tujuan tertentu. Kami akan melihatnya ketika kami menjelaskan apa yang dilakukan kode.
Kita menetapkan tujuan, mari kita lanjutkan ke kode. Pertama, kita memiliki fungsi LSR (pergeseran logis ke kanan) untuk offset 11. Fungsi ini membagi nilai menjadi dua dan menulis sisanya untuk dibawa. 11 adalah tempat di memori yang menyimpan nilai yang sesuai dengan keadaan permainan, dan sebelum instruksi ini nilainya adalah 01. Ketika kita membaginya dengan dua, kita mendapatkan 00 dengan sisanya 01, karena pembagiannya adalah integer. Pada offset 11, nilai 00 ditulis, dan dalam transfer, 01.Kemudian kita memiliki fungsi ROX untuk mengimbangi 0D (putar kiri, kiri) dengan indeks X. Karena kita menimpa status aksi dari hantu ketiga yang kita buat, X sama dengan tiga. 0D + X adalah 0D + 03, yang merupakan heksadesimal 10. Oleh karena itu, fungsi ini berputar ke kiri pada offset 10. Fungsi ini mengalikan nilai pada offset 10 dengan 2 dan menambah hyphenation. Nilai dimulai dari 0. Nol kali 2 sama dengan 0 dan 0 + 1 sama dengan 1, jadi nilainya ditulis untuk mengimbangi 10.Sekarang operasi ROX diulangi lagi untuk mengimbangi 0D dengan indeks X. Nilai X belum berubah, masih tiga, oleh karena itu, ini adalah belokan ke kiri pada offset 10. 1 * 2 = 2, dan karena kita telah menggunakan transfer, nilainya 0. 2 + 0 = 2, sehingga nilai 2 ditulis untuk mengimbangi 2.Selanjutnya, kami memiliki operasi ROX ketiga untuk offset 0D. 2 * 2 = 4, dan 4 + 0 = 4, sehingga nilai 4 ditulis pada offset 10.Instruksi berikutnya adalah LSR pada offset 12. Kita mulai dengan nilai 5, jadi membaginya dengan dua memberi kita sisanya 1. 2 ditulis pada offset 12 , dan 1 - dalam transfer.Kemudian putaran ROX terakhir ke kiri pada offset 10. 4 * 2 = 8. Nilai transfer adalah 1, kami menambahkannya ke produk, mendapatkan 9.Instruksi terakhir sebelum kembali adalah perubahan logis ke kanan pada offset 62D. Nilai di sini adalah 1, karena kita berada di pencarian kedua. Dan karena kita dibagi 2, nilainya akan sama dengan 0. Jadi kita masuk ke mode pencarian hibrida.Sekarang kami melakukan fungsi kembali dan permainan memperbarui nilai-nilai keadaannya dengan nilai-nilai yang dicatat dalam kode kami. Ini menyelesaikan bug, dan kami masuk ke kamar Zelda. Kami sampai di sana karena pencarian hibrida membingungkan permainan. Dia tidak tahu persis di mana harus meletakkan Link. Jadi kami menemukan diri kami di kamar Zelda, setelah melewati permainan.
Saya harap ini membantu Anda memahami apa yang terjadi di dalam game Legend of Zelda ketika mengeksekusi kode arbitrer dan mengeksekusi bug game cepat.Source: https://habr.com/ru/post/id398171/
All Articles