Тайники ZX Spectrum и вечная жизнь в 600 играх 1992 г.

Глава 3-5 - защита программ на бейсике. "Защитные" управляющие символы. Символы смены атрибутов.


3. ЗАЩИТА ПРОГРАММ НА БЕЙСИКЕ

Прочитанная программа, как правило, не должна выглядеть нормально. Например, в программе
есть строка с номером 0 или строки упорядочены по убыванию номеров. Нельзя вызвать EDIT ни для какой
строки, видно подозрительную инструкцию RANDOMIZE USR 0 или просто ничего не видно, т.к. про-
грамма не позволяет листать себя. Если в программе, куда ты вламываешься, ты увидел что-то необычное,
то лучше просматривать ее другим способом, несколько отличающимся от обычного - не с помощью LIST,
а непосредственно, используя функцию РЕЕК, однако, сначала мы должны узнать каким образом размещен
в памяти текст программы на БЕЙСИКе. Программа складывается из последовательных строк и так и хра-
нится в памяти.

Отдельная строчка программы выглядит так:

Она занимает не менее 5 (а точнее 6, т.к. текст пустым быть не может) байт. Два первых означают
её номер. Но он записан наоборот, от всех обычных 2-байтовах чисел, хранимых в памяти (MSB - старший
байт, LST - младший байт).

Следующие 2 байта - это длина строки, т.е. число символов содержащихся в сроке вместе с завер-
шающим ее знаком "ENTER" (#0D). За этими байтами находится текст строки, заканчиваемый "ENTER".

Если введем, к примеру, такую строку:

10 REM BASIC

и запишем её, нажимая клавишу "ENTER", то она будет записана в память как последовательность байт:

Параметр "Длина строки" касается только её текста, следовательно, хотя строка занимает в памяти
11 байт, этот параметр указывает только на 7 байт: 6 байт текста и 1 байт "ENTER", заканчивающий строку.

Тебе, наверное, уже понятно, на чём основано часто применяемый трюк со строкой, имеющий ну-
левой номер. Достаточно впервые два байта занести число 0 (с помощью POKE), чтобы эта строка стала
нулевой строкой. Если мы хотим изменить номер первой строки в программе (а к интерфейсу никакая бы-
страя память не подключена, т.к. в этом случае измениться адрес начала БЕЙСИКа), то достаточно напи-
сать:

POKE 23775,X: POKE 23765,Y
и строка получит номер 256*X+Y. Не зависимо от его значения строка останется в памяти там где была.
Если введём:

10 REM НОМЕР СТРОКИ 10
2 0 REM НОМЕР СТРОКИ 2 0
POKE 23775,0: POKE 23765,30
то первой строке в программе будет присвоен номер 30, но она останется в памяти как первая, а на экране
мы получим след:

30 REM НОМЕР СТРОКИ 10
20 REM НОМЕР СТРОКИ 20
следовательно, чтобы начать разблокировать программу, в которой имеются нулевые строки или строки,
упорядоченные по убыванию номеров, следует найти адрес каждой строки и в их поле "НОМЕР СТРОКИ"
последовательно размещать к примеру: 10,20. В памяти строки располагаются одна за другой, следова-
тельно с обнаружением их начал ты не будешь иметь трудностей. Если X указывает адрес какой-нибудь
программной строки, то следующий адрес равен:

X+PEEK(X+2)+256*PEEK(X+3)+4
т.е. к адресу сроки добавляется длина её текста, увеличенная на 4 байта, т.к. именно столько занимают па-
раметры "НОМЕР СТРОКИ" и "ДЛИНА СТРОКИ".

Такой способ нахождения не действует, к сожалению, когда применяется другой способ защиты -
фальшивая длина строки. Он основан на том, что в поле "ДЛИНА СТРОКИ" вместо настоящего значения
даётся очень большое число - порядка 43-65 тысяч. Этот способ применяется очень часто, т.к. обычно де-
лается невозможным считывание программы с помощью MERGE (т.е. так, чтобы не было самостарта). Де-
лается это потому, что MERGE загружает программу в область WORKSPACE, а затем интерпретатор ана-
лизирует всю считанную программу строка за строкой: последовательно проверяет номер каждой из них, а
затем размещает её в соответствующем месте области, предназначенной для текста программы на БЕЙСИ-
Ке. Для этой строки необходимо там подготовить соответствующее количество свободных байт, "РАЗ-
ДВИГАЯ" уже существующий текст программы. Если в поле "ДЛИНА СТРОКИ" стоит очень большое
число, то интерпретатор старается выделить именно столько байт пространства в область текстов програм-
мы, что завершиться сообщением "OUT OF MEMORY" или просто зависание системы. Чтобы прочесть та-
кую программу не вызывая её самозапуск, следует применить соответствующую отмычку, например, такую
как представленная в разделе 2 программа "LOAD/MERGE". Дополнительным эффектом от применения
фальшивой длины является невозможность в корректировке такой строки путём занесения в её поле. Это
требует слишком большого количества памяти, следовательно, кончается только предупредительным звон-
ком.

Если программа защищена таким способом, то адреса очередных строк приходится искать вручную
или догадываться, где находятся, помня, что каждая строка заканчивается знаком ENTER (но не каждое
число 13 означает ENTER). Чтобы просмотреть программу на БЕЙСИКе, введи такую строку:

FOR N=23755 TO PEEK 23627+256*PEEK 23628:
PRINT N; PEEK N; CHR$ PEEK N AND PEEK 31:
NEXT N

Она последовательно высветит: адрес, содержимое байта с этим адресом, а также символ, имеющий
этот код, если это только не управляющий символ (т.е. с кодом 0.31).

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

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

Вернёмся к первому примеру (строка "10 REM BASIC"). Текст строки складывается из 7 символов
- ключевого слова REM (все ключевые слова - инструкции и функции, а также знаки <=, >= и <> имеют
однобайтовые коды из диапазона 165.. .255. Если ты не знаешь какие, то введи:

FOR N=165 TO 255: PRINT N, CHR$ N: NEXT N
и ознакомиться), а также пяти литер и символа "ENTER". Так бывает всегда, если в строке находится инст-
рукция REM - все знаки, введённые с клавиатуры после этой инструкции, будут размещены в памяти без
малейших изменений. Иначе выглядит ситуация, когда в строке находится другие инструкции, требующие
числовых параметров (а обычно так и бывает). Введём, к примеру, строку:
10 PLOT 10,9

и посмотрим, каким образом она запишется в память (лучше вводя переменную выше строки H=23755 то

1 I ^ кт т т u 11I I nm тп п т к'1''111 '1 n'l n Mi1 НГ[' л

Как видно, текст был модифицирован - после последней цифры каждого числа, выступающего в
тексте строки как параметр, интерпретатор сделал 6 байтов пространства и поместил там символ с кодом
14, а также 5 байтов, в которых записано значение этого числа, но способом, понятным интерпретатору.
Это убыстряет, в определённой мере, выполнение программы на БЕИСИКе, т.к. во время выполнения не
должен каждый раз переводить числа из алфавитно-цифрового представления (последовательности цифр)
на пяти байтовое представление, пригодное для вычислений. Готовое значение выбирается из памяти из-за
управляющего символа CHR$ 14. Эта странная запись дает большие возможности в БЕИСИКе в деле за-
труднения доступа к программам. Во многих программах загрузчиков (LOADER) присутствует такая стро-
ка:

0 RANDOMIZE USR 0: REM...

На первый взгляд эта программа после запуска должна затереть всю память, но так, однако, не про-
исходит. После более внимательного просмотра (с помощью РЕЕК - строка FOR N=23755 ...) оказывается,
после USR 0 и символа CHR$ 14 совсем нет пяти нулей (именно так выглядит в пятибайтовой записи число
0). Если это целое число, то в пятибайтовой записи оно выглядит:

Байт 1 - 0

Байт2 - 0 (для +) и255 (для -)

Байт 3 и 4 - последовательно младший и старший

(или дополнение до 2 для -)

Байт 5 -0

К примеру, 0, 0, 218, 92, 0 это равнозначно числу 23770. Функция USR не осуществляется, и тогда
идет переход по адресу 0, а именно по 23770, а это как раз адрес бейта, находящегося сразу после инструк-
ции REM в нашем примере. Там обычно находится программа - загрузчик, написанная на машинном языке.

Следующим управляющим символом, часто применяемым для защиты, является CHR$ 8 -
"BACKSPASE" или пробел назад. Высвечивание этого символа вызывает сдвиг позиции ввода на одну по-
зицию назад влево, следовательно, с его помощью можно закрывать некоторые позиции на листинге, печа-
тая на их месте другой текст, если, например, в памяти находятся знаки:

LET A=USR 0: REM <<<<<<<<<<<<<<<<

LOAD " " : ...

(< обозначает CHR$ 8), то инструкция LOAD "" и последующий текст закроет инструкцию LET A=USR 0:.
Хотя на листинге теперь видна только инструкция LOAD "":, но дальнейшая часть программы загружается
не ею, а машинной программой, запускаемой инструкцией USR 0 (что очевидно, не должно обозначать пе-
реход к адресу 0). Такая защита, например, применяется в загрузчике программы BETA BASIC 1.0.

4. "ЗАЩИТНЫЕ" УПРАВЛЯЮЩИЕ СИМВОЛЫ

В этом разделе мы познакомимся с остальными управляющей символами. Три из них касаются из-
менения места вывода, шесть - смены атрибутов.

4.1. CHR# 6 - "COMMA CONTROL"

Этот символ (управляющая запятая) действует также как запятая, отделяющая тексты в инструкции
PRINT, т.е. выводит столько пробелов (но всегда не менее одного), чтобы оказаться в колонке 0 или 16:

PRINT "1", "2"

а также

PRINT "1"+CHR$ 6+"2"
имеют идентичное значение.

4.2. CHR$ 22 - "AT CTRL"

Этот символ (AT управляющий) позволяет переносить позиции вывода в любое место экрана так
же, как AT в инструкции PRINT. После этого знака должны появиться два байта, определяющие номер
строки и номер колонки, в которой должен быть расположен следующий знак: PRINT AT 10,7,"!" равно-
значно PRINT CHR$ 22; CHR$ 10; CHR$7; "!".

Чтобы убедиться, как с помощью этого символа делать программы невидимыми (листинги про-
грамм), введи, например:

10 RANDOMIZE USR 300 00: REM НИЧЕГО НЕ ВИДНО!

После инструкции REM введи три пробела, а после восклицательного знака две управляющие запя-
тые. Их можно получить непосредственно с клавиатуры, нажимая последовательно клавиши: EXTENT (или
o6a SНIFTа вместо) чтобы получить курсор "Е", а затем клавишу "6" (курсор сменить цвет на желтый) и
DELETE курсор перескочит к ближайшей половине экрана. После ввода этой строки заменим три этих
пробела на знак AT 0,0.

С помощью: POKE 23774,22: POKE 23775,0: POKE 23776,0 попробуем теперь посмотреть програм-
му На экране появится текст всей строки - начальная часть её закрыта надписью, находящейся после инст-
рукции REM и знака AT CTRL. Такие же трудности возникают, если эту строчку перенести в зону редакти-
рования (клавиша EDIT).

Координаты, заданные в символе AT CTRL должны находиться в поле экрана, т.е. номер строки не
может быть больше 21, номер колонки не больше 31. Задание больших значений в случае PRINT или LIST
вызывает сообщение "OUT OF SCREEN" и отмену дальнейшего вывода, если же листинг получен нажати-
ем "ENTER" (автоматический листинг) - также наступит прекращение дальнейшего вывода, а кроме того, в
нижней части экрана появится мигающий знак вопроса - сигнал ошибки. Следовательно, это практически
способ защиты от просмотра текста для каждой программы.

4.3. CHR$ 23 - "TAB CTRL"

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

5. СИМВОЛЫ СМЕНЫ АТРИБУТОВ

Эту группу управляющих символов составляют символы, меняющие атрибуты:
CHR$ 16 - INK CTRL
CHR$ 17 - PAPER CTRL
CHR$ 18 - FLASH CTRL
CHR$ 19 - BRIGHT CTRL
CHR$ 20 - INVERSE CTRL
CHR$ 21 - OVER CTRL

После каждого из этих символов обязателен один байт, уточняющий о каком атрибуте идет речь,
после символов INK и PAPER это могут быть числа 0.9, после FLASH и BRIGHT 0, 1, 8 после INVERSE и
OVER: 0 и 1. Задание других значений вызывает сообщение: "INVALID COLOUR" и, естественно, преры-
вание просмотра программы.

Высвечивая программу, защищенную управляющими символами цветов, принимаем для себя сле-
дующую последовательность действий. Например, если, просматривая текст программы мы встречаем код
знака PAPER CTRL, то заносим в его второй байт значение 0, если INK CTRL - значение 7, в остальные
управляющие цветами символы - значение 0. Кроме того, удаляем все знаки BACK SPACE < CHR$ 8) пу-
тем их замены пробелами (CHR$ 32) .Также ликвидируем знаки AT CTRL - заменяем с помощью POKE три
байта символа на пробелы. После такой корректуры программу можно уже листать без всяких сложностей.

Бели ты хочешь взломать программу - загрузчик, то его не обязательно и в принципе не надо очи-
щать - важно узнать, что эта программа делает, каким образом загружается в память и запускает следую-
щие блоки, а не стараться, чтобы она делала "ЛАДНО" и была написана чисто и прозрачно. Это тем более
важно, пока не узнаешь точно программу, лучше не делать в ней никаких изменений - одна ловушка может
проверяться другой, поэтому наилучший способ раскалывания программы это анализ ее работы шаг за ша-
гом, считывая последовательные байты памяти:

0 BORDER 0: PAPER 0: INK 0: CLS 0: PRINT #0, "LOADING";: FOR N=0 TO 20
STEP 4: BEEP 2,N : NEXT N: LOAD "" CODE: PRINT AT 19,0;: LOAD "" CODE: PRINT
AT 19,0;: LOAD "" CODE: PRINT AT 19,0;: LOAD "" CODE: PRINT AT 19,0; RANDOM-
IZE USR 24064

Помни о правильной интерпретации очередных байтов: сначала два байта номера строки, потом два
байта содержания ее длины (которая может быть фальшивой), затем текст: инструкция БЕИСИКа, потом ее
параметры. За каждым числом записывается CHR$ 14 и пять байтов, содержащих значение этого числа за
параметрами - двоеточие и следующая инструкция или ENTER и новая строка программы.

Это было бы все, если речь идет об управляющих символах, но есть еще одна вещь, которую требу-
ется объяснить, что бы ты не имел неприятностей с чтением БЕИСИКа. Речь идет об инструкции DEF FN.
Введи, а затем внимательно просмотри такую строку:

10 DEF FN А(А,В$,С)=А+С

Кажется, что она должна занять в памяти 19 байтов (номер строки, ее длина, ENTER, а также 14
введенных символов), но это не так. Интерпретатор после каждого параметра функции поместил знак
CHR$ 14 и дорезервировал зачем-то следом еще пять байт. Введи:

PRINT FN A(l,"125",2)
и снова посмотри содержимое памяти с адреса 23755. После первого параметра в определении функции да-
лее находится CHR$ 14, но после него последовательно расположились: 0, 0, 1, 0, 0, что в памяти байтов
записи обозначает 1. Также после третьего параметра функции находится CHR$ 14 и байты, содержащие
число 2. После параметра В$ также находится значение использованного параметра: CHR$ 14 и пять бай-
тов, которые последовательно содержат: первый для нас не имеет значения, второй и третий содержат ад-
рес, по которому находится цепочка символов "125" (вызов функции был осуществлен в директивном ре-
жиме, следовательно этот адрес относится к области редактирования строки БЕИСИКа), а байты 4-5 это
длина цепочки - в нашем случае она составляет три знака.

Помни об этом, читая БЕИСИК с помощью РЕЕК, а не LIST, иногда случается, что именно в этих
байтах, зарезервированных для действительных аргументов функции скрыты проверки, определяющие ра-
ботоспособность программы или даже машинная программа, загружающая последующие блоки (например,
BETA BASIC 1.0).

В конце немного о программах-загрузчиках. Их задачей является считывание и запись всех блоков,
составляющих программу. Обычно они это делают способом, максимально затрудняющим понимание их
работы - так, чтобы запуск программы другим способом, а не через загрузчик (или на практике взлом про-
граммы) был не возможен. Посмотрим на загрузчики применяемые в большинстве продукции фирмы
ULTIMATE (например, ATIC ATAC, KNIGHTLORE, PENTAGRAM, NIGHT SHADE и т.д.). Выглядят они
так:

FOR N=23755 ТО PEEK 23627+256*PEEK 23628: PRINT N;" "; PEEK N; CHR$

PEEK N AND PEEK>31: NEXT N

После такого загрузчика на ленте находятся пять следующих блоков: экран, закодированный блок
программы, а за ним три коротеньких блока, защищающих программу: однобайтовый (код инструкции JP
(HL), из нескольких байтов (это процедура, которая декодирующею программу) и последний двухбайтовый
загружаемый по адресу 23627, или в переменную FRAMES. Значение этой переменной увеличивается на 1
через каждые 1/50 секунды. В машинной программе, запущенной с помощью RANDOMIZE USR 24064, её
значение проверяется и если отличается от того каким должно быть (что означает, что где-то после загруз-
ки программа была остановлена на какое-то время), наступает обнуление памяти компьютера. Взлом про-
грамм этого типа весьма прост. Достаточно загрузить все блоки за исключением последнего, а после про-
смотра программы или же приведения в ней определенных изменений (например, вписание РОКЕ) доста-
точно лишь ввести: LOAD "" CODE: RANDOMIZE USR 24064 (но обязательно в одной строке, разделяя
инструкции двоеточием), чтобы запустить игру.

Отдельной работой является декодирование программы, или запуск процедур, декодирующих так,
чтобы она вернулась в БЕИСИК. У читателя, знающего ассемблер, это не должно вызывать затруднений,
однако с точки зрения на распространенность этого типа защиты, особенно в пользовательских программах
(например ART STUDIO или THE LAST WORD), мы еще вернемся к этой теме.




СОДЕРЖАНИЕ:


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

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



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

Похожие статьи:
Музыкалка - Обзор музыкальных демонстраций: Absent, Shock.
Экспертиза - подробный разбор второй часть игры "HACKER". Вам предстоит стать участником захватывающей детективной истории.
Комната смеха - КАК СДЕЛАТЬ ЖУРНАЛ. Советы "экспертов"
История Dizzy - Интереснейший материал о Dizzy: История Codemasters, платформенные и приключенческие игры-головоломки.
Юмор - Анекдот.

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