Mod Menu Configuration Stealth Concealment
Влияние на механику Minecraft
Mod Menu — стандартный графический интерфейс просмотра модификаций для Fabric и Quilt, считывающий списки загруженных дополнений и пользовательские настройки для отображения меню в игре.
ModMenu_Config_Hiding выявляет модификации, которые скрытно перезаписывают файл <config>/modmenu.json на диске для удаления своего названия из списка. В исследованном образце (com.example.timer.TimerClientMod) код мода выполняется в точке входа onPreLaunch. До загрузки графического интерфейса метод hideFromModMenu() находит modmenu.json, парсит его через JsonParser.parseReader(), обходит массивы "hidden_mods" и "hiddenMods" и добавляет строку "timer". После этого метод создает необходимые директории и сохраняет обновленный JSON на диск через Files.newBufferedWriter() и GsonBuilder().toJson(root, writer).
Когда Mod Menu инициализирует интерфейс, он считывает обновленный файл и не показывает модификацию в списке модов. Чит продолжает исполняться с полными правами, но исключается из отображаемого в интерфейсе списка установленных дополнений.
Этот метод дополняет правило Modlist_Self_Hide (score 9): если Modlist_Self_Hide фиксирует удаление контейнера мода из списка загрузчика через рефлексию (FabricLoaderImpl.getMods().removeIf(...)), то ModMenu_Config_Hiding фиксирует модификацию конфигурационного файла Mod Menu на диске. В легитимном окружении файл modmenu.json редактируется самим Mod Menu (com.terraformersmc.modmenu) по запросу пользователя через меню настроек либо поставляется в готовых сборках. Программная правка чужого конфига сторонним модом на этапе preLaunch свидетельствует о намеренном сокрытии чита.
Анализируемые функции и API
modmenu.json
Файл конфигурации Mod Menu, содержащий пользовательские настройки и списки скрытых модов.
hidden_mods / hiddenMods
Ключи JSON-массива, определяющие идентификаторы скрытых от отображения модификаций.
Декомпилированный вид (Vineflower)
private static void hideFromModMenu() {
try {
Path path = FabricLoader.getInstance().getConfigDir().resolve("modmenu.json");
JsonObject root;
if (Files.exists(path)) {
try (Reader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
root = JsonParser.parseReader(reader).getAsJsonObject();
}
} else {
root = new JsonObject();
}
for (String key : new String[]{"hidden_mods", "hiddenMods"}) {
JsonArray hidden;
if (root.has(key) && root.get(key).isJsonArray()) {
hidden = root.getAsJsonArray(key);
} else {
hidden = new JsonArray();
root.add(key, hidden);
}
boolean present = false;
for (JsonElement entry : hidden) {
if (entry.isJsonPrimitive() && entry.getAsString().equals("timer")) {
present = true;
break;
}
}
if (!present) {
hidden.add("timer");
}
}
Files.createDirectories(path.getParent());
try (Writer writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {
new GsonBuilder().setPrettyPrinting().create().toJson(root, writer);
}
} catch (Exception e) {
TimerClientMod.LOGGER.warn("Failed to hide timer from Mod Menu", e);
}
}
Сигнатура байткода и статический детект
Детектор анализирует строковые константы в байткоде классов, требуя одновременного присутствия двух строк в одном классе:
- Имя файла конфигурации:
modmenu.json. - Ключи массива скрытых модов:
hidden_modsилиhiddenMods.
Такая комбинация выявляет модификации, перезаписывающие файл конфигурации Mod Menu на диске для исключения собственного идентификатора из графического меню. Правило ориентировано на проверку дисковой конфигурации Mod Menu, дополняя отдельное правило Modlist_Self_Hide (оценка 9), нацеленное на очистку списка загрузчика в рантайме через рефлексию.