Захист програми від нелегального копіювання

[ виправити ] текст може містити помилки, будь ласка перевіряйте перш ніж використовувати.

скачати

Міністерство освіти і науки України
ПОЯСНЮВАЛЬНА ЗАПИСКА
до курсового проекту
на тему "Захист програми від нелегального копіювання"
за курсом "Кодування та захист інформації"
2004

Зміст
Введення
1 Опис існуючих методів
Перевірка типу ПК і версії ДОС
Перевірка дати виготовлення та контрольної суми ПЗУ
Перевірка місця розташування файлу на диску
Перевірка складу апаратних засобів
Використання прихованих частин програми і особливостей фізичних носіїв інформації
2 Обгрунтування вибору використовуваного методу
3 Опис програми
Висновок
Список посилань
Додаток

Введення
У даному курсовому проекті розглядається проблема створення програми, нелегальне (без відома офіційного розповсюджувача) копіювання якої призводить до втрати програми здатності нормально працювати.
Сьогодні в нашій країні склалася парадоксальна ситуація, коли в більшості вітчизняних ПК в основному використовується «крадене» програмне забезпечення. Рідкісний виняток становлять ті професіонали, які працюють у приватних фірмах або на спільних підприємствах.
У відповідності з міжнародним правом працю автора програми прирівняний до праці інших творців інтелектуальної власності, таких як письменники, музиканти, художники, і підлягає захисту. Однак на відміну від книги або картини масове копіювання програми не викликає будь-яких технічних труднощів і не вимагає спеціального обладнання (якщо, зрозуміло, не вважати ПК). У відношенні програма набагато більш беззахисною, ніж, скульптура чи книжка. Загальний збиток від нелегального копіювання програм за оцінками західних експертів складає від 2 до 10 млрд. доларів на рік [1].
Цілком зрозумілим тому здається прагнення авторів додати своїм програмам такі властивості, які б унеможливлювали їх нелегальне (крім волі авторів) поширення. Тут буде розглянута техніка розробки програм, в тій чи іншій мірі володіють цими властивостями. Однак досвідчений програміст, що володіє відповідними навичками, завжди зможе знайти те місце (ті місця) в програмі, де приймається рішення про легальність копії, і, змінивши кілька байт в коді програми позбавити її можливості самозахисту. Саме з цієї причини провідні фірми-виробники масового програмного забезпечення практично ніколи не ставлять захист на свої програми, справедливо вважаючи, що не варто витрачати сили і кошти на створення систем захисту, якщо все одно програма буде «розкрита». Ці фірми отримують свої дивіденди за рахунок масовості обсягу продажів при порівняно низькій ціні за кожну копію.
Трохи інакше йде справа з програмами, які не розраховані на масового користувача. До таких відносяться різного роду системи автоматизованого проектування, предиздательскіе системи, спеціалізовані бази даних, і т. п. Як правило, це досить наукоємні товари, тобто в них особливо великий внесок висококласних фахівців у конкретних прикладних областях. Праця таких фахівців зазвичай високо цінується, тому загальні витрати на створення подібного роду програм можуть бути вельми великі, що в поєднанні з порівняно невеликим колом потенційних користувачів робить ціну кожної копії дуже великий. Такі програми зазвичай забезпечуються захистом, так як обмежений попит на них формує вельми специфічний ринок: покупцями цих програм є фахівці в конкретній прикладної діяльності, які не мають потрібних навичок і, головне, бажання займатися «розкриттям» програм.

1. Опис існуючих методів
Будемо вважати програму захищеною від нелегального копіювання, якщо в ній є вбудовані засоби, що дозволяють перевірити саму програму і / або характерні ознаки ПК, на якому вона виконується, з метою визначити, чи була копія програми зроблена з дотриманням всієї необхідної технології. Якщо технологія створення копії була порушена, програма перестає працювати нормальним чином. Таким чином, легальне копіювання увазі використання деякої особливої ​​технології створення копій, у незвичайності якої і полягає весь фокус. Лише втаємничений у цей секрет людина за допомогою спеціального обладнання або особливої ​​програми може створити легальну копію.
Будь-яка копія захищеної програми повинна містити в собі або в зовнішньому файлі «ключ» - одне або декілька кодових чисел. У момент перевірки програма порівнює деякі специфічні ознаки робочого середовища із заздалегідь закодованими у ключі і за результатами порівняння формує відповідний ознака. Таким чином, мало створити копію програми: для того щоб ця копія стала працездатною, їй необхідно передати ключ, налаштований на роботу з цілком певним комп'ютером [1].
Аналіз індивідуальних ознак середовища має на увазі, якими специфічними ознаками може володіти та програмно-апаратна середовище, в якому працює програма. Для IBM-сумісних ПК цими ознаками можуть бути:
- Тип ПК і тип (версія) операційної системи;
- Дата виготовлення ПЗУ BIOS і / або його контрольна сума;
- Фізичне положення файлів на дисковому носії;
- Склад апаратних засобів;
- Наявність прихованих частин програми;
- Фізичні особливості (в тому числі дефекти) носія.
Деякі з цих ознак дуже індивідуальні (наприклад, фізичні особливості неякісного носія), інші мають меншу індивідуальністю (тип ПК, версія ДОС). Програма може використовувати один або кілька ознак для перевірки легальності копії. При цьому особливого значення набуває спосіб використання програми: якщо програма розрахована на роботу на конкретному ПК, вибираються одні ознаки, якщо вона може вільно переміщатися з одного комп'ютера на інший без втрати працездатності, - інші. Назвемо програми першого типу стаціонарними, а другого - мобільними. Захист тих і інших програм має свої особливості, які ми будемо обговорювати.
У всіх випадках перевірка легальності не повинна істотно позначитися на швидкодії програми або вимагати від користувача якихось додаткових дій. Наприклад, навряд чи можна вважати скільки-небудь ефективної систему, що використовує пароль (кодове слово), який повинен ввести користувач: по-перше, користувач може забути це слово і тим самим позбутися законно придбаної програми, а по-друге, ніщо не заважає йому повідомити це слово будь-якому іншому користувачеві. Система захисту не повинна перевіряти користувача, вона повинна перевірити копію [3].
1.1 Перевірка типу ПК і версії ДОС
Ці перевірки дуже прості, але не мають високий ступінь індивідуальності в тому сенсі, що можуть існувати багато сотень тисяч ПК одного типу, в яких використовується однакова ДОС. Тому зазвичай ці перевірки використовуються в поєднанні з перевірками інших індивідуальних ознак, і призначені для захисту стаціонарних програм.
Тип ПК записаний в ПЗП за адресою $ F000: $ FFFE, тобто в передостанньому байті мегабайтного адресного простору ПК [4].
Версію ДОС можна отримати за допомогою функції ДОС $ 30 [4]. При зверненні до цієї функції в регістрі AL повертається старший, а в AH - молодший номер версії. Регістр AL може містити 0, якщо використовується більш рання ДОС ніж 2.0.
Цінність цих перевірок полягає в їх виключній простоті, однак можуть знайтися тисячі однотипних комп'ютерів, на яких використовується однакова версія ДОС, тому обмежуватися тільки цими перевірками навряд чи має сенс.
1.2 Перевірка дати виготовлення та контрольної суми ПЗУ
Постійний запам'ятовуючий пристрій (ПЗУ) є невід'ємною складовою частиною будь-якого IBM - сумісного ПК. Вміст ПЗУ враховує особливості реалізації конкретного ПК і може відрізнятися в комп'ютерах різного типу. Більш того, в кінці ПЗУ (за адресою $ F000: $ FFF5) зазвичай записується дата його виготовлення, тому навіть для ПК одного типу (і однієї і тієї ж фірми-виробника) контрольна сума ПЗУ відрізняється в різних примірниках ПК. Дата виготовлення ПЗУ займає 8 суміжних байт. Дані зберігаються в символьному вигляді в форматі ММ / ДД / РР [1], [4].
Як показує практика, будь-яка поважаюча себе фірма-виробник ПЗУ для IBM - сумісних ПК ретельно стежить за коректністю цієї дати. Звичайно, кожен день у всьому світі виготовляються тисячі мікросхем ПЗУ з однаковою датою, однак імовірність того, що десь використовується ПК того ж типу і з такою ж датою виготовлення, як і у комп'ютера власника програми вкрай мала. Зрозуміло, при масовій закупівлі ПК, наприклад для оснащення навчального класу, багато хто або навіть всі одночасно придбані комп'ютери можуть мати одну й ту ж дату виготовлення ПЗУ. Однак у таких випадках здатність захищених програм вільно переноситися з одного спорідненого комп'ютера на інший можна розглядати як цілком природну. Ця перевірка використовується для захисту стаціонарних програм.
1.3 Перевірка місця розташування файлу на диску
Доброю індивідуальністю має власний номер для кластера, починаючи з якого на жорсткому диску розташовується файл з захищеною програмою. Дійсно, навряд чи що-небудь інше в апаратно-програмної середовищі ПК (крім, зрозуміло, вмісту оперативної пам'яті) змінюється настільки ж динамічно, як файлова структура жорсткого диска. При створенні легальної копії номер початкового кластера для файлу програми на жорсткому диску в загальному випадку абсолютно випадковий. Якщо в момент запуску програма перевірить цей номер, то в переважній більшості випадків вона легко виявить факт нелегального копіювання. Тим не менш, такий спосіб захисту не можна вважати ідеальним з багатьох причин. Перевірка номера кластера виконується далеко не так просто, як перевірка типу ПК або дати виготовлення ПЗУ, оскільки в стандартному Турбо Паскалі немає засобів для роботи з дисками на фізичному рівні. Однак головний недолік полягає в іншому: будь-яка зміна місця розташування файлу навіть у межах одного каталогу призводить до того, що раніше встановлена ​​копія стає нелегальною. Це вкрай незручно для користувача, особливо, якщо він часто вдається до процедури переупорядоченія файлової структури з допомогою дефрагментації жорсткого диска. Якщо користувач вважає, що номер єдиного кластеру не володіє необхідною ступенем індивідуальності, то можна перевіряти ланцюжок кластерів за таблицею 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].
Далі, доріжки на дискеті розташовуються так, що самої зовнішньої є нульова доріжка, а самої внутрішньої - доріжка з максимальним номером. Зазвичай ДОС використовує однакову кількість секторів на всіх доріжках, тому кутовий розмір кожного сектора постійний, а, отже, щільність запису інформації зростає зі зростанням числа доріжки. Кількість секторів на доріжці і доріжок на дискеті ДОС призначається так, щоб навіть для самої внутрішньої доріжки ця щільність не перевищила деякого значення, ще гарантує впевнену роботу схем контролера ГД. На практиці виявляється, що переважна більшість сучасних контролерів здатне обслуговувати більшу кількість доріжок, ніж прийнято в ДГЗ. З метою захисту від копіювання програма може створити і використовувати одну або декілька додаткових доріжок, але не вносити їх до списку «видимих ​​для ДОС», тобто не змінювати поле в завантажувальному секторі, яке вказує загальну кількість секторів на диску. Зрозуміло, можливий і інший варіант: можна «вкрасти» у ДГЗ кілька доріжок, зменшивши стандартне значення цього поля, проте нестандартна ємність дискети легко виявляється, що знижує ефективність захисту. Додаткові доріжки можуть відокремлюватися від основної робочої зони дискети невідформатований інтервалом, що може ускладнити їх виявлення спеціальними програмами копіювання [1], [4].
Нарешті, програма може використовувати нестандартний фактор чергування секторів. Цей фактор впливає на час читання / запису групи суміжних секторів. Якщо яку-небудь доріжку відформатувати з навмисно неоптимальним фактором чергування, час читання цієї доріжки може виявитися помітно більше, ніж при читанні будь-якої іншої доріжки - це ще один спосіб захисту.

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]. Програма зберігає копію старої таблиці параметрів дискети і після завершення роботи відновлює її. У перший сектор нової доріжки записується довільна інформація і число установок на жорсткий диск захищається програми, (це число установок вводиться користувачем при створенні ключової дискети). Потім сектор читається і перевіряється правильність операції запису-читання. У кінці програми вимірюється час доступу до нової доріжці і стандартною доріжці. Для читання і запису сектора використовується переривання $ 13. У випадку помилки читання або запису сектора, програма виводить повідомлення про помилку і відновлює стару таблицю параметрів дискети.
Жорстка прив'язка програми до дискети створює цілком зрозумілі незручності користувачеві. Ці незручності можна усунути, якщо дозволити користувачеві здійснювати самостійну установку програми на жорсткий диск, тобто створювати легальні стаціонарні варіанти програми. Щоб процес тиражування був контролюємо, кожна установка програми на ЖД здійснюється тільки за наявності ключової дискети, при цьому програма веде підрахунок загальної кількості створених з її допомогою стаціонарних копій.
Процедура ProtCheck викликається з захищається програми кожного разу, коли потрібно встановити легальність копії. Її заголовок має наступний вигляд:
Procedure ProtCheck (var Norma, Alarm; var Res: Integer)
У тілі процедури параметри-змінні Norma і Alarm трактуються як параметри процедурного типу
type
ProcType = Procedure;
тобто є адресами двох процедур без параметрів. Процедура Alarm викликається в разі, якщо програма виявила ознаки нелегального копіювання, а Norma - якщо ці ознаки відсутні. У змінній Res повертається результат роботи ProtCheck: 0 - якщо виконувалася процедура Norma (легальна копія), 1 - виконувалася Alarm (нелегальна копія), 2 - не виконувалася ні та, ні інша процедура, оскільки програма не виявила дискету в приводі ГД і не змогла перевірити легальність копії.
Процедура ProtCheck починає роботу з перевірки поля Hard у глобальній типізованої константі Key. Це поле використовується для аналізу стаціонарності програми: якщо поле має значення 0, реалізується контроль мобільного варіанту, в іншому випадку - стаціонарного варіанту (установка поля Hard і всієї константи Key здійснюється за допомогою функції SetOnHD, див. нижче).
При контролі мобільного варіанта програма намагається прочитати спочатку на диску А, а якщо це не вдається - на диску У ключовий сектор (перший сектор на нульовий поверхні доріжки номер 81) розміром 256 байт. Цей сектор має містити наступну інформацію:
1-й байт - ключ для шифрування вмісту сектора за допомогою операції XOR;
17-й байт - кількість вже створених стаціонарних копій;
200-й байт - максимальна кількість стаціонарних копій (якщо 255 - кількість копій не обмежена);
256-й байт - контрольна сума з 2-го по 255-й байт.
Якщо поле Hard константи Key містить нульове значення, здійснюється контроль стаціонарного варіанту. У цьому випадку поле Dat містить еталон дати створення ПЗУ, а поле Hard використовується як ключ для шифрування цього поля за допомогою операції XOR.
Якщо контроль стаціонарного варіанту дає негативний результат (нелегальна копія), автоматично здійснюється аналіз мобільного варіанту (контроль дискети). Таким чином, будь-яка копія програми гарантовано працює, якщо у розпорядженні користувача є ключова дискета. Однак після правильної установки програми на жорсткий диск за допомогою процедури SetOnHD програма може працювати і без цієї дискети до тих пір, поки вона не буде перенесена на новий комп'ютер, дата створення ПЗУ якого відрізняється від еталонної.
Для правильного створення стаціонарної копії програми використовується функція SetOnHD, що має такий напис:
Function SetOnHD: Integer;
Перед використанням функції SetOnHD необхідно будь-якими стандартними для ДОС засобами скопіювати програму в один з каталогів жорсткого диска. Ця функція викликається в такій «нелегальної» копії, перед цим у будь-який привід ГД необхідно вставити ключову дискету зі знятим захистом від запису. Результат, що повертається функцією, має такий зміст:
1 - у привід ГД не вставлена ​​дискета;
2 - у привід вставлена ​​дискета не еталонного типу (не 1,44 Мб чи ні прихованого сектора);
3 - дискета захищена від запису або під час запису на неї виникла помилка;
4 - даний варіант програми не скопійований попередньо на жорсткий диск;
5 - помилка доступу до жорсткого диска (програма не може прочитати власний файл або не може записати в нього нове значення константи Key);
6 - вичерпаний ліміт стаціонарних копій;
7 - дана програма вже є стаціонарний варіант програми, тобто константа Key в ній вже визначена.
Будь-яке позитивне значення результату свідчить про успішне створення стаціонарної копії і дорівнює кількості ще не використаних установок програми на жорсткий диск.
У ході установки програма контролює ключову дискету, потім визначає характерні ознаки даного ПК і заносить ці ознаки в константу Key. Оскільки захищається тільки той екземпляр програми, в якому викликана функція SetOnHD, потрібно передбачити відповідний варіант запуску програми. Наприклад, можна проаналізувати ключі команди запуску з метою перевірки спеціального ключа створення стаціонарного варіанту або передбачити відповідну опцію у діалоговому меню, створюваному програмою в ході роботи.
Функція RemoveFromHD здійснює зворотні дії: знищує поточну стаціонарну копію і відповідним чином збільшує запас невитрачених установок програми. Вона повертає одне з наступних значень:
1 - у привід ГД не вставлена ​​дискета;
2 - у привід вставлена ​​дискета нееталонного типу (ємністю не 1,44 Мб чи ні прихованого сектора);
3 - дискета захищена від запису або під час запису на неї виникла помилка;
4 - даний варіант програми не скопійований попередньо на жорсткий диск;
5 - помилка доступу до жорсткого диска (програма не може прочитати власний файл або не може записати в нього нове значення константи Key).
Будь-яке позитивне значення свідчить про успішне видаленні стаціонарної копії і дорівнює кількості ще не використаних установок програми на жорсткий диск.
Також додається програма TEST.EXE, яка ілюструє прийоми роботи з модулем F_Prot. Програма аналізує ключі запуску: якщо використовується ключ / SET, здійснюється установка програми на жорсткий диск, якщо ключ / REMOVE, знищується стаціонарна копія програми, якщо цих ключів немає в команді запуску, програма здійснює контроль легальності копії. Ключова дискета має бути попередньо підготовлена ​​за допомогою програми Diskette.
Програма Diskette і модуль F_Prot використовують модуль F_Disk, який призначений для роботи з диском на фізичному рівні.

Висновок
В результаті виконання даного курсового проекту, був написаний модуль для захисту програм від нелегального копіювання. Даний модуль дозволяє захищати як стаціонарні і мобільні варіанти програм. Цей модуль підключається до програми, яку потрібно захистити, і програма використовує його функції. Якщо функція перевірки легальності копії повертає негативний результат, то, на свій розсуд, програміст може зробити так, щоб програма або не запускалася або виводила попередження і реалізовувала не всі свої можливості. Для захисту стаціонарного варіанту програми використовується перевірка дати створення ПЗУ даного ПК, якщо вона не дорівнює еталонної, то копія вважається нелегальною. Для захисту мобільного варіанта програми на ключовий дискеті була створена доріжка з секторами нестандартного розміру. Перший сектор доріжки містить інформацію, про програму, що захищається від нелегального копіювання. Ключова дискета була підготовлена ​​за допомогою програми 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; {=====================}
{
+------------------------------------------------- ------------+
| Модуль містить підпрограми для гнучкої роботи з дисками. |
| У всіх підпрограмах параметр DISK відноситься до логічних |
| Дискам: 0 = А, 1 = В, 2 = С, 3 = D і т. д. Параметр SEC - відноси-|
| Тільних номер сектора; 0 = завантажувальний сектор, далі по |
| Секторам до кінця доріжки, по голівках, по циліндрах. |
+------------------------------------------------- ------------+
}
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 + divd (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 для перевірки парності}
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 divd (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]);
{Записуємо на дискету новий ключ}
if not DiskettPrepare (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
{
+------------------------------------------------- -------+
| Форматування доріжки нестандартними секторами з помо-|
| Могою переривання $ 13. Ця програма готує дискету для |
| Роботи з модулем F_Prot. |
+------------------------------------------------- -------+}
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 1910 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.
Додати в блог або на сайт

Цей текст може містити помилки.

Програмування, комп'ютери, інформатика і кібернетика | Курсова
121.3кб. | скачати


Схожі роботи:
Програми копіювання рядка
Копіювання переміщання та форматування абзаців Опис основних прийомів копіювання переміщання
Захист від несанкціонованої аудіозаписи Захист комп`ютерної інформації Криптографічні
Захист інформації за допомогою антивірусної програми Panda
Захист інформації Комп`ютерні віруси та антивірусні програми
Захист від несанкціонованого доступу
Захист організмів від радіації
Захист від зміни частоти
Соціальний захист від безробіття
© Усі права захищені
написати до нас