Heroes of Might and Magic IV: tavern bug atau patching classic

Cerita pendek ini menggambarkan salah satu proyek yang dilakukan oleh proyek Equilibris , sebuah mod tidak resmi untuk Heroes of Might dan Magic IV. Dari sudut pandang reverse engineering dan patching, ini bukan hal yang menarik - hanya bagian akhirnya yang ternyata agak lucu.



gambar


Seperti yang Anda ketahui, dalam rangkaian game di setiap bar ini, pemain hanya bisa menyewa satu Hero baru per minggu. Tapi…



Deskripsi bug: Jika tidak ada perekrutan di kedai eksternal, maka mulai hari ke-8, Anda dapat membeli dua pahlawan dalam dua hari.



Untuk pekerjaan, file Heroes4.exe yang dibongkar dari add-on resmi terbaru "Winds of War" digunakan. Prosedur pengoperasian kedai ditemukan oleh tim sebelumnya dan terletak di alamat 4705E0. Dari seluruh algoritme karyanya, saya tertarik pada tempat di mana ditentukan apakah mungkin untuk menyewa Pahlawan di bar saat ini, atau jika perlu menunggu. Dalam game, ini dimanifestasikan oleh output dari pesan yang sesuai:





Dari sudut pandang programatik, ini adalah jendela baru yang dibuat dalam game menggunakan fungsi NewWindowCreate (720C80) (fungsi yang dikenali dalam disassembler diberi nama mereka sendiri). Ada beberapa panggilan ke fungsi ini dalam prosedur kedai minuman, dan penantang pertama adalah panggilan ke alamat 470823. Dengan bantuan debugger, saya memastikan bahwa, pada kenyataannya, panggilan ini membuat kotak dialog yang diinginkan. Kode yang mengontrol panggilan ini ke NewWindowCreate ada di atas pada 470645:



00470638                 call    HeroesPricesInTavern_Lost
0047063D                 mov   al, [ebp+48h]  // 0 –   ; 1 –     ( 7 ).
00470640                 add     esp, 8
00470643                 test    al, al
00470645                jz      loc_470866 //   ,      470823


Saya membeli di kedai Pahlawan, lalu mengatur "breakpoint" untuk menulis ke sel yang dialamatkan ke [ebp + 48h], setelah itu saya menunggu selama 7 hari dalam game. Ketika kedai "dikosongkan", debugger muncul di alamat 470DFF. Mari kita lihat kode sekitarnya:



00470DF0 TavernCountDays proc near               
00470DF0                 mov     dl, [ecx+48h] // ECX+48h –   :
DL=0 –   ;
DL=1 –     ( 7 )
00470DF3                 xor     eax, eax 
00470DF5                 cmp     dl, al
00470DF7                 jz      short loc_470E06
00470DF9                 cmp     dword ptr [ecx+4Ch], 7 //  [ECX+4Ch] -         .   7 – . 
00470DFD                 jl      short loc_470E06
00470DFF                mov     [ecx+48h], al  //   (AL=0)
00470E02                 mov     [ecx+4Ch], eax  //   
00470E05                 retn
00470E06
00470E06 loc_470E06:                             
00470E06                                         
00470E06                 inc     dword ptr [ecx+4Ch] //          
00470E09                 retn
00470E09 TavernCountDays endp


Prosedur kecil ini digunakan untuk memeriksa jumlah hari kedai ditutup untuk disewa. Perhatikan bahwa itu dipanggil untuk setiap kedai minuman di peta setiap hari pertandingan. Apa yang menyebabkan bug? Untuk beberapa alasan, program terus menghitung jumlah hari di mana pahlawan tidak dipekerjakan di bar dan setelah minggu saat kedai ditutup (lihat konter di 470E06). Hasilnya, kami mendapatkan gambar berikut. Biarkan perekrutan pertama Pahlawan berlangsung hanya pada hari pertandingan kedelapan. Di pintu masuk ke prosedur, nilai dari tanda ketersediaan kedai di [ecx + 48h] akan sama dengan "1" (kedai ditutup), dan nilai penghitung hari di [ecx + 4Ch] akan sama dengan "8". Namun, setelah perbandingan di 470DF9, kontrol akan menerima kode di 470DFF, yang membuka kembali kedai untuk disewa! Ini akan mengatur ulang penghitung hari.dan setelah mempekerjakan Pahlawan kedua, algoritme akan berfungsi seperti yang diinginkan penulis. Tapi setelah dua minggu dalam game, seluruh siklus akan terulang kembali.



Cara termudah untuk memperbaiki bug adalah dengan melewati penghitungan hari. Biarkan penghitung bekerja hanya ketika kedai ditutup (yang lebih logis), dan sisa waktu kita setel ke nol. Ini dicapai dengan sangat sederhana - dengan mengubah transisi di alamat 00470DF7 ke akhir fungsi:



00470DF5                 cmp     dl, al
00470DF7                 jz      short loc_470E09


Sekarang yang tersisa hanyalah menambal kode yang ada. Untuk melakukan ini, lihat aslinya





dan dimodifikasi





pilihan.



Seperti yang Anda lihat, hasil yang diinginkan dapat dicapai dengan mengganti 0D dengan 10 di alamat 470DF8. Genre klasik: tambal bug dengan mengganti hanya satu byte!



All Articles