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

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

ОГЛАВЛЕНИЕ

Следующая >>

пользоваться ею для обхода защиты было бы для противника странно.


I. Осмысленные имена функций
иим тоном при разработке сложных проектов является разделение за-
ма отдельные, минимально связанные между собой части. При таком щ
це жестко определяются интерфейсы, с помощью которых отдельные •
шлющие могут общаться между собой, и каждая часть разрабатывается
1симо от всех остальных.
ого чтобы программистам было проще обращаться к узлам, разрабо-
IM другими людьми, функциям, через которые происходят взаимодей-
, дают легко запоминающиеся и самоописываюшие имена. Например,
запОМНИТЬ, ЧТО CreateFile ИСПОЛЬЗуеТСЯ ДЛЯ СОЗДЭНИЯ файла,
.ceioControi — для управления устройствами.
сомпиляции программы некоторые фрагменты кода могут быть выне-
во внешние библиотеки и импортированы по именам. Иногда по име-
•нкции можно восстановить даже количество и тип аргументов, кото-
га функция принимает на вход. Многие визуальные среды разработки
имер Borland Delphi и C++ Builder) сохраняют внутри исполняемых
I строки с именами функций, являющихся обработчиками событий,
B
каюших в интерфейсе программы (перемещение мыши, нажатие
:и и т. д.). А иногда в готовой программе остаются фрагменты отла-
й информации, в которой присутствуют имена функций. В любом из
игучаев не составляет большого труда определить, что по конкретному
/ начинается код функции с таким-то именем.
162 Часть III. Как не надо защищать программы

Это не является проблемой для функций, выполняющих действия, не свя-
занные с защитой программы. Но если функция называется CheckLicense
или btnRegisterciick, то противнику имеет смысл в первую очередь иссле-
довать именно такую функцию, т. к. с большой вероятностью в ней выпол-
няются важные действия, относящиеся к работе защиты.
Стоит всячески избегать попадания осмысленных имен функций, относя-
щихся к защитным механизмам, в исполняемые файлы и библиотеки, яв-
ляющиеся частью готовой программы. Если в исходном тексте программы
функции должны фигурировать под осмысленными именами (по соображе-
ниям удобочитаемости), то для сокрытия истинных имен функций можно
использовать макроподстановки (листинг 14.1).

:
Листинг 14.1. Использование #de?ine для сокрытия имон функции п С
^_ - t _ _ _
t (




#de?ine CheckLicense fn23


void CheckLicense (char *pszLic) {
/* текст функции */



void main (void) {
CheckLicense ("License Sting");



При компиляции такого кода препроцессор вместо checkLicense везде под-
ставит fn23, а значит, имя, которое могло бы дать какую-то информацию
противнику, просто не попадется ему на глаза.
Библиотечные функции могут импортироваться и экспортироваться не
только по имени (by name), но и по номеру (by ordinal). Это позволяет во-
обще исключить соответствующие имена из программы и библиотек.
Однако никогда не помешает в готовых исполняемых файлах выполнить
поиск текстовых строк, содержащих названия важных для защиты функ-
ций — никогда нельзя быть уверенным, что компилятор или редактор свя-
зей нигде не оставил важных имен.


14.2. Транслируемые языки
Некоторые широко распространенные языки программирования в процессе
компиляции преобразуют исходный текст в так называемый псевдокод —
Глава 14. Приемы, облегчающие работу противника 163

некоторое промежуточное представление текста программы, не являющееся
машинным кодом. К таким языкам можно отнести Clipper, C#, FoxPro, ин-
сталляционные сценарии InstallShield, Java, Maplnfo Map Basic, MicroStation
MDL, Python, Visual Basic и многие другие. При выполнении программы
виртуальная машина интерпретирует псевдокод и выполняет его на вирту-
альном процессоре.
Теоретически использование виртуальной машины может являться эффек-
тивным способом противодействия исследованию программы, т. к. до нача-
ла анализа алгоритма необходимо разобраться с устройством виртуальной
машины. Но это справедливо только для ситуации, когда система команд,
применяемая машиной, нигде и никем не была описана, т. е. является уни-
кальной.
Очевидно, что для популярных языков программирования это совершенно
не так. Для некоторых языков (например С#, Java и Python) в Интернете
нетрудно найти подробное описание того, как кодируется та или иная опе-
рация, поддерживаемая виртуальной машиной. А интерпретатор языка
Python вообще распространяется в исходных текстах, что не позволяет со-
хранять устройство виртуальной машины в тайне.
Если для какого-то языка нет описания кодов операций, но этим языком
пользуется довольно много программистов, рано или поздно кто-то задастся
целью разобраться в деталях псевдокода и разработает весь необходимый
инструментарий.
Существует несколько причин, почему разобраться с системой команд вир-
туальной машины для транслируемого языка программирования обычно бы-
вает не очень сложно.
Прежде всего, исследователь может компилировать любые примеры и смот-
реть, в какой псевдокод будут превращаться команды. Это очень важно, т. к.
внося незначительные изменения в исходный текст и анализирую разницу
в оттранслированном псевдокоде, гораздо легче устанавливать закономерно-
сти, чем внося изменения в псевдокод и контролируя изменения в поведе-
нии виртуальной машины.
Кроме того, виртуальная машина обычно проектируется, исходя из требова-
ний максимизации производительности. Следовательно, знание базовых
принципов построения эффективных виртуальных машин часто позволяет
быстро разобраться с особенностями конкретной реализации.
Вдобавок система команд виртуальной машины редко бывает очень слож-
ной. Это на реальном процессоре обнулить регистр еах можно несколькими
способами, например:
sub еах, еах; хог еах, еах; and еах, еах; mov еах, 0.
164 Часть III. Как не надо защищать программы

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


Модификация виртуальной машины FoxPro
FoxPro является одной из популярных коммерческих систем управления база-
ми данных (СУБД) и позволяет создавать законченные приложения, функцио-
нирующие независимо от среды разработки. Но существует несколько очень
хороших декомпиляторов, способных полностью восстановить исходный текст
программ, созданных в FoxPro, например ReFox или UnFoxAII. И некоторые
разработчики, использующие FoxPro (например авторы программы Hardware
Inspector), пытаются найти способ защитить свои программы от декомпиляции.
Делается это примерно следующим образом.

Программа разрабатывается в среде FoxPro и компилируется самым обычным
образом. Чтобы ReFox невозможно было использовать для получения исходно-
го текста программы, необходимо изменить способ кодирования псевдокода.
Но тогда и виртуальная машина не сможет работать с перекодированным
псевдокодом. Следовательно, необходимо исправить и виртуальную машину.
Глава 14. Приемы, облегчающие работу противника 165_

После этого скомпилированную программу можно распространять вместе
с модифицированной виртуальной машиной, и ReFox окажется бессилен.

Однако в данной схеме есть одно слабое звено. Дело в том, что исполняющая
часть виртуальной машины обычно оформляется в виде динамической библио-
теки (vfp50O.dll для FoxPro 5 или vfp6r.dll для FoxPro 6) и разрешается свобод-
ное распространение этой библиотеки (как redistributable component). Следова-
тельно, оригинальная (неизмененная) версия виртуальной машины может быть
легко найдена в Интернете. Далее достаточно выяснить, чем отличается мо-
дифицированная версия виртуальной машины, и либо перекодировать про-
грамму, приведя ее к виду, доступному для понимания ReFox, либо модифици-
ровать ReFox таким же образом, каким была модифицирована виртуальная
машина.

Так что модификация виртуальной машины является весьма сомнительным
средством для защиты от декомпиляции псевдокода. К тому же, распростране-
ние модифицированной виртуальной машины почти всегда является наруше-
нием условий лицензии, в согласии с которыми эта машина должна использо-
ваться. В случае с виртуальной машиной FoxPro, являющейся собственностью
корпорации Microsoft, ущемленными оказываются права последней, а это мо-
жет вызвать серьезные последствия.


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


14.3. Условно бесплатные
и Demo-версии
Для того чтобы потенциальные пользователи смогли лучше оценить воз-
можности программы, разработчики часто распространяют демонстрацион-
ные версии своих продуктов. Такие версии, как правило, имеют ограничен-
ный набор функций и/или ограничение на время использования или
количество запусков программы.
Условно бесплатные продукты обычно являются ограниченными версиями,
которые предоставляют пользователю возможность ввода регистрационного
кода, после чего все ограничения снимаются.
166 Часть III. Как не надо защищать программы

14.3.1. Ограничение функциональности
Если автор демонстрационной версии программы хочет сделать недоступ-
ным, например, пункт меню Save, то он может пойти двумя путями:
П при инициализации программы сделать этот пункт недоступным;
• удалить из программы весь код, относящийся к сохранению данных на
диске, и при инициализации программы сделать пункт меню Save недос-
тупным.
Очевидно, что реализация первого способа требует значительно меньших
усилий. Однако существуют специальные инструменты, позволяющие ме-
нять свойства элементов диалога в процессе выполнения программы. С по-
мощью подобных инструментов можно каждый элемент любого диалогового
окна сделать доступным, превратив демонстрационную версию в полноцен-
ную по функциональности программу. А некоторые инструменты можно
даже "обучить" автоматически делать доступными нужные кнопки и пункты
меню при открытии соответствующего диалога.
Поэтому необходимо исключить из кода демонстрационной программы те
функции, которые должны присутствовать только в полной версии. Достичь
желаемого результата, не создавая две очень похожих программы, можно,
например, путем использования директив условной компиляции, поддержи-
ваемых препроцессором языка С. К полезным директивам относятся, на-
пример, #define, #ifndef, #ifdef, #else и #endif. С ИХ П М Щ Ю М Ж О
ОО Ь ОН
добиться того, что, изменяя в настройках проекта всего одно определение
препроцессора (аналог #define), можно будет из одного набора исходных
текстов получить совершенно разные по набору функций программы.

14.3.2. Ограничение
периода использования
Для того чтобы ограничить период возможного использования продукта,
необходимо в некоторой области компьютера сохранить дату установки или
количество запусков. Обычно для этого используются произвольные файлы
или реестр (Registry Database). Многие считают, что, спрятав такой счетчик
в самый дальний угол операционной системы, они сделают невозможным
обнаружение его местоположения, а следовательно, и сброс счетчика.
Однако существует два семейства инструментов, позволяющих определить,
где именно располагается счетчик. К первому семейству относятся программы-
мониторы. Они отслеживают все обращения к файлам или реестру и прото-
колируют те из них, которые попросил запомнить пользователь. Мониторы
Глава 14. Приемы, облегчающие работу противника 167

обычно состоят из двух частей: драйвера, устанавливаемого в ядро операци-
онной системы, и интерфейсной части, посредством которой пользователь
имеет возможность управлять работой монитора и получать результаты про-
токолирования. Наиболее известными являются, наверное, программы File
System Monitor и Registry Monitor, разработанные компанией Sysinternals.
Однако программы могут противодействовать мониторам. Очень важен тот
факт, что монитор является активным инструментом — для того чтобы мо-
нитор выполнял свои функции, он должен находиться в памяти во время
работы исследуемой программы. Следовательно, защищенная программа
может обнаружить присутствие монитора и скорректировать свое выполне-
ние разными способами. Например, она может просто отказаться работать,
если в памяти присутствует монитор. Другой способ заключается в посылке
интерфейсной части монитора сообщения о необходимости завершения ра-
боты. При этом монитор оказывается выгруженным из памяти, программа
продолжает функционирование в чистом окружении. Красиво выглядит и
следующий способ: защищенная программа посылает драйверу монитора
команду временно приостановить регистрацию событий, выполняет важные
обращения, а затем снова разрешает драйверу работать. При этом интер-
фейсная часть монитора никак не отражает тот факт, что работа драйвера
была откорректирована. И пользователь находится в полной уверенности,
что программа не производила никаких обращений, в то время как они
имели место, но монитор в это время просто был отключен.
Противодействие мониторам работает только в том случае, если программа
знает, как определить наличие монитора в памяти и как его обезвредить.

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

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

ОГЛАВЛЕНИЕ

Следующая >>