УДК 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 с последующим объединением их в комплексный алгоритм обфусцирующего преобразования и использование его при создании обфускатора.
После анализа существующих обфускаторов для языка JavaScript, была разработана диаграмма прецедентов для проектируемого обфускатора (см. рис. 1).
Диаграмма прецедентов UML – это диаграмма, показывающая связи между актёрами и действиями (прецедентами), которые они могут выполнять. Эта диаграмма позволяет точно описать полную функциональность программной системы [7].
Как видно из рис. 1, пользователь может загружать файл с исходным кодом, сохранять обфусцированный код в файл, запускать обфускацию, а также выбирать режимы обфускации:
Все вышеперечисленные режимы обфускации применяются в заданном программой порядке, который невозможно изменить, то есть являются этапами. Это позволяет максимально эффективно запутать исходный код, так как появляются связи ещё и между самими режимами обфускации.
Исходя из спроектированной диаграммы прецедентов, была разработана диаграмма потоков данных обфускатора, которая представлена на рис. 2.
Приложение будет состоять из одной формы, в которой будет содержаться главный рабочий класс Обфускатор, который будет выполнять всю работу. В свою очередь, в зависимости от выбранных режимов, обфускатор будет использовать различные запутыватели, которые предоставляет программа. После обфускации программа будет выдавать выходной файл, который будет содержать обфусцированный JavaScript код.
Все остальные диаграммы, разработанные при проектировании обфускатора, представлены в [8].
Эффективность программы-обфускатора зависит от её алгоритмов, которые преобразовывают (запутывают) исходный код программ [9].
Необходимо отметить, что основным объединяющим алгоритмом программы является алгоритм, в котором объединяются все запутывающие алгоритмы и используются для приведения исходного кода в непонятный для человека вид (см. рис. 3).
Суть этого преобразования заключается в том, чтобы преобразовать допустимые условные выражения if-else в тернарный оператор. Этот алгоритм нацелен на ухудшение читаемости кода, без каких-либо фундаментальных преобразований. Алгоритм преобразования на псевдоязыке представлен на рис. 4.
Данный алгоритм представляет собой простое инвертирование сравнений таким образом, чтобы вместо исходного оператора использовалось отрицание противоположного оператора с отрицаемыми операндами (см. рис. 5) [8]. На схеме алгоритма указана только часть логических операторов. Операторы, не попавшие на схему, преобразуются по такому же алгоритму.
Необходимо отметить, что можно использовать и другие логические преобразования, применяя логические эквиваленции, однако в JavaScript результатом логического выражения может быть не только true или false, но и ещё и объект, поэтому использование более сложных формул преобразований может приводить к ошибкам.
По стандарту ECMA Script 5.0 в языке JavaScript ещё нет констант, которые бы обеспечивал сам язык, поэтому программа сама анализирует переменные, которые не изменяют своего значения в исходном коде пользовательской программы.
Данное преобразование состоит из следующих этапов:
Детальное описание алгоритма представлено на рис. 6.
Данный алгоритм очень важен для программной системы, так как большая часть исходного кода в любом языке программирования использует огромное количество чисел для счётчиков различного рода, для циклов и для числовых констант.
Суть этого алгоритма заключается в том, чтобы заменить числа некоторым набором арифметических выражений, и после этого представить все числа в 16-ичной системе счисления [8]. Детальный алгоритм представлен на рис. 7.
Данный алгоритм также является важным в работе программной системы. Почти во всех программах используются какие-либо строковые константы, будь то сообщения об ошибках или текстовые элементы интерфейса. Для, того чтобы труднее было разобрать исходный код программы, необходимо преобразовать и скрыть явное использование таких строковых констант.
В данном преобразовании строки представляются в виде конкатенации вызовов различных функций, а также в виде кодировки BASE64. Все разбиение строковых констант происходит каждый раз случайно, обеспечивая различный исходный код на выходе обфускатора [9].
Детальный алгоритм представлен на рис. 8.
Данное преобразование исходного кода является самым не затратным с точки зрения ресурсов преобразования, но с точки зрения конечного запутывания является одним из самых эффективных алгоритмов обфускации.
Главная особенность этого алгоритма в том, чтобы представить имена переменных и функций в виде непонятного для человека набора символов [9]. Подробный разбор алгоритма представлен на рис. 9.
Данный алгоритм не является напрямую запутывающим преобразованием, его нельзя выбрать через интерфейс пользователя. Он служит для того, чтобы перемешивать глобальные функции, которые могли появиться вследствие других запутывающих преобразований (мусорные функции). Иначе, если это преобразование не использовать, то мусорные функции располагались бы в конце документа с исходным кодом и могли бы легко быть удалены человеком, который взялся бы деобфусцировать исходный код. Детальное описание алгоритма представлено на рис. 10.
После разработки алгоритмов, они были реализованы на языке Java, где и были объединены в один главный обфусцирующий алгоритм.
На рис. 11 представлена экранная форма обфускатора, на которой видно, что пользователь выбрал все доступные режимы обфускации. Также видно, что исходный код существенно изменился.
Как показало тестирование на экспертных группах, код стал более запутанным. Эксперты сделали субъективное заключение – сложность понимания исходного кода увеличилась в 3 раза.
В ходе выполнения данной работы были проанализированы существующие обфускаторы языка JavaScript. После их детального изучения, были выявлены присущие им недостатки, учитывая которые, было решено разработать и реализовать собственные эффективные алгоритмы обфускации.
Также была разработана последовательность объединения запутывающих преобразований (общий алгоритм работы обфускатора) с максимальной степенью запутанности.
Дальнейшие разработка и реализация алгоритмов обфускации являются перспективными, так как разработанные алгоритмы работают напрямую с кодом, меняя внешнее представление кода, в то время как, преобразования потока управления программ не производится. То есть разработанные алгоритмы принадлежат к категориям лёгкой и средней степеням обфускации, а алгоритмы обфускации сложной степени необходимо разрабатывать.
Литература