Защита программ от взлома
Эту статью я нашел на бескрайних
просторах Интернета.
Введение
Эта статья
посвящена достаточно актуальной в настоящее
время тематике - защите программ от взлома и
нелегального копирования. Этой теме
посвящено много статей, одна из наиболее
интересных (из тех, которые попались мне) -
статья "Защита shareware-программ" Владимира
Каталова в Компьютерре Online#240. Он привел
ряд советов по написанию shareware программ
и я не хочу повторяться - сходите,
почитайте.
Рассмотрим
некоторые тонкости организации защиты на
достаточно популярном примере -
предполагаем, что программа защищена
некоторым кодом (серийным номером, паролем),
который сообщается пользователю после
соблюдения им определенных условий. До
регистрации в этой программе заблокирован
ряд каких либо полезных функций,
используется надоедливая реклама или
ограничен строк работы. После ввода этого
кода производится его проверка и при
положительном исходе проверки программа
начинает нормально работать.
По неизвестной
мне причине в большинстве современных
программ данная защита сделана однообразно и
для ее снятия необходимо 10-15 минут. В этой
статье я постараюсь поделиться опытом в
построении систем защиты. Могу сразу
предупредить - хорошему хакеру противостоять
практически бесполезно, да и не нужно - при
желании любая защита может быть взломана,
это вопрос времени.
Инструментарий хакера
Современный
хакер имеет в своем арсенале набор
разнообразных утилит для взлома.
Их можно
подразделить на несколько категорий
-
Отладчики. Позволяют прерывать
выполнение программы при достижении
заранее заданных условий, производить
пошаговое выполнение программы, изменять
содержимое памяти и регистров и т.п.
Наиболее популярным, удобным и мощным
является отладчик SoftICE, который при
достаточно примитивном интерфейсе
обладает приличными возможностями и
весьма стабильно работает.
-
Дизассемблеры. Производят
дизассемблирование программы для
дальнейшего изучения полученного кода.
Один из наиболее мощных и популярных -
IDA. От дизассемблера достаточно легко
защититься - зашифровать или
заархивировать программу. Тогда
дизассемблируется только архиватор или
кодировщик. Однако тот-же IDA имеет
мощный встроенный скриптовой язык,
позволяющий производить расшифровку
программы
-
Средства мониторинга. Это набор утилит,
отслеживающих операции с файлами,
реестром, портами и сетью.
-
Средства пассивного анализа программы.
Показывают разную информацию о программе
- извлекают ресурсы, показывают связи,
используемые библиотеки. Классический
пример - утилита DEPENDS.EXE из
комплекта Visual Studio. Она показывает,
какие библиотеки используются программой
и какие функции импортируются.
-
Прочие утилиты. Их великое множество
(можно найти на диске типа "Все для
хакера", причем в изобилии). Это
разнообразные редакторы, анализаторы ...
Наиболее
популярны следующие программы мониторинга :
-
FileMon - утилита, позволяющая вести
мониторинг всех операций с файлами.
Имеет удобный фильтр, может сохранять
отчет в файле. Поэтому нет смысла делать
"секретные" файлы где-нибудь в Windows/System
- их элементарно найти.
-
RegMon - аналог FileMon, только ведется
мониторинг всех операций с реестром.
Аналогично файлам, бессмысленно
создавать в реестре "секретные" ключи -
они сразу бросаются в глаза.
-
PortMon - мониторинг работы с портами
ввода/вывода
-
TCP_VIEW - монитор соединений по TCP-IP
-
RegUtils - набор утилит для контроля за
реестром - делает копии реестра,
позволяет сравнивать копии и
просматривать изменения.
Утилиты типа
FileMon могут резко упростить взлом
программы - легко определить место, в
котором программа обращается к указанному
файлу или ключу реестра.
Основы построения защиты - шаг за шагом
Как ввести
регистрационный код.
Ввод пароля или регистрационного номера
является ответственным делом - хакер
постарается отловить адрес памяти, в который
будет записан пароль. Затем на обращение по
этому адресу ставится точка останова
(команда BPM в SoftICE), что позволяет
поймать начало процедуры проверки
регистрационного кода. Если для ввода
используются стандартные элементы ввода
Windows, то алгоритм действий хакера можно
формализовать и выглядит он примерно так:
-
Устанавливает точку останова на
считывание текста из стандартного
элемента ввода (функции GetWindowText,
GetGlgItemText модуля KERNEL32)
-
При вызове этой функции анализируем ее
параметры и таким образом определяем, по
какому адресу будет размещено
считываемое значение и ставим обращение
к этой области памяти точку останова. А
достоверности определенного адреса легко
убедиться - после выполнения функции там
появится введенная строка
-
При срабатывании этой точки останова мы
попадаем в анализатор введенного
значения и либо делаем генератор
регистрационных ключей, либо ломаем
процедуру проверки. И то, и другое
весьма просто сделать - достаточно
только изучить ассемблер и API
Набор этих
действий стандартен и мне не раз попадались
подробные руководства типа "Взлом Windows
программ - шаг за шагом", ориентированные на
продвинутого пользователя.
Рассмотри
несколько решений, которые могут затруднить
взлом на этом этапе.
Совет _0.
Старайтесь как можно меньше применять
стандартные функции (особенно API-шные) и
компоненты VCL. Так что Assembler, Assembler
и еще раз Assembler ...
Сущность этого
совета надеюсь очевидна - современные
дизассемблеры умеют распознавать стандартные
процедуры высокоуровневых языков, а API -
вообще отдельный разговор - SoftICE обладает
изумительной возможностью - загружать
символьные имена для любых указанных
библиотек (особенно для KERNEL32.DLL) -
отладка резко упрощается, т.к. мы видим
имена вызываемых функций и можем ставить
точки останова на вызов функций по их имени.
Совет 1.
Применяйте нестандартный способ ввода
пароля.
Наипростейший
путь - написать свой визуальный компонент
для ввода регистрационного кода. Он конечно
должен будет обрабатывать события от
клавиатуры, но момент считывания кода нельзя
поймать избитыми методами. Это уже что-то,
но есть второй способ взлома, основанный на
поиске введенного кода в памяти. Для этого в
SoftICE есть удобная команда "S стартовый
адрес L длина 'образец'" , которая позволяет
найти введенное значение в памяти.
Совет 2. Не
храните введенный код в одном месте !
Если введенный
код или регистрационный номер хранить в
одном месте, то достаточно легко установить
точку останова на эону памяти, в которой
зазмещен введенный код.
Совет 3. Не
храните введенный код открытым текстом !
Итак, что же
следует сделать. Для начала необходимо
завести в программе 5-10 переменных типа
STRING и после ввода кода переписать
введенное значение в них. Делать это лучше
всего не в одном месте, а распределить по
программе. Таким образом поиск даст кучу
адресов, по которым будет находиться
введенный код. Я в таком случае поступаю так
- по таймеру создаю в динамической памяти
новую строковую переменную, пишу в нее код.
Затем на следующем срабатывании таймера
создаю новую переменную, переписываю в нее
код, а старую уничтожаю. При определенном
навыке можно заполонить память значениями
введенного кода и сделать поиск почти
бесполезным. Причем такое копирование можно
совместить с проверкой кода или эмуляцией
этой проверки. Затем с эти строками неплохо
поделать какие-либо операции - сравнить с
чем-нибудь ...
Советы 3 и 1
можно объединить - создать свой компонент,
который позволит вводить код нестандартным
способом с его одновременной шифровкой.
Анализ
регистрационного кода.
Итак, код введен и приняты меры для того,
чтобы его было непросто найти (хотя найти то
его можно, но это время, навык ...). Теперь
следующий шаг - анализ. Поэтому сразу совет:
Совет 4. Ни в
коем случае не анализируйте код сразу после
его ввода.
Чем дальше ввод
кода от его анализа, тем лучше. Самое
разумное - после ввода кода поблагодарить
пользователя за сотрудничество и сообщить,
что со временем будет выполнена регистрация
программы. А анализ кода произвести,
например, через 1-2 минуты в совершенно
другом месте программы.
Совет 5. Не
проверяйте код только в одном месте и не
пишите для проверки функцию.
Достаточно найти
и отключить эту проверку, и защита взломана.
Если проверок несколько, они разные и
распределены по программе, то взлом
затрудняется.
Совет 6. Не
проверяйте пароль одним алгоритмом.
Рекомендуется
разработать 2-3 алгоритма проверки, например
1-2 цифры должны делиться на 3, а 3-7
наложенные по какому-либо алгоритму на имя
пользователя должны дать в сумме 4. Эти две
проверки осуществляем в различных местах с
достаточно большим временным разносом -
взломав первый метод хакер не будет
догадываться о существовании еще нескольких,
которые проявятся со временем.
Совет 7. Ни в
коем случае не предпринимайте никаких
действий после проверки.
По неизвестной причине большинство программ
выглядят примерно так
IF NOT(SuperRegCodeCheck) then
Begin
ShowMessage('Неверный код, дальнейшая
работа невозможна');
halt;
end;
В примере некая
процедура проверяет код и при несовпадении
предпринимает активные действия, которые
буквально кричат "вот она где защита !!".
Наилучший шаг - выждать день-два (или хотя
бы минут 15). Причем все действия по
проверке следует как можно дальше отнести от
выдачи сообщений и прочих действий,
предпринимаемых при обнаружении
неправильного кода.
Совет 8.
Отвлекающие маневры.
Кроме реальных
функций проверки кода очень неплохо сделать
пару бутафорских - они будут вызываться
после ввода кода, проводить активные
манипуляции с введенным значением, выдавать
сообщения о некорректности введенного кода
... - т.е. отвлекать внимание от реальной
проверки.
Совет 9. Не
храните результатов проверки в переменной и
не используйте ее для явного ограничения
функций незарегистрированной программы.
Классический
пример нарушения этого правила
IF NOT(LegalCopy) then
ShowMessage('Сохранение работает только в
зарегистрированной версии')
else
SaveFile;
Таким образом
элементарный анализ показывает, что
переменная LegalCopy хранит результат
проверки и поставив на нее точку останова
можно выловить саму проверку. Отредактировав
это значения в памяти можно временно сделать
копию "зарегистрированной",а установка точки
останова на изменение этой переменной
выведет на место ее проверки. Да и взлом
сводится к тому, что функция проверки кода
урезается до двух команд ассемблера:
MOV [адрес LegalCopy], 1
RET
Совет 10.
(вытекает из 9) Не храните результатов
проверки на диске или в реестре.
Типичная ошибка -
выяснили, что копия зарегистрирована и
сделали где-нибудь метку. Отловить это
достаточно просто (см. описание REGMON и
FILEMON). Наилучший способ - сохранить
пароль и имя пользователя в том виде, в
котором он их ввел. Затем при каждом запуске
программы проверять корректность этого кода,
но не забывая Совет _11. Ничего не
проверяйте сразу при запуске приложения или
сразу после считывания сохраненного имени
или кода. Помните, что считывание кода и
его ввод в окне регистрации идентичны по
мерам защиты - дублирование в разных
областях памяти, шифрование ...
Выводы: мы
устроим проверку кода в нескольких местах
программы, при этом применим несколько
алгоритмов проверки, не будем использовать
API.Кроме того, стоит проделать несколько
отвлекающих маневров.
Общие советы по
защите программ
-
CRC - контрольные суммы. Любой файл,
строку или блок данных можно защитить
контрольной суммой, которую затем можно
рассчитать и сравнить с эталоном. При
сравнении с эталоном конечно следует
весть осторожно - см. первые 11 советов.
Итак, совет 12. Защищайте программы и
данные контрольными суммами. Это
поможет не только от взлома, но и
защитит программы от вируса или
внедрения троянца.
-
Применяйте шифровку программ и данных.
Очень неплохо сжать программу и данные.
Я, например, разработал свой собственный
архиватор - RAR-у и ZIP-у он конкуренции
не составит, но сжатые им данные разжать
очень непросто, придется изрядно
повозиться. Да и изменить их
проблематично - придется разжать,
изменить и сжать.
-
Отлов пошаговой отладки программы.
Существует много способов, я в свое
время провел целое исследование этого
вопроса под DOS, насобирал и придумал не
менее 20 методов, но они мало приемлемы
под Windows. Самый простой и надежный
способ - таймер. При работе программы
периодически фиксируем системное время и
рассчитываем время работы фрагментов
кода между ними. И если 200-400 команд
процессора работают 2-3 минуты, то тут
есть над чем задуматься.
Совет 13. Не
определяйте дату и время стандартными
способом !! Придумайте
что-нибудь оригинальное.
Совет 14.Не стоит
хранить что-либо секретное в файлах или
реестре.
Работа с файлами или реестром может быть
детально запротоколирована и
проанализирована, и все тайное станет явным.
Совет 15.Не
храните ничего важного открытым текстом,
особенно сообщения типа "Это
незарегистрированная версия ...", "Введенный
пароль не верен ...".
Они для хакера - как для быка красная
тряпка, и действительно - находим такое
сообщение, ставим точку останова на
обращение к участку памяти с этим сообщением
и получаем возможность поймать момент выдачи
этого сообщения.
Советы по
созданию меток для организации ограничения
по времени
Защита
"ограничение времени работы" состоит в том,
что программа каким образом фиксирует момент
своего первого запуска и работает
установленное время (обычно 20-30 дней).
После истечения этого срока программа
отказывается запускаться. Как проверить
текущую дату я уже где-то тут писал -
нестандартным способом, например по дате на
файлах реестра или свежесозданном своем
файле. Весь фокус в другом - как
зафиксировать на компьютере дату первого
запуска (естественно так, чтобы изничтожение
программы и ее повторная установка не давали
эффекта). Использование "секретных" файлов в
системных папках или изменения в
существующих файлах легко отловить при
помощи FILEMON. Реестр то же отпадает из-за
REGMON. Прочие методы (типа записи в ВООТ
сектор ...) тоже неприемлемы - не те
времена, по Windows все это не пройдет.
Наиболее оригинально (на мой взгляд) прошить
дату в саму программу и постоянно обновлять
ее на своем сайте (естественно,
автоматически). Таким образом отсчет неявно
идет от момента скачивания программы с
сайта. Есть тут правда и минус - после
завершения срока можно повторно скачать эту
программу и получить еще 15-20 дней ... . С
другой стороны это оригинально -
пользователю рано или поздно надоест
скачивать эту программу и он или откажется
от нее, или купит. Но при этом стоит
помнить, что программу можно скачать
несколько раз и сравнить варианты, выявив,
где лежит дата. Поэтому стоит позаботиться о
том, чтобы изменился почти весь файл
(например, изменить пару опций компилятора)
Советы по
формированию регистрационных кодов
Формирование
кодов может вестись по следующим основным
направлениям:
-
Жестко фиксированные коды, прошитые в
программу. Их обычно немного и их
огласка сводит защиту к нулю.
-
Некий алгоритм проверки кода. Немного
лучше первого, но лишь немного. Возьмите
за пример код Windows - его знает любой
пользователь
-
Алгоритм проверки кода, использующий имя
пользователя. Очевидно, что для каждого
имени будет уникальный номер (или номера
- их может быть несколько, в зависимости
от алгоритма). Это уже лучше, но
нелегальное распространение держится на
эгоизме зарегистрированных пользователей
- ничто не мешает им предать имя/пароль
огласке, но тогда хотя бы можно
вычислить виновника и заблокировать его
код
-
Алгоритм проверки кода, использующий имя
пользователя и некоторые уникальные или
динамически изменяющиеся параметры,
например информацию о компьютере. Это
надежно, дает привязку к компьютеру, но
в наш век постоянных апгрейдов очень
неудобен.
-
On-Line регистрация. Состоит в том, что
программа в On-Line связывается с сайтом
разработчиков (или компании,
осуществляющей продужу софта) и передает
туда ревизиты пользователя. В ответ
программе передается регистрационная
информация. Этот метод может и хорош для
ряда программ, но на мой взгляд не
выдерживает никакой критики по двум
соображениям:
1. Никто не может гарантировать, что
конкретно передаст программа в Инет. А
передать она может все, что угодно -
параметры компьютера, пароли, любые
данные и т.п.
2. Конкретный пользователь может не
иметь доступа к Инет. Это особенно важно
для программ, работа которых не связана
напрямую с Сетью. И зарегистрировать
такую программу его практически никто к
себе на компьютер не пустит (из
соображений п.п. 1)
Рекомендовать тут
что-либо бесполезно, но я например использую
разновидности метода 3.