У наш вік інформації та комп’ютерів стало одним з найбільш ходових товарів. Однак, на відміну від звичайних, госязаемыхе продуктів, як відомо, досить просто вкрасти. Ця проблема вже давно змушує програмістів піклуватися про захист своїх розробок від нелегального копіювання.

Можна сказати, що злом засобів захисту став свого роду професією або навіть більше + покликанням. Багато молоді люди вважають його найкращим засобом самоствердження, не кажучи вже про те, що це дуже захоплююче заняття. Вони називають себе хакерами (hackers, від дієслова hack + грубитье, гкромсатье) або кракерами (crackers, від дієслова crack + гломатье)1. Судячи з усього, протистояння розробників ПЗ і зломщиків збережеться і в осяжному майбутньому, а тому проблема гвандалоустойчивойе захисту не втратить актуальності. У даній статті ми розглянемо деякі технічні та психологічні аспекти її створення на платформі Windows-Intel.

Філософія захисту
Насамперед потрібно сказати, що хакер + не надприродна істота, а самий звичайний чоловік, і якщо розробник спроможеться вивчити методику його роботи, написання ефективного захисту перестане бути нерозв’язною завданням. Як правило, хакер добре знає зворотне проектування (reverse engineering), але відносно погано (порівняно з розробником) + системне програмування і математику. Крім того, він використовує деякі специфічні инструменты2, з якими розробник зазвичай не знайомий, і працює не з оригіналу, а з двійковим кодом.

Але не варто применшувати здібності хакера. Це гідний супротивник, не прощає ні наївних схем захисту, ні прорахунків у її реалізації. Зазвичай чомусь забувають, що загальна стійкість захисту визначається найслабшою її ланкою, так що будь-які хитрощі начебто апаратних ключів або прив’язки до комп’ютера будуть марними, якщо деяка частина алгоритму виконує тривіальну перевірку прапора гсвой/чужойе на відповідність бажаного результату.

Фактично процес протистояння розробника і хакера носить динамічний характер, і завдання розробника + завжди бути на крок попереду. Кожна нова версія програми повинна мати нову схему захисту або, принаймні, настільки сильно модифіковану стару, щоб наявні у хакера напрацювання давали осічку. Звідси, до речі, випливає ще один важливий принцип: будь-яка схема захисту повинна існувати в єдиному екземплярі. Ні в якому разі не можна застосовувати один і той же алгоритм в різних програмах. Роблячи так, ви реально захищаєте тільки одну програму, а решта гсдаете без бояе.

Слід пам’ятати, що абсолютно надійного захисту не існує. Будь-який захист можна зламати, тому необхідно шукати оптимальне співвідношення витрат на створення захисту і прогнозованих витрат на її злом, враховуючи також цінове співвідношення з самим захищеним.

Мабуть, єдиний спосіб надійно захистити свою програму на рівні ідеології + це перевести її з розряду в розряд платформ. Під платформою тут розуміється досить складний програмний комплекс, експлуатація якого вимагає тісної співпраці з виробником. Зрозуміло, таке можливо тільки для високотехнологічних програмних систем корпоративного призначення. Але якщо дозволяє змістити акценти в цю сторону (що, наприклад, відбувається сьогодні з мультимедійними програмами, які плавно перетікають з домашніх комп’ютерів офіси), то виробник отримує додатковий важіль для поліпшення захисту.

Метафізика захисту
Очевидно, що схема захисту повинна бути розрахована не стільки на виконання фіскальних функцій по відношенню до рядовому користувачеві, скільки на протидію спробам хакера вивести з ладу даний фіскальний функціонал. Тому найпершим етапом в алгоритмі захисту, мабуть, варто назвати, образно висловлюючись, виявлення хакера. Звичайно, можна виявити не самого хакера, а інструменти, якими він користується. Звідси випливає висновок: перед тим, як писати якусь захист, розробник повинен з’ясувати, чим в даний момент користуються хакери (ясно, що через півроку ситуація кардинально зміниться, точно так само, як і на всьому ринку інформаційних технологій).

Як правило, в арсеналі хакера присутні кілька отладчиков, дизассемблеров і програм моніторингу системи (таких, як FileMon і RegMon). Всі вони, за винятком дизассемблеров, виконуються паралельно з програмою, а значить, можуть бути виявлені нею.

Найпростіший спосіб + виклик функції EnumWindows, яка перераховує створені в системі вікна верхнього рівня. Отримавши їх дескриптори, програміст може ідентифікувати запущені процеси (по заголовках вікон, назвам виконуваних модулів, інформації про версії файлів). Можна також скористатися готладочнойе за своїм призначенням бібліотекою ToolHelp.

У Windows NT засобів для контролю за станом системи більше, ніж в Windows 95/98 (наприклад, там є спеціальна функція IsDebuggerPresent для виявлення відладчика). Оскільки приблизно з 2000 р. прогнозується масовий перехід на нову версію Windows NT, можна припускати, що розробники скоро отримають додаткові кошти для боротьби з хакером. Ще один популярний спосіб виявлення відладчика + це вимір часу виконання критичних ділянок коду, який дозволяє виявити покроковий режим роботи. Дуже корисно також перевірити, не поставлені точки зупину (байт 0xCC) на деякі функції API або самої програми: таким шляхом хакери часто намагаються визначити, в який момент створюється вікно реєстрації або перевіряються дані захисту. У лістингу показана перевірка на наявність точки зупину функції CreateWindowExA.

Перевірка наявності точки — зупинки у функції CreateWindowExA
extrn @CheckCRC$qv: FAR
extrn CreateWindowExA: FAR
public checkdeb; прототип — extern LCL int WINAPI checkdeb(void);
checkdeb proc near
push esi
push ds
push cs
pop ds
; lea esi, @CheckCRC$qv; у разі прикладних
; функцій Сі++ необхідно враховувати перекручування імен
; (name mangling)
lea esi, CreateWindowExA; функція, імпортована з DLL
mov esi,[esi+2]; для функції з DLL спершу
; знаходимо її адресу в таблиці перенаправлення,
; цей крок слід опустити у випадку контролю
; внутримодульной функції
mov eax,[esi]; беремо перші байти функції
and eax,0FFh; конкретно самий перший
cmp eax,0CCh; є точка зупину?
mov eax,100h
je Debug_here
xor eax,eax
Debug_here:
pop ds
pop esi
ret; якщо всі LчистоL, повертаємо 0
checkdeb endp
Іноді цікаву інформацію можна отримати, вивчивши системне оточення за допомогою функції GetEnvironmentStrings.

Якщо виявлений відладчик або програма моніторингу, то постає питання про вибір схеми протидії. Активна схема припускає, що необхідно спробувати гзавеситье ворожу програму або всю систему цілком, благо таких способів багато. Можна, наприклад, злегка зіпсувати стек або просто викликати функцію DestroyWindow для вікні налагоджувача. Більш екзотичні способи порушення штатної роботи небажаної програми, що залежать від особливостей її реалізації, можуть включати некоректне взаємодію з нею за протоколом DDE або COM: у першому випадку іноді достатньо передати сміття замість дескриптора блоку даних, у другому + зайвий раз (передчасно) викликати метод Release для використовуваного об’єкта.

Але активна схема протидії не позбавлена недоліків. По-перше, отладчики і монітори працюють не тільки в злочинних цілях, так що в результаті контратаки може безневинно постраждати законослухняний користувач. По-друге, виконуючи активні дії, захист виявляє себе і дає можливість локалізувати гочаг сопротивленияе.

Хитрішою і, мабуть, дієвою є пасивна схема, коли захист при хакерській атаці просто-напросто вимикається (переводиться в гспящийе режим). В результаті у хакера складається враження, що програма вже зламана або взагалі не містить захисту, але варто її запустити на гчистоме комп’ютері, як захист гпросыпаетсяе.

Математика захисту
Як правило, захист оперує не менш ніж двома наборами даних, отриманих з різних джерел (наприклад, введені користувачем ім’я та прізвище і отриманий від розробника реєстраційний ключ). Щоб визначити, чи легально використовується програма (або в якому режимі вона використовується + демонстраційному або повнофункціональному), над цими наборами виконуються певні перетворення, і результати потім порівнюються. Зрозуміло, що відповідні фрагменти коду + головний об’єкт уваги хакера.

За великим рахунком хакер може застосувати при зломі два різних підходи: або, дослідивши алгоритми перетворення даних, навчитися самому генерувати реєстраційні ключі, які програма буде сприймати як правильні (це більш гпрестижныйе метод), або просто зіпсувати захист + злегка гподправитье програму, так щоб захист перестала працювати або вважала б будь-якого користувача легальним.

Один з можливих способів боротьби зі спробами відтворити алгоритм генерації ключів + створення самомодифицирующегося коду. У цьому випадку статичний код, аналізований дизассемблером, не дає необхідних відомостей для відновлення алгоритму генерації реєстраційних ключів, і виникає необхідність аналізу програми в режимі налагодження. Якщо припустити, що ми успішно засікаємо отладчики і не активізуємо захист в їх присутності, то локалізувати потрібний фрагмент коду буде складно. А оскільки не можна перманентно модифікувати код, який динамічно формується тільки на стадії виконання програми, відключити захист теж стає неможливо або, принаймні, дуже важко (адже це потрібно робити для більшого обсягу коду в різних частинах програми).

Реалізувати самомодифицирующийся код на мові високого рівня досить складно + краще використовувати асемблер. Можна, правда, обійтися і без модифицирующегося коду, але тоді бажано, щоб перетворення даних при перевірці прав користувача не зводилися до елементарних операцій.

Наприклад, розробники умовно-безкоштовних програм досить часто застосовують для отримання реєстраційного ключа з імені користувача (або навпаки) складні комбінації двійковими і цілочисельних арифметичних операцій. Однак настирного хакера такі вправи здебільшого тільки радують: утиліту реєстрації нелегальних користувачів він будує за кілька годин. Єдиний вихід + використовувати що-небудь більш гзубодробительноее, скажімо шифрування з відкритим ключем або нетривіальні операції з плаваючою комою.

Схема шифрування може бути наступною. Розробник завчасно генерує пару ключів + відкритий і закритий. Користувач, що купив програму, посилає розробнику відомості про себе, а той, у свою чергу, шифрує їх з допомогою закритого ключа і відсилає зашифровані дані разом з відкритим ключем назад користувачеві. Вбудований у програму механізм захисту розшифровує за допомогою відкритого ключа інформацію, отриману від розробника, і порівнює її з наявними даними про користувача. Очевидно, що знання алгоритму дешифрування не дасть хакеру можливості створити реєстраційну утиліту: адже для неї потрібно не тільки відтворити алгоритм шифрування, але і знати закритий ключ розробника. Зрозуміло, надійність такого захисту залежить від криптостійкості алгоритму шифрування і довжини ключа. Windows NT, а також 98 і 95 починаючи з версії OSR2 містять програмний інтерфейс CryptoAPI, що дозволяє використовувати захисту ПО комерційні системи шифрування, в тому числі із застосуванням пластикових карток та інших апаратних засобів.

Суть підходу, заснованого на нетривіальних математичних операціях, така. Нехай для перетворення інформації про користувача X в реєстраційний ключ Y служить певна функція Y=F(X), визначена на просторі дійсних чисел; при цьому зворотна функція X=F-1(Y), перетворююча реєстраційний ключ в інформацію про користувача, така, що її значення неможливо розрахувати виходячи з F (за досить довгий період часу). Зрозуміло, що така ситуація приблизно аналогічна виникає при шифруванні з відкритим ключем.

Існує й інший варіант + вибрати функцію F складної форми і розраховувати її в алгоритмі захисту без використання записи в аналітичному вигляді. Як це здійснити? Наприклад, з допомогою нейронної мережі зворотного поширення. Розробник створює і навчає нейронну мережу на апроксимацію F, після чого навчена мережа впроваджується по фрагмент захисту. Ще більше ускладнити життя хакеру можна, розбивши F на два щаблі. У цьому випадку функція F запишеться у вигляді суперпозиції функцій H1 і H2. У схемі захисту необхідно апроксимувати функції H1 і H2-1, які будуть застосовуватися наступним чином:
Z1=H1(X)
Z2=H2-1(Y)

Числа Z1 і Z2 отримуються відповідно з інформацією про користувача і перевіряється реєстраційному ключа. Їх збіг означає законне використання програми. Даний підхід ускладнює експлуатацію ще одного улюбленого методу роботи хакера, який укладається в гтупоме перебір значень реєстраційного ключа.

Зрозуміло, якщо код програми не самомодифицирующийся, хакеру нескладно буде гвыломатье з неї всю цю математику. Тут найбільш простим і в той же час ефективним способом боротьби є підрахунок контрольних сум для критично важливих ділянок коду.

Хронологія захисту
Досить часто захист містить фрагмент, відповідальний за відстеження терміну пробної експлуатації програми. Ділянка коду, в якому час, що минув з моменту установки програми, порівнюється з дозволеним періодом безкоштовного використання, повинен захищатися з допомогою методів, аналогічних розглянутим раніше. Однак гхронология защитые має одне додаткове вразливе місце: хакер може модифікувати дату установки програми (або дату її останнього запуску + це іноді практикується для того, щоб запобігти, принаймні теоретично, дієвість трюків з перекладом системних годин). Ні реєстр, ні файлова система не гарантують гтайну вкладове. Можна порекомендувати зберігати дату у відкритому або закодованому вигляді, наприклад, у реєстрі і додавати до неї контрольну суму. В Windows NT є корисна функція RegQueryInfoKey, яка повідомляє дату створення ключа реєстру.

Хімія захисту
Наостанок наведу кілька нескладних правил, які дозволять розробнику гсхимичитье при створенні захисту.

Застосовуйте нелінійні (у тому числі подієво-керовані) алгоритми захисту. Наприклад, можна розбити перевірку прав користувачів на кілька частин і виконувати їх в довільному порядку в різні моменти часу.
Використовуйте для зберігання даних захисту системні ресурси Windows: додаткову пам’ять, виділену для параметрів вікон та доступну за допомогою функцій SetWindowLong/GetWindowLong або SetProp/GetProp; атоми; локальні сховища потоків (TlsAlloc) і т. п.
У рамках алгоритму захисту порівнюйте два числа яким-небудь нестандартним способом: наприклад, розраховується величина, яка в разі правильної реєстрації повинна бути негативною, і з неї вилучається квадратний корінь, а установка прапора гсвой-чужойе виноситься в обробку помилки області визначення.
Напишіть кілька хитромудрих функцій, що імітують дії з нібито реєстраційною інформацією, + це відверне хакера від справжньої захисту.
Додайте в різних частинах програми кілька різних за змістом функцій з однаковим ефектом.
Створіть кілька різних змінних з різними значеннями для позначення одного і того ж події (наприклад, спроби порушення захисту).
Не звертайтесь до змінних, що містить інформацію про захисті, напряму; використовувати непряму адресацію, можливо, із застосуванням стандартних бібліотечних функцій роботи з пам’яттю.
Виконуйте стандартні операції у відповідь на нестандартні повідомлення Windows, а захисні дії, навпаки, у відповідь на стандартні повідомлення, такі як WM_PAINT; підміну можна робити прямо в циклі обробки повідомлень.
Якщо програма розрахована на роботу з Internet, не пропустіть можливість вмонтувати в неї перевірку бази користувачів через мережу.

Цей список кожен розробник легко продовжить сам. Багато свіжих ідей можна почерпнути з Internet та телеконференцій. Все це потрібно творчо переробити (щоб алгоритм захисту був унікальним, хоча і ґрунтується на відомих методах) і об’єднати + надмірність у захисті ніколи не заважає. Досить часто здаються прямо-таки ідеальними схеми захисту насправді легко зламуються за допомогою простого, але не врахованого програмістом прийому. Краще всього, звичайно, мати знайомого гчестногое хакера, який візьметься гполоматье вашу програму, щоб виявити слабкі місця захисту, але така можливість є не у всіх. Іншим залишається лише на свій страх і ризик прийняти умови гри і вступити в інтелектуальне протиборство з крадіями.

——————————————————————————–

1. Словом гхакере спочатку позначали програмістів зі специфічним гмашинно-ориентированныме стилем роботи, не обов’язково навіть займаються чимось протизаконним. Зараз так найчастіше називають тих, хто зламує сервери віддалених інформаційних систем (у той час як л. кракерами + це саме фахівець із злому програм), але термін цілком застосовний і до комп’ютерних зломщикам взагалі.

2. У більшості випадків розробник все-таки знайомий з інструментами хакера, але тільки не підозрює, що це вони і є, оскільки використовує їх не для злому, а для відлагодження програми.