Подходы к защите программного обеспечения от атак злонамеренного хоста

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

Full description

Saved in:
Bibliographic Details
Date:2006
Main Authors: Анисимов, А.В., Иванов, И.Ю.
Format: Article
Language:Russian
Published: Інститут програмних систем НАН України 2006
Subjects:
Online Access:https://nasplib.isofts.kiev.ua/handle/123456789/2336
Tags: Add Tag
No Tags, Be the first to tag this record!
Journal Title:Digital Library of Periodicals of National Academy of Sciences of Ukraine
Cite this:Подходы к защите программного обеспечения от атак злонамеренного хоста / А.В. Анисимов, И.Ю. Иванов // Проблеми програмування. — 2006. — N 1. — С. 41-61. — Бібліогр.: 21 назв. — рос.

Institution

Digital Library of Periodicals of National Academy of Sciences of Ukraine
_version_ 1859890626857795584
author Анисимов, А.В.
Иванов, И.Ю.
author_facet Анисимов, А.В.
Иванов, И.Ю.
citation_txt Подходы к защите программного обеспечения от атак злонамеренного хоста / А.В. Анисимов, И.Ю. Иванов // Проблеми програмування. — 2006. — N 1. — С. 41-61. — Бібліогр.: 21 назв. — рос.
collection DSpace DC
description Рассматриваются существующие методы защиты ПО от действий злонамеренного хоста, для каждого метода дается оценка его стоимости и обеспечиваемой защиты. Приводится комбинированный метод защиты ПО, представляющий собой усовершенствование двух существующих методов: затемнения, основанного на непроницаемых предикатах, и защиты от внесения изменений в код на основе «забывчивого хэширования». Предложенный метод обеспечивает более высокий уровень защиты по сравнению с базовыми методами, а также является применимым для более широкого класса программ. Розглядаються існуючі методи захисту ПЗ від дій злочинного хоста, для кожного методу надається оцінка його вартості і захисту, який він забезпечує. Наводиться комбінований метод захисту ПЗ, що являє собою удосконалення двох існуючих методів: затемнення, що базується на непроникливих предикатах, і захисту від внесення змін до коду на основі “забудькуватого хешування”. Запропонований метод забезпечує більш високий рівень захисту у порівнянні з базовими методами, а також може бути застосованим для більш широкого класу програм. The article considers existing approaches to software protection against attacks caused by malicious hosts. For each approach its cost and provided protection level are considered. As a result of improvement of two existing methods (obfuscation based on opaque predicates and tampering protection based on oblivious hashing), new complex protection method is proposed. The suggested method guarantees higher protection level than the base methods, besides it can be used with wider class of programs.
first_indexed 2025-12-07T15:53:56Z
format Article
fulltext Захист інформації © А.В. Анисимов, И.Ю. Иванов, 2006 ISSN 1727-4907. Проблеми програмування. 2006. № 1 41 УДК 681.3:004.056 А. В. Анисимов, И. Ю. Иванов ПОДХОДЫ К ЗАЩИТЕ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ОТ АТАК ЗЛОНАМЕРЕННОГО ХОСТА Рассматриваются существующие методы защиты ПО от действий злонамеренного хоста, для каждого ме- тода дается оценка его стоимости и обеспечиваемой защиты. Приводится комбинированный метод защиты ПО, представляющий собой усовершенствование двух существующих методов: затемнения, основанного на непроницаемых предикатах, и защиты от внесения изменений в код на основе «забывчивого хэширова- ния». Предложенный метод обеспечивает более высокий уровень защиты по сравнению с базовыми мето- дами, а также является применимым для более широкого класса программ. Введение В настоящее время достаточно акту- альной является проблема защиты программ- ного обеспечения (ПО). Под защитой ПО по- нимается противодействие атакам – злона- меренным действиям, направленным на то, чтобы заставить ПО работать некорректно (возможно, с выгодой для атакующего), либо на раскрытие конфиденциальной информа- ции. Атаки на ПО можно разделить на два класса: атаки злонамеренного клиента и атаки злонамеренного хоста. В первом случае ПО выполняется на гарантированно доверенном хосте и подвергается атакам множества кли- ентов, пытающихся воздействовать на него извне через предоставляемый им интерфейс. Угроза атаки злонамеренного хоста имеет ме- сто, когда окружение, в котором будет вы- полняться ПО, на этапе его разработки неиз- вестно, а значит, последнее потенциально может подвергаться любому воздействию со стороны хоста, а также других программ, вы- полняющихся на нем. В дальнейшем изложе- нии наряду с использованием термина «ПО» будем использовать термины «программа», «программный продукт», «приложение». Следует отметить, что в данном случае термин «хост» понимается в более широком смысле, чем «узел в Интернете», и подразу- мевает некоторую управляющую сущность, в рамках которой выполняется программный код. Хостом может выступать и ядро опера- ционной системы, исполняющее программу, и некая виртуальная машина, интерпрети- рующая байт-код. Хост инкапсулирует вы- полнение ПО и, следовательно, имеет полный доступ к его коду и данным. Следует отметить, что противостоять атакам злонамеренного хоста гораздо слож- нее, чем атакам злонамеренного клиента. Не- которую защиту от злонамеренных клиентов может обеспечить ограничение свободы кли- ента (sandboxing) – разрешение или запреще- ние клиенту исполнения конкретных команд. Кроме того, злонамеренный клиент не имеет доступа к коду и данным приложения. В слу- чае же выполнения программы на злонаме- ренном хосте последний может делать с ней все, что угодно (точнее, все, что вычисли- тельно позволимо) – просматривать код, ана- лизировать зависимость выходных данных от входных, трассировать выполнение команд в памяти по шагам. Таким образом, если зло- намеренному клиенту приходится иметь дело с «черным ящиком», то злонамеренному хос- ту программа доступна «на блюдечке». Так как злонамеренный хост может выполнять любые действия с кодом клиент- ской программы, то он имеет все возможно- сти для ее анализа. Поэтому говорить об аб- солютной защите автономного клиентского приложения в этом контексте нельзя – любая защита, которую инкапсулирует приложение, доступна атакующему и может быть в тече- ние определенного времени проанализиро- вана и снята. Тем не менее, несмотря на то что полной защищенности клиентского при- ложения достичь невозможно, некоторого уровня защиты все же можно добиться. При- веденный Б. Шнайером [1] принцип гласит, Захист інформації 42 что защита себя оправдывает, если ее взлом обойдется атакующему дороже, чем создание с нуля продукта, аналогичного защищенному. На сегодня существуют методы обеспечения достаточно сложной для взлома защиты от злонамеренных хостов. Они будут рассмот- рены ниже. Рассмотрим следующие цели: • проанализировать и классифицировать атаки на ПО со стороны злонамерен- ного хоста; • проанализировать существующие ме- тоды защиты исходя из стоимости внедрения и обеспечиваемого уровня защиты; • предложить метод, обеспечивающий защиту от обратного проектирования и модификации кода и данных про- граммы, основываясь на методах, предложенных в последних работах в этой области. Виды атак на клиентское ПО Можно выделить три наиболее распро- страненных вида атак на ПО со стороны зло- намеренного хоста: несанкционированное ис- пользование либо распространение ПО (пи- ратство); кража интеллектуальной собст- венности или конфиденциальных данных, со- держащихся в программе (в том числе вос- становление логики работы программы); не- законная модификация кода программы. Пиратство – вид деятельности, свя- занный с неправомерным распространением или использованием ПО. Существует множе- ство способов проведения таких атак, среди которых (но не ограничиваясь ими) можно выделить следующие: • неправомерное копирование – пере- нос программы на другой компьютер и ее выполнение на нем в случае, если это не разрешено лицензией. Следует отметить, что для противодействия этой атаке важно сделать невозмож- ным не столько копирование про- граммы, сколько ее выполнение на другом компьютере. Этот вид атаки мало распространен в связи с несо- вместимостью с современной бизнес- моделью распространения ПО; • неправомерное использование – вы- полнение программы (либо использо- вание ее результатов) пользователем, которому автор или владелец не пре- доставил разрешения на выполнение; • нарушение требований лицензии на ПО; • перепродажа программного продукта от своего имени. Незаконное копирование и перепро- дажа ПО, а также несанкционированное его использование лицами, которые не имеют на это права, ежегодно обходится производите- лям, по разным оценкам, потерями от 10 до 12 млрд. долларов. По данным [2], 36% всего используемого в мире ПО является пират- ским. Можно утверждать, что пиратство –ос- новная проблема для разработчиков и рас- пространителей коммерческого ПО. Это под- тверждается и обилием решений для проти- востояния пиратству. За всю историю ком- мерческого ПО были придуманы тысячи спо- собов (как программных, так и аппаратных) защиты ПО от нелегального использования и распространения. Кража интеллектуальной собствен- ности или конфиденциальных данных – это целенаправленный процесс анализа кода ПО с целью извлечения из него определенных функциональных возможностей, а также рас- крытия алгоритмов или данных, используе- мых в программе, которые могут представ- лять интерес для атакующего. Анализируя извлеченные из программы данные, атакую- щий (называемый также обратным проекти- ровщиком, или реверс-инженером) может получить доступ к алгоритмам программы. Возможно, некоторые из этих алгоритмов за- щищены патентами или просто должны со- храняться в секрете (например, по причине манипулирования секретными ключами). Основными инструментами реверс- инженера являются дизассемблер и отладчик (большинство современных реализаций объе- диняют эти инструменты в одном продукте). Захист інформації 43 Дизассемблер позволяет по выполняемому коду восстановить исходный код программы в виде инструкций на языке ассемблера, а в некоторых случаях – и в виде программы на языке более высокого уровня (например, С). Отладчик позволяет загрузить программу «внутрь себя» и контролировать ход ее вы- полнения (выполнять инструкции программы «по шагам», предоставлять доступ к ее адрес- ному пространству, отслеживать обращения к разным участкам памяти). Следует отметить, что реверс-инженер может обойтись и без этих средств, просто рассматривая программу как «черный ящик», подавая ей на вход спе- циальным образом сформированные данные и анализируя выходные данные. Однако анализ по методу «черного ящика» крайне неэффек- тивен ввиду его малой производительности. Поскольку этот метод практически не ис- пользуется при взломе программ, выполняю- щихся на злонамеренном хосте, вопрос про- тиводействия ему в данной статье не рас- сматривается. Стоить отметить, что обратный анализ программ, написанных на языках, которые компилируются в промежуточный интерпре- тируемый код (например, Java, C# и другие CLR-языки), на порядок проще программ, на- писанных на языках, компилирующихся в машинный код, поскольку в исполняемый код таких программ записывается информация про их семантическую структуру (об их клас- сах, полях, методах и т.д.). В связи с возрос- шей популярностью таких языков програм- мирования (и, в частности, .NET Framework) задача защиты программ, написанных с их использованием, становится все более акту- альной. Третьим видом атак на клиентские приложения со стороны злонамеренного хос- та является модификация кода программы – преднамеренное или непреднамеренное из- менение выполняемого кода программы, при- водящее к отклонениям программы от нор- мального хода выполнения. Например, ата- кующий может изменить процедуру проверки лицензионного ключа так, чтобы для любого переданного значения она возвращала TRUE, и, таким образом, программа бы считала лю- бое значение лицензионного ключа коррект- ным. Кроме того, атакующий может добавить в программу-жертву код, отсылающий кон- фиденциальную информацию об окружении, в котором она выполняется, на его компью- тер. Способность программы определять, что она была изменена, очень важна, так как из- менения, внесенные в программу, могут при- вести к самым печальным последствиям (на- пример, в случае, если программа обслужива- ет больницу, электростанцию или другую критическую службу). Отдельно стоит отме- тить, что подобные атаки могут быть выпол- нены компьютерными вирусами, заражаю- щими исполняемый код. Если программа су- меет противостоять заражению (или опреде- лить, что она была заражена), это сможет су- щественно снизить темпы распространения вирусной эпидемии. Следует отметить, что вышеперечис- ленные виды атак не являются независимыми друг от друга. Скорее, даже наоборот – они очень тесно переплетены между собой. Так, проблема создания генератора ключей (пи- ратство) опирается на исследование, каким образом в программе выполняется проверка лицензионного ключа (реверс-инженерию); еще один подход софтверных пиратов – уда- ление из программы кода проверки лицензи- онного ключа – связан с модификацией кода. Поэтому рассматривать атаки независимо друг от друга не имеет смысла, нужно скон- центрироваться на методах, обеспечивающих максимальное противодействие всем видам атак одновременно. Методы противодействия атакам на клиентское ПО Среди множества существующих ре- шений для противодействия вышеописанным атакам можно выделить следующие наиболее распространенные методы защиты ПО: • затемнение (obfuscation); • вынесение критического программ- ного кода в отдельный защищенный модуль; • использование лицензионной метки; • проверка целостности программного кода; Захист інформації 44 • создание защищенной среды выполне- ния. Основной целью первых двух методов есть противодействие обратному проектиро- ванию. Очевидно, чтобы усложнить работу реверс-инженеру, нужно либо «запутать» ис- ходные данные так, чтобы в них было сложно отследить какие-либо зависимости (а следо- вательно, и разобраться в принципе их ра- боты), либо поместить их на такой носитель, где анализировать их будет невозможно или очень сложно. Метод на основе лицензионной метки частично решает проблему пиратства. Он ос- нован на присваивании каждому «легаль- ному» пользователю некоего кода, необходи- мого для корректной работы программы. На- личие этого кода является необходимым ус- ловием для запуска программы либо ее пол- нофункциональной работы. Одним из необходимых условий безо- пасности является гарантия целостности про- граммного кода. Допускать выполнение про- граммного кода, в который были внесены из- менения, ни в коем случае нельзя ввиду не- возможности гарантирования его корректно- сти. Поэтому важнейшей задачей обеспече- ния как безопасности выполняемого кода, так и его корректности является проверка цело- стности программы. Под защищенной средой выполнения понимается некоторое окружение, обладаю- щее свойствами обеспечения защиты испол- няемого программного кода от исследования или изменения извне. В качестве примера за- щищенной среды выполнения можно при- вести операционную систему, не предостав- ляющую доступа к адресному пространству памяти программы. Рассмотрим каждый из методов более подробно. Затемнение Затемнение (другой термин – запуты- вание) программного кода является естест- венным следствием желания обеспечить со- хранность чувствительного кода путем мак- симального сокрытия логики его работы. Очевидно, что с помощью одного запутыва- ния невозможно достичь уровня защиты хотя бы сравнимого с уровнем, который обеспечи- вают криптографические методы, такие как, например, шифрование публичным ключем. Однако защита программного обеспечения от злонамеренного хоста – область, в которой вся мощь шифрования оказывается полно- стью беззащитной перед реверс-инженерией. Единственным способом, который мог бы за- труднить обратный анализ, является запуты- вание семантики, сокрытие логики работы программы с тем, чтобы помешать атакую- щему понять особенности ее работы. Верхним пределом времени, необхо- димого для выполнения реверс-инженерии программы P, является время, необходимое для изучения P как черного ящика (т.е. осно- вываясь только на анализе зависимости вы- ходных данных программы от входных), плюс время, необходимое для реализации об- наруженных зависимостей в новой про- грамме. Таким образом, идеальным затемне- нием будет преобразование, которое по про- грамме P строит такую программу P’, что стоимость ее анализа эквивалентна анализу P по методу черного ящика. Коллберг [3] выделил основные прин- ципы затемнения программного кода. Затем- нить программу P означает, имея множество затемняющих преобразований T = {T1, …, TN} и программу P, состоящую из объектов исходного кода (классы, методы, функции, операторы) {S1, …, SK}, построить новую программу P’ = {S1’=Ti1(S1), …, Sj’ = Tij(Sj), … } такую, что выполняются следующие условия: • поведение P’ полностью совпадает с поведением P (преобразования явля- ются семантически замкнутыми); • P’ обеспечивает сокрытие семантики, т.е. анализ и реверс-инженерия P’ бу- дет занимать не меньшее время, чем анализ и реверс-инженерия программы P; • устойчивость каждого преобразования Tik(Sj) является максимальной, т.е. вычислительно сложно построить про- Захист інформації 45 грамму, которая будет выполнять об- ратные преобразования, или же вы- полнение такой программы будет чрезвычайно ресурсоемким; • незаметность преобразований Tik яв- ляется максимальной, т.е. по стати- ческим свойствам S’j будут доста- точно близки к Sj; • стоимость (дополнительное время вы- полнения или занимаемое простран- ство, вызываемое преобразованиями) P’ является минимальной. Выделяется несколько видов затем- няющих преобразований исходя из способа их действия [3]. Лексические преобразования. Этот вид затемняющих преобразований является самым простым и в то же время обеспечива- ющим наименьшую защиту. Действие лекси- ческих преобразований распространяется то- лько на лексическую структуру программы, т.е. фактически сводится к переименованию идентификаторов (переменных, классов, ме- тодов), затрудняя, таким образом, анализ ло- гики программы на основании имен этих идентификаторов. Основной сферой применения лекси- ческих преобразований являются интерпре- тируемые языки программирования, исход- ные программы на которых компилируются не в машинный код, а в байт-код – машинно- независимый псевдоязык, понятный интер- претатору языка. Обычно байт-код содержит информацию об исходном коде (имена иден- тификаторов, названия классов и т.п.), по- этому вопрос скрытия такой информации яв- ляется существенным для интерпретируемых языков. Пример лексического преобразования показан на рис. 1. Слева приведен оригиналь- ный код программы, а справа – код той же программы после лексического затемнения. Лексические преобразования предот- вращают некоторое количество попыток во- ровства интеллектуальной собственности, однако не создают серьезного препятствия для опытного реверс-инженера. Преобразования хода выполнения. Второй тип затемняющих преобразований оперирует с процессом выполнения про- граммы, т.е. субъектом преобразований яв- ляются последовательности вызовов функций (операторов). Коллберг в [4] предложил несколько затемняющих преобразований процесса вы- полнения программы, основанных на непро- ницаемых предикатах (opaque predicates). Предикат P является непроницаемым (opaque), если его значение известно до за- enum CoffeeType { Espresso, Capuccino, Glasse }; class CoffeeMaker { void PrepareCoffee(CoffeeType type) { BoilWater(); if (type == Espresso) { PrepareEspresso(); } else if (type == Capuccino) { PrepareCapuccino(); } else if (type == Glasse) { PrepareGlasse(); } } } enum Xk83_cC_394bn { OZ0ec, j0_390_SdntSXs, a }; class _LdlJS38_38ncJ { void Sdc03_299(Xk83_cC_394bn xd93) { X304afel3004dl(); if (xd93 == OZ0ec) { XrKdJXld(); } else if (xd93 == j0_390_SdntSXs) { os9Scxmd(); } else if (xd93 == a) { dKcmx_eek291__x_c99(); } } } Рис. 1. Пример лексического затемнения Захист інформації 46 темнения, но его сложно получить при ана- лизе затемненной программы. Будем исполь- зовать обозначение PF (PT), если выходом P всегда есть ложь (истина), и P?, если P иногда может возвращать истину, а иногда ложь. На базе множества непроницаемых предикатов можно построить затемняющие преобразова- ния, которые вносят изменения в ход выпол- нения некоторой процедуры. На рис. 2,а мы разделяем блок последовательно выполняю- щихся команд A и B , вставляя простой ис- тинный предикат Р Т, который позволяет предположить, что B выполняется только иногда. На рис. 2,б команда В разделяется на две различные затемненные команды B и B’. Предикат P? случайным образом выбирает одну из них во время выполнения. На рис. 2,в Р Т всегда выбирает В из пары команд В и Вbug, намеренно созданной неправильной версией В. Существует достаточно большое коли- чество преобразований процесса выполнения, похожих на показанные на рис. 3; некоторые из них обсуждаются в [4]. Устойчивость та- ких преобразований напрямую зависит от ус- тойчивости непроницаемых предикатов, на которые они опираются. Таким образом, очень важно уметь конструировать устойчи- вые непроницаемые предикаты, важными критериями выбора которых является их не- заметность и низкая стоимость. При выборе непроницаемых предика- тов логично исходить из проблем, с которыми сталкиваются автоматизированные системы, проводящие анализ затемненного кода. Так, большинство таких систем используют ме- тоды, основанные на статическом анализе. Принимая во внимание факт, что точный ста- тический анализ структур, основанных на указателях и параллельных регионах, явля- ется достаточно затруднительным, эти струк- туры можно использовать в качестве непро- ницаемых предикатов. В качестве примера сильных непрони- цаемых предикатов можно привести преди- каты на основе псевдонимов. Основной идеей этого метода является добавление в про- грамму кода, который строит множество сложных динамических структур. Множество глобальных указателей ссылаются на вер- шины внутри этих структур. Вышеуказанные структуры в ходе выполнения программы бу- дут случайным образом обновляться (будут изменяться внутренние указатели, добав- ляться и удаляться вершины и т.п.), но неко- торые условия будут поддерживаться в неиз- менном состоянии. Примерами таких условий могут быть «указатели p и q никогда не будут указывать на один и тот же элемент в куче», «существует путь от p до q» и т.п. Впоследст- вии эти условия будут использоваться для ге- нерации непроницаемых предикатов. В [5] в качестве преобразования хода выполнения предлагается вырождение графа логики выполнения. Определение графа ло- гики программы извне – достаточно простая операция, линейно зависящая от количества простых блоков в программе. По вырожден- ному же графу достаточно сложно опреде- лить ход работы программы. Вырождение графа логики проходит в Рис. 2 Захист інформації 47 два этапа. На первом этапе высокоуровневые структуры (например, циклы) преобразуются в эквивалентные if-then-goto-структуры. Пример такого преобразования показан на рис. 3. На втором этапе операторы goto, появившиеся в результате первого этапа, мо- дифицируются таким образом, что цели этих операторов goto определяются динамически. Пример второго этапа приведен на рис. 4. Преобразования данных. Третьим типом затемняющих преобразований является затемнение данных. Наиболее распростра- ненным преобразованием является разделение переменной – представление атомарного зна- чения (например, целого или булевого) в виде некоторой сложной структуры данных. В [3] представлено несколько таких представле- ний. Как пример, рассмотрим разделение, по- казанное на рис. 5. Булева переменная V раз- деляется на две целые переменные p и q. Ис- пользуя это представление, можно создать новые реализации для булевых операций. На рис. 6 показана реализация операции логиче- ского И. На рис. 6 показано разделение трех бу- левых переменных A, B и C на короткие це- лые переменные а1, а2, b1, b2, c1, c2 соответственно. Интересным аспектом выбранной интерпретации является возмож- ность вычислить одно значение несколькими способами. Например, операторы (2’) и (3’) отличаются друг от друга, однако оба при- сваивают переменной значение False. В [5] показано преобразование данных, основанное на замене прямых обращений к данным на косвенные. Так, вместо использо- вания локальных переменных-указателей все указатели хранятся в некотором глобальном массиве. При необходимости получить нуж- ный указатель вычисляется некоторая функ- ция, зависящая от состояния программы. Воз- вращаемое значение этой функции содержит индекс в глобальном массиве, где находится нужный указатель. Шифрование кода. Отдельным слу- чаем затемнения являются методы, реали- зующие шифрование программного кода (как всего кода программы, так и отдельных его участков). Эти методы используются в ос- новном для затемнения выполняемого ма- шинного (не интерпретируемого) кода. int a, b; a = 1; b = 2; while (a < 10) { b = a + b; if (b > 10) { b--; } a++; } doSomething(b); Рис. 3 a = 1, b = 2; L1: if (!(a < 10)) goto L4; b = a + b; if (!(b > 10)) goto L2 b—-; L2: a++; goto L1; doSomething(b); Захист інформації 48 Ключ для шифрования (и соответст- венно расшифрования) кода должен быть на- дежно защищен. В идеале, он должен отли- чаться для каждого пользователя программы. Однако такой подход является неприемле- мым для продуктов с большим количеством пользователей, поскольку предполагает, что каждому пользователю будет выдаваться от- дельная копия продукта, зашифрованная ключом этого пользователя. Поэтому более удобным вариантом является использование общего ключа и его хранение в недоступном месте (например, на смарт-карте или сервере приложений). Еще одним подходом к хране- нию ключа является его генерация в ходе вы- полнения программы, однако такой подход более уязвим к реверс-инженерии. Можно выделить два подхода к рас- шифровке выполняемого кода: расшифровка всего кода при запуске программы и поэтап- ная расшифровка необходимых участков кода непосредственно перед их выполнением с по- следующей обратной зашифровкой. Слабо- стью первого подхода является возможность снять копию участка памяти, содержащего расшифрованную программу, поэтому этот подход крайне ненадежен. Для того чтобы получить программу в незашифрованном ви- де, атакующему нужно выполнить все за- шифрованные фрагменты, для каждого фраг- мента снимая дамп памяти. При большом ко- личестве зашифрованных фрагментов эта за- дача становится практически невыполнимой. Шифрование программного кода так- же может применяться и для защиты кода от модификации. При шифровании некоторого Рис. 4 switch(swVar) a=1; b=1; swVar=2; if (!(a<10)) swVar=6; else swVar=3; b=b+a; if(!(b>10 )) swVar=5; else swVar=4 b--; swVar=5; a++; swVar=2; doSomethi ng(b); goto switch И 0 1 2 3 0 3 0 0 0 1 3 1 2 3 2 0 2 1 3 3 3 0 0 3 Рис. 5 g(V) F(p,q) P Q V 2p + q 0 0 False 0 0 1 True 1 1 0 True 2 1 1 False 3 Захист інформації 49 участка кода совместно с данными этого уча- стка кода можно поместить контрольную сумму его байт. Таким образом, если зло- умышленник внесет изменения в шифртекст (например, с экспериментальной целью), кон- трольная сумма при расшифровке не совпадет с оригинальной и эти изменения можно будет отследить. Определенный интерес представляет метод на основании саморасшифровывающе- гося кода, описанный в [6]. Данный метод не предполагает наличия некоторого фиксиро- ванного ключа. Программный код разбива- ется на участки, которые шифруются незави- симо друг от друга. В процессе выполнения программы каждый следующий участок рас- шифровывается ключом, полученным приме- нением некоторой функции к байтам преды- дущего. Очевидно, что этот метод не предос- тавляет защиты от неавторизованного выпол- нения кода, а только от реверс-инженерии и модификации выполняемого кода. В [7] предлагается использовать шиф- рование кода в связке с интерпретатором ко- манд. Инструкции последовательно расшиф- ровываются и передаются на выполнение ин- терпретатору. Другие подходы. В [6] представлен практический метод затемнения, направлен- ный на сокрытие информации о следе выпол- нения (execution path) и доступе к данным. Идея заключается в том, что программа де- лится на сегменты, постоянно перемещаемые в памяти. Каждый раз после выполнения кон- кретный участок кода перемещается в другую область памяти, делая, таким образом, невоз- можным анализ кода, исследуя последова- тельность адресов доступа при выполнении программы. Лоурейро и Молва [8] представили ме- тод сокрытия функций, выполняющихся в по- тенциально опасном окружении, на основе кодов, исправляющих ошибки. Целями ме- тода являются обеспечение секретности ис- полняемого алгоритма (т.е. сокрытие внут- реннего поведения программы) и его целост- ности (атакующий не может изменить алго- ритм). В [9] предложен метод сокрытия функций, который основывается на шифро- вании функций таким образом, что и в за- шифрованном виде они могут выполняться. Существенным его ограничением является неуниверсальность (метод позволяет скры- вать только полиномиальные функции). Эффективность защиты. Затемнение путем преобразований хода выполнения или разделения данных обеспечивает достаточно серьезную защиту от обратного проектирова- ния кода. Для взлома затемненной таким об- разом программы атакующему необходимо воссоздать граф выполнения, для чего он должен определить, какие из непроницаемых предикатов являются «реальными», а какие – «заглушками». Автоматизировать подобную задачу достаточно сложно. Поскольку большинство затемняющих преобразований детерминированы, то задача затемнения легко автоматизируется, ее можно свести к задаче создания лексиче- ского/синтаксического анализатора и преоб- разования полученных от них структур. По- (1) bool A, B, C; (1’) short a1, a2, b1, b2, c1, c2; (2) B = False; (2’) b1 = 0; b2 = 0; (3) C = False; (3’) c1 = 1; c2 = 1; (4) C = A & B; (4’) x = AND[2*a1 + a2, 2*b1 + b2]; c1 = x/2; x2 = x%2; (5) C = A & B; (5’) c1 = (a1 ^ a2) & (b1 ^ b2) ; c2 = 0; (6) if (A) …; (6’) x = 2 * a1 + a2; if ((x==1) || (x==2)) …; (7) if (B) …; (7’) if (b1 ^ b2)…; Рис. 6 Захист інформації 50 скольку все без исключения компиляторы включают такие анализаторы, то на уровне компилятора затемнение реализуется просто. Существенным плюсом затемнения, делающим его наиболее предпочтительным из всех рассматриваемых методов, является его полная автономность. Затемнение опери- рует только с исполняемым кодом и не тре- бует для своей работы никаких дополнитель- ных модулей (как программных, так и аппа- ратных). Однако затемнение не является уни- версальным методом. В [10] доказывается, что невозможно построить затемняющее пре- образование, которое будет эффективно за- темнять любую входную программу, и при- водится семейство функций, для которых не- возможно построить эквивалентные затем- ненные функции. Тем не менее на практике для большинства программ можно построить затемняющие преобразования, которые будут обеспечивать достаточно высокий уровень защиты. Вынесение критического программного кода в отдельный защищенный модуль Эта группа методов основывается на переносе критических участков программ- ного кода (например, конфиденциальных данных или данных, представляющих собой интеллектуальную собственность) на отдель- ный носитель, имеющий существенно боль- шую степень защиты, чем исходная про- грамма. Под такой степенью защиты понима- ется невозможность доступа к коду и к дан- ным, которые хранятся на носителе, невоз- можность трассировки выполнения кода и т.п. Когда программе необходимо получить защищенные данные или выполнить секрет- ный код, она обращается к такому носителю по некоторому протоколу и получает эти данные. Таким образом, атакующему, вместо исходной программы, приходится противо- действовать укрепленному носителю инфор- мации. Кроме обеспечения сокрытия данных, метод с применением защищенного модуля решает и другие задачи защиты программ- ного обеспечения. Во-первых, защищенный носитель может проверять, имеет ли пользо- ватель право на запуск программного про- дукта (аутентифицировать пользователя). Во- вторых, он обеспечивает целостность про- граммного кода, по крайней мере тех участ- ков, которые хранятся и выполняются на нем. Защищенные модули можно разделить на два типа: пассивные (модули, которые только сохраняют данные, но не имеют воз- можности выполнять программный код) и ак- тивные (модули, поддерживающие выполне- ние кода). Очевидно, что вторые представ- ляют больший интерес, так как, в отличие от первых обеспечивают защиту алгоритмов. Можно привести несколько разновид- ностей таких модулей. Защитные заглушки были одним из первых средств противодействия атакам на программное обеспечение. Заглушка пред- ставляет собой небольшое устройство, кото- рое подсоединяется к некоторому (например, LPT, COM или USB) порту компьютера. Наиболее простые устройства могут содержать только идентификационные дан- ные пользователя. Программа при своем за- пуске проверяет наличие такого устройства в соответствующем месте и при его отсутствии аварийно завершается. Такой подход обеспе- чивает защиту только от пиратства и не обла- дает большой стойкостью, так как злоумыш- ленник легко может отключить команды про- верки наличия заглушки в коде программы. Более сложные заглушки могут содержать микроконтроллер, который обладает способ- ностью обрабатывать запросы программы, возвращая соответствующие им ответы. В ходе выполнения программа посылает уст- ройству запросы и проверяет правильность ответов. Такой подход обеспечивает не- сколько большую защиту, однако и она мо- жет быть снята использованием таблиц во- просов-ответов. Наиболее сложные устрой- ства обладают небольшим количеством па- мяти и поддерживают возможность выполне- ния программного кода. В этом случае наи- более критические части программного обес- печения могут располагаться и выполняться на контроллере. В этом случае стойкость за- Захист інформації 51 щиты полностью зависит от стойкости за- щиты контроллера. Смарт-карты являются сравнительно молодой разновидностью защитных заглу- шек. Ранее они уже успешно применялись в качестве средства обеспечения защиты в дру- гих областях – например, предоплаченные телефонные карточки, sim-карты. Концепту- ально смарт-карты имеют лишь два отличия от заглушек: • являются стандартизированными, уст- ройства их чтения и записи доступны для большинства платформ; • смарт-карты могут содержать более чем одну систему защиты, так как данные на них могут сохраняться и изменяться после выпуска карты. В настоящее время широко использу- ются так называемые криптокарты (crypto to- ken). Криптокарта представляет собой не- большое устройство, которое «умеет» хра- нить секретные ключи и выполнять крипто- графические вычисления (например, вычис- ление модульной экспоненты). Методы за- щиты ПО, основанные на использовании ус- тойчивых аппаратных средств, достаточно широко описаны в литературе (напр., [11]). Подход на базе использования дове- ренного сервера обеспечивает достаточно высокий уровень защиты. Он заключается в следующем. Разработчик ПО размещает в Интернете сервер приложений и на этот сер- вер перекладывается функциональность наи- более критичных участков ПО. Выполняясь на пользовательской машине, программа по- сылает серверу запрос на выполнение опре- деленной функции, после чего он возвращает ей результат: Очевидно, что метод с использованием доверенного сервера полностью решает про- блему противостояния обратному проектиро- ванию и, в частности, проблему раскрытия секретного алгоритма. Кроме того, этот метод может использоваться для защиты от пират- ства путем добавления в протокол вызова серверной функции аутентификации клиента. Однако за обеспечиваемую защиту прихо- дится платить некоторыми недостатками. Во- первых, поскольку обмен данными между клиентскими компьютерами и сервером вы- полняется через сеть, доступ к которой может получить кто угодно, то необходима строгая аутентификация доверенного сервера и шиф- рование потоков данных между клиентскими компьютерами и доверенным сервером. Во- вторых, на разработчика ложится двойная за- дача – необходимо обеспечить еще и защиту сервера от внешних атак (подходы к органи- зации такой защиты выходят за рамки данной статьи). Слабостью подхода является и доста- точно низкая производительность (например, при необходимости многократного выполне- ния на сервере некоторой функции), связан- ная с пропускной способностью каналов свя- зи и ограниченными возможностями сервера. Кроме того, недостатком является зави- симость конечного пользователя от наличия подключения к Интернету и состояния сер- вера (при этом пользователь не может быть уверен, что разработчик будет на рынке через 5-10 лет). Существует множество вариантов ([9], [12]), рассматривающих разные способы применения подхода на базе использования доверенного сервера; большинство из них по- священо протоколам обмена данными между сервером и компьютерами пользователей. Эффективность защиты. Описанная схема защиты имеет несколько узких мест, соответственно, существует несколько воз- можных атак, связанных с их эксплуатацией. Первым узким местом, общим и для доверенного сервера, и для защитных заглу- шек, является канал между клиентским ком- Рис. 7 Захист інформації 52 пьютером и защищенным устройством. Так как кто угодно может вклиниться в этот ка- нал, то возникает необходимость обеспечения аутентификации обоих участников и шифро- вания протокола, по которому стороны обме- ниваются информацией. Вторым узким местом является защи- щенное устройство. В этом аспекте доверен- ный сервер является гораздо более предпоч- тительным вариантом, нежели заглушки, так как злоумышленник не имеет к нему физиче- ского доступа. Обеспечить защиту сервера от внешних атак гораздо дешевле, чем создать полностью защищенную от вторжения за- глушку. Стоимость подхода на основе защит- ных заглушек или смарт-карт достаточно вы- сока, поскольку каждого пользователя про- дукта нужно обеспечить таким устройством. Поэтому данный подход подойдет продуктам, имеющим не слишком большое количество пользователей. Существенно более дешевым является подход на базе доверенного сервера. При сравнительно невысокой стоимости он обеспечивает очень высокий уровень защиты. Защита от модификации кода программы Целостность программного кода мо- жет быть нарушена по разным причинам. Во- первых, внести изменения в код может вирус, внедрившийся в программу. Во-вторых, из- менения могут быть преднамеренно внесены недоброжелателем (например, таким спосо- бом он может отключить систему проверки лицензионного кода либо попытаться удалить из программы водяной знак). В любом случае выполнение измененного программного кода нежелательно по причине невозможности га- рантировать правильность его работы. Для предотвращения выполнения программы в случае, если ее код был модифицирован, в программу можно добавить специальный проверочный код, который, в случае обнару- жения изменений в коде программы, будет ее аварийно завершать. На этот код будут воз- ложены такие задачи: а) обнаружить, что программа была модифи- цирована, б) если это возможно, исправить обнаружен- ные изменения, в противном случае обеспе- чить аварийное завершение программы, В идеальном случае обнаружение из- менения в коде и соответствующее этому об- наружению аварийное завершение про- граммы должны быть широко распределены во времени и коде программы, чтобы запу- тать потенциального атакующего. Очевидно, что код if (tampered()) i = 1 / 0 не обеспечивает достаточной защиты в связи с возможностью тривиального обнаружения и устранения. Следующие два способа являются наибо- лее используемыми для проверки программы на наличие внесенных изменений: 1) исследовать исполняемый код про- граммы и проверить, совпадает ли он с оригинальным. Для этого можно ис- пользовать некоторую односторон- нюю хэш-функцию (напр., SHA1). Проверять можно не весь код, а только части, для которых неизменность яв- ляется критичной. Один из вариантов таких проверок приводился в разделе, посвященном шифрованию программ- ного кода; 2) исследовать правильность промежу- точных результатов, получаемых в программе. Этот подход известен как проверка программы (program verifica- tion) [13], [14]. Кроме рассматривае- мых целей, этот метод также может использоваться для верификации/ тес- тирования программы на этапе разра- ботки и является самым применяемым для решения задачи обнаружения из- менений в программе. В [15] для проверки приложения на неизменность предлагается использовать множество объектов – троек <тестер, ин- тервал, корректор>. Тестером называется подпрограмма, вычисляющая хэш-функцию от некоторого интервала (участка кода) про- граммы и сравнивающая его с оригиналом. В процессе выполнения тестеры запускаются и проверяют правильность интервала, за кото- рый они отвечают, путем сравнения ориги- нального значения хэш-функции и действи- тельного. В случае, если значения не совпа- Захист інформації 53 дают, программа через некоторое время ава- рийно завершается. Корректоры необходимы для безопасного хранения оригинальных зна- чений хэш-функции. Корректоры могут быть как константами (т.е. просто массивом байт), так и подпрограммами, конструирующими оригинальное значение. Очевидно, второй подход обеспечивает большую защиту. Использование каскадного подхода и создание зависимостей между тестерами по- может усложнить защиту, обеспечиваемую тестерами. В этом случае злоумышленнику для снятия защиты даже только с одного ин- тервала потребуется обнаружение и уничто- жение всех тестеров, находящихся в коде программы. Подход, похожий на рассмотренный выше, описывается в [16]. Ответственными за целостность приложения являются страж- ники (guards) – подпрограммы, выполняющие проверку целостности данных. Отличием их от тестеров является возможность восстанов- ления измененных участков кода. Для ис- правления изменений используются коды, исправляющие ошибки. Несмотря на то, что на основе подхода, использующего проверки участков выпол- няемого кода, можно построить достаточно сложную систему защиты программы от мо- дификации, этот подход имеет один серьез- ный концептуальный недостаток. Поскольку проверка целостности выполняется на уровне программного кода, то после внедрения за- щиты код программы не может быть изменен. Это приводит к достаточно большим неудоб- ствам – например, к невозможности оптими- зации (сжатия) программного кода с сохране- нием семантики, невозможности добавить к программе цифровую подпись. Кроме того, поскольку тестеры следят за целостностью лексики, в то время как в первую очередь сто- ит задача обеспечить целостность семантики, возникает проблема анализа и оценки уровня обеспечиваемой защиты. У этого подхода имеются и другие не- достатки. Например, операция чтения про- граммой своего собственного кода (характер- ная для приложений, проверяющих свою це- лостность вышеописанным образом) является редко выполняемой, а потому легко обнару- жимой. Поэтому более предпочтительны ме- тоды, проверяющие целостность семантиче- ской структуры программы. Один из таких методов, названный за- бывчивым хэшированием (oblivious hashing), рассматривается [17]. В качестве объекта проверки предлагается использовать не би- нарный код, а след выполнения программы (функции, участка программы и т.п.). Про- грамма (функция, участок) представляется в виде последовательности абстрактных ма- шинных инструкций I={i1, i2, …, iN}, ко- торые обращаются для чтения и записи к уча- сткам памяти M={m1, m2, …, mK}, начального состояния памяти M0, счетчика инструкций C и его начального значения C0. Следом некото- рого участка программы называется пятерка (I, M, C0, M0, P), где P – некоторый вход- ной параметр, который влияет на выполнение участка программы. В процессе выполнения участка программы вычисляется хэш-функ- ция от его следа. Поскольку след отражает семантику участка программы, полученное значение хэш-функции по сути является циф- ровой подписью, выполненной над поведе- нием этого участка. Изменение как кода, так и данных, с которыми оперирует участок, приведет к несовпадению полученного при проверке значения хэш-функции с ориги- нальным значением, что позволит определить наличие изменений в программе. Недостатком данного подхода являе- тся его неуниверсальность. Так, проверку ме- тодом забывчивого хэширования невозможно реализовать для участков программы, опери- рующих случайными данными (такими, как текущее время, положение указателя мыши и т.п.), или данными, множество значений ко- торых очень велико. Это существенно огра- ничивает множество подпрограмм, для кото- рых можно применить этот метод. Эффективность защиты. Защита от модификации кода программы является не- отъемлемой частью общей задачи защиты клиентского программного обеспечения от злонамеренного хоста. Кроме того, про- грамма, обладающая возможностью обнару- жить изменения в своем коде, является более Захист інформації 54 безопасной и в случае выполнения на добро- желательном хосте. Программа, не отслежи- вающая изменений в своем коде, потенци- ально может представлять угрозу как для се- бя, так и для других программ, выполняю- щимся с ней в одном окружении. Основными атаками на алгоритмы за- щиты от модификации, оперирующие бинар- ным кодом программы, являются удаление проверочного кода и подмена хранимых внутри программы оригинальных значений хэш-функции. Из этого следует, что и прове- рочный код, и оригинальные значения хэш- функций должны быть хорошо защищены от воздействия. Для этих целей можно исполь- зовать затемнение, помещение проверочного кода/данных на защищенный носитель и т.п. Атаки на алгоритмы, проверяющие целост- ность семантической структуры программы, являются гораздо более сложными, в первую очередь из-за сложности их обнаружения. Создание защищенной среды выполнения Методом, обеспечивающим доста- точно высокий уровень безопасности клиент- ского ПО, является перенос функций защиты в среду выполнения. Примерами таких защи- щенных сред может быть ядро операционной системы, виртуальная выполняющая машина или же оборудование, поддерживающее за- щиту ПО. Под защищенностью среды пони- мается сложность или невозможность дос- тупа к устройству и содержимому среды из- вне, а также наличие определенных функцио- нальных возможностей по обеспечению безо- пасности выполняемой программы. Можно выделить основные задачи, ко- торые будут возложены на защищенную сре- ду выполнения: • проверка целостности программного кода; • запрещение доступа к адресному про- странству программы; • контроль за выполнением программы, отслеживание необычных и ошибоч- ных ситуаций; • обеспечение шифрования про- граммного кода; • проверка наличия у пользователя пра- ва на выполнение конкретной копии программы. Очевидно, что реализация данного ме- тода не сводится к реализации только защи- щенной среды (например, операционной сис- темы). Понадобится целая инфраструктура различных объектов (среда выполнения, ком- пиляторы, отладчики и т.п.) для того, чтобы обеспечить разработчикам возможность легко разрабатывать программы, не волнуясь про обеспечение их защищенности. Однако, не- смотря на сложность реализации подхода, защита, которую будет обеспечивать такая инфраструктура, будет более чем высокой. Схематично данный подход можно изобразить следующим образом: В подходе используются два основных Рис. 8 Захист інформації 55 инструмента – шифровщик и защищенная выполняющая среда. Шифровщик содержит в себе два ключа – открытый (для шифрования) и закрытый (для подписывания). Зашифро- ванная среда также содержит в себе два клю- ча, соответствующие ключам, принадле- жащим шифровщику. Выполняемый код, созданный компи- лятором, передается на вход шифровщику, который подписывает его секретным ключом, после чего шифрует открытым. При загрузке защищенной программы, среда расшифровы- вает ее с помощью закрытого ключа и прове- ряет целостность с помощью открытого. Если целостность программы подтверждена, за- щищенная среда выполняет ее. Таким образом, программа является защищенной криптографически стойкими ме- тодами как от анализа кода, так и от измене- ния. Кроме встроенных в ядро функций за- щиты, защищенная среда может предостав- лять программе некоторый API (программ- ный интерфейс) для работы с защитой. На- пример, программа сама может «спросить» у ОС (вызывая ее функции), не была ли она из- менена. Кроме того, программа может пере- давать введенный пользователем лицензион- ный ключ на проверку операционной сис- теме, а операционная система (для проверки лицензионного ключа) будет соединяться с сервером разработчика программы. Среда также может предоставлять программе за- щищенное хранилище данных. Таким обра- зом, вместо того чтобы хранить секретные данные в открытом виде, программа может передавать их на хранение среде для обеспе- чения их безопасности. Эффективность защиты. Защищен- ная среда выполнения обеспечивает очень высокий уровень защиты от злонамеренного хоста. Во-первых, гарантируется полноценная защита от модификации программного кода, поскольку его целостность проверяется на уровне среды и защищается надежными криптографическими методами. Кроме того, вследствие ограничения доступа к данным программы во время ее выполнения задача обратного проектирования существенно ус- ложняется. Фактически реверс-инженеру ос- таются только две возможности: анализиро- вать выполняемый код программы (причем и эта возможность отпадает при использовании шифрования программного кода ) либо огра- ничиться для анализа программы методом «черного ящика». Следует также отметить, что программы, выполняющиеся в такой сре- де, абсолютно устойчивы к заражению ком- пьютерными вирусами. Есть и другое достоинство – перенос задачи защиты в защищенную среду выпол- нения позволит разработчику решать постав- ленные перед ним задачи, не тратя времени на построение системы защиты для своей программы. Данный подход имеет два узких места, которые может эксплуатировать атакующий. Первой возможной атакой является подмена среды выполнения. Поэтому необходима обя- зательная аутентификация программой за- щищенной среды (эта возможность может встраиваться в программу шифровщиком). Для аутентификации среды могут использо- ваться криптографические алгоритмы с от- крытым ключом, такие, как RSA или алго- ритм Эль-Гамаля. Вторым узким местом является необ- ходимость обеспечения целостности самой защищенной среды. Поскольку такая среда является полностью доступной для анализа, обеспечение ее целостности – достаточно сложная задача. Создатели среды должны также обеспечить безопасное хранение сек- ретных ключей, необходимых для подписы- вания и расшифровки кода. Аналогичные требования накладываются и на программу- шифровщик. Лицензионные метки Под термином лицензионной метки будем понимать некоторую сущность, позво- ляющую определить, имеет ли программа право на выполнение определенной функции на определенной вычислительной машине. Основной и практически единственной зада- чей лицензионных меток является защита ПО от пиратства. Этот метод очень распростра- нен среди современных производителей ПО. Захист інформації 56 В качестве метки могу выступать: уникаль- ный лицензионный номер (ключ), код акти- вации, файл с лицензией, менеджер лицензий, онлайновая система активации ПО и т.п. Ли- цензия может привязываться к имени пользо- вателя, организации или существующему оборудованию компьютера. Безопасность, обеспечиваемая данным методом, зависит исключительно от алгорит- мов формирования и проверки лицензионных меток. Для взлома простейших лицензионных меток достаточно приобрести одну лицензию на программу, после чего раздать получен- ную лицензионную метку всем желающим. Более надежной является лицензионная метка с необходимостью онлайновой активизации и привязкой к оборудованию, но она приводит к некоторым неудобствам для пользователей. Основными атаками на такие схемы защиты, кроме непосредственного опублико- вания лицензионной метки, являются: а) соз- дание генератора меток и б) обнаружение и удаление кода, отвечающего за проверки ли- цензионной метки из ПО. Можно отметить, что генератор ключей для Windows XP был создан за несколько месяцев, несмотря на то что эта операционная система имела доста- точно хорошо проработанную защиту с сис- темой онлайновой активации и привязкой к аппаратуре. Для избежания атаки (а) необходимо, чтобы алгоритм, используемый для построе- ния лицензионных меток, основывался на стойком криптографическом алгоритме. В ча- стности, в [18] описана система лицензирова- ния, основанная на криптографических алго- ритмах с открытым ключом. Что касается противостояния атаке (б), то сама по себе ли- цензионная метка ее не обеспечивает. По- этому при использовании этого метода важно обеспечить также защиту программы от об- ратного проектирования. Принимая во внимание то, что недос- татком систем онлайновой активизации явля- ется требование подключения к Интернету, такая схема может быть удобна для про- граммных продуктов, деятельность которых связана с этим (браузеры, почтовые клиенты, серверы и т.п.) Следует отметить, что лицензионной меткой не обязательно должен являться чи- словой регистрационный ключ. Так, в этом качестве можно использовать часть функцио- нальности продукта. Например, меткой мо- жет быть динамически подключаемая биб- лиотека (DLL), содержащая в себе, кроме ли- цензионных данных, некоторую функцио- нальность, необходимую для корректной ра- боты продукта. Такой подход обладает двумя положительными качествами – во-первых, его применение сделает невозможным созда- ние генератора ключей, так как атакующий не обладает исходным кодом для сборки биб- лиотек, во-вторых, атакующий не может при- менить атаку на обнаружение проверочного кода ввиду ее бессмысленности. Эффективность защиты. Как было сказано выше, уровень защиты, обеспечивае- мый лицензионной меткой, зависит только от алгоритмов, используемых в ней самой. Про- цесс обработки меток программой сам по се- бе достаточно неустойчив к обратному про- ектированию, поэтому использование меток имеет смысл только при наличии дополни- тельной защиты программы в виде затемне- ния или защищенного модуля. Когда такая защита присутствует, лицензионные метки являются эффективным и недорогим методом защиты от пиратства. Альтернативные подходы к защите про- граммного обеспечения Кроме описанных выше методов за- щиты клиентского ПО, можно отметить не- сколько альтернативных подходов к его безо- пасности. Так, в [19] предлагается метод, ко- торый можно назвать эволюционированием продукта (software aging). Этот метод осно- вывается на периодических обновлениях ПО. Несмотря на кажущуюся бессмысленность, он оказался достаточно хорошей защитой от нелицензионного использования ПО. Суть его заключается в том, что производитель ПО через некоторые, сравнительно небольшие интервалы времени (один-два месяца), обнов- ляет продукт, немного изменив метод за- щиты. Пользователям, обладающим легаль- ной версией, необходимо просто ее обновить. Захист інформації 57 Пользователи же, использующие нелегаль- ную версию, должны ждать, пока взломщик вскроет обновленную версию. Таким обра- зом, нелегальные пользователи становятся зависимыми от взломщика, у которого появ- ляется несравнимо больше обязанностей – теперь ему нужно взламывать новую версию продукта каждый месяц. Задача усложняется, если от версии к версии производитель серь- езно изменяет защиту продукта. Важность взлома очередной версии для нелегального пользователя тем больше, чем больше оче- редная версия продукта несовместима с пре- дыдущей. Еще одним альтернативным подходом к защите является кастомизация – независи- мая сборка программного продукта для каж- дого конкретного клиента. В таких случаях для каждого зарегистрированного пользова- теля собирается отдельная версия продукта с помещенной в код информацией про особу, на которую регистрируется продукт. Таким образом предотвращается получение копии продукта случайным лицом, а в случае, если продукт будет незаконно опубликован, по имеющейся в его коде информации можно будет определить совершившего кражу. Од- нако метод создания персональных сборок имеет смысл только при достаточно неболь- шом количестве пользователей программы, так как при большом количестве пользовате- лей он будет слишком накладным. Кроме математических подходов к за- щите ПО существует еще множество техни- ческих подходов, усложняющих вломщику задачу. Так, широко распространенным явля- ется применение защиты от отладки – ком- плекса мер, направленных на обнаружение факта запуска программы «внутри» отлад- чика, и аварийное завершение ее работы в случае, если отладчик будет обнаружен. Затемнение + проверка целостности – ком- бинированный метод защиты ПО Основываясь на [4], [17], [20] и [21], можно предложить комбинированный метод защиты ПО от обратного проектирования и модификации кода. Метод состоит из двух независимых составляющих: затемнения и добавления проверочного кода. По аналогии с определением непрони- цаемого предиката, приведенного выше, при- ведем понятие непроницаемой функции. Непроницаемой назовем функцию, ре- зультат которой известен на этапе затемне- ния, но его сложно определить при исследо- вании затемненной программы. Затемняющая составляющая метода состоит из двух этапов, суть каждого из кото- рых состоит в выполнении преобразований над исходным кодом программы. На первом этапе каждой переменной (константе), ис- пользуемой в программе, поставим в соответ- ствие некоторый объект, который будет хра- нить ее значение. В качестве такого объекта можно рассматривать как объект в понима- нии ООП, так и просто указатель на струк- туру, в которой хранятся данные переменной. Создадим глобальный массив obj всех объ- ектов, используемых в программе. Таким об- разом, каждая переменная/константа, исполь- зуемая в программе, будет уникально иден- тифицироваться индексом в этом глобальном массиве. Далее, определим в программе функ- цию f такую, что • f является непроницаемой; • входом f является целое число – уни- кальный идентификатор некоторой точки в программе, в которой проис- ходит обращение к переменной, выхо- дом – индекс в массиве объектов. В исходной программе заменим все обращения к переменным/константам на об- ращения к соответствующим объектам таким образом: • каждый объект адресуется как некото- рый элемент глобального массива объ- ектов; • номер объекта в массиве определяется динамически с помощью вызова фун- кции f. Таким образом, после первого этапа все прямые обращения к переменным в коде программы заменены на косвенные, что су- щественно усложняет обратный анализ про- Захист інформації 58 граммы. При достаточной непроницаемости функции f задача определения, какой именно объект является параметром при вызове той или иной функции, является практически не- разрешимой. На втором этапе создадим глобальный массив func всех функций, вызываемых в программе. Каждой функции в соответствие поставим уникальный индекс в этом массиве. Аналогично функции f введем непроницае- мую функцию g, которая, принимая некото- рый уникальный целочисленный аргумент, будет возвращать номер функции в массиве функций. Аналогично тому, как это делалось на первом этапе, заменим все прямые вызовы функций на косвенные. Замена всех прямых вызовов функций на косвенные, определяемые на этапе выпол- нения, делает недоступной для статического анализа информацию о последовательности вызовов функций. Фактически единственная информация, доступная атакующему после второго этапа, – это последовательность вы- полнения условных операторов и операторов цикла (if, while, switch и т.п.), однако этой информации недостаточно для восстановле- ния логики работы программы, так как аргу- менты этих операторов защищены непрони- цаемой функцией. Составляющая метода, ответственная за проверку целостности, основывается на принципе «забывчивого хэширования», пред- ложенном в [17]. Однако в отличие от «забы- вчивого хэширования», применимость кото- рого имеет существенные ограничения, пред- ложенный метод может использоваться для проверки целостности любых подпрограмм. Объектом проверки являются непре- рывные безусловные участки кода, це- лостность каждого из которых проверяется независимо. По ходу выполнения участка, команда за командой, на вход хэш-функции h подаются индексы вызываемой функции и ее параметров в глобальных массивах. После завершения выполнения участка программы полученное значение хэш-функ- ции сравнивается с корректным значением для этого участка. Таким образом обеспечи- вается целостность хода выполнения програ- ммы – последовательности вызываемых фун- кций и передаваемых им объектов-парамет- ров. Однако следует отметить, что вышеука- занная процедура проверяет только целост- ность ссылок на параметры функцій, т.е. что в функцию передаются правильные перемен- ные. Процедура не гарантирует целостности s = 1; t = x; u = y; while (u) { if (u & 1) s = (s * t) % n; u >>= 1; t = (t * t) % n; } Console.WriteLine(s); obj[f(1)] = obj[f(2)]; obj[f(3)] = obj[f(4)]; obj[f(5)] = obj[f(6)]; while (obj[f(7)]) { if (obj[f(8)] & obj[f(9)]) obj[f(10)] = (obj[f(11)]*obj[f(12)])%obj[f(13)]; obj[f(14)] >>= obj[f(15)]; obj[f(16)] = (obj[f(17)]*obj[f(18)])%obj[f(19)]; } obj[f(20)].WriteLine(obj[f(21)]); Рис. 9. Результат первого этапа 0 1 2 3 4 5 6 7 s 1 t x u y N Console Рис. 10. Глобальный массив объектов 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 0 1 2 3 4 5 4 4 1 0 0 2 6 4 1 2 2 2 6 7 0 Рис. 11. Возвращаемые значения функции f Захист інформації 59 данных, содержащихся в используемых объе- ктах. Для проверки целостности данных мо- жно использовать следующий подход. При изменении значения, хранящегося в каком- либо объекте, вычисляется хэш-функция от значений всех объектов в глобальном мас- сиве. При каждом последующем обращении к значению объекта, значение хэш-функции подчитывается заново и сверяется с сохра- ненным. Различие этих двух значений свиде- тельствует о том, что в данные программы были внесены изменения. Стойкость предложенного метода на- прямую зависит от стойкости используемых непроницаемых функций. Проблема построе- ния таких функций выходит за рамки данной статьи, с некоторыми примерами можно ознакомиться в [4]. Предложенный метод имеет следующие достоинства по сравнению с методами, описанными в литературе ранее. • Использование непроницаемых функ- ций вместо непроницаемых предика- тов, а также косвенных вызовов функ- ций позволяет достичь более высокого уровня сокрытия данных. • Процедура проверки целостности «ра- ботает» на уровне логики программы, а не на уровне выполняемого кода, а следовательно, является независимой от особенностей среды выполнения. • Процедура проверки целостности яв- ляется универсальной и не зависит от защищаемой программы. Выводы Проблема защиты ПО от действий злонамеренного хоста является сравнительно молодой (первые работы в этой области поя- вились в 1996 году). В то же время за послед- ние несколько лет было опубликовано мно- жество работ, посвященных этой проблеме. Во многом это обусловлено ее актуальностью в связи с лавинообразным ростом отрасли 0 1 2 3 4 5 6 = !=0 & * % >>= WriteLine Рис. 13. Глобальный массив функций func[g(1)](obj[f(1)], obj[f(2)]); func[g(2)](obj[f(3)], obj[f(4)]); func[g(3)](obj[f(5)], obj[f(6)]); while (func[g(4)](obj[f(7)])) { if (func[g(5)](obj[f(8)], obj[f(9)])) func[g(8)](obj[f(10)], func[g(7)](func[g(6)](obj[f(11)], obj[f(12)]), obj[f(13)])); func[g(9)](obj[f(14)], obj[f(15)]); func[g(12)](obj[f(16)], func[g(11)](func[g(10)](obj[f(17)], obj[f(18)]), obj[f(19)])); } func[g(13)](obj[f(20)], obj[f(21)]); Рис. 12. Результат второго этапа func[0](obj[0], obj[1]); func[0](obj[2], obj[3]); func[0](obj[4], obj[5]); while (func[1](obj[4])) { if (func[2](obj[4], obj[1])) func[0](obj[0], func[4](func[3](obj[0], obj[2]), obj[6])); func[5](obj[4], obj[1]); func[0](obj[2], func[4](func[3](obj[2], obj[2]), obj[6])); } func[6](obj[7], obj[0]); h(0, 0, 1); h(0, 2, 3); h(0, 4, 5); h(while, 1, 4); h(if, 2, 4, 1); h(0, 0, 4, 3, 0, 2, 6); h(5, 4, 1); h(0, 2, 4, 3, 2, 2, 6); h(6, 7, 0); Рис. 14. Хэширование данных в ходе выполнения программы Захист інформації 60 информационных технологий. Несмотря на большое количество опубликованных работ, проблема защиты ПО от злонамеренного хоста находится в заро- дышевом состоянии. Практически отсутст- вуют работы, по формализации проблемы и постановке общих задач, связанных с защи- той; большинство посвящено конкретным ме- тодам, защите от конкретных атак, а не об- щему подходу к защите. Отсутствуют мате- матические модели угроз и методы оценки способов защиты. Тем не менее методы, приведенные в статье, несмотря на их техничность, являются достаточно общими и обеспечивают защиту от широкого спектра атак. В то же время ни один из них не обеспечивает универсальной защиты, а потому для достижения макси- мального уровня защиты рекомендуется ис- пользование всех доступных методов. Из рассмотренных наиболее привлека- тельным представляется метод, основанный на использовании защищенной среды выпол- нения. Однако его внедрение достаточно до- рого, поскольку связано с разработкой такой защищенной среды. Тем не менее уровень защиты, обеспечиваемый защищенной сре- дой, является адекватным стоимости ее раз- работки. Метод с использованием доверенного сервера также обеспечивает высокий уровень защиты, однако доставляет определенные не- удобства для пользователей ПО. Затемнение кода – удачный компромисс между обеспечи- ваемой им защитой, стоимостью внедрения и удобством использования. В отличие от ме- тода на основе защищенного модуля оно не требует никаких дополнительных средств для своей работы (как программных, так и аппа- ратных). Однако стойкость защиты, обеспе- чиваемой затемнением, зависит от конкрет- ного его вида, и, кроме того, затемнение мо- жет применяться не для всего множества про- грамм. Отдельно следует отметить важность процедуры проверки целостности программы. Если программа будет в состоянии опреде- лить присутствие изменений в ее коде, это сможет предотвратить не только попытки не- законного завладения программой, а и мно- жество других отрицательных воздействий, таких, как заражение компьютера вирусом или кража конфиденциальных данных. По- этому проверка на присутствие изменений в коде программы существенно повышает об- щую защищенность программы и вычисли- тельных устройств, на которых она будет вы- полняться. Комбинированный метод защиты, приведенный в статье, развивает идеи, пред- ложенные в [17] и [20]. Предложенный метод обладает рядом достоинств, в частности уни- версальностью и независимостью от конкрет- ной платформы. Кроме того, использование непроницаемых функций вместо непрони- цаемых предикатов обеспечивает более высо- кий уровень сокрытия программного кода. Проблема защиты программного обес- печения от злонамеренного хоста является одной из наиболее сложных проблем совре- менного этапа развития информационных технологий. Несмотря на обилие предлагае- мых решений, задача защиты еще даже не формализована. Можно выделить следующие шаги, которые должны быть предприняты в этом направлении в первую очередь: • формализовать понятия «защитить программу», «взломать программу»; • определить модель угроз, дать матема- тические оценки сложности конкрет- ных атак и методов противостояния им. Только после того, как два эти шага будут сделаны, можно будет оценивать стой- кость алгоритмов в математическом смысле. 1. Шнайер Б. Прикладная криптография. Протоколы, алгоритмы, исходные тексты на языке Си. - М.: Изд. ТРИУМФ, 2002. - 816 с. 2. Business Software Alliance. - http://www.bsa.org. 3. Collberg C., Thomborson C., Low D. Breaking Ab- stractions and Unstructuring Data Structures // Proc. of the 1998 Intern. Conf. on Computer Languages, 1998. - P. 28. 4. Collberg C., Thomborson C., Low D. Manufacturing Cheap, Resilient and Stealthy Opaque Constructs // Proc. of 25th ACM SIGPLAN-SIGACT Symp. on Principles of Programming Languages, 1999. - P. 184 – 196. 5. Wang C., Davidson J., Hill J. Protection of Software- based Survivability Mechanisms // Proc. of the 2001 In- Захист інформації 61 tern. Conf. on Dependable Systems and Networks (for- merly: FTCS), 2001. - P. 193 – 202. 6. Aucsmith D. Tamper Resistant Software: An Imple- mentation // Proc. of the First Intern. Workshop on Infor- mation Hiding, 1996. - P. 317 – 333. 7. Zeljko Vrba. User-mode program tracing and an appli- cation to encrypted execution engine. - http://www.core- dump.com.hr/documents/cryptexec.pdf. 8. Loureiro S., Molva R. Function Hiding Based on Error Correcting Codes // Proc. of Cryptec99 - Intern. Workshop on Cryptographic Techniques and Electronic Commerce, 1999. - P. 92 – 98. 9. Sander T., Tschudin C. On Software Protection via Function Hiding // Proc. of the Second Intern. Workshop on Information Hiding, 1998. — P. 111 – 123. 10. On the (Im)possibility of Obfuscating Programs / B. Barak, O. Goldreich, R. Impagliazzo et al // Proc. of the 21st Annual International Cryptology Conf. on Advances in Cryptology, 2001. - Vol. 2139. - P. 1 – 18. 11. License protection with a tamper-resistant token / C. Chong, B. Ren, J. Doumen et al // Proc. of 5th WISA, 2004. — P. 223 – 237. 12. Zhang X., Gupta R. Hiding Program Slices for Soft- ware Security // Proc. of the Intern. Symp. on Code gen- eration and optimization: feedback-directed and runtime optimization, 2003. - Vol. 37. - P. 325 – 336. 13. Blum M. Program Result Checking: A New Approach to Making Programs More Reliable // Proc. of the 20th Intern. Colloquium on Automata, Languages and Pro- gramming, 1993. - P. 1–14. 14. Spot-Checkers / F. Ergün, S. Kannan, S. Kumar et al // Proc. of the 30th Annual ACM Symp. on Theory of Com- puting, 1998. - P. 259 – 268. 15. Horne B., Matheson L., Sheehan C. Dynamic Self- Checking Techniques for Improved Tamper Resistance // Proc. of the ACM CCS-8 Workshop on Security and Pri- vacy in Digital Rights Management, 2001. - Vol. 2320. - P. 141 – 159. 16. Segurson A. Summary of Protecting Software Code by Guards. — www.cs.arizona.edu/~collberg/Teaching/620/2002/Papers/ Segurson1.ps. 17. Oblivious Hashing: A Stealthy Software Integrity Veri- fication Primitive / Y. Chen, R. Venkatesan, M. Cary et al // Proc. of the 5th Intern. Workshop on Information Hid- ing, 2002. - Vol. 2578. - P. 400 – 414. 18. Lee B., Kim K. Software Protection Using Public Key Infrastructure // Proc. of Symp. on Cryptography and In- formation Security, 1999. - P. 433 – 437. 19. Jakobsson M., Reiter M. Discouraging Software Piracy Using Software Aging // Proc. of the ACM CCS-8 Work- shop on Security and Privacy in Digital Rights Man- agement, 2002. - Vol. 2320. - P. 1 – 12. 20. Collberg C., Thomborson C. Watermarking, Tamper- proofing, and Obfuscation — Tools for Software Protec- tion // IEEE Transactions on Software Engineering, 2002. - Vol. 28. - N. 8. - P. 735 – 746. 21. Collberg C., Thomborson C. Software Watermarking: Models and Dynamic Embeddings // Proc. of 26th ACM SIGPLAN-SIGACT Symp. on Principles of Programming Languages, 1998. - P. 311 – 324. Получено 23.09.05 Об авторах Анисимов Анатолий Васильевич доктор физ.-мат. наук, профессор Место работы автора Киевский национальный университет им. Т. Шевченко, декан факультета киберне- тики 03680, Киев, просп. Академика Глушкова, 2, корп. 6 Тел.: (044) 259 0129 E-mail: deanoffice@unicyb.kiev.ua Иванов Иннокентий Юркевич аспирант, инженер-программист Место работы автора ТОВ “Софтпанорама плюс плюс”. 03187, Киев, ул. Академика Заболотного, 12, кв. 55. Тел.: (044) 522 2957 E-mail: innokentiy@gmail.com
id nasplib_isofts_kiev_ua-123456789-2336
institution Digital Library of Periodicals of National Academy of Sciences of Ukraine
issn 1727-4907
language Russian
last_indexed 2025-12-07T15:53:56Z
publishDate 2006
publisher Інститут програмних систем НАН України
record_format dspace
spelling Анисимов, А.В.
Иванов, И.Ю.
2008-09-17T15:03:57Z
2008-09-17T15:03:57Z
2006
Подходы к защите программного обеспечения от атак злонамеренного хоста / А.В. Анисимов, И.Ю. Иванов // Проблеми програмування. — 2006. — N 1. — С. 41-61. — Бібліогр.: 21 назв. — рос.
1727-4907
https://nasplib.isofts.kiev.ua/handle/123456789/2336
681.3:004.056
Рассматриваются существующие методы защиты ПО от действий злонамеренного хоста, для каждого метода дается оценка его стоимости и обеспечиваемой защиты. Приводится комбинированный метод защиты ПО, представляющий собой усовершенствование двух существующих методов: затемнения, основанного на непроницаемых предикатах, и защиты от внесения изменений в код на основе «забывчивого хэширования». Предложенный метод обеспечивает более высокий уровень защиты по сравнению с базовыми методами, а также является применимым для более широкого класса программ.
Розглядаються існуючі методи захисту ПЗ від дій злочинного хоста, для кожного методу надається оцінка його вартості і захисту, який він забезпечує. Наводиться комбінований метод захисту ПЗ, що являє собою удосконалення двох існуючих методів: затемнення, що базується на непроникливих предикатах, і захисту від внесення змін до коду на основі “забудькуватого хешування”. Запропонований метод забезпечує більш високий рівень захисту у порівнянні з базовими методами, а також може бути застосованим для більш широкого класу програм.
The article considers existing approaches to software protection against attacks caused by malicious hosts. For each approach its cost and provided protection level are considered. As a result of improvement of two existing methods (obfuscation based on opaque predicates and tampering protection based on oblivious hashing), new complex protection method is proposed. The suggested method guarantees higher protection level than the base methods, besides it can be used with wider class of programs.
ru
Інститут програмних систем НАН України
Захист інформації
Подходы к защите программного обеспечения от атак злонамеренного хоста
Підходи до захисту програмного забезпечення від атак з боку злочинного хоста
Approaches to protecting software from malicious host attacks
Article
published earlier
spellingShingle Подходы к защите программного обеспечения от атак злонамеренного хоста
Анисимов, А.В.
Иванов, И.Ю.
Захист інформації
title Подходы к защите программного обеспечения от атак злонамеренного хоста
title_alt Підходи до захисту програмного забезпечення від атак з боку злочинного хоста
Approaches to protecting software from malicious host attacks
title_full Подходы к защите программного обеспечения от атак злонамеренного хоста
title_fullStr Подходы к защите программного обеспечения от атак злонамеренного хоста
title_full_unstemmed Подходы к защите программного обеспечения от атак злонамеренного хоста
title_short Подходы к защите программного обеспечения от атак злонамеренного хоста
title_sort подходы к защите программного обеспечения от атак злонамеренного хоста
topic Захист інформації
topic_facet Захист інформації
url https://nasplib.isofts.kiev.ua/handle/123456789/2336
work_keys_str_mv AT anisimovav podhodykzaŝiteprogrammnogoobespečeniâotatakzlonamerennogohosta
AT ivanoviû podhodykzaŝiteprogrammnogoobespečeniâotatakzlonamerennogohosta
AT anisimovav pídhodidozahistuprogramnogozabezpečennâvídatakzbokuzločinnogohosta
AT ivanoviû pídhodidozahistuprogramnogozabezpečennâvídatakzbokuzločinnogohosta
AT anisimovav approachestoprotectingsoftwarefrommalicioushostattacks
AT ivanoviû approachestoprotectingsoftwarefrommalicioushostattacks