Эта статья посвящена достаточно актуальной в настоящее время тематике -
защите программ от взлома и нелегального копирования. Этой теме посвящено много
статей, одна из наиболее интересных (из тех, которые попались мне) - статья
"Защита shareware-программ" Владимира Каталова в Компьютерре Online#240. Он
привел ряд советов по написанию shareware программ и я не хочу повторяться -
сходите, почитайте.
Рассмотрим некоторые тонкости организации защиты на достаточно популярном
примере - предполагаем, что программа защищена некоторым кодом (серийным
номером, паролем), который сообщается пользователю после соблюдения им
определенных условий. До регистрации в этой программе заблокирован ряд каких
либо полезных функций, используется надоедливая реклама или ограничен строк
работы. После ввода этого кода производится его проверка и при положительном
исходе проверки программа начинает нормально работать. По неизвестной мне
причине в большинстве современных программ данная защита сделана однообразно и
для ее снятия необходимо 10-15 минут. В этой статье я постараюсь поделиться
опытом в построении систем защиты. Могу сразу предупредить - хорошему хакеру
противостоять практически бесполезно, да и не нужно - при желании любая защита
может быть взломана, это вопрос времени.
Инструментарий хакера.
Современный хакер имеет в своем арсенале набор разнообразных утилит для
взлома. Их можно подразделить на несколько категорий
- Отладчики. Позволяют прерывать выполнение программы
при достижении заранее заданных условий, производить пошаговое
выполнение программы, изменять содержимое памяти и регистров и
т.п. . Наиболее популярным, удобным и мощным является отладчик
SoftICE, который при достаточно примитивном интерфейсе обладает
приличными возможностями и весьма стабильно работает.
- Дизассемблеры. Производят дизассемблирование
программы для дальнейшего изучения полученного кода. Один из
наиболее популярных - 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.
Автор: Зайцев Олег
|