Для парсинга определенных данных AJAX-нагруженного web-сайта требовалось отправлять запрос к API сервера, в параметрах которого должен быть указан токен. По понятным причинам название и адрес сайта я оставлю в секрете.
Половина нужного для запроса токена присутствовала в data-атрибутах тега script. Вторая половина токена вычислялась JavaScript-кодом, загружаемым на страницу. Понятное дело, данный код был зашифрован (или на профессиональном жаргоне - обфусцирован).
Существует большое число автоматических деобфускаторов, которые должны расшифровывать обфусцированный JavaScript. К сожалению, автоматические деобфускаторы справляются с простым шифрованием. По настоящему серьезно зашифрованный код приходится расшифровывать вручную. К счастью, это проще, чем вы думаете.
Ниже я покажу зашифрованный участок кода и то, как его расшифровать на примере одной функции.
Всю работу я выполнял в дебугере JavaScript кодов, встроенном в стандартное средство разработчика (DevTool) браузера. Все современные браузеры имеют средства разработчика. По определенным причинам мне больше всего нравится FireFox for Developers.
Итак, загрузив JS код в дебугер браузера, я могу просматривать код:
...
Y = function (e) {
var t = J;
return {
r: e.split(X) [0],
s: e[t(532, '9592')](X) [1]
}
},
...
Ну, да... при минификации кода все переменные заменены отдельными буквами. В остальном все просто, кроме стоки:
s: e[t(532, '9592')](X) [1]
Судя по всему - это атрибут объекта. Но как он вычисляется.
Очевидно t(532, '9592') - это вызов функции с определенными параметрами.
Однако почему вызов функции заключен в квадратные скобки?
Мы знаем, что JS позволяет обращаться к методам объектов как через точечную запись, так и через название метода, заключенное в квадратные скобки. То есть записи:
- e.split(X) и
- e['split'](X)
эквивалентны.
Хорошо, однако, что означает t(532, '9592')? Точнее, что возвращает эта функция?
Чтобы понять это, поставим точку останова в начале функции и проследим.
Код t(532, '9592') вызывает функцию J(), которая была скопирована в переменную t.
Сама функция J() приведена ниже:
function J(e, t) {
var n = K();
return J = function (t, r) {
var a = n[t -= 478];
if (void 0 === J.IrgHUW) {
var o = function (e) {
for (var t, n, r = '', a = '', o = 0, i = 0; n = e.charAt(i++); ~n && (t = o % 4 ? 64 * t + n : n, o++ % 4) ? r += String.fromCharCode(255 & t >> ( - 2 * o & 6)) : 0) n = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/='.indexOf(n);
for (var l = 0, c = r.length; l < c; l++) a += '%' + ('00' + r.charCodeAt(l).toString(16)).slice( - 2);
return decodeURIComponent(a)
},
i = function (e, t) {
var n,
r,
a = [
],
i = 0,
l = '';
for (e = o(e), r = 0; r < 256; r++) a[r] = r;
for (r = 0; r < 256; r++) i = (i + a[r] + t.charCodeAt(r % t.length)) % 256,
n = a[r],
a[r] = a[i],
a[i] = n;
r = 0,
i = 0;
for (var c = 0; c < e.length; c++) i = (i + a[r = (r + 1) % 256]) % 256,
n = a[r],
a[r] = a[i],
a[i] = n,
l += String.fromCharCode(e.charCodeAt(c) ^ a[(a[r] + a[i]) % 256]);
return l
};
J.QpnMzl = i,
e = arguments,
J.IrgHUW = !0
}
var l = n[0],
c = t + l,
u = e[c];
return u ? a = u : (void 0 === J.kBpyAg && (J.kBpyAg = !0), a = J.QpnMzl(a, r), e[c] = a),
a
},
J(e, t)
}
Ого!
Мало того, что сама функция довольно наворочена, так она еще и выполняется в форме замыкания.
К счастью, нам не надо разбираться в этой функции, нам нужно лишь посмотреть, что она вернет. Последовательно кликая F11 (зайти в...) отладчика средства разработчика после того, как исполнение программы приостановилось на установленной точке останова, мы провалимся в функцию J(). Сразу после этого необходимо нажать Shift + F11 (Выйти из...) И посмотреть в раздел "Область видимости" отладчика. Там будет указано значение, возвращаемое функцией. Забавно, функция J() возвращает строку 'split'. Таким образом, через последовательный вызов функции J() с разными параметрами, обфускатор может зашифровать любые токены кода, которые для нас будут выглядеть как абракадабра, типа: ee = typeof window !== z(530, 'HMB[') + z(512, ')pxh') ? window : n.g,
При этом, где-то выше в коде в переменную z была скопирована уже известная нам функция J() (точнее ее замыкание), которая теперь вызывается с разными параметрами и возвращает нужные токены кода.
Немного терпения и навыков работы со средством разработчика (точнее отладчиком JS браузера) и дело в шляпе!