Тайники ZX Spectrum и как установить вечную жизнь 1994 г.

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


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

В предыдущем разделе были представлены процедуры из ROM: SAVE-BYTES и
LOAD-BYTES. Здесь рассказывается как использовать эти процедуры для защиты
программ* Займемся блоками машинного кода, которые запускаются удивительным
образом. Первым таким способом является прикрытие загрузчика блоком, который им
загружается. Такая защита, к примеру, применяется в игре ТРИ НЕДЕЛИ В ПАРА-
ДИС СИТИ". Проследим способ чтения этой программы так, чтобы она не стартовала
автоматически.

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

24130 DI Запрет прерывания

24131 LD SP,0 ;LDSP,65536
24134 LD HL, (23627) ;ВИЬадресна28
24137 LD DE,28 ;Больше чем значение

24140 ADD HL,DE {Переменной VARS

24141 LD DE,63116 ;ВОЕАдрес,ав
24144 LD ВС,196 ;BC длина блока
24147 LDIR ;

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

--------------—--------------------{программы с другого

24152 LD DC,16384 -.адреса

24156 LD DE,69212

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

63116 LD DC, 16384 {Подготовка загрузки

63120 LD DE,6912 ;картинки на экран

63123 LD А,255 ;с помощью процедуры

63125 SCF ; LOAD-BYTES ИЗ ROM

63126 CALL 1366

63129 LD IX,26490 ;Параметры главного

63133 LD DE.38582 ;блока программы,

63136 LD А,255 ;который считывало»

63138 SCF ;затирает эту процедуру

63139 CALL 1366 считывания блока

63142 IP NZ.+79 ;После возврата из

63144 CP А (процедуры 1366 здесь

63146 CALL 65191 ;уже находится другая

63149 JP NC,-2 (программа

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

С левой стороны расписано содержимое памяти до, а с правой после считывания
программы. Команды, отмеченные звездочками (*), ложатся на выполняемую про-
грамму.

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

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

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

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

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

Займемся теперь расчленением блоков с длиной превышающей 42К. Взлом блоков
этого типа основан на разделении их на такие фрагменты, чтобы в памяти еще
осталось место для MONSa или другого дизассемблера, исправления этих фрагментов,
а затем их склеивания в одно целое либо написание новой процедуры загрузки.
Обычно достаточно разделить длинный блок на две части. Чтобы получить первую,
используем процедуру 1366, но с другими параметрами (не с теми, которых требует
разделенный на части блок). Ранее из процедуры загрузки или если такой нет, то из
заголовка этого блока получаем ее длину и адрес загрузки. Просто задаем адрес, по
которому хотим разместить блок (выше RAMTOR), а также длину, примерно 16К
(несмотря на то» что блок этот значительно длиннее). Считываем теперь этот блок
через CALL 1366 или CALL 2050, но во втором случае сообщение Таре loading error",
которое появится, не даст нам ни какой информации о верности считывания - загру-
жаем часть блока и, следовательно, без контрольного байта, который находится в
конце. Считанную таким образом первую часть блока записываем на ленту и сразу же
приступаем ко второй части. Ее считывание труднее, но тоже возможно несмотря на
ограничения по памяти. Достаточно использовать тот факт, что в SPECTRUM суще-
ствуют 16К ROM, запись в которую невозможна. Например» вызываем процедуру
1366 с адресом равным 0 и начальные 16К будут просто потеряны, а в память RAM
считается только следуюие 32К или меньше (в зависимости от длины блока ). Счи-
тываемый блок займет в памяти RAM адрес с 16384 и дальше, заходя на системные
переменные и оставляя без изменения лишь те байты, адреса которых больше длины
блока. Поэтому необходимо позаботиться о том, чтобы машинный стек, а также
написанную нами процедуру загрузки разместить в конце памяти. Надо также по-
мнить о том, что система БЕЙСИКа будет уничтожена и записать сразу считанный
блок на ленту можно только процедурой, написанной на ассемблере. Кроме того в
промежутках времени между считыванием фрагмента блока и его записью нельзя
разблокировать прерывания, т. к. они изменяют содержимое ячеек с адресами 23552-
23560, а также 23672-23673, а там находится считанный блок. Чтобы выполнить это
последнее условие войдем в середину процедуры 1366, благодаря чему после считы-
вания блока не будет выполнена процедура 1343. Именно она еще и разблокирует
прерывания.

С помощью CLEAR 64999 переносим машинный стек, а с адреса 65500 помещаем
процедурузагрузки:

ORG 65000

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

LD DEVDL ;Длина блока

LD A,255 {Подготовка к считыванию

SCF ;блока

INC D {Таким способом

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

DEC D {процедуры 1366

DI {а затем входим

LD A, 15 {в ее середину:

OUT (254) Л

CALL 1378

LD A,0 {Черная рамка

JR С,OK {будет означать

LD A,7 {верное считывание

OK OUT (254) Л ;белая-ошибочное

CZEKAJ LD A, 191 {Ожидаем нажатие

IN A, (254) {"ENTER"

RRA ;

JR C.CZEKAJ ;
LD IX,0 {Запись считанного

LD DE,DL-163843 {блока на
LD A,255 {ленту

CALL 1218 ;a также

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

JR 4633 ;системы БЕЙСИКА

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

20 INPUT "DLINA ВЫ)КАГ{ DLiRANDOMIZE DL

:LET X-PEEK 23670: LET Y-PEEK 23671: LET S-0
30 FOR N-65000 TO 65054: READ A:LET S-S+A:

POKE N^: NEXT N
40 IFS<>5254+2* (X+Y) -64 THEN

PRINT OSIBKA V DANNYH STOP
50 PRINT WSE DANNYE OK-> WKLUCHIMAGNITOFON"
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А17,X,Y-64,62,255,205,194,4,33
231*253,202,25,18

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

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

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

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

Листинг 2

10 CLEAR 65361: LET S-0
20 FOR N<H65362 ТО 65395: READ А
:РОКЕ N,A: LET SHS+A: NEXT N
30 IF S<>3437 THEN PRINT "OSIBKA W STROKE DATA"
:STOP

40 SAVE "BEEP" CODE 65362,34
50 DATA 88,255,3,19,0,62,221,2

29,6,8,197,17,30,0,33,0,8,43,124,18
60 DATA 181,3,33,0,8,43,124,18,

1,32,251,193,16,235,221,225,201
Она запишет на ленте короткий блок машинного кода, который будет запускаться
самостоятельно. После записи этого блока освободи память с помощью RANDOMIZE
USR 0 или RESET (по возможности переставь RAMTOR на нормальное значение с
помощью CLEAR 65367) и прочти его вводя LOAD""CODE. Цель этого блока проин-
формировать о том, что он запустился - он делает это несколькими звуковыми сипш-
лами. Ты услышишь их сразу после считывания программы, как только с рамки
исчезнут гранатово-желтые полосы.




СОДЕРЖАНИЕ:


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

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



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

Похожие статьи:
Gameland - Как написать игру. Том 3. Часть первая.
Музыкальная страничка - песня Крематорий "Хабибулин".
Открытые письма Nemo №6.261

В этот день...   2 декабря