Разработка и ромхакинг > Ромхакинг
[SMD] Какие валидные инструкции не встречаются в коде игр?
GManiac:
--- Цитата: perfect_genius от 11 Сентябрь 2025, 16:59:24 ---В её счётчике скачиваний я давно есть :)
Но она кажется ещё сложнее. Например, что значит xxxl, Bxxx и крестики в самой таблице?
--- Конец цитаты ---
xxxl - это Immediate инструкции (ADDI, SUBI), они сделаны отдельным блоком опкодов
Bxxx - это операции над единичным битом: BTST, BSET и т.д.
Вообще-то, в колонке справа это прямо написано. Ещё можно догадаться из столбца "Операнды" и блоков immediate_op / bit_op внутри кода.
Изначально таблица была сделана HWMan-ом, но второй лист у него (сортировка по опкодам) был слишком громоздким и нечитаемым. Вот я решил для себя её максимально сжать, чтобы лучше понимать схему декодирования, как раз перед написанием своего дизассемблера. Это делалась "под себя", поэтому человек со стороны, тем более который не знает систему команд, конечно, может, что-то не понять. Вы можете её доработать.
Выделение цветом я сделал, в основном, чтобы видеть различия между похожими кодами.
--- Цитата: perfect_genius от 11 Сентябрь 2025, 16:59:24 ---Т.е. как я делаю, но я наоборот для недопустимых?
--- Конец цитаты ---
Не знаю, как делаешь ты, глянь исходник Никодима по ссылке. Там в файле M68K.PAS есть массив opCodes, логика простая, разберём на примере 1-й записи оттуда - SWAP.
--- Код: --- opCodes : array [0..82] of TInstruction = (
(Mnem:'SWAP'; Mask:$FFF8; Map:$4840; Suf:False; SFunc:SWAP),
--- Конец кода ---
В моей эксельке видно, что идентифицирующие биты этой команды - это D3-D15, а изменяемые (регистр Dn) - D0-D2. Т.е. допустимые коды - от $4840 до $4847. Поэтому маска для AND будет состоять из единичных битов в D3-D15 и нулевых в D0-D2, т.е. $FFF8, а "основа" (в исходнике названо Map) - $4840. Это что получится, если любой код SWAP сделать AND с маской.
На самом деле маскирование можно было сделать и с функцией OR, но там всё наоборот: неизменяемые биты берём 0, а изменяемые - 1, тогда маска будет $0007, а Map - $4847. Любое значение SWAP, например:
$4842 OR $0007 = $4847
В BizHawk тоже вроде как-то похожим образом сделано, не помню.
Т.е. декодирование идёт не кучей if-ов, а пробеганием по этому массиву. В теории это медленней, но проще. В теории можно поисковое дерево сгенерить на основе этого массива, но никто не заморачивается, в StarScream для скорости просто генерится таблица на 65536 jump-ов - все комбинации из 2 байт.
GManiac:
Вот мой дизасм и анализатор, построенный на его основе. Выкладываю в сыром виде, кому надо, разберётся. Это было написано давно, в 2008-м.
Там анализатор был в 4 разных варинатах в разных папках, поэтому вспомогательные файлы Visual Studio могут ругаться.
В папке аналзатор см. разные файлы main_*.cpp. Я уже не помню точную разницу между версиями, но примерно так:
1 - более-менее базовая версия, но уже скорей всего, переделанная под что-то
2 и 3 - версии специально для Landstalker, чтобы учитывать дурацкую функцию в TRAP, которая меняет A7
4 - судя по именам экзешников в этой папке (см. папку Analyzer_v4), я его дописывал, чтобы искать "гомологичный код" - т.е. там маскируются номера регистров, адреса и т.д.. Т.к. одинаковый по суи код в разных местах или играх может иметь немного разные номера регисторв, но последовательность команд скорей всего будет одинаковая.
В файле readme.txt краткое описание идеи и алгоритм.
Экзешники и батник для "полного цикла" вытаскивания строк "только кода" из рома я выкладывал здесь
https://www.emu-land.net/forum/index.php?action=dlattach;topic=72028.0;attach=290207
( В этой теме https://www.emu-land.net/forum/index.php/topic,72028.msg1631115.html#msg1631115 )
Экзешник совпадает с версией Analyzer_v2_landstalker.exe.
Беларус учит русский:
Да уж, этот твой эксперимент впечатляющий. Я делаю рекурсивный дизассэмблер, по вычисляемым прыжкам будет прыгать человек - в эмуляторе. Но весь остальной пропущенный код придётся искать по твоему способу, какими-то подобными эвристиками. Даже думаю реверсить Иду, чтобы подсмотреть у неё :)
--- Цитата: GManiac от 02 Март 2025, 02:21:59 ---Поиск делался методом от противного: цепочка опкодов, которая НЕ приводит к явной ошибке, а например, заканчивается условным прыжком, потенциально может быть валидным кодом. Благо, в m68k очень много ограничений на валидные опкоды (включая требование к чётным адресам) и метод от противного хорошо работает.
--- Конец цитаты ---
Но а что насчёт перекрывающегося кода? Т.е. массив байт может распознаваться как один код, а если начать чуть с другого места - уже другой.
И TRAP ведь не только Landstalker может использовать.
GManiac:
В случае перекрывающегося кода есть приоритет у хорошо распознанных последовательностей команд в форвард режиме, это в анализаторе учтено. Помечается флагом не вся вообще команда (может быть, неправильная), а только первые 2 байта, т.е. опкод. Сначала весь ром помечается как not_processed. Потом делается форвард анализ, и первые байты команд в форварде помечаются, как распознанные. Потом прогоняется анализ "от противного" - вот у него приоритет ниже, чем у форварда. Я точно всю эвристику не помню, кажется, я ещё последовательно 0000 пропускал, т.к. это дизасмится в
ORI #0, D0
и это явно не нормальная инструкция.
В Landstalker дело не в TRAP, а в том, что сам обработчик этого TRAP двигает A7. Это могла быть и простая субрутина. В каких ещё играх - не знаю, вон там в коде в комментах упоминается Микки Маус, может, там тоже какие-то приколы.
Под все случаи анализатор не напишешь, а т.к. его надо было компилировать, я просто кастомизировал код на живую. Ну вообще это всё WIP.
SeregaZ:
perfect_genius, по идее там не столько реверсить иду - сколько реверсить шелловский эмулятор. это он эти всякие адреса находит, а ида потом уже их получает и разбирает. хотя я, если засяду, буду не столько реверсить - сколько искать в памяти где это, чтобы оттуда читать. так как полагаться на дизасм внутри эмулятора не стоит. там с ошибками код разбирает. а вот адреса выколупывать - в самый раз.
pav13:
выяснилось, что таблица валидности из musashi (ранее тут выложенная) для процессора именно m68000, а не для m680x0.
https://www.emu-land.net/forum/index.php/topic,91585.msg1655231.html#msg1655231
https://www.emu-land.net/forum/index.php/topic,91585.msg1655372.html#msg1655372
чтобы сделать из неё таблицу для процессора m68000 именно SEGA MD надо дополнительно убрать нечётные опкоды ветвлений.
для m680x0 есть такая таблица
https://github.com/capstone-engine/capstone/blob/next/arch/M68K/M68KInstructionTable.inc
Беларус учит русский:
Переименовал тему, обновил первый пост в надежде, что увидят опытные реверсеры Сеги и вдруг у кого-то возникнут идеи.
Мне надо как можно точнее определить в роме инструкции BEQ/BNE (0x67/0x66), алгоритм сейчас такой:
0x67/0x66 по чётному адресу, смещение чётное, не указывает на себя и за пределы рома.
Следующие два байта и два байта по смещению - валидная инструкция (проверка по таблице). Обрабатывается и длинная форма инструкций, когда смещение нулевое.
В ситуации типа 66 00 67 02 (не длинный BEQ, а два байта данных и короткий BNE) сохраняются оба варианта.
pav13:
--- Цитата: perfect_genius от 08 Сентябрь 2025, 02:38:58 ---Sharpnull сжал сильнее, но код довольно сложный для глаз.
--- Конец цитаты ---
У него сжато только изначальное хранение. При проверке опкода на валидность используется массив на 8192 байта. Разница в том, что у тебя этот массив объявлен и инициализирован, а у Sharpnull он лишь объявлен и инициализируется отдельной функцией. Но массивы в кеше одного размера.
Беларус учит русский:
Точно, забыл, и переписал понятнее.
Навигация
Перейти к полной версии