Защита дисков от копирования методом прямого управления ВГ93
© Андрей Кулаков, 1994 г. Самарская обл, с. Новый Буян.
Как правило, тот, кто способен создать качественную программу, способен ее надежно защитить. Но, к сожалению, работа по защите требует исследования "лазеек" в операционной системе машины, заставляющих последнюю вести себя нестандартно. Если сюда еще добавить необходимость какого-либо изощренного метода защиты от просмотра, то весь этот процесс выливается в значительную потерю времени и сил.
О методах, использующих внесение изменений в нулевую дорожку упоминать не стану, т.к. они мало кого остановят в силу своей предельной простоты. Можно еще использовать загрузку программ с диска не файлами, а путем непосредственного доступа к определенным секторам. В этом случае диск всегда можно скопировать один к одному.
Теперь вкратце (для начала) рассмотрим, на чем может быть построена более-менее надежная защита:
1. Кодирование Бейсик-файлов на диске и запись ключевого кода на тот же диск, но в место, недоступное ни копировщикам, ни стандартной DOS (индексные массивы, пробелы). При этом boot, запускающий отдельные программы, должен раскодировать их перед запуском.
2. Использование длин секторов, отличных от 256 байт.
3. Автозапуск диска при попытке провести с ним любую стандартную операцию, позволяющую просмотреть, или изменить информацию диска, в т.ч. и дисковым доктором.
Перед детальным рассмотрением каждого из этих методов приведу некоторую справочную информацию о контроллере накопителя на гибких магнитных дисках 1818 ВЕ 93, а также дисассемблер, или адреса с параметрами для некоторых подпрограмм TR DOS.
Контроллер 1818 ВЕ 93 способен производить операции с секторами четырех длин: 128 байт, 256 байт, 512 байт и 1024 байт. Код длины сектора записывается в индексном массиве (индексный массив - это шесть служебных байт + два байта контрольного кода индексного массива, записываемых на диск перед каждым сектором, и содержащие информацию о номере дорожки, номере стороны, номере сектора и о длине сектора) в соответствии с таблицей:
код длины сектора |
длина сектора |
00 |
128 байт |
01 |
256 байт |
02 |
512 байт |
03 |
1024 байт |
Команды 1818 ВГ 93.
Коды команд передаются процессором в регистр команд микросхемы 1818 ВЕ 93. В процессе выполнения команд контроллер (ВЕ 93) либо выполняет операцию с передачей информации по линии ДИСКОВОД -КОНТРОЛЛЕР - ПРОЦЕССОР в каком-либо направлении, сопровождая каждый байт стробом или запросом, либо выполняет операцию без передачи информации над дисководом или собой. Как именно организована работа с процессором при обмене информацией, каковы адреса внутренних регистров контроллера в адресном пространстве ввода-вывода процессора - все это я не обсуждаю, т.к. ниже будут приведены подпрограммы TR DOS, которые возьмут на себя Ваши заботы по этому поводу, а иной доступ к регистрам контроллера, кроме как через эти подпрограммы все равно невозможен (разве только Вы соберетесь писать собственную DOS для ПЗУ).
Система команд контроллера 1818 ВЕ 93 представлена в таблице.
Система команд контроллера 1818 ВГ 93.
Команда |
Код |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
Восстановление |
0 |
0 |
0 |
0 |
h |
v |
ч1 |
ч2 |
Поиск |
0 |
0 |
0 |
1 |
h |
v |
ч1 |
ч2 |
Шаг |
0 |
0 |
1 |
И |
h |
v |
ч1 |
ч2 |
Направление вперед |
0 |
1 |
0 |
И |
h |
v |
ч1 |
ч2 |
Направление назад |
0 |
1 |
1 |
И |
h |
v |
ч1 |
ч2 |
Чтение сектора |
1 |
0 |
0 |
m |
S |
E |
C |
0 |
Запись сектора |
1 |
0 |
1 |
m |
S |
E |
C |
a0 |
Чтение адреса |
1 |
1 |
0 |
0 |
0 |
E |
0 |
0 |
Чтение дорожки |
1 |
1 |
1 |
0 |
0 |
E |
0 |
0 |
Запись дорожки |
1 |
1 |
1 |
1 |
0 |
E |
0 |
0 |
Принудительное прерывание |
1 |
1 |
0 |
1 |
J |
К |
L |
M |
h - код установки МГ в рабочее состояние. При h=0 МГ поднята, при h=l МГ устанавливается в рабочее положение.
v - код, определяющий необходимость проверки положения МГ. При v=0 положение МГ не проверяется.
ч1, ч2 - скорость перемещения МГ.
При своей работе TR DOS устанавливает h=l, ч1=ч2=1.
И - код, определяющий состояние регистра дорожки при перемещении МГ. При И=0 регистр дорожки не изменяется.
S - номер стороны диска.
Е - код, указывающий на необходимость выполнения задержки 15 миллисекунд при установки МГ в рабочее положение. При E=0 задержки нет.
С - код необходимости проверки стороны диска при идентификации индексной области (С=0 - без проверки).
a0 - выбор одного из двух возможных байт признака защиты данных для записи в область индексной адресной метки (при a0=1 записывается F8 - данные могут стираться при a0=0 записывается FB-данные сохраняются.
ВОССТАНОВЛЕНИЕ - переход МГ на нулевую дорожку.
ПОИСК выполняется при v=l. Осуществляется поиск дорожки, номер которой предварительно помещен в регистр дорожки.
ШАГ - перемещение МГ на одну дорожку. Направление не меняется.
НАПРАВЛЕНИЕ ВПЕРЕД и НАПРАВЛЕНИЕ НАЗАД - установка направления перемещения МГ для последующих команд ПОИСК и ШАГ.
ЧТЕНИЕ СЕКТОРА - номер сектора предварительно должен быть помещен в регистр сектора. Осуществляется поиск сектора с заданным номером (именно поиск, а не отсчет) на той дорожке, куда установлена МГ, и чтение информации этого сектора, сопровождающееся выдачей строба процессору для каждого байта. Количество стробов зависит от длины сектора. Поиск сектора и определение его длины осуществляется при предварительном поиске и чтении индексных массивов. Индексные массивы процессору не передаются.
Во время передачи данных ВГ 93 автоматически накапливает их контрольную сумму и, после окончания передачи, сравнивает ее с записанной контрольной суммой на диске сразу после данных. Последняя также не передается процессору. В процессе чтения ВГ 93 вырабатывает байт состояния и соответствующие признаки. Последние здесь рассматриваться не будут, т.к. определение состояния готовности, конца обмена, нормального или аварийного завершения берут на себя приведенные ниже подпрограммы с подробными комментариями.
ЗАПИСЬ СЕКТОРА - выполняется аналогично чтению с той разницей, что информация записывается на диск, а не считывается с него.
ЧТЕНИЕ АДРЕСА - чтение первого индексного массива на дорожке с его собственным кодом контрольной суммы (контрольный код передается тоже).
ЧТЕНИЕ ДОРОЖКИ - считывается вся информация дорожки, включая индексные массивы, данные, пробелы и контрольные коды. Проверка контрольных кодов не осуществляется.
ЗАПИСЬ ДОРОЖКИ - с помощью этой команды выполняется форматирование дорожки. ВГ 93 запрашивает данные у процессора и записывает их на диск. Если данные не находятся в области данных сектора, то они могут интерпретироваться: например, когда ВГ 93 после данных сектора или индексного массива принимает байт #F7, то он не запишет на диск #F7, a вычислит контрольную сумму и запишет ее на диск двумя байтами.
Рассмотрим более подробно, как выполняется форматирование диска в TR DOS.
Сектора на дорожке размещаются не в порядке возрастания их номеров. TR DOS дает следующую последовательность номеров секторов на дорожке: 01 09 02 0А 03 0В 04 ОС 05 0D 06 0Е 07 0F 08 10. Эта последовательность задана в таблице, размещенной по адресу #1FB9. В этой таблице после последнего номера #10 стоит повторяющийся номер #01, т.к. подпрограмма проверки секторов дорожки использует эту таблицу со второго порядкового номера, пропуская начальный номер #01. Для микросхемы контроллера не имеет значения порядок следования секторов на дорожке, т.к. она ищет их по номеру, указанному в индексном массиве, а не отсчитывает с начала дорожки. Изменение порядка следования секторов применено с целью увеличения скорости обмена информацией.
Подпрограмма разметки дорожки.
Перед обращением необходимо передать следующие параметры: номер дорожки в регистре Е процессора, а также в ячейке памяти по адресу #5CD8 должен быть установлен признак необходимости проверки дорожки, если (#5CD8)=0, то будет выполнена проверка. В ячейке памяти по адресу #5CD6 организован счетчик плохих секторов при выполнении проверки.
#1FFD FORM.TRACK DI запретить прерывания.
код команды ЗАПИСЬ ДОРОЖКИ передача его в RG ком. ВГ 93 адрес таблицы номеров секторов. адрес RG данных ВГ 93 M1 LD B,#0A выдать на ВГ 93 10 байтов
#4E. Это будет пробел.
выдать 12 байтов #00
DI |
|
LD |
A, #F4 |
OUT |
(#1F),A |
LD |
HL,#1FB9 |
LD |
C, #7F |
LD |
B, #0A |
LD |
D, #4E |
CALL |
#20B1 |
LD |
B, #0C |
LD |
D, #00 |
выдать 3 байта #F5. ВГ 93
запишет на диск байт #A1 и как
бы пометит себе, что сейчас
будет адресная метка.
байт #FE - адресная метка
индексных данных, т.е. далее
будет индексный массив.
выдать "номер дорожки. CALL #2 0AF
номер стороны всегда=0. CALL #2 0AF
номер сектора. CALL #2 0AF
длина сектора. TR DOS использует
код #01, т.е. длину в 25 6 байт.
Это означает, что после адресной метки
данных ВГ 93 выдаст 256 запросов
байтов данных, которые не будут
интерпретироваться. Если здесь
будет, например, код #02, то область
данных будет содержать 512
байт, и процессору будет для них
выдано 512 запросов.
запись 2-х байтов контрольной
суммы индексного массива.
выдать 22 байта #4Е (пробел)
12 байт #00 запись #А1
адресная метка данных сектора данные сектора. 25 6 байт.
запись 2-х байт контрольной суммы сектора 50 байт #4Е (пробел)
сохранить для сравнения
следующая позиция таблицы
номеров секторов
таблица исчерпана?
если нет, то следующий сектор
все сектора размечены, пробел
перейти, если команда выполнена иначе еще один пробел слово состояния защита записи?
если да, то переход на процедуру выдачи сообщения
если (#5CD8)<>0,
то возврат, иначе проверка
секторов
адрес RG данных ВГ 93 номер дорожки
занести в регистр, дорожки
2-я позиция табл.номеров секторов
#2 0B1 B, #03 D, #F5 #2 0B1
D, #FE #2 0AF
D, E D, #00 D,(HL) D, #01 #2 0AF
CALL
LD
LD
CALL LD
CALL
LD LD LD LD
CALL
D, #F7 #2 0AF B, #16 D, #4E #2 0B1 B, #0C D, #00 #2 0B1 B, #03 D, #F5 #2 0B1 D, #FB #2 0AF B, #00 D, #00 #2 0B1 D, #F7 #2 0AF B, #32 D, #4E #2 0B1 сектора
A,(HL) HL
#10 NZ, M1
B, #00 #2 0B1 M,M2 #2 0B1 A,(#1F) #40
NZ,#3F3 9
A,(#5CD8)
A
NZ
C, #7F A, E
(#3F),A HL,#1FBA
LD
CALL
LD
LD
CALL
LD
LD
CALL
LD
LD
CALL LD
CALL
LD
LD
CALL LD
CALL
LD
LD
CALL конец LD INC
CP 0R LD
CALL JP
CALL IN AND JP
LD 0R RET
LD LD 0UT LD
M3
M4
M5
#2 0AF #20B1 L1:
регистр состояния ВГ 93 готов? ожидать, если не готов команда выполнена раньше, чем был передан весь массив (потеря данных)
0UT (C),D выдать содержимое RG D в RG данных ВГ 93
DJNZ L1 повторять, пока RG B<>0
RET нормальное завершение
При входе в #20AF на ВГ 93 выщается только один байт.
При входе в #20В 1 количество передаваемых байт определяется содержимым RG B процессора. При аварийном завершении работы подпрограммы, выкод осуществляется со сброшенным признаком P. Подпрограмма #3FE5 является частью подпрограммы #3FD5 "чтение сектора", которая будет рассмотрена ниже. Можно отметить их различия по части управления количеством управляемых байтов. Если в подпрограмме #20AF (#20B1) управление осуществляется регистром В, то в подпрограмме #3FE5 этим управляет ВГ93. Это замечательное свойство позволит нам "расширить" ее применение, но обо всем по порядку.
Собственно говоря, эта часть и считывает сектор в область памяти, адрес которой задан в регистровой паре
HL.
Последней командой ВГ 93, которую мы еще не рассмотрели, является команда ПРИНУДИТЕЛЬНОЕ ПРЕРЫВАНИЕ, которая служит для завершения выполнения какой-либо команды. Ее отличия от других команд состоит в том, что ВГ 93 способен принять ее к выполнению в любой момент.
При этом: если J...M=0, прекращается выполнение команды, но готовность ВГ 93 не выфабатышается. При 3=1 прерывание выполняется после перехода входного сигнала из низкого уровня в высокий. При К=1 прерывание осуществляется по приходу индексного импульса (начало дорожки). При М=1 немедленное прекращение выполнения команды.
Перед дальнейшим рассмотрением подпрограмм TR DOS, на всякий случай (если Вы этого не знаете), замечу, что прямой вызов подпрограмм TR DOS из программы, работающей в оперативной памяти (короче говоря, из Вашей программы), возможен только вы том случае, если старший байт адреса этой подпрограммы равен #3D. В противном случае у Вас вызовется какая-нибудь; гадость из SOS. Для устранения этой неприятности служит точка входа #3D2F, где находится:
#3D2F NOP
RET
При переходе на этот адрес выполнится переход на любой адрес ПЗУ TR DOS, предварительно помещенный на вершину стека.
Например, для вызова подпрограммы #20В1 необходимо выполнить; следующие мероприятия:
CALL M1 Обеспечение возврата из TR DOS.
JR M2 Завершение.
M1 LD HL,#20B1 Адрес подпрограммы TR DOS.
LD |
B, #03 |
счетчик повторения |
LD |
A,(HL) |
номер сектора |
0UT |
(#5F),A |
занести в регистр сектора |
PUSH |
HL |
|
DI |
|
|
LD |
A, #80 |
код команды ЧТЕНИЕ СЕКТОРА |
0UT |
(#1F),A |
занести в регистр команд |
PUSH |
BC |
|
CALL |
#3FE5 |
чтение сектора |
IN |
A,(#1F) |
слово состояния |
AND |
#7F |
ошибка? |
P0P |
BC |
|
0R |
Z,M5 |
перейти, если нет ошибки |
DJNZ |
M4 |
повтор. до 3-х раз при ошибке |
LD |
HL,#5CD6 |
адрес счетчика плохих секторов |
INC |
(HL) |
увеличение счетчика на 1 |
P0P |
HL |
|
LD |
A,(HL) |
сохранить для сравнения |
INC |
HL |
номер следующего сектора |
CP |
#01 |
все сектора исчерпаны? |
0R |
NZ,M3 |
следующий сектор |
EI |
|
разрешить прерывания |
RET |
|
|
Подпрограмма разметки дорожки сама использует еще две подпрограммы:
LD
IN
AND
0R
RET
B, #01 A,(#FF) #C0 Z,L1
M
PUSH HL Сохранить в стеке.
JP #3D2F Переход на "трамплин".
М2 ... ............Дальнейший текст Вашей программы.
Итак, продолжаем.
#3D9A - передача кода команды в ВГ 93 из регистра A процессора. Ожидание выполнения команды. Выход из ожидания через BREAK. Другими словами, возврата из этой программы не будет, пока не завершится выполнение команды контроллером .
#3EDF - выполнение микросхемой 1818 ВГ 93 команды, код которой содержится в регистре A процессора. Ячейка памяти по адресу #5CD1 должна содержать код #FF.
Эта программа передает в ВГ 93 код команды и сразу завершает свою работу, т.е. не ожидает окончания работы ВГ 93, что позволит организовать следом передачу данных.
#3D98 - восстановление. Переход М.Г. на нулевую дорожку. #1FF6 - установить в системном регистре интерфейса сторону 0. #1FEB - установить в системном регистре интерфейса сторону 1.
Две последние подпрограммы сообщают интерфейсу, с какой стороной диска ему работать.
Подпрограмма чтения.
#3FD5 |
LOAD: |
LD |
В, #04 |
счетчик повторений |
L1: |
|
IN |
A,(#FF) |
регистр состояния |
|
|
AND |
#C0 |
|
|
|
JR |
NZ, L3 |
переход, если ВГ 93 готов |
|
|
INC |
DE |
контроллер не готов |
|
|
LD |
A, E |
|
|
|
OR |
D |
|
|
|
JR |
NZ,L1 |
повторять, пока DE<>0 |
|
|
DJNZ |
L1 |
повторять, пока B<>0 |
|
|
RET |
|
аварийное завершение, контроллер |
#3FE5 |
L2 : |
IN |
A,(#FF) |
регистр состояния |
|
|
AND |
#C0 |
|
|
|
JR |
Z,L2 |
ожидание готовности ВГ 93 |
! ! ! |
|
RET |
M |
выход, если ВГ 93 закончил. |
L3 : |
|
INI |
|
ввод байта |
|
|
JR |
L2 |
|
Теперь перейдем к двум таким важным подпрограммам, как чтение сектора и запись сектора. Целиком эти программы интересны нам только тем, что при выполнении групповых операций над секторами, считывание и запись сектора ведутся сами по себе, а увеличение базового адреса на 256, само по себе. Другими словами, после считывания каждого сектора, адрес загрузки следующего будет на 256 больше, чем начало предыдущего. Что за бред, спросите Вы, ведь здесь и так все понятно. Но все дело в том, что вложенные здесь подпрограммы считывают в память ровно столько байт, какую длину содержит сектор, а ведь она может оказаться не равной 256 байт (выше было отмечено, что в этом случае объемом информации при обмене управляет не программа, а ВГ 93). После считывания вызывающая программа корректирует адрес загрузки следующего сектора таким образом, как будто было считано 256 байт. Запомните этот важный момент.
Перед вызовом: RG C=#7F, HL -адрес загрузки, DE - время ожидания готовности ВГ 93.
Подпрограмма записи.
#3FBA SAVE: ...
Построение этой подпрограммы аналогично подпрограмме чтения.
Эти подпрограммы замечательны еще и тем, что они успешно функционируют не только при чтении/записи сектора, но и при чтении/записи дорожки и при чтении адреса.
Для операции над секторами, непосредственно этими подпрограммами мы пользоваться не сможем, вернее сможем, но не будем этого делать, т.к. не сможем отдельно установить номер сектора, а если будем использовать эти подпрограммы более расширенно, включив туда установку номера сектора, то при нестандартном количестве секторов на дорожке это ни к чему хорошему не приведет. Если в программе TR DOS сможете найти безобидный способ установки регистра сектора, тогда другое дело. А пока мой совет будет таков: для операций с секторами длиной, отличной от 256 байт, или при нестандартном количестве секторов на дорожке, можно использовать обычную стандартную подпрограмму с вызовом через #3D13, или следующую подпрограмму:
#1Е64 чтение/запись сектора(ов). RG A - признак операции: 0 - чтение, иначе запись; D - номер трека; Е -номер сектора; HL - адрес буфера; В - количество секторов.
Здесь есть одно но. Операцию чтения/записи необходимо производить по одному сектору, и после операции с каждым сектором восстанавливать необходимые значения адреса буфера, номера трека, номера сектора.
Вернемся к конкретным методам защиты от копирования.
КОДИРОВАНИЕ ФАЙЛОВ. Смысл этого метода состоит в том, что кодируются Бейсик-загрузчики, a boot раскодирует перед запуском выбранный Бейсик-загрузчик. Возможен вариант, при котором кодируются кодовые файлы, но раскодировать их перед запуском программы должен уже загрузчик программы, а не boot.
Ключевая информация для раскодирования должна быть записана на том же диске, но в таком месте, откуда её невозможно изъять стандартными способами.
Лучше всего ключевую информацию скрыть где-нибудь в индексном массиве, или пробеле. Записать её можно, отформатировав соответствующим образом выбранную дорожку, а считать с помощью команды ВГ 93 ЧТЕНИЕ ДОРОЖКИ, или ЧТЕНИЕ АДРЕСА.
Отформатировать дорожку можно, сформировав в памяти информацию дорожки, затем с помощью подпрограммы #3EDF выщать на ВГ 93 команду ЗАПИСЬ ДОРОЖКИ, и передать управление подпрограмме SAVE, предварительно передав в регистры процессора требуемые параметры.
Может быть Вам понадобится, чтобы в процессе работы какие-либо из Ваших программ изменяли "секретные" места на диске. В этом случае приведенный выше способ записи несколько громоздок (определение и уничтожение массива информации дорожки занимает относительно много памяти и времени, что облегчает взлом), можно воспользоваться методом TR DOS по аналогии с программой FORM.TRACK, поместив в памяти её аналог и используя вызовы подпрограмм #20AF и #20B1. В последнем случае есть недостаток: время на вызов подпрограмм увеличивает вероятность потери информации контроллером, но для наших целей (сохранить 1-2 байта) он вполне подходит, если использовать не несущую информации дорожку.
Для чтения кодовой информации используется команда ЧТЕНИЕ ДОРОЖКИ, но это опять громоздко. Можно считывать её в конец памяти, чтобы ненужная часть пошла в область ПЗУ, или забить область памяти, куда будет производиться считывание дорожки информацией, отличающейся от информации дорожки парой десятков байт в разных местах. Считывание дорожки осуществляйте с использованием подпрограммы LOAD.
Способы чтения/записи ключевой информации должны быть хорошо закрыпы от просмотра, поэтому выгоднее всего кодировать только Бейсик-файлы. Кодирование осуществляется отдельной программой, а раскодируются они при запуске программой boot.
При кажущейся сложности этого метода, его достаточно воплотить только один раз, а потом им только пользоваться. Кроме того, раз уж на диске присутствует "секретная" информация, можно к ней добавить и количество возможных копий, уменьшающееся на 1 при каждом использовании процедуры переноса дорожки с кодовой информацией на другой диск, создав, таким образом, возможность ограниченного копирования. Последнее является большим плюсом по сравнению дисками, с которых вообще невозможно снять ни одной копии.
В случае хорошего закрытия от просмотра этот метод очень эффективен.
НЕСТАНДАРТНЫЕ СЕКТОРА. В этом случае хотя бы boot должен находиться на секторах стандартной
длины.
Если Вы имеете дело не со своими .программами, то лучше всего отделить все их Бейсик-файлы и поместить только их на сектора нестандартной длины, boot считывает выбранный Бейсик-файл по принципу, изложенному при рассмотрении подпрограмм записи/чтения сектора.
Принцип работы этого метода следующий: при считывании нестандартных секторов системой TR DOS начала областей, в которые считываются отдельные сектора, отстоят друг от друга на 256 байт, следовательно, если длина секторов будет отлична от 256 байт, возникнут разрывы или наложения информации .
Как разметить дорожки с соответствующими длинами секторов мы уже знаем: достаточно выдать на ВГ 93 код команды ЗАПИСЬ ДОРОЖКИ и, с помощью подпрограммы SAVE передать информацию в дисковую систему. Массив информации должен быть подготовлен правильно: если код длины сектора содержит, например, #02, то и массив данных этого сектора должен содержать соответственно 512 байт.
Кроме того, следите за количеством секторов на дорожке, чтобы информация последних не наложилась на первые при очевидном обороте диска.
АВТОЗАПУСК ДИСКА. Чтобы понять, как это сделать, достаточно сопоставить несколько ключевых моментов.
1. Любая операция по просмотру, или изменению информации диска влечет собой предварительное считывание. 8-го сектора.
2. Если 8-й сектор отформатирован, скажем, на 512 байт, то эти 512 байт исправно считаются в память независимо от того, что освобожден буфер размером всего в256 байт.
3. Буфер резервируется с адреса #5D25. На 257 байт сдвигается не только область Бейсика, а ещё область информации о каналах, расположенная перед областью Бейсика.
Если случилось чудо и Вы догадались, что это могло бы означать, то можете сделать следующее: сформируйте вторую половину 8-го сектора таким образом, чтобы изменить адреса программ обслуживания каналов на адрес своей программы, которая должна быть размещена здесь же и считана в память вместе с 8-м сектором. Что должна сделать эта программа? Восстановить все, что Вы испортили длинным сектором и запустить boot.
Если Вы испытываете затруднения в понимании информации о каналах, загляните в сборник "ZX-РЕВЮ" за 1991 год.
Автозапуск диска - экзотическая, но очень слабая защита. Этот метод сразу заметен, и остается только переформатировать 8-й сектор.
Использование секторов нестандартной длины тоже без особого труда поддается анализу.
В случае кодирования файлов доступ к достоверной информации файла невозможен, если не обнаружить местонахождение кода и не вскрыть раскодирующую программу, а закрыть её от просмотра можно очень хорошо, было бы желание.
Закрытие от просмотра служит не столько для того, чтобы исключить просмотр программы (это невозможно), а для того, чтобы максимально его затруднить.
Наиболее удобным в этом случае является использование команды процессора XOR для кодирования отдельных блоков программы с использованием регистра регенерации памяти R, значение которого непостоянно. Ниже Вы увидите нечто подобное.
Различные способы реализации этого метода описаны в работе Н.Родионова "Адаптация программ к системе TR DOS". Они несложны для вскрытия: достаточно, например, переместить часть программы в другую область памяти, но, тем не менее, сначала надо определить, каким образом происходит раскодирование, что в некоторых замысловатых случаях не легко. Кроме того, циклическое кодирование применяется не для того, чтобы создать короткую непробиваемую защиту, а для того, чтобы с помощью множества операций кодирования извести того, кто будет раскручивать в обратную сторону этот процесс.
Пример процедуры кодирования/раскодирования (операция XOR, выполненная повторно, восстанавливает прежнее значение).
LD |
HL, M2 |
Начало кодированного блока |
LD |
В,ДЛИНА |
Его длина. |
LD |
А, #50 |
Любое число. |
LD |
R, A |
Начальная установка R. |
LD |
A, R |
|
XOR |
(HL) |
Кодировать/раскодировать |
LD |
(HL),A |
Пересылка байта результата |
INC |
HL |
|
DJNZ |
M1 |
|
M2
Если при вскрытии программы после раскрутки одного такого блока глазам является следующий, само собой в различных вариациях, и так много раз, и не знаешь, какой сюрприз тебя ждет дальше, это нелегко вынести. А сюрприз может быть следующим:
#С961
AND |
(HL) |
RET |
|
XOR |
В |
RET |
|
XOR |
D |
RET |
|
XOR |
H |
RET |
|
XOR |
(HL) |
RET |
|
LD |
(HL),A |
RET |
|
AND |
H |
RET |
|
LD |
HL, 0 |
ADD |
HL, SP |
LD |
(#8000),HL |
LD |
SP,#C9B0 |
LD |
A,5 |
LD |
R, A |
LD |
DE,#5912 |
LD |
HL,#C982 |
LD |
B, #20 |
INC |
HL |
LD |
A, R |
ADD |
A, L |
RR |
D |
POP |
IY |
LD |
SP, IY |
DJNZ |
#C9B1 |
#C983 #C983
закодированным текст дальнейшей программы, необходимую подпрограмму и запустит её.
#С9А4
#C9B0 #C9B1
Не знаю, как у кого, а у меня, прежде всего, возникли бы сомнения в правильности предыдущего раскодирования.
Тем не менее, этот, на первый взгляд бред, неплохо работает.
Перемещение участков программы с целью вставить точку прерывания монитора выводит её из строя. Программа может работать в одном единственном варианте своего размещения в памяти.
Последняя часть программы находится в области стека и состоит из собственных адресов возврата. Разумеется, нажатие кнопки MAGIC все разрушает.
Существует неплохой способ борьбы с подобными неприятностями (не с точки зрения взломщика, а с точки зрения программиста, ведь это надо сначала как-то закодировать).
Этот способ основан на применении второго режима прерываний с расчетом задержки:
DI |
|
LD |
I, #FE |
LD |
HL, M3 |
LD |
(#FEFF),HL |
IM |
2 |
EI |
|
HALT |
|
LD |
HL, M4 |
LD |
(#FEFF),HL |
EI |
|
LD |
HL,#0BA9 |
DEC |
HL |
LD |
A, H |
OR |
L |
JR |
NZ,M5 |
JP |
#C961 |
IM |
1 |
DI |
|
команды выхода в монитор.
Эта программа запустит приведенную ранее раскодирующую программу, но через время, равное 20 ms минус программную задержку, прервет её и передаст управление монитору.
Остается только подстроить задержку, чтобы успели раскодироваться несколько байтов (здесь главное, чтобы раскодируемая программа не успела полностью раскодироваться и запуститься), чтобы просмотреть их и определить, как кодируются первые три байта, и перед следующим запуском программы раскодирования записать их таким образом, чтобы в результате работы программы здесь получилась точка прерывания монитора, и исходный текст у Вас в кармане. Только не забудьте, потом опять исправить первые три байта, из которых Вы сделали точку прерывания.
Лучше всего кодировать отдельные (ключевые) небольшие участки программы с коротким временем жизни. То есть: перенос блока на исходное место в памяти, раскодирование, запуск, работа, уничтожение.
Примечание: адреса подпрограмм подходят для всех версий TR DOS, кроме 5.01.
* * *