Русский
Русский
English
Статистика
Реклама

Из песочницы Heroes of Might and Magic IV баг с таверной или классика патчинга

Эта короткая история описывает одну из работ, проведенную в рамках проекта Equilibris неофициального мода для игры Heroes of Might and Magic IV. С точки зрения как реверс-инжиниринга, так и патчинга она не представляет особого интереса несколько забавным оказался только лишь финал.

image

Как известно, в данной серии игр в каждой таверне игрок может нанимать лишь одного нового Героя в неделю. Однако

Описание бага: Если во внешней таверне не было найма, то, начиная с 8-го дня, можно купить двух героев в течение двух дней.

Для работы используется дизассемблированный файл heroes4.exe из последнего официального аддона Winds of War. Процедура работы таверны найдена командой ранее и расположена по адресу 4705E0. Из всего алгоритма ее работы меня интересует место, в котором определяется, можно ли в данный момент нанять в таверне Героя, либо необходимо ожидание. В игре это проявляется выводом соответствующего сообщения:


С программной точки зрения это новое окно, которое в игре создается с помощью функции NewWindowCreate (720C80) (распознанным в дизассемблере функциям даны собственные имена). В процедуре таверны несколько вызовов этой функции, и первым претендентом является вызов по адресу 470823. С помощью отладчика убеждаюсь, что, действительно, этот вызов создает искомое диалоговое окно. Код, управляющий этим вызовом NewWindowCreate, находится выше по адресу 470645:

00470638                 call    HeroesPricesInTavern_Lost0047063D                 mov   al, [ebp+48h]  // 0  если таверна работает; 1  если героя нанять нельзя (ждешь 7 дней).00470640                 add     esp, 800470643                 test    al, al00470645                jz      loc_470866 // Если таверна работает, пропустить вывод сообщения по адресу 470823

Покупаю в таверне Героя, затем устанавливаю бряк на запись на ячейку, адресуемую [ebp+48h], после чего жду 7 игровых дней. Когда таверна освобождается, отладчик всплывает по адресу 470DFF. Давайте посмотрим окружающий код:

00470DF0 TavernCountDays proc near               00470DF0                 mov     dl, [ecx+48h] // ECX+48h  флаг работы таверны:DL=0  если таверна работает;DL=1  если Героя нанять нельзя (ждешь 7 дней)00470DF3                 xor     eax, eax 00470DF5                 cmp     dl, al00470DF7                 jz      short loc_470E0600470DF9                 cmp     dword ptr [ecx+4Ch], 7 // В [ECX+4Ch] - число дней с момента найма последнего героя в таверне. Если меньше 7  выходим. 00470DFD                 jl      short loc_470E0600470DFF                mov     [ecx+48h], al  // Таверна работает (AL=0)00470E02                 mov     [ecx+4Ch], eax  // Обнулить число дней00470E05                 retn00470E0600470E06 loc_470E06:                             00470E06                                         00470E06                 inc     dword ptr [ecx+4Ch] // Увеличить число дней с момента найма последнего Героя в таверне00470E09                 retn00470E09 TavernCountDays endp

Эта небольшая процедура служит для проверки числа дней, в которые таверна закрыта для найма. Замечу, что она вызывается для каждой таверны на карте в каждый игровой день. Что же порождает баг? Программа зачем-то продолжает вести подсчет числа дней, в течении которых в таверне не было найма Героя и по истечении недели, в которую таверна была закрыта (см. счетчик по адресу 470E06). В результате получаем следующую картину. Пусть первый найм Героя происходит только на восьмой игровой день. На входе в процедуру значение флага доступности таверны по адресу [ecx+48h] будет равно 1 (таверна закрыта), а значение счетчика дней по адресу [ecx+4Ch] будет равно 8. Однако при этом, после сравнения по адресу 470DF9, управление получит код по адресу 470DFF, вновь открывающий таверну для найма! При этом счетчик дней сбросится, и после найма второго Героя алгоритм уже отработает, как задумывали авторы. Но через две игровых недели весь цикл повторится.

Самый простой способ пофиксить баг отказаться от подобного подсчета дней. Пусть счетчик работает только тогда, когда таверна закрыта (что логичнее), а в остальное время зададим его равным нулю. Это достигается очень просто изменением перехода по адресу 00470DF7 в конец функции:

00470DF5                 cmp     dl, al00470DF7                 jz      short loc_470E09

Теперь остается лишь пропатчить имеющийся код. Для этого смотрим исходный


и измененный


варианты.

Как видно, необходимого результата можно достичь, заменив 0D на 10 по адресу 470DF8. Классика жанра: пропатчить баг, заменив всего лишь один байт!
Источник: habr.com
К списку статей
Опубликовано: 21.11.2020 14:13:58
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Реверс-инжиниринг

Игры и игровые приставки

Heroes of might and magic iv

Категории

Последние комментарии

© 2006-2020, personeltest.ru