Привет всем!
Вам предлагается маленький быстрый туториал по восстановлению прошивки GameBoy. Начнём с фотографий, изображающих прошивку металлическими соединениями (mask ROM) от Nintendo GameBoy, и на выходе получим файл ROM, который можно будет дизассемблировать или эмулировать.
Гаджет GameBoy для этого хорошо подходит, поскольку в нём используется так называемая «постоянная память, программируемая перемычками» (Via ROM). Это означает, что отдельные биты кодируются металлическими перемычками между слоями, и эти биты можно считывать с поверхности чипа. Кроме того, сама прошивка достаточно невелика, и поэтому я смог включить её в репозиторий на Github, тем самым сэкономив вам недели времени, которые вы могли бы потратить на исправление мелких побитовых ошибок.
Привет из Ноксвилла,
— Трэвис Гудспид
Фотография
Начнём со снимка dmg01cpurom.bmp, который я сделал в домашней лаборатории, перед этим декапсулировав чип в азотной кислоте и очистив его в ацетоне на ультразвуковой бане. Такой чип не требует расслаивания во фтороводородной кислоте или окрашивания битов при помощи раствора Dash Etch, состоящего из азотной кислоты, фтороводородной кислоты и уксусной кислоты.
Фотография была получена под моим металлургическим микроскопом в виде 22 кадров с 50-кратным увеличением, которые затем сшивались при помощи Hugin. Биологический микроскоп в данном случае не сработает, так как кремниевая подложка непроницаема для видимого света. Напротив, под металлургическим микроскопом свет отражается от образца, а не проникает через него.
Мой исходный снимок получился слишком большим (и его не получилось выложить на Github), поэтому вам для работы предлагается версия с пониженным разрешением. Обошлось без сжатия с потерями, поскольку такая техника может привести к путанице при распознавании изображения.
Если вы сами сфотографируете чип DMG-01-CPU, то и с ним будут применимы те инструкции, что изложены ниже.
Извлечение битов
В этом руководстве мы будем извлекать биты при помощи MaskRomTool, моей CAD-программы, позволяющей очень быстро выполнять именно такие операции.
Для начала скомпилируем MaskRomTool и установим его. Если вы на «ты» с командной строкой unix, то должны устроить, чтобы исполняемый файл maskromtool
оставался у вас на $PATH
. Если нет — просто пропустите эту часть, поскольку все возможности доступны и через GUI.
Запустив эту программу, откройте при помощи MaskRomTool файл dmg01cpurom.bmp
.
Обратите внимание: за мышкой следует серый крестик. Сначала он ровный, но постепенно его очертания начнут меняться, чтобы соответствовать строкам и столбцам, которые вы накладываете на прошивку. Таким образом, с одной стороны монитора просматривается, какие биты окажутся на другой стороне монитора.
Также обратите внимание, что биты в прошивке идут парами регулярных строк и группами по восемь регулярных столбцов. В MaskRomTool вы будете расставлять строки и столбцы, но у нас нет строгих правил, согласно которым эти группы должны были бы оказаться точно такими же, как и в исходном изображении. Если вы обнаружите, что можно надёжно расположить огромную строку, которая пересечёт картинку по всей ширине или, наоборот, столбец во всю высоту изображения — делайте так, не сомневайтесь.
Навигация
Расстановка строк
Начнём с нескольких коротких строк, а позже попробуем с длинными. Сначала щёлкните мышью левее самого крайнего левого бита, а затем сдвиньте курсор вправо, но НЕ щёлкайте. Вместо этого нажмите клавишу R, чтобы поставить строку (Row). Между двумя этими точками протянется тонкая чёрная линия.
Можно проделать это столько раз, сколько потребуется, и расставить все строки, но это обременительно, а при работе с самыми длинными строками вам придётся сильно их прокручивать. Лучше не отодвигайте мышь от правого края, а просто потихоньку сдвигайте вниз. Когда «прицел» сравняется с нужной строкой, нажмите Shift+R или пробел, чтобы поставить ещё одну строку.
Если повторить эту операцию с множеством строк, то очень скоро все они окажутся размечены. На следующем скриншоте я разметил короткие сегменты первых восьми строк.
Как только вы наловчитесь расставлять повторяющиеся строки, придерживаясь правого края, вы также сможете разметить их по всей картинке — это сэкономит вам время и силы. Если не получается добиться, чтобы прямые линии пересекали изображение целиком, то размечайте более короткие участки или, ещё лучше, скорректируйте панораму, с которой делаются фотографии.
Расстановка столбцов
Притом, что вы уже успели разметить несколько столбцов, программа по-прежнему «не знает», где находятся разряды, так как вы пока не разметили ни одного бита.
Чтобы отметить первый столбец, сначала щёлкните мышью над верхним битом вашего первого столбца. Далее, как и при работе со строкой, опустите курсор мыши под последний разряд в столбце и нажмите Shift+C, чтобы провести линию столбца (повторно НЕ щёлкайте.)
Повторяйте так далее, размещая курсор под следующими крайними битами и нажимая Shift+C. По мере того, как каждый столбец протягивается через строки, над каждым битом будут появляться синие квадратики. Программа уже знает, где находятся биты, а далее нам требуется научить её отличать нули от единиц.
Распознавание битов
Чтобы выявить биты, программа должна располагать не только их позициями, которые вы предоставили, но и пороговым значением, а также цветовым каналом для различения битов. Щёлкните «Edit» (Редактирование), а затем «Choose Bit Threshold» (Выбрать битовый порог). В результате увидите следующую диаграмму и сможете выбрать на нём устраивающее вас пороговое значение.
На этой гистограмме для первых 64 битов заметны пробелы во всех цветовых каналах. Самый большой пробел — в зелёном канале, поэтому для этого канала я установил бы пороговое значение 172. Обратите внимание, как битовые рамки автоматически меняют размер, когда вы корректируете пороговое значение.
Хотя синий и красный каналы для этого изображения выглядят сносно, нас интересует именно зелёный канал, так как именно в нём самый большой пробел. Когда мы начинаем добавлять биты на изображение, нужно подобрать цветовые различия побольше: так мы гарантированно получим меньше побитовых ошибок.
Установив первые шестьдесят четыре бита, щёлкните «View» (Просмотр) и «ASCII Preview» (Предпросмотр в ASCII), чтобы они стали видны. Симпатично, правда?
11101011
01111111
00110111
01101111
10110001
01101110
01011100
10110101
Ещё больше битов
Теперь, напрактиковавшись в распознавании первых шестидесяти четырёх битов, давайте распознаем все биты на картинке.
Сначала сотрём всю нашу предыдущую работу. Для этого можно либо удалить dmg01cpurom.bmp.json и открыть dmg01cpurom.bmp в заново вызванном Mask ROM, либо скопом стереть все ваши строки. Чтобы стереть строки скопом, выделите их, потащив за край, а потом вырежьте сразу все, нажав Shift+D.
(Строго говоря, также можно было бы разметить изображение множеством очень мелких строк и столбцов. Единственная причина, по которой я не рекомендую так делать — не потому, что метод не сработает, а потому, что на это может уйти целая вечность.)
Стерев все строки, поставьте новую, которая протянется с левого края экрана до правого. Если вы решите затирать ранее сделанную работу, а не удалять её предварительно, то в таком случае помогает начать со сравнительно короткой строки, чтобы наклон курсора-крестика соответствовал наклону изображения. Далее можно попытаться проделать наклон с каждой строкой при помощи R, затем удаляя ненужное при помощи D, а программа всё равно не забудет, какая позиция была начальной.
Начертив первую линию, протяните курсор мыши по правому краю фотографии прошивки, нажимая Shift+R или пробел всякий раз по завершении очередной строки, чтобы заложить ниже ещё одну линию. Если заметите, что, отмечая бит, то поможет клавиша S: с её помощью устанавливается позиция последней линии, эта линия переносится мышкой на новое место.
Начертив длинные строки, начинайте чертить столбцы. Чтобы задать начальную точку, просто однократно щёлкните над каждым верхним битом, а затем, удерживая C, протяните вниз, чтобы получился столбец. Если нажать Shift+C, то будет заложен новый столбец под тем же углом на другой крайней точке. Поэтому можно таким образом пройти через всю картинку и быстро начертить все столбцы.
Когда установите все биты, воспользуйтесь командами «Edit / Choose Bit Threshold»(Редактирование)/(Выбрать битовый порог), чтобы этот порог немного откорректировать. Обратите внимание, что с таким большим количеством битов кривая получается гораздо более плавной, и поэтому может быть целесообразнее остановиться на 151, а не на 172.
Находим и устраняем ошибки
Очень приятно чувствовать, что все эти биты размечены, но обычно работа на этом не заканчивается. Прежде чем продолжать расшифровку прошивки, хорошо бы провести несколько быстрых проверок работоспособности и убедиться, что мы не допустили никаких ошибок.
Клавишей V или командой «DRC/Evaluate Rules» (Проверка правил проектирования/Оценить правила) можно по-быстрому проверить, насколько грамотно вы всё спроектировали. Например, вдруг вы неправильно расположили линию, и цвет бита оказался подозрительно близок к пороговому значению? Можете получить примерно такую ошибку DRC, а решается она путём коррекции расстановки линий. При каждом нарушении, выявляемом при проверке правил проектирования (DRC) позиция, в которой допущена ошибка, указывается в GUI жёлтым квадратиком.
Такие ошибки, как неправильное расположение линий — это, конечно, ваша вина, но исправлять нужно и те ошибки, к которым вы непричастны. Иногда на фотоснимок может попасть пылинка, скрадывающая значение бита в определённой точке. Иногда из-за ошибки с реактивами бит может просматриваться не слишком хорошо.
В таких ситуациях, когда вам явно виднее, чем машине, нажмите Shift+F, когда мышь наведена на бит, чтобы принудительно задать его значение, и поместите бит в зелёный квадратик. Если будет применено неверное значение, вновь нажмите Shift+F и обратите бит.
Когда всё это будет сделано, работа над битами, наконец, завершена!
11101011111100101100101100110010011000110111110000100001011100101110000000110011110001001001001011000100001011110001101100001000
01111111011100110111111101100011001010110100111100000110101010110011001111110011001010111011000000111000001011101101011011101111
00110111110110110111011111010111011001101001011101111111110110100111000101110010010110000101011101110001111101110111101111011000
01101111111000111110111011110110001011100101011010100010001110000111100000111010011111000111001100101111010000110100111111101001
10110001001100001011100110110111000110011101100111100000111100111111011010110001111111100111011010111010000110100100001100010011
01101110011100110110100100110111011100110101101001001111111100110010111110100011001110011010011101111010000111100111001010110010
01011100011101111111110001110111101100000101101100111000011100010011000011110101001100001011111000110000100110010111011111010010
10110101110101111011101001011111001110101111101000010101111100011101011111000011111010111010101100001110011110011011011111000101
00011111000101000001101110011110101111001111000011111011011100000100011011010000110100111001100100110011110101000101101110110110
11011001000101100111100100011011001110010101010100001101111101110110010110000111010101101101111010101100101100101101111110010111
11111111111110011111101101010101001101111101000010100110101010011011010011111001101101001101010110101010010101011110010110100011
01011110111110100001111011010110001000011101000000111011001011100101001111101110100110110000101000101011100001000001111000100001
11011000100010111011110010010101001011000111000010011000111110001111011011000000100111001101010010001100111101100010100110111001
11111011001010011111101000111101001100101111100110110101011111011011110110000101001011001101000100110111001101011110110110001010
00111111111101100011101111110010001011001111010000011111101111010011011111110111001101110111010101011111111110110101011100111111
10101101111101111000110111110110100001010111100111001101101110100100111111000011011110101010001101011100100011111100111110011111
О CLI
До сих пор мы работали с нашим инструментом через графический пользовательский интерфейс (GUI). Но, когда переходишь к сравнительно крупным проектам, бывает желательно автоматизировать некоторые вещи через CLI (интерфейс командной строки). По умолчанию при работе в автономном режиме лучше задать -platform offscreen -e
, чтобы избежать открытия окон, а также выйти из программы по завершении работы.
Usage: maskromtool [options] image json
Mask ROM Tool
Options:
-h, --help Displays help on commandline options.
--help-all Displays help including Qt specific options.
-v, --version Displays version information.
-e, --exit Exit after processing arguments.
--opengl Enable OpenGL. (Not yet stable.)
-d, --drc Run default Design Rule Checks.
-D, --DRC Run all Design Rule Checks.
--diff-ascii <file> Compares against ASCII art, for finding errors.
-a, --export-ascii <file> Export ASCII bits for use in ZorRom.
--export-csv <file> Export CSV bits for use in Matlab or Excel.
--export-json <file> Export JSON bit positions.
--export-python <file> Export Python arrays.
--export-marc4 <file> Export MARC4 ROM banks, left to right.
--export-arm6 <file> Export ARM6L (MYK82) ROM.
--export-photo <file> Export a photograph.
Arguments:
image ROM photograph to open.
json JSON lines to open.
Декодирование прошивки
Теперь, когда ваш проект размечен, и все биты выглядят точно, потребуется передать файл с битами в другие инструменты, чтобы можно было его декодировать. Лучше всего для этой цели подходит Zorrom — коллекция скриптов Python, собранная Джоном Макмастером.
Декодирование при помощи Zorrom
Для начала нам понадобится ASCII-файл, в котором представлены все биты прошивки. В GUI инструмента MaskRomTool
этот файл можно сгенерировать командами File / Export / ASCII (Файл / Экспорт / ASCII), а из CLI та же операция делается так: maskromtool -platform offscreen dmg01cpurom.bmp -a DMG_ROM.txt -e
.
Далее, как описано в документации Zorrom, можно приказать Zorrom представить нам в декодированном виде все те биты, первый байт которых равен 0x31
. (В GameBoy 0x31
— это код операции, позволяющий установить значение стека вызовов и, логично предположить, что это первая инструкция в прошивке).
air% ./solver.py --bytes 0x31 DMG_ROM.txt DMG_ROM
Loaded 128x x 16 h => 2048 bits (256 words)
66 match True, score 1.000
r-180_flipx-1_invert-1_cols-left
69 match True, score 1.000
r-180_flipx-1_invert-1_cols-downr
Tries: 80
Best score: 1.000, r-180_flipx-1_invert-1_cols-left
Keep matches: 2
Writing DMG_ROM/r-180_flipx-1_invert-1_cols-left.bin
Writing DMG_ROM/r-180_flipx-1_invert-1_cols-downr.bin
air%
Имея два потенциальных значения, можно дизассемблировать каждое из них, чтобы найти верное. И действительно, r-180_flipx-1_invert-1_cols-downr.bin
содержит нужные байты, так как загружает 0xFFFE
в указатель стека.
air% r2 -a gb r-180_flipx-1_invert-1_cols-downr.bin
[0x00000000]> pd 1
0x00000000 31feff ld sp, 0xfffe
^D
air% r2 -a gb r-180_flipx-1_invert-1_cols-left.bin
[0x00000000]> pd 1
0x00000000 311147 ld sp, 0x4711
^D
air%