Как увидеть JavaScript код, написанный с использованием непечатных символов


В статье «Как обфусцировать JavaScript код», в самом её конце я привёл «эзотерические» примеры от автора JSFuck в которых большая часть кода просто невидима. Рекомендую посмотреть на те концепты, которые подготовил автор — они вызывают восхищение и заставляют задуматься о возможностях JavaScript. Автор кода записывает строки в виде экранированной последовательности и/или использует трюки с кодировкой. Как бы там не было, но при копировании-вставке код выглядит так:

<body onload=eval(eval('"'+escape("############################################################################################################################################################################################################################################################################################################################################################################################################################").replace(/..(.)..(.)/g,'\\x$1$2')+'"'))>

А при просмотре исходного кода страницы JavaScript код выглядит так:

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

1. Вывод кода перед последней eval

Начнём с примера, который так и называется «Invisible Code» — невидимый исходный код: http://aem1k.com/0/

Видим, что код начинается с «<body onload=eval(eval('"'+escape("». Первый вызов eval получает некий код для выполнения и полученный результат передаётся во второй вызов eval также для выполнения в качестве кода. И уже результат второго вызова eval выводится на экран. То есть интересующий нас исходный код передаётся на вторую eval. Убираем этот вызов и присваиваем то, что должно было отдаться eval для выполнения, переменной a.

Изменения кода я буду делать в Инструментах разработчика в веб-браузере, а чтобы они сохранились после обновления страницы я воспользуюсь методом из статьи «Как сделать так, чтобы изменения в Инструментах разработчика браузера сохранялись после перезагрузки страницы».

Чуть преобразуем код, чтобы можно было вносить в него свои изменения и проверяем его работоспособность:

<body>

<script>
eval(eval('"'+escape("СКРЫТЫЙ КОД").replace(/..(.)..(.)/g,'\\x$1$2')+'"'))
</script>

Код сохранил свою функциональность, поэтому продолжаем изменения:

<body>

<script>
a=eval('"'+escape("СКРЫТЫЙ КОД").replace(/..(.)..(.)/g,'\\x$1$2')+'"')
document.write(a);
</script>

Как можно увидеть, задумка в целом сработала, что вывод кода оборвался из-за какой-то причины:

Опять чуть меняю подход к выводу кода:

<script>
a=eval('"'+escape("СКРЫТЫЙ КОД").replace(/..(.)..(.)/g,'\\x$1$2')+'"')
alert(a);
</script>

Я получил код:

Видимо мой фокус с <pre> не сработал по той причине, что в коде также используется тег <pre>.

Для проверки работоспособности, сохраняю этот код в файл с расширением .htm:

И открываю в браузере, чтобы убедиться, что код рабочий:

Итак, я сумел получить код, который до этого был невидим и отображался в виде точек.


2. Ставим JavaScript на паузу

Следующий пример // VOID — невидимые переменные и код: https://aem1k.com/void/

Использование здесь предыдущего метода затруднено, т. к. код записан в несколько строк и вообще на последнем этапе не используется eval. Более того, большинство модификаций исходного кода (даже лишние пробелы после </script>) делают этот код нефункциональным.

Посмотрим структуру DOM документа:

Много интересного, но в этот раз не помогло.

Возвращаемся на вкладку Sources и ставим JavaScript на паузу:

Обратите внимание, что открылся новый файл — в нём содержится JavaScript код, выполняемый в момент поставки на паузу. Сделаем этот код более читаемым, получим:

i = setInterval(I=>{
    document.body.style.backgroundColor = 0;
    document.body.style.color = "#0FF";
    document.body.innerHTML = "<pre>&lt;script>   " + document.head.textContent.replace(/ᅠ‌*/g, "").replace(/"'.*'"/, "\"''\"").replace(/./g, x=>`<b style="color:#${["F0F", "FF0"][i++ % 5] || "0FF"}">${x}</b>`) + "&lt;/script>"
}
, 100)

Опять сохраняю код в .htm файл:

И открываю в веб-браузере для проверки:

Всё работает! Хотя теперь переливаются другие символы. Суть в том, что переливается сам исходный код, каким бы он ни был (то есть да, на страницу выводится исходный код, который работает в данный момент, вот такой интересный скрипт).


3. Невидимые символы в JavaScript всё равно можно копировать!

Попробуем деобфусцировать невидимый код с помощью JStillery.


Вновь возьмём предыдущий пример (https://aem1k.com/void/), скопируем исходный код в его оригинальном виде и вставим в JStillery. Поразительно, но этот фокус удался:

Получен код после деобфускации:

undefined;
ᅠ‌‌‌‌‌‌‌‌ = '"';
ᅠ‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌ = true;
ᅠ‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌ = false;
ᅠ‌‌‌‌‌‌‌‌‌‌‌‌ = 'true';
ᅠ‌‌‌‌‌‌‌ = 'false';
ᅠ‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌ = 0;
ᅠ‌ = 1;
ᅠ‌‌‌‌‌‌‌‌‌ = {};
ᅠ‌‌ = 2;
ᅠ‌‌‌ = 3;
ᅠ‌‌‌‌‌ = 5;
ᅠ‌‌‌‌‌‌‌‌‌ = '[object Object]';
ᅠ‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌ = 'undefined[object Object]';
ᅠ‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌ = 'n';
ᅠ‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌ = 't';
ᅠ‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌ = 'r';
ᅠ‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌ = 'c';
ᅠ‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌ = 'u';
ᅠ‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌ = 'o';
ᅠ‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌ = 's';
ᅠ‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌ = 'e';
ᅠ‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌ = 'a';
ᅠ‌‌‌‌‌‌‌‌‌‌‌‌‌ = '\\)(';
ᅠ‌‌‌‌‌‌‌‌‌‌‌ = '\\';
ᅠ‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌ = '(';
ᅠ‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌‌ = ')';
ᅠ‌‌‌‌‌‌ = 'return ';
ᅠ‌‌‌‌‌‌‌‌‌‌ = 'constructor';
ᅠ‌‌‌‌‌‌‌‌‌‌‌‌‌‌ = Function;
(function anonymous() {
    i = setInterval(I =>
        /* Called:undefined | Scope Closed:false| writes:false*/
        {
            document.body.style.backgroundColor = 0;
            document.body.style.color = '#0FF';
            document.body.innerHTML = '<pre>&lt;script>   ' + document.head.textContent.replace(/ᅠ‌*/g, '').replace(/"'.*'"/, '"\'\'"').replace(/./g, x => /* Called:undefined | Scope Closed:false| writes:false*/
            '<b style="color:#' + ([
                'F0F',
                'FF0'
            ][i++ % 5] || '0FF') + '">' + x + '</b>') + '&lt;/script>';
        }, 100);;
}());

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

Код также рабочий:

Код из первого примера (http://aem1k.com/0/) не получилось деобфусцировать таким образом. Думаю, дело скорее всего в том, что код портится при копировании и вставке (примечание: позже я понял, что дело в том, что нужно было убрать HTML теги, копипаст бы тоже сработал, как и в примере выше). Поэтому попробуем другим образом — сохраним файл с невидимым JavaScript. Но сохранять будем не в веб браузере — веб-браузеры добавляют много лишнего. Сохраним с помощью wget или cURL, например:

wget http://aem1k.com/0/ -O 0.htm

Из сохранённой страницы убираем всё, что не относится к JavaScript, например, в данном случае я убрал <body onload= и >.

Запускаем деобфускацию в командной строке с помощью jstillery:

jstillery 0.htm

Всё сработало, и мы вновь получили фрагмент кода, который уже видели раньше.

4. Деобфускация JavaScript написанного на нелатинских системах письменности

Возьмём ещё один пример: aurebesh.js — перевод JavaScript на другие системы письменности: https://aem1k.com/aurebesh.js/

К примеру, следующий код работает в веб-брауезре:

<script>
        ก='',ว=!ก+ก,อ=!ว+ก,ซ=ก+{},ฝ=ว[ก++],ค=ว[ง=ก],ญ=++ง+ก,ฒ=ซ[ง+ญ],ว[ฒ+=ซ[ก]+(ว.อ+ซ)[ก]+อ[ญ]+ฝ+ค+ว[ง]+ฒ+ฝ+ซ[ก]+ค][ฒ](อ[ก]+อ[ง]+ว[ญ]+ค+ฝ+"(ก)")()
</script>

Но как узнать, что именно в нём записано?

Если из этого кода убрать тег <script></script>, то его можно деобфусцировать с помощью JStillery:


jstillery thai.htm

Получаем код:

ก = '';
ว = 'true';
อ = 'false';
ซ = '[object Object]';
ฝ = 't';
ค = 'true'[ง = 1];
ญ = 3;
ฒ = 'c';
'true'[ฒ = 'constructor'].constructor('alert(ก)')();

Деобфускация удалась, но нужно отметить, что полученный код не в полной мере сохранил функциональность (вместо единицы alert выводит пустую строку).

Попробуем ещё один вариант. Откроем файл с кодом в веб-браузере и нажмём кнопку «Форматирование». Также поставим выполнение скриптов на паузу:

Поскольку скрипт уже завершил выполнение, перезагрузим страницу и будем нажимать F9 для пошагового выполнения JavaScript.

В результате будет показан выполняемый код в обычном виде, в том, каким его видеть движок JavaScript:

(function anonymous(
) {
alert(ก)
})

Заключение

Мне эти примеры понравились в качестве головоломок. Рассмотренные фрагменты кода, несмотря на их «аномальность», ещё раз подтверждают — защитить от анализа и изменения код JavaScript и HTML очень трудно и в большинстве случаев невозможно.


Рекомендуется Вам:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *