9. ДЕКОДИРОВАНИЕ ЗАКОДИРОВАННЫХ БЛОКОВ ТИПА "BYTES"
Кодирование программы - это род достаточно простого
шифра, делающего невозможным правильную работу программы.
Для ее запуска служит специальная декодирующая процедура.
которая находится в той же программе, и, что самое важное-
не закодирована. Кодирование просто затрудняет доступ к
тексту программы,после того как все предшествующие зашиты
устранены и программа считана без самозапуска. В этом слу-
чае в памяти лежит "полуфабрикат" программы, который только
после обработки декодирующей процедурой становится програм-
мой. Кодирование может быть основано на инверсии всех
байтов в программе. Хорошим примером является программа
"ART STUDIO ".Ее бейсиковая часть практически не защищена
никак, но главный блок программы ("STUDIO-MC" CODE 76000,
30672) частично закодирован. По адресу 26000 находится
инструкция JP 26024, которая осуществляет переход к деко-
дирующей процедуре:
26024 |
21С154 |
LD HL,26049: |
запись адреса |
26027 |
ES |
PUSH HL; |
26049 в стек |
26028 |
21С165 |
LD HL,26049; |
адрес начала |
26031 |
11476С |
LD DE,2 6719; |
адрес конца |
26034 |
7Е |
LD A, (HL) ; |
выбор байта из |
26035 |
D622 |
SUB 34; |
памяти, переко- |
26037 |
07 |
RLCA: |
дирование его с |
26038 |
ЕЕСС |
XOR #CC |
помощью SUB, RLCA, |
26040 |
77 |
LD(HL); |
XOR и запись . |
26041 |
23 |
INC HL; |
следующий адрес |
26042 |
В7 |
OR A; |
проверка признака |
26043 |
ED52 |
SBC HL-DE: |
конца, возврат в |
26045 |
19 |
ADD HL.DE: |
цикл или выход |
26046 |
2 0F2 |
JP NZ,26034; |
по адресу, записан |
26048 |
С9 |
RET; |
ному е стеке |
26049 |
B2EDF1 |
|
или 26049 |
сначала процедура помещает в стек адрес 26049.Теперь
собственно, начинается декодирование : в регистр HL заново
загружается адрес 26049 - как начало декодированного блока,
в DE - 27719. как адрес последнего декодированного блока.
Затем в цикле декодируются последовательно байты-инструкция
SUB 34, RLCA, и XOR #CC являются ключом, с помощью которого
расшифровывается эта часть программы. Наконец, проверяется
условие достижения адреса 27719. как последнего декодируемого
(содержится в DE) . Выполняется инструкция RET, но последним
записсанным в стек является не адрес возврата в бейсик, а
записанный в начале с помощью PUSH HL адрес 26049 или адрес
только что раскодированного блока, следовательно происходит
его запуск.
Обратим внимание, какие инструкции осуществляют дешифра-
цию - никакая из них не теряет ни одного бита. Вычитание
производится по модулю 256 и для двух разных входных данных
результаты тоже различны. RLCA заменяет значения битов 7,6,
3 и 2 на противоположные. Другими инструкциями, имеющими те
же самые свойства яеляются. например. ADD, ING, DEC, RRCA,
NEG, CPL но не OR или AND. Как раскодировать этот блок?
Проще всего - вводя в бейсике:
РОКЕ 26027,0 (код инструкции NOR)
Тем самым ликвидируя инструкцию PUSH (RET в конце программы
перейдет в бейсик). и выполнить RANDOMIZE USR 2 6000. Однако
помнить о том. что декодирующая программа может проверяться
другим фрагментом программы. Вот дальнейшая часть программы
в "ART STUDIO" :
26283 |
67 |
LD H, A; |
в А уже находится |
26264 |
6F |
LD L, A; |
значение 0 |
26285 |
E5 |
PUSH HL; |
запись его в стек |
26292 |
3AA8 6 5 |
LD A, (26024) ; |
и проверка |
26295 |
FE21 |
CP #21 |
содержимого ячеек |
26297 |
C0 |
RET NZ; |
с адресами |
26298 |
2AA9 6 5 |
LD HL.(26025); |
26024..27027 |
26301 |
B7 |
OR A; |
и если оно другое- |
26302 |
11C165 |
LD DE26049; |
то стирание |
26302 |
ED52 |
SBC HL,DE; |
памяти с помощью |
263 07 |
C0 |
RET NZ; |
RET NZ |
26308 |
3AAB6 5 |
LD A, (2 6027) ; |
(под адрес 0) |
26311 |
FEE5 |
CP #E5; |
если все нормально |
26313 |
C0 |
RET NZ: |
то снятие адреса 0 |
26314 |
E1 |
POR NZ; |
и нормальный |
26315 |
C9 |
RET: |
выход |
Этот фрагмент проверяет : наверняка ли декодирующая проце-
дура запустила всю программу, и если нет. то с помощью RET
NZ стирает память (т.к. на стеке записан адрес 0) .
Что делать в этом случае? Ножно ликвидировать и эти меры
защиты; но в некоторых программах это не поможет. Тогда
остается другой выход : снова закодировать программу т.е.
сделать обратное, чем декодирующая программа. В нашем
случае следовало бы выполнить:
XOR #СС
RRCA
ADD 34
В "ART STUDIO" неизвестно для чего была помещена кодирующая
программа. Она находится под адресом 2 6003 и выглядит так:
26003 |
21С165 |
LD |
HL.2 6; |
адрес начала |
26006 |
1147 6С |
LD |
DE,27719; |
адрес конца |
26009 |
7Е |
LD |
A, (HL) ; |
выборка байта из |
26010 |
ЕЕСС |
XOR |
#CC; |
памяти, кодирование |
26012 |
OF |
RRCA; |
|
его с помощью XOR |
26013 |
С622 |
ADD |
34; |
RRCA, ADD и его |
26015 |
77 |
LD |
(HL).A: |
запись |
26016 |
23 |
ING |
HL; |
следующий адрес |
26017 |
В7 |
OR |
A; |
проверка окончания |
26018 |
ED52 |
SBC |
HL,DE; |
переход в цикл или |
26020 |
19 |
ADD |
HL, DE; |
возврат в |
26021 |
20F2 |
JR |
NZ,26009; |
бейсик |
Как видно, она построена аналогично декодирующей
процедуре. Если такой нет, то ее можно быстро и просто
написать на основе декодирующей процедуры (например ATIS
АТАС, NIGHT SHADE, THE WORD).
При защите программ применяются также малоизвестные
и не опубликованные в фирменных каталогах команды микропро-
цессора Z80. Благодаря их применению программа становится
малочитаемой, да и просмотр ее дизассемблером затруднен.
Наиболее часто встречаемыми не опубликованными
инструкциями являются команды, оперирующие на половинках
индексных регистров IX и IY в группе команд, которым не
предшествует никакой иной префикс (т.е. SBH или EDH) .
Основываются они на префиксации кодом DDH или FDH команды,
касающейся регистра Н или L. В этом случае вместо этого
регистра берется соответствующая половина индексного
регистра. Через НХ обозначается старшая часть регистра НХ,
через IX - младшая. Аналогично HY и IY. Вот пример:
Это верно для всех команд однобайтовых пересылок
между регистрами и восьмибитовых операций AND, OR, ADD,
ADC, SUB, ВС, СР - выполняемых в аккумуляторе.
Префикс FDH или DDN относится ко всем регистрам Н^
или HL, присутствующим в команде, следовательно, в одной
инструкции невозможно использование ячейки адресованной
как (HL) , регистра HL. H или L одновременно с НХ, HY, IX,
IY (в дальнейшем ограничимся регистром IX, но все это
относит:" и к регистру IY I. например:
Несколько иначе представляется ротация ячейки, адресуе-
мой индексным регистрам, т.е. инструкцией начинающейся
кодом 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 + * ) , так и в соответствующий внутренний регистр
микропроцессора . Например:
СВ13 RL Е
DDCB0113 RL (IX + 1) , Е
DDSB0116 RL (IX+1)
DDSE01 LD Е, ( IX+1)
В конце рассмотрения инструкции этого типа следует вспомнить
что команд EX DE, IX или EX DE. IY нет. Префиксирование
команды EX DE, HL не дает никаких результатов. Также и
префиксирование команд, коды которых начинаются с EDH, а
также тех, в которых не присутствует ни один из регистров
И, L или пары HL ( например LD В, Н RRCA и т.д.).
Еще одной интересной командой является SLI (SHIFT
LEFT AND INCREMENT), выполнение которой аналогично SLA с
той разницей, что самый младший бит устанавливается в 1.
Признаки устанавливаются идентично SLA и другим сдвигам:
СС3 7 SLI A
СВЗб SLI (HL)
DDCB«*3 6 SLI (IX+*)
DDCB«* 5 7 SLI (IX + * ) ,A
Временами некоторые проблемы вызывают построение
Флажкового регистра, особенно тогда, когда он используется
достаточно нетипично. Например:
PUSH AF
POP ВС
RL С
JP NC,...
Его вид представлен на рисунке
Дополнительной особенностью регистра Е является то. что
биты 3 и 5 ( обозначенные как F3 и F5 ) точно отражают
состояние восьмибитовой арифметической или логической
операции IN%, (C) и IN F, (С) . Например:
XOR А; запись значения 0
ADD A, 15; результат - 00001111
указатели будут : F5 = 0 : F3=1
Еще одна возможность построения зашиты основана
на использовании регистра обновления памяти Р, а точнее того,
что когда после очередного машинного цикла микропроцессора
инкрементируется значение этого регистра, то его старший бит
остается неизменным, следовательно, может быть использован
для хранения любой, естественно однобитовой информации. Важно
так же то, что инструкция LD A,R с помощью которой может
быть получена эта информация, устанавливает также указатель
S и, следовательно, не требуется дополнительная инструкция,
проверяющая значение этого бита.
Б конце несколько коротких, но часто применяемых
мер защиты. Их ликвидация основывается обычно на действиях
обратных защитным. Например, переменная (DF SZ (23659) опре-
деляет число строк в нижней части экрана, требуемое для
вывода сообщения об ошибке или для ввода данных. Во время
работы программы это значение равно двум, но достаточно
занести туда 0, чтобы программа зависла при попытке
прерывания (т.к. выводится сообщение, для которого нет
места) . Если в программе находится инструкция INPUT или CLS,
то она имеет подобный результат.
Распостраненным способом защиты является также
изменение значений переменной BORDER (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), что
затрудняет работу с компьютером, но для ее ликвидации
требуется всего лишь быстрая реакция компьютера (запишите
с помощью РОКЕ нормальные значения : 3 5 и 5 -
соответственно).