ГЛАВА 7
ДЕКОДИРОВАНИЕ ЗАКОДИРОВАННЫХ БЛОКОВ ТИПА
«BYTES»
Что означает то, что программа или блок закодированы? Кодирование
это род весьма простого шифра, делающего невозможной правильную рабо-
ту программы. Для её запуска служит специальная декодирующая процедура,
которая находится в этой же программе, а также, что самое важное, не за-
кодирована. Так для чего же служит кодирование? Это просто очередное за-
труднение доступа к тексту программы после того, как все предшествующие
предохранители взломаны и программа считана без запуска. В этом случае в
памяти лежит «полуфабрикат», который только после переработки де-
кодирующей процедурой становится программой. Кодирование может быть
основано, например, на инверсии всех байтов в программе. Хорошим приме-
ром является программа «ART STUDIO». Её Basic-чэсть практически не
защищена никак, но главный блок программы ("STUDIO-MC CODE
26000.30672) частично закодирован. Что сделать, чтобы раскодировать его?
Просто найти адрес, с которого этот блок запускается. В случае «ART
STUDIO» этот адрес - 26000. Там находится инструкция JP 26024, которая
осуществляет переход к декодирующей процедуре. Вот её текст:
26024 |
21С165 LDHL,26049; |
запись адреса |
26027 |
Е5 |
PUSH HL; |
26049 в стек |
26028 |
21С165 LD HL,26049; |
адрес начала |
26031 |
11476С LD DE,27719; |
адрес конца |
26034 |
IE |
LD A,(HL); |
выбор байта из |
26035 |
D622 |
SUB 34; |
памяти, переко- |
26037 |
07 |
RLCA; t |
дирование его с |
26038 |
EECC |
XOR #CC; |
помощью SUB,RLCA, |
26040 |
77 |
LD (HL),A; |
XOR и запись |
26041 |
23 |
INC HL; |
следующий адрес |
26042 |
B7 |
OR A; |
проверка признака |
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, но последним, записанным в стек адре-
сом, является не адрес возврата в Basic, а записанный вначале с помощью
PUSH HL адрес 26049, или адрес только что раскодированного блока, следо-
вательно, происходит его запуск.
Обратим внимание, какие инструкции осуществляют дешифрацию:
никакая из них не теряет ни одного бита. Вычитание производится по моду-
лю 256 и для двух разных входных данных результаты тоже различны. RLCA
заменяет значения битов 7,6,3 и 2 на противоположные. Другими
инструкциями, имеющими те же самые свойства, являются, например, ADD,
INC, DEC, RRCA, NEG, CPL, но не OR или AND. Как раскодировать этот блок:
Проще всего - вводя в BASIC'e:
РОКЕ 26027,0 (код инструкции NOP)
тем самым ликвидируя инструкцию £USH HL (RET в конце программы
перейдет в BASIC), и выполнить RANDOMIZE USR 26000. Стоит, однако,
помнить о том, что декодирующвая программа может проверяться другим
фрагментом программы. В «ART STUDIO» так и есть. Вот дальнейшая часть
программы:
26283 67 LD Н,А; в А уже находится
26284 6F LDUA; -значение 0
26285 Е5 PUSH HL; запись его в стек
26292 ЗАА865 LD А,(26024); и проверка
26295 FE21 CP #21; содержимого ячеек
26297 СО . RET NZ; с адресами
26298 2АА965 LD HL,(26025); 26024 ...26027
26301 В7 OR А; и если оно другое,
26302 11С165 LDDE,26049; то стирание
26305 ED52 SBC HL,DE; памяти с помощью
26307 СО RET NZ; RET NZ
26308 3AAB65 LD A,(26027); (под адрес 0)
26311 FEE5 CP #E5; если все нормально
26313 CO RET NZ; то снятие адреса 0
26314 El POP NZ; и «нормальный»
26315 C9 RET; выход
Этот фрагмент проверяет: наверняка ли декодирующая процедура за-
пустила всю программу, и если нет, то с помощью RET NZ стирает память
(так как на стеке записан адрес 0).
Что делать в этом случае? Можно ликвидировать и эти меры защиты, но
в некоторых программах это не поможет. Тогда остается другой выход: снова
закодировать программу, т.е. сделать обратное, чем декодирующая програм-
ма. В нашем случае следовало бы выполнить:
XOR #СС
RRCA
ADD 34
В «ART STUDIO» неизвестно для чего была помещена кодирующая
программа. Она находится под адресом 26003 и выглядит следующим обра-
зом:
26003 21С1165 LD HL,26049; адрес начала
26006 11476С LD DE,27719; адрес конца
26009 7Е LD AA,(HL); выборка байта из
26010 ЕЕСС XOR #СС; памяти, кодирование
26012 OF RRCA; его с помощью XOR,
26013 С622 |
ADD |
34; |
RRCA, ADD и его |
26015 77 |
LD |
<HL),A |
запись |
26016 23 |
INC |
HL; |
следующий адрес |
26017 В7 |
OR |
A; |
проверка окончания |
26018 ED52 |
SBC |
HLtDE; |
переход в цикл или |
26020 19 |
ADD |
HL,DE; |
возврат |
26021 20F2 |
JR |
NZ,26009; |
в BASIC |
26022 С9 |
RET; |
|
|
Как видно, она построена аналогично декодирующей процедуре. Если
такой нет, то её можно быстро и просто написать на основе декодирующей
процедуры (например ATI С АТАС. NIGHT SHADE. THE LAST WORD).
При защите программ применяются также малоизвестные и нео-
публикованные в фирменных каталогах команды микропроцессора Z80. Бла-
годаря их применению работа программы становится малочитаемой, да и
просмотр её Дизасемблером затруднен.
Наиболее часто встречаемыми неопубликованными инструкциями явля-
ются команды, оперирующие на половинках индексных регистров IX и IY в
группе команд, которым не предшествует никакой иной префикс (т.е. СВН
или EDH). Основываются они на префиксации кодом DDH или FDH команды,
касающейся регистра Н или L. В этом случае вместо этого регистра берется
соответствующая половина индексного регистра. Через НХ обозначается
старшая часть регистра IX, через IX - младшая. Аналогично HY и IY. Вот
примеры:
; КОД : |
КОМАНДА I |
код |
КОМАНДА |
код |
КОМАНДА |
! 24 • |
INC Н |
DD24 |
NC НХ |
FD24 |
INC HY |
! 2D ■ |
DEC L |
DD2D |
DEC IX |
FD2D |
INC IY |
1 4С i |
| LD С,Н |
DD4C |
LD С,НХ |
FD4C |
LD C.HY |
I 64 |
LD Н.Н |
DD64 |
LD НХ,НХ |
FD64 |
LD HY.HY |
1 2601 |
LD Н,1 |
DD2601 |
LD НХ,1 |
FD2601 |
LD HY,1 |
В5 |
OR L |
DDB5 |
OR IX |
FDB5 |
OR IY |
Это верно для всех команд пересылки однобайтовых чисел между
регистрами и восьмибитовых операций AND, OR, XOR, ADD, ADC, SUB, ВС
и CP, выполняемых в аккумуляторе.
Префикс FDH или DDH относится ко всем регистрам Н, L или HL,
присутствующим в команде. Следовательно, в одной инструкции невозмож-
но использование ячейки, адресованной как (HL), регистра HL, Н или L
одновременно с НХ, HY, IX, IY (в дальнейшем ограничимся регистром IX, но
все это относится также и к регистру IV). например:
66 LD H,(HL> DD66" LD НХ,ЦХ+*)
75 LD (HL),L DD75** LD <IX+*),IX
65 LD H,L DD65 LD HX.IX
Несколько иначе представляется ротация ячейки, адресуемой индекс-
ным регистром, т.е. инструкций, начинающихся кодом DDCB. Инструкция
типа RR (IX+**) и ей подобные подробно описаны во всех доступных ма-
териалах о микропроцессорах 280, но мало кто знает об инструкциях типа RR
(1Х+**), % и им подобных, где % обозначает любой регистр микропроцессо-
ра. Они основываются на префиксовании кодом DuH или FDH инструкции
типа RR %.
Также обстоит дело с инструкциями SET N,(lX+*)r%, 3 также RES
N,(IX+*), % (но для BIT - уже нет). Выполнение такой инструкции основано на
выполнении нормальной команды RR (1Х+*) (или подобной), SET N,(tX+*) или
RES N»(IX+*), а затем пересылки результата как в ячейку (1Х+*), так и соответ-
ствующий внутренний регистр микропроцессора. Например:
СВ13 RL Е
DDCB0113 RL <1X41 )tE
вызывает сдвиг ячейки с адресом IX-H влево на 1 бит и пересылку
результата в регистр Е, что обычным способом следовало бы сделать так:
DDCB0116 RL UX+1)
DD5E01 LD ЕД1X4-1)
В конце рассмотрения инструкции данного типа следует вспомнить, что
команд EX DEJX или EX OE,IY нет. Префиксование команды EX DE,HL не
дает никаких результатов. Также - префиксирэвание команд, коды которых
начинаются с EDH, а также тех, в которых не присутствует ни один из
регистров Н, L или пары HL (например. LD B,N , RRCA и т.дА Очередной лю-
бопытной командой является SU (SHIFT LEFT AND INCREMENT), выпол-
нение которой аналогично SLA с той разницей, что самый младший бит
устанавливается в 1. Признаки устанавливаются идентично SLA и другим
сдвигам:
СВ37 SL1 А
СВ36 SLI (HL)
DDCB**36 SLI <IX+*)
DDCB**57 SU (IX-^УЛ
Временами некоторые проблемы вызывает построение флажкового
регистра, особенно тогда, когда он используется достаточно нетипично,
например:
PUSH AF
POP ВС
RL С
JP NC,...
Его вид представ/ген на рис. 1:
Дополнительной особенностью регистра В является то, что биты 3 и 5
(обозначенные как F3 и F5) точно отражают состояние восьмибитовой
арифметической или логической операции IN %,(С) и IN F,(C). Например,
после выполнения:
XOR А; запись значения 0
ADD А, 15; результат - 00001111
указатели будут: F5-0; F3-1
Последняя (кто поручится, что их больше нет?) тайна Z80 - это регистр
обновления памяти R. А точнее то. что когда после очередного машинного
цикла инкрементируется значение этого регистра, то его старший бит оста-
ется неизменным, следовательно, может быть использован для хранения лю-
бой, естественно, однобитовой информации. Важно также то, что
инструкция LD A,R, с помощью которой может быть получена эта инфор-
мация, устанавливает также указатель S и, следовательно, не требуется до-
полнительная инструкция, проверяющая значение этого бита.
В конце несколько коротких, но часто применяемых мер защиты. Их
ликвидация основывается обычно на действиях обратных защитным. Чаще
всего с этой точки зрения эксплуатируются системные переменные.
Например, переменная DFSZ (23659) определяет число строк в нижней части
экрана, требуемое для вывода сообщения об ошибке или для ввода данных.
Во время работы программы это значение обычно равно 2, но достаточно за-
нести туда 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» и любой дру-
гой ошибки - программа заново запускается с места в котором произошла
ошибка. Смена содержимого «дна» машинного стека или другая замена зна-
чения ERRSFJ может вызвать зависание или даже рестарт компьютера.