Mod Menu Configuration Stealth Concealment
Minecraft Gameplay Impact
Mod Menu is the standard graphical modification browser for the Fabric and Quilt ecosystems, reading loaded mods and configuration settings to render in-game mod lists.
ModMenu_Config_Hiding detects modifications that tamper with the persistent <config>/modmenu.json configuration file to hide themselves from the menu interface. In the inspected sample (com.example.timer.TimerClientMod), the mod registers as an onPreLaunch entrypoint. Before the client user interface is loaded, hideFromModMenu() locates modmenu.json, parses its structure via JsonParser.parseReader(), iterates through both "hidden_mods" and "hiddenMods" arrays, and injects "timer" if it is not already present. It then creates any missing parent directories and writes the modified JSON document back to disk via Files.newBufferedWriter() and GsonBuilder().toJson(root, writer).
When Mod Menu subsequently initializes, it reads the modified file and omits the modification from the GUI list. The mod continues executing with full privileges, but is omitted from the visible in-game modification list.
This file-based concealment technique directly complements Modlist_Self_Hide (score 9): while Modlist_Self_Hide targets mods that use reflection to purge their container from the loader's mod list, ModMenu_Config_Hiding targets mods that manipulate Mod Menu's configuration file on disk. In legitimate software, Mod Menu itself (com.terraformersmc.modmenu) manages modmenu.json via user interactions in its settings screen, and modpack creators may pre-populate configuration files. However, an independent third-party mod programmatically altering this file in preLaunch indicates deliberate stealth evasion.
Functions & APIs Analyzed
modmenu.json
Configuration file storing Mod Menu preferences and hidden mod arrays.
hidden_mods / hiddenMods
JSON array keys designating modifications hidden from the graphical menu.
Decompiled Representation (Vineflower View)
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);
}
}
Bytecode Signature & Static Detection
The detector inspects string constants within class files, requiring two concurrent string matches in a single class:
- The configuration file name:
modmenu.json. - The hidden mod array keys:
hidden_modsorhiddenMods.
This combination detects modifications that alter the persistent Mod Menu configuration on disk to suppress their own entry from in-game mod listings. This rule targets persistent on-disk configuration tampering, complementing Modlist_Self_Hide (score 9), which detects runtime loader collection tampering via reflection.