<< Предыдущая

стр. 22
(из 39 стр.)

ОГЛАВЛЕНИЕ

Следующая >>

содержащую значение всех регистров. При этом значения, которые окажут-
ся в этой структуре после завершения обработчика исключения, будут зане-
сены в соответствующие регистры центрального процессора, включая отла-
дочные.
Самую значительную трудность при снятии защиты, наверное, представляет
собой восстановление таблиц импорта. Однако и эта задача может быть ус-
пешно решена.
Следует отметить, что большинство протекторов оставляют в защищенной
программе таблицу импорта, содержащую как минимум по одной импорти-
руемой функции из каждой библиотеки, использованной в оригинальной
программе, иначе загрузчик, являющийся частью операционной системы,
вообще не отобразит библиотеку в адресное пространство процесса. Это по-
зволяет легко определить точный список библиотек, из которых импорти-
руются функции. В крайнем случае, если перечень импортируемых библио-
тек получить не удалось, существует возможность узнать, какие вообще
библиотеки были подключены к процессу. Это будут все библиотеки, на ко-
торые есть ссылки в таблицах импорта незащищенной программы, и неко-
торое количество других библиотек.
После того как все используемые динамические библиотеки оказались ото-
бражены в адресное пространство программы, можно получить адрес каж-
дой экспортируемой функции для всех необходимых библиотек.
Теперь остается только найти в оперативной памяти, принадлежащей про-
грамме, расположение таблицы адресов импортированных функций и опре-
делить размер этой таблицы.
После загрузки и настройки программы в памяти таблица адресов импорти-
руемых функций имеет обычно следующий вид. Сначала идут несколько
32-битовых значений, являющихся адресами функций, импортируемых из
первой библиотеки. Последовательность указателей, относящаяся к первой
'лава 13. Использование навесных защит 149_

даблиотеке, заканчивается нулевым элементом. Сразу за ним идут указатели
ia импортируемые функции второй библиотеки, которые снова заканчива-
отся нулем, и так далее для всех библиотек.
Гаким образом, следует искать последовательности 32-битовых значений,
аканчивающиеся нулевым элементом, и все элементы последовательности
1олжны совпадать с адресами функций одной из библиотек. Каждая такая
юследовательность будет относиться к одной из библиотек, а все последо-
;ательности вместе будут образовывать таблицу адресов импортируемых
пункций.
'едактор связей (linker) обычно размещает таблицу адресов импортируемых
функций в секции статических данных, так что имеет смысл начинать поиск
[менно оттуда.
Тосле того как определены все функции, необходимо построить и осталь-
[ые таблицы, отвечающие за импорт, но это делается в соответствии со
пецификацией формата переносимого исполняемого файла и очень легко
втоматизируется.
'ешение самой последней задачи — удаление кода протектора — на самом
еле не является необходимым для получения работоспособной программы,
хли восстановить зашифрованные секции, индексы ресурсов, таблицы им-
орта, найти точку входа и создать новый исполняемый файл, в котором
удет присутствовать код протектора, он должен работать и так. Но, удалив
од и данные, добавленные протектором, можно несколько сократить раз-
tep исполняемого файла.


3.4. Борьба технологий
i а щиты и взлома
азумеется, разработчики протекторов не хотят мириться с тем, что восста-
овить оригинальную программу так легко. И они всячески стремятся если
е сделать восстановление невозможным, то хотя бы максимально услож-
ить этот процесс и, самое главное, предотвратить полную автоматизацию.
,ело в том, что для использования автоматического депротектора не нужно
ыть гением, достаточно иметь базовые знания. А вот деактивировать дейст-
ительно серьезную навесную защиту могут буквально единицы — считан-
ые проценты от числа всех людей, серьезно занимающихся исследованием
рограмм (Reverse Engineering), и тысячные доли процента от общего числа
ользователей.
днако специалисты по исследованию программ тоже не хотят мириться
тем, что протектор не удается быстро снять, и придумывают новые методы
150 Часть III. Как не надо защищать программы

обхода защиты. Такое неофициальное противостояние продолжается не пер-
вый год, и конца ему пока не видно. Но, похоже, именно это противостоя-
ние и стимулирует развитие протекторов. Иначе уровень технологических
решений, используемых для защиты, остался бы на уровне, достигнутом
еще в первых версиях протекторов.
Для того чтобы код (исполняемые инструкции) нельзя было прочитать из
памяти после запуска программы, применяется шифрование отдельных уча-
стков кода с расшифровкой их непосредственно перед началом и зашиф-
ровкой сразу по окончании выполнения.
Одним из способов достижения такого поведения программы является ис-
пользование API протектора. То есть программист, разрабатывающий про-
грамму, которая позже будет подвергнута обработке протектором, специаль-
ным образом описывает, какие области должны быть зашифрованы
большую часть времени выполнения программы, и явным образом указыва-
ет, в какой момент должны производиться расшифрование фрагмента и его
последующее зашифрование, обращаясь к API протектора.
Обычно эта схема применяется в сочетании с использованием регистраци-
онных кодов или лицензионных файлов. Пока пользователь имеет в своем
распоряжении только незарегистрированную (ограниченную) версию про-
граммы, некоторые функции хранятся в зашифрованном виде и не могут
быть выполнены. Если же пользователь приобретает лицензию на програм-
му и вводит правильный регистрационный код (или указывает расположе-
ние лицензионного файла), программа получает возможность расшифровать
и выполнить зашифрованные фрагменты, обеспечив тем самым полную
функциональность.
Другой способ защиты кода заключается в использовании особенностей рабо-
ты процессора. Можно модифицировать некоторые фрагменты программы
таким образом, чтобы при передаче им управления возникали так называемые
исключительные ситуации (Exception). Эти исключительные ситуации будут
обработаны кодом протектора, который должен восстановить правильное со-
держимое модифицированного фрагмента кода, позволить ему выполниться,
а затем снова привести к неработоспособному состоянию.
Для сокрытия адреса точки входа можно, например, первые несколько де-
сятков или сотен байт скопировать внутрь тела протектора и передать
управление оригинальному коду программы уже после того, как часть ко-
манд была выполнена. В результате, некоторое количество команд, выпол-
няемых только один раз при старте программы, просто будут отсутствовать
в коде программы.
Для программ, написанных с использованием "чистого" Win32 API (без биб-
лиотек типа Object Windows Library или Visual Components Library), pecypc
Слава 13. Использование навесных защит 757

окна часто подгружается в память неявно путем вызова функции
CreateDialogParam ИЛИ CreateDialoglndirectParam. При Э О
Т М не сама
Программа, а менеджер диалогов, являющийся частью операционной систе-
мы, читает ресурс, описывающий окно, и интерпретирует его, подгружая из
ресурсов меню, картинки и т. д.
Однако некоторые средства разработки сохраняют описания диалогов в соб-
ственном формате и практически не полагаются на то, как работает с ресур-
сами менеджер диалогов, встроенный в Windows. Это характерно, например,
для программ, созданных при помощи Borland Delphi или C++ Builder с ис-
пользованием библиотеки Visual Components Library' (VCL).
Ресурс, описывающий диалоговое окно в VCL, хранится как двоичный блок
данных RCDATA, и Windows не пытается самостоятельно его читать и интер-
претировать. Поэтому протектор может модифицировать программу таким
образом, чтобы ресурсы, описывающие диалоги, сохранялись в зашифро-
ванном виде и расшифровывались только в тот момент, когда программа
обращается к VCL с запросом на загрузку диалога из ресурсов. Этот метод
позволяет лучше защитить ресурсы, но, разумеется, применим далеко не ко
всем программам.
С защитой данных можно поступать так же, как и с ресурсами, если извест-
ны особенности использованного компилятора и редактора связей. Очень
многие программы, созданные в определенных средах разработки, начинают
выполнение с одних и тех же действий. Протектор может определить, если
защищаемая программа начинается именно с таких действий, и перенести
их выполнение в тело протектора. Таким образом, к тому моменту как
управление будет передано защищенной программе, протектор уже выпол-
нит часть настроек, но позаботится о том, чтобы программа их не выполня-
ла повторно. Следовательно, в оригинальной точке входа программа содер-
жит модифицированные данные, и получить на их основе работоспособную
копию со снятой защитой будет проблематично.
Для защиты содержимого таблицы адресов импортируемых функций могут
применяться весьма разнообразные подходы. Например, протектор может
заполнить таблицу импорта ссылками на маленькие функции-переходники,
расположенные в теле протектора. А каждый переходник будет передавать
Управление нужной функции во внешней библиотеке. Как расширение
этого метода, внутрь функции-переходника из библиотечной функции мо-
жет копироваться несколько первых инструкций, которые будут выполнять-
ся в теле протектора. Следовательно, управление будет передаваться не на
Первую инструкцию библиотечной функции. Это не только затрудняет вос-
становление таблиц импорта, но и позволяет обойти точки останова, разме-
ренные в начале библиотечных функций.
152 Часть III. Как не надо защищать программы
_

Некоторые функции Win32 API во время выполнения процесса всегда воз-
врашают одно и то же значение. Примером такой функции является;
GetcommandLine. Протектор может вызвать эту функцию до передачи управ-
ления основной программе и запомнить результат, а на запросы программы,
к GetcommandLine просто возвращать сохраненное значение. При этом nepe-j
дачи управления библиотечной функции вообще не происходит.
Наконец, в распоряжении разработчиков протектора есть довольно сложное
в реализации, но очень эффективное средство — трансляция части инструк-
ций в псевдокод и выполнение этого псевдокода на встроенной виртуальной
машине. То есть к моменту передачи упра&чения в тело защищенной про-
граммы некоторые фрагменты кода, относящиеся или к самой программе,
или к импортированным функциям, представлены не в системе команд
процессора семейства х86, а в некотором альтернативном виде, и выполнить
эти фрагменты кода может только протектор. Следовательно, для снятия
защиты необходимо разобраться в системе команд виртуальной машины и
перевести защищенные фрагменты из системы команд протектора в систему
команд процессора х86, а это весьма трудоемкая задача.
Как уже упоминалось ранее, некоторые средства защиты имеют свой собст-
венный API, позволяющий защищаемой программе обращаться к протекто-
ру во время выполнения. Это способствует интеграции протектора с про-
граммой и создает очевидные трудности, связанные с необходимостью
эмуляции API протектора при снятии защиты.
Также разработчики некоторых протекторов предлагают тем, кто хочет за-
щищать свои программы, различные способы, помогающие установить
факт, что с программы была снята защита. Эти способы могут являться ча-
стью API протектора, а могут и основываться на характерных признаках,
которыми обладает защищенная программа после ее загрузки в память.
Очень многие протекторы усиленно препятствуют использованию средств
отладки, включая отладочные регистры. Одним из методов, мешающих
использованию аппаратных точек останова, является хранение промежу-
точных данных протектора в отладочных регистрах, следствием чего явля-
ется отказ в запуске программы, если содержимое отладочных регистров
меняется извне.
Но, как уже было сказано выше, исследователи программ постоянно совер-
шенствуют методы атаки на протекторы, и если такие способы защиты, как
виртуальная машина, способны противостоять автоматическим депротекто-
рам, то против ручной распаковки, наверное, не способен устоять ни один
из существующих протекторов.
Глава 13. Использование навесных защит 153


13.5. Несколько интересных протекторов
В настоящий момент разработано достаточно большое число протекторов
исполняемых файлов. Многие из них бесплатны и созданы энтузиастами,
которым просто интересно попробовать свои силы в защите программ. Ра-
зумеется, есть и коммерческие протекторы. А некоторые протекторы явля-
ются составляющими частями более сложных комплексов, включающих
в себя привязку к аппаратным ключам или компакт-дискам.
Рассмотрим основные характеристики нескольких наиболее интересных
протекторов.

13.5.1. ASProtect
Для защиты условно бесплатных программ чаще всего, наверное, применя-
ется ASProtect — протектор, разработанный Алексеем Солодовниковым.
ASProtect был чуть ли не первым серьезным протектором, сочетавшим в се-
бе основные функции, применяемые для защиты программ:
• работа с регистрационными кодами на базе RSA-1024;
П поддержка "черного списка" регистрационных кодов;
П ограничение периода работы пробной версии;
П ограничение функциональности пробной версии;
• динамическое расшифрование фрагментов кода при наличии правиль-
ного регистрационного кода;
П API для интеграции защищаемой программы с протектором;
П оригинальные методы противодействия исследованию под отладчиком;
• оригинальные методы защиты от снятия протектора.
Однако благодаря огромной популярности ASProtect является и одним из
самых хорошо изученных протекторов — почти для всех хитростей, приме-
няемых в ASProtect, разработаны или автоматические, или полуавтоматиче-
ские средства обхода.
Иногда у программ, защищенных ASProtect, возникают проблемы с работой
под новыми версиями операционных систем, но автор не прекращает рабо-
ты по совершенствованию протектора и стремится оперативно исправлять
все обнаруженные ошибки, а также добавлять новые защитные механизмы.

13.5.2. Armadillo
Непривычный метод взаимодействия с защищаемой программой использует
протектор, разработанный компанией The Silicon Realms Toolworks и нося-
154 Часть III. Как не надо защищать программы

щий название Armadillo. При запуске защищенная программа выполняется
как 2 процесса. Первый процесс, в котором работает основной код протек-
тора, создает в режиме отладки второй процесс, содержащий собственно
защищенную программу, и управляет его выполнением.
Протектор Armadillo применяет оригинальные технологии, называемые
CopyMemll и Nanomites, для защиты кода выполняемой программы от счи-
тывания из памяти. Технология CopyMemll уже хорошо изучена и легко
обходится автоматическими депротекторами. Про существование автомата,
способного обойти Nanomites, пока не известно, но были многочисленные
сообщения о ручной распаковке программ, при защите которых использова-
лась эта технология.
Протектор Armadillo также включает в себя менеджер лицензий.


13.5.3. РАСЕ InterLok
Протектор InterLok, разработанный компанией РАСЕ Anti-Piracy, имеет
версии для Windows и Macintosh. Набор функций, предлагаемых протекто-
ром, вполне обычный: менеджер лицензий, демонстрационные версии, API
для интеграции и т. д.
Версия для Windows устанавливает драйвер ядра, препятствующий отладке
защищенного приложения и выполняющий часть проверок. Но, несмотря
на наличие такого сложного элемента, как драйвер, защита исполняемого
файла довольно слабая. Например, все секции шифруются потоковым шиф-
ром с одним и тем же ключом. Это приводит к тому, что если секция кода
больше, чем любая другая секция, то можно прочитать из памяти запущен-
ной программы расшифрованную секцию кода, вычислить гамму, наклады-
ваемую при шифровании, и расшифровать все остальные секции файла, да-
же не прибегая к сложным инструментам. Драйвер практически не имеет
защиты от исследования, а таблицы импорта вообще никак не защищены.


13.5.4. HASP Envelope
В комплект разработчика, поставляемый вместе с ключами HASP, входит
протектор HASP Envelope. Цель этого протектора — защитить программу от

<< Предыдущая

стр. 22
(из 39 стр.)

ОГЛАВЛЕНИЕ

Следующая >>