Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

articles:protect.overview [2017/09/05 02:55] (текущий)
Строка 1: Строка 1:
 +====== protect.overview ======
 +<​sub>​{{protect.overview.odt|Original file}}</​sub>​
 +
 +====== как защищают программное обеспечение ======
 +
 +крис касперски
 +
 +//​…важно не только научиться. Важно не научиться. Не научиться бывает подчас трудней. Нарушить стандарт,​ отвергнуть шаблон. Не принять общепринятого.//​
 +
 +Л. Озеров. "​Выбор и предпочтение"​
 +
 +**потребность в защитных механизмах растет с каждым днем, но качество их реализации падает вниз стремительным домкратом. давным-давно сломан ****HASP, FLEX LM, ASProtect ****и другие широко разрекламированные решения,​ лишний раз подтверждая,​ что вы не можете доверять никакому коду, кроме своего собственного,​ но что делать,​ если вы и себе не уверены?​ эта статья перечисляет основные ошибки,​ совершаемые разработчиками и дает советы по усилению защиты. предполагается,​ что читатель имеет достаточный опыт программирования,​ поскольку в силу драконических ограничений на объем детали реализации пришлось опустить.**
 +
 +===== введение =====
 +
 +В борьбе за выживание защитные механизмы обречены. Защита лишь продляет мучения программы,​ отодвигая ее взлом не некоторый срок. Теоретически. Практически же ничего не стоит создать защиту,​ позволяющую программе дожить до преклонных лет и "​умереть"​ собственной смертью,​ передавая наследие следующей версии. Главное – правильно забаррикадироваться. Бессмысленно вешать стальную дверь на картонный дом. Линия обороны должна быть однородна во всех направлениях,​ поскольку стойкость защиты определяется наиболее уязвимой ее частью.
 +
 +Мы не будем касаться вопросов целесообразности защиты как таковой (это отдельный больной вопрос),​ а сразу перейдем к рекомендациям,​ следование которым усилит защиту настолько насколько это возможно. Необходимо не только добиться экономической нецелесообразности взлома,​ но и убить моральный стимул к хакерству "на интерес"​ – для этого защита должна быть максимально "​тупой",​ а ее взлом – рутинным.
 +
 +Выбирайте только надежные,​ системно-независимые методики. Хитроумные антиотладочные приемы только разжигают хакерский интерес,​ и что еще хуже – выдают ложные срабатывая у легальных пользователей,​ а этого допускать ни в коем случае нельзя. Делайте ставку на частый выход новых версий и техническую поддержку – это оттолкнет пользователей от "​кракнутых"​ версий.
 +
 +===== джин из бутылки – или недостатки решений из коробки =====
 +
 +Зачем изобретать велосипед,​ когда на рынке представлено множество готовых решений – как программных (ASProtect, FLEX LM, Extreme Protector), так и аппаратных (HASP, Sentinel, Hardlock)?​Ответ – стойкость защиты обратно пропорциональна ее распространенности,​ особенно если она допускает универсальный взлом (а все вышеперечисленные выше решения его допускают). Даже начинающий хакер, скачавший руководство по борьбе с HASP'​ом,​ за короткое время "​отвяжет"​ программу,​ особенно если защита сводится к тривиальному:​ "if (IsHaspPresent() != OK) exit();"​.Защитный механизм должен быть глубоко интегрирован в программу,​ тесно переплетен с ней. Все, что быстро защищается,​ быстро и ломается!
 +
 +Хуже всего, что многие протекторы содержат грубые ошибки реализации,​ нарушающие работоспособность защищаемой программы или отличающиеся вызывающим поведением (например,​ Armadilloзагаживает [замусоривает] реестр,​ замедляя работу системы,​ и это никак не отражено в документации!) Защищая программу самостоятельно,​ вы тратите силы и время, но зато получаете предсказуемый результат. "​Фирменный"​ защитный механизм – кот в мешке и не известно какие проблемы вас ждут.
 +
 +===== от чего защищаться =====
 +
 +Чаще всего программы защищают от несанкционированного копирования,​ ограничения времени триального [испытательного] использования,​ реконструкции оригинального алгоритма и модификации файла на диске и памяти.
 +
 +==== защита от копирования,​ распространения серийного номера ====
 +
 +От** несанкционированного копирования** в принципе защищает привязка к машине (как это сделать с прикладного уровня см. "​пакетные команды интерфейса ATAPI" в N22 "​системного администратора"​). "В принципе"​ потому,​ что пользователям очень не нравится,​ когда ограничивают их свободу,​ поэтому привязывайтесь только к носителю (лазерному диску). Нет никакой необходимости выполнять проверку при каждом запуске программы,​ требуя наличия диска в проводе (а сможет ли ваша программа "​увидеть"​ его по сети?), достаточно проверки на этапе инсталляции. Не надо бояться,​ что это ослабит защиту – в любом случае при наличии ключевого диска, программа отвязываться элементарно. Цель "​привязки"​ противостоять не хакерам,​ а пользователям. О том как создать диск, не копируемый копировщиками защищенных дисков (Alcohol, CloneCD) можно прочитать в "​Технике защиты CD" Криса Касперски или Kris Kaspersky0000-00-00T00:​00:​00n2kвспоминал в каком номере была опубликована статья по защите,​ но так и не вспомнил
 +
 +Системном Администраторе.
 +
 +Пусть защита периодически "​стучится"​ в Интернет,​ передавая вам серийный номер. Если один и тот же серийный номер будет поступать со многих IP адресов,​ можно передать удаленную команду на дезактивацию "​флага"​ регистрации (удалять файл с диска категорически недопустимо!). Только не возлагайте на этот механизм больших надежд – пользователь может поставить брандмауэр (правда,​ локальные брандмауэры легко обойти,​ см. "​побег через брандмауэр"​) или же вовсе работать на машине изолированной от сети.
 +
 +Защита серийным номером (далее по тексту – s/n) не препятствует несанкционированному копированию,​ но если все серийные номера различны можно вычислить пользователя,​ выложившего свой s/n в сеть. Тоже самое относится и к паром имя-пользователя/​код-активации (далее по тексту – u/r). Идея: пусть сервер генерирует дистрибьютивный файл индивидуально на основе регистрационных данных,​ переданных пользователям. Тогда его u/r не подойдет к чужим файлам и весь дистрибьтив придется выкладывать в сеть целиком,​ что намного более проблематично и к тому же далеко не каждый пользователь рискнет скачивать двоичный файл из ненадежных источников.
 +
 +==== защита испытательным сроком ====
 +
 +При защите "​испытательным сроком"​ никогда не полагайтесь на системную дату – ее очень легко перевести "​назад"​. Сравнивайте текущую дату с датой последнего запуска программы,​ сохраненной в надежном месте, помня о том, что не всякое расхождение свидетельствует о попытке взлома – может это пользователь поправляет неверно идущие часы. Используйте несколько независимых источников – отталкивайтесь от даты открываемых файлов (или в общем случае – файлов,​ обнаруженных на компьютере пользователя),​ подключайтесь к службе атомного времени (если доступен Интернет) и т. д.
 +
 +Для предотвращения деинсталляции/​повторной инсталляции никогда не оставляйте никаких "​скрытых"​ пометок в реестре или файловой системе. Во-первых,​ "​уходя,​ заметай следы",​ а, во-вторых,​ пользователь может запускать вашу программу из-под эмулятора ПК (Microsoft Virtual PC, VM Ware),​ –современные аппаратные мощности это уже позволяют. Он просто переформатирует виртуальный диск, уничтожая //​**все**//​ следы пребывая вашей программы. Сохраняйте количество запусков в обрабатываемых программой документов (естественно,​ в нетривиальном формате,​ который непросто подправить вручную). Противостоять этому очень трудно!
 +
 +Вообще же, лучшая защита демонстрационных версий – физическое усечение функциональности с удалением программного кода, отвечающего,​ например,​ за печать или сохранение файла.
 +
 +==== защита от реконструкции алгоритма ====
 +
 +Чтобы хакер не смог реконструировать алгоритм защитного механизма и слегка "​доработать"​ его напильником (или же попросту "​подсмотреть"​ правильный пароль) обязательно используйте //​**многослойную динамическую шифровку**//​ по s/n или u/r. Тогда без s/n,r/u взлом программы станет невозможным. Не проверяйте CRC s/n! Чтобы убедиться в правильности ввода s/n проверяйте CRC расшифрованного кода (восстановить исходный s/n по его CRC на достаточных аппаратных мощностях вполне возможно,​ но CRC расшифрованного кода криптоаналитику вообще ни о чем не говорит!). Игнорируйте первые четыре символа s/n, посадив на них подложный расшифровщик – обычно хакеры ставят точку останова на начало s/n, но не на его середину. Еще лучше, если каждый из нескольких расшифровщиков будет использовать "​свою"​ часть s/n. Спрашивайте имя, копанию,​ прочие регистрационные данные,​ делайте с ними сложные манипуляции,​ но никак их не используйте – пусть хакер сам разбирается какие из них актуальные,​ а какие нет.
 +
 +Шифровка называется динамической,​ если ни в какой момент весь код программы не расшифровывается целиком,​ в противном случае хакер может снять с него дамп и привет! Расшифровывайте функцию перед вызовом,​ а по выходу из нее – зашифровывайте вновь. Используйте несколько независимых шифровщиков и перекрытия шифроблков,​ иначе хакер расшифрует программу "​руками"​ самого расшифровщика,​ просто передавая ему номера/​указатели зашифрованных блоков и сбросит дамп. Многословная шифровка:​ делаем на каждом "​слое"​ что-то полезное,​ затем расшифровываем следующий слой и т. д. Программный код как бы размазывается между слоями,​ вынуждая хакера анализировать каждый из них. Если же весь программный код сосредоточен в одном-единственном слое, количество слоев шифровки,​ "​оборачивающих"​ его, не имеет никакого значения и ничуть не усложняет взлом.
 +
 +Разбавляйте защитный код (и в особенности код расшифровщика) большим количеством мусорных инструкций – процедуру размером в 1 Мбайт за разумное время дизассемблировать практически нереально. Мусор легко генерировать и автоматически (ищите в вирусных журналах полиморфные движки) – следите только за тем, чтобы визуально он был неотличим от полезного кода. Еще лучший результат дают виртуальные машины,​ выполняющие элементарные логические операции (Сети Петри, Стрелка Пирса, Машина Тьюринга). Даже если хакер напишет декомпилятор,​ на реконструкцию алгоритма уйдет вся оставшаяся жизнь (подробнее см. "​Техника и философия хакерских атак"​ Криса Касперски).
 +
 +При использовании однослойной шифровки,​ размазывайте расшифровщик по телу программы,​ никогда не располагайте весь код расшифровщика в конце модуля – тогда переход на оригинальную точку входа может быть распознан по скачкообразному изменению регистра EIP. Кстати,​ стартовый код, внедряемый компиляторами в программу,​ в большинстве случаев начинается с обращения к FS:[0] (регистрация собственного обработчика исключений) –почаще обращайтесь к этой ячейке из расшифровщика,​ не позволяя хакеру быстро определить момент завершения расшифровки (вызовы должны следовать из разных мест, иначе хакер просто наложит фильтр,​ благо современные отладчики это позволяют).
 +
 +Обязательно привязывайтесь к начальному значению глобальных инициализированных переменных,​ т. е. поступайте так: FILE *f = 0;​ main(){if (!f) f = fopen(,,​)…}.,​ тогда дамп, снятый вне оригинальной точке входа окажется,​ неработоспособным. Дизассемблируете стандартный "​Блокнот"​ – он именно так и устроен.
 +
 +==== защита от модификации на диске и в памяти ====
 +
 +Модификацию кода предотвращает проверка целостности,​ причем следует проверять как целостность самого файла, так и целостность дампа в памяти (хакер может модифицировать программу на лету, или даже ничего не модифицировать,​ а на время перехватить управление и проэмулировать несколько следующих команд,​ или же просто изменить значение регистра EAX после выполнения команды типа TEST EAX,​ EAX или подобной ей).
 +
 +Перехвату управления/​эмуляции противостоять практически невозможно,​ а вот предотвратить модификацию легко – используйте несистематические коды Рида-Соломона (подробнее см. одноименную статью в Системном Администраторе). Чтобы взломать программу хакеру потребуется не только разобраться какие именно байты следует подправить для взлома программы,​ но и рассчитать новые коды Рида-Соломона,​ а для этого ему придется написать собственный кодировщик что не так-то просто (несистематическое кодирование изменяет все кодируемые байты, в отличии от систематического кодирования,​ где к программе просто дописывается "​контрольная сумма",​ проверка которой может быть легко "​отломана"​). Опять-таки это актуально только для многослойной динамической шифровки,​ в противном случае хакер просто дождется завершения декодирования кодов Рида-Соломона и снимет беззащитный дамп.
 +
 +Как вариант – используйте несимметричные криптоалгоритмы. Это предотвратит модификацию файла на диске (но не в памяти!) и, что еще хуже, зашифровка функции по выходу из нее оказывается невозможной (мы ведь не хотим сообщать хакеру секретный ключ?), а, значит,​ код программы рискнут в какой-то момент оказаться расшифрованным целиком. Чтобы этого не произошло,​ расшифровывайте код функции во временный буфер (общий для всех функций),​ не трогая оригинал.
 +
 +Проверяя целость кода не забывайте о перемещаемых элементах. Если программа/​динамическая библиотека будет загружена по адресу отличному от указанного в PE-заголовке,​ системный загрузчик автоматически скорректирует все ссылки на абсолютные адреса. Либо избавьтесь от перемещаемых элементов (ключ /FIXED линкера MS Link), либо, что лучше, проверяйте только ячейки не упомянутые в relocation table.
 +
 +Никогда не блокируйте некоторые пункты меню/​кнопки для ограничения функциональности демонстрационной версии – их не разблокирует только ленивый! Лучше физически вырезайте соответствующий код или на худой конец время от время проверяйте состояние заблокированных элементов управления,​ т. к. они могут быть разблокированы не только в ресурсах,​ но и динамически – посылкой сообщения окну.
 +
 +===== от кого защищаться =====
 +
 +Арсенал современных хакеров состоит преимущественно из: дизассемблера,​ отладчика,​ дампера памяти и монитора обращений к фалам/​реестру.Это очень мощные средства взлома,​ но с ними все-таки можно справиться.
 +
 +==== анти-дизассемблер ====
 +
 +Дизассемблер – в девяти из десяти случаев это IDA Pro. Существует множество приемов,​ приводящих ее в замешательство ("​ее"​ – потому,​ что Ида женское имя): множественные префиксы,​ искажение заголовка PE-файла и т. д., однако,​ смысла в них немного и многослойной шифровки на пару с мусорным кодом для ослепления дизассемблера вполне достаточно (правда,​ опытные хакеры могут написать плагин,​ автоматизирующий расшифровку и вычищающую мусорный код, но таких единицы и вы можете гордиться,​ что ломались у них). Кстати говоря,​ активное использование виртуальных функций в С++ существенно затрудняет дизассемблирование программы,​ поскольку для определения эффективных адресов приходится выполнять громоздкие вычисления или "​подсматривать"​ их в отладчике (но про борьбу с отладчиками мы еще поговорим). Только помните,​ что оптимизирующие компиляторы при первой возможности превратят виртуальные функции в статические.
 +
 +==== анти-отладка ====
 +
 +Еще ни одну из отладчиков не удалось полностью скрыть свое присутствие от отлаживаемой программы и потому он может быть обнаружен. Чаще всего используется сканирование реестра и файловой системы на предмет наличия популярных отладчиков,​ проверка флага трассировки,​ чтение содержимого отладочных регистров/​IDT,​ замер времени выполнения между соседними командами и т. д. (если хотите узнать больше – наберите "​anti-debug"​ в Гугле и нажмите "​ВВОД"​). Однако,​ запрещать пользователю иметь отладчик категорически недопустимо – защита должна реагировать лишь на активную отладку. К тому же все эти проверки элементарно обнаруживаются и удаляются. Надежнее трассировать самого себя, подцепив на трассировщик процедуру расшифровки или генерировать большое количество исключительных ситуаций,​ повесив на SEH-обработчики процедуры,​ делающие что-то полезное. Попутно:​ оставьте в покое soft-ice – в хакерском арсенале есть и альтернативные отладчики.
 +
 +Эмулирующим отладчикам противостоять труднее,​ но ничего невозможного нет. Используйте MMX-команды,​ сравнивая время их выполнения со временем выполнения "​нормальных"​ команд. На живом процессоре MMX-команды работают быстрее. Отладчики же либо вообще не эмулируют MMX-команд,​ либо обрабатывают их медленнее нормальных команд.
 +
 +==== анти-монитор ====
 +
 +Мониторы – очень мощное хакерское средство,​ показывающее к каким файлам и ключам реестра обращалась защищенная программа. Активному мониторингу в принципе можно и противостоять,​ но лучше этого не делать,​ ведь существует и пассивный мониторинг (снятие слепка с реестра/​файловой системы и сравнение его до и после запуска программы),​ а против него не попрешь.
 +
 +Не храните регистрационную информацию в явном виде. Забудьте о флагах регистрации! Вместо этого размазывайте ключевые данные маленькими кусочками по всему файлу, считывайте их в случайное время из случайный мест программы,​ расшифровывая очередной кусок кода.
 +
 +Вместо fopen/​fseek/​fread используйте файлы, проецируемые в память,​ они намного сложнее поддаются мониторингу,​ но это уже тема отдельного разговора.
 +
 +==== анти-дамп ====
 +
 +Дамперам противостоять проще простого. Их много разных,​ но нет ни одного по-настоящему хорошего. Затирайте PE-заголовок в памяти (но тогда не будут работать функции типа LoadResource) или по крайней мере его часть (о том, какие поля можно затирать читайте в "​путь воина внедрение в pe/coff файлы",​ опубликованную в Nxxx "​системного администратора"​). Выключайте временно неиспользуемые страницы функцией VirtualProtect(.,​PAGE_NOACCES,​),​ а перед использованием включайте их вновь. Впрочем,​ хакер может уронить NT в "​синий экран",​ получив образ подопытного процесса в свое распоряжение. Однако,​ при использовании динамической многослойной шифровки толку от этого образа будет немного.
 +
 +===== как защищаться =====
 +
 +Никогда не давайте хакеру явно понять,​ что программа взломана! Тогда ему остается найти код, выводящий ругательное сообщение (а сделать это очень легко) и посмотреть кто его вызвал – вот сердце защитного механизма и локализовано. Используйте несколько уровней защиты. Первый – защита от ввода неправильного s/n и непредумышленного нарушения целостности программы (вирусы,​ дисковые сбои и т. д.). Второй – защита от хакеров. Обнаружив факт взлома,​ первый уровень "​ругается"​ явно, и хакер быстро его нейтрализует,​ после чего в игру вступает второй,​ время от времени вызывающий зависания программы,​ делающий из чисел "​винегрет",​ подменяющий слова при выводе документа на принтер и т. д. При грамотной реализации защиты,​ нейтрализация второго уровня потребует полного анализа всей программы. Да за это время можно десять таких программ написать! Второй уровень никогда не срабатывает у честных пользователей,​ а только у тех, кто купит "​крак"​. Если же вы боитесь,​ что второй уровень случайно сработает в результате ошибки,​ лучше вообще не беритесь за программирование,​ это не для вас.
 +
 +Не показывайте хакеру каким путем регистрируется защита. Это может быть и ключевой файл, и определенная комбинация клавиш,​ и параметр командной строки. Ни в коем случае не считываете s/n или u/r через WM_GETTEXT/​GetWindowText,​ вместо этого обрабатывайте нажатия одиночных клавиш (WM_CHAR, WM_KEYUP/​WM_KEYDOWN),​ прямо из основного потока ввода данных,​ и тут же их шифруйте. Смысл шифровки в том, чтобы вводимая пользователем строка нигде не присутствовала в памяти в явном виде (тогда хакер просто поставит на нее точку останова и могучий soft-ice перенесет его прямо в самый центр защитного механизма). Интеграция с основным потоком ввода предотвращает быстрый взлом программы. Бряк [Точка останова] на WM_XXX ничего не дает, поскольку не позволяет быстро отличить обычные вводимые данные от s/n.
 +
 +Возьмите на вооружение генератор случайный чисел – пусть проверки идут с разной периодичностью с различных частей программы (использовать общие функции при этом недопустимо – перекрестные ссылки и регулярный поиск выдадут вас с головой!). Не используйте функцию rand() – вместо этого отталкивайтесь от вводимых данных,​ преобразуя в псевдослучайную последовательность задержки между нажатиями клавиш,​ коды вводимых символов,​ последовательность открытый меню и т. д.
 +
 +Ни в коем случае не храните "​ругательные"​ строки открытым текстом и не вызывайте их по указателю – хакер мгновенно найдет защитный код по перекрестным ссылкам. Лучше так: берем указатель на строку. Увеличивает его на N байт. Сохраняем указатель в программе,​ а перед использованием вычитаем N на лету (при этом вам придется сражаться с коварством оптимизирующих компиляторов,​ норовящих вычесть N еще на стадии компиляции). ​
 +
 +Избегайте прямого вызова API функций. Наверняка хакер поставит на них бряк [точку останова]. Используйте более прогрессивные методики – копирование API-функий в свое тело, вызов не с первой машинной команды,​ распознание и дезактивация точек останова (подробности в "​Записках мыщъх'​а"​).
 +
 +Разбросайте защитный механизм по нескольким потокам. Отладчики не выполняют переключение контекста и остальные потоки просто не получат управление. Кроме того, очень трудно разобраться в защитном механизме исполняющимся сразу с нескольких мест.
 +
 +Создайте несколько подложных функций,​ дав им осмысленные имена типа CheckRegisters – пусть хакер тратит время на их изучение!
 +
 +===== заключение =====
 +
 +Можно уметь ломать программы,​ не умея их защищать,​ а вот обратное утверждение неверно. Это не вопрос морали,​ это вопрос профессиональных навыков (закон разрешает "​взлом",​ если он не влечет за собой прочих нарушений авторских и патентных прав). Теория – это хорошо,​ но в машинных кодах все не так, как на бумаге и большое количество взломов свидетельствует отнюдь не о всемогуществе хакеров,​ а о катастрофической ущербности защитных механизмов,​ многие из которых ломаются за считанные минуты!
 +
 +Личное наблюдение – за последние несколько лет качество защит ничуть не возросло. Причем,​ если раньше творчески настроенные программисты активно экспериментировали со своими идеями,​ то сейчас все больше склоняются к готовым решениям. Хотелось бы, чтобы эта статья послужила своеобразным катализатором и подтолкнула вас к исследованиям.
 +
 +