From 02bc27e887ffb2ed6c13a2a6886c124bd170b091 Mon Sep 17 00:00:00 2001 From: Skylot <118523+skylot@users.noreply.github.com> Date: Sun, 2 Jun 2024 16:46:03 +0100 Subject: [PATCH] build exclude list in init method to improve performance, add default values migration --- README.md | 4 +- .../src/main/java/jadx/cli/JadxCLIArgs.java | 5 +- .../src/main/java/jadx/api/JadxArgs.java | 3 +- .../main/java/jadx/api/JadxDecompiler.java | 1 - jadx-core/src/main/java/jadx/core/Jadx.java | 2 + .../jadx/core/deobf/InitRenameProviders.java | 20 +++++ .../core/deobf/conditions/DeobfWhitelist.java | 77 ++++++++++++++++--- .../java/jadx/core/dex/nodes/RootNode.java | 9 --- .../java/jadx/gui/settings/JadxSettings.java | 7 +- 9 files changed, 100 insertions(+), 28 deletions(-) create mode 100644 jadx-core/src/main/java/jadx/core/deobf/InitRenameProviders.java diff --git a/README.md b/README.md index ffe2a2e94..4d1395a54 100644 --- a/README.md +++ b/README.md @@ -126,9 +126,9 @@ options: 'read-and-autosave-before-closing' - read and autosave before exiting the app or closing the project 'ignore' - don't read or save (can be used to skip loading mapping files referenced in the project file) --deobf - activate deobfuscation - --deobf-min - min length of name, renamed if shorter, default: 2 + --deobf-min - min length of name, renamed if shorter, default: 3 --deobf-max - max length of name, renamed if longer, default: 64 - --deobf-whitelist - space separated list of classes (full name) and packages (ends with '.*') to exclude from deobfuscation + --deobf-whitelist - space separated list of classes (full name) and packages (ends with '.*') to exclude from deobfuscation, default: android.support.* android.os.* androidx.core.os.* androidx.annotation.* --deobf-cfg-file - deobfuscation mappings file used for JADX auto-generated names (in the JOBF file format), default: same dir and name as input file with '.jobf' extension --deobf-cfg-file-mode - set mode for handling the JADX auto-generated names' deobfuscation map file: 'read' - read if found, don't save (default) diff --git a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java index 92b5af42a..f5e5039b7 100644 --- a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java +++ b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java @@ -28,6 +28,7 @@ import jadx.api.args.GeneratedRenamesMappingFileMode; import jadx.api.args.IntegerFormat; import jadx.api.args.ResourceNameSource; import jadx.api.args.UserRenamesMappingsMode; +import jadx.core.deobf.conditions.DeobfWhitelist; import jadx.core.utils.exceptions.JadxArgsValidateException; import jadx.core.utils.files.FileUtils; @@ -136,7 +137,7 @@ public class JadxCLIArgs { protected boolean deobfuscationOn = false; @Parameter(names = { "--deobf-min" }, description = "min length of name, renamed if shorter") - protected int deobfuscationMinLength = 2; + protected int deobfuscationMinLength = 3; @Parameter(names = { "--deobf-max" }, description = "max length of name, renamed if longer") protected int deobfuscationMaxLength = 64; @@ -145,7 +146,7 @@ public class JadxCLIArgs { names = { "--deobf-whitelist" }, description = "space separated list of classes (full name) and packages (ends with '.*') to exclude from deobfuscation" ) - protected String deobfuscationWhitelistStr = ""; + protected String deobfuscationWhitelistStr = DeobfWhitelist.DEFAULT_STR; @Parameter( names = { "--deobf-cfg-file" }, diff --git a/jadx-core/src/main/java/jadx/api/JadxArgs.java b/jadx-core/src/main/java/jadx/api/JadxArgs.java index 0509e93ea..1e67a14ef 100644 --- a/jadx-core/src/main/java/jadx/api/JadxArgs.java +++ b/jadx-core/src/main/java/jadx/api/JadxArgs.java @@ -32,6 +32,7 @@ import jadx.api.plugins.loader.JadxPluginLoader; import jadx.api.usage.IUsageInfoCache; import jadx.api.usage.impl.InMemoryUsageInfoCache; import jadx.core.deobf.DeobfAliasProvider; +import jadx.core.deobf.conditions.DeobfWhitelist; import jadx.core.deobf.conditions.JadxRenameConditions; import jadx.core.plugins.PluginContext; import jadx.core.utils.files.FileUtils; @@ -109,7 +110,7 @@ public class JadxArgs implements Closeable { /** * List of classes and packages (ends with '.*') to exclude from deobfuscation */ - private List deobfuscationWhitelist = new ArrayList<>(); + private List deobfuscationWhitelist = new ArrayList<>(DeobfWhitelist.DEFAULT_LIST); /** * Nodes alias provider for deobfuscator and rename visitor diff --git a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java index 1079f2c82..dfad61f89 100644 --- a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java +++ b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java @@ -118,7 +118,6 @@ public final class JadxDecompiler implements Closeable { loadInputFiles(); root = new RootNode(args); - root.init(); root.setDecompilerRef(this); root.mergePasses(customPasses); root.loadClasses(loadedInputs); diff --git a/jadx-core/src/main/java/jadx/core/Jadx.java b/jadx-core/src/main/java/jadx/core/Jadx.java index 12b48e39a..b42e426b8 100644 --- a/jadx-core/src/main/java/jadx/core/Jadx.java +++ b/jadx-core/src/main/java/jadx/core/Jadx.java @@ -13,6 +13,7 @@ import org.slf4j.LoggerFactory; import jadx.api.CommentsLevel; import jadx.api.JadxArgs; import jadx.core.deobf.DeobfuscatorVisitor; +import jadx.core.deobf.InitRenameProviders; import jadx.core.deobf.SaveDeobfMapping; import jadx.core.dex.attributes.AFlag; import jadx.core.dex.visitors.AnonymousClassVisitor; @@ -102,6 +103,7 @@ public class Jadx { passes.add(new CollectConstValues()); // rename and deobfuscation + passes.add(new InitRenameProviders()); passes.add(new DeobfuscatorVisitor()); passes.add(new SourceFileRename()); passes.add(new RenameVisitor()); diff --git a/jadx-core/src/main/java/jadx/core/deobf/InitRenameProviders.java b/jadx-core/src/main/java/jadx/core/deobf/InitRenameProviders.java new file mode 100644 index 000000000..ac880ae0d --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/deobf/InitRenameProviders.java @@ -0,0 +1,20 @@ +package jadx.core.deobf; + +import jadx.api.JadxArgs; +import jadx.core.dex.nodes.RootNode; +import jadx.core.dex.visitors.AbstractVisitor; +import jadx.core.utils.exceptions.JadxException; + +public class InitRenameProviders extends AbstractVisitor { + + @Override + public void init(RootNode root) throws JadxException { + JadxArgs args = root.getArgs(); + if (args.isDeobfuscationOn() || !args.getRenameFlags().isEmpty()) { + args.getAliasProvider().init(root); + } + if (args.isDeobfuscationOn()) { + args.getRenameCondition().init(root); + } + } +} diff --git a/jadx-core/src/main/java/jadx/core/deobf/conditions/DeobfWhitelist.java b/jadx-core/src/main/java/jadx/core/deobf/conditions/DeobfWhitelist.java index 0cd605580..2fad92575 100644 --- a/jadx-core/src/main/java/jadx/core/deobf/conditions/DeobfWhitelist.java +++ b/jadx-core/src/main/java/jadx/core/deobf/conditions/DeobfWhitelist.java @@ -1,37 +1,90 @@ package jadx.core.deobf.conditions; +import java.util.Arrays; import java.util.HashSet; +import java.util.List; import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.PackageNode; import jadx.core.dex.nodes.RootNode; +import jadx.core.utils.Utils; public class DeobfWhitelist extends AbstractDeobfCondition { + private static final Logger LOG = LoggerFactory.getLogger(DeobfWhitelist.class); + + public static final List DEFAULT_LIST = Arrays.asList( + "android.support.*", + "android.os.*", + "androidx.core.os.*", + "androidx.annotation.*"); + + public static final String DEFAULT_STR = Utils.listToString(DEFAULT_LIST, " "); + private final Set packages = new HashSet<>(); - private final Set classes = new HashSet<>(); + private final Set classes = new HashSet<>(); + private boolean reportMissingItems = false; @Override public void init(RootNode root) { packages.clear(); classes.clear(); - for (String whitelistItem : root.getArgs().getDeobfuscationWhitelist()) { - if (!whitelistItem.isEmpty()) { - if (whitelistItem.endsWith(".*")) { - packages.add(whitelistItem.substring(0, whitelistItem.length() - 2)); - } else { - classes.add(whitelistItem); - } + List excludeList = root.getArgs().getDeobfuscationWhitelist(); + reportMissingItems = !excludeList.equals(DEFAULT_LIST); + for (String name : excludeList) { + if (name.isEmpty()) { + continue; + } + if (name.endsWith(".*")) { + excludePackage(root, name.substring(0, name.length() - 2)); + } else { + excludeClass(root, name); } } + LOG.debug("Excluded from deobfuscation: {} packages, {} classes", packages.size(), classes.size()); + } + + private void excludeClass(RootNode root, String clsFullName) { + ClassNode cls = root.resolveClass(clsFullName); + if (cls == null) { + if (reportMissingItems) { + LOG.info("Can't exclude from deobfuscation: class '{}' not found", clsFullName); + } + return; + } + excludeClsNode(cls); + } + + private void excludeClsNode(ClassNode cls) { + classes.add(cls); + cls.addInfoComment("Class excluded from deobfuscation"); + } + + private void excludePackage(RootNode root, String fullPkgName) { + PackageNode pkg = root.resolvePackage(fullPkgName); + if (pkg == null) { + if (reportMissingItems) { + LOG.info("Can't exclude from deobfuscation: package '{}' not found", fullPkgName); + } + return; + } + excludePkgNode(pkg); + } + + private void excludePkgNode(PackageNode pkg) { + packages.add(pkg.getFullName()); + pkg.getClasses().forEach(this::excludeClsNode); + pkg.getSubPackages().forEach(this::excludePkgNode); } @Override public Action check(PackageNode pkg) { - String pkgName = pkg.getPkgInfo().getFullName(); - if (packages.stream().anyMatch(pattern -> pattern.equals(pkgName) || pkgName.startsWith(pattern + "."))) { + if (packages.contains(pkg.getPkgInfo().getFullName())) { return Action.FORBID_RENAME; } return Action.NO_ACTION; @@ -39,10 +92,10 @@ public class DeobfWhitelist extends AbstractDeobfCondition { @Override public Action check(ClassNode cls) { - if (classes.contains(cls.getClassInfo().getFullName())) { + if (classes.contains(cls)) { return Action.FORBID_RENAME; } - return check(cls.getPackageNode()); + return Action.NO_ACTION; } @Override diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java index 45570d963..c7f2bb8a8 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java @@ -109,15 +109,6 @@ public class RootNode { this.typeUtils = new TypeUtils(this); } - public void init() { - if (args.isDeobfuscationOn() || !args.getRenameFlags().isEmpty()) { - args.getAliasProvider().init(this); - } - if (args.isDeobfuscationOn()) { - args.getRenameCondition().init(this); - } - } - public void loadClasses(List loadedInputs) { for (ICodeLoader codeLoader : loadedInputs) { codeLoader.visitClasses(cls -> { 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 697adb6bb..d14b58b3b 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java @@ -35,6 +35,7 @@ import jadx.api.args.ResourceNameSource; import jadx.api.args.UserRenamesMappingsMode; import jadx.cli.JadxCLIArgs; import jadx.cli.LogHelper; +import jadx.core.deobf.conditions.DeobfWhitelist; import jadx.gui.cache.code.CodeCacheMode; import jadx.gui.cache.usage.UsageCacheMode; import jadx.gui.settings.data.ShortcutsWrapper; @@ -53,7 +54,7 @@ public class JadxSettings extends JadxCLIArgs { private static final Path USER_HOME = Paths.get(System.getProperty("user.home")); private static final int RECENT_PROJECTS_COUNT = 30; - private static final int CURRENT_SETTINGS_VERSION = 20; + private static final int CURRENT_SETTINGS_VERSION = 21; private static final Font DEFAULT_FONT = new RSyntaxTextArea().getFont(); @@ -805,6 +806,10 @@ public class JadxSettings extends JadxCLIArgs { tabDndGhostType = TabDndGhostType.OUTLINE; fromVersion++; } + if (fromVersion == 20) { + deobfuscationWhitelistStr = DeobfWhitelist.DEFAULT_STR; + fromVersion++; + } if (fromVersion != CURRENT_SETTINGS_VERSION) { LOG.warn("Incorrect settings upgrade. Expected version: {}, got: {}", CURRENT_SETTINGS_VERSION, fromVersion); }