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

Глава 8 - декодирование закодированных блоков.


8. ДЕКОДИРОВАНИЕ ЗАКОДИРОВАННЫХ БЛОКОВ ТИПА "BYTES"

Что означает, что программа или блок закодированы? Кодирование - это род весьма простого шиф-
ра, делающего невозможным правильную работу программы. Для её запуска служит специальная декоди-
рующая процедура, которая находится в той же программе, а также, что самое важное, не закодированная.
Так для чего же служит кодирование? Это просто очередное затруднение доступа к тексту программы по-
сле того как все предшествующие предохранители взломаны и программа считана без самозапуска. В этом
случае в памяти лежит "полуфабрикат", который только после переработки декодирующей процедурой
становится программой. Кодирование может быть основано на инверсии всех байтов в программе. Хоро-
шим примером является программа "ART STUDIO". Её бейсиковая часть практически не защищена никак,
но главный блок программы ("STUDIO-MC" CODE 26000,30672) частично закодирован. Что делать, чтобы
раскодировать "ART STUDIO" этот адрес 26000. Там находится инструкция JP 26024, которая осуществля-
ет переход к декодирующей процедуре. Вот её текст:

26024 21C165 LD HL,26049 ; 3апись адреса

26027 Е5 PUSH HL ; 26049 СТЕК

26028 21C165 LD HL,26049 ; Адрес начала
26031 11476C LD DE,27719 ; Адрес конца

26034 7Е LD A, (HL) ; Выбор байта из

26035 D622 SUB 34 ; памяти, переко-

26037 07 RLCA ; дарование его с

26038 EECC XOR #CC ; помощью SUB, RLCA,
26040 77 LD (HL),A ; XOR и запись

2 6041 23 INC HL ; Следующий адрес

26042 B7 OR А ; Проверка признака

26043 ED52 SBC HL,DE ; конца, возврат в

26045 19 ADD HL,DE ; цикл или выход

26046 20F2 JP NZ,26034 ; по адресу, записан-

26048 C9 RET ; ному в стеке

26049 B2EDF1 ; или 26049

Сначала процедура помещает в стек адрес 26049. Теперь начинается декодирование: в регистр HL
заново загружается адрес 26049 - как начало декодированного блока, в DE - 27719, как адрес последнего
закодированного блока. Затем в цикле декодируются последовательно байты - инструкция SUB 34, RLCA и
XOR #СС являются ключом, с помощью которого расшифровывается эта часть программы. Наконец, про-
веряется условие достижения адреса 27719, как последнего декодируемого (содержится в DE). Выполняет-
ся инструкция RET, но последним записанным в стек является не адрес возврата в БЕИСИК, а записанный
в начале с помощью PUSH HL адрес 26049 или адрес только что раскодированого блока, следовательно
происходит его запуск.

Обратим внимание, какие инструкции осуществляют дешифрацию? никакая из них не теряет ни од-
ного бита. Вычитание производится по модулю 256 и для двух разных входных данных. Результаты тоже
различны. RLCA заменяет значения битов 7, 6, 3 и 2 на противоположные. Другими инструкциями, имею-
щими те же самые свойства, являются, например, ADD, INC, DEC, RRCA, NEG, CPL, но не OR или AND.
Как раскодировать этот блок? Проще всего, вводя в БЕИСИКе:
РОКЕ 26027,0 (код инструкции NOP)

Тем самым ликвидировать инструкцию PUSH (RET в конце программы переведет в БЕИСИК) и
выполнить RANDOMIZE USR 26000, Стоит однако помнить о том, что декодирующая программа может
проверяться другим фрагментом программы. Вот дальнейшая часть программы в "ART STUDIO":

26283 67 LD H,A ; B А уже находится

26284 6F LD L,A ; значение 0

26285 Е5 PUSH HL ; 3апись его в стек
26292 3AA865 LD А, (26024) ; и проверка

26295 FE21 CP #21 ; содержимого ячеек

2 62 97 C0 RET NZ ; с адресами

26298 2AA965 LD HL, (26025) ; 26024...27027

26301 B7 OR A ; и если оно другое

26302 11C165 LD DE,(26049) ; то стирание

2 6305 ED52 SBC HL,DE ; памяти с помощью

26307 C0 RET NZ ; RET NZ

26308 3AAB65 LD A, (26027) ; (под адрес 0)
26311 FEE5 CP #E5 ; Если все нормально

26313 C0 RET NZ ; то снятие адреса 0

26314 E1 POP NZ ; и нормальный

26315 C9 RET ; выход

Этот фрагмент проверяет: наверняка ли декодирующая процедура запустила программу, и если нет,
то с помощью RET NZ стирает память (т.к. в стеке записан адрес 0).

Что делать в этом случае? Можно ликвидировать и эти меры защиты, но в некоторых программах
это не поможет. Тогда остается другой выход: снова закодировать программу, т.е. сделать обратное, чем
декодирующая программа. В нашем случае следовало бы выполнить.

XOR #CC

RRCA

ADD 34

В "ART STUDIO" неизвестно для чего была помещена кодирующая программа. Она находится под
адресом 26003 и выглядит так:

26003 21C165 LD HL,26049 ; Адрес начала

26006 1147 6C LD DE,27719 ; Адрес конца

26009 7Е LD A, (HL) ; Выборка байта из

26010 EECC XOR #CC ; памяти, кодирование
26012 0F RRCA ; его с помощью XOR

26013 С622 ADD 34 ; RRCA, ADD и его

26015 77 LD (HL),A ; 3апись

2 6016 23 INC HL ; Следующий адрес

26017 B7 OR A ; Проверка окончания

26018 ED52 SBC HL,DE ; Переход в цикл или

26020 19 ADD HL,DE ; возврат в БЕЙСИК

26021 20F2 JR NZ,26009

Как видно, она построена аналогично декодирующей процедуре. Если таковой нет, то ее можно бы-
стро и просто написать на основе декодирующей процедуры (например, ATIC ATAC, NIGHT SHADE, THE
WORLD).

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

Наиболее часто встречаемыми не опубликованными инструкциями являются команды, оперирую-
щие на половинках индексных регистров IX и IY в группе команд, которым не предшествует никакой иной
префикс (т.е. CBH и EDH). Основываются они на префиксации кодом DDH или FDH команды, касающейся
регистра Н или L. В этом случае вместо этого регистра соответствующая половина индексного регистра.
Через HX обозначается старшая часть регистра IX, через LX - младшая. Аналогично HY и LY. Вот пример:

Это верно для всех команд однобайтовых пересылок между регистрами и восьмибитовых операций
AND, OR, ADD, ADC, SUB, BC, CP - выполняемых в аккумуляторе.

Префикс FDH или DDH относится ко всем регистрам Н, L, или HL, присутствующим в команде,
следовательно, в одной инструкции не возможно использование ячейки адресованной как (HL), регистра
HL, Н или L одновременно с НХ, HY, LX, LY (в дальнейшем ограничимся регистром IX, но все это отно-
сится также и к регистру IY), например:

66 LD H, (HL) DD66** LD HX, (IX+*)

75 LD (HL) , L DD7 5 * * LD (IX+*),LX

65 LD H,D DD65 LD HX, D

Несколько иначе представляется ротация ячейки, адресуемой индексным регистром, т.е. инструк-
цией начинающейся кодом DDCB. Инструкция типа RR (IX+**) и ей подобные подробно описаны во всех
доступных материалах о микропроцессорах Z80, но мало кто знает об инструкциях типа RR (IX+***),% и
им подобных, где % обозначает любой регистр микропроцессора. Они основаны на пре фиксировании ко-
дом DDH или FDH инструкции типа RR%, также обстоит дело с инструкциями SET N,(IX+*),% а также
RES N,(IX+*),% (но для BIT -уже нет). Выполнение такой инструкции основано на выполнении нормаль-
ной команды RR (IX+*) (или подобной), SET N,(IX+*) или RES N,(IX+*), а затем пересылки результата как
в ячейку (IX+*) так и в соответствующий внутренний регистр микропроцессора, например:
CB13 RLE

DDCB0113 RL (IX+1),E

DDCB0116 RL (IX+1)

DD5E01 LD E,(IX+1)

В конце рассмотрения инструкции этого типа следует вспомнить, что команд ЕХ DE,IX или EX
DE,IY нет. Префиксирование команды EX DE,HL не дает никаких результатов. Также префиксирование
команд, коды которых начинаются с EDH, a также тех, в которых не присутствует ни один из регистров Н,
L или пары HL (например LD В,Н, RRCA и т.д.).

Очередной любопытной командой является SLI (SHFIT LEFT AND INCREMENT), выполнение ко-
торой аналогично SLA с той разницей, что самый младший бит устанавливается в 1. Признаки устанавли-
ваются идентично SLA и другим сдвигам:
CC37 SLI А

CB36 SLI (HL)

DDCB**36 SLI (IX+ *)

DDCB**57 SLI (IX+ *),A

Временами некоторые проблемы вызываю? построение флажкового регистра, особенно тогда, когда
он используется достаточно нетипично, например:
PUSH AF
POP BC
RL С

JP NC,...

Дополнительной особенностью регистра F является то, что биты 3 и 5 (обозначенные как F3 и F5)
точно отражают состояние восьмибитовой арифметической или логической операции INC %,(С) и IN F,(C),
например:

XOR А ; Запись значения 0

ADD A, 15 ; Результат - 00001111

; Указатели будут: F5=0; F3=1
Последняя (кто поручится, что их больше нет?) тайна Z80 это регистр обновления памяти Р. А точ-
нее то, что когда после очередного машинного цикла микропроцессора инкрементируется значение этого
регистра, то его старший бит остается неизменным, следовательно, может быть использован для хранения
любой, естественно однобитовой, информации. Важно также то, что инструкция LD A,P с помощью кото-
рой может быть получена эта информация, устанавливает также указатель S и, следовательно, не требуется
дополнительная инструкция, проверяющая значение этого бита.

В конце несколько коротких, но часто приемлемых мер защиты. Их ликвидация основывается
обычно на действиях обратным защитным. Например, переменная DFSZ (23659) определяет число строк в
нижней части экрана, требуемое для вывода сообщения об ошибке или для ввода данных. Во время работы
программы это значение равно двум, но достаточно занести туда 0, чтобы программа зависла при попытке
прерывания (т.к. выводится сообщение для которого нет места). Если в программе находится инструкция
INPUT или CLS, то она имеет подобный результат.

Распространенным способом защиты является также изменение значений переменной BORDCR
(23624), которая определяет цвет рамки и атрибутов нижней части экрана. Значение отдельных битов сле-
дующие:

7 бит - FLASH

6 бит - BRIGHT

5,4,3 биты - BORDER
2,1,0 биты - INK

Способ защиты основывается на том, что цвет чернил тот же самый, что и у рамки, следовательно,
при остановке программы, можно подумать, что она зависла (т.к. не видно сообщения). Разблокировать
программу можно вписав BORDER 0 (или другой цвет).

Очередным способом защиты является изменение переменной ERRSP (23613-23614) или дна ма-
шинного стека (эта переменная указывает дно стека), где обычно находится адрес процедуры обрабаты-
вающей ошибки языка BASIC (вызываемой с помощью RST 8). Уменьшение значения ERRSP на 2 вызыва-
ет защиту программы от "BREAK" и любой другой ошибки - программа заново запускается с того места, в
котором произошла ошибка. Смена содержимого дна машинного стека или другая замена значений ERRSP
может вызывать зависание или даже рестарт компьютера.

Известным способом защиты является также занесение значения больше 9999 в ячейки памяти,
обозначающие номер строки БЕЙСИКа (первый байт - старший). Если он размещается в границах 10000 -
16383, а на листинге это выглядит, например 0000 (вместо 10000) и строки невозможно скорректировать
(EDIT), если же превышает 16384 - дальнейшая часть программы считается не существующей. Можно так-
же встретить защиту, основывающуюся на занесении минимальных значений (то есть 1) в переменные
REPDEL (23561) и REPPER (23562), что затрудняет работу с компьютером, но для её ликвидации требуется
лишь быстрая реакция (запиши с помощью РОКЕ нормальные значения: 35 и 5).

Стоит вспомнить еще о переменной NMIADD, которая не используется из-за ошибки в ROM. Она
должна была содержать адрес обслуживания прерывания NMI (всегда понимаемого). Ошибка в том, что
микропроцессор переходит туда лишь тогда, когда он - 0. Если, однако, добавить к SPECTRUM соответст-
вующую приставку, путь к задержке любой программы был бы открыт.

С ЧАСТЛИВОЙ РАЗБОРКИ ПРОГРАММ!!!




СОДЕРЖАНИЕ:


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

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



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

Похожие статьи:
От авторов - Что ж, вот и OРЕX#1...
Система - адаптация программ к TR-DOS.
Мозаика - Что нового нам готовит Magic Soft?
Юмор - "Штирлиц - русский SysOp" (глава 10 и заключение).
Итог - Вот и все, мне не о чем писать, - и я потерян для сцены.

В этот день...   2 мая