Аппаратный взлом с использованием немаскируемого прерывания
© Владимир Куприянов, Свердловская обл. 1994
Поводом написать это письмо для меня послужила заметка В. Корчагина в разделе ФОРУМ, где он, в основном, рассказывает о своей прекрасной разработке "Хаккер-93". Автор сетует на отсутствие "полета мысли", "эффективного, ставящего в тупик приема" в статье Г. Луппова. И чуть ниже по тексту приводит "искусный приём" с
Я<ВЯ{ОФСК№ ФЛЗФЛ'БО^КЛ
командой RET. Да с каких это пор RET стал искусным приемом? Дожили. "Подавляющее большинство программистов" широко использует этот прием. Более того, есть ситуации, когда без него просто не обойтись -покопайтесь в TR-DOS. И "землю копать" хакеру все равно, в любом случае придется. Ведь остановить работающую программу мало - надо в ней разобраться. Вот тут Вам и "полет мысли".
Что касается программно-аппаратного взлома - эта тема мне знакома до боли. И здесь не всё так просто -нажал кнопку, и готово. Этой проблеме я в своё время посвятил много месяцев, и сейчас хочу поделиться своими идеями, многие из которых, кстати, проверены на практике. Может быть, кто-нибудь воплотит их в реально работающую систему.
Итак, первым делом нужно уяснить: при остановке программы по NMI нужно всячески избегать порчи содержимого ОЗУ. Почему - объяснять не надо. Далее, сама идеология Спектрума подразумевает нежелательность внесения каких-либо изменений в систему ПЗУ. Чем это чревато - знают многие. И дело, видимо, не в том, что фирменные программы проверяют ПЗУ, это по крайней мере глупо. Но вспомните про режим прерывания IM 2 - ведь вектор этого прерывания может быть установлен в том месте ПЗУ, которое подверглось изменению. В результате -зависание.
Так, где же разместить подпрограмму обработки NMI? Удобнее всего - в теневой памяти. Да, это приводит к необходимости аппаратной доработки компьютера. Но любой нормально работающий интерфейс - BETA-DISK, ZX LPRINT, Interface 2 и т.п. построен на этом принципе. Там реализованы теневые ПЗУ, я же предлагаю использовать теневое ОЗУ. Преимущества ОЗУ очевидны - его ведь можно модифицировать, там можно хранить всё, что угодно -содержимое регистров, экрана... 16 кб - не так уж и мало! И можно не трогать основное ОЗУ, что очень важно при остановке работающей программы. Единственный недостаток - после включения компьютера теневой монитор нужно загружать с внешнего носителя. Но ведь только один раз!
Коротко об аппаратном окружении. В исходном состоянии (после сброса) теневое ОЗУ должно быть отключено и защищено от записи. Для обеспечения загрузки монитора из основного ОЗУ в теневое необходим порт (например, с адресом 0), при обращении к которому можно активировать теневое ОЗУ, подключив его вместо ПЗУ, и снова привести все в исходный вид. Причем, проще и удобнее организовать этот порт так, чтобы, например, чтение из него отключало ПЗУ и активизировало теневое ОЗУ с разрешением записи в него, а запись в этот порт делала бы обратное. Кроме того, подключать ОЗУ на место ПЗУ необходимо и при нажатии кнопки NMI, то есть, когда /NMI=0, /MREQ=0, /RD=0 и /M1=0. Сама кнопка должна иметь триггерную защиту от дребезга, причем триггер должен сработать только когда /Ml неактивен, то есть, равен "1". Прилагаю схему, объясняющую вышеизложенное. Конечно, её необходимо доработать для конкретной модели компьютера.
MREQ-rd-
а0-а1-
_А2-
iorq-
WR-reset-
Блок-схема отладочного комплекса.
Теперь о самой программе. Точка входа по немаскированному прерыванию, как известно, 0066Н. Именно отсюда начинает работу монитор. Первым делом он должен сохранить значения всех регистров в рабочих ячейках теневого ОЗУ. При этом не следует забывать о регистре IFF2 - его значение командами LD A,I или LD A,R пересылается во флаг P/V. Разрешать или запрещать прерывания нет смысла, так как до выхода из монитора они и так запрещены и не мешают, а по команде RET N будет восстановлен регистр маски IFF1. Далее, нужно сохранить содержимое экрана (благо памяти для этого хватает) и очистить его. Теперь можно приступить к диалогу с пользователем, не забыв переставить стек.
Некоторые ухищрения придется применить при выходе из монитора в программу для её продолжения с прерванного места. После восстановления содержимого экрана и всех регистров, в том числе SP, необходимо дать команду OUT (0),A. Эта команда обязательно должна находиться по адресу 0070Н. После её использования произойдет переключение с теневого ОЗУ на ПЗУ, и далее, уже в ПЗУ, процессор по адресу 0072Н обнаружит команду RET N, которая вернёт его к прерванной основной программе.
К сожалению, описанная идеология не позволяет организовать вход в монитор-отладчик путем установки в программе точек прерывания. Можно, правда, изменить схему реагирования на NMI, подав на верхний по схеме вывод элемента D2 не сигнал /NMI, а другой сигнал, принимающий значение логического "0" только тогда, когда на шине данных появится адрес 0066H. Но это заметно усложнит схему, поскольку придется контролировать все 16 адресных линий. Но игра, видимо, стоит свеч. Ведь достаточно будет записать в программу JP 0066H в качестве точки прерывания, либо, что менее надежно, записать в стек 0066H, а точкой прерывания будет RET. Разумеется, при входе в отладчик по точке останова необходимо восстанавливать имевшиеся ранее значения измененных байтов.
Что должен уметь резидентный монитор-отладчик? Здесь все зависит от фантазии и опыта программиста. Безусловно, необходимы такие команды, как DISASSEMBLE, DUMP, LIST, EDIT, MOVE, SEARCH. Очень кстати будет возможность работы с внешними накопителями - магнитофоном и дисководом. Кстати, о TR-DOS. Там нужно удалить все цепи, относящиеся к NMI, то есть кнопке "MAGIC", во избежание конфликта с монитором. Кроме того, для доступа к контроллеру 1818ВГ93 и регистрам управления необходимо изменить логику их выборки при активном теневом ОЗУ. Это не так сложно.
Поскольку функция "MAGIC SHOT" TR-DOS заблокирована, необходимо, чтобы монитор мог её имитировать, то есть отгружать на ленту или диск все 48 кб ОЗУ, предваряя этот блок Бейсик-загрузчиком, который бы занимался установкой регистров и режима прерывания (IM) перед входом в программу. Здесь тоже без ухищрений не обойтись, но проблема решаема. Между прочим, одним из главных недостатков процессора Z80 является то, что невозможно достоверно определить режим прерывания в конкретный момент времени. Но применительно к Spectrum задача упрощается тем, что режим IM1 практически не применяется, так как он ничем не отличается от IM0. И если монитор-отладчик будет иметь команду для смены режима маскируемого прерывания, то проблему, возникавшую при использовании "MAGIC SHOT" TR-DOS, можно снять путем косвенного определения текущего режима. В конце концов, можно выгрузить программу дважды - с режимом IM0 и IM2. Работоспособной окажется одна из копий.
Если позволит место (10 кб для кода не так уж много), в отладчик можно встроить всевозможный сервис, например, просмотр сохраненного экрана, его распечатка на принтере или сохранение на внешнем носителе; поддержка принтера с возможностью переключения его шрифтов и режимов; визуализация памяти в битовой форме (для просмотра символьных наборов и спрайтов); преобразование HEX - DEC и наоборот; калькулятор и т.д.
Вообще, создание описанного программно-аппаратного отладочного комплекса - задача очень интересная, но и сложная. Ведь в Вашем распоряжении уже нет ПЗУ - абсолютно всё монитор должен делать сам. Вывод на экран, опрос клавиатуры, ввод-вывод на магнитофон, прямое программирование контроллера дисковода... Полностью автономная программа! Вот Вам и простор для "полета мысли". Создайте СИСТЕМУ.
623418, Свердловская обл., г. Каменск-Уральский, пр. Победы, Д.78, кв.23
Куприянов Владимир.
* * *