Отладка/реверсинг JavaScript: мониторинг памяти и поиск значений в ней
Допустим, есть у меня простой код:
<html><body><script type=text/javascript>
゚ω゚ノ= /`m´)ノ ~┻━┻ //*´∇`*/ ['_']; o=(゚ー゚) =_=3; c=(゚Θ゚) =(゚ー゚)-(゚ー゚);
(゚Д゚) =(゚Θ゚)= (o^_^o)/ (o^_^o);(゚Д゚)={゚Θ゚: '_' ,゚ω゚ノ : ((゚ω゚ノ==3) +'_') [゚Θ゚]
,゚ー゚ノ :(゚ω゚ノ+ '_')[o^_^o -(゚Θ゚)] ,゚Д゚ノ:((゚ー゚==3) +'_')[゚ー゚] }; (゚Д゚) [゚Θ゚]
=((゚ω゚ノ==3) +'_') [c^_^o];(゚Д゚) ['c'] = ((゚Д゚)+'_') [ (゚ー゚)+(゚ー゚)-(゚Θ゚) ];
(゚Д゚) ['o'] = ((゚Д゚)+'_') [゚Θ゚];(゚o゚)=(゚Д゚) ['c']+(゚Д゚) ['o']+(゚ω゚ノ +'_')[゚Θ゚]+
((゚ω゚ノ==3) +'_') [゚ー゚] + ((゚Д゚) +'_') [(゚ー゚)+(゚ー゚)]+ ((゚ー゚==3) +'_') [゚Θ゚]+
((゚ー゚==3) +'_') [(゚ー゚) - (゚Θ゚)]+(゚Д゚) ['c']+((゚Д゚)+'_') [(゚ー゚)+(゚ー゚)]+ (゚Д゚)
['o']+((゚ー゚==3) +'_') [゚Θ゚];(゚Д゚) ['_'] =(o^_^o) [゚o゚] [゚o゚];(゚ε゚)=((゚ー゚==3)
+'_') [゚Θ゚]+ (゚Д゚) .゚Д゚ノ+((゚Д゚)+'_') [(゚ー゚) + (゚ー゚)]+((゚ー゚==3) +'_') [o^_^o
-゚Θ゚]+((゚ー゚==3) +'_') [゚Θ゚]+ (゚ω゚ノ +'_') [゚Θ゚]; (゚ー゚)+=(゚Θ゚); (゚Д゚)[゚ε゚]='\\';
(゚Д゚).゚Θ゚ノ=(゚Д゚+ ゚ー゚)[o^_^o -(゚Θ゚)];(o゚ー゚o)=(゚ω゚ノ +'_')[c^_^o];(゚Д゚) [゚o゚]='\"';
(゚Д゚) ['_'] ( (゚Д゚) ['_'] (゚ε゚+(゚Д゚)[゚o゚]+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (゚Θ゚)+
(゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ ((゚ー゚)+
(゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+
(゚Θ゚)+ ((o^_^o) +(o^_^o))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (c^_^o)+
(゚Д゚)[゚ε゚]+(゚ー゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚Θ゚)+ (c^_^o)+
(゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+
(゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) +
(゚Θ゚))+ ((゚ー゚) + (o^_^o))+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚ー゚)+
(c^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚Θ゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (゚Θ゚)+
(゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) +(o^_^o))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+
(゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) - (゚Θ゚))+ (o^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+
(o^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+
((゚ー゚) + (゚Θ゚))+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+
((o^_^o) +(o^_^o))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚ー゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+((゚ー゚)
+ (゚Θ゚))+ (゚Θ゚)+ (゚Д゚)[゚o゚]) (゚Θ゚)) ('_');
</script>
(это пример работы кодировщика с https://utf-8.jp/public/aaencode.html с примером по умолчанию)
Мне известно, что если этот код запустить, то где-то внутри соберется строка "Hello, JavaScript" и будет выведена на экран через alert();.
Если код засунуть в http://int3.github.io/metajs/, то можно увидеть, как в процессе пошаговой работы, внизу стека виднеется:
BinaryExpression
BinaryExpression → return"\141\154\145\162\164\50\42\110\145\
Где в качестве литерала, выступает закодированное представление
alert("He
Это - кусочек искомого значения, которое известно заранее (тут было закодировано alert("Hello, JavaScript"))
Внимание, вопросы:
- Как отловить фрагменты кода, ответственные за генерацию этой строки?
- Поставить брейкпоинт в тот момент, когда искомая строка уже сформирована, чтобы получить хотя бы бектрейс?
- Есть ли инструменты для преобразования этого в AST-деревья на манер https://esprima.org/demo/parse.html - но с возможностью редактирования и перекомпиляции обратно? Или таким образом ловить строчку бесполезно?
Ответы (1 шт):
Каждый кодировщик надо рассматривать отдельно, они все имеют свои особенности. Конкретно тот что вы указали в примере (aaencode) довольно легко и быстро вскрыть. Вот пошаговая инструкция:
- Открываем консоль браузера.
- Пишем в консоли ключевое слово debugger, ставим после него точку с запятой, вставляем нашу зашифрованную строку и нажимаем ентер.
- Если все сделали правильно оказываемся на вкладке с исходным кодом в режиме отладки. Для удобства нажимаем кнопочку pretty print {} чтобы видеть отформатированный код.
- Посмотрев на код можно сделать вывод, что интересующая нас строка находится в самом конце. Потому что она самая длинная. Скорее всего все строки кроме последней - просто подготовительные операции. Так что ставим брекпоинт на последнюю строку и жмем Ф8.
- Находясь в последней строке ми можем увидеть что идет обращение к свойству "_" объекта (゚Д゚). Смотрим что там находится Function. Это позволяет нам сделать вывод, что наша строка будет передана первым аргументом в Function и полученная функция будет сразу вызвана (так как это последняя строчка в коде).
- На основе информации из предыдущего шага мы понимаем, что надо просто взять последнюю строчку кода, убрать сначала строчки обращение Function и в конце убрать скобки.
- То что осталось от последней строчки надо выполнить в консоле.
- Профит... Мы увидели исходный код.
Используя такую же логику можно копнуть глубже и пошагово проследить как получается исходная строка просто провалившись внутрь этих всех функций.


