УДК 004.056.5

Обфускатор программного кода языка JavaScript

Авторы: Медгаус С.В., Чернышова А.В.

Источник: Информатика и кибернетика, № 4(6), – Донецк: ДонНТУ, 2016. с. 59 – 66. [ссылка на сборник]

Аннотация: В тексте данной статьи представлен краткий обзор существующих обфускаторов для языка JavaScript. После их анализа предложено реализовать собственный обфускатор, применяющий сложные преобразования исходного кода. В статье подробно описаны применяющиеся алгоритмы запутывания.
Ключевые слова: обфускатор, Java, JavaScript, запутывание, обфускация, алгоритмы обфускации, Esprima, Nashorn.

Введение

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

Подробный анализ существующих способов защиты web-приложений рассмотрен в [1]. Исходя из изложенного в статье материала, можно сказать, что обфускация – это наименее ресурсоёмкий способ защиты исходного кода web-приложений. Обфускация (от лат. obfuscare — затенять, затемнять; и англ. obfuscate — делать неочевидным, запутанным, сбивать с толку) или запутывание кода — приведение исходного текста или исполняемого кода программы к виду, сохраняющему его функциональность, но затрудняющему анализ и понимание алгоритмов работы. Существуют специальные программы, производящие обфускацию, так называемые обфускаторы [2].

На текущий момент существуют различные программные продукты, которые предлагают обфускацию исходного кода, однако эффективные алгоритмы обфускации предлагают только платные продукты, бесплатные же работают на уровне минификации кода (уменьшение размера исходного кода, путём сокращения имён переменных и функций, удаление символов форматирования кода). В работе [1] были рассмотрены и проанализированы такие полнофункциональные и эффективные программные продукты как YUI Compressor [3], Packer [4], JavaScript Obfuscator [5] и Google Closure Compiler [6]. После анализа существующих обфускаторов было принято решение разработать комбинацию эффективных обфусцирующих алгоритмов.

Целью работы является реализация алгоритмов обфускации для языка JavaScript с последующим объединением их в комплексный алгоритм обфусцирующего преобразования и использование его при создании обфускатора.

1 Проектирование обфускатора

После анализа существующих обфускаторов для языка JavaScript, была разработана диаграмма прецедентов для проектируемого обфускатора (см. рис. 1).

Диаграмма прецедентов UML – это диаграмма, показывающая связи между актёрами и действиями (прецедентами), которые они могут выполнять. Эта диаграмма позволяет точно описать полную функциональность программной системы [7].

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

  • удаление форматирования;
  • преобразование условных конструкций;
  • логическое преобразование;
  • сокращение констант;
  • кодирование числовых констант;
  • кодирование строковых констант;
  • переименование переменных.

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

UML диаграмма прецедентов
Рисунок 1 – UML диаграмма прецедентов

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

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

Диаграмма потоков данных работы обфускатора
Рисунок 2 – Диаграмма потоков данных работы обфускатора

Все остальные диаграммы, разработанные при проектировании обфускатора, представлены в [8].

2 Алгоритмы обфусцирующих преобразований

Эффективность программы-обфускатора зависит от её алгоритмов, которые преобразовывают (запутывают) исходный код программ [9].

2.1 Общий алгоритм работы обфускатора

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

Общий алгоритм работы обфускатора
Рисунок 3 – Общий алгоритм работы обфускатора

2.2 Общий алгоритм работы обфускатора

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

Алгоритм преобразования условных выражений
Рисунок 4 – Алгоритм преобразования условных выражений

2.3 Алгоритм логического преобразования

Данный алгоритм представляет собой простое инвертирование сравнений таким образом, чтобы вместо исходного оператора использовалось отрицание противоположного оператора с отрицаемыми операндами (см. рис. 5) [8]. На схеме алгоритма указана только часть логических операторов. Операторы, не попавшие на схему, преобразуются по такому же алгоритму.

Алгоритм преобразования логических выражений
Рисунок 5 – Алгоритм преобразования логических выражений

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

2.4 Алгоритм сокращения констант

По стандарту ECMA Script 5.0 в языке JavaScript ещё нет констант, которые бы обеспечивал сам язык, поэтому программа сама анализирует переменные, которые не изменяют своего значения в исходном коде пользовательской программы.

Данное преобразование состоит из следующих этапов:

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

Детальное описание алгоритма представлено на рис. 6.

Алгоритм сокращения констант
Рисунок 6 – Алгоритм сокращения констант

2.5 Алгоритм кодирования чисел

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

Суть этого алгоритма заключается в том, чтобы заменить числа некоторым набором арифметических выражений, и после этого представить все числа в 16-ичной системе счисления [8]. Детальный алгоритм представлен на рис. 7.

Алгоритм кодирования чисел
Рисунок 7 – Алгоритм кодирования чисел

2.6 Алгоритм кодирования строк

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

В данном преобразовании строки представляются в виде конкатенации вызовов различных функций, а также в виде кодировки BASE64. Все разбиение строковых констант происходит каждый раз случайно, обеспечивая различный исходный код на выходе обфускатора [9].

Детальный алгоритм представлен на рис. 8.

Алгоритм кодирования строковых констант
Рисунок 8 – Алгоритм кодирования строковых констант

2.7 Алгоритм переименования переменных

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

Главная особенность этого алгоритма в том, чтобы представить имена переменных и функций в виде непонятного для человека набора символов [9]. Подробный разбор алгоритма представлен на рис. 9.

Алгоритм переименования переменных
Рисунок 9 – Алгоритм переименования переменных

2.8 Алгоритм перемешивания функций

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

Алгоритм перемешивания функций
Рисунок 10 – Алгоритм перемешивания функций

3 Результат разработки программы-обфускатора

После разработки алгоритмов, они были реализованы на языке Java, где и были объединены в один главный обфусцирующий алгоритм.

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

Как показало тестирование на экспертных группах, код стал более запутанным. Эксперты сделали субъективное заключение – сложность понимания исходного кода увеличилась в 3 раза.

Экранная форма обфускатора
Рисунок 11 – Экранная форма обфускатора

Выводы

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

Также была разработана последовательность объединения запутывающих преобразований (общий алгоритм работы обфускатора) с максимальной степенью запутанности.

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

Литература

  1. Обзор существующих обфускаторов и их алгоритмов. Компьютерная и программная инженерия – 2015 год: – Донецк: ДонНТУ, 2015. – С.117 – 119.
  2. Википедия. Обфускация [электронный ресурс]. – Режим доступа: https://ru.wikipedia.org/wiki/Обфускация
  3. YUI Compressor [электронный ресурс]. – Режим доступа: https://github.com/yui/yuicompressor
  4. Packer [электронный ресурс]. – Режим доступа: http://dean.edwards.name/packer
  5. JavaScriptObfuscator. Canada [электронный ресурс]. – Режим доступа: https://www.javascriptobfuscator.com/
  6. Google Closure Compiler [электронный ресурс]. – Режим доступа: https://developers.google.com/closure/compiler/
  7. Диаграммы прецедентов: крупным планом [электронный ресурс]. – Режим доступа: http://www.intuit.ru/studies/courses/1007/ 229/lecture/5962
  8. Проектирование обфускатора для языка JavaScript. ИУСМКМ – 2016: VII Международная научно-техническая конференция, 26 мая 2016: – Донецк: ДонНТУ, 2016. – С. 167 – 173.
  9. LynX. Обфускация и защита программных продуктов // CITForum. [электронный ресурс]. – Режим доступа: http://citforum.ru/security/articles/obfus/
  10. Java Obfuscator – String Encryption // zelix.com [электронный ресурс]. – Режим доступа: http://www.zelix.com/klassmaster/featuresStringEncryption.html