From 1d831d82d40f37ae4f347c65f9736887113f4ed1 Mon Sep 17 00:00:00 2001 From: Histausse <28675452+histausse@users.noreply.github.com> Date: Sat, 29 Nov 2025 20:50:49 +0100 Subject: [PATCH] feat: select better class from duplicates (#2701)(PR #2702) * select the right class in in case of duplicate * select correct class in gui in case of duplication * fix code format --------- Co-authored-by: Jean-Marie 'Histausse' Mineau --- .../main/java/jadx/api/JadxDecompiler.java | 3 +- .../src/main/java/jadx/api/JavaPackage.java | 10 +++++ .../java/jadx/core/dex/nodes/PackageNode.java | 9 +++++ .../java/jadx/core/dex/nodes/RootNode.java | 39 ++++++++++++++++++- .../jadx/gui/utils/pkgs/PackageHelper.java | 2 +- 5 files changed, 59 insertions(+), 4 deletions(-) diff --git a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java index d69c9d26d..c172a77a1 100644 --- a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java +++ b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java @@ -543,9 +543,10 @@ public final class JadxDecompiler implements Closeable { return foundPkg; } List clsList = Utils.collectionMap(pkg.getClasses(), this::convertClassNode); + List clsListNoDup = Utils.collectionMap(pkg.getClassesNoDup(), this::convertClassNode); int subPkgsCount = pkg.getSubPackages().size(); List subPkgs = subPkgsCount == 0 ? Collections.emptyList() : new ArrayList<>(subPkgsCount); - JavaPackage javaPkg = new JavaPackage(pkg, clsList, subPkgs); + JavaPackage javaPkg = new JavaPackage(pkg, clsList, clsListNoDup, subPkgs); if (subPkgsCount != 0) { // add subpackages after parent to avoid endless recursion for (PackageNode subPackage : pkg.getSubPackages()) { diff --git a/jadx-core/src/main/java/jadx/api/JavaPackage.java b/jadx-core/src/main/java/jadx/api/JavaPackage.java index 4fa16f1d9..91ebb90f1 100644 --- a/jadx-core/src/main/java/jadx/api/JavaPackage.java +++ b/jadx-core/src/main/java/jadx/api/JavaPackage.java @@ -15,11 +15,17 @@ import jadx.core.dex.nodes.PackageNode; public final class JavaPackage implements JavaNode, Comparable { private final PackageNode pkgNode; private final List classes; + private final List clsListNoDup; private final List subPkgs; JavaPackage(PackageNode pkgNode, List classes, List subPkgs) { + this(pkgNode, classes, classes, subPkgs); + } + + JavaPackage(PackageNode pkgNode, List classes, List clsListNoDup, List subPkgs) { this.pkgNode = pkgNode; this.classes = classes; + this.clsListNoDup = clsListNoDup; this.subPkgs = subPkgs; } @@ -49,6 +55,10 @@ public final class JavaPackage implements JavaNode, Comparable { return classes; } + public List getClassesNoDup() { + return clsListNoDup; + } + public boolean isRoot() { return pkgNode.isRoot(); } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/PackageNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/PackageNode.java index c9fdc43fb..5eb5aeabb 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/PackageNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/PackageNode.java @@ -3,6 +3,7 @@ package jadx.core.dex.nodes; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -188,6 +189,14 @@ public class PackageNode extends LineAttrNode return classes; } + public List getClassesNoDup() { + return classes.stream() + .map(ClassNode::getClassInfo) + .collect(Collectors.toSet()) + .stream() + .map(e -> root.resolveClass(e)).collect(Collectors.toList()); + } + public JavaPackage getJavaNode() { return javaNode; } 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 2a64283a9..a214d6c59 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 @@ -217,8 +217,43 @@ public class RootNode { public void addClassNode(ClassNode clsNode) { classes.add(clsNode); - clsMap.put(clsNode.getClassInfo(), clsNode); - rawClsMap.put(clsNode.getRawName(), clsNode); + ClassNode prevClsNode = clsMap.get(clsNode.getClassInfo()); + if (prevClsNode == null) { + clsMap.put(clsNode.getClassInfo(), clsNode); + rawClsMap.put(clsNode.getRawName(), clsNode); + } else { + String prevFileName = prevClsNode.getInputFileName(); + String fileName = clsNode.getInputFileName(); + + boolean prevFileNameIsValid = prevFileName.matches("classes[1-9]\\d*.dex") && !prevFileName.equals("classes1.dex"); + boolean fileNameIsValid = fileName.matches("classes[1-9]\\d*.dex") && !fileName.equals("classes1.dex"); + + ClassNode newClsNode = clsNode; + // classes.dex has precedence + if (fileName.equals("classes.dex")) { + newClsNode = clsNode; + } else if (prevFileName.equals("classes.dex")) { + newClsNode = prevClsNode; + } else if (prevFileNameIsValid && !fileNameIsValid) { + // valid dex names have precedence + newClsNode = prevClsNode; + } else if (!prevFileNameIsValid && fileNameIsValid) { + newClsNode = clsNode; + } else if (prevFileNameIsValid && fileNameIsValid) { + // if both are valid, the lower index has precedence + long index = Long.parseLong(fileName.substring(7, fileName.length() - 4)); + long prevIndex = Long.parseLong(prevFileName.substring(7, prevFileName.length() - 4)); + + if (index < prevIndex) { + newClsNode = clsNode; + } else { + newClsNode = prevClsNode; + } + } + + clsMap.put(newClsNode.getClassInfo(), newClsNode); + rawClsMap.put(newClsNode.getRawName(), newClsNode); + } } public void loadResources(ResourcesLoader resLoader, List resources) { diff --git a/jadx-gui/src/main/java/jadx/gui/utils/pkgs/PackageHelper.java b/jadx-gui/src/main/java/jadx/gui/utils/pkgs/PackageHelper.java index ad9dd36db..5f1c7eba0 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/pkgs/PackageHelper.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/pkgs/PackageHelper.java @@ -174,7 +174,7 @@ public class PackageHelper { if (synthetic) { classes = Collections.emptyList(); } else { - classes = Utils.collectionMap(javaPkg.getClasses(), nodeCache::makeFrom); + classes = Utils.collectionMap(javaPkg.getClassesNoDup(), nodeCache::makeFrom); classes.sort(CLASS_COMPARATOR); } return nodeCache.newJPackage(javaPkg, synthetic, pkgEnabled, classes);