ZX-Spectrum изнутри 1969 г.

Глава 6 - использование системных процедур.


6. ИСПОЛЬЗОВАНИЕ СИСТЕМНЫХ ПРОЦЕДУР.

В предыдущем разделе были представлены процедуры из ROM:
L0AD_8YTES и SAVE BYTES. Здесь рассказывается, как эти процедуры ис-
пользуются для защиты программ. Займемся блоками машинного кода, ко-
торые запускаются удивительным образом.

Первым таким способом является прикрытие загрузчика блоков, кото-
рый им загружается, Такая защита, к примеру, применяется в игре "Три
недели в Парадиз-сити". Проследим способ чтения этой программы, не
вызывающий ее автоматического запуска.

Начнем, как обычно, с- БЕЙСИКа. Оказывается, практически единс-
твенно важной инструкцией является RANDOMIZE USR. Лучше всего восста-
новить эту процедуру, начиная с адреса запуска (РЕЕК 23627+25б*РЕЕК
23628), т.е. в нашем случае с адреса 24130. Подобные процедуры ис-
пользуют известную нам процедуру 1365, считывающую блоки без заголов-
ка . Так и в этом случае, но перед ее вызовом процедура загрузки пере-
носит сама себя в конец памяти (по адресу 6311Б)с помощью команды
L0IR и переходит туда сама по команде JP:

24130 01 ;запрет прерываний

24131 LD 5Р,0 ;ID SP.65536

24134 LD HI,(23627} ;в HL адрес на 28 больше, чем

24137 LD 0[,ге ;значение переменной VARS

24140 ADD Hi,0Е ;

24141 LD DE,63116 ;а DE - адрес, а в
24144 LD ВС,196 ;ВС - длина блока
24147 L0IR ;

24149 JP 63116 ;продолжение выполнения

Теперь считывается картинка, а затем - главный блок данных;

63116 LD IX,16384 подготовка загрузки картинки

63120 LD DE.6912 ;на экран с помощью процедуры

63123 LD А,255 ;L0ADBYTES из ROM

63125 SCF ;

63126 CALL 1366 ;

63129 LD IX,26490 параметры главного блока

63133 LD DE,38582 программы, который при своей

63136 LD А,255 ;загрузке затирает эту процедуру

63138 SCF ;

63139 CALL 1366

63142 JP NZ,+79 ;после возврата из процедуры

63144 CP А ;1366 здесь ухе находится

63146 CALL 65191 ;другая программа

Как Вы знаете, каждая инструкция CALL заносит в машинный стек ад-
рес, с которого продолжает работать программа после выхода из подп-
рограммы. В этом загрузчике после выполнения второй инструкции CALL
1366 в стек заносится адрес команды, следующей за CALL, т.е, 63142, и
процедура загрузки самым обычным способом затирает сама себя, т.к.
считывает байты с магнитофона в ту область памяти, где она была раз-
мещена. Существенное значение имеет способ запуска считываемой прог-
раммы: процедура 1366 кончается, естественно, инструкцией RET, кото-
рая означает переход по адресу, записанному в стек. В нашем случае
это 63142. В процессе считывания программы процедуре, которая находи-
лась там, была заменена считанной программой. Но микропроцессор этого
не замечает - он возвращается по адресу, с которого выполнен CALL
1366, не обращая внимания, что там находится уже совершенно другая
программа. Это схематично представлено на рис.7:

С левой стороны расписано содержимое памяти до, а с правой - пос-
ле считывания программы.

Возникает вопрос, как распознать защиту данного типа и как ее
ликвидировать. Начинаем, естественно, с БЕЙСИКа. Считываем загрузчик
(в АССЕМБЛЕРе) и определяем адреса окончаний считанных блоков, добав-
ляя к адресу начала (регистр IX) длину блока (регистр DE). Есак какой
-либо из блоков накрывает процедуру загрузки, то это означает, что
программа считывается и запускается именно этим способов

Дальше все просто* Достаточно, опираясь на данные о блоках (адрес
и длину), написать коротенькую процедуру, загружающую интересующий
нао»блок кода» мам подготовить соответствующий заголовок, затем с по-
нощью CLEAR ADR установить соответствующим образом машинный стек
(чтобы считываемая программа не уничтожила стек) и, наконец, считать
программу. После выполнения в ней необходимых изменений записываем ее
на ленту, но таким же образом, каким был записан оригинал (длина
должна совпадать прежде всего). Если этот блок был без заголовка (а
так и есть в нашем случае), то записываем его обычным SAVE **...** CODE
..., но опуская заголовок, т.е. включаем магнитофон в перерыве между
заголовком и блоком кода. Также можно попробовать запустить считанный
блок переходом на требуемый адрес командой RANDOMIZE USR ..., но это
не всегда может получиться. В игре "Три недели в Парадиз-сити" этим
адресом, конечно, будет 63142 и, как Вы можете убедиться, этот метод
срабатывает.

Другим интересным способом запуска блоков машинного кода является
считывание программы в область машинного стека. Этим способом можно
запускать блоки машинного кода, загружая их просто через LOAD ""
CODE. Этот метод схематично представлен на рис.8.

Указатель стека (регистр SP) принимает показанное на рис.8 состо-
яние в процессе выполнения процедуры 1366 (вызванной из БЕЙСИКа через
LOAD "" CODE). Способ запуска программы весьма прост. Адрес считыва-
ния блока рассчитан так, что блок считывается на машинный стек именно
с того места, в котором находится (записанный интерпретатором БЕЙСИ-
Ка) адрес возврата из инструкции LOAD "" CODE (он тогда равен значе-
нию системной переменной ERRSP-2) или прямо из процедуры LOAD_BYTES
(в этом случае он равен ERRSP-6). Тогда два первых байта программы
обозначают адрес ее запуска. Этот способ очень похож на предыдущий,
за исключением того, что там подменялась процедура загрузки, а здесь

- адрес возврата из этой процедуры или просто адрес возврата из инс-
трукции LOAD. После считывания блока кода микропроцессор считывает
содержимое стека и переходит по прочитанному адресу, который только
что появился в памяти вместе со считанной программой. По этому адресу
в программе находится начало процедуры загрузки ее последующих блоков

- это видно на рис.8.

Метод обхода этой защиты также весьма прост. Достаточно заменить
RAMTOP на более низкое значение, а затем прочитать блок кода, который
благодаря этому не запустится. Ситуация может осложниться, если блок
очень длинный (что редко, но случается) - тогда мы должны поступить с

ним так же, как с любым длинным блоком, но помнить, с какого адреса
он запускается.

Займемся теперь расчленением блоков кода с длиной, превышающей
42К. Взлом блоков этого типа основан на разделении их на такие фраг-
менты, чтобы в памяти еще осталось место MCNSa или другого ДИЗАССЕМБ-
ЛЕРА; исправлении этих фрагментов и их склеивании в одно целое (или
написание новой программы загрузки). Обычно достаточно разделить'
длинный блок иа две части. Чтобы получить первую, используем процеду-
ру 1366, но с другими параметрами (не с теми, которые требует разде-
ляемый на части блок). Ранее из процедуры загрузки или (если такой
нет) из заголовка этого блока получаем его длину и адрес загрузки. В
качестве адреса загрузки указываем величину выше RAMT0P, а длину - 16
К.' Считываем теперь этот блок через CALL 1366 или CALL 2050, но в
обоих случаях появившееся сообщение "ТАРЕ LOADING ERROR" не даст нам
никакой информации о верности считывания - загружаем лишь часть блока
(следовательно - без контрольного байта). Считанную таким образом
первую часть блока записываем на ленту и сразу хе приступаем ко вто-
рой части. Ее считывание труднее, но тоже возможно, несмотря на огра-
ничение по памяти. Достаточно использовать тот факт, что у SPECTRUM
существует 16К ROM, запись в которую просто невозмохна. Итак, вызыва-
ем процедуру 1366 с адресом чтения 0, и начальные 16К считываемого
блока будут потеряны, а в память RAM считаются только следующие 32К
или меньше (в зависимости от длины блока). Считываемый блок займет в
памяти адреса с 16384 и дальше, заходя на системные переменные и ос-
тавляя без изменения лишь те байты, адреса которых бэльше длины бло-
ка. Поэтому необходимо позаботиться о том, чтобы машинный стек, а
также написанную нами процедуру загрузки разместить в конце памяти.
Надо также помнить о том, что система БЕЙСИКа будет уничтожена, и за-
писать считанный блок на ленту можно будет только процедурой, напи-
санной на АССЕМБЛЕРе. Кроме того, в промежутках времени между считы-
ванием фрагмента блока и его записью нельзя разблокировать
прерывания, т.к. они изменяют содержимое ячеек памяти с адресами
23552..23560, а также 23672..23673. Чтобы выполнить последнее усло-
вие, войдем в середину процедуры 1366, благодаря чему прсле считыва-
ния блока не будет выполнена процедура 1343. Именно она и разблокиру-
ет прерывания. С помощью CLEAR 64999 переносим машинный стек, а с
адреса 65000 помещаем процедуру загрузки:
ORG 65000

LD IX,0 ;адрес считывания

LD DE,DL ;длина блока

LD А,255 подготовка к

SCF считыванию блока

INC D ;таким способом

EX AF,AF* ;заменяем начало

DEC 0 ;процедуры 1366,

01 ;а затем входим

LD А,15 ;в ее середину

OUT (254),А ;
CALL 1378 ;

LD А,0 ;черная рамка

JR С,0К .'будет означать

LD А,7 ;верное считывание,

• OK OUT (254),А ;белое - ошибочное

CZ LD A, 191 ;ожидаем нажатия

IN A,(254) ; "ENTER"

RRA ;

JR C,CZ ;

.LD IX,0 ,'запись считанного

LD DE,DL-16384 ;блока кода

LD A,255 ;на ленту,

CALL 1218 ;a также

LD HL,64999 ;инициализация

JP 4633 ;системы БЕЙСИКа

Вместо того, чтобы считывать АССЕМБЛЕР и вводить эту программу,
можно запустить программу на БЕЙСИКе, представленную на листинге:
10 CLEAR 84999

20 INPUT "ДЛИНА БЛОКА?"; DL: RANDOMIZE DL:

LET Х=РЕЕК 23670: LET Y-PEEK 23671: LET S-0
30 FOR N=65000 TO 65053: READ A: LET S=S+A:P0KE N,A: NEXT N
40 IF S<>5254+2*(X+Y)-64 THEN PRINT "ОШИБКА В ДАННЫХ": STOP
50 PRINT "ВСЕ ДАННЫЕ O'K, ВКЛЮЧИТЕ МАГНИТОФОН"
60 RANDOMIZE USR 65550
70 DATA 221,33,0,0,17,X,Y,62,2,55,55,

20,8,21,243,62,15,211,254
80 DATA 205,98,5,62,0,56,2,62,7,211,254,

62,191,219,254,31,56
90 DATA 249,221,33,0,0,17,X,Y-64,62,255,205,
194,4,33,231,253,202,25,18

Как выглядит разделение блока? Устанавливаем ленту на блоке кода,
который желаем разделить. Если он имеет заголовок, то его опускаем.
Запускаем процедуру и включаем магнитофон. Не пугайтесь, если в опре-
деленный момент увидите, что программа считывается на экран - так и
должно быть. После загрузки блока цвет рамки сигнализирует правиль-
ность считывания: если рамка черная, то все в порядке, если белая -
была ошибка. Вложите в магнитофон другую кассету, включите запись и
нажмите "ENTER". Программа запишется на ленту, а потом процедура вер-
нет управление в БЕЙСИК, инициализируя систему с сообщением "(С) 1982
...", но не очищая память, (уничтожается только область от начала эк
рана до адреса примерно 24000). Теперь, подготавливая заголовок или
записывая коротенькую процедуру, можно прочесть полученный блок по
любому адресу.

Когда Вы найдете то, что искали и захотите запустись измененную
программу, то придется немного помучиться и склеить разделенную прог-
рамму или написать для нее новую процедуру загрузки. Если программа
заполняла полностью ч8К памяти RAM, то возможен только второй метод.

Если надо соединить блоки, то достаточно напитать процедуру, по-
хожую на разделяющую. Она должна считывать первой блок по адресу
16384, второй - сразу за ним, а затем записывать вместе как один
блок.

В этом разделе был описан способ запуска блоков машинного кода
путем считывания в область машинного стека. Чтобы такой блок запус-
тился, достаточно ввести инструкцию LOAD "" CODE, которая запустит
его. Чтобы убедиться в этом на практике, введите и запустите програм-
му с листинга:

10 CLEAR 65381: LET S«0

20 FOR N«65362 TO 65395: READ A: POKE N,A:LET S»S+A: NEXT N

30 IF S<>3437 THEN PRINT "ОШИБКА В СТРОКЕ DATA": STOP

40 SAVE "BEEP" CODE 65362,34

50 DATA 88,255,3,19,0,62,221,229,5,8,
197,17,30,0,33,112,5,205

60 DATA 181,3,33,0,8,43,124,181,32,251,
193,16,235,221,225,201

Она запишет на ленте короткий блок машинного кода, который будет
запускаться самостоятельно. После записи этого блока освободите па-
мять с помощью RANDOMIZE USR 0 или RESET (по возможности переставьте
RAMT0P на нормальное значение с помощью CLEAR 65367) и прочтите его,
введя LOAD "" CODE. Цель этого блока - проинформировать о том, что он
запустился: он это делает несколькими эзуковыми сигналами. Вы услыши-
те их сразу после считывания как только с рамки исчезнут гранато-
во-желтые полосы.




  Оставте Ваш отзыв:

  НИК/ИМЯ
  ПОЧТА (шифруется)
  КОД



Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Похожие статьи:
Тусовка - "Орбита-2".
CODING - Почему он так режет? (о выводе картинок на экран).
А знаете ли вы что - Нашей группой готовится к выходу печаталка текстов 8х8...
BIRTHDAY - С днем рождения SECTOR, MEGAVOLT.
Sketches among the Ruins of my Mind - Нa двoрe зиma, пaдaeт oбильный cнeг, хoлoд прoбирaeт дo кocтeй, нo этo лишь cпocoбcтвyeт выхoдy нoвoй НeresY. :)

В этот день...   23 ноября