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

Сре­ди читате­лей навер­няка есть те, кому тема скрип­товой обфуска­ции гораз­до милее ассем­бле­ра и про­чего низ­коуров­невого кол­дунс­тва. Имен­но это­му воп­росу была пос­вящена, нап­ример, моя недав­няя статья «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.log(_0x321d33), то есть _0x1e0595(0x4f3, 0x854, 0x1210, 0x19e1, 0x3ca, 0x992, 0x665, 0xf98, 0x185b, 0x1073) == "log". Поп­робу­ем про­вер­нуть этот фарш назад: ищем в коде стро­ку function _0x1e0595:

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();
}

Продолжение доступно только участникам

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

Присоединяйся к сообществу «Xakep.ru»!

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии