Надоело делать патч-коды к игре? Хотели бы чтобы игра сама загружала нужную жизнь в игре! Это реально, но нужно разбираться в ассемблере. Любая игра обязана записывать все промежуточные данные о жизне, о графике и т.п. в ОЗУ. Взломав игру на жизнь патч-кодом мы находим тот адрес в ОЗУ куда записывается жизнь и не даем ей измениться. На этом можно было бы остановиться, но надоедает их все время активировать в эмуляторе. Зачем париться если можно найти то место в программе игры откуда начальная жизнь считывается и изменить ее на любое другое.
Для этих целей возьмем игру на Нинтендо Super Contra 6 и эмулятор с дебаггером FCEUltra 0.98 c сайта CaH4e3.
Для начала взломаем игру на жизнь патч-кодом, т.е. найдем адрес ОЗУ игры где храниться жизнь. В нашем случае это адрес 0x00B5. Ставим брекпоинт на запись в этот адрес и перезапускаем игру:
а) Сразу же срабатывает брекпоинт и мы видим:
fd4d: 95 00 STA $00,X @$00B5
fd4f: 9D 00 01 STA $0100,X @$01B5
Не интересно, т.к. в ячейке 00B5 пока 00. Снова запускаем.
б) Снова сработал брекпоинт:
f301: 95 00 STA $00,X @$00B5
f303: E8 INX
Не интересно, т.к. в ячейке 00B5 пока 00. Снова запускаем. Игра пошла и начинаем игру нажав START.
в) и тут же сработал брекпоинт на том же участке программы и опять в ячейке 00B5 пока 00. Снова запускаем. Мы перешли в режим выбора игрока. Выбираем игрока и:
г)Снова сработал брепоинт:
f63b: 85 B5 STA $B5
f63d: 85 B6 STA $B6
f63f: 60 RST
Вот оно. В ячейку 00B5 пишется число 9 из аккумулятора командой STA $B5, т.е начальное значение жизни. Посмотрим текст программы чуть выше до этой команды:
f635: A9 09 LDA #$09
f637: 85 B3 STA $B3
f639: 85 B4 STA $B4
f63b: 85 B5 STA $B5
Нашли. Команда LDA #$09 загружает число 9 в аккумулятор, а затем пишет его а ОЗУ командой STA $B5.
Теперь ищем строку A9 09 85 B3 85 B4 нех- редактором и меняем 09 на любое желаемое. Я поставил 99. Загружаем снова игру и что мы видим у героя 98 жизней. Ха, получилось!!!!
Дальше будем делать так, чтобы жизнь вообще не уменьшалась. Снова загружаем игру и выбираем игрока и вперед в игру. Переключаемся в дебаггер и ставим брекпоинт на тот же адрес 00B5 на запись в него. Даем игроку умереть и срабатывает брекпоинт:
ac9e: 99 B3 00 STA $00B3, Y @00B5
aca1: D0 1E BNE $ACC1
После выполнения команды STA $00B3, Y в ячейке 00B5 появляется новое значение жизни, т.е было 9 а стало 8. Посмотрим чуть выше:
ac9c: A5 0A LDA $0A @000A = $07
ac9e: 99 B3 00 STA $00B3, Y @00B5
Скорее всего по адресу 000A в ОЗУ хранится вычисленное значение жизни после смерти уменьшенное на 1. Затем командой LDA $0A загружается в аккумулятор, а из аккумулятора командой STA $00B3, Y новое значение жизни заносится в ОЗУ в нашу ячейку 00B5. Можно было бы найти то место где это значение вычисляется, но это можно обойти следующим образом:
Заменяем 99 B3 00 STA $00B3, Y
На это EA NOP
EA NOP
EA NOP
Т.е. препятствуем записи нового значения жизни в ОЗУ
Еще пример: игра Final Mission. Сделаем так, чтобы жизнь не уменьшалась.
Ищем адрес в ОЗУ где хранится жизнь и находим его: 001С
Начинаем игру и ставим брекпоинт на запись в этот адрес. Даем попасть в вас и сразу же срабатывает брекпоинт:
df1b: 95 1C STA $1C, X @001C
df1d: A9 1B LDA #$1B
Вот она команда загрузки в ячейку с жизнью. Посмотри что выше этого:
df16: B5 1C LDA $1C, X @001C = $03
df18 38 SEC
df19 E9 01 SBC #$01
df1b: 95 1C STA $1C, X @001C
Команда LDA $1C, X считывает старое значение жизни, после этого командой SBC #$01 уменьшает жизнь и, наконец, командой STA $1C, X записывает в ОЗУ новое значение жизни. Нашли!!!!!!!!! Можно заменить команду SBC #$01 на две команды NOP или STA $1C, X на две команды NOP. Остановимся на втором варианте.
Ищем строку B5 1C 38 E9 01 95 в нех-редакторе и находим ее по адресу: 1df2b. Меняем 95 1C на EA EA. Проверяем в игре- работает!!!!!!
Теперь найдем откуда игра грузит жизнь.
Сбросим игру и пусть игра пройдет заставку и очутимся на титульном экране. Ставим брейкпоинт на тот же адрес 001С и нажимаем START.
А) Срабатывает брейкпоинт:
cecd: 85 1C STA $1C
cecf: 85 1D STA $1D
cecf: 85 87 STA $87
Выполняем один шаг программы и смотрим что грузит игра в нее: 00. А должен 3, т.к. 3 жизни. Следовательно, пропускаем этот кусок программы и нажимаем RUN в дебаггере.
Б)И снова остановка:
cef8: 86 1C STX $1С
cefa: 60 RTS
Ага команда STX $01 загружает из регистра Х в ОЗУ в ячейку 001С байт. Смотрим что в регистре Х – число 3. Ага это и есть наша жизнь. Но нужно выяснить откуда он его грузит. Для этого посмотрит программу чуть выше:
cef0: A2 03 LDX #$03
cef2: A5 1A LDA $1A @$001A = $00
cef4: F0 02 BEQ $CEF8
cef6: 86 1D STX $1D
cef8: 86 1C STX $1С
Вот, командой LDX #$03 и загружается начальная жизнь. Команда BEQ $CEF8 – это переход по адресу CEF8, если установлен флаг обнуления (назначение не ясно). Далее это число из регистра Х грузится в ОЗУ. Все нашли!!!!!!! Теперь замените число 03 на любое другое и радуйтесь результату. У меня работает.
Пример посложнее Teenage_Mutant_Ninja_Turtles_3. Сделаем так чобы жизнь неизменялась.
Находим адрес в ОЗУ жизни 006A. Всего сначала 3 жизни.
Начинаем игру и даем игроку потерять жизнь и видим что вы перед выбором нового игрока. Вы можете выбрать другую черепаху. Но пока не выбираем, а ставим брейкпоинт на найденный адрес и давим START и:
А) Срабатывает брейкпоинт:
c2fc: 95 6A STA $6A,X @006A
c2fe: B5 6A LDA $6A,X @006A = $C2
c300: 29 3F AND #$3F
c302: 95 6A STA $6A,X @006A
Угу в ОЗУ пишется новое значение С2 командой STA $6A,X. Затем командой LDA $6A,X снова считывается в аккумулятор. А потом производится логическая операция командой AND #$3F так что от С2 остается только 02 и затем оно снова записывается в ОЗУ командой STA $6A,X. Понятно!!!!! Но где оно уменьшается. Посмотрит чуть выше:
c2f7: B5 6A LDA $6A, X @006A = $C3
c2f9: 38 SEC
c2fa: E9 01 SBC #$01
c2fc: 95 6A STA $6A,X @006A
Ага нашли команду уменьшения жизни: SBC #$01. Оказывается жизнь сначало в виде значения С3 а потом его уменьшают до С2 и убирают приставку С и после этого снова помещают в ОЗУ но уже это число 02. Заменить с302: STA $6A,X на две холостые команды мы не можем, т.к. в ОЗУ останется число С3 и мы не достигнем своей цели. Тогда будем заменять команду SBC #$01 на SBC #$00. Ищем строку B5 6A 38 E9 01 95 6A в роме. И меняем 01 на 00, сохраняем. Смотрим, работает!!!!!!!!
Попробуем найти откуда изначально при старте игра грузит начальное значение жизни. Грузим игру, нажимает на START и мы в таблице выбора игрока. Ставим брейкпоинт на наш адрес 006A и выбираем игрока и
А)срабатывает брейкпоинт:
a270: 85 6A STA $6A
a272: A4 28 LDY $28 @ $0028 = $00
Здесь команда STA $6A грузит в ОЗУ начальное значение жизни, но откуда он его грузит, смотрим выше:
a26d: B9 90 83 LDA $8390,Y @ $8390 = $03
a270: 85 6A STA $6A
Ага, изначально жизнь грузится из другого участка ОЗУ по адресу 8390 командой
LDA $8390,Y и затем из него заносится в ОЗУ в наш адрес 006A. Можно найти участок где записывается по адресу 8390 число 03, но можно заменить команду LDA $8390,Y на NOP и LDA #$09. Т.е. загружаем в аккумулятор не из ячейки 8390, а напрямую число 9.
Т.е. заменяем B9 90 83 на EA A9 09 и запускаем. Не идет. Снова лезем в отладчик и повторяем процедуру заново. Выбираем игрока и пропускаем первое срабатывание брекпоинта и смотрим:
aс87: B9 90 83 LDA $8390,Y @ $8390 = $03
ac8a: 85 6A STA $6A
Таже хрень, но подругому адресу. Ищем строку B9 90 83 85 6A в роме. Она по адресу 10С97. Опять меняем B9 90 83 на EA A9 09 и запускаем. Смотрим, работает!!!!!!! Видать в игре дважды загружается начальное значение жизни.
Готовые хаки на отмену уменьшения жизни:
1)Iron Tank: ищем строку 9D 06 03 по адресу 1e84c и меняем ее на EA EA EA
2) Shatterhand ищем строку 8D C5 05 по адресу 103c6 и меняем ее на EA EA EA
так же строку 9D F0 06 по адресу 1D521 и меняем ее на EA EA EA
3) Tom & Jerry ищем строку E9 01 по адресу 1F0B0 и меняем 01 на 00
Надеюсь статья понятна даже ламерам. Описание всех команд для нинтендо можно найти на русском языке на сайте tv-games.narod.ru