logo

Документация к FCEUd #2

  1. Разминка
    Перед тем как начать, лучше сразу сказать, что нужно знать, чтобы полностью понять этот документ. Во-первых, нужно знать основы программирования на ассемблере. (Основы просты: нужно понимать каждую команду и режимы адресации команд). Так же нужно быть сведущим в двоичной системе счисления, включая и вычисления в ней. В общем, для того чтобы начать пользоваться FCEUd нужно не так много. Документы по ассемблеру процессоров семейства 6502 и разные доки можно найти на Zophar Domain и ROMHacking.net. Или просто используйте поиск со словами “6502 Assembly” в Гугле. То, что нам понадобится:
    • FCEUd
    • РОМ игры Metroid,
    • Разные доки, которые можно найти на: www.zophar.net/, www.obelisk.demon.co.uk/6502/reference.html, www.romhacking.net/
  2. Консоль отладки FCEUd
    Далее весь документ будет посвящён версии FCEUltra от Dragineye Studios со встроенным отладчиком. В настоящий момент появились намного более совершенные программы подобного типа (например, FCEUXD, FCEUXDSP). Однако, принципы отладки остались прежними, а консоль отладки и вовсе перешла почти без изменений, лишь получив новые фичи. И не только в области отладки: появились разные просмотрщики, трейсер, встроенный хексредактор, которым удобнее пользоваться и т.д.
    Когда вы загружаете FCEUd, вы встретите знакомый интерфейс FCEUltra 0.81 Также в меню “NES” строку “Debug”(отладка). Она открывает консоль отладки FCEUd. Консоль состоит из нескольких групп. Слева расположен подробный дизассемблер. В середине можно найти кнопки управления программой. Под ними - группа управления РС (ProgramCounter - программный счётчик) . Ещё ниже - регистры и дамп стека. В верхнем правом углу консоли находится группа останова, под которой - группа флагов состояния программы. Подробный дизассемблер будет производить все вычисления, связанные с особенностями режимов адресации за вас, основываясь на содержании регистров. Когда вы продвигаетесь по ассемблерному коду, дизассемблер берёт текущие значения регистров и вычисляет всё “На лету”. Такой метод дизассемблирования имеет свои недостатки, однако достоинства с лихвой их перекрывают. Из-за особенностей программирования в каждой игре, единственно гарантированно правильный адрес будет у кода, на котором стоит программный счётчик. Например, если вы остановили выполнение программы по адресу $8096, то только команда по адресу $8096 будет на 100% правильно отображена в подробном дизассемблере. Остальные команды будут отображены правильно, однако, в зависимости от того, какой режим адресации они используют, дополнительная информация может быть неправильной вплоть до момента выполнения этой команды. Вот пример того, как подробная информация может вам помочь. Возьмём произвольную команду:
    $E322:FD 85 51 SBC $5185,X @ $518A = #$8D
    Вы видите сначала адрес, за которым следуют байты, сформировавшие команду. В нашем случае команда “SBC”, а её операнд - “$5185,X.” В нашем случае, это индексный режим адресации. Следующая часть информации поведает нам об адресе, на который воздействует данная команда. В настоящий момент, значение, загруженное в Х - #$05. Можно просто прибавить 5 к адресу, указанному в операнде, однако подробная информация позаботится об этом и выдаст нам адрес, над которым работает данная инструкция. Вот ещё пример, который займёт намного больше времени для вычисления адреса, над которым работает команда:
    $E1D7:01 18 ORA ($18,X) @ $0300 = #$00
    Опять же, команды имеют тот же формат. Знак “@” говорит о том, что после него пойдёт адрес, над которым работает операция, а после знака “=” пойдут данные, которые содержатся по адресу, над которым работает эта команда. В любом случае, основной целью такой подробной информации является задача облегчить процесс вскрытия игр.

  3. Быстрое описание остановов
    Остановы (бряки) - простое средство мощной отладки. Остановы позволяют остановить программу в любой момент её выполнения, и посмотреть что она делает. Вы не говорите программе где остановиться, скорее, вы говорите почему ей надо остановиться. FCEUd поддерживает 3 основных типа остановов. Непосредственно останов, который останавливает программу, когда выполняется команда по заданному адресу. А также остановы чтения(read) и записи(write), которые остановят программу, если в заданный адрес происходит запись или из него происходит чтение.
    В консоли FCEUd остановы можно добавлять(add), также как и удалять(delete) и редактировать(edit) в группе остановов. Нажатие на кнопку Add откроет новое окно, которое позволит вписать один адрес, или интервал адресов. Тут же устанавливается тип останова. Чтобы установить один адрес, оставьте второе окошко пустым. Чтобы ввести интервал адресов, введите в первое окошко начальный адрес, а затем во второе - конечный адрес интервала. Можно устанавливать до 64 остановов. Окошко выбора останова позволит вам выбрать останов, удалить его или редактировать. Просто кликните на останове, чтобы выбрать его. Чтобы активировать или деактивировать останов на нём надо кликнуть два раза в окне выбора останова. Адрес или интервал адресов будет отображаться как останов в окошке выбора, после того как был добавлен. Изменяя тип останова, активируяего, изменит флаги, отображающиеся сразу после адреса останова или интервала адресов.
    Флаги могут быть следующими: “ERWX”. “E”(enabled) - означает, что останов активирован, “R”(read) - означает, что это останов чтения. “W”(write) - означает, что это,соответственно, останов записи. “Х”(eXecute) - останов выполнения. Если какой-либофлаг снят, то он заменяется дефисом.

  4. Управление программой и дизассемблером
    Кнопки управления программой позволяют управлять выполнение кода. Если нажата кнопка “Run”, то игра будет идти как обычно, пока не будет остановлена (вами или остановом). Кнопка “Step Into” будет выполнятьпо одной команде при каждом нажатии. Эту кнопку обычно используют, если хотят продвигаться по коду медленно и точно посмотреть как он работает. Кнопка “Step Over” работает также, как и Step Into, за тем исключением, что она позволит перепрыгивать через подпрограммы (subroutine). Т.е. всё будет происходить также, как и при нажатии Step Into, однако будут обходиться команды JSR. Если нажать Step Over на команде JSR, то весь код внутри следующей подпрограммы будет выполнен как обычно и будет выполнен выход из неё(если, конечно выполнение не остановит ваша рука или останов). Вследствие особенностей программирования отдельных игр, не все подпрограммы будут выходить как того ожидает Step Over. Это приведёт к странным результатам: выхода из подпрограммы не будет или адрес на который надо вернуться не совпадает с тем адресом, который стоит сразу после вызова JSR. Кнопка “Step Out” буквально выйдет из текущей подпрограммы к вызывающей её команде. Это может быть полезно во многих случаях, которые сейчас не так очевидны.
    Управление дизассемблером позволяет быстро ориентироваться в памяти NES через дизассемблер. Можно быстро искать(seek)через дизассемблер любой адрес. Можно также искать и определённый PC, если вы далеко забрались и нужно быстро вернуться в исходную точу.

  5. Приступаем к вскрытию
    В первом документе, мы поменяем высоту прыжка Samus в игре Metroid. Сделать это несколько легче, чем это звучит. В первую очередь, нам понадобится адрес в ОЗУ (RAM), с которым мы будем работать. Так как мы будем изменять высоту прыжка, нам нужен адрес, который отвечает за прыжки! Я загрузил NESten (В настоящий момент у него лучше система поиска читов, чем у FCEUd).
    В новых приложениях предусмотрен отличный поиск читов - намного удобнее, чемв NESten’e или FCEUltra.
    И сделал небольшой поиск читов. Пока Samus была на земле и не двигалась, значение ячейки памяти было всегда равно предыдущему. Потом я подпрыгнул и посмотрел какие ачейки изменились, пока Samus была в воздухе. А потом опять искал изменившиеся ячейки, уже когда Samus приземлилась. Я сделал так несколько раз и получил три адреса: $01EE, $01F0, $0312. Известно, что любая ячейка в интервале $0100 - $01FF зарезервировано под стек. Так что эти адреса можно сразу отбросить. Остался один адрес - $0312.
    Теперь, когда мы получили рабочий адрес, загружаем Metroid в FCEUd и открываем отладочную консоль. Затем добавляем останов на запись в полученную ячейку. И игра с радостью встанет, как только в этот адрес будет что-нибудь записано. Нажмём Run несколько раз, чтобы убедиться, что в эту ячейку всегда записывает только одна команда. И вот команда, которая остановила выполнение программы в моём случае: $E3AE:8D 12 03 STA $0312 = #$00
    Это просто STA $0312 - большая неожиданность. Теперь надо немного прокрутить дизассемблер вверх, чтобы посмотреть что записывает в аккумулятор значение. Вот что я нашёл:

    $E3A7:AD 12 03 LDA $0312 = #$00 
    $E3AA:18 CLC                    
    $E3AB:6D 14 03 ADC $0314 = #$00 
    $E3AE:8D 12 03 STA $0312 = #$00 

    Значение загружается из нашей же ячейке. Клёво. А потом к нему добавляется значение ячейки $0314. Эта сумма помещается в наш адрес. Так что теперь, нам нужно узнать кто записывает значения в адрес $0314. Нажмём Run и игра продолжит своё выполнение как обычно. Нажмём прыжок, и АГА! Игра встала. Немного прокрутим дизассемблер и посмотрим что же произошло…

    $CFF3:A0 18 LDY #$18             
    $CFF5:AD 78 68 LDA $6878 = #$00  
    $CFF8:29 02 AND #$02             
    $CFFA:F0 02 BEQ $CFFE            
    $CFFC:A0 12 LDY #$12             
    $CFFE:8C 14 03 STY $0314 = #$00  

    STY - это то, что остановило программу, а LDY говорит нам какое именно значение записывается по нашему адресу! LDY #$12, да? Можно его просто поменять! Чтобы найти наш код в РОМе, скопируем те байты, что показывает дизассемблер: A0 12 8C 14 03. Потом найдём такую комбинацию в РОМе. Такая комбинация встречается в файле по адресу $1D00C. Всё, что остаётся сделать - поменять $12 на что-нибудь другое, например, $15. Сохраним файл и проверим получившийся хак.
    В новых приложениях появился встроенный инлайновый ассемблер - можно ткнуть на месте нужной команды (слева от неё) и в появившемся окошке вписать нужную команду. А потом можно сохранить. Впрочем, для этой же цели существует и появившийся RomPatcher: вводите адрес команды - он сразу выдаёт адрес в РОМе. Всё чтобы облегчить жизнь.
    Сработало? Ну, и да и нет.
    Если вы знакомы с читами Metroid’a, то вы можете знать что такое $6878. Это адрес, отвечающий за вещи, которые имеет Samus. AND #$02 - это проверка на то есть ли у неё ботинки высокого прыжка(high jump boots)! BEQ пропустит загрузку этого LDY, если у Samus этих ботинок нет. Таким образом, у нас есть два значения высоты прыжка, в зависимости от того есть у нас ботинки или нет.
    Первая команда LDY устанавливает высоту прыжка, если у вас нет ботинок. Вторая команда LDY устанавливает высоту, если ботинки есть. И, как вы уже догадались, чем ниже это значение, тем выше прыгает Samus.