Содержание статьи
Среди читателей наверняка есть те, кому тема скриптовой обфускации гораздо милее ассемблера и прочего низкоуровневого колдунства. Именно этому вопросу была посвящена, например, моя недавняя статья «JSFuck. Разбираем уникальный метод обфускации JS-кода» или упоминаемая в ней обзорная статья про обфускаторы на Хабре. Но что делать, если перечисленные в этой статье кастомные деобфускаторы не помогают? В таком случае обфускацию придется обходить самостоятельно, и сейчас я расскажу, как это делается.
Используем автоматические деобфускаторы
Для примера возьмем некое браузерное JavaScript-приложение. Объем его составляет около трех мегабайт, примерно три четверти из которых занимает жестко обфусцированный код, начинающийся так, как показано на следующем скриншоте.
А заканчивается этот код вот так.
Если ты уже успел ознакомиться с упомянутой выше статьей, характерные имена идентификаторов (_0x58cd18
, _0x2f8935_0x321d33
, _0x1e0595
) должны были натолкнуть тебя на мысль, что код запутан обфускатором obfuscator.io. Однако попытка деобфускации его стандартным онлайн‑деобфускатором при любых настройках не приносит положительного результата: читаемый код в правом окне просто не появляется.
Точно так же не приносят результатов и попытки деобфускации другими упомянутыми в статье инструментами. Например, универсальный деобфускатор de4js выдает совершенно неинформативный результат.
Похоже, надежды на автоматические деобфускаторы мало и нам придется учиться работать руками.
info
Забегая вперед, скажу, что перечисленными выше инструментами мы не исчерпали все средства автоматической деобфускации. Например, можно попытаться оптимизировать код через нейросеть Llama. Автоматически деобфусцировать подобный код умеет проект webcrack, но давай все‑таки сделаем вид, что с использованием автоматических средств у нас ничего не вышло, — так намного интереснее!
Деобфусцируем код вручную
Для начала натравим на сырой код JS Beautifier, дабы придать ему читабельность. Пробежавшись по теперь уже структурированному коду, обращаем внимание на многочисленные вызовы функций с десятью шестнадцатеричными константами в виде параметров:
_0x4a0111(0xa0c, 0xc0b, 0x13ef, 0x1e3e, 0x15e2, 0x1a29, 0x1b08, 0x94b, 0x968, 0x753)_0x114a88(-0x6d, 0x126c, 0x621, 0xa59, -0x5f2, 0x72a, 0x6cf, 0x8a, 0xbf9, -0x4b4)_0x27e22f(0xbf2, 0x670, 0xe4, 0x132e, 0x1267, 0xbf9, -0xb6, 0x697, 0x51f, 0x6da)_0x1e51ce(0xe58, 0x1b5e, 0x2457, 0x191a, 0x224a, 0x133c, 0xf61, 0x1c11, 0x128d, 0xc77)_0x33055f(0x16f4, 0x1704, 0xbaf, 0x231d, 0x163e, 0x161a, 0xca1, 0x15ba, 0x1c3f, 0x1649)_0x1b164c(0x485, -0x398, 0x1e0, 0xf51, 0xcdd, 0x2de, 0xfea, 0x82f, -0x54a, 0x37)...
Логично предположить, что таким образом зашифрованы константы, которые первым делом надо перевести в нормальный читаемый вид. Как обычно, начнем с конца. Код заканчивается следующим фрагментом:
...} catch (_0x321d33) { console[_0x1e0595(0x4f3, 0x854, 0x1210, 0x19e1, 0x3ca, 0x992, 0x665, 0xf98, 0x185b, 0x1073)](_0x321d33); }});
Если мыслить логически, это console.
, то есть _0x1e0595(
. Попробуем провернуть этот фарш назад: ищем в коде строку function
:
function _0x1e0595(_0x4bc581, _0x4ecbba, _0x1d5a39, _0x50dcae, _0x403da8, _0x4ad34e, _0x2b446e, _0x3b51da, _0x44854e, _0x1491c6) { return _0x340121(_0x1491c6 - 0x5ff, _0x4ecbba - 0xb8, _0x1d5a39 - 0xb2, _0x50dcae - 0x81, _0x403da8 - 0x80, _0x4ad34e - 0x1b, _0x2b446e - 0x99, _0x44854e, _0x44854e - 0x72, _0x1491c6 - 0x10f);}
Как видишь, эта хрень ссылается на другую субхрень по имени _0x340121
. Ищем и ее тоже:
function _0x340121(_0x5ae465, _0x101079, _0x1d662f, _0x55f16c, _0x4029db, _0x3a7a06, _0x1d53e1, _0x5b0eb3, _0x4c47fe, _0x445726) { return _0x3a86(_0x5ae465 - -0x26c, _0x5b0eb3);}
Эта субхрень, в свою очередь, ссылается на протохрень под именем _0x3a86
, которая, по счастью, последняя (точнее, первая) в этой цепочке:
function _0x3a86(_0x37610f, _0x5cbb3a) { const _0x1214fd = _0x5e2d(); return _0x3a86 = function(_0x3aa59b, _0x1ad7b1) { _0x3aa59b = _0x3aa59b - (-0x10 * -0x80 + 0x69 * -0x1 + 0xa * -0xb5); _0x4072cc = _0x1214fd[_0x3aa59b]; return _0x4072cc; }, _0x3a86(_0x37610f, _0x5cbb3a);}
Пока что все просто. Осталось найти массив строковых констант, возвращаемый _0x5e2d
:
function _0x5e2d(){ const _0x552e21=['t?id=','mjSUq','wcYjB','pljgg','ct:\x20<','accou','nlMqS', ... 'se,\x22s','xESCJ']; _0x5e2d=function(){ return _0x552e21; }; return _0x5e2d();}
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»