5. ЗАЩИТА ЗАГРУЗЧИКОВ.
Все игры имеют очень хорошо защищенную программу, написанную на
БЕЙСИКе, т.к. это - важнейший (с точки зрения действенности защиты)
элемент всей программы - ведь с БЕЙСИКа начинается считывание всей
программы. Если БЕЙСИКовский загрузчик защищен слабо, то взлом всей
программы значительно облегчается. (Примером этого являются загрузчики
фирмы ULTIMATE, представленные в предыдущем разделе. Одним из спосо-
бов снятия защиты загрузчиков является их считывание с помощью прог-
раммы "LOAD/MERGE" (см. раздел 2). Однако иногда лучше поместить этот
загрузчик не в памяти, предназначенной для БЕЙСИКа, а выше RAMT0P,
чтобы можно было спокойно просматривать его, не опасаясь возможности
случайных изменений в нем.Для этого имеется очень действенный метод: считывание программы
на БЕЙСИКе к$к блока машинного кода по удобному для нас адресу. Чтобы
добиться этого, требуется знать длину программы, которую мы хотим
считать (можете использовать процедуру "CZYTACZ" из раздела 2), хотя
можно обойтись и без длины. Кроме того требуется немного свободного
места на магнитной ленте. Этот способ основывается на обмане инструк-
ции LOAD путем подмены заголовка.На свободном месте ленты записываем заголовок блока кода с по-
мощью SAVE "BAS" CODE 30000,750, если знаем, что длина программы сос-
тавляет 750 байт. Если не знаем, то подаем соответсвенно большее зна-
чение - порядка нескольких килобайт или даже десятков килобайт, хотя
программа может иметь всего лишь 100 байтов длины. На ленте записыва-
ем только сам заголовок, прерывая запись после этого нажатием клавиши
"BREAK". Теперь устанавливаем ленту точно перед записанным заголов-
ком, а ленту с программой - сразу после заголовка программы, но перед
требуемым блоком данных. ВводимCLEAR 29999: LOAD "" CODE
и считываем заголовок. Сразу после его считывания нажимаем STOP на
магнитофоне, заменяем кассету и вновь нажимаем PLAY (все это время
компьютер ждал блок данных). Теперь считывается программа на БЕЙСИКе,
но по адресу 30000 (выше RAMT0P). Если мы указали в заголовке завы-
шенную длину программы, то считывание кончается "ТАРЕ L0ADIND ERROR",
но это не мешает - теперь уже можно смотреть считанную программу лю-
бым способом, даже набирая с этой целью программу на БЕЙСИКе.Кроме этого метода существует и второй, но для того чтобы им вос-
пользоваться, обязательно знание АССЕМБЛЕРа (а пользоваться стоит, т.
к. он дает большие возможности в раскрытии программ).Очень часто (особенно в новейших программах) встречаются блоки,
записанные и считываемые в память компьютера без заголовка. Это дос-
таточно оригинальная и эффективная мера защиты обычно отпугивает на-
чинающих, но раскрытие такой программы вовсе не является трудным. Вся
тайна основана на использовании хранящихся в ROM SPECTRUM процедур,
используемых инструкциями LOAD, SAVE, VERIFY и MERGE.По адресу #0556 (1366) находится процедура LOAD^BYTES, считываю-
щая с магнитофона блок данных или пилот и следующую за ни минформа-
цию. При этом неважно - будет ли это заголовок или требуемый блок
данных, которые следует разместить где-то в памяти.Начнем асе же с начала. Каждая зачитанная программа начинается с
загрузчика, написанного на БЕЙСИКе. Программа, применяющая загрузку
без заголовков (с помощью процедуры 1366 или другой), должна быть на-
писана в машинном коде, как всякая процедура, обслуживающая магнито-
фон. Чаще всего эта программа помещается в одной из строк БЕЙСИКа,
например после инструкции REM или в области переменных БЕЙСИКа. После
считывания загрузчик на БЕЙСИКа запускается и выполняет инструкцию,
RANDOMIZE USR инициируя тем самым работу машинной программы.Процедура LOAD_BYTES требует соответствующих входных параметров.
Они передаются в соответствующих регистрах микропроцессора. Так, в
регистре IX указываем адрес, по которому хотим прочесть блок данных,
а в регистре DE - длину этого блока. В аккумулятор помещаем 0, если
хотим считать заголовок и 255, если это - блок данных. Кроме того,
указатель переноса (CARRY) устанавливаем в 1, т.к. иначе процедура
1366 вместо LOAD выполнит VERIFY. Ниже дан пример процедуры, загружа-
ющей с ленты образ экрана без заголовка:
LD IX,16384 ;адрес считывания
LD ОЕ»6912 ;длина блока
LD А,255 ;блок, а не заголовок
SCF ' ;LOAD, а не VERIFYCALL 1366 ;вызов L0AD8YTES
RET ;во$врат из п/пПроцедура 1366 в случав ошибки считывания не выводит сообщение
"ТАРЕ LOADING ERROR", но существует еще одна процедура загрузки, ко-
торая это делает. Она находится по адресу 20Б0 и выглядит так:
2050 CALL 1366 «-считывание блока данных2053 RET С ;возврат, если не было ошибки
2054 RST 8 ;иначе RST 8 с сообщением
2055 DEFB 26 ;"ТАРЕ LOADING ERROR"
После возврата из процедуры 1366 указатель переноса содержит ин-
формацию о правильности считывания блока. Если он равен 0, то это
сигнализирует об ошибке. Некоторые загрузчики используют именно про-
цедуру 2050, а не 1366.Иногда загрузчики не пользуются ни той, ни другой процедурой, а
заменяют их собственной. Однако, она часто является просто переделкой
.1366: например, блоки загружаются от больших адресов к меньшим или
загрузка идет с другой скоростью. Такую программу следует анализиро-
вать с помощью дизассемблера (например HONS), сравнивая ее фрагменты
с тем, что находится в ROM.Сейчас мы объясним, как использовать процедуры из ROM для считы-
вания из БЕЙСИКа под любой адрес, а не в область, предназначенную для
него: сначала с помощью "CZYTACZ" прочитаем заголовок программы, ко-
торую мы хотим взломать, и запомним ее длину (т.е. длину всего блока
- программы вместе с переменными). Затем введем соответствующую прог-
рамму на ассемблере, которая прочитает БЕЙСИК-програиму по адресу,
который мы зададим (выше RAMT0P):
LD IX,addr
LD DE,len
LD A,255
SCFJP 2050
Так же, как и при подмене заголовка, можем указать завышенное
значение длины программы (если не знаем истинного значения), но вэтой случае чтение завершится сообщением "ТАРЕ LOADING ERROR", Чтобы
не считывать АССЕМБЛЕР всякий раз для ввода вышеприведенной програм-
мы, создадим эту программу из БЕЙСИКа с помощью РОКЕ:
10 INPUT "АДРЕС ЧТЕНИЯ 7"; А
20 RANDOMIZE A: CLEAR А-1
30 LET А-РЕЕК 23670: LET В-РЕЕК 23671
40 LET ADR*256*B+A
50 INPUT "ДЛИНА ?"; С
60 RANDOMIZE С: LET С-РЕЕК 23570
70 LET D«PEEK 23671
80 FOR N-ADR TO ADR*11
90 READ X; POKE N,X: NEXT N
110 DATA 221,33,A,8Д7,С,0,62,255,125,2,8
120 RANDOMIZE USR ADRУстанавливаем ленту с обрабатываемой программой за ее заголовком,
затем запускаем вышеприведенную программу, вводим данные и включаем
магнитофон. Результат аналогичен тому, который получаем при подмене
заголовков, но первым же видимым достоинством этого способа является
то, что мы не создаем беспорядка на кассетах.В завершение стоит вспомнить еще об одной процедуре, размещенной
в ROM по адресу 1218. Это процедура SAVE_BYTES3 обратная LQAD_BYTES,
т.е. записывающая на ленту блок о заданными параметрами: перед ее вы-
зовом в регистре IX размещаем адрес, с которого начинается записывае-
мый блок, DE должен содержать длину этого блока, в аккумуляторе поме-
чаем, должен ли это быть заголовок (0) или же собственно блок (255).
Состояние указателя CARRY значения не имеет.