В предыдущей статье мы разобрались, что такое обфускация JavaScript кода и зачем она применяется. Обфусцированный код сложно читать и анализировать, что является и плюсом для сокрытия логики, и минусом для отладки и поддержки кода. Но что делать, если нам все же нужно понять, как работает обфусцированный скрипт? Здесь на помощь приходит деобфускация — процесс восстановления кода в читаемый вид.
Деобфускация требуется в нескольких случаях:
Анализ вредоносных скриптов. Хакеры и авторы малвари часто обфусцируют свой код, чтобы скрыть вредоносную функциональность. Чтобы понять алгоритм работы малвари, необходимо восстановить код.
Поиск и устранение багов. Обфускация усложняет отладку, т.к. в минифицированном коде сложно ставить брейкпоинты и смотреть значения переменных. Деобфускация упрощает поиск ошибок.
Изучение функциональности скрытых фич. Иногда разработчики обфусцируют код с недокументированными возможностями. Через деобфускацию можно узнать об этих фичах.
Модификация проприетарного кода. В закрытом ПО код обычно обфусцирован. Деобфускация открывает возможности для изменения логики под свои задачи.
Стоит помнить, что деобфускация проприетарного кода может нарушать лицензионное соглашение. Всегда сверяйтесь с правилами использования перед реверс-инжинирингом.
Базовые методы деобфускации
1. Форматирование (Beautify)
Первый шаг деобфускации — форматирование минифицированного кода. Это не даст полностью читаемый результат, но упростит дальнейший анализ. Для форматирования можно использовать:
- Инструменты в браузерах, например «Pretty print» в Chrome DevTools
- Плагины для популярных редакторов кода.
2. Переименование переменных
Следующий этап — дать переменным и функциям осмысленные имена вместо однобуквенных. Хорошие деобфускаторы делают это автоматически на основе контекста использования имен.
Например, переменную «a» переименуют в «userName», если она используется в строке «Hello » + a.
3. Удаление мертвого кода
Обфускаторы часто вставляют недостижимый код, который никогда не выполняется, но сильно мешает анализу. При деобфускации его нужно удалять. Например:
if (false) { x = 10; }
4. Восстановление потока управления
Обфускация запутывает последовательность выполнения кода бессмысленными условиями и переходами. Задача деобфускатора — восстановить нормальную структуру с линейными блоками кода и минимумом условий.
Пример с избыточным условием:
// Обфусцировано function check(x) { if (x >= 0) { if (x > 0) { console.log('Positive'); } else { console.log('Zero'); } } } // Деобфусцировано function check(x) { if (x > 0) { console.log('Positive'); } else if (x === 0) { console.log('Zero'); } }
5. Подстановка значений
Многие обфускаторы выносят строки и числа в отдельные массивы, а затем обращаются к ним по индексу. При деобфускации эти значения нужно встраивать обратно в код.
// Обфусцировано const strings = ['Hello', 'world', '!']; console.log(strings[0] + ' ' + strings[1] + strings[2]); // Деобфусцировано console.log('Hello' + ' ' + 'world' + '!');
6. Упрощение выражений
Обфускаторы усложняют арифметические и логические выражения. Например, x * 1
вместо x
или !!x
вместо x
. При восстановлении кода такие излишества нужно удалять.
Было:
if (!!x && y !== undefined) { result = x * 1 + y / 2; }
Стало:
if (x && y !== undefined) { result = x + y / 2; }
Инструменты для деобфускации
Ручная деобфускация — кропотливый процесс, который может занять много времени. К счастью, существуют инструменты для автоматизации распутывания кода:
JS Nice — онлайн-сервис, который не только форматирует, но и переименовывает переменные, удаляет мертвый код и восстанавливает поток управления.
de4js — еще один онлайн-деобфускатор с поддержкой распаковки, подстановки строк и упрощения управления.
JavaScript Deobfuscator — десктопный деобфускатор с широкими возможностями по анализу AST (абстрактного синтаксического дерева).
Расширения для браузеров, например JavaScript Deobfuscator для Chrome, которое позволяет деобфусцировать код прямо в DevTools.
Однако даже самые продвинутые инструменты не дают 100% читаемый код, особенно при нестандартных техниках обфускации. Поэтому для полноценного восстановления логики часто требуется ручной анализ.
Пошаговый пример деобфускации
Разберем на практике восстановление читаемого кода из обфусцированного скрипта.
Допустим, у нас есть такой код:
(function() { var _0x5d2a = ['log', 'Hello\x20World']; (function(_0x25a336, _0x5d2afa) { var _0x41fb0f = function(_0x9fdc11) { while (--_0x9fdc11) { _0x25a336['push'](_0x25a336['shift']()); } }; _0x41fb0f(++_0x5d2afa); }(_0x5d2a, 0x78)); var _0x41fb = function(_0x25a336, _0x5d2afa) { _0x25a336 = _0x25a336 - 0x0; var _0x41fb0f = _0x5d2a[_0x25a336]; return _0x41fb0f; }; console[_0x41fb('0x0')](_0x41fb('0x1')); })();
Шаг 1. Форматирование
Отформатируем минифицированный код, чтобы он стал чуть более структурирован:
Шаг 2. Анализ массива строк
В первой строке кода объявляется массив _0x5d2a
с двумя элементами: ‘log’ и ‘Hello\x20World’.
Запомним это и продолжим анализ.
Шаг 3. Анализ IIFE
Следующий блок — immediately invoked function expression (IIFE).
Он принимает массив _0x5d2a
и число 0x78 (120 в десятичной системе), которое затем увеличивается на 1.
Внутри IIFE объявляется функция _0x41fb0f
, которая принимает число _0x9fdc11 и в цикле вызывает push
и shift
у массива _0x25a336
. По сути это перемешивание массива.
После объявления _0x41fb0f
сразу вызывается с аргументом 0x79 (121 в десятичной).
Шаг 4. Анализ функции-геттера
Дальше объявляется функция _0x41fb
, которая принимает 2 аргумента.
Первый аргумент _0x25a336
уменьшается на 0x0 (0 в десятичной), то есть остается без изменений.
Затем из массива _0x5d2a
по индексу _0x25a336 берется элемент и возвращается. То есть это обертка для обращения к массиву по индексу.
Шаг 5. Подстановка значений
В последней строке у объекта console вызывается метод с индексом ‘0x0’ (0) и аргументом ‘0x1‘ (1).
В массиве _0x5d2a
по индексу 0 хранится ‘log’, а по индексу 1 — ‘Hello\x20World’.
Подставив эти значения, получим:
console['log']('Hello\x20World');
Или, упростив:
console.log('Hello World');
Итог
Изначальный обфусцированный код всего лишь выводил в консоль ‘Hello World‘, хотя и очень запутанным способом.
Собрав все шаги деобфускации, получим такой код:
(function() { console.log('Hello World'); })();
Советы по анализу обфусцированного кода
Начинайте с форматирования и анализа объявлений переменных. Это даст общее представление о структуре кода.
Если видите непонятные числа в коде, попробуйте перевести их в другие системы счисления (десятичную, ASCII).
Обращайте внимание на уязвимые встроенные функции вроде
eval()
иFunction()
. Через них могут исполняться другие части обфусцированного кода.Используйте отладчик и пошаговое выполнение, чтобы смотреть значения переменных и последовательность операций.
Если не понимаете логику работы участка кода, попробуйте его временно удалить или заменить заглушкой. Возможно, он не влияет на основную функциональность.
Ищите паттерны и повторения. Обфускаторы генерируют шаблонный код, поэтому одни и те же конструкции могут использоваться в разных частях.
Запаситесь терпением. Деобфускация может отнимать много времени, особенно при разборе вредоносных скриптов.
Заключение
Деобфускация JavaScript кода — ценный навык, который пригодится для анализа вредоносных скриптов, поиска багов, скрытых возможностей и настройки закрытого кода. Основные методы деобфускации включают форматирование, переименование переменных, удаление мертвого кода, восстановление потока управления, подстановку значений и упрощение выражений.
Для автоматизации этапов деобфускации существуют онлайн-сервисы вроде JS Nice и de4js, а также десктопные утилиты и браузерные расширения. Однако при сложной обфускации не обойтись без ручного анализа. Сам процесс деобфускации состоит из последовательного разбора участков кода, подстановки значений переменных и упрощения логики. Он требует терпения, знания JavaScript и дедуктивных способностей.
Чтобы эффективнее анализировать обфусцированный код, начинайте с форматирования и разбора объявлений переменных. Обращайте внимание на подозрительные встроенные функции, используйте отладчик для пошагового выполнения. Ищите шаблоны и повторения в сгенерированном обфускаторами коде. Изучение деобфускации развивает навыки внимательного чтения кода, отладки и реверс-инжиниринга. Это суперсила для программиста, которая точно пригодится вам в работе или участии в CTF-соревнованиях.
Желаю успехов в нетривиальном, но захватывающем процессе распутывания клубков обфусцированного JavaScript кода! Помните, что за сложностями всегда скрывается элегантность исходной задумки автора.