В данной публикации обсуждаются вопросы защиты .Net продуктов, в частности
путем обфускации (запутывания)
Защита Netпродуктов от подглядывания и воспроизведения.
Один из выходов – обфускация. А может быть единственный.
Ver 1.0 beta.
Просьба извинить за собственную интерпретацию некоторых терминов из области
.Net и необьяснение некоторых других терминов – уйдет
слишком много времени, за неформальный стиль, да и статья предназначена тем, кто
уже с этими вещами знаком. Названия продуктов в этой статье сознательно не
упоминаются.
Что такое
обфускация?
Для
того чтобы реально защитить .Net продукт от дизасcемблирования надо
писать свой компилятор и свой линковщик(linker). Вот к таким
выводам можно прийти после исследования всех возможностей и невозможностей
.Net Framework.
Начну с того, что формат PE- файла .Net сборок
(dll, exe, .module) обозван как
COFF-совместимый формат, т.е. вкратце имеющий ту же структуру что и обычные
PE- файлы. Но в отличии от обычных PE- файлов
.Net сборка (assembly - как теперь принято называть
бывшие exe, dll, application,
library) содержит все
интересные данные – metadata, метаданные.
Метаданные несут своеобразную роль – они описывают содержание сборки, т.е содержат структуру сборки, ее
атрибуты, ресурсы, типы, методы и пр. т.е содержат практически все что нужно для
ее работы. Но и то что нужно для ее исследования. К чему веду?
К тому что ваш продукт (неважно на каком языке он написан) я могу как минимум дизассемблировать – утилитка
к .Net Framework прилагается –
ildasm.exe.
Я могу даже изменить некоторые данные в полученном il-файле и
собрать ее под новым названием, подписав своим файлом ключа (strong name
sign), снять защиту, которую найти не трудно, даже если она
находится в не в .Net сборке. Т.е хакнуть сборку легко.
Пожалуй могу сказать что хакнуть managed C++ сборку будет
немного труднее так как компилятор VC++ пихает в .Net сборку
embedded native code и вообще много чего туда
пихает, добавлят в
permissions сборки SkipVerification - и выдает сборку которую
PeVerify, утилитка, входящая в .Net Framework SDK
(далеее - .Net) «закрывает глаза»
на некторые нарушения целостности сборки.
Microsoft ( далее MS) предлагает также утилитку
ngen, которая должна по идее вашу сборку превратить native
code сборку, т.е представленную в машинном коде. Не всегда вы получите
нужный результат, а мой опыт ее использования показывает что результат не
поддается прогнозу – ваша сборка после этой обработки может не работать. Могу
добавить, что опыт небольшой ;). Но пусть мне возразят – мне будет
интересно.
Возвращаясь к проблеме. В любом случае ваш код можно просмотреть в более
удобном чем ассемблерный код виде, а если воспользоваться Anakrino
– то даже посмотреть в очень удобном виде. Это я насчет подглядывания, поиска на
вопрос – как же это он (создатель некой суперпроги) умудрился что-то
сделать.
Т.е что у вас может быть для исследования чужого продукта – структура
классов, их реализация (методы и поля), различные ресурсы – графика, тексты,
видео и пр.
Т.е код можно получить без всякого open source. Дело за малым –
им воспользоваться, целым ли, частями ли, неважно. Труд создателя используется
на халяву. Нехорошо - скажут создатели, хорошо – скажут многие ;).
Так как же защитить il-managed code от просмотра и
неавторизованного воспроизведения (когда чужой продукт выдается за свой), взлома
защиты (который вообще без native code не рализовать)?
Спецам известно что идеальной защиты не существует. Человеку, задавшемуся
целью взломать чужую прогу нужно только время. Т.е у него есть
timeout. Надо сделать защиту крайне тяжелой для взлома, чтобы
время, потраченное на взлом/исследование продукта было адекватно долгим
(настолько чтобы взломщик посчитал это дело неразумной тратой времени).
Что тут могут предложить спецы из области защиты .Net
сборок:
- Обфускация продукта
- Превращение кода в
native при установке (говорят это возможно :)) продукта.
- Написать свою
CorExeMain/CorDllMain – т.е писать свой
загрузчик(пока не видел)
Выбор небогат.
Насчет
последних двух сложновато, да и требования к безопасности сборок таковы, что
вариант 3. может не пройти, либо быть очень индивидульным. Вариант 2 – мне дал
результат – моя простенькая прога оказалась после ngen запихнутой в
GAC и «is not a valid Win32 Application»
- непригодной для запуска.
Небольшое
лирическое отступление:
Вообще
многие утилитки .Net типа al.exe,
ngen.exe выполняют функцию зря
потраченного времени. Зато названия какие –
Assembly Linker, Native Image Generator, блин…
Осталась
обфускация…
Обфускация.
Обфускация – это
затруднение, запутывание исследователя в его «неблагородном» деле анализа чужих программ. На деле это дело
выглядит так – вы, исследуя чужой код мало что понимаете, и ни за что не можете
зацепиться глазом – сплошные, ничего не говорящие нолики, единицы, или
бессмысленные сочетания символов. Гораздо труднее понять взимодействие классов,
вызовы методов необходимо анализировать гораздо более внимательнее (после
обфускации в классе могут быть все методы названные как «0», в этом случае их
разделять можно по ReturnType и списку параметров и их типов,
причем параметры после обфускации могут быть также обезличены).
Вот
вам два примера – до и после обфускации.
Вроде
бы все хорошо – все запутано, и некий взломщик или любитель халявы не покусится
на ваш продукт. Тогда вам к началу это раздела. На все нужно время и
желание.
Обфускация
может крайне затруднить исследование вашей сборки (даже отбить такое желание),
может затрудненить ее воспроизведение. Но во первых надо понимать что происходит
при обфускации и как сделать так чтобы обфускация была наиболее качественной но
не мешала сборке работать корректно.
Возможности Reflection просто поражают удобством и
возможностями– вы можете знать структуру класса вплоть до private
полей, и при желании узнать их содержание и даже изменить. Можно скопировать
структуру чужой сборки, вплоть до тел методов, пусть они даже и приватны. Т.е
возможностей для воспроизведения чужих продуктов – хоть отбавляй. Конечно надо
будет попотеть, но это не сверхсложно.
Обфускаторы.
Net обфускаторы бывают нескольких видов. Имеется в виду подход обфускатора –
каким образом он анализирует метаданные и генерит.
- Парсеры – эти проги
парсят сгенерированный ildasm-ом il-код. В ходе
парсинга производят анализ типов, методов, полей и прочих членов сборки,
изменяют их и производят новый il-код. Потом, при помощи
ilasm этот il-код превращают обратно в сборку.
Недостактов у этих систем много – низкая скорость, зависимость от
ilasm, ildasm (ручками сделанный il-код может не
пропарситься), невозможность работы со сборками с embedded native code.
- Более продвинутый
вид – обфускаторы, «знакомые» с форматом .Net PE-файла и форматом
метаданных в нем. Эти продукты анализируют таблицы метаданных (фактически
метаданные – это небольшая реляционная база данных). В этом случае можно
надеяться на более качественный результат, база данных – понятие более
дисциплинированное. О структуре
метаданных можно почитать в документации, идет с .Net Framework
SDK ("…\FrameworkSDK\Tool Developers Guide\docs\"). Эти продукты между
собой подразделяются на продукты, которые вручную анализируют метаданные и
продукты, которые пользуют некоторые интерфейсы, предоставляемые
MSCOREE.DLL,
MSCORPE.DLL
- таковых большинство, поэтому большинство обфусктаоров написано на
VC, так как в C# работать с
IMetadataDispenserEx
интерфейсом трудновато – все эти интерфейсы надо ручками перенести в
C# и работать с непривычными для C# указателями.
Некоторые даже обьявляют о возможно сделать тело методов ваших сборок после
обфускации нечитаемыми в ildasm. Но ildasm – не самый
навороченный инструмент в деле дизассемблирования, уже есть и более
продвинутые (Reflector от Lutz Roeder, например).
- Компилятор и
линковщик – это высший уровень, пока никем не занятый, хотя ходят слухи… Хотя
если говорить о компиляторе Il-кода – это ilasm то
подобного рода компиляторы не поддерживают embedded native code,
ildasm не дизассемблирует тело метода если он embedded
native, и, соответственно ilasm – некорректно соберет
сборку. Так что если вы пишете свой продукт на managed C++ -
вариант 1 и 3 вам для обфускации не подойдут, так как ваша сборка может быть
некорректно разобрана и собрана (ваш продукт может содержать подобный код).
Правда и в варианте 2 некоторые продукты не смогут вам помочь с embedded
native code – но они то хотя бы оставят его в покое и в теле сборки.
Вариант 3 может помочь в том, чем
пока не может помочь al.exe – обьединении нескольких
сборок или модулей в одну (merge), экстракции модулей или ряда
классов из сборки и генерировании новой сборки из них. Средства в
.Net для этого есть и это возможно.
- Есть также класс
программ, который нельзя назвать обфускаторами, но они служат делу защиты
сборок. Подобные проги защищают только EXE-вид сборок. Т.е если
вы распространяете DLL- сборки (компоненты, сервисы) – это для
вас не подойдет. Суть таких инструментов проста. Программа формирует
exe- сборку, куда включает в виде managed resource
зашифрованные сборки и ресурсы. При запуске прога расшифровывает сборки,
загружает в свой домен (AppDomain) и передает управление «родной»
Main функции. Также шифруются и обфусцируются ресурсы сборок.
Есть и другой сценарий, более продвинутый – прога может забрать
TypeResolve, AssemblyResolve,
ResourceResolve события, генерируемые доменом при возникающих
затруднениях с типами, сборками, ресурсами, и переназначать их. При
затребовании необходимого, прога динамически необходимое подгружает.
Разумеется такая защита более мощная, как кажется, но если не «спрятать»
данные для шифрации и таблицу соответствия сборок, ресурсов их переименованным
аналогам то взлом – дело пустяшное. А это головная боль для авторов таких
прог. Дизассемблируем сборку, находим механизм шифрации, таблицу соответствия,
переименовываем и расшифровываем. Недостаток этих прог – в скорости
инициализации, могут возникнуть проблемы с безопасностью, некоторые виды
сборок, например managed c++ сборки не смогут быть загружены
Assembly.Load(byte[]) путем (не знаю
почему, не копал так глубоко, сборка c reference к
mscorlib и
Microsoft.VisualC.dll, с внешними
сборками, проблема закопана в глубинах Load(byte[]),точнее в
используемой внутри нее native nLoadImage()), т.е из оперативной
памяти, а выгружать их на диск – это только на пользу любопытным. Хотя можно
комбинировать обфускацию с этим методом.
Итого
К
чему я это все написал? Существует проблема – сборки в .Net не
защищены от несанкционированного исследования, дизассемблирования и взлом их
проще чем native сборок. Перед разработчиками стоит непростой выбор
– как же защищать свои продукты от этих напастей. Какое средство защиты выбрать,
какое из них наиболее подходящее? Вот я в меру сил и рассказал об этой проблеме
и о том, какие виды прог в этой сфере предлагаются, как они работают, и осветил
некоторые проблемы их использования. Да и времени было затрачено на анализ всех
этих средств, их возможности и принцип их работы, так что делюсь опытом.
Проблемы на этом не кончаются. В следующий раз
расскажу об обфускаторах, вариант 2, как наиболее подходящем средстве для защиты
.Net сборок. Но не идеальном. Было бы идеальное средство – не было
бы этой статьи :).
Небольшое лирическое отступление:
Вообще многие утилитки .Net типа
al.exe, ngen.exe
выполняют функцию зря потраченного времени. Зато
названия какие – Assembly Linker, Native Image
Generator, блин…