Приветствую,

Пока Хабр заполоняют новости от его "замечательной" информационной службы о выходе каждой новой версии какого-то noname софта, которым пользуется полтора, а то и даже два землекопа, я подумал: "а чем я хуже?" и решил написать о своём новом проекте - о загрузчике SNES ромов и нормальном (!) процессорном модуле WD65816 для IDA Pro. Может быть, он будет даже полезен какому-то количеству людей. Создавался проект в помощь тем, кто хотел или уже пытался реверсить SNES ромы в Иде, но у него ничего не получалось.
В этой, можно сказать, обзорной статье, вы узнаете:
Почему уже имеющийся в дистрибутиве Иды загрузчик и процессорный модуль, мягко скажем, плохие
Почему реверс-инжиниринг SNES - это боль
Чем мой проект может быть полезен
Как я докатился до такого?
Начну с того, что я люблю исследовать код старых игр - это для меня своего рода медитация. Для старушки Sega Mega Drive у меня давно уже есть плагин-отладчик/загрузчик для ромов - smd_ida_tools2. И есть проекты по реверсу игр под неё. Супернинтенды у меня никогда не было, поэтому и интереса к ней не проявлял особо.
Но, однажды мне довелось поиграть в такую игру под SNES как Clock Tower, и она запала мне в душу. К тому же, на этой приставке был и порт одной из моих любимых игр для Sega - Thunder Force 3. Так я и пришёл к тому, что занялся реверсом игр ещё и под SNES.
SNES и IDA - это больно
Когда впервые открываешь незнакомый тебе формат/платформу в Иде в принципе - получаешь незабываемые ощущения, а если добавить сюда особенности самой SNES - становится вообще не по себе.

Банки памяти
Во-первых, на этой архитектуре есть сегментация памяти (так называемые "банки"). Существует несколько сегментных регистров, через которые можно задавать базовый адрес типа 0xNN0000
. И есть команды, которые этот базовый адрес учитывают при адресации. Мне хватило реверса DOS-игр в Иде когда-то - больше не хочется. От слов "селектор", "параграф", "сегментный регистр" по отношению к IDA Pro меня каждый раз коробит.

Размер инструкции может изменяться
Во-вторых, у WD65816 есть такой механизм, при котором размер инструкции определяется значением некоторых флагов процессора. Например, если флаг m
равен 1
, константа в инструкции SBC #const
(опкод $E9
) будет занимать два байта, а при m==0
- три. И флаг этот может задаваться вообще хрен знает где в другой функции! Оригинальный модуль для работы с процессором пытается отслеживать флаги (m
и x
) и выставлять их для таких инструкций, но делает он это крайне плохо - изменить то, как чего наопределялось, крайне сложно и не интуитивно.

.A8
, .A16
, .I8
, .I16
- это и есть режим для последующих инструкцийЗеркалирование
В-третьих, "зеркалирование" диапазонов памяти. Это когда одни и те же данные/регистры/код могут быть доступны по разным адресам. У Иды с этим совсем плохо. Приведу пример. Есть диапазон WRAM $7F0000
-$7F1FFF
, и у него есть "зеркало" в виде диапазона $000000
-$001FFF
. Обращаться можно к любому из них - данные там будут те же. Со стороны Иды при этом возникает трудная задача - нужно, чтобы ссылки вели в один и тот же сегмент. Если создать оба - ссылки и метки будут создаваться в двум местах, и отслеживать их потом будет крайне сложно. До недавнего времени в Иде вообще не было возможности работать с таким вот "зеркалированием".
Некорректный синтаксис
Если описанных выше проблем вам кажется и так достаточно, чтобы никогда не заниматься реверсом SNES, то я ещё накину: Ида выводит инструкции WD65816 некорректно, неправильно высчитывает целевые ссылки и т.п. Ещё она насильно некорректно определяет некоторые начальные вектора прерываний, но при некоторой подготовке это решаемо.

.80:80B6
и .80:80BA
Загрузчик SNES ромов
Идовский оригинальный загрузчик создаёт множество сегментов со своими селекторами (б-р-р-р), ориентироваться и проставлять ссылки между которыми лично мне было трудно.

Сделай лучше!
Терпел я, терпел это безобразие, и сдался - было принято волевое решение написать по-своему, понятнее и удобнее, без этих самых "сегментных селекторов". Вот какими идеями я руководствовался:
-
Реверс платформы не должен доставлять боль. У процессора SNES есть одни и те же опкоды, но с разным режимом адресации, и при этом визуально, не смотря в байты, понять какая именно версия используется, нереально. Я добавил пометки типа "Uses Data Bank", "Uses Direct Page" для понимания, относительно какого регистра высчитывается смещение. Также были добавлены постфиксы типа .B, .W, .L для понимания размеров операндов
Для понимания адресуемого пространства -
Со ссылками на код, переменные и данные работать должно быть просто. Если ссылка указывает не туда, нажал
O
илиCtrl+O
- получил нужный вариант ссылки. В оригинальном варианте от Ильфака Гильфанова нужно лезть в диалог изменения регистров (Alt+G
) и без малейшего понятия какой за что отвечает пытаться их редактироватьБыло Стало -
Добавил возможность отключать
BRK
/COP
/WDM
опкоды, которые хоть и имеют поддержку процессором WD65816, по факту не используются практически ни в одной игре. При этом, они здорово мешают Иде нормально дизассемблировать. Получается мусорный код. Учитывая, что в этом процессоре на каждый байт реализовано по опкоду (то есть имеем все256
), дизассемблируется практически всё, и это не есть хорошо. С возможностью выключать эти опкоды должно стать лучше (все три опкода у меня выключены по-умолчанию)Возможность настройки опкодов За основу я брал известный эмулятор MESEN2, который на этой самой эмуляции собаку съел, поэтому парсинг ромов и корректность реализации опкодов должна быть неплохой
-
Переключение режима инструкций по
Shift+X
. Видно, что начался некорректный код? - найдите предыдущие инструкции, где можно поменять режим, и попробуйте снова. Часто должно помогатьБыло Стало Конечно же, зеркала сегментов. Я долго мучился (больше года), чтобы понять, как это реализовать и, вроде бы, смог. Завернул скрин под спойлер, так как даже ида с его размерами не справляется
Большая картинка

Чего пока не сделано (считай, TODO):
Дать имена управляющим регистрам
Вклинить MESEN2 в качестве отладчика прямо в плагин. Думаю, через grpc
Разреверсить полностью любимые игры:)
Спасибо за внимание. Ссылка на проект: https://github.com/lab313ru/snes_ida