From 57238de6ff216f6fa447649edaf12bc900d0370f Mon Sep 17 00:00:00 2001 From: Skylot <118523+skylot@users.noreply.github.com> Date: Fri, 1 Nov 2024 20:13:34 +0000 Subject: [PATCH] feat(cli): add option to disable plugins (#2277) --- .../src/main/java/jadx/cli/JadxCLIArgs.java | 8 +++ .../main/java/jadx/cli/JadxCLICommands.java | 4 +- .../jadx/cli/commands/CommandPlugins.java | 49 ++++++++++++++----- .../src/main/java/jadx/api/JadxArgs.java | 11 +++++ .../main/java/jadx/api/JadxDecompiler.java | 8 +-- .../jadx/core/plugins/JadxPluginManager.java | 13 ++++- .../java/jadx/gui/settings/JadxSettings.java | 1 + .../gui/utils/plugins/CollectPlugins.java | 3 +- .../jadx/plugins/tools/JadxPluginsTools.java | 15 ++++++ 9 files changed, 94 insertions(+), 18 deletions(-) diff --git a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java index 184de4da1f2..525bdb79303 100644 --- a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java +++ b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java @@ -264,6 +264,9 @@ public class JadxCLIArgs { @Parameter(names = { "-q", "--quiet" }, description = "turn off output (set --log-level to QUIET)") protected boolean quiet = false; + @Parameter(names = { "--disable-plugins" }, description = "comma separated list of plugin ids to disable") + protected String disablePlugins = ""; + @Parameter(names = { "--version" }, description = "print jadx version") protected boolean printVersion = false; @@ -370,6 +373,7 @@ public JadxArgs toJadxArgs() { args.setIntegerFormat(integerFormat); args.setUseDxInput(useDx); args.setPluginOptions(pluginOptions); + args.setDisabledPlugins(Arrays.stream(disablePlugins.split(",")).map(String::trim).collect(Collectors.toSet())); return args; } @@ -580,6 +584,10 @@ public Map getPluginOptions() { return pluginOptions; } + public String getDisablePlugins() { + return disablePlugins; + } + static class RenameConverter implements IStringConverter> { private final String paramName; diff --git a/jadx-cli/src/main/java/jadx/cli/JadxCLICommands.java b/jadx-cli/src/main/java/jadx/cli/JadxCLICommands.java index edfc98ec8f4..fface7016e6 100644 --- a/jadx-cli/src/main/java/jadx/cli/JadxCLICommands.java +++ b/jadx-cli/src/main/java/jadx/cli/JadxCLICommands.java @@ -1,7 +1,7 @@ package jadx.cli; +import java.util.LinkedHashMap; import java.util.Map; -import java.util.TreeMap; import com.beust.jcommander.JCommander; @@ -10,7 +10,7 @@ import jadx.core.utils.exceptions.JadxArgsValidateException; public class JadxCLICommands { - private static final Map COMMANDS_MAP = new TreeMap<>(); + private static final Map COMMANDS_MAP = new LinkedHashMap<>(); static { JadxCLICommands.register(new CommandPlugins()); diff --git a/jadx-cli/src/main/java/jadx/cli/commands/CommandPlugins.java b/jadx-cli/src/main/java/jadx/cli/commands/CommandPlugins.java index 9d925d6df73..5ee12ec6e52 100644 --- a/jadx-cli/src/main/java/jadx/cli/commands/CommandPlugins.java +++ b/jadx-cli/src/main/java/jadx/cli/commands/CommandPlugins.java @@ -1,12 +1,17 @@ package jadx.cli.commands; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; import com.beust.jcommander.JCommander; import com.beust.jcommander.Parameter; import com.beust.jcommander.Parameters; +import jadx.api.plugins.JadxPluginInfo; import jadx.cli.JCommanderWrapper; +import jadx.cli.LogHelper; import jadx.plugins.tools.JadxPluginsList; import jadx.plugins.tools.JadxPluginsTools; import jadx.plugins.tools.data.JadxPluginMetadata; @@ -24,6 +29,9 @@ public class CommandPlugins implements ICommand { @Parameter(names = { "-l", "--list" }, description = "list installed plugins") protected boolean list; + @Parameter(names = { "--list-all" }, description = "list all plugins including bundled and dropins") + protected boolean listAll; + @Parameter(names = { "-a", "--available" }, description = "list available plugins") protected boolean available; @@ -53,8 +61,12 @@ public void process(JCommanderWrapper jcw, JCommander subCommander) { jcw.printUsage(subCommander); return; } - if (!subCommander.getUnknownOptions().isEmpty()) { - System.out.println("Error: found unknown options: " + subCommander.getUnknownOptions()); + Set unknownOptions = new HashSet<>(subCommander.getUnknownOptions()); + boolean verbose = unknownOptions.remove("-v") || unknownOptions.remove("--verbose"); + LogHelper.setLogLevel(verbose ? LogHelper.LogLevelEnum.DEBUG : LogHelper.LogLevelEnum.INFO); + + if (!unknownOptions.isEmpty()) { + System.out.println("Error: found unknown options: " + unknownOptions); } if (install != null) { @@ -79,7 +91,10 @@ public void process(JCommanderWrapper jcw, JCommander subCommander) { } } if (list) { - printInstalledPlugins(); + printPlugins(JadxPluginsTools.getInstance().getInstalled()); + } + if (listAll) { + printAllPlugins(); } if (available) { @@ -107,13 +122,11 @@ public void process(JCommanderWrapper jcw, JCommander subCommander) { } } - private static void printInstalledPlugins() { - List installed = JadxPluginsTools.getInstance().getInstalled(); + private static void printPlugins(List installed) { System.out.println("Installed plugins: " + installed.size()); for (JadxPluginMetadata plugin : installed) { StringBuilder sb = new StringBuilder(); - sb.append(" - "); - sb.append(plugin.getPluginId()); + sb.append(" - ").append(plugin.getPluginId()); String version = plugin.getVersion(); if (version != null) { sb.append(" (").append(version).append(')'); @@ -121,14 +134,28 @@ private static void printInstalledPlugins() { if (plugin.isDisabled()) { sb.append(" (disabled)"); } - sb.append(" - "); - sb.append(plugin.getName()); - sb.append(": "); - sb.append(plugin.getDescription()); + sb.append(" - ").append(plugin.getName()); + sb.append(": ").append(plugin.getDescription()); System.out.println(sb); } } + private static void printAllPlugins() { + List installed = JadxPluginsTools.getInstance().getInstalled(); + printPlugins(installed); + Set installedSet = installed.stream().map(JadxPluginMetadata::getPluginId).collect(Collectors.toSet()); + + List plugins = JadxPluginsTools.getInstance().getAllPluginsInfo(); + System.out.println("Other plugins: " + plugins.size()); + for (JadxPluginInfo plugin : plugins) { + if (!installedSet.contains(plugin.getPluginId())) { + System.out.println(" - " + plugin.getPluginId() + + " - " + plugin.getName() + + ": " + plugin.getDescription()); + } + } + } + private void installPlugin(String locationId) { JadxPluginMetadata plugin = JadxPluginsTools.getInstance().install(locationId); System.out.println("Plugin installed: " + plugin.getPluginId() + ":" + plugin.getVersion()); diff --git a/jadx-core/src/main/java/jadx/api/JadxArgs.java b/jadx-core/src/main/java/jadx/api/JadxArgs.java index 18fd5e46357..62b749ca679 100644 --- a/jadx-core/src/main/java/jadx/api/JadxArgs.java +++ b/jadx-core/src/main/java/jadx/api/JadxArgs.java @@ -7,6 +7,7 @@ import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -194,6 +195,8 @@ public enum UseKotlinMethodsForVarNames { private Map pluginOptions = new HashMap<>(); + private Set disabledPlugins = new HashSet<>(); + private JadxPluginLoader pluginLoader = new JadxBasePluginLoader(); private boolean loadJadxClsSetFile = true; @@ -766,6 +769,14 @@ public void setPluginOptions(Map pluginOptions) { this.pluginOptions = pluginOptions; } + public Set getDisabledPlugins() { + return disabledPlugins; + } + + public void setDisabledPlugins(Set disabledPlugins) { + this.disabledPlugins = disabledPlugins; + } + public JadxPluginLoader getPluginLoader() { return pluginLoader; } diff --git a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java index c986257905b..eee71f46d7e 100644 --- a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java +++ b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java @@ -85,7 +85,7 @@ public final class JadxDecompiler implements Closeable { private static final Logger LOG = LoggerFactory.getLogger(JadxDecompiler.class); private final JadxArgs args; - private final JadxPluginManager pluginManager = new JadxPluginManager(this); + private final JadxPluginManager pluginManager; private final List loadedInputs = new ArrayList<>(); private RootNode root; @@ -93,7 +93,7 @@ public final class JadxDecompiler implements Closeable { private List resources; private final IDecompileScheduler decompileScheduler = new DecompilerScheduler(); - private final ResourcesLoader resourcesLoader = new ResourcesLoader(this); + private final ResourcesLoader resourcesLoader; private final List customCodeLoaders = new ArrayList<>(); private final List customResourcesLoaders = new ArrayList<>(); @@ -106,7 +106,9 @@ public JadxDecompiler() { } public JadxDecompiler(JadxArgs args) { - this.args = args; + this.args = Objects.requireNonNull(args); + this.pluginManager = new JadxPluginManager(this); + this.resourcesLoader = new ResourcesLoader(this); } public void load() { diff --git a/jadx-core/src/main/java/jadx/core/plugins/JadxPluginManager.java b/jadx-core/src/main/java/jadx/core/plugins/JadxPluginManager.java index b79d42bff10..4cd235a2083 100644 --- a/jadx-core/src/main/java/jadx/core/plugins/JadxPluginManager.java +++ b/jadx-core/src/main/java/jadx/core/plugins/JadxPluginManager.java @@ -4,12 +4,14 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; import java.util.function.Consumer; import java.util.stream.Collectors; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,6 +28,7 @@ public class JadxPluginManager { private final JadxDecompiler decompiler; private final JadxPluginsData pluginsData; + private final Set disabledPlugins; private final SortedSet allPlugins = new TreeSet<>(); private final SortedSet resolvedPlugins = new TreeSet<>(); private final Map provideSuggestions = new TreeMap<>(); @@ -35,6 +38,7 @@ public class JadxPluginManager { public JadxPluginManager(JadxDecompiler decompiler) { this.decompiler = decompiler; this.pluginsData = new JadxPluginsData(decompiler, this); + this.disabledPlugins = decompiler.getArgs().getDisabledPlugins(); } /** @@ -55,12 +59,19 @@ public void load(JadxPluginLoader pluginLoader) { public void register(JadxPlugin plugin) { Objects.requireNonNull(plugin); PluginContext addedPlugin = addPlugin(plugin); + if (addedPlugin == null) { + LOG.debug("Can't register plugin, it was disabled: {}", plugin.getPluginInfo().getPluginId()); + return; + } LOG.debug("Register plugin: {}", addedPlugin.getPluginId()); resolve(); } - private PluginContext addPlugin(JadxPlugin plugin) { + private @Nullable PluginContext addPlugin(JadxPlugin plugin) { PluginContext pluginContext = new PluginContext(decompiler, pluginsData, plugin); + if (disabledPlugins.contains(pluginContext.getPluginId())) { + return null; + } LOG.debug("Loading plugin: {}", pluginContext); if (!allPlugins.add(pluginContext)) { throw new IllegalArgumentException("Duplicate plugin id: " + pluginContext + ", class " + plugin.getClass()); diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java index 8a92b9b44bc..1c3fffc90ca 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java @@ -61,6 +61,7 @@ public class JadxSettings extends JadxCLIArgs { static final Set SKIP_FIELDS = new HashSet<>(Arrays.asList( "files", "input", "outDir", "outDirSrc", "outDirRes", "outputFormat", "deobfuscationMapFile", + "disablePlugins", "verbose", "quiet", "logLevel", "printVersion", "printHelp")); diff --git a/jadx-gui/src/main/java/jadx/gui/utils/plugins/CollectPlugins.java b/jadx-gui/src/main/java/jadx/gui/utils/plugins/CollectPlugins.java index f993e88f255..c25ff29bfd7 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/plugins/CollectPlugins.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/plugins/CollectPlugins.java @@ -33,7 +33,8 @@ public List build() { .ifPresent(decompiler -> allPlugins.addAll(decompiler.getPluginManager().getResolvedPluginContexts())); // collect and init not loaded plugins in new temp context - try (JadxDecompiler decompiler = new JadxDecompiler(new JadxArgs())) { + JadxArgs jadxArgs = mainWindow.getSettings().toJadxArgs(); + try (JadxDecompiler decompiler = new JadxDecompiler(jadxArgs)) { JadxPluginManager pluginManager = decompiler.getPluginManager(); pluginManager.registerAddPluginListener(pluginContext -> { AppContext appContext = new AppContext(); diff --git a/jadx-plugins-tools/src/main/java/jadx/plugins/tools/JadxPluginsTools.java b/jadx-plugins-tools/src/main/java/jadx/plugins/tools/JadxPluginsTools.java index c48bb426fb1..f70d6085c60 100644 --- a/jadx-plugins-tools/src/main/java/jadx/plugins/tools/JadxPluginsTools.java +++ b/jadx-plugins-tools/src/main/java/jadx/plugins/tools/JadxPluginsTools.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.stream.Collectors; import java.util.stream.Stream; import org.jetbrains.annotations.Nullable; @@ -115,6 +116,20 @@ public List getInstalled() { return loadPluginsJson().getInstalled(); } + /** + * Return all loadable plugins info (including installed, bundled and dropins). + *
+ * For only installed plugins prefer {@link jadx.plugins.tools.JadxPluginsTools#getInstalled} + * method. + */ + public List getAllPluginsInfo() { + try (JadxExternalPluginsLoader pluginsLoader = new JadxExternalPluginsLoader()) { + return pluginsLoader.load().stream() + .map(JadxPlugin::getPluginInfo) + .collect(Collectors.toList()); + } + } + public List getAllPluginJars() { List list = new ArrayList<>(); for (JadxPluginMetadata pluginMetadata : loadPluginsJson().getInstalled()) {