Учебная работа. Курсовая работа: Защита программы от нелегального копирования
ПОЯСНИТЕЛЬНАЯ ЗАПИСКА
к курсовому проекту
на тему «защита программки от незаконного копирования»
по курсу «Кодирование и защита инфы»
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.
]]>