From 19572a674e2148e94de6e3938ce5501ef140f489 Mon Sep 17 00:00:00 2001 From: Skylot Date: Sat, 20 Mar 2021 15:48:22 +0000 Subject: [PATCH] fix: improve deobfuscation performance for overridden methods (#1133) --- .../java/jadx/core/deobf/DeobfPresets.java | 16 ++++++ .../java/jadx/core/deobf/Deobfuscator.java | 51 +++++++++---------- .../java/jadx/core/dex/info/MethodInfo.java | 13 ++++- .../java/jadx/core/dex/nodes/RootNode.java | 6 ++- .../main/java/jadx/core/utils/DebugUtils.java | 25 +++++++++ 5 files changed, 81 insertions(+), 30 deletions(-) diff --git a/jadx-core/src/main/java/jadx/core/deobf/DeobfPresets.java b/jadx-core/src/main/java/jadx/core/deobf/DeobfPresets.java index 9760d90ee..639654a55 100644 --- a/jadx-core/src/main/java/jadx/core/deobf/DeobfPresets.java +++ b/jadx-core/src/main/java/jadx/core/deobf/DeobfPresets.java @@ -163,17 +163,33 @@ public class DeobfPresets { } public String getForCls(ClassInfo cls) { + if (clsPresetMap.isEmpty()) { + return null; + } return clsPresetMap.get(cls.makeRawFullName()); } public String getForFld(FieldInfo fld) { + if (fldPresetMap.isEmpty()) { + return null; + } return fldPresetMap.get(fld.getRawFullId()); } public String getForMth(MethodInfo mth) { + if (mthPresetMap.isEmpty()) { + return null; + } return mthPresetMap.get(mth.getRawFullId()); } + public Set getForVars(MethodInfo mth) { + if (varPresetMap.isEmpty()) { + return null; + } + return varPresetMap.get(mth.getRawFullId()); + } + public void clear() { clsPresetMap.clear(); fldPresetMap.clear(); diff --git a/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java b/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java index a7b7fca7f..9b617b116 100644 --- a/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java +++ b/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java @@ -43,6 +43,8 @@ public class Deobfuscator { private final Set pkgSet = new TreeSet<>(); private final Set reservedClsNames = new HashSet<>(); + private final NavigableSet mthProcessQueue = new TreeSet<>(); + private final int maxLength; private final int minLength; private final boolean useSourceNameAsAlias; @@ -155,6 +157,13 @@ public class Deobfuscator { for (ClassNode cls : root.getClasses()) { processClass(cls); } + while (true) { + MethodNode next = mthProcessQueue.pollLast(); + if (next == null) { + break; + } + renameMethod(next); + } } private void processClass(ClassNode cls) { @@ -182,9 +191,8 @@ public class Deobfuscator { } renameField(field); } - for (MethodNode mth : cls.getMethods()) { - renameMethod(mth); - } + mthProcessQueue.addAll(cls.getMethods()); + for (ClassNode innerCls : cls.getInnerClasses()) { processClass(innerCls); } @@ -203,9 +211,10 @@ public class Deobfuscator { } private void renameMethod(MethodNode mth) { - Set names = deobfPresets.getVarPresetMap().get(mth.getMethodInfo().getRawFullId()); + MethodInfo mthInfo = mth.getMethodInfo(); + Set names = deobfPresets.getForVars(mthInfo); if (names != null) { - mth.getMethodInfo().setVarNameMap(names); + mthInfo.setVarNameMap(names); } String alias = getMethodAlias(mth); if (alias != null) { @@ -219,28 +228,25 @@ public class Deobfuscator { } private void applyMethodAlias(MethodNode mth, String alias) { - MethodInfo methodInfo = mth.getMethodInfo(); - methodInfo.setAlias(alias); - String prev = mthMap.put(methodInfo, alias); - if (prev == null) { - resolveOverriding(mth, alias); - } - } + setSingleMethodAlias(mth, alias); - private void resolveOverriding(MethodNode mth, String alias) { MethodOverrideAttr overrideAttr = mth.get(AType.METHOD_OVERRIDE); if (overrideAttr != null) { for (MethodNode ovrdMth : overrideAttr.getRelatedMthNodes()) { - if (ovrdMth == mth) { - continue; + if (ovrdMth != mth) { + setSingleMethodAlias(ovrdMth, alias); } - MethodInfo methodInfo = ovrdMth.getMethodInfo(); - methodInfo.setAlias(alias); - mthMap.put(methodInfo, alias); } } } + private void setSingleMethodAlias(MethodNode mth, String alias) { + MethodInfo mthInfo = mth.getMethodInfo(); + mthInfo.setAlias(alias); + mthMap.put(mthInfo, alias); + mthProcessQueue.remove(mth); + } + public void addPackagePreset(String origPkgName, String pkgAlias) { PackageNode pkg = getPackageNode(origPkgName, true); pkg.setAlias(pkgAlias); @@ -497,15 +503,6 @@ public class Deobfuscator { if (alias != null) { return alias; } - MethodOverrideAttr overrideAttr = mth.get(AType.METHOD_OVERRIDE); - if (overrideAttr != null) { - for (MethodNode relatedMthNode : overrideAttr.getRelatedMthNodes()) { - String assignedAlias = getAssignedAlias(relatedMthNode.getMethodInfo()); - if (assignedAlias != null) { - return assignedAlias; - } - } - } if (shouldRename(mth.getName())) { return makeMethodAlias(mth); } diff --git a/jadx-core/src/main/java/jadx/core/dex/info/MethodInfo.java b/jadx-core/src/main/java/jadx/core/dex/info/MethodInfo.java index 69855ba48..62881850b 100644 --- a/jadx-core/src/main/java/jadx/core/dex/info/MethodInfo.java +++ b/jadx-core/src/main/java/jadx/core/dex/info/MethodInfo.java @@ -19,6 +19,9 @@ public final class MethodInfo implements Comparable { private final List argTypes; private final ClassInfo declClass; private final String shortId; + private final String rawFullId; + private final int hash; + private String alias; private Map varNameMap; @@ -29,6 +32,8 @@ public final class MethodInfo implements Comparable { this.argTypes = args; this.retType = retType; this.shortId = makeShortId(name, argTypes, retType); + this.rawFullId = declClass.makeRawFullName() + '.' + shortId; + this.hash = calcHashCode(); } public static MethodInfo fromRef(RootNode root, IMethodRef methodRef) { @@ -103,7 +108,7 @@ public final class MethodInfo implements Comparable { } public String getRawFullId() { - return declClass.makeRawFullName() + '.' + shortId; + return rawFullId; } /** @@ -172,9 +177,13 @@ public final class MethodInfo implements Comparable { return varNameMap != null && varNameMap.size() > 0; } + public int calcHashCode() { + return shortId.hashCode() + 31 * declClass.hashCode(); + } + @Override public int hashCode() { - return shortId.hashCode() + 31 * declClass.hashCode(); + return hash; } @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 e8cb426d0..2568f7987 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 @@ -111,7 +111,11 @@ public class RootNode { // sort classes by name, expect top classes before inner classes.sort(Comparator.comparing(ClassNode::getFullName)); initInnerClasses(); - LOG.info("Classes loaded: {}", classes.size()); + + // print stats for loaded classes + int mthCount = classes.stream().mapToInt(c -> c.getMethods().size()).sum(); + int insnsCount = classes.stream().flatMap(c -> c.getMethods().stream()).mapToInt(MethodNode::getInsnsCount).sum(); + LOG.info("Loaded classes: {}, methods: {}, instructions: {}", classes.size(), mthCount, insnsCount); } private void addDummyClass(IClassData classData, Exception exc) { diff --git a/jadx-core/src/main/java/jadx/core/utils/DebugUtils.java b/jadx-core/src/main/java/jadx/core/utils/DebugUtils.java index df9faef45..cbe7e34ee 100644 --- a/jadx-core/src/main/java/jadx/core/utils/DebugUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/DebugUtils.java @@ -1,11 +1,14 @@ package jadx.core.utils; import java.io.File; +import java.util.Comparator; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -18,13 +21,16 @@ import jadx.api.ICodeWriter; import jadx.api.impl.SimpleCodeWriter; import jadx.core.codegen.InsnGen; import jadx.core.codegen.MethodGen; +import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.IAttributeNode; +import jadx.core.dex.attributes.nodes.MethodOverrideAttr; import jadx.core.dex.nodes.BlockNode; import jadx.core.dex.nodes.IBlock; import jadx.core.dex.nodes.IContainer; import jadx.core.dex.nodes.IRegion; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; +import jadx.core.dex.nodes.RootNode; import jadx.core.dex.visitors.AbstractVisitor; import jadx.core.dex.visitors.DotGraphVisitor; import jadx.core.dex.visitors.IDexTreeVisitor; @@ -184,4 +190,23 @@ public class DebugUtils { public static void printStackTrace(String label) { LOG.debug("StackTrace: {}\n{}", label, Utils.getStackTrace(new Exception())); } + + public static void printMethodOverrideTop(RootNode root) { + LOG.debug("Methods override top 10:"); + root.getClasses().stream() + .flatMap(c -> c.getMethods().stream()) + .filter(m -> m.contains(AType.METHOD_OVERRIDE)) + .map(m -> m.get(AType.METHOD_OVERRIDE)) + .filter(o -> !o.getOverrideList().isEmpty()) + .filter(distinctByKey(methodOverrideAttr -> methodOverrideAttr.getRelatedMthNodes().size())) + .filter(distinctByKey(MethodOverrideAttr::getRelatedMthNodes)) + .sorted(Comparator.comparingInt(o -> -o.getRelatedMthNodes().size())) + .limit(10) + .forEach(o -> LOG.debug(" {} : {}", o.getRelatedMthNodes().size(), Utils.last(o.getOverrideList()))); + } + + private static Predicate distinctByKey(Function keyExtractor) { + Set seen = ConcurrentHashMap.newKeySet(); + return t -> seen.add(keyExtractor.apply(t)); + } }