From a81cec77012c577f68e936b69a44735266d99de1 Mon Sep 17 00:00:00 2001 From: bagipro Date: Wed, 8 May 2024 16:16:08 +0700 Subject: [PATCH 1/4] Improve deobf whitelist --- README.md | 2 +- .../src/main/java/jadx/cli/JadxCLIArgs.java | 3 +- .../src/main/java/jadx/api/JadxArgs.java | 3 +- .../core/deobf/conditions/DeobfWhitelist.java | 31 +++++++++---------- 4 files changed, 18 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index c827dcc75d7..9ff056e17bd 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ options: --deobf - activate deobfuscation --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, default: android.support.v4.* android.support.v7.* android.support.v4.os.* android.support.annotation.Px androidx.core.os.* androidx.annotation.Px + --deobf-whitelist - space separated list of classes (full name) and packages (ends with '.*') to exclude from deobfuscation --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 f5e5039b793..29da37fd56a 100644 --- a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java +++ b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java @@ -28,7 +28,6 @@ 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; @@ -146,7 +145,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 = DeobfWhitelist.DEFAULT_STR; + protected String deobfuscationWhitelistStr = ""; @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 df7ff1cfdb2..0509e93ea5f 100644 --- a/jadx-core/src/main/java/jadx/api/JadxArgs.java +++ b/jadx-core/src/main/java/jadx/api/JadxArgs.java @@ -32,7 +32,6 @@ 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; @@ -110,7 +109,7 @@ public class JadxArgs implements Closeable { /** * List of classes and packages (ends with '.*') to exclude from deobfuscation */ - private List deobfuscationWhitelist = DeobfWhitelist.DEFAULT_LIST; + private List deobfuscationWhitelist = new ArrayList<>(); /** * Nodes alias provider for deobfuscator and rename visitor 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 68712609ff8..0cd605580ec 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,27 +1,15 @@ package jadx.core.deobf.conditions; -import java.util.Arrays; import java.util.HashSet; -import java.util.List; import java.util.Set; 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 { - - public static final List DEFAULT_LIST = Arrays.asList( - "android.support.v4.*", - "android.support.v7.*", - "android.support.v4.os.*", - "android.support.annotation.Px", - "androidx.core.os.*", - "androidx.annotation.Px"); - - public static final String DEFAULT_STR = Utils.listToString(DEFAULT_LIST, " "); - private final Set packages = new HashSet<>(); private final Set classes = new HashSet<>(); @@ -42,7 +30,8 @@ public void init(RootNode root) { @Override public Action check(PackageNode pkg) { - if (packages.contains(pkg.getPkgInfo().getFullName())) { + String pkgName = pkg.getPkgInfo().getFullName(); + if (packages.stream().anyMatch(pattern -> pattern.equals(pkgName) || pkgName.startsWith(pattern + "."))) { return Action.FORBID_RENAME; } return Action.NO_ACTION; @@ -53,6 +42,16 @@ public Action check(ClassNode cls) { if (classes.contains(cls.getClassInfo().getFullName())) { return Action.FORBID_RENAME; } - return Action.NO_ACTION; + return check(cls.getPackageNode()); + } + + @Override + public Action check(FieldNode fld) { + return check(fld.getParentClass()); + } + + @Override + public Action check(MethodNode mth) { + return check(mth.getParentClass()); } } From 794e5adb7f70c8f9808e7a4936c9ab3f8d229b6d Mon Sep 17 00:00:00 2001 From: bagipro Date: Thu, 9 May 2024 18:22:02 +0700 Subject: [PATCH 2/4] Change deobf-min default to 2 --- README.md | 2 +- jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9ff056e17bd..ffe2a2e942e 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ 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: 3 + --deobf-min - min length of name, renamed if shorter, default: 2 --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-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 diff --git a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java index 29da37fd56a..92b5af42a86 100644 --- a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java +++ b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java @@ -136,7 +136,7 @@ public class JadxCLIArgs { protected boolean deobfuscationOn = false; @Parameter(names = { "--deobf-min" }, description = "min length of name, renamed if shorter") - protected int deobfuscationMinLength = 3; + protected int deobfuscationMinLength = 2; @Parameter(names = { "--deobf-max" }, description = "max length of name, renamed if longer") protected int deobfuscationMaxLength = 64; 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 3/4] 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 ffe2a2e942e..4d1395a5446 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 92b5af42a86..f5e5039b793 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.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 0509e93ea5f..1e67a14ef6b 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.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 1079f2c82ce..dfad61f8940 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 void load() { 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 12b48e39a3d..b42e426b83e 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 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 static List getPreDecompilePassesList() { 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 00000000000..ac880ae0d0c --- /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 0cd605580ec..2fad9257579 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 Action check(PackageNode pkg) { @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 45570d96355..c7f2bb8a82a 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 RootNode(JadxArgs args) { 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 697adb6bb38..d14b58b3bb3 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.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 @@ private void upgradeSettings(int fromVersion) { 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); } From 5640f3b93152ae0bb3f268606e408f91ae98d0af Mon Sep 17 00:00:00 2001 From: Skylot <118523+skylot@users.noreply.github.com> Date: Sun, 2 Jun 2024 18:21:21 +0100 Subject: [PATCH 4/4] fix test --- .../deobf/a/TestNegativeRenameCondition.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/jadx-core/src/test/java/jadx/tests/integration/deobf/a/TestNegativeRenameCondition.java b/jadx-core/src/test/java/jadx/tests/integration/deobf/a/TestNegativeRenameCondition.java index 50a7377afaf..ef80e4d383f 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/deobf/a/TestNegativeRenameCondition.java +++ b/jadx-core/src/test/java/jadx/tests/integration/deobf/a/TestNegativeRenameCondition.java @@ -1,9 +1,14 @@ package jadx.tests.integration.deobf.a; import java.util.Collections; +import java.util.List; import org.junit.jupiter.api.Test; +import jadx.api.deobf.IDeobfCondition; +import jadx.api.deobf.impl.CombineDeobfConditions; +import jadx.core.deobf.conditions.AvoidClsAndPkgNamesCollision; +import jadx.core.deobf.conditions.JadxRenameConditions; import jadx.tests.api.IntegrationTest; import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; @@ -34,6 +39,11 @@ public void test() { // disable all renaming options args.setRenameFlags(Collections.emptySet()); + // disable rename by collision between class and package names + List list = JadxRenameConditions.buildDefaultDeobfConditions(); + list.removeIf(c -> c.getClass().equals(AvoidClsAndPkgNamesCollision.class)); + args.setRenameCondition(CombineDeobfConditions.combine(list)); + assertThat(getClassNode(TestCls.class)) .code() .doesNotContain("renamed from")