Учебная работа. Курсовая работа: Защита программы от нелегального копирования

1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (Пока оценок нет)
Загрузка...
Контрольные рефераты

Учебная работа. Курсовая работа: Защита программы от нелегального копирования

Министерство образования и науки Украины

ПОЯСНИТЕЛЬНАЯ ЗАПИСКА

к курсовому проекту

на тему «защита программки от незаконного копирования»

по курсу «Кодирование и защита инфы»

2004


Содержание

Введение

1 Описание имеющихся способов

Проверка типа ПК и версии ДОС

Проверка даты производства и контрольной суммы ПЗУ

Проверка места расположения файла на диске

Проверка состава аппаратных средств

Внедрение укрытых частей программки и особенностей физических носителей инфы

2 Обоснование выбора применяемого способа

3 Описание программки

Заключение

Перечень ссылок

приложение


Введение

В данном курсовом проекте рассматривается неувязка сотворения программки, незаконное (без ведома официального распространителя) копирование которой приводит к потере программки возможности нормально работать.

Сейчас в нашей стране сложилась феноминальная ситуация, когда в большинстве российских ПК в главном употребляется «ворованное» программное обеспечение. Редчайшее исключение составляют те мастера, которые работают в личных фирмах либо на совместных предприятиях.

В согласовании с интернациональным правом труд создателя программки приравнен к труду остальных творцов умственной принадлежности, таковых как писатели, музыканты, живописцы, и подлежит защите. Но в отличие от книжки либо картины общее копирование программки не вызывает каких-то технических проблем и не просит специального оборудования (если, очевидно, не считать ПК ). В отношении программка намного беззащитнее, чем, статуя либо книжка. Общий вред от незаконного копирования программ по оценкам западных профессионалов составляет от 2 до 10 миллиардов. баксов в год [1].

Полностью понятным потому кажется рвение создателей придать своим программкам такие характеристики, которые бы делали неосуществимым их незаконное (кроме воли создателей) распространение. тут будет рассмотрена техника разработки программ, в той либо другой мере владеющих этими качествами. Но опытнейший программер, владеющий надлежащими способностями, постоянно сумеет отыскать то пространство (те места) в программке, где принимается решение о легальности копии, и, изменив несколько б в коде программки лишить ее способности самозащиты. Конкретно по данной для нас причине ведущие фирмы-производители массового программного обеспечения фактически никогда не ставят защиту на свои программки, справедливо полагая, что не стоит растрачивать силы и средства на создание систем защиты, если все равно программка будет «вскрыта». Эти компании получают свои несколько по другому обстоит дело с программками, которые не рассчитаны на массового юзера. К таковым относятся различного рода системы автоматического проектирования, предыздательские системы, спец базы данных и т. п. Как правило, это очень наукоемкие продукты, т. е. в их в особенности велик вклад классных профессионалов в определенных прикладных областях. труд таковых профессионалов обычно высоко ценится, потому общие Издержки на создание подобного рода программ могут быть очень значительны, что в сочетании со сравнимо маленьким кругом возможных юзеров делает стоимость каждой копии весьма большенный. Такие программки обычно снабжаются защитой, потому что ограниченный Спрос на их сформировывает очень специфичный Рынок: покупателями этих программ являются спецы в определенной прикладной деятель, которые не имеют подходящих способностей и, основное, желания заниматься «вскрытием» программ.


1. Описание имеющихся способов

Будем считать программку защищенной от незаконного копирования, если в ней есть интегрированные средства, дозволяющие проверить саму программку и/либо соответствующие признаки ПК , на котором она производится, с целью найти, была ли копия программки изготовлена с соблюдением всей нужной технологии. Если разработка сотворения копии была нарушена, программка перестает работать обычным образом. Таковым образом, законное копирование предполагает внедрение некой особенной технологии сотворения копий, в необычности которой и заключается весь фокус. Только посвященный в этот секрет человек при помощи специального оборудования либо особенной программки может сделать законную копию.

Неважно какая копия защищенной программки обязана содержать внутри себя либо во наружном файле «ключ» — одно либо несколько кодовых чисел. В момент проверки программка ассоциирует некие специальные признаки рабочей среды с заблаговременно закодированными в ключе и по результатам сопоставления сформировывает соответственный признак. Таковым образом, не достаточно сделать копию программки: для того чтоб эта копия стала работоспособной, ей нужно передать ключ, настроенный на работу с полностью определенным компом [1].

Анализ личных признаков среды предполагает, какими специфичными признаками может владеть та программно-аппаратная среда, в какой работает программка. Для IBM-совместимых ПК этими признаками могут быть:

— тип ПК и тип (версия) операционной системы;

— дата производства ПЗУ ==BIOS и/либо его контрольная сумма;

— физическое положение файлов на дисковом носителе;

— состав аппаратных средств;

наличие укрытых частей программки;

— физические индивидуальности (в том числе недостатки) носителя.

Некие из этих признаков весьма персональны (к примеру, физические индивидуальности плохого носителя), остальные владеют наименьшей индивидуальностью (тип ПК , версия ДОС). Программка может применять один либо несколько признаков для проверки легальности копии. При всем этом особенное программка рассчитана на работу на определенном ПК , выбираются одни признаки, если она может свободно передвигаться с 1-го компа на иной без утраты работоспособности, — остальные. Назовем программки первого типа стационарными, а второго – мобильными. Защита тех и остальных программ имеет свои индивидуальности, которые мы будем дискуссировать.

Во всех вариантах проверка легальности не обязана значительно сказаться на быстродействии программки либо добиваться от юзера каких-либо доп действий. к примеру, навряд ли можно считать сколько-либо действенной систему, использующую пароль (кодовое слово), который должен ввести юзер: во-1-х, юзер может запамятовать это слово и тем лишиться легитимно полученной программки, а во-2-х, ничто не мешает ему сказать это слово хоть какому другому юзеру. Система защиты не обязана инспектировать юзера, она обязана проверить копию [3].

1.1
Проверка типа ПК и версии ДОС

Эти проверки весьма ординарны, но не владеют высочайшей степенью особенности в том смысле, что могут существовать почти все сотки тыщ ПК 1-го типа, в каких употребляется однообразная ДОС. Потому обычно эти проверки употребляются в сочетании с проверками остальных личных признаков, и предусмотрены для защиты стационарных программ.

Тип ПК записан в ПЗУ по адресу $F000:$FFFE, т. е. в предпоследнем б мегабайтного адресного места ПК [4].

Версию ДОС можно получить при помощи функции ДОС $30 [4]. При воззвании к данной для нас функции в регистре AL ворачивается старший, а в AH – младший номера версии. Регистр AL может содержать 0, если употребляется наиболее ранешняя ДОС чем 2.0.

Ценность этих проверок заключается в их исключительной простоте, но могут найтись тыщи однотипных компов, на которых употребляется однообразная версия ДОС, потому ограничиваться лишь этими проверками навряд ли имеет смысл.

1.2
Проверка даты производства и контрольной суммы ПЗУ

Неизменное запоминающее устройство (ПЗУ) является неотъемлемой составной частью хоть какого IBM – совместимого ПК . содержимое ПЗУ учитывает индивидуальности реализации определенного ПК и может различаться в компах различного типа. Наиболее того, в конце ПЗУ (по адресу $F000:$FFF5) обычно записывается дата его производства, потому даже для ПК 1-го типа (и одной и той же фирмы-изготовителя) контрольная сумма ПЗУ различается в различных экземплярах ПК . Дата производства ПЗУ занимает 8 смежных б. Данные хранятся в символьном виде в формате ММ/ДД/ГГ [1],[4].

Как указывает практика, неважно какая уважающая себя Компанияизготовитель ПЗУ для IBM – совместимых ПК кропотливо смотрит за правильностью данной для нас даты. естественно, любой денек во всем мире делаются тыщи микросхем ПЗУ с схожей датой, но возможность того, что кое-где употребляется ПК такого же типа и с таковой же датой производства, как и у компа обладателя программки очень мала. Очевидно, при массовой закупке ПК , к примеру для оснащения учебного класса, почти все либо даже все сразу обретенные компы могут иметь одну и ту же дату производства ПЗУ. Но в таковых вариантах способность защищенных программ свободно переноситься с 1-го схожего компа на иной можно разглядывать как полностью естественную. Эта проверка употребляется для защиты стационарных программ.

1.3
Проверка места расположения файла на диске

Неплохой индивидуальностью владеет физический номер кластера, начиная с которого на твердом диске размещается файл с защищенной программкой. Вправду, навряд ли что-либо другое в аппаратно-программной среде ПК (не считая, очевидно, содержимого оперативки) изменяется настолько же оживленно, как файловая структура твердого диска. При разработке законной копии номер исходного кластера для файла программки на твердом диске в общем случае совсем случаен. Если в момент пуска программка проверит этот номер, то в подавляющем большинстве случаев она просто увидит факт незаконного копирования. Тем не наименее, таковой метод защиты недозволено считать безупречным по почти всем причинам. Проверка номера кластера производится далековато не так просто, как проверка типа ПК либо даты производства ПЗУ, так как в обычном Турбо Паскале нет средств для работы с дисками физически. Но основной недочет заключается в другом: хоть какое изменение местоположения файла даже в границах 1-го каталога приводит к тому, что ранее установленная копия становится незаконной. Это очень неловко для юзера, в индивидуальности, если он нередко прибегает к процедуре переупорядочения файловой структуры при помощи дефрагментации твердого диска. Если юзер считает, что номер единственного кластера не владеет нужной степенью особенности, то можно инспектировать цепочку кластеров по таблице FAT либо исходные кластеры нескольких файлов. Очевидно, таковая проверка может употребляться для защиты лишь стационарных программ [1],[3].

1.4
Проверка состава аппаратных средств

программка может проверить размер доступной оперативки, наличие и размер расширенной памяти, тип центрального микропроцессора и ориентировочную скорость его работы, наличие математического сопроцессора, количество и тип дисководов для гибких дисков, количество и тип каналов для подключения наружных устройств. Любая из этих черт может повторяться в тыщах остальных ПК , но они все в комплексе будут довольно персональны и потому могут с фуррором употребляться для защиты стационарных программ.

Некие зарубежные компании для защиты мобильных программ выпускают так именуемые электрические ключи – относительно дешевенькие устройства, которые перед пуском защищаемой программки подсоединяются к обычному каналу поочередного либо параллельного ввода-вывода. электрические ключи реализуются на базе заказных микросхем и производят необходимое интерфейсное взаимодействие с защищаемой программкой.

Получение сведений о конфигурации ПК быть может осуществлено средством чтения данных по определенным адресам КМОП-памяти. Наиболее тщательно о этом написано в [1], [4], [5].

1.5
Внедрение укрытых частей программки и особенностей физических носителей инфы

Довольно действенным методом защиты (основным образом для мобильных программ) может служить создание и внедрение укрытых частей программки и/либо особенностей физических носителей инфы [3].

Сокрытые части программки – это участки дискового носителя, тем либо другим методом связанные с программкой, но не зафиксированные в качестве файлов ДОС. В подавляющем большинстве случаев программке нет необходимости искусственно создавать такие участки, так как они уже имеются в «хвосте» хоть какого файла. Дело в том, что ДОС распределяет дисковое место кластерами, имеющими длину от 512 до 4096 и наиболее байтов. Даже если нужная длина файлов составляет всего несколько б, ДОС выделит такому файлу целый кластер, большая часть которого будет заполнена «мусором» — случайной информацией, сохранившейся от предшествующего использования кластера в составе другого файла. При копировании файлов обычными средствами ДОС копируется лишь нужная часть кластера, так что на новеньком месте «хвост» файла в общем случае будет заполнен иной информацией [1].

Наиболее утонченный, но никак не наиболее действенный метод защиты состоит в разработке и использовании доп укрытых кластеров. Такие кластеры могут помечаться в FAT как сбойные либо «потерянные» (т. е. не относящиеся ни к какому зарегистрированному файлу). Во всех вариантах, помещается ли ключ в хвост файла либо в отдельный кластер, защита быть может просто нейтрализована, если употребляется копирование дискеты «блок в блок» при помощи утилиты DISKCOPY либо подобных несистемных программ.

Значительно наилучшей способностью противостоять попыткам незаконного копирования владеет система защиты, основанная на учете личных особенностей дискет, до этого всего на анализе неискоренимых изъянов. В этом случае система проверки защиты «понимает» перечень дефектных секторов уникальной дискеты и пробует их отформатировать. Если опосля форматирования обмен информацией с секторами проходит нормально, означает соответственный сектор – бездефектный и, как следует, юзер имеет дело с незаконной копией дискеты. основное достоинство этого метода защиты заключается в принципной невозможности сделать программными средствами на обычной дискете неискоренимые недостатки.

Как указывает практика, только весьма маленькое число дискет (наименее 1%) имеет заводские недостатки производства, потому при массовом тиражировании коммерческих программ приходится создавать такие недостатки искусственно. Для этого время от времени употребляются лазеры, а почаще – обычная булавка. Но следует подразумевать, что царапинки и проколы на поверхности дискеты могут разрушить считывающие головки накопителя [1].

Недочетом описанного метода защиты являются относительно огромные Издержки времени на контроль всей дискеты и утрата работоспособности некой ее части. От этих недочетов можно избавится, если на дискете сделать программным методом неординарные для ДОС индивидуальности [1]. Этими возможностями могут быть:

— необычная длина секторов на всей дискете либо какой-нибудь одной ее дорожке;

— особое размещение (фактор чередования) секторов на дорожке;

— необычное количество дорожек.

Очевидно, надежность защиты при помощи программно создаваемых особенностей структуры дискеты будет существенно меньше. Тем не наименее, следует держать в голове, что почти всегда «взломщик» программки стремится поменять ее код, а не имитировать особенный метод копирования дискет, так что легкая защита, сплетенная с внедрением доборной дорожки, может оказаться никак не ужаснее, чем защита при помощи «лазерной дырки».

Как понятно, ДОС может оперировать лишь с секторами длиной по 512 б (исключением является создание утилитой VDISK виртуальных электрических дисков, размер секторов которых может различаться от 512 б). В то же время контроллеры ГД способны создавать и применять секторы другого размера – по 128, 256 либо 1024 б. При обнаружении сектора необычного размера ДОС считает этот сектор сбойным, что можно применять для защиты программки [5].

Дальше, дорожки на дискете размещаются так, что самой наружной является нулевая дорожка, а самой внутренней – дорожка с наибольшим номером. Обычно ДОС употребляет однообразное количество секторов на всех дорожках, потому угловой размер всякого сектора постоянен, а, как следует, плотность записи инфы вырастает с ростом номера дорожки. количество секторов на дорожке и дорожек на дискете ДОС назначается так, чтоб даже для самой внутренней дорожки эта плотность не превысила некого значения, еще гарантирующего уверенную работу схем контроллера ГД. На практике оказывается, что подавляющее большая часть современных контроллеров способно обслуживать большее количество дорожек, чем принято в ДОС. В целях защиты от копирования программка может сделать и применять одну либо несколько доп дорожек, но не заносить их в перечень «видимых для ДОС», т.е. не изменять поле в загрузочном секторе, указывающее общее количество секторов на диске. Очевидно, вероятен и иной вариант: можно «украсть» у ДОС несколько дорожек, уменьшив обычное

В конце концов, программка может применять необычный фактор чередования секторов. Этот фактор влияет на время чтения/записи группы смежных секторов. Если какую-либо дорожку отформатировать с преднамеренно неоптимальным фактором чередования, время чтения данной для нас дорожки может оказаться приметно больше, чем при чтении хоть какой иной дорожки – это еще один метод защиты.


2. Обоснование выбора применяемого способа

В данном курсовом проекте реализовано два способа защиты программки от незаконного копирования. Это защита стационарной программки и мобильной. Для защиты стационарной программки был избран способ проверки даты сотворения ПЗУ. Этот способ был избран благодаря тому, что дата сотворения ПЗУ является довольно персональной для всякого ПК , если лишь они не были приобретены в партии, к примеру для оснащения компьютерного класса, но в таком случае защита программки от незаконного копирования становится неактуальной. Для защиты мобильного варианта программки на дискете была сотворена доборная дорожка с секторами необычного размера. Этот вариант был избран поэтому, что в данном случае на дискету не требуется наносить физических повреждений, что приводит к потере работоспособности некой ее части. И эта защита может оказаться не никак не ужаснее, чем защита с внедрением изъянов дискет.


3. Описание программки

защита программ от незаконного копирования реализована в виде модуля, написанного на языке Turbo Pascal. Выбор этого языка программирования разъясняется тем, что этот язык относительно прост, и при всем этом дозволяет созодать ассемблерные вставки и впрямую обращаться к памяти. Этот модуль подключается к программкам, которые требуется защитить, и в программках употребляются его функции.

Модуль рассчитан на защиту как мобильных, так и стационарных программ. Главный процедурой модуля является процедура ProtCheck, осуществляющая контроль копии. Легальность мобильного варианта программки устанавливается за счет контроля укрытого сектора на главный дискете, в случае стационарного варианта проверяется дата сотворения ПЗУ. Две остальные процедуры модуля разрешают установить защиту файла на твердом диске (процедура SetOnHD) и удалить стационарный вариант программки (процедура RemoveFromHD).

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

Для сотворения законной копии программки обязана употребляться особенная разработка подготовки дискеты. Эта изюминка в данном случае состоит в том, что на обычной дискете поперечником 3,5 дюйма, рассчитанной на емкость 1,44 Мб, создается доборная дорожка из необычных 256-байтных секторов. один из секторов данной для нас дорожки употребляется для записи главный инфы. В процессе проверки легальности копии программки процедура ProtCheck считывает этот сектор и контролирует его содержимое. Для сотворения ключа употребляется программка Diskette. Эта программка на дискете емкостью 1,44 Мб делает 81-ю дорожку с 18-ю секторами размером по 256 б, при этом для нее употребляется оборотный фактор чередования, т. е. секторы на дорожке располагаются в последовательности 18,17,16,…,2,1. Для этого она изменяет таблицу характеристик дискеты, которую берет либо в ПЗУ либо в ОЗУ по определенному адресу [4], [5]. программка сохраняет копию старенькой таблицы характеристик дискеты и опосля окончания работы восстанавливает ее. В 1-ый сектор новейшей дорожки записывается случайная информация и число установок на твердый диск защищаемой программки, (это число установок вводится юзером при разработке главный дискеты). Потом сектор читается и проверяется корректность операции записи-чтения. В конце программки измеряется время доступа к новейшей дорожке и обычной дорожке. Для чтения и записи сектора употребляется прерывание $13. В случае ошибки чтения либо записи сектора, программка выводит сообщение о ошибке и восстанавливает старенькую таблицу характеристик дискеты.

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

Процедура ProtCheck вызывается из защищаемой программки каждый раз, когда требуется установить легальность копии. Ее заголовок имеет последующий вид:

ProcedureProtCheck(varNorma,Alarm;varRes:Integer)

В теле процедуры параметры-переменные Norma и Alarm трактуются как характеристики процедурного типа

type

ProcType = Procedure;

т. е. являются адресами 2-ух процедур без характеристик. Процедура Alarm вызывается в случае, если программка нашла признаки незаконного копирования, а Norma – если эти признаки отсутствуют. В переменной Res ворачивается итог работы ProtCheck: 0 – если производилась процедура Norma (законная копия), 1 — производилась Alarm (незаконная копия), 2 – не производилась ни та, ни иная процедура, потому что программка не нашла дискету в приводе ГД и не смогла проверить легальность копии.

Процедура ProtCheck начинает работу с проверки поля Hard в глобальной типизированной константе Key. Это поле употребляется для анализа стационарности программки: если поле имеет случае – стационарного варианта (установка поля Hard и всей константы Key осуществляется при помощи функции SetOnHD, см. ниже).

При контроле мобильного варианта программка пробует прочесть поначалу на диске А, а если это не удается – на диске В главный сектор (1-ый сектор на нулевой поверхности дорожки номер 81) размером 256 б. Этот сектор должен содержать последующую информацию:

1-й б – ключ для шифровки содержимого сектора при помощи операции XOR;

17-й бколичество уже сделанных стационарных копий;

200-й б – наибольшее количество стационарных копий (если 255 — количество копий не ограничено);

256-й б – контрольная сумма со 2-го по 255-й б.

Если поле Hard константы Key содержит нулевое случае поле Dat содержит идеал даты сотворения ПЗУ, а поле Hard употребляется как ключ для шифровки этого поля при помощи операции XOR.

Если контроль стационарного варианта дает плохой результат (незаконная копия), автоматом осуществляется анализ мобильного варианта (контроль дискеты). Таковым образом, неважно какая копия программки гарантированно работает, если в распоряжении юзера есть главная дискета. Но опосля правильной установки программки на твердый диск при помощи процедуры SetOnHD программка может работать и без данной для нас дискеты до того времени, пока она не будет перенесена на новейший комп, дата сотворения ПЗУ которого различается от эталонной.

Для правильного сотворения стационарной копии программки употребляется функция SetOnHD, имеющая последующий заголовок:

FunctionSetOnHD: Integer;

Перед внедрением функции SetOnHD нужно хоть какими обычными для ДОС средствами скопировать программку в один из каталогов твердого диска. Эта функция вызывается в таковой «незаконной» копии, перед сиим в хоть какой привод ГД нужно вставить главную дискету со снятой защитой от записи. Итог, возвращаемый функцией, имеет последующий смысл:

1 – в привод ГД не вставлена дискета;

2 – в привод вставлена дискета не эталонного типа (не 1,44 Мб либо нет укрытого сектора);

3 – дискета защищена от записи либо при записи на нее появилась ошибка;

4 – данный вариант программки не скопирован за ранее на твердый диск;

5 – ошибка доступа к твердому диску (программка не может прочесть свой файл либо не может записать в него новое

6 – исчерпан предел стационарных копий;

7 – данная программка уже представляет собой стационарный вариант программки, т. е. константа Key в ней уже определена.

Хоть какое неотрицательное диск.

В процессе установки программка контролирует главную дискету, потом описывает соответствующие признаки данного ПК и вносит эти признаки в константу Key. Так как защищается лишь тот экземпляр программки, в каком вызвана функция SetOnHD, необходимо предугадать соответственный вариант пуска программки. к примеру, можно проанализировать ключи команды пуска с целью проверки специального ключа сотворения стационарного варианта либо предугадать подобающую опцию в диалоговом меню, создаваемом программкой в процессе работы.

Функция RemoveFromHD производит оборотные деяния: уничтожает текущую стационарную копию и подходящим образом наращивает припас неизрасходованных установок программки. Она возвращает одно из последующих значений:

1 – в привод ГД не вставлена дискета;

2 – в привод вставлена дискета неэталонного типа (емкостью не 1,44 Мб либо нет укрытого сектора);

3 – дискета защищена от записи либо при записи на нее появилась ошибка;

4 – данный вариант программки не скопирован за ранее на твердый диск;

5 – ошибка доступа к твердому диску (программка не может прочесть свой файл либо не может записать в него новое

Хоть какое неотрицательное диск.

Также прилагается программка TEST.EXE, которая иллюстрирует приемы работы с модулем F_Prot. Программка анализирует ключи пуска: если употребляется ключ /SET, осуществляется установка программки на твердый диск, если ключ /REMOVE, уничтожается стационарная копия программки, если этих ключей нет в команде пуска, программка производит контроль легальности копии. Главная дискета обязана быть за ранее подготовлена при помощи программки Diskette.

программка Diskette и модуль F_Prot употребляют модуль F_Disk, который предназначен для работы с диском физически.


Заключение

В итоге выполнения данного курсового проекта, был написан модуль для защиты программ от незаконного копирования. Данный модуль дозволяет защищать как стационарные и мобильные варианты программ. Этот модуль подключается к программке, которую требуется защитить, и программка употребляет его функции. Если функция проверки легальности копии возвращает нехороший итог, то, на свое усмотрение, программер в состоянии сделать так, чтоб программка либо не запускалась либо выводила предупреждающее сообщение и реализовывала не все свои способности. Для защиты стационарного варианта программки употребляется проверка даты сотворения ПЗУ данного ПК , если она не равна эталонной, то копия считается незаконной. Для защиты мобильного варианта программки на главный дискете была сотворена дорожка с секторами необычного размера. 1-ый сектор дорожки содержит информацию, о программке, защищаемой от незаконного копирования. Главная дискета была подготовлена при помощи программки Diskett. Для тестирования работы модуля была написана программка Test.exe. Она показала, что написанный модуль работает правильно.


Перечень ссылок

1. Фаронов В.В. Турбо Паскаль (в 3-х книжках). Кн.3. Практика программирования. – М.: Учебно-инженерный центр «МВТУ – ФЕСТО ДИДАКТИК», 1993. – 304с.

2. Юров В., Хорошенко С. Ассемблер: учебный курс – СПб: Издательство «Питер», 2000. – 672с.

3. Расторгуев С.П., Дмитриевский Н.Н. Искусство защиты и «раздевания» программ. – М.: Совмаркет, 1991. – 94с.

4. Фролов А.В., Фролов Г.В. Аппаратное обеспечение IBM PC: В 2-х ч. Ч. 1. – М.: «ДИАЛОГ – МИФИ». 1992. – 208с.

5. Фролов А.В., Фролов Г.В. Аппаратное обеспечение IBM PC: В 2-х ч. Ч. 2. – М.: «ДИАЛОГ – МИФИ». 1992. – 208с.


приложение


Тексты программ:

1 Текст модуля F_Disk

2 текст модуля F_Prot

3 Текст программки Diskett

4 текст программки Test.exe

1 ТЕКСТ МОДУЛЯ F_DISK


{===================} UNIT F_Disk; {=====================}

секторам до конца дорожки, по головкам, по цилиндрам.

INTERFACE

type

{Информация из BPD загрузочного сектора:}

BPB_Type=record

SectSiz: Word; {Количество б в секторе}

ClustSiz: Byte; {Количество секторов в кластере}

ResSecs: Word; {Количество секторов перед FAT}

FatCnt: Byte; {Количество FAT}

RootSiz:Word; {Количество частей корневого каталога}

TotSecs:Word; {Количество секторов на диске}

Media:Byte; {Дескриптор носителя}

FatSize:Word {Количество секторов в FAT}

end; {BPB_Type}

{Доплнительная информация из загрузочного сектора:}

Add_BPB_Type=record

TrkSecs:Word; {Количество секторов на дорожке

для разделов меньше 32 Мбайт либо 0}

HeadCnt:Word; {Количество головок}

HidnSecLo:Word; {Количество спрятанных секторов для

разделов меньше 32 Мбайт}

HidnSecHi:Word; {Вкупе с HidnSecLo дает количество

спрятанных секторов для разделов больше 32 Мбайт}

LargSectors:LongInt; {Общее количество секторов для

разделов больше 32 Мбайт}

end; {Add_BPB_Type}

{Элементдисковогокаталога:}

Dir_Type=record case Byte of

1:(

Name:array[1..8] of Char; {Имяфайлаиликаталога}

Ext:array[1..3] of Char; {Расширение}

FAttr:Byte; {Атрибутыфайла}

Reserv:array[1..10] of Byte; {Резервноеполе}

Time:Word; {Время сотворения}

Date:Word; {Дата сотворения}

FirstC:Word; {Номер первого кластера}

Size:LongInt {Размер файла в б});

2:(NameExt:array[1..11] of Char)

end; {Dir_Type}

{Описатель логического раздела}

PartType=record

Act:Boolean; {Флаг активности раздела}

BegHead:Byte; {Головка начала раздела}

BegSC:Word; {Сектор/цилиндр начала}

SysCode:Byte; {Системный код}

EndHead:Byte; {Головка конца раздела}

EndSC:Word; {Сектор/цилиндр конца}

RelSect:LongInt; {Относительный сектор начала}

FoolSiz:LongInt {Размер в секторах}

end; {PartType}

{Загрузочный сектор диска}

PBoot=^TBoot;

TBoot=record

case Byte of

0:(

a:array[1..11] of Byte;

BPB:BPB_Type;

Add:Add_BPB_Type;

c:array[1..+$1BE-(SizeOf(BPB_Type)+SizeOf(Add_BPB_Type)+11)] of Byte;

Par:array[1..4] of PartType);

1:(b:array[1..512] of Byte)

end;

{Описатель диска по структуре IOCTL}

IOCTL_Type=record

BuildBPB:Boolean; {СтроитьВРВ}

TypeDrv:Byte; {Типдиска}

Attrib:Word; {Атрибутыдиска}

Cylindrs:Word; {Числоцилиндров}

Media:Byte; {Типносителя}

BPB:BPB_Type;

Add:Add_BPB_Type;

Reserv:array[1..10] of Byte;

end;

{Описательдиска}

TDisk=record

Number:Byte; {Номердиска 0=А, …}

TypeD:Byte; {Типдиска}

AttrD:Word; {Атрибутыдиска}

Cyls:Word; {Число цилиндров на диске}

Media:Byte; {Дескриптор носителя}

SectSize:Word; {Количество б в секторе}

TrackSiz:Word; {Количество секторов на дорожке}

TotSecs:LongInt; {Полная длина в секторах}

Heads:Byte; {Количество головок}

Tracks:Word; {Число цилиндров на носителе}

ClusSize:Byte; {Количество секторов в кластере}

MaxClus:Word; {Наибольший номер кластера}

FATLock:Word; {Номер 1-го сектора FAT}

FATCnt:Byte; {Количество FAT}

FATSize:Word; {Длина FAT в секторах}

FAT16:Boolean; {Признак 16-битового элемента FAT}

RootLock:Word; {Начало корневого каталога}

RootSize:Word; {Количество частей каталога}

DataLock:Word; {Исходный сектор данных}

end;

{Перечень описателей диска}

PListDisk=^TListDisk;

TListDisk=record

DiskInfo:TDisk;

NextDisk:PListDisk

end;

var

Disk_Error:Boolean; {Флагошибки}

Disk_Status:Word; {Кодошибки}

const

Disks:PListDisk=NIL; {Начало перечня описателей диска}

function ChangeDiskette(Disk:Byte):Boolean;

{Возвращает TRUE, если изменялось положение

запора на обозначенном проиводе гибкого диска}

procedure FreeListDisk(var List: PListDisk);

{Удаляетсписокописателейдисков}

procedure GetAbsSector(Disk,Head:Byte; CSec:Word; var Buf);

{Читает абсолютный дисковый сектор при помощи прерывания $13}

function GetCluster(Disk:Byte; Sector:Word):Word;

{Возвращает номер кластера по данному номеру сектора}

function GetDefaultDrv:Byte;

{Возвращает номер диска по дефлоту}

procedure GetDirItem(FileName:String; var Item:Dir_Type);

{Возвращает элемент справочника для обозначенного файла}

procedure GetDirSector(Path:String; var Disk:Byte; var Dirs,DirSize:Word);

{Возвращает адресок сектора, в каком содержится

начало подходящего каталога, либо 0, если каталог не найден.

Вход:

PATH — полное имя каталога (», если каталог текущий).

Выход:

DISK — номер диска;

DIRS — номер первого сектора каталога либо 0;

DIRSIZE — размер каталога (в элементах DIR_TYPE).}

procedure GetDiskInfo(Disk:Byte; var DiskInfo:TDisk);

{Возвращает информацию о диске DISK}

function GetDiskNumber(c:Char):Byte;

{Конвертирует имя диска A…Z в номер 0…26.

Если обозначено недействительное имя, возвращает 255}

function GetFATItem(Disk:Byte;Item:Word):Word;

{Возвращает содержимое обозначенного элемента FAT}

procedure GetIOCTLInfo(Disk:Byte; var IO:IOCTL_Type);

{Получить информацию о устройстве согласно общему выову IOCTL}

procedure GetListDisk(var List:PListDisk);

{Формируетсписокописателейдисков}

procedure GetMasterBoot(var Buf);

{Возвращает в переменную Buf основной загрузочный сектор}

function GetMaxDrv:Byte;

{Возвращает количество логических дисков}

function Getsector(Disk:Byte;Cluster:Word):Word;

{Конвертирует номер кластера в номер сектора}

function PackCylSec(Cyl,Sec:Word):Word;

{Упаковывает цилиндр и сектор в одно слово для прерывания $13}

procedure ReadSector(Disk:Byte;Sec:LongInt;NSec:Word;var Buf);

{Читает сектор(секторы) на обозначенном диске}

procedure SetAbsSector(Disk,Head:Byte;CSec:Word;var Buf);

{Записывает абсолютный дисковый сектор при помощи прерывания $13}

procedure SetDefaultDrv(Disk:Byte);

{Устанавливает диск по дефлоту}

procedure SetFATItem(Disk:Byte;Cluster,Item:Word);

{Устанавливает содержимое ITEM в элемент CLUSTER таблицы FAT}

procedure SetMasterBoot(var Buf);

{Записывает в основной загрузочный сектор содержимое Buf}

procedure UnPackCylSec(CSec:Word;var Cyl,Sec:Word);

{Декодирует цилиндр и сектор для прерывания $13}

procedure WriteSector(Disk:Byte;Sec:LongInt;NSec:Word;var Buf);

{Записывает сектор(секторы) на обозначенный диск}

IMPLEMENTATION

uses DOS;

var

Reg:Registers;

procedure Output;

{Формируетзначения Disk_Status и Disk_Error}

begin

with Reg do

begin

Disk_Error:=Flags and FCarry=1;

Disk_Status:=ax

end

end; {Output}

{———————-}

function ChangeDiskette(Disk:Byte):Boolean;

{Возвращает TRUE, если изменялось положение

запора на обозначенном приводе гибкого диска}

begin

with Reg do

begin

AH:=$16;

DL:=Disk;

Intr($13,Reg);

Output;

ChangeDiskette:=Disk_Error and (AH=6)

end

end; {ChangeDiskette}

{———————-}

procedure FreeListDisk(var List:PListDisk);

{Удаляет перечень дисковых описателей}

var

P:PListDisk;

begin

while List<>NIL do

begin

P:=List^.NextDisk;

Dispose(List);

List:=P

end

end; {FreeListDisk}

{———————}

procedure GetAbsSector(Disk,Head:Byte;CSec:Word;var Buf);

{Читает абсолютный дисковый сектор при помощи прерывания $13}

begin

with Reg do

begin

ah:=2; {Операциячтения}

dl:=Disk; {Номер привода}

dh:=Head; {Номер головки}

cx:=CSec; {Цилиндр/сектор}

al:=1; {Читать один сектор}

es:=seg(Buf);

bx:=ofs(Buf);

Intr($13,Reg);

Output

end

end; {GetAbsSector}

{———————}

function GetCluster(Disk:Byte;Sector:Word):Word;

{Возвращает номер кластера по данному номеру сектора}

var

DI:TDisk;

begin

GetDiskInfo(Disk,DI);

if not Disk_Error then with DI do

if(Sector-DataLock>=0) and (TotSecs-Sector>=0) then

GetCluster:= {Нормальноеобращение}

(Sector-DataLock) div ClusSize+2

else

GetCluster:=0 {Неправильный номер сектора}

else GetCluster:=0 {Неправильный номер диска}

end; {GetCluster}

{———————-}

function GetDefaultDrv:Byte;

{Возвращает номер диска по дефлоту}

begin

with Reg do

begin

AH:=$19;

MSDOS(Reg);

GetDefaultDrv:=AL

end

end; {GetDefaultDrv}

{———————}

procedure GetDirItem(FileName:String;var Item:Dir_Type);

{Возвращает элемент справочника для обозначенного файла}

var

Dir:array[1..16] of Dir_Type; {Буферна 1 секторкаталога}

Path:DirStr; {Маршрутпоиска}

NameF:NameStr; {Имяфайла}

Ext:ExtStr; {Расширениефайла}

Disk:Byte; {Номердиска}

Dirs:Word; {Номерсектора}

DirSize:Word; {Размеркаталога}

Find:Boolean; {Флагпоиска}

j:Integer; {Номерэлементакаталога}

{————}

procedure FindItem;

{Отыскивает подходящий элемент в секторах каталога}

var

k,i:Integer;

m:array[1..11] of char; {Массивимени}

Clus:Word; {Номеркластера}

DI:TDisk;

begin

GetDiskInfo(Disk,DI); {Получаем длину кластера}

ReadSector(Disk,Dirs,1,Dir); {Читаемпервыйсектор}

k:=0; {Количество просмотренных частей}

j:=1; {Текущий элемент каталога}

{Готовим имя и расширение для поиска}

FillChar(m,11,’ ‘);

Move(NameF[1],m[1],Length(NameF));

if ext<>» then

Move(Ext[2],m[9],Length(ext)-1);

Find:=False;

{Циклпоиска}

repeat

if Dir[j].Name[1]=#0 then

exit; {Обнаруженконецпоиска}

if (Dir[j].FAttr and $18)=0 then

begin {Проверяем еще одно имя в каталоге}

Find:=True;

i:=1;

While Find and (i<=11) do

begin

Find:=m[i]=Dir[j].NameExt[i];

inc(i)

end;

end;

if not Find then inc(j);

if j=17 then

begin

inc(k,16);

if k>=DirSize then

exit; {Дошли до конца каталога}

j:=1; {Продолжаем с первого элемента последующего сектора}

if (k div 16) mod DI.ClusSize=0 then

if succ(Dirs)<DI.DataLock then

inc(Dirs) {Корневойкаталог}

else

begin {Конецкластера}

{Новыйкластер}

Clus:=GetFATItem(Disk,GetCluster(Disk,Dirs));

{Новыйсектор}

Dirs:=GetSector(Disk,Clus)

end

else {Очередной сектор — в кластере}

inc(Dirs);

ReadSector(Disk,Dirs,1,Dir)

end

until Find

end; {FindItem}

{———}

begin {GetDirItem}

{Готовимимяфайла}

FileName:=FExpand(FileName);

FSplit(FileName,Path,NameF,Ext);

{Искатькаталог}

GetDirSector(Path,Disk,Dirs,DirSize);

Find:=Dirs<>0; {Dirs=0 — ошибкавмаршруте}

if Find then

FindItem; {Ищемнужныйэлемент}

if Find then

begin

{Переносимэлементкаталогав Item}

Move(Dir[j],Item,SizeOf(Dir_Type));

{Сброситьошибку}

Disk_Error:=False

end

else

begin {Файл не найден}

Disk_Error:=True;

Disk_Status:=$FFFF

end

end; {GetDirItem}

{————————}

Procedure GetDirSector(Path:String;var Disk:Byte;var Dirs,DirSize:Word);

{Возвращает адресок сектора, в каком содержится начало

подходящего каталога, либо 0, если каталог не найден.

Вход:

PATH — полное имя каталога (», если каталог — текущий).

Выход:

DISK — номер диска;

DIRS — номер первого сектора каталога либо 0;

DIRSIZE — размер каталога (в элементах DIR_TYPE).}

var

i,j,k:Integer; {Вспомогательные переменные}

Find:Boolean; {Признак поиска}

m:array[1..11] of Char; {Массив имени каталога}

s:string; {Вспомогательная переменная}

DI:TDisk; {Информация о диске}

Dir:array[1..16] of Dir_Type; {Секторкаталога}

Clus:Word; {Текущий кластер каталога}

label

err;

begin

{Исходный шаг: готовим путь к каталогу и диск}

if Path=» then {Если каталог текущий,}

GetDir(0,Path); {дополняем маршрутом поиска}

if Path[2]<>’:’ then {Если нет диска,}

Disk:=GetDefaultDrv {берем текущий}

else

begin {По другому проверяем имя диска}

Disk:=GetDiskNumber(Path[1]);

if Disk=255 then

begin {Недействительное имя диска}

Err: {Точка входа при неудачном поиске}

Dirs:=0; {Нет сектора}

Disk_Error:=True; {Флаг ошибки}

Disk_Status:=$FFFF; {Статус $FFFF}

exit

end;

Delete(Path,1,2) {Удаляемимядискаизпути}

end;

{Готовимциклпоиска}

if Path[1]=» then {Удаляемсимволы }

Delete(Path,1,1); {сначала}

if Path[Length(Path)]=» then

Delete(Path,Length(Path),1); {иконцемаршрута}

GetDiskInfo(Disk,DI);

with DI do

begin

Dirs:=RootLock; {Сектор с каталогом}

DirSize:=RootSize {Длинакаталога}

end;

ReadSector(Disk,Dirs,1,Dir); {Читаемкорневойкаталог}

Clus:=GetCluster(Disk,Dirs); {Кластерначалакаталога}

{Цикл поиска по каталогам}

Find:=Path=»; {Path=» — конец маршрута}

while not Find do

begin

{Получаемв S первоеимядосимвола }

s:=Path;

if pos(»,Path)<>0 then

s[0]:=chr(pos(»,Path)-1);

{Удаляем выделенное имя из маршрута}

Delete(Path,1,Length(s));

if Path[1]=» then

Delete(Path,1,1); {Удаляем разделитель }

{Готовим массив имени}

FillChar(m,11,’ ‘);

move(s[1],m,ord(s[0]));

{Просмотр еще одного каталога}

k:=0; {Количество просмотренных частей каталога}

j:=1; {Текущий элемент в Dir}

repeat {Цикл поиска в каталоге}

if Dir[j].Name[1]=#0 then {Еслиимя}

Goto Err; {Начинается с 0 — это конец каталога}

if Dir[j].FAttr=Directory then

begin

Find:=True;

i:=1;

while Find and (i<=11) do

begin {Проверяемтип}

Find:=m[i]=Dir[j].NameExt[i];

inc(i)

end

end;

if not Find then inc(j);

if j=17 then

begin {Исчерпансекторкаталога}

j:=1; {Продолжаем с 1-го элемента последующего сектора}

inc(k,16); {k — сколько частей просмотрели}

if k>=DirSize then

goto Err; {Дошлидоконцакаталога}

if (k div 16) mod DI.ClusSize=0 then

begin {Исчерпан кластер — отыскиваем последующий}

{Получаем новейший кластер}

Clus:=GetFATItem(Disk,Clus);

{Можно не инспектировать на конец цепочки,

т. к. каталог еще не исчерпан}

{Получаемновыйсектор}

Dirs:=GetSector(Disk,Clus)

end

else {Очередной сектор — в текущем кластере}

inc(Dirs);

ReadSector(Disk,Dirs,1,Dir);

end

until Find;

{Найден каталог для еще одного имени в маршруте}

Clus:=Dir[j].FirstC; {Кластерначала}

Dirs:=GetSector(Disk,Clus); {Сектор}

ReadSector(Disk,Dirs,1,Dir);

Find:=Path=» {Продолжаемпоиск, еслинеисчерпанпуть}

end {while not Find}

end; {GetDirSector}

{—————}

procedure ReadWriteSector(Disk:Byte;

Sec:LongInt;Nsec:Word;var Buf;Op:Byte);forward;

procedure GetDiskInfo(Disk:Byte;var DiskInfo:TDisk);

{Возвращает информацию о диске DISK}

var

Boot:TBoot;

IO:IOCTL_Type;

p:PListDisk;

label

Get;

begin

Disk_Error:=False;

if (Disk<2) or (Disks=NIL) then

goto Get; {Не находить в перечне, если дискета либо нет перечня}

{Отыскиваем в перечне описателей}

p:=Disks;

while (p^.DiskInfo.Number<>Disk) and (p^.NextDisk<>NIL) do

p:=p^.NextDisk; {Если не тот номер диска}

if p^.DiskInfo.Number=Disk then

begin {Найден подходящий элемент — выход}

DiskInfo:=p^.DiskInfo;

exit

end;

{Формируем описатель диска с птмощью вызова IOCTL}

Get:

IO.BuildBPB:=True; {Требуем выстроить ВРВ}

GetIOCTLInfo(Disk,IO); {Получаем информацию}

if Disk_Error then

exit;

with DiskInfo, IO do {Формируемописатель}

begin

Number:=Disk;

TypeD:=TypeDrv;

AttrD:=Attrib;

Cyls:=Cylindrs;

Media:=BPB.Media;

SectSize:=BPB.SectSiz;

TrackSiz:=Add.TrkSecs;

TotSecs:=BPB.TotSecs;

if TotSecs=0 then

begin

ReadWriteSector(Number,0,1,Boot,2); {Дискбольшойемкости}

TotSecs:=Boot.Add.LargSectors; {Читаемзагрузочныйсектор}

end;

Heads:=Add.HeadCnt;

Tracks:=(TotSecs+pred(TrackSiz)) div (TrackSiz*Heads);

ClusSize:=BPB.ClustSiz;

FATLock:=BPB.ResSecs;

FATCnt:=BPB.FatCnt;

FATSize:=BPB.FatSize;

RootLock:=FATLock+FATCnt*FATSize;

RootSize:=BPB.RootSiz;

DataLock:=RootLock+(RootSize*SizeOf(Dir_Type)) div SectSize;

MaxClus:=(TotSecs-DataLock) div ClusSize+2;

FAT16:=(MaxClus>4086) and (TotSecs>20790)

end

end; {GetDiskinfo}

{—————-}

function GetDiskNumber(c:Char):Byte;

{Конвертирует имя диска A…Z в номер 0…26.

Если обозначено недействительное имя, возвращает 255}

var

DrvNumber:Byte;

begin

if UpCase(c) in [‘A’..’Z’] then

DrvNumber:=ord(UpCase(c))-ord(‘A’)

else

DrvNumber:=255;

if DrvNumber>GetMaxDrv then

DrvNumber:=255;

GetDiskNumber:=DrvNumber;

end; {GetDiskNumber}

{———————}

function GetFATItem(Disk:Byte;Item:Word):Word;

{Возвращает содержимое обозначенного элемента FAT}

var

DI:TDisk;

k,j,n:Integer;

Fat:record

case Byte of

0: (w:array[0..255] of Word);

1: (b:array[0..512*3-1] of Byte);

end;

begin

GetDiskInfo(Disk,DI);

if not Disk_Error then with DI do

begin

if (Item>MaxClus) or (Item<2) then

Item:=$FFFF {Задан неверный номер кластера}

else

begin

if FAT16 then

begin

k:=Item div 256; {Подходящий сектор FAT}

j:=Item mod 256; {Смещение в секторе}

n:=1 {Количество читаемых секторов}

end

else

begin

k:=Item div 1024; {Подходящая тройка секторов FAT}

j:=(3*Item) shr 1-k*1536; {Смещение в секторе}

n:=3 {Количество читаемых секторов}

end;

{Читаем 1 либо 3 сектора FAT}

ReadSector(Disk,FATLock+k*n,n,Fat);

if not Disk_Error then

begin

if FAT16 then

Item:=Fat.w[j]

else

begin

n:=Item; {Старенькое

Item:=Fat.b[j]+Fat.b[j+1] shl 8;

if odd(n) then

Item:=Item shr 4

else

Item:=Item and $FFF;

if Item>$FF6 then

Item:=$F000+Item

end;

GetFatItem:=Item

end

end

end

end; {GetFATItem}

{——————}

procedure GetIOCTLInfo(Disk:Byte;var IO:IOCTL_Type);

{Получаем информацию о устройстве согласно общему вызову IOCTL}

begin

with Reg do

begin

ah:=$44; {Функция 44}

al:=$0D; {Общийвызов IOCTL}

cl:=$60; {Отдать характеристики устройства}

ch:=$8; {Устройство — диск}

bl:=Disk+1; {Диск 1=А,…}

bh:=0;

ds:=seg(IO);

dx:=ofs(IO);

Intr($21,Reg);

Output

end

end; {GetIOCTLInfo}

{——————-}

procedure GetListDisk(var List:PListDisk);

{Сформировывает перечень дисковых описателей}

var

Disk:Byte;

DI:TDisk;

P,PP:PListDisk;

begin

Disk:=2; {Начать с диска С:}

List:=NIL;

repeat

GetDiskInfo(Disk,DI);

if not Disk_Error then

begin

New(P);

if List=NIL then

List:=P

else

PP^.NextDisk:=P;

with P^ do

begin

DiskInfo:=DI;

NextDisk:=NIL;

inc(Disk);

PP:=P

end

end

until Disk_Error;

Disk_Error:=False

end; {GetListDisk}

{———————}

procedure GetMasterBoot(var Buf);

{Возвращаетвпеременной Buf главныйзагрузочныйсектор}

begin

GetAbsSector($80,0,1,Buf)

end; {GetMasterBoot}

{———————}

function GetMaxDrv:Byte;

{Возвращает количество логических дисков}

const

Max:Byte=0;

begin

if Max=0 then with Reg do

begin

ah:=$19;

MSDOS(Reg);

ah:=$0E;

dl:=al;

MSDOS(Reg);

Max:=al

end;

GetMaxDrv:=Max

end; {GetMaxDrv}

{——————-}

function GetSector(Disk:Byte;Cluster:Word):Word;

{Преобразуем номер кластера в номер сектора}

var

DI:TDisk;

begin

GetDiskInfo(Disk,DI);

if not Disk_Error then with DI do

begin

Disk_Error:=(Cluster>MaxClus) or (Cluster<2);

if not Disk_Error then

GetSector:=(Cluster-2)*ClusSize+DataLock

end;

if Disk_Error then

GetSector:=$FFFF

end; {GetSector}

{———————-}

function PackCylSec(Cyl,Sec:Word):Word;

{Упаковывает цилиндр и сектор в одно слово для прерывания $13}

begin

PackCylSec:=Sec+(Cyl and $300) shr 2+(Cyl shl 8)

end; {PackCylSec}

procedure ReadWriteSector(Disk:Byte;

Sec:LongInt;NSec:Word; var Buf; Op:Byte);

{Читает либо записывает сектор (секторы):

Ор = 0 — читать; 1 — записать (малый диск)

= 2 — читать; 3 — записать (большенный диск)}

type

TBuf0=record

StartSec:LongInt;

Secs:Word;

AdrBuf:Pointer

end;

var

Buf0:TBuf0;

S:Word;

O:Word;

begin

if Op>1 then with Buf0 do

begin

{Готовим ссылочную структуру для огромного диска}

AdrBuf:=Ptr(Seg(Buf),Ofs(Buf));

StartSec:=Sec;

Secs:=NSec;

S:=Seg(Buf0);

O:=Ofs(Buf0);

asm

mov CX,$FFFF

mov AL,Op

shr AX,1

mov AL,Disk

push DS

push BP

mov BX,O

mov DS,S

jc @1

int 25H

jmp @2

@1: int 26H

@2: pop DX

pop BP

pop DS

mov BX,1

jc @3

mov Bx,0

xor AX,AX

@3: mov Disk_Error,BL

mov Disk_Status,AX

end

end

else {Воззвание к диску малой емкости}

asm

mov DX,Word Ptr Sec {DX:=Sec}

mov CX,NSec {CX:=NSec}

push DS {Сохраняем DS — он будет испорчен}

push BP {Сохраняем BP}

lds BX,Buf {DS:BX — адресок буфера}

mov AL,Op {AL:=Op}

shr AX,1 {Переносим младший бит Oр в CF}

mov AL,Disk {AL:=Disk}

jc @Write {Перейти, если младший бит Ор<>0}

int 25H {Читаем данные}

jmp @Go {Обойти запись}

@WRITE:

int 26H {Записываем данные}

@GO:

pop DX {Извлекаем флаги из стека}

pop BP {Восстанавливаем BP}

pop DS {Восстанавливаем DS}

mov BX,1 {BX:=True}

jc @Exit {Перейти, если была ошибка}

mov BX,0 {BX:=False}

xor AX,AX {Обнуляем код ошибки}

@EXIT:

mov Disk_Error,BL {Флаг ошибки взять из BX}

mov Disk_Status,AX {Код ошибки взять из AX}

end

end; {ReadWriteSector}

{————————}

procedure ReadSector(Disk:Byte;Sec:LongInt;NSec:Word;var Buf);

{Читает сектор(секторы) на обозначенном диске}

var

DI:TDisk;

begin

GetDiskInfo(Disk,DI);

if DI.TotSecs>$FFFF then {Дискбольшойемкости?}

ReadWriteSector(Disk,Sec,Nsec,Buf,2) {-Да: операция 2}

else

ReadWriteSector(Disk,Sec,Nsec,Buf,0) {-Нет: операция 0}

end; {ReadSector}

{————————}

procedure SetAbsSector(Disk,Head:Byte;CSec:Word;var Buf);

{Записывает абсолютный дисковый сектор при помощи прерывания $13}

begin

with Reg do

begin

ah:=3; {Операциязаписи}

dl:=Disk; {Номер привода}

dh:=Head; {Номер головки}

cx:=CSec; {Цилиндр/сектор}

al:=1; {Читаем один сектор}

es:=seg(Buf);

bx:=ofs(Buf);

Intr($13,Reg);

Output

end

end; {SetAbsSector}

{——————}

procedure SetDefaultDrv(Disk:Byte);

{Устанавливаетдискпоумолчанию}

begin

if Disk<=GetMaxDrv then with Reg do

begin

AH:=$E;

DL:=Disk;

MSDOS(Reg)

end

end;

{———————}

procedure SetFATItem(Disk:Byte;Cluster,Item:Word);

{Устанавливаем содержимое ITEM в элемент CLUSTER таблицы FAT}

var

DI:TDisk;

k,j,n:Integer;

Fat:record

case Byte of

0:(w: array[0..255] of Word);

1:(b: array[0..512*3-1] of Byte);

end;

begin

GetDiskInfo(Disk,DI);

if not Disk_Error then with DI do

begin

if (Cluster<=MaxClus) and (Cluster>=2) then

begin

if FAT16 then

begin

k:=Cluster div 256; {Нужныйсектор FAT}

j:=Cluster mod 256; {Смещение в секторе}

n:=1

end

else

begin

k:=Cluster div 1024; {Нужнаятройкасекторов FAT}

j:=(3*Cluster) shr 1-k*1536;

n:=3

end;

ReadSector(Disk,FatLock+k*n,n,Fat);

if not Disk_Error then

begin

if FAT16 then

Fat.w[j]:=Item

else

begin

if odd(Cluster) then

Item:=Item shl 4+Fat.b[j] and $F

else

Item:=Item+(Fat.b[j+1] and $F0) shl 12;

Fat.b[j]:=Lo(Item);

Fat.b[j+1]:=Hi(Item)

end;

if not FAT16 then

begin {Проверяем «хвост» FAT}

k:=k*n; {к — смещение сектора}

while k+n>FatSize do dec(n)

end;

inc(FATLock,k); {FATLock — номерсекторав FAT}

{Записываем изменение в FatCnt копий FAT}

for k:=0 to pred(FatCnt) do

WriteSector(Disk,FATLock+k*FatSize,n,Fat)

end

end

end

end; {SetFATItem}

{———————-}

procedure SetMasterBoot(var Buf);

{Записываемвглавныйзагрузочныйсекторсодержимое Buf}

begin

with Reg do

begin

ah:=3; {Операциязаписи}

al:=1; {Кол-во секторов}

dl:=$80; {1-й твердый диск}

dh:=0; {Головка 0}

cx:=1; {1-й сектор 0-й дорожки}

es:=seg(Buf);

bx:=ofs(Buf);

Intr($13,Reg);

Disk_Error:=(Flags and FCarry<>0);

if Disk_Error then

Disk_Status:=ah

else

Disk_Status:=0

end

end; {SetMasterBoot}

{———————}

procedure UnpackCylSec(CSec:Word;var Cyl,Sec:Word);

{Декодируем цилиндр и сектор для прерывания $13}

begin

Cyl:=(CSec and 192) shl 2+CSec shr 8;

Sec:=CSec and 63

end; {RecodeCylSec}

{———————-}

procedure WriteSector(Disk:Byte;Sec:LongInt;NSec:Word;var Buf);

{Записывает сектор (секторы) на обозначенный диск}

var

DI:TDisk;

begin

GetDiskInfo(Disk,DI);

if DI.TotSecs>$FFFF then

ReadWriteSector(Disk,Sec,Nsec,Buf,3)

else

ReadWriteSector(Disk,Sec,Nsec,Buf,1);

end; {ReadSector}

{=============} end. {Unit F_Disk} {==============}

2 ТЕКСТМОДУЛЯ F_PROT

{==================} Unit F_Prot; {=======================}

INTERFACE

procedure ProtCheck(var P1,P2; var Res: Integer);

{Инспектирует легальность копии:

Р1 — адресок процедуры NORMA; Р2 — адресок процедуры ALARM;

Res — итог работы:

0: был вызов NORMA;

1: был вызов ALARM;

2: не вставлена дискета.

Хоть какое другое

function SetOnHD: Integer;

{Устанавливает копию на твердый диск. Возвращает:

-1 — не вставлена дискета;

-2 — не мастер-дискета;

-3 — защита от записи либо ошибка записи;

-4 — программка не скопирована на ЖД;

-5 — ошибка доступа к ЖД;

-6 — исчерпан предел установок;

-7 — программка уже установлена;

>=0 — количество оставшихся установок}

function RemoveFromHD: Integer;

{Удаляет копию с твердого диска. Возвращает:

-1 — не вставлена дискета;

-2 — не мастер-дискета;

-3 — защита от записи либо ошибка записи ГД;

-4 — программка не скопирована на ЖД;

-5 — ошибка доступа к ЖД;

>=0 — количество оставшихся установок}

IMPLEMENTATION

Uses DOS, F_Disk;

type

TDate=array[1..4] of Word;

TKey=record case Byte of

0:(

Hard: Word; {Ключ для шифровки данных}

Dat: TDate); {Дата сотворения ПЗУ}

1:(KeyW: array[1..5] of Word);

end;

const

TRK=80; {Номердорожки}

HED=0; {Номерголовки}

SEC=1; {Номер сектора}

SIZ=1; {Код размера секторов}

ETracks=80; {Эталонное количество дорожек на дискете}

ETrackSiz=18; {Эталонное количество секторов на дорожке}

Key:TKey=(KeyW:(0,0,0,0,0)); {Ключ стационарной программки}

{—————-}

type

TBuf=array[1..256] of Byte;

var

P:Pointer; {Ссылка на прежнюю ТПД}

Bif:TBuf; {Буфер чтения/записи сектора}

R:registers; {Регистры}

{—————-}

function DiskettPrepare(var DSK: Byte):Boolean;

type

DBT_Type=record {Структура таблицы характеристик дискеты}

Reserv1:array[0..2] of Byte;

SizeCode:Byte; {Код размера сектора}

LastSect:Byte; {Количество секторов на дорожке}

Reserv2:array[5..10] of Byte

end;

var

Info: TDisk;

DBT,OldDBT:^DBT_Type;

begin

{проверяем наличие дискеты}

DSK:=0; {начинаем с диска А:}

repeat

GetDiskInfo(DSK,Info);

if Disk_Error then

if DSK=0 then

DSK:=1 {Повторяем для диска В:}

else

DSK:=2 {Окончить с ошибкой}

until not Disk_Error or (DSK=2);

if Disk_Error then

begin {Нет доступа ни к А:, ни к В:}

DiskettPrepare:=False;

Exit

end;

{проверяемтипдискеты}

with Info do

begin

if(Tracks<>ETracks) or

(TrackSiz<>ETrackSiz) then

begin {Неэталонныйтип}

DiskettPrepare:=False;

DSK:=3;

Exit

end;

{ПереустанавливаемТПД}

GetIntVec($1E,P);

OldDBT:=P;

New(DBT);

DBT^:=OldDBT^;

with DBT^ do

begin

SizeCode:=SIZ;

LastSect:=ETrackSiz

end;

SetIntVec($1E,DBT)

end;

DiskettPrepare:=True

end; {DiskettPrepare}

{—————-}

function LegalDiskett(var DSK:Byte):Boolean;

{Инспектирует легальность мобильной копии}

var

k,n:Word;

begin

{Подготавливаемдискету}

if DiskettPrepare(DSK) then

begin

{читаемключевойсектор}

for k:=1 to 256 do

bif[k]:=0;

With R do

begin

ah:=0;

dl:=DSK;

Intr($13,R);

ah:=2;

al:=1;

ch:=TRK;

cl:=SEC;

dh:=HED;

dl:=DSK;

es:=seg(Bif);

bx:=ofs(Bif);

Intr($13,R);

ah:=0;

dl:=DSK;

Intr($13,R);

SetIntVec($1E,P);

if (Flags and FCarry)<>0 then

begin

LegalDiskett:=False;

DSK:=4;

Exit

end

else

begin {проверяем содержимое сектора}

for k:=2 to 256 do

Bif[k]:=Bif[k] xor Bif[1];

N:=0;

{$R-}

for k:=2 to 255 do

N:=N+Bif[k];

if (N mod 256=Bif[256]) then

begin

if N=0 then

begin

DSK:=4;

LegalDiskett:=False;

Exit

end;

DSK:=0;

LegalDiskett:=True

end

else

begin

DSK:=4;

LegalDiskett:=False

end

end

end

end

else

LegalDiskett:=False

end; {LegalDiskett}

function LegalHD(var DSK: Byte): Boolean;

{инспектирует легальность стационарной копии}

var

k:Word;

Date:^TDate;

Legal:Boolean;

label

ExitL;

begin

{Расшифровываемключ}

with Key do for k:=2 to 5 do

KeyW[k]:=KeyW[k] xor KeyW[1];

{Проверяем дату производства ПЗУ}

k:=1;

Date:=ptr($F000,$FFF5);

repeat

Legal:=Date^[k]=Key.Dat[k];

inc(k)

until not Legal or (k=5);

LegalHD:=Legal;

{проверяемдискету}

if Legal then

DSK:=0

else

Legal:=LegalDiskett(DSK);

LegalHD:=Legal

end;

{—————-}

procedure ProtCheck(var P1,P2;var Res:Integer);

{Инспектирует легальность копии:

Р1 — адресок процедуры NORMA; Р2 — адресок процедуры ALARM;

Res — итог работы:

0: был вызов NORMA;

1: был вызов ALARM;

2: не вставлена дискета.

Хоть какое другое

type

PType = Procedure;

var

Norma: PType absolute P1;

Alarm: PType absolute P2;

DSK: Byte;

label

L1,L2;

begin

Res:=-1;

if Key.Hard=0 then

if LegalDiskett(DSK) then

begin

L1:

Norma;

Res:=0

end

else

begin

L2:

if DSK=2 then

Res:=2

else

begin

Alarm;

Res:=1

end

end

else

if LegalHD(DSK) then

goto L1

else

goto L2

end; {ProtCheck}

{—————}

Procedure HidnSec(var Buf:TBuf;Inst,Limit:Byte);

{Шифрует буфер главного сектора}

var

k,n:Word;

begin

Randomize;

for k:=2 to 254 do

Buf[k]:=Random(256);

Buf[1]:=Random(255)+1; {Ключдляшифровки}

{$R-}

Buf[17]:=Inst; {Счетчикустановок}

Buf[200]:=Limit; {Лимитустановок}

n:=0; {ПодсчетКС}

for k:=2 to 255 do

n:=n+Buf[k];

Buf[256]:=n mod 256; {Контрольная сумма}

{Шифруемвседанные}

for k:=2 to 256 do

Buf[k]:=Buf[k] xor Buf[1];

{$R+}

end; {HidnSec}

{——————}

Function SetOnHD: Integer;

{Устанавливает стационарную копию на твердый диск. Возвращает:

-1 — не вставлена дискета;

-2 — не мастер-дискета;

-3 — защита от записи либо ошибка записи ГД;

-4 — программка не скопирована на ЖД;

-5 — ошибка доступа к ЖД;

-6 — исчерпан предел установок;

-7 — программка уже установлена.

>=0 — количество оставшихся установок}

var

DSK:Byte; {Диск}

F:file; {Файл с программкой}

Date:^TDate; {Дата ПЗУ}

NameF:String; {Название файла с программкой}

W:array[1..5] of Word; {Заголовокфайла}

n:Word; {Счетчик}

L:LongInt; {Файловоесмещение}

Inst:Byte; {Количествоустановок}

label

ErrWrt;

begin

if Key.Hard<>0 then

begin

SetOnHD:=-7;

Exit

end;

{проверяем резидентность программки}

NameF:=FExpand(ParamStr(0));

if NameF[1] in [‘A’,’B’] then

begin

SetOnHD:=-4;

Exit

end;

{проверяемдискету}

if not LegalDiskett(DSK) then

begin

case DSK of

2: SetOnHD:=-1;

else

SetOnHD:=-2;

end;

Exit

end;

if (Bif[200]<>255) and (Bif[17]>=Bif[200]) then

begin {Исчерпанлимитустановок}

SetOnHD:=-6;

Exit

end;

{Запоминаем дату производства ПЗУ}

Date:=ptr($F000,$FFF5);

Key.Dat:=Date^;

{Шифруемпараметры}

Randomize;

with Key do

while Hard=0 do Hard:=Random($FFFF);

for n:=2 to 5 do with Key do

KeyW[n]:=KeyW[n] xor Hard;

{Открываем файл с программкой}

Assign(F,NameF);

Reset(F,1);

{Читаем заголовок файла}

BlockRead(F,W,SizeOf(W),n);

if n<>SizeOf(W) then

begin

SetOnHD:=-5;

Exit

end;

{Ищемвфайлеположение Hard}

R.ah:=$62;

MSDOS(R);

P:=@Key;

L:=round((DSeg-R.bx-16+W[5])*16.0)+ofs(P^);

Seek(F,L);

{Записываем в файл}

BlockWrite(F,Key,SizeOf(Key),n);

if n<>SizeOf(Key) then

begin

SetOnHD:=-5;

Close(F);

Exit

end;

{Шифруемключевойсектор}

Inst:=Bif[200]-Bif[17]-1;

HidnSec(Bif,Bif[17]+1,Bif[200]);

{записываем на дискету новейший ключ}

ifnotDiskettPrepare(DSK) then

begin {Ошибка доступа к дискете: удаляем установку}

ErrWrt:

FillChar(Key,SizeOf(Key),0);

Seek(F,L);

BlockWrite(F,Key,SizeOf(Key),n);

SetOnHD:=-3;

Close(F);

Exit

end;

with R do

begin

ah:=0;

dl:=DSK;

Intr($13,R);

ah:=3;

al:=1;

ch:=TRK;

cl:=SEC;

dh:=HED;

dl:=DSK;

es:=seg(Bif);

bx:=ofs(Bif);

Intr($13,R);

if(Flags and FCarry)<>0 then

goto ErrWrt

end;

{Обычное окончание}

SetOnHD:=Inst;

SetIntVec($1E,P);

Close(F)

end; {SetOnHD}

{—————-}

function RemoveFromHD: Integer;

{Удаляет стационарную копию. Возвращает:

-1 — не вставлена дискета;

-2 — не мастер-дискета;

-3 — защита от записи либо ошибка записи ГД;

-4 — программка не скопирована на ЖД;

-5 — ошибка доступа к ЖД;

>=0 — количество оставшихся установок}

var

k,n:Integer;

NameF:String;

B:array[1..512] of Byte;

F:file;

DSK,Inst:Byte;

begin

if Key.Hard=0 then

begin

RemoveFromHD:=-4;

Exit

end;

if not LegalDiskett(DSK) then

begin

if DSK=2 then

RemoveFromHD:=-1

else

RemoveFromHD:=-2;

Exit

end;

{СтираемфайлспрограммойнаЖД}

NameF:=FExpand(ParamStr(0));

if NameF[1] in [‘A’..’B’] then

begin

RemoveFromHD:=-4;

Exit

end;

Assign(F,NameF);

{$I-}

Reset(F,1);

{$I+}

if IOResult<>0 then

begin

RemoveFromHD:=-5;

Exit

end;

{Уничтожаемзаголовокфайла}

FillChar(B,512,0);

BlockWrite(F,B,512,n);

if n<>512 then

begin

RemoveFromHD:=-5;

Exit

end;

Close(F);

Erase(F); {Стеретьфайл}

{Шифруемключевойсектор}

Inst:=Bif[200]-Bif[17]+1;

HidnSec(Bif,Bif[17]-1,Bif[200]);

{Записываем на дискету новейший ключ}

if not DiskettPrepare(DSK) then

begin

RemoveFromHD:=-1;

Exit

end;

with R do

begin

ah:=0;

dl:=DSK;

Intr($13,R);

ah:=3;

al:=1;

ch:=TRK;

cl:=SEC;

dh:=HED;

dl:=DSK;

es:=seg(Bif);

bx:=ofs(Bif);

Intr($13,R);

if (Flags and FCarry)<>0 then

RemoveFromHD:=-3

else

RemoveFromHD:=Inst

end;

end; {RemoveFormHD}

{==================} end. {F_Prot} {=======================}

3 ТЕКСТПРОГРАММЫ DISKETT

+———————————————————+

Program Diskett;

Uses DOS, F_disk;

const

TRK=80; {Номер необычной дорожки}

DSK=0; {Номер диска}

SIZ=1; {Код размера сектора}

type

PDBT_Type=^DBT_Type; {Указатель на ТПД}

{Таблица характеристик дискеты}

DBT_Type=record

Reserv1 : array [0..2] of Byte;

SizeCode: Byte; {Кодразмерасектора}

LastSect: Byte; {Количество секторов на дорожке}

Reserv2 : array [5..7] of Byte;

FillChar: Char; {Знак-заполнитель форматирования}

Reserv3 : Word

end;

{Элемент буфера форматирования}

F_Buf=record

Track:Byte; {Номердорожки}

Head:Byte; {Номерголовки}

Sect:Byte; {Номерсектора}

Size:Byte {Кодразмера}

end;

var

Old: PDBT_Type; {Указатель на начальную ТПД}

{——————-}

Procedure Intr13(var R: registers; S: String);

{Обращается к прерыванию 13 и анализирует ошибку (CF=1 — признак ошибки).

Если ошибка найдена, печатает строчку S и завершает работу программки}

begin

Intr($13, R);

if R.Flags and FCarry<>0 then

if R.ah<>6 then {Игнорируем ошибку от смены типа дискеты}

begin

WriteLn(S);

SetIntVec($1E, Old); {ВосстанавливаемстаруюТПД}

Halt

end

end; {Intr13}

Function AccessTime(DSK,TRK: Byte):Real;

{Определяет время доступа к дорожке и возвращает его своим результатом (в секундах)}

var

E: array [1..18*512] of Byte;

t,k: LongInt;

R: registers;

begin

t:=MemL[0:$046C];

while t=MemL[0:$046C] do;

for k:=1 to 10 do with R do

begin

ah:=2;

al:=9;

ch:=TRK;

cl:=1;

dh:=0;

dl:=DSK;

es:=seg(E);

bx:=ofs(E);

Intr13(R, ‘Error’)

end;

AccessTime:=(MemL[0:$046C]-t-1)*0.055

end;

{—————}

var

B: array [1..18] of F_Buf; {Буфердляформатирования}

k,N:Integer; {Счетчикцикла}

R:registers; {Регистры}

DBT:PDBT_Type; {УказательнановуюТПД}

C, D: array[1..1024] of Byte; {Буферчтения/записи}

Size: Word; {Длинасектора}

Info: TDisk;

begin {Основная программка}

{Проверяем доступ к диску и настраиваем драйвер}

GetDiskInfo(DSK, Info);

if Disk_Error then

begin

WriteLn(‘Ошибка доступа к диску’);

Halt

end;

{Получаем длину сектора в б}

case SIZ of

0: Size:=128;

1: Size:=256;

2: Size:=512;

3: Size:=1024

else

WriteLn(‘Недопустимыйкоддлинысектора’)

end;

{Корректируем таблицу характеристик дискеты. Так как начальная ТПД быть может

в ПЗУ, делаем ее копию в ОЗУ и изменяем нужные элементы}

Old:=ptr(MemW[0:$1E*4+2],MemW[0:$1E*4]);

New(DBT);

DBT^:=Old^; {ПолучаемкопиюТПДвОЗУ}

SetIntVec($1E,DBT); {ИзменяемссылкунаТПД}

with DBT^ do

begin

SizeCode:=SIZ;

LastSect:=18;

FillChar:=’+’

end;

with R do

begin

{Сбрасываемдисковод}

ax:=0;

dl:=DSK;

Intr13(R,’Ошибка доступа к диску’);

{Готовим буфер форматирования с оборотным фактором чередования секторов}

for k:=1 to 18 do {Для всякого из 18 секторов:}

with B[k] do

begin

Track:=TRK; {указываемномердорожки}

Head:=0; {номер головки}

Sect:=19-k; {номер сектора в оборотной последовательности}

Size:=SIZ {и код размера}

end;

{Форматируем дорожку}

ah:=$05; {Код операции форматирования}

al:=18; {Создаем 18 секторов}

ch:=TRK; {на дорожке TRK}

cl:=1; {начиная с сектора 1}

dh:=0; {на поверхности 0}

dl:=DSK; {диска DSK}

es:=seg(B); {ES:BX — адресок буфера}

bx:=ofs(B);

Intr13(R,’Ошибка форматирования’);

{Заполняем сектор случайными числами}

Randomize;

for k:=2 to 255 do

C[k]:=Random(256);

{Запрашиваем количество инсталяций на ЖД}

Write(‘Кол-во установок на ЖД: ‘);

ReadLn(C[200]);

C[17]:=0;

{Cчитываем контрольную сумму}

N:=0;

for k:=2 to 255 do

N:=N+C[k];

C[256]:=N mod 256;

{Шифруем сектор}

C[1]:=Random(255)+1;

for k:=2 to 256 do

C[k]:=C[k] xor C[1];

{Записываем сектор}

ah:=$03; {Код операции записи}

al:=1; {Записать 1 сектор}

ch:=TRK; {На дорожке TRK}

cl:=1; {Начиная с сектора 1}

dh:=0; {На поверхности 0}

dl:=DSK; {Диск DSK}

es:=seg(C);{Адресок буфера С для записи}

bx:=ofs(C);

Intr13(R,’Ошибка записи’);

{Читаем сектор}

ah:=$02;

al:=1;

ch:=TRK;

cl:=1;

dh:=0;

dl:=DSK;

es:=seg(D); {Адресок буфера D для чтения}

bx:=ofs(D);

Intr13(R,’Ошибка чтения’)

end;

{Проверяемсовпадение}

for k:=1 to Size do

if c[k]<>d[k] then

begin

WriteLn(‘Несовпадениеданных’);

SetIntVec($1E,Old);

Halt

end;

WriteLn(‘Сотворена и испытана ‘,TRK+1,

‘-я дорожка с секторами по ‘,Size,’ б‘);

{измеряем время доступа к новейшей дорожке}

Write(‘время доступа к сокрытой дорожке: ‘);

WriteLn(AccessTime(DSK,TRK):6:2,’ c’);

{измеряем время доступа к обычной дорожке}

DBT^.SizeCode:=2; {Указываем обычную длину сектора в ТПД}

Write(‘Доступ к обыкновенной дорожке: ‘);

WriteLn(AccessTime(DSK,20):6:2,’ c’);

{Восстанавливаем старенькую ТПД}

SetIntVec($1E,Old)

end.

2 текст ПРОГРАММЫ TEXT.EXE

uses F_Prot,F_Disk;

procedure Alarm;Far;

begin

writeln(‘Нелегальнаякопия’)

end;

procedure Norma;Far;

begin

writeln(‘Легальнаякопия’)

end;

function ParStr:String;

var

S:string;

k:Byte;

begin

S:=ParamStr(1);

for k:=1 to Length(S) do S[k]:=UpCase(S[k]);

ParStr:=S

end;

var

p1,p2:Pointer;

d:Integer;

dsk:Byte;

begin

p1:=@Norma;

p2:=@Alarm;

if ParStr=’/SET’ then

Writeln(‘установка на ЖД: ‘,SetOnHD)

else

if ParStr=’/REMOVE’ then

writeln(‘Удаление с ЖД: ‘,RemoveFromHD)

else

begin

ProtCheck(p1,p2,d);

Writeln(‘Итог проверки ‘,d);

readln

end

end.

]]>