обфускация исходного кода c

Защищаем приложение, написанное на C#

Периодически мне пишут с вопросом о том, как защитить свою программу, написанную на дотнетовской платформе. Что и говорить, но вопрос защиты интеллектуальной собственности возникает у программистов всегда.

К сожалению, дотнетовские программы, равно как джавовские, довольно-таки просто «вскрыть». Виной всему байт-код, позволяющий специальными программами легко дизассемблировать и воссоздать первоначальный код. Далеко ходить не нужно за примерами, так как даже VS поставляется с дизассемблером ILDasm.

обфускация исходного кода c

На самом деле, эту тему можно копнуть на страниц 20 и все равно она получится достаточно поверхностной. Поэтому давайте будем отталкиваться от инструментария, которыми пользуются крэкеры и программисты. Хотя это и будет скорее обзорная статья, тем не менее, постараюсь давать именно полезную информацию.

Методы защиты C# приложения

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

Да, серьезные продукты привязывают к электронным ключам, вставляемым в ПК, но у инди-разработчиков подобных возможностей просто нет, да и даже такие решения все же можно эмулировать, хотя и довольно-таки сложно.

обфускация исходного кода c

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

Для отображения декомпилированного кода dotPeek использует большинство функций, привычных пользователям Microsoft Visual Studio: открытие декомпилированных файлов в отдельных вкладках, подсветка синтаксиса, сворачивание блоков кода, нумерация строк и многое другое.

Если вы хотите не только исследовать сборку, но и внести в нее изменения, dotPeek позволит вам преобразовать и сохранить ее в проект Microsoft Visual Studio и, таким образом, продолжить работу уже с исходным кодом сборки.

Занятная тулза, не так ли? Но самое главное – dotPeek абсолютно бесплатный, в отличие от «дедушки» .NET Reflector, который раньше был едва ли не стандартом де-факто для декомпиляции дотнетовских приложений. Так как вам все равно нужно будет смотреть то, насколько хорошо вы защитили свое приложение, то dotPeek должен быть у вас всегда. Скачать его можно на сайте https://jetbrains.ru/products/dotpeek/

Естественно, что когда все знают о том, что программу на C# может «взломать» любой, способный крякнуть игру, начинается шевеление и волнение. В итоге, нужно устранить очевидные проблемы в защите.

обфускация исходного кода c

Еще один обфускатор, который лично мне нравится – это пресловутый ConfuserEx. Он опенсорсный и дает весьма сильную защиту. Например, можете увидеть то, как он «путает» код и не дает прочитать его с помощью еще одного исследователя ILSpy.

обфускация исходного кода c

обфускация исходного кода c

Скачать его можете на https://yck1509.github.io/ConfuserEx/. Тем не менее, его можно все же снять небезызвестным De4dot, при условии, что реверсер его правильно определит, имейте это в виду.

2 ) Крипторы и пакеры. На самом деле, хотя и дают сильную защиту, но большинство проектов попросту брошены или они стоят безумно больших денег. Поэтому на них я останавливаться не стану.

3) Новые студии разработки (начиная с 2015) меня больше радуют. Майкрософт разработала технологию .NET Native, позволяющую скомпилировать дотнетовское приложение в нативный код.

обфускация исходного кода c

Резюме

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

C# приложения были изначально уязвимы для реверсинга. В этом плане, они не сильно далеко ушли даже от того же JS, хотя там вообще все очевидно. Обфускация запутает код и инструменты типа dotPeek или ILSpy. Но важно понимать, что все они обрасли дополнительными плагинами, которые позволяют определить защиту и всячески ее снять. Тем не менее, пренебрегать ими не стоит.

Если вы точно знаете, что приложение будет написано только для майкрософтовской платформы, я бы все-таки порекомендовал компилировать его в нативный код с помощью .NET Native.

Тем не менее, не забывайте, что любую программу всегда можно взломать. Вопрос стоит только в том, кому это понадобится.

Источник

Обфускация как метод защиты программного обеспечения

обфускация исходного кода c

Или то, почему вы не можете издать свою улучшенную версию Counter Strike и уехать жить на Гавайи.

О чём речь?

обфускация исходного кода cКрасивый пример из Википедии кода, прошедшего обфускацию.

Далее в программе

Как это должно работать?

Состояние дел сейчас

Зачем это нужно?

Так же, алгоритмы обфускации активно используются не только для затруднения анализа кода, но и для уменьшения размера программного кода, что, в свою очередь, активно используется при разработке различных веб-сервисов и баз данных.

Как это должно работать?

Как понятно из вышесказанного, методы обфускации должны усложнить код, преобразовав его таким образом, чтобы скрыть от третьих лиц логику его работы.

Как это работает

Большинство методов обфускации преобразуют следующие аспектов кода:

• Данные: делают элементы кода похожими на то, чем они не являются

• Поток кода: выставляют исполняемую логику программы абсурдной или даже недетерминированной

• Структура формата: применяют различное форматирование данных, переименование идентификаторов, удаление комментариев кода и т.д.

Инструменты обфускации могут работать как с source или байт кодом, так и с бинарным, однако обфускация двоичных файлов сложнее, и должна варьироваться в зависимости от архитектуры системы.

При обфускации кода, важно правильно оценить, какие части когда можно эффективно запутать. Следует избегать обфускации кода, критичного относительно производительности.

Методы

обфускация исходного кода c

1. Преобразование данных

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

По ссылке можно ознакомится с интересными примерами использования двоичной формы записи чисел для усложнения читабельности кода, а так же изменений формы хранения данных и замены значений различными тождественными им выражениями.

2. Обфускация потока управления кодом

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

3. Обфускация адресов

Данный метод изменяет структура хранения данных, так чтобы усложнить их использование. Например алгоритм, может выбирать случайными адреса данных в памяти, а также относительные расстояния между различными элементами данных. Данный подход примечателен тем, что даже если злоумышленник и сможет «декодировать» данные, используемые приложением на каком-то конкретном устройстве, то на других устройствах он всё равно не сможет воспроизвести свой успех.

4. Регулярное обновление кода

Этот метод предотвращает атаки, регулярно выпуская обновления обфусцированного программного обеспечения. Своевременные замены частей существующего программного обеспечения новыми обфусцированными экземплярами, могут вынудить злоумышленника отказаться от существующего результата обратного анализа, так как усилия по взлому кода в таком случае могут превысить получаемую от этого ценность.

5. Обфускация инструкций ассемблера

Преобразование и изменение ассемблерного когда также может затруднить процесс обратного инжиниринга. Одним из таких методов является использование перекрывающихся инструкций (jump-in-a-middle), в результате чего дизассемблер может произвести неправильный вывод. Ассемблерный код также может быть усилен против проникновения за счёт включения бесполезных управляющих операторов и прочего мусорного кода.

6. Обфускация отладочной информации

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

Заключение

Я не стал описывать историю развития различных подходов к обфускации, так как на мой взгляд, она неплохо отражена в уже существующей на Хабре статье.

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

В заключение, хотел бы добавить, что при использовании методов обфускации не следует пренебрегать и прочими методами защиты вашего кода, ведь обфускация далеко не серебряная пуля в вопросе защиты программ от взлома.

обфускация исходного кода c

Ссылки и источники

[3] Barak B., Goldreich O., Impagliazzo R., Rudich S., Sahai A., Vadhan S. and Yang K. «On the (im) possibility of obfuscating programs.» CRYPTO 2001.

Источник

Обфускация строк C++ в Visual Studio

Бинарная защита своих программ — дело часто нелёгкое и неблагодарное, ведь если продукт кому-то нужен, его всё равно сломают, как ни старайся. При этом самая лучшая защита всегда должна писаться, ну или по крайней мере настраиваться, вручную, а всякие там пакеры/кодировщики/виртуальные машины тоже конечно помогают, но чем более автоматически работает защита, тем легче она потом ломается, к тому же если использовать какой-то известный пакер, то кракеры его уже 10 раз ломали в других продуктах, и знают что в нём к чему. К тому же все более-менее удачные пакеты защит стоят немалых денег.

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

Вот и встаёт вопрос в том чтобы найти компромис между уровнем защиты/затраченным временем/удобочитаемостью кода/стоимостью и т. д.

Хочу поделиться с вами моим собственным решением для обфускации строк в программе, которое хоть и даёт лишь минимальную защиту, но является 1) бесплатным 2) лёгким 3) почти не портящим внешний вид кода 4) новым, которое кракер скорее всего в данной конкретной конфигурации ещё не видел.

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

Обфускация строк — зачем это нужно

Требования

Мои требования были достаточно простыми — обфускация должна проводиться автоматически, минимально изменять мой код, и строк не должно оставаться в читаемом виде, ни в бинарнике, ни в образе программы в памяти.

Многие решения просто кодируют строки в бинарнике, а при старте программы проходят по всем строкам и декодируют их. Это контр-продуктивно, ведь из образа строки читаются также легко как и из файла — даже тем же Process Explorer. (Который вообще даже не кракерский инструмент а просто заменитель taskmgr.)

Скрипт для непосредственно кодирования строк я написал в php, так что вам понадобится и сам php и путь к php.exe в PATH.

Итак, преступим к делу.
Я использовал Visual Studio 2008, но всё будет работать примерно точно также и в других версиях Visual Studio. В других компиляторах будет даже легче, учитывая некоторые странности VS, о которых чуть позже.

Для начала — заголовок обфускации, obfuscator.h
Его нужно будет подключить к каждому файлу в котором будет использоваться обфускация.

Как вы видите, я использую макроc X(), в который будет оборачиваться каждая обфуцированная строка. Поскольку название состоит из всего лишь одной буквы (для минимального влияния на защищяемый код), и есть шанс что в другом месте проекта (или в чужой библиотеке) такой же макрос тоже объявлен, я добавил сообщение которое в таком случае покажется в окне лога билда.

Если DO_OBFUSCATE_STRINGS определён, то строка заменяется на X(s)obDecodeStr(OBPREPROCESSENCODEDSTR(s))
«OBPREPROCESSENCODEDSTR» — это всего лишь токен, который будет потом искать мой скрипт кодирующий строки. Название специально сделано длинным чтобы исключить что это сочетание букв встретится где-то ещё в проекте. Итак, сам скрипт:

Как вы видите, скрипт читает имя файла из командной строки и ищет наши токены, «OBPREPROCESSENCODEDSTR». Найдя такой токен, строка «кодируется».

Алгоритм кодирования конечно не самый стойкий, но его вы можете легко изменить под себя, если считаете что это добавит больше защиты. Здесь он приведён просто как рабочий пример.

Положите этот файл в корневую директорию вашего проекта и назовите obfuscate-i.php

Ну и наконец, файл содержащий функцию декодирования

Что интересно в этой функции это что она не декодирует строку в том месте где она расположена, а декодирует её в специальный буфер, адрес которого уже возвращает. Это приводит к тому что все строки никогда не будут в декодированном состоянии в образе программы. Декодированными будут всего лишь 4 из них, те которые были декодированы последними.

__forceinline использован в качестве попытки предотвратить атакующего просто поставить брейкпойнт в самой функции obDecodeStr и магическим образом получить все наши строки.

Почему буферов 4 а не 1?

Представьте себе вызов MessageBox(0, X(“Some value”), X(“some another value”), MB_OK)
Если бы не обфускация, то в функцию MessageBox попали бы просто адреса строк и всё было бы нормально. Но когда обфускация активирована, этот вызов превращается в MessageBox(0, obDecodeStr(“Some value”), obDecodeStr(“some another value”), MB_OK), и оба вызова obDecodeStr выполняются *перед* тем как выполняется непосредственно MessageBox. И если бы использовался всего один буфер, то второй вызов obDecodeStr просто переписал бы первоначальную строку, и в оба аргумента функции попало бы одно и тоже:MessageBox(0, “some another value”, “some another value”, MB_OK).

Поэтому 4 буфера вместо 1. Если вы используете функции которые принимают больше чем 4 обфусцированных char* параметра сразу, то вам придётся увеличить количество буферов.

Конфигурация проекта

Итак, как это всё сконфигурировать автоматически?
(Все опции пишу на английском, я думаю разберётесь если даже у вас русская студия)

Теперь достаточно обернуть каждую строку обернуть в X(), подключить заголовок обфускации и ваши строки будут защищены.

При обычной разработке используйте старые конфигурации Release/Debug, которые должны остаться неизменными, а когда придёт время выпускать бинарник, постройте сначала конфигурацию Release-obfuscated-prestep (которая не построится до конца), и наконец Release-obfuscated, которая и сгенерирует «защищённый» бинарник.

Источник

(Это продолжение обзора обфускаторов. Начало тут)

1. Обфускаторы.

1.1. Методики

Объединение сборок и пространств имён (Assembly Merge, Namespace Flatten)
Данная методика сама по себе не задерживает злоумышленника ни на минуту, но очень полезна для дальнейшего его запутывания. Т.к. чем больше классов будет содержать результирующая сборка, тем сложнее без детального анализа будет в ней найти то, что надо.
Опять же, при попытке украсть ваш код, злоумышленник получит вместо нескольких проектов-библиотек и одной программы только один проект, в котором все классы будут лежать в одной папке (и в одном неймспейсе).

Для объединения сборок можно использовать утилиту ilmerge, либо встроенную в обфускатор функциональность. Пространства имён обычно объединяются во время обфускации имён классов (чтобы не было коллизий с одинаково названными классами из разных неймспейсов).

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

Обфускация control flow
На этом этапе меняется порядок инструкций в коде и даже меняются сами инструкции. Пожалуй, самый интересный и самый спорный этап.
Данная методика позволяет ввести в заблуждение (а иногда и в полный ступор) большинство декомпиляторов языков высокого уровня. Что очень хорошо противодействует «воровству» кода. Также «запутывает» кракеров и авторов кейгенов.
Обратная сторона медали — иногда сниженная производительность. Логично что чем больше мы запутываем ход выполнения программы, тем дольше она выполняется. Особенно это относится к использованию исключений.
В большинстве случаев код метода бьётся на блоки, эти блоки перемешиваются в случайном порядке и «склеиваются» с помощью безусловных переходов (инструкции br и br.s). В качестве примера:

Бывают и случаи, когда метод очень короткий, и «перемешать» его хорошо не получается, в этом случае некоторые обфускаторы выдают переход на следующую инструкцию:

Между инструкцией перехода, и её целью очень часто вставляется всякое «фуфло», типа выпадения в дебаггер, или просто невалидных инструкций:

Некоторые обфускаторы заменяют инструкции перехода (как оригинальные, так и вставленные) на загрузку константы и переход на switch:

L_0000: br.s L_0023
L_0002: ldloc num3
L_0006: switch (L_005b, L_0068, L_00ce, L_00af, L_0047, L_007b)
.
.
.
L_003c: ldc.i4 4
L_0041: stloc num3
L_0045: br.s L_0002

очевидно, что в данном примере инструкция со смещением L_0045 «в девичестве» была br L_0047, а если учесть предыдущие методики, то это вообще nop 😉

Иногда можно встретить «переход на переход»:
обфускация исходного кода c
в одной из программ я видел цепочку из 6 (шести) таких переходов 😉

Интересный подход — использование условных переходов для выражений, которые всегда верны (или неверны).
Самый простой пример:

То же самое, но слегка более запутанное:

L_0014: ldc.i4.1
L_0015: stloc.0
L_0016: br.s L_001c
L_0018: nop
L_0019: ldarg.1
L_001a: br.s L_002e
L_001c: ldloc.0
L_001d: brtrue.s L_0018

L_0000: ldc.i4.5
L_0001: dup
L_0002: dup
L_0003: ldc.i4.6
L_0004: sub
L_0005: blt L_0001

Простое перемешивание некоторых инструкций, например:

L_0000: ldc.i4 4
L_0005: stloc num
L_0009: ldstr «\u5f03»
L_000e: ldloc num

компилятор иногда может выдавать код вида stloc X, ldloc X, когда требуется записать значение в локальную переменную, но не убирать его со стэка. В случае обфускаторов, эта переменная (num) добавлена искусственно, и нигде кроме данных двух инструкций больше не используется.

Один из самых «жёстких» методов — всегда выбрасываемое внутри блока try—catch исключение. Данный подход используется очень редко, т.к. резко снижает производительность и может нарушить логику приложения при неверном использовании. Скриншот я не привожу т. к. он занимает много места.

Кажется, что самые популярные методики я перечислил, если вы знаете что-то ещё, сообщите пожалуйста в комментах.

Invalid IL
Тут всё очень просто. В участки кода, которые никогда не будут исполнены, вставляются не описанные в стандарте опкоды (т.е. невалидные инструкции).
В рефлекторе вы увидите примерно такое:
обфускация исходного кода c
или, если переключится на IL:
обфускация исходного кода c
Данная методика обескураживает начинающих «хаксоров». Но не является чем-то сложным для обхода (данные опкоды просто заменяются на nop).

Сокрытие строк
Деобфускаторами это называется «шифрование строк», но назвать это шифрованием у меня не поворачивается язык.
Обычно это делается каким-нибудь «детским» алгоритмом шифрования типа XOR на константу:

public static string Decode( string str, int num)
<
int length = str.Length;
char [] chArray = str.ToCharArray();
while (—length >= 0)
chArray[length] = ( char )(chArray[length] ^ num);
return new string (chArray);
>

Иногда строки объединяются в одну, и потом происходит вызов метода Substring; иногда строки прячут в ресурсы.

В любом случае «шифрование» представлено в виде статического метода с несколькими аргументами, обычно это строка и/или число. Никаких криптографических алгоритмов не применяется, что вполне логично: если применить здесь настоящее шифрование, то программа будет безбожно тормозить.
Данный метод спасает от начинающих кракеров, которые будут искать по коду строки типа “Invalid serial number” или другие тексты сообщений.

Специфичные атрибуты и баги декомпиляторов
Самый часто встречаемый атрибут — [SuppressIldasm], который «вежливо просит» не работать на данной сборке официальный декомпилятор Microsoft — ildasm. Существуют также специфичные атрибуты для рефлектора и для коммерческих декомпиляторов.

В качестве багов можно встретить как чисто технические недоработки декомпиляторов (например, рефлектор выпадает на инструкции ldfld string 儽.凍::儽, а большинство деобфускаторов на базе Mono.Cecil — на неправильных RVA), также и алгоритмичиские допущения: многие декомпиляторы высокого уровня отслеживают состояние стэка, но идут по методу не как по графу, а линейно, и радостно валятся на методах в которых после последней инструкции ret вставлен бесконечный цикл. Против плагина Reflexil хорошо «помогает» инструкция, переходящая сама на себя.

Спасибо пользователю Exaktus за комментарии и дополнения
Далее будет часть 1.2. Обзор обфускаторов
продолжение следует.
* All source code was highlighted with MSVS and wordpad 😉

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *