logo

Изучение систем паролей в играх SEGA MD/Genesis

В этом документе нам понадобятся:

Сразу нужно сказать, что процесс исследования систем паролей Сеги почти не отличается от NES. Разница лишь в инструментах.
Итак, как и в доке по паролям на NES, сначала мы должны найти пароль в оперативной памяти. Тут нам и пригодится поиск читов в Gens VKNTracer: открываем наш ROM, переходим на экран ввода пароля и открываем CPU -> Debug -> Поиск-кодов. Для удобства можно выставить Data Type: Hexadecimal. Так как мы ищем одну букву, наверное, она не будет занимать больше одного байта в памяти, поэтому Data Size: 1 byte. Нажимаем ‘Сброс’ и в левом окне у нас появляются все ячейки оперативной памяти, среди которых сейчас находится и ячейка с первой буквой пароля, которую мы будем искать. Жмём ОК, а через некоторое время открываем окно поиска читов снова. Так как мы не меняли значение первой буквы пароля, ячейка, которая ее содержит, не изменилась. Смело выставляем =(равен) и жмем поиск. Пока это не заметно, но часть ячеек, значения которых поменялись с момента первого вызова окна поиска читов, уже отброшена и зона поиска заметно снизилась. Теперь сделаем первой буквой пароля ‘B’. Опять вызываем окно поиска читов и, так как, код буквы, скорее всего, увеличился на единичку, выставляем >(больше чем) и жмем поиск. Увеличивая и уменьшая первую букву, легко добиться отсечения всех ненужных нам ячеек:
cheat search
Ячейка 0xFF280E выглядит очень подозрительно. Во-первых, она изменилась только на единичку, как и наша буква. А во-вторых, значение 0x41 соответствует коду буквы ‘A’ в кодировке ASCII. Чтобы убедиться наверняка, откроем просмотрщик памяти: CPU->Debug->Genesis Main 68k RAM, и найдем адрес 0x280E. Оставив просмотрщик в фоне, перейдем в окно эмулятора и поизменяем значения разных букв пароля. Тут уж сомнений не остается: по адресам в оперативной памяти 0x280E - 0x2812 расположены наши пять знаков пароля. Еще мы узнали, что все буквы содержатся в кодировке ASCII и, если буква еще не набрана, в ячейке будет ноль.
Чтобы теперь нам найти алгоритм проверки пароля, поставим останов на чтение из этого диапазона: CPU -> Debug -> Genesis Main 68k Debugger:
check debug
Выставляем останов, нажимаем ОК и проверяем введенный пароль в игре (пусть даже неверный). Игра останавливается по адресам 0x01A460 и 0x1A46E. Наверное, это и есть процедура проверки пароля, которая после прочтения значений букв говорит, правильный это пароль или нет. Но изучать эту процедуру в отладчике сплошное мучение.После загрузки ROM’а в IDA и распознания кода по адресу 0x01A460, получаем чистую и довольно понятную процедуру проверки:
Graph_of_Password_Check Где
Graph_of_CheckSum_Shuffle
и
Graph_of_CheckSum_Calc
где
Graph_of_Shuffle_Data
Итак, что мы видим из этой процедуры? Игра берет 5 знаков пароля, затем по определенной, не имеющей почти никакого математического смысла, процедуре из знаков пароля рассчитывает чексумму пароля. Из младших битов чексуммы находятся номер уровня и число оставшихся в живых солдат (эти две величины проверяются на превышение максимальных значений). Затем из этих двух величин рассчитывается вторая чексумма, которую должен иметь правильный пароль с таким номером уровня и таким числом солдат. Если он совпадает с первой чексуммой, значит пароль верный. Процедура возвращает в регистре d0 ноль. Если хотя бы одно из условий верности пароля не выполняется (номер уровня больше 23, число солдат больше 360 или чексуммы паролей не совпали), процедура возвращает -1 в d0. Особенно интересен конец процедуры:

ROM:0001A4C8 cmpi.l #'MGFJ',(Pwd_Chars_1_4).l
ROM:0001A4D2 bne.s Exit_Password_Check
ROM:0001A4D4 cmpi.b #'H',(Pwd_Chars_5).l ; MGFJH - секретный пароль
ROM:0001A4DC bne.s Exit_Password_Check
ROM:0001A4DE st (Cheat_Flag).l ; FF-чит активирован

Это же явная проверка на определенный пароль (MGFJH)! Из процедуры видно, что даже, если введенный пароль MGFJH, игра напишет Invalid Code, но перед этим выставит флаг включения чита в определенной ячейке памяти. Метод поиска того, что же делает этот пароль остается за рамками данного документа. На самом деле, после введения этого пароля, если во время битвы выбрать белый флаг, миссия будет пройдена, а не провалена, как это обычно бывает. На момент написания документа (ноябрь 2008 года), Интернет об этом пароле не знает.
Что еще мы можем выудить из этой процедуры? Ну, можно написать небольшой брутфорс, который бы перебирал все пароли по порядку, начиная от AAAAA и заканчивая ZZZZZ, проверял их по этому алгоритму и выводил только те, что прошли проверку. Реализацию подобного алгоритма и исходники на паскале можно взять здесь.
Кстати, тут можно сразу проверить известный портал GameFaqs на ноябрь 2008:

Passwords
NTSC Passwords
Enter these on the load screen

Password Effect
UTEFD 10
***
MMVI 24
***
PXJND Level 1

Contributed By: AlaskaFox

PAL Passwords
Levels 1-9 have the same passwords as the NTSC version.

Password Effect
YJKCF 10
***
GWZED 22

Contributed By: AlaskaFox

Know Something We Don’t?

Да уж, мы точно знаем что-то, чего не знаете Вы… Непонятно о каких PAL и NTSC паролях идет речь - судя по результатам работы брутфорса, это все пароли одной версии (а ведь другой-то и нет), просто с разным количеством солдат. Видимо 1-9 уровни, AlaskaFox проходил с одними и теми же потерями, а потом потери стали различаться и пароли на те же уровни тоже. Что это за четырехзначный пароль на 24 уровень? Мы-то уже знаем, что любой пароль на 24 уровень, даже если у него будет правильная чексумма, игра признает неправильным.
Ну и напоследок, можно взять попробовать реверснуть саму процедуру с целью написания генератора паролей. Есть, конечно, люди, которые могут и Triple RSA закейгенить, но я не из таких (к сожалению). Куда проще найти процедуру, которая выводит сделанный игрой пароль на экран и просто записать ее на языке высокого уровня.
Зайдя на экран вывода готового пароля, выясняем, что ячейки, в которых хранятся буквы пароля, не изменились (0x280E - 0x2812). Поэтому поставим останов на запись в этот диапазон и войдем на экран вывода пароля:
Debug_Write
Игра вывалилась по адресу 1A53E - это процедура записи готового пароля в ячейки:
Graph_of_Write_Password
Видно, что процедуре через регистр d7 передается чексумма будущего пароля. Если посмотреть на возможные места вызова этой процедуры (в отладчике или просто анализируя участки кода выше), натыкаемся на такое место:
Graph_of_Pass_Output
Где встречаем уже знакомую нам процедуру CheckSum_Calc.Теперь у нас есть все, чтобы без проблем написать генератор паролей для игры. Исходники генератора на Паскале можно посмотреть здесь.А страничка со скриптом, вычисляющим пароли, как всегда, здесь.

Напоследок, почему рекомендуется оформлять генератор паролей в виде .html страницы:

  1. Размер. В большинстве случаев, размер страницы оказывается меньше, чем подобное приложение на ассемблере (и несравнимо меньше затраты времени на разработку). JavaScript обладает всеми необходимыми для генератора элементами формы (чекбоксы, выпадающие списки, области ввода, кнопки и т.п.). А скорости от генератора никто и не требует.
  2. Скрипт не исполняется на машине пользователя и не имеет доступа к файловой системе / реестру напрямую. Значит, вероятность того, что ваш генератор окажется вредоносным, минимальна - это успокаивает пользователя, который всегда с опаской запускает на своей машине исполняемый файл от незнакомого человека.
  3. JavaScript должен одинаково работать на любых браузерах под любыми операционными системами, что и не снилось исполняемым файлам.
  4. JavaScript элементарно встраивается в любую нужную вам HTML страницу, например вот так:
    SEGA Genesis/MD “Cannon Fodder” Password Generator

    Level:
    Soldiers Alive:
    Password:
    Hangs up the game:
    *Level_Number < 24, Soldiers_Alive_Quantity < 361;
    *If Soldiers_Alive_Quantity > (Level_Number x 15), password will hang up the game;
    *If Soldiers_Alive_Quantity < (Soldiers_On_Mission + 1), password will hang up the game.
        </center>

    К тому же, исходник скрипта любопытствующие смогут всегда увидеть в исходном коде страницы.