diff --git a/jadx-core/src/main/java/jadx/api/DefaultJadxArgs.java b/jadx-core/src/main/java/jadx/api/DefaultJadxArgs.java index 9618dd29b..3b922fcf8 100644 --- a/jadx-core/src/main/java/jadx/api/DefaultJadxArgs.java +++ b/jadx-core/src/main/java/jadx/api/DefaultJadxArgs.java @@ -56,12 +56,12 @@ public class DefaultJadxArgs implements IJadxArgs { @Override public int getDeobfuscationMinLength() { - return Integer.MIN_VALUE+1; + return Integer.MIN_VALUE + 1; } @Override public int getDeobfuscationMaxLength() { - return Integer.MAX_VALUE-1; + return Integer.MAX_VALUE - 1; } @Override diff --git a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java index 0d6e9be84..dc5fe8fb8 100644 --- a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java +++ b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java @@ -4,8 +4,6 @@ import jadx.core.Jadx; import jadx.core.ProcessClass; import jadx.core.codegen.CodeGen; import jadx.core.codegen.CodeWriter; -import jadx.core.deobf.DefaultDeobfuscator; -import jadx.core.deobf.Deobfuscator; import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.RootNode; import jadx.core.dex.visitors.IDexTreeVisitor; @@ -93,6 +91,8 @@ public final class JadxDecompiler { resources = null; xmlParser = null; root = null; + passes = null; + codeGen = null; } public static String getVersion() { @@ -254,56 +254,27 @@ public final class JadxDecompiler { void parse() throws DecodeException { reset(); - root = new RootNode(); + init(); + + root = new RootNode(args); LOG.info("loading ..."); root.load(inputFiles); - if (args.isDeobfuscationOn()) { - final String firstInputFileName = inputFiles.get(0).getFile().getAbsolutePath(); - final String inputPath = org.apache.commons.io.FilenameUtils.getFullPathNoEndSeparator( - firstInputFileName); - final String inputName = org.apache.commons.io.FilenameUtils.getBaseName(firstInputFileName); - - final File deobfuscationMapFile = new File(inputPath, inputName + ".jobf"); - - DefaultDeobfuscator deobfuscator = new DefaultDeobfuscator(); - - if (deobfuscationMapFile.exists()) { - try { - deobfuscator.load(deobfuscationMapFile); - } catch (IOException e) { - LOG.error("Failed to load deobfuscation map file '{}'", - deobfuscationMapFile.getAbsolutePath()); - } - } - - deobfuscator.setInputData(root.getDexNodes()); - deobfuscator.setMinNameLength(args.getDeobfuscationMinLength()); - deobfuscator.setMaxNameLength(args.getDeobfuscationMaxLength()); - - deobfuscator.process(); - - try { - if (deobfuscationMapFile.exists()) { - if (args.isDeobfuscationForceSave()) { - deobfuscator.save(deobfuscationMapFile); - } else { - LOG.warn("Deobfuscation map file '{}' exists. Use command line option '--deobf=rewrite-cfg'" + - " to rewrite it", deobfuscationMapFile.getAbsolutePath()); - } - } else { - deobfuscator.save(deobfuscationMapFile); - } - } catch (IOException e) { - LOG.error("Failed to load deobfuscation map file '{}'", - deobfuscationMapFile.getAbsolutePath()); - } - - Deobfuscator.setDeobfuscator(deobfuscator); - } - + root.initClassPath(); root.loadResources(getResources()); root.initAppResClass(); + + initVisitors(); + } + + private void initVisitors() { + for (IDexTreeVisitor pass : passes) { + try { + pass.init(root); + } catch (Exception e) { + LOG.error("Visitor init failed: {}", pass.getClass().getSimpleName(), e); + } + } } void processClass(ClassNode cls) { diff --git a/jadx-core/src/main/java/jadx/api/JavaClass.java b/jadx-core/src/main/java/jadx/api/JavaClass.java index 706c2e80f..7cca1b1af 100644 --- a/jadx-core/src/main/java/jadx/api/JavaClass.java +++ b/jadx-core/src/main/java/jadx/api/JavaClass.java @@ -1,7 +1,6 @@ package jadx.api; import jadx.core.codegen.CodeWriter; -import jadx.core.deobf.Deobfuscator; import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.nodes.LineAttrNode; import jadx.core.dex.info.AccessInfo; @@ -116,6 +115,9 @@ public final class JavaClass implements JavaNode { public CodePosition getDefinitionPosition(int line, int offset) { Map map = getCodeAnnotations(); + if (map.isEmpty()) { + return null; + } Object obj = map.get(new CodePosition(line, offset)); if (!(obj instanceof LineAttrNode)) { return null; @@ -151,16 +153,16 @@ public final class JavaClass implements JavaNode { @Override public String getName() { - return Deobfuscator.instance().getClassShortName(cls); + return cls.getShortName(); } @Override public String getFullName() { - return Deobfuscator.instance().getClassFullName(cls); + return cls.getFullName(); } public String getPackage() { - return Deobfuscator.instance().getPackageName(cls.getPackage()); + return cls.getPackage(); } @Override diff --git a/jadx-core/src/main/java/jadx/api/JavaPackage.java b/jadx-core/src/main/java/jadx/api/JavaPackage.java index 1256dc061..9af545729 100644 --- a/jadx-core/src/main/java/jadx/api/JavaPackage.java +++ b/jadx-core/src/main/java/jadx/api/JavaPackage.java @@ -1,7 +1,5 @@ package jadx.api; -import jadx.core.deobf.Deobfuscator; - import java.util.List; import org.jetbrains.annotations.NotNull; @@ -11,7 +9,7 @@ public final class JavaPackage implements JavaNode, Comparable { private final List classes; JavaPackage(String name, List classes) { - this.name = Deobfuscator.instance().getPackageName(name); + this.name = name; this.classes = classes; } diff --git a/jadx-core/src/main/java/jadx/core/Jadx.java b/jadx-core/src/main/java/jadx/core/Jadx.java index b083b25d7..e832b22ae 100644 --- a/jadx-core/src/main/java/jadx/core/Jadx.java +++ b/jadx-core/src/main/java/jadx/core/Jadx.java @@ -14,6 +14,7 @@ import jadx.core.dex.visitors.MethodInlineVisitor; import jadx.core.dex.visitors.ModVisitor; import jadx.core.dex.visitors.PrepareForCodeGen; import jadx.core.dex.visitors.ReSugarCode; +import jadx.core.dex.visitors.RenameVisitor; import jadx.core.dex.visitors.SimplifyVisitor; import jadx.core.dex.visitors.blocksmaker.BlockExceptionHandler; import jadx.core.dex.visitors.blocksmaker.BlockFinallyExtract; @@ -106,6 +107,10 @@ public class Jadx { passes.add(new ProcessVariables()); passes.add(new DependencyCollector()); + + if (args.isDeobfuscationOn()) { + passes.add(new RenameVisitor()); + } } return passes; } diff --git a/jadx-core/src/main/java/jadx/core/ProcessClass.java b/jadx-core/src/main/java/jadx/core/ProcessClass.java index 08f686f46..bb81cc3ab 100644 --- a/jadx-core/src/main/java/jadx/core/ProcessClass.java +++ b/jadx-core/src/main/java/jadx/core/ProcessClass.java @@ -33,9 +33,7 @@ public final class ProcessClass { for (IDexTreeVisitor visitor : passes) { DepthTraversal.visit(visitor, cls); } - for (ClassNode clsNode : cls.getDependencies()) { - process(clsNode, passes, null); - } + processDependencies(cls, passes); cls.setState(PROCESSED); } if (cls.getState() == PROCESSED && codeGen != null) { @@ -52,4 +50,14 @@ public final class ProcessClass { } } } + + static void processDependencies(ClassNode cls, List passes) { + for (ClassNode depCls : cls.getDependencies()) { + if (cls.getTopParentClass() == cls) { + // ignore inner classes of this class + continue; + } + process(depCls, passes, null); + } + } } diff --git a/jadx-core/src/main/java/jadx/core/clsp/ClsSet.java b/jadx-core/src/main/java/jadx/core/clsp/ClsSet.java index ffb284352..fdcf7358b 100644 --- a/jadx-core/src/main/java/jadx/core/clsp/ClsSet.java +++ b/jadx-core/src/main/java/jadx/core/clsp/ClsSet.java @@ -1,6 +1,6 @@ package jadx.core.clsp; -import jadx.core.dex.info.ClassInfo; +import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.RootNode; import jadx.core.utils.exceptions.DecodeException; @@ -77,15 +77,15 @@ public class ClsSet { public static NClass[] makeParentsArray(ClassNode cls, Map names) { List parents = new ArrayList(1 + cls.getInterfaces().size()); - ClassInfo superClass = cls.getSuperClass(); + ArgType superClass = cls.getSuperClass(); if (superClass != null) { - NClass c = getCls(superClass.getRawName(), names); + NClass c = getCls(superClass.getObject(), names); if (c != null) { parents.add(c); } } - for (ClassInfo iface : cls.getInterfaces()) { - NClass c = getCls(iface.getRawName(), names); + for (ArgType iface : cls.getInterfaces()) { + NClass c = getCls(iface.getObject(), names); if (c != null) { parents.add(c); } diff --git a/jadx-core/src/main/java/jadx/core/clsp/ClspGraph.java b/jadx-core/src/main/java/jadx/core/clsp/ClspGraph.java index 8074110d8..168770b72 100644 --- a/jadx-core/src/main/java/jadx/core/clsp/ClspGraph.java +++ b/jadx-core/src/main/java/jadx/core/clsp/ClspGraph.java @@ -45,16 +45,10 @@ public class ClspGraph { throw new JadxRuntimeException("Classpath must be loaded first"); } int size = classes.size(); - for (ClassNode cls : classes) { - size += cls.getInnerClasses().size(); - } NClass[] nClasses = new NClass[size]; int k = 0; for (ClassNode cls : classes) { nClasses[k++] = addClass(cls); - for (ClassNode inner : cls.getInnerClasses()) { - nClasses[k++] = addClass(inner); - } } for (int i = 0; i < size; i++) { nClasses[i].setParents(ClsSet.makeParentsArray(classes.get(i), nameMap)); @@ -62,8 +56,9 @@ public class ClspGraph { } private NClass addClass(ClassNode cls) { - NClass nClass = new NClass(cls.getRawName(), -1); - nameMap.put(cls.getRawName(), nClass); + String rawName = cls.getRawName(); + NClass nClass = new NClass(rawName, -1); + nameMap.put(rawName, nClass); return nClass; } diff --git a/jadx-core/src/main/java/jadx/core/clsp/ConvertToClsSet.java b/jadx-core/src/main/java/jadx/core/clsp/ConvertToClsSet.java index 654885b0f..483102a53 100644 --- a/jadx-core/src/main/java/jadx/core/clsp/ConvertToClsSet.java +++ b/jadx-core/src/main/java/jadx/core/clsp/ConvertToClsSet.java @@ -1,5 +1,6 @@ package jadx.core.clsp; +import jadx.api.DefaultJadxArgs; import jadx.core.dex.nodes.RootNode; import jadx.core.utils.exceptions.DecodeException; import jadx.core.utils.files.InputFile; @@ -42,7 +43,7 @@ public class ConvertToClsSet { LOG.info("Loaded: {}", inputFile.getFile()); } - RootNode root = new RootNode(); + RootNode root = new RootNode(new DefaultJadxArgs()); root.load(inputFiles); ClsSet set = new ClsSet(); diff --git a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java index 663b29c0a..bd9161f10 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java @@ -1,8 +1,6 @@ package jadx.core.codegen; import jadx.api.IJadxArgs; -import jadx.core.Consts; -import jadx.core.deobf.Deobfuscator; import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.AttrNode; @@ -81,14 +79,14 @@ public class ClassGen { CodeWriter clsCode = new CodeWriter(); if (!"".equals(cls.getPackage())) { - clsCode.add("package ").add(Deobfuscator.instance().getPackageName(cls.getPackage())).add(';'); + clsCode.add("package ").add(cls.getPackage()).add(';'); clsCode.newLine(); } int importsCount = imports.size(); if (importsCount != 0) { List sortImports = new ArrayList(importsCount); for (ClassInfo ic : imports) { - sortImports.add(Deobfuscator.instance().getClassFullName(ic)); + sortImports.add(ic.getAlias().getFullName()); } Collections.sort(sortImports); @@ -126,7 +124,7 @@ public class ClassGen { } // 'static' modifier not allowed for top classes (not inner) - if (!cls.getClassInfo().isInner()) { + if (!cls.getAlias().isInner()) { af = af.remove(AccessFlags.ACC_STATIC); } @@ -143,15 +141,15 @@ public class ClassGen { } else { clsCode.add("class "); } - clsCode.add(Deobfuscator.instance().getClassShortName(cls)); + clsCode.add(cls.getShortName()); addGenericMap(clsCode, cls.getGenericMap()); clsCode.add(' '); - ClassInfo sup = cls.getSuperClass(); + ArgType sup = cls.getSuperClass(); if (sup != null - && !sup.getFullName().equals(Consts.CLASS_OBJECT) - && !sup.getFullName().equals(Consts.CLASS_ENUM)) { + && !sup.equals(ArgType.OBJECT) + && !sup.getObject().equals(ArgType.ENUM.getObject())) { clsCode.add("extends "); useClass(clsCode, sup); clsCode.add(' '); @@ -163,8 +161,8 @@ public class ClassGen { } else { clsCode.add("implements "); } - for (Iterator it = cls.getInterfaces().iterator(); it.hasNext(); ) { - ClassInfo interf = it.next(); + for (Iterator it = cls.getInterfaces().iterator(); it.hasNext(); ) { + ArgType interf = it.next(); useClass(clsCode, interf); if (it.hasNext()) { clsCode.add(", "); @@ -192,7 +190,7 @@ public class ClassGen { if (type.isGenericType()) { code.add(type.getObject()); } else { - useClass(code, ClassInfo.fromType(cls.dex(), type)); + useClass(code, type); } if (list != null && !list.isEmpty()) { code.add(" extends "); @@ -201,7 +199,7 @@ public class ClassGen { if (g.isGenericType()) { code.add(g.getObject()); } else { - useClass(code, ClassInfo.fromType(cls.dex(), g)); + useClass(code, g); } if (it.hasNext()) { code.add(" & "); @@ -407,7 +405,7 @@ public class ClassGen { if (type.isGenericType()) { code.add(type.getObject()); } else { - useClass(code, ClassInfo.fromType(cls.dex(), type)); + useClass(code, type); } } else if (stype == PrimitiveType.ARRAY) { useType(code, type.getArrayElement()); @@ -417,14 +415,9 @@ public class ClassGen { } } - public void useClass(CodeWriter code, ClassInfo classInfo) { - ClassNode classNode = cls.dex().resolveClass(classInfo); - if (classNode != null) { - code.attachAnnotation(classNode); - } - String baseClass = useClassInternal(cls.getClassInfo(), classInfo); - code.add(baseClass); - ArgType[] generics = classInfo.getType().getGenericTypes(); + public void useClass(CodeWriter code, ArgType type) { + useClass(code, ClassInfo.extCls(cls.dex(), type)); + ArgType[] generics = type.getGenericTypes(); if (generics != null) { code.add('<'); int len = generics.length; @@ -449,54 +442,63 @@ public class ClassGen { } } - private String useClassInternal(ClassInfo useCls, ClassInfo classInfo) { - String fullName = classInfo.getFullName(); + public void useClass(CodeWriter code, ClassInfo classInfo) { + ClassNode classNode = cls.dex().resolveClass(classInfo); + if (classNode != null) { + code.attachAnnotation(classNode); + } + String baseClass = useClassInternal(cls.getAlias(), classInfo.getAlias()); + code.add(baseClass); + } + + private String useClassInternal(ClassInfo useCls, ClassInfo extClsInfo) { + String fullName = extClsInfo.getFullName(); if (fallback) { return fullName; } - fullName = Deobfuscator.instance().getClassFullName(classInfo); - String shortName = Deobfuscator.instance().getClassShortName(classInfo); - if (classInfo.getPackage().equals("java.lang") && classInfo.getParentClass() == null) { + fullName = extClsInfo.getFullName(); + String shortName = extClsInfo.getShortName(); + if (extClsInfo.getPackage().equals("java.lang") && extClsInfo.getParentClass() == null) { return shortName; } else { // don't add import if this class inner for current class - if (isClassInnerFor(classInfo, useCls)) { + if (isClassInnerFor(extClsInfo, useCls)) { return shortName; } // don't add import if this class from same package - if (classInfo.getPackage().equals(useCls.getPackage()) && !classInfo.isInner()) { + if (extClsInfo.getPackage().equals(useCls.getPackage()) && !extClsInfo.isInner()) { return shortName; } // don't add import if class not public (must be accessed using inheritance) - ClassNode classNode = cls.dex().resolveClass(classInfo); + ClassNode classNode = cls.dex().resolveClass(extClsInfo); if (classNode != null && !classNode.getAccessFlags().isPublic()) { return shortName; } - if (searchCollision(cls.dex(), useCls, classInfo)) { + if (searchCollision(cls.dex(), useCls, extClsInfo)) { return fullName; } - if (classInfo.getPackage().equals(useCls.getPackage())) { - fullName = Deobfuscator.instance().getClassName(classInfo); + if (extClsInfo.getPackage().equals(useCls.getPackage())) { + fullName = extClsInfo.getNameWithoutPackage(); } for (ClassInfo importCls : getImports()) { - if (!importCls.equals(classInfo) + if (!importCls.equals(extClsInfo) && importCls.getShortName().equals(shortName)) { - if (classInfo.isInner()) { - String parent = useClassInternal(useCls, classInfo.getParentClass()); + if (extClsInfo.isInner()) { + String parent = useClassInternal(useCls, extClsInfo.getParentClass().getAlias()); return parent + "." + shortName; } else { return fullName; } } } - addImport(classInfo); + addImport(extClsInfo); return shortName; } } private void addImport(ClassInfo classInfo) { if (parentGen != null) { - parentGen.addImport(classInfo); + parentGen.addImport(classInfo.getAlias()); } else { imports.add(classInfo); } @@ -530,7 +532,7 @@ public class ClassGen { if (classNode != null) { for (ClassNode inner : classNode.getInnerClasses()) { if (inner.getShortName().equals(shortName) - && !inner.getClassInfo().equals(searchCls)) { + && !inner.getAlias().equals(searchCls)) { return true; } } diff --git a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java index efd6f9243..9aa6ea261 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -162,14 +162,14 @@ public class InsnGen { public static void makeStaticFieldAccess(CodeWriter code, FieldInfo field, ClassGen clsGen) { ClassInfo declClass = field.getDeclClass(); - boolean fieldFromThisClass = clsGen.getClassNode().getFullName().startsWith(declClass.getFullName()); + boolean fieldFromThisClass = clsGen.getClassNode().getClassInfo().equals(declClass); if (!fieldFromThisClass) { // Android specific resources class handler ClassInfo parentClass = declClass.getParentClass(); if (parentClass != null && parentClass.getShortName().equals("R")) { clsGen.useClass(code, parentClass); code.add('.'); - code.add(declClass.getShortName()); + code.add(declClass.getAlias().getShortName()); } else { clsGen.useClass(code, declClass); } @@ -186,6 +186,10 @@ public class InsnGen { makeStaticFieldAccess(code, field, mgen.getClassGen()); } + public void useClass(CodeWriter code, ArgType type) { + mgen.getClassGen().useClass(code, type); + } + public void useClass(CodeWriter code, ClassInfo cls) { mgen.getClassGen().useClass(code, cls); } @@ -200,9 +204,6 @@ public class InsnGen { protected boolean makeInsn(InsnNode insn, CodeWriter code, Flags flag) throws CodegenException { try { - if (insn.getType() == InsnType.NOP) { - return false; - } Set state = EnumSet.noneOf(Flags.class); if (flag == Flags.BODY_ONLY || flag == Flags.BODY_ONLY_NOWRAP) { state.add(flag); @@ -531,7 +532,7 @@ public class InsnGen { ClassNode cls = mth.dex().resolveClass(insn.getClassType()); if (cls != null && cls.isAnonymous() && !fallback) { // anonymous class construction - ClassInfo parent; + ArgType parent; if (cls.getInterfaces().size() == 1) { parent = cls.getInterfaces().get(0); } else { @@ -600,7 +601,7 @@ public class InsnGen { break; case STATIC: - ClassInfo insnCls = mth.getParentClass().getClassInfo(); + ClassInfo insnCls = mth.getParentClass().getAlias(); ClassInfo declClass = callMth.getDeclClass(); if (!insnCls.equals(declClass)) { useClass(code, declClass); diff --git a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java index 6771c7446..5b8b003a0 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java @@ -1,11 +1,11 @@ package jadx.core.codegen; -import jadx.core.deobf.Deobfuscator; import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.annotations.MethodParameters; import jadx.core.dex.attributes.nodes.JadxErrorAttr; import jadx.core.dex.info.AccessInfo; +import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.nodes.InsnNode; @@ -86,7 +86,7 @@ public class MethodGen { code.add(' '); } if (mth.getAccessFlags().isConstructor()) { - code.add(Deobfuscator.instance().getClassShortName(classGen.getClassNode())); // constructor + code.add(classGen.getClassNode().getShortName()); // constructor } else { classGen.useType(code, mth.getReturnType()); code.add(' '); @@ -209,7 +209,7 @@ public class MethodGen { public static void addFallbackInsns(CodeWriter code, MethodNode mth, InsnNode[] insnArr, boolean addLabels) { InsnGen insnGen = new InsnGen(getFallbackMethodGen(mth), true); for (InsnNode insn : insnArr) { - if (insn == null) { + if (insn == null || insn.getType() == InsnType.NOP) { continue; } if (addLabels && (insn.contains(AType.JUMP) || insn.contains(AType.EXC_HANDLER))) { diff --git a/jadx-core/src/main/java/jadx/core/codegen/NameGen.java b/jadx-core/src/main/java/jadx/core/codegen/NameGen.java index 7a4e7b7c9..321648c5f 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/NameGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/NameGen.java @@ -158,8 +158,8 @@ public class NameGen { if (alias != null) { return alias; } - ClassInfo clsInfo = ClassInfo.fromType(mth.dex(), type); - String shortName = clsInfo.getShortName(); + ClassInfo extClsInfo = ClassInfo.extCls(mth.dex(), type); + String shortName = extClsInfo.getShortName(); String vName = fromName(shortName); if (vName != null) { return vName; @@ -223,12 +223,12 @@ public class NameGen { return null; } - private static String makeNameFromInvoke(MethodInfo callMth) { + private String makeNameFromInvoke(MethodInfo callMth) { String name = callMth.getName(); if (name.startsWith("get") || name.startsWith("set")) { return fromName(name.substring(3)); } - ArgType declType = callMth.getDeclClass().getType(); + ArgType declType = callMth.getDeclClass().getAlias().getType(); if ("iterator".equals(name)) { return "it"; } diff --git a/jadx-core/src/main/java/jadx/core/deobf/DefaultDeobfuscator.java b/jadx-core/src/main/java/jadx/core/deobf/DefaultDeobfuscator.java deleted file mode 100644 index 36ef399e9..000000000 --- a/jadx-core/src/main/java/jadx/core/deobf/DefaultDeobfuscator.java +++ /dev/null @@ -1,415 +0,0 @@ -package jadx.core.deobf; - -import jadx.core.dex.info.ClassInfo; -import jadx.core.dex.nodes.ClassNode; -import jadx.core.dex.nodes.DexNode; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; - -import org.apache.commons.io.FileUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class DefaultDeobfuscator implements IDeobfuscator { - private static final Logger LOG = LoggerFactory.getLogger(DefaultDeobfuscator.class); - - private static final boolean DEBUG = false; - - public static final char classNameSepearator = '.'; - - private int maxLength = 40; - private int minLength = 2; - - private int pkgIndex = 0; - private int clsIndex = 0; - - private List dexNodes; - private static PackageNode rootPackage = new PackageNode(""); - - private static final String MAP_FILE_CHARSET = "UTF-8"; - - /** - * Gets package node for full package name - * - * @param fullPkgName full package name - * @param _creat if {@code true} then will create all absent objects - * - * @return package node object or {@code null} if no package found and _creat set to {@code false} - */ - public static PackageNode getPackageNode(String fullPkgName, boolean _creat) { - if (fullPkgName.isEmpty() || fullPkgName.equals(classNameSepearator)) { - return rootPackage; - } - - PackageNode result = rootPackage; - PackageNode parentNode; - do { - String pkgName; - int idx = fullPkgName.indexOf(classNameSepearator); - - if (idx > -1) { - pkgName = fullPkgName.substring(0, idx); - fullPkgName = fullPkgName.substring(idx+1); - } else { - pkgName = fullPkgName; - fullPkgName = ""; - } - - parentNode = result; - result = result.getInnerPackageByName(pkgName); - if ((result == null) && (_creat)) { - result = new PackageNode(pkgName); - parentNode.addInnerPackage(result); - } - } while (!fullPkgName.isEmpty() && (result != null)); - - return result; - } - - private class DefaultDeobfuscatorClassInfo { - public ClassNode cls; - public PackageNode pkg; - public String alias; - - public DefaultDeobfuscatorClassInfo(ClassNode cls, PackageNode pkg) { - this.cls = cls; - this.pkg = pkg; - } - - public String getNameWithoutPackage(DefaultDeobfuscatorClassInfo deobfClsInfo) { - final ClassNode clsNode = deobfClsInfo.cls; - String prefix; - ClassNode parentClass = clsNode.getParentClass(); - if (parentClass != clsNode) { - DefaultDeobfuscatorClassInfo parentDeobfClassInfo = DefaultDeobfuscator.clsMap.get(parentClass.getClassInfo()); - - if (parentDeobfClassInfo != null) { - prefix = getNameWithoutPackage(parentDeobfClassInfo) + DefaultDeobfuscator.classNameSepearator; - } else { - prefix = DefaultDeobfuscator.getNameWithoutPackage(parentClass.getClassInfo()) + DefaultDeobfuscator.classNameSepearator; - } - } else { - prefix = ""; - } - - return prefix + ((deobfClsInfo.alias != null) ? deobfClsInfo.alias : deobfClsInfo.cls.getShortName()); - } - - public String getFullName() { - return pkg.getFullAlias() + DefaultDeobfuscator.classNameSepearator + getNameWithoutPackage(this); - } - } - - private Map preloadClsMap = Collections.emptyMap(); - private static Map clsMap = new HashMap(); - - public static String getNameWithoutPackage(ClassInfo clsInfo) { - String prefix; - ClassInfo parentClsInfo = clsInfo.getParentClass(); - if (parentClsInfo != null) { - DefaultDeobfuscatorClassInfo parentDeobfClsInfo = DefaultDeobfuscator.clsMap.get(parentClsInfo); - - if (parentDeobfClsInfo != null) { - prefix = parentDeobfClsInfo.getNameWithoutPackage(parentDeobfClsInfo) + DefaultDeobfuscator.classNameSepearator; - } else { - prefix = getNameWithoutPackage(parentClsInfo) + DefaultDeobfuscator.classNameSepearator; - } - } else { - prefix = ""; - } - return prefix + clsInfo.getShortName(); - } - - private void doClass(ClassNode cls) { - final String pkgFullName = cls.getPackage(); - - PackageNode pkg = getPackageNode(pkgFullName, true); - doPkg(pkg, pkgFullName); - - if (preloadClsMap.containsKey(cls.getFullName())) { - DefaultDeobfuscatorClassInfo clsInfo = new DefaultDeobfuscatorClassInfo(cls, pkg); - clsInfo.alias = preloadClsMap.get(cls.getFullName()); - clsMap.put(cls.getClassInfo(), clsInfo); - return; - } - - if (clsMap.containsKey(cls)) { - return; - } - - final String className = cls.getShortName(); - if (shouldRename(className)) { - DefaultDeobfuscatorClassInfo clsInfo = new DefaultDeobfuscatorClassInfo(cls, pkg); - - clsInfo.alias = String.format("C%04d%s", clsIndex++, short4LongName(className)); - clsMap.put(cls.getClassInfo(), clsInfo); - } - } - - private String short4LongName(String name) { - if (name.length() > maxLength) { - return "x" + Integer.toHexString(name.hashCode()); - } else { - return name; - } - } - - private Set pkgSet = new TreeSet(); - - private void doPkg(PackageNode pkg, String fullName) { - if (pkgSet.contains(fullName)) { - return; - } - pkgSet.add(fullName); - - // doPkg for all parent packages except root that not hasAlisas - PackageNode parentPkg = pkg.getParentPackage(); - while (!parentPkg.getName().isEmpty()) { - if (!parentPkg.hasAlias()) { - doPkg(parentPkg, parentPkg.getFullName()); - } - parentPkg = parentPkg.getParentPackage(); - } - - final String pkgName = pkg.getName(); - if (shouldRename(pkgName) && !pkg.hasAlias()) { - final String pkgAlias = String.format("p%03d%s", pkgIndex++, short4LongName(pkgName)); - pkg.setAlias(pkgAlias); - } - } - - private void preprocess() { - if (dexNodes != null) { - for (DexNode dexNode : dexNodes) { - for (ClassNode cls : dexNode.getClasses()) { - doClass(cls); - } - } - } - } - - private boolean shouldRename(String s) { - return s.length() > maxLength || s.length() < minLength || NameMapper.isReserved(s); - } - - private void dumpClassAlias(ClassNode cls) { - PackageNode pkg = getPackageNode(cls.getPackage(), false); - - if (pkg != null) { - if (!cls.getFullName().equals(getClassFullName(cls))) { - LOG.info("Alias name for class '{}' is '{}'", cls.getFullName(), getClassFullName(cls)); - } - } else { - LOG.error("Can't find package node for '{}'", cls.getPackage()); - } - } - - private void dumpAlias() { - for (DexNode dexNode : dexNodes) { - for (ClassNode cls : dexNode.getClasses()) { - dumpClassAlias(cls); - } - } - } - - /** - * Sets input data for processing - * - * @param nodes - * - * @return @{code this} - */ - public DefaultDeobfuscator setInputData(List nodes) { - this.dexNodes = nodes; - return this; - } - - /** - * Sets minimum name length, if name length lesser than value, - * DefaultDeobfuscator will work - * - * @param value - * - * @return @{code this} - */ - public DefaultDeobfuscator setMinNameLength(int value) { - this.minLength = value; - return this; - } - - /** - * Sets maximum name length, if name length greater than value, - * DefaultDeobfuscator will work - * - * @param value - * - * @return @{code this} - */ - public DefaultDeobfuscator setMaxNameLength(int value) { - this.maxLength = value; - return this; - } - - /** - * Loads DefaultDeobfuscator presets - * - * @param config - * @throws IOException - */ - public void load(File mapFile) throws IOException { - if (mapFile.exists()) { - List lines = FileUtils.readLines(mapFile, MAP_FILE_CHARSET); - - for (String l : lines) { - if (l.startsWith("p ")) { - final String rule = l.substring(2); - final String va[] = rule.split("="); - - if (va.length == 2) { - PackageNode pkg = getPackageNode(va[0], true); - pkg.setAlias(va[1]); - } - } else if (l.startsWith("c ")) { - final String rule = l.substring(2); - final String va[] = rule.split("="); - - if (va.length == 2) { - if (preloadClsMap.isEmpty()) { - preloadClsMap = new HashMap(); - } - preloadClsMap.put(va[0], va[1]); - } - } - } - } - } - - public void process() { - preprocess(); - if (DEBUG) { - dumpAlias(); - } - - preloadClsMap.clear(); - preloadClsMap = Collections.emptyMap(); - } - - private static void dfsPackageName(List list, String prefix, PackageNode node) { - for (PackageNode pp : node.getInnerPackages()) { - dfsPackageName(list, prefix + '.' + node.getName(), pp); - } - - if (node.hasAlias()) { - list.add(String.format("p %s.%s=%s", prefix, node.getName(), node.getAlias())); - } - } - - /** - * Saves DefaultDeobfuscator presets - * - * @param mapFile - * @throws IOException - */ - public void save(File mapFile) throws IOException { - List list = new ArrayList(); - - // packages - for (PackageNode p : rootPackage.getInnerPackages()) { - for (PackageNode pp : p.getInnerPackages()) { - dfsPackageName(list, p.getName(), pp); - } - - if (p.hasAlias()) { - list.add(String.format("p %s=%s", p.getName(), p.getAlias())); - } - } - - // classes - for (DefaultDeobfuscatorClassInfo deobfClsInfo : clsMap.values()) { - if (deobfClsInfo.alias != null) { - list.add(String.format("c %s=%s", deobfClsInfo.cls.getFullName(), deobfClsInfo.alias)); - } - } - - FileUtils.writeLines(mapFile, MAP_FILE_CHARSET, list); - list.clear(); - } - - - @Override - public String getPackageName(String packageName) { - final PackageNode pkg = getPackageNode(packageName, false); - if (pkg != null) { - return pkg.getFullAlias(); - } - return packageName; - } - - @Override - public String getClassShortName(ClassNode cls) { - return getClassShortName(cls.getClassInfo()); - } - - @Override - public String getClassShortName(ClassInfo clsInfo) { - final DefaultDeobfuscatorClassInfo deobfClsInfo = clsMap.get(clsInfo); - if (deobfClsInfo != null) { - return (deobfClsInfo.alias != null) ? deobfClsInfo.alias : clsInfo.getShortName(); - } - - return clsInfo.getShortName(); - } - - @Override - public String getClassName(ClassNode cls) { - return getClassName(cls.getClassInfo()); - } - - @Override - public String getClassName(ClassInfo clsInfo) { - final DefaultDeobfuscatorClassInfo deobfClsInfo = clsMap.get(clsInfo); - if (deobfClsInfo != null) { - return deobfClsInfo.getNameWithoutPackage(deobfClsInfo); - } - - return getNameWithoutPackage(clsInfo); - } - - @Override - public String getClassFullName(ClassNode cls) { - return getClassFullName(cls.getClassInfo()); - } - - @Override - public String getClassFullName(ClassInfo clsInfo) { - final DefaultDeobfuscatorClassInfo deobfClsInfo = clsMap.get(clsInfo); - if (deobfClsInfo != null) { - return deobfClsInfo.getFullName(); - } - - return getPackageName(clsInfo.getPackage()) + DefaultDeobfuscator.classNameSepearator + getClassName(clsInfo); - } - - @Override - public String getClassFullPath(ClassInfo clsInfo) { - final DefaultDeobfuscatorClassInfo deobfClsInfo = clsMap.get(clsInfo); - if (deobfClsInfo != null) { - return deobfClsInfo.pkg.getFullAlias().replace(DefaultDeobfuscator.classNameSepearator, File.separatorChar) - + File.separatorChar - + deobfClsInfo.getNameWithoutPackage(deobfClsInfo).replace(DefaultDeobfuscator.classNameSepearator, '_'); - } - - - return getPackageName(clsInfo.getPackage()).replace('.', File.separatorChar) - + File.separatorChar - + clsInfo.getNameWithoutPackage().replace('.', '_'); - } -} 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 a9e966b98..93e2f107b 100644 --- a/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java +++ b/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java @@ -1,37 +1,365 @@ package jadx.core.deobf; +import jadx.api.IJadxArgs; +import jadx.core.dex.info.ClassInfo; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.DexNode; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import org.apache.commons.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class Deobfuscator { + private static final Logger LOG = LoggerFactory.getLogger(Deobfuscator.class); - private static final StubDeobfuscator stubDeobfuscator; - private static IDeobfuscator deobfuscatorInstance; + private static final boolean DEBUG = false; - static { - stubDeobfuscator = new StubDeobfuscator(); - deobfuscatorInstance = stubDeobfuscator; - } - - /** - * Gets instance of active deobfuscator - * - * @return deobfuscator instance - */ - public static IDeobfuscator instance() { - return deobfuscatorInstance; + private static final String MAP_FILE_CHARSET = "UTF-8"; + private static final String classNameSeparator = "."; + private static final String innerClassSeparator = "$"; + + private final Map clsMap = new HashMap(); + private final IJadxArgs args; + private final File deobfMapFile; + private final List dexNodes; + + private int maxLength = 40; + private int minLength = 2; + private int pkgIndex = 0; + private int clsIndex = 0; + + private PackageNode rootPackage = new PackageNode(""); + private Map preLoadClsMap = Collections.emptyMap(); + + public Deobfuscator(IJadxArgs args, List dexNodes, File deobfMapFile) { + this.args = args; + this.dexNodes = dexNodes; + this.deobfMapFile = deobfMapFile; + + this.minLength = args.getDeobfuscationMinLength(); + this.maxLength = args.getDeobfuscationMaxLength(); } - /** - * Sets active deobfuscator - * - * @param deobfuscator object that makes deobfuscation or {@code null} - * to set stub deobfuscator - * - */ - public static void setDeobfuscator(IDeobfuscator deobfuscator) { - if (deobfuscator != null) { - deobfuscatorInstance = deobfuscator; - } else { - deobfuscatorInstance = stubDeobfuscator; + public void execute() { + if (deobfMapFile.exists()) { + try { + load(); + } catch (IOException e) { + LOG.error("Failed to load deobfuscation map file '{}'", deobfMapFile.getAbsolutePath(), e); + } + } + process(); + try { + if (deobfMapFile.exists()) { + if (args.isDeobfuscationForceSave()) { + save(); + } else { + LOG.warn("Deobfuscation map file '{}' exists. Use command line option '--deobf-rewrite-cfg' to rewrite it", + deobfMapFile.getAbsolutePath()); + } + } else { + save(); + } + } catch (IOException e) { + LOG.error("Failed to load deobfuscation map file '{}'", deobfMapFile.getAbsolutePath(), e); } } + public void process() { + preProcess(); + if (DEBUG) { + dumpAlias(); + } + preLoadClsMap.clear(); + preLoadClsMap = Collections.emptyMap(); + + for (DexNode dexNode : dexNodes) { + for (ClassNode classNode : dexNode.getClasses()) { + ClassInfo clsInfo = classNode.getClassInfo(); + String fullName = getClassFullName(clsInfo); + clsInfo.rename(dexNode, fullName); + } + } + } + + /** + * Gets package node for full package name + * + * @param fullPkgName full package name + * @param create if {@code true} then will create all absent objects + * @return package node object or {@code null} if no package found and create set to {@code false} + */ + public PackageNode getPackageNode(String fullPkgName, boolean create) { + if (fullPkgName.isEmpty() || fullPkgName.equals(classNameSeparator)) { + return rootPackage; + } + PackageNode result = rootPackage; + PackageNode parentNode; + do { + String pkgName; + int idx = fullPkgName.indexOf(classNameSeparator); + + if (idx > -1) { + pkgName = fullPkgName.substring(0, idx); + fullPkgName = fullPkgName.substring(idx + 1); + } else { + pkgName = fullPkgName; + fullPkgName = ""; + } + parentNode = result; + result = result.getInnerPackageByName(pkgName); + if ((result == null) && (create)) { + result = new PackageNode(pkgName); + parentNode.addInnerPackage(result); + } + } while (!fullPkgName.isEmpty() && (result != null)); + + return result; + } + + private final class DeobfClsInfo { + public ClassNode cls; + public PackageNode pkg; + public String alias; + + public DeobfClsInfo(ClassNode cls, PackageNode pkg) { + this.cls = cls; + this.pkg = pkg; + } + + public String makeNameWithoutPkg() { + String prefix; + ClassNode parentClass = cls.getParentClass(); + if (parentClass != cls) { + DeobfClsInfo parentDeobfClsInfo = clsMap.get(parentClass.getClassInfo()); + if (parentDeobfClsInfo != null) { + prefix = parentDeobfClsInfo.makeNameWithoutPkg(); + } else { + prefix = getNameWithoutPackage(parentClass.getClassInfo()); + } + prefix += innerClassSeparator; + } else { + prefix = ""; + } + + return prefix + ((this.alias != null) ? this.alias : this.cls.getShortName()); + } + + public String getFullName() { + return pkg.getFullAlias() + classNameSeparator + makeNameWithoutPkg(); + } + } + + public String getNameWithoutPackage(ClassInfo clsInfo) { + String prefix; + ClassInfo parentClsInfo = clsInfo.getParentClass(); + if (parentClsInfo != null) { + DeobfClsInfo parentDeobfClsInfo = clsMap.get(parentClsInfo); + if (parentDeobfClsInfo != null) { + prefix = parentDeobfClsInfo.makeNameWithoutPkg(); + } else { + prefix = getNameWithoutPackage(parentClsInfo); + } + prefix += innerClassSeparator; + } else { + prefix = ""; + } + return prefix + clsInfo.getShortName(); + } + + private void doClass(ClassNode cls) { + final String pkgFullName = cls.getClassInfo().getPackage(); + + PackageNode pkg = getPackageNode(pkgFullName, true); + doPkg(pkg, pkgFullName); + + if (preLoadClsMap.containsKey(cls.getClassInfo().getFullName())) { + DeobfClsInfo clsInfo = new DeobfClsInfo(cls, pkg); + clsInfo.alias = preLoadClsMap.get(cls.getFullName()); + clsMap.put(cls.getClassInfo(), clsInfo); + return; + } + + if (clsMap.containsKey(cls.getClassInfo())) { + return; + } + + final String className = cls.getClassInfo().getShortName(); + if (shouldRename(className)) { + DeobfClsInfo clsInfo = new DeobfClsInfo(cls, pkg); + clsInfo.alias = String.format("C%04d%s", clsIndex++, short4LongName(className)); + clsMap.put(cls.getClassInfo(), clsInfo); + } + } + + private String short4LongName(String name) { + if (name.length() > maxLength) { + return "x" + Integer.toHexString(name.hashCode()); + } else { + return name; + } + } + + private Set pkgSet = new TreeSet(); + + private void doPkg(PackageNode pkg, String fullName) { + if (pkgSet.contains(fullName)) { + return; + } + pkgSet.add(fullName); + + // doPkg for all parent packages except root that not hasAlisas + PackageNode parentPkg = pkg.getParentPackage(); + while (!parentPkg.getName().isEmpty()) { + if (!parentPkg.hasAlias()) { + doPkg(parentPkg, parentPkg.getFullName()); + } + parentPkg = parentPkg.getParentPackage(); + } + + final String pkgName = pkg.getName(); + if (shouldRename(pkgName) && !pkg.hasAlias()) { + final String pkgAlias = String.format("p%03d%s", pkgIndex++, short4LongName(pkgName)); + pkg.setAlias(pkgAlias); + } + } + + private void preProcess() { + if (dexNodes != null) { + for (DexNode dexNode : dexNodes) { + for (ClassNode cls : dexNode.getClasses()) { + doClass(cls); + } + } + } + } + + private boolean shouldRename(String s) { + return s.length() > maxLength || s.length() < minLength || NameMapper.isReserved(s); + } + + private void dumpClassAlias(ClassNode cls) { + PackageNode pkg = getPackageNode(cls.getPackage(), false); + + if (pkg != null) { + if (!cls.getFullName().equals(getClassFullName(cls))) { + LOG.info("Alias name for class '{}' is '{}'", cls.getFullName(), getClassFullName(cls)); + } + } else { + LOG.error("Can't find package node for '{}'", cls.getPackage()); + } + } + + private void dumpAlias() { + for (DexNode dexNode : dexNodes) { + for (ClassNode cls : dexNode.getClasses()) { + dumpClassAlias(cls); + } + } + } + + /** + * Loads deobfuscator presets + * + * @throws IOException + */ + public void load() throws IOException { + if (!deobfMapFile.exists()) { + return; + } + List lines = FileUtils.readLines(deobfMapFile, MAP_FILE_CHARSET); + for (String l : lines) { + if (l.startsWith("p ")) { + final String rule = l.substring(2); + final String va[] = rule.split("="); + + if (va.length == 2) { + PackageNode pkg = getPackageNode(va[0], true); + pkg.setAlias(va[1]); + } + } else if (l.startsWith("c ")) { + final String rule = l.substring(2); + final String va[] = rule.split("="); + + if (va.length == 2) { + if (preLoadClsMap.isEmpty()) { + preLoadClsMap = new HashMap(); + } + preLoadClsMap.put(va[0], va[1]); + } + } + } + } + + private static void dfsPackageName(List list, String prefix, PackageNode node) { + for (PackageNode pp : node.getInnerPackages()) { + dfsPackageName(list, prefix + '.' + node.getName(), pp); + } + if (node.hasAlias()) { + list.add(String.format("p %s.%s=%s", prefix, node.getName(), node.getAlias())); + } + } + + /** + * Saves DefaultDeobfuscator presets + */ + public void save() throws IOException { + List list = new ArrayList(); + // packages + for (PackageNode p : rootPackage.getInnerPackages()) { + for (PackageNode pp : p.getInnerPackages()) { + dfsPackageName(list, p.getName(), pp); + } + if (p.hasAlias()) { + list.add(String.format("p %s=%s", p.getName(), p.getAlias())); + } + } + // classes + for (DeobfClsInfo deobfClsInfo : clsMap.values()) { + if (deobfClsInfo.alias != null) { + list.add(String.format("c %s=%s", deobfClsInfo.cls.getFullName(), deobfClsInfo.alias)); + } + } + Collections.sort(list); + FileUtils.writeLines(deobfMapFile, MAP_FILE_CHARSET, list); + list.clear(); + } + + public String getPackageName(String packageName) { + final PackageNode pkg = getPackageNode(packageName, false); + if (pkg != null) { + return pkg.getFullAlias(); + } + return packageName; + } + + public String getClassName(ClassInfo clsInfo) { + final DeobfClsInfo deobfClsInfo = clsMap.get(clsInfo); + if (deobfClsInfo != null) { + return deobfClsInfo.makeNameWithoutPkg(); + } + return getNameWithoutPackage(clsInfo); + } + + public String getClassFullName(ClassNode cls) { + return getClassFullName(cls.getClassInfo()); + } + + public String getClassFullName(ClassInfo clsInfo) { + final DeobfClsInfo deobfClsInfo = clsMap.get(clsInfo); + if (deobfClsInfo != null) { + return deobfClsInfo.getFullName(); + } + return getPackageName(clsInfo.getPackage()) + classNameSeparator + getClassName(clsInfo); + } } diff --git a/jadx-core/src/main/java/jadx/core/deobf/IDeobfuscator.java b/jadx-core/src/main/java/jadx/core/deobf/IDeobfuscator.java deleted file mode 100644 index 16ea78bc9..000000000 --- a/jadx-core/src/main/java/jadx/core/deobf/IDeobfuscator.java +++ /dev/null @@ -1,18 +0,0 @@ -package jadx.core.deobf; - -import jadx.core.dex.info.ClassInfo; -import jadx.core.dex.nodes.ClassNode; - -public interface IDeobfuscator { - - public String getPackageName(String packageName); - - public String getClassShortName(ClassNode cls); - public String getClassShortName(ClassInfo clsInfo); - public String getClassName(ClassNode cls); - public String getClassName(ClassInfo clsInfo); - public String getClassFullName(ClassNode cls); - public String getClassFullName(ClassInfo clsInfo); - - public String getClassFullPath(ClassInfo clsInfo); -} diff --git a/jadx-core/src/main/java/jadx/core/deobf/PackageNode.java b/jadx-core/src/main/java/jadx/core/deobf/PackageNode.java index c95dd6fa1..1d9b79df4 100644 --- a/jadx-core/src/main/java/jadx/core/deobf/PackageNode.java +++ b/jadx-core/src/main/java/jadx/core/deobf/PackageNode.java @@ -14,7 +14,7 @@ public class PackageNode { private List innerPackages = Collections.emptyList(); public static final char separatorChar = '.'; - + private String packageName; private String packageAlias; @@ -51,14 +51,14 @@ public class PackageNode { if (packageAlias != null) { return packageAlias; } - + return packageName; } public void setAlias(String alias) { packageAlias = alias; } - + public boolean hasAlias() { return (packageAlias != null); } @@ -108,9 +108,8 @@ public class PackageNode { /** * Gets inner package node by name - * + * * @param name inner package name - * * @return package node or {@code null} */ public PackageNode getInnerPackageByName(String name) { @@ -127,7 +126,7 @@ public class PackageNode { /** * Fills stack with parent packages exclude root node - * + * * @return stack with parent packages */ private Stack getParentPackages() { diff --git a/jadx-core/src/main/java/jadx/core/deobf/StubDeobfuscator.java b/jadx-core/src/main/java/jadx/core/deobf/StubDeobfuscator.java deleted file mode 100644 index 862fa78fd..000000000 --- a/jadx-core/src/main/java/jadx/core/deobf/StubDeobfuscator.java +++ /dev/null @@ -1,48 +0,0 @@ -package jadx.core.deobf; - -import jadx.core.dex.info.ClassInfo; -import jadx.core.dex.nodes.ClassNode; - -public class StubDeobfuscator implements IDeobfuscator { - - @Override - public String getPackageName(String packageName) { - return packageName; - } - - @Override - public String getClassShortName(ClassNode cls) { - return cls.getShortName(); - } - - @Override - public String getClassShortName(ClassInfo clsInfo) { - return clsInfo.getShortName(); - } - - @Override - public String getClassName(ClassNode cls) { - return cls.getClassInfo().getNameWithoutPackage(); - } - - @Override - public String getClassName(ClassInfo clsInfo) { - return clsInfo.getNameWithoutPackage(); - } - - @Override - public String getClassFullName(ClassNode cls) { - return cls.getFullName(); - } - - @Override - public String getClassFullName(ClassInfo clsInfo) { - return clsInfo.getFullName(); - } - - @Override - public String getClassFullPath(ClassInfo clsInfo) { - return clsInfo.getFullPath(); - } - -} diff --git a/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java b/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java index 25bf74c47..a78749620 100644 --- a/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java +++ b/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java @@ -16,17 +16,27 @@ public final class ClassInfo { private String fullName; // for inner class not equals null private ClassInfo parentClass; + // class info after rename (deobfuscation) + private ClassInfo alias; private ClassInfo(DexNode dex, ArgType type) { - if (!type.isObject()) { + this(dex, type, true); + } + + private ClassInfo(DexNode dex, ArgType type, boolean inner) { + if (!type.isObject() || type.isGeneric()) { throw new JadxRuntimeException("Not class type: " + type); } this.type = type; + this.alias = this; - splitNames(dex, true); + splitNames(dex, inner); } public static ClassInfo fromType(DexNode dex, ArgType type) { + if (type.isArray()) { + type = ArgType.OBJECT; + } ClassInfo cls = dex.getInfoStorage().getCls(type); if (cls != null) { return cls; @@ -39,26 +49,32 @@ public final class ClassInfo { if (clsIndex == DexNode.NO_INDEX) { return null; } - ArgType type = dex.getType(clsIndex); - if (type.isArray()) { - type = ArgType.OBJECT; - } - return fromType(dex, type); + return fromType(dex, dex.getType(clsIndex)); } public static ClassInfo fromName(DexNode dex, String clsName) { return fromType(dex, ArgType.object(clsName)); } + public static ClassInfo extCls(DexNode dex, ArgType type) { + ClassInfo classInfo = fromName(dex, type.getObject()); + return classInfo.alias; + } + + public void rename(DexNode dex, String fullName) { + this.alias = new ClassInfo(dex, ArgType.object(fullName), isInner()); + } + + public ClassInfo getAlias() { + return alias; + } + private void splitNames(DexNode dex, boolean canBeInner) { String fullObjectName = type.getObject(); - assert fullObjectName.indexOf('/') == -1 : "Raw type: " + type; - String clsName; int dot = fullObjectName.lastIndexOf('.'); if (dot == -1) { - // rename default package if it used from class with package (often for obfuscated apps), - pkg = Consts.DEFAULT_PACKAGE_NAME; + pkg = ""; clsName = fullObjectName; } else { pkg = fullObjectName.substring(0, dot); @@ -83,8 +99,12 @@ public final class ClassInfo { if (NameMapper.isReserved(clsName)) { clsName += "_"; } - this.fullName = (parentClass != null ? parentClass.getFullName() : pkg) + "." + clsName; this.name = clsName; + if (parentClass != null) { + this.fullName = parentClass.fullName + "." + clsName; + } else { + this.fullName = pkg.isEmpty() ? clsName : pkg + "." + clsName; + } } public String getFullPath() { @@ -97,28 +117,23 @@ public final class ClassInfo { return fullName; } - public boolean isObject() { - return fullName.equals(Consts.CLASS_OBJECT); - } - public String getShortName() { return name; } - public String getRawName() { - return type.getObject(); - } - public String getPackage() { return pkg; } - public boolean isPackageDefault() { - return pkg.isEmpty() || pkg.equals(Consts.DEFAULT_PACKAGE_NAME); + public String getRawName() { + return type.getObject(); } public String getNameWithoutPackage() { - return (parentClass != null ? parentClass.getNameWithoutPackage() + "." : "") + name; + if (parentClass == null) { + return name; + } + return parentClass.getNameWithoutPackage() + "." + name; } public ClassInfo getParentClass() { @@ -154,7 +169,7 @@ public final class ClassInfo { } if (obj instanceof ClassInfo) { ClassInfo other = (ClassInfo) obj; - return this.getFullName().equals(other.getFullName()); + return this.type.equals(other.type); } return false; } diff --git a/jadx-core/src/main/java/jadx/core/dex/info/FieldInfo.java b/jadx-core/src/main/java/jadx/core/dex/info/FieldInfo.java index b98d7a716..5073831b8 100644 --- a/jadx-core/src/main/java/jadx/core/dex/info/FieldInfo.java +++ b/jadx-core/src/main/java/jadx/core/dex/info/FieldInfo.java @@ -5,7 +5,7 @@ import jadx.core.dex.nodes.DexNode; import com.android.dex.FieldId; -public class FieldInfo { +public final class FieldInfo { private final ClassInfo declClass; private final String name; diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java b/jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java index 91c09cd78..fd7d8c563 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java @@ -621,12 +621,10 @@ public class InsnDecoder { regs[i] = InsnArg.reg(regNum, elType, typeImmutable); } } - InsnNode node = new FilledNewArrayNode(elType, regs == null ? 0 : regs.length); + InsnNode node = new FilledNewArrayNode(elType, regs.length); node.setResult(resReg == -1 ? null : InsnArg.reg(resReg, arrType)); - if (regs != null) { - for (InsnArg arg : regs) { - node.addArg(arg); - } + for (InsnArg arg : regs) { + node.addArg(arg); } return node; } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java index 2e385ce2d..69ce659e3 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java @@ -26,6 +26,7 @@ public abstract class ArgType { public static final ArgType OBJECT = object(Consts.CLASS_OBJECT); public static final ArgType CLASS = object(Consts.CLASS_CLASS); public static final ArgType STRING = object(Consts.CLASS_STRING); + public static final ArgType ENUM = object(Consts.CLASS_ENUM); public static final ArgType THROWABLE = object(Consts.CLASS_THROWABLE); public static final ArgType UNKNOWN = unknown(PrimitiveType.values()); diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java index db0c1d6ae..c73087162 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java @@ -30,6 +30,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,8 +47,8 @@ public class ClassNode extends LineAttrNode implements ILoadable { private final DexNode dex; private final ClassInfo clsInfo; private final AccessInfo accessFlags; - private ClassInfo superClass; - private List interfaces; + private ArgType superClass; + private List interfaces; private Map> genericMap; private final List methods; @@ -70,11 +71,11 @@ public class ClassNode extends LineAttrNode implements ILoadable { if (cls.getSupertypeIndex() == DexNode.NO_INDEX) { this.superClass = null; } else { - this.superClass = ClassInfo.fromDex(dex, cls.getSupertypeIndex()); + this.superClass = dex.getType(cls.getSupertypeIndex()); } - this.interfaces = new ArrayList(cls.getInterfaces().length); + this.interfaces = new ArrayList(cls.getInterfaces().length); for (short interfaceIdx : cls.getInterfaces()) { - this.interfaces.add(ClassInfo.fromDex(dex, interfaceIdx)); + this.interfaces.add(dex.getType(interfaceIdx)); } if (cls.getClassDataOffset() != 0) { ClassData clsData = dex.readClassData(cls); @@ -111,7 +112,7 @@ public class ClassNode extends LineAttrNode implements ILoadable { int sfIdx = cls.getSourceFileIndex(); if (sfIdx != DexNode.NO_INDEX) { String fileName = dex.getString(sfIdx); - if (!this.getFullName().contains(fileName.replace(".java", "")) + if (!clsInfo.getFullName().contains(fileName.replace(".java", "")) && !fileName.equals("SourceFile")) { this.addAttr(new SourceFileAttr(fileName)); LOG.debug("Class '{}' compiled from '{}'", this, fileName); @@ -129,7 +130,7 @@ public class ClassNode extends LineAttrNode implements ILoadable { this.accessFlags = new AccessInfo(accFlagsValue, AFType.CLASS); } catch (Exception e) { - throw new DecodeException("Error decode class: " + getFullName(), e); + throw new DecodeException("Error decode class: " + clsInfo, e); } } @@ -191,12 +192,12 @@ public class ClassNode extends LineAttrNode implements ILoadable { // parse class generic map genericMap = sp.consumeGenericMap(); // parse super class signature - superClass = ClassInfo.fromType(dex, sp.consumeType()); + superClass = sp.consumeType(); // parse interfaces signatures for (int i = 0; i < interfaces.size(); i++) { ArgType type = sp.consumeType(); if (type != null) { - interfaces.set(i, ClassInfo.fromType(dex, type)); + interfaces.set(i, type); } else { break; } @@ -247,11 +248,12 @@ public class ClassNode extends LineAttrNode implements ILoadable { } } - public ClassInfo getSuperClass() { + @Nullable + public ArgType getSuperClass() { return superClass; } - public List getInterfaces() { + public List getInterfaces() { return interfaces; } @@ -403,12 +405,14 @@ public class ClassNode extends LineAttrNode implements ILoadable { } public boolean isEnum() { - return getAccessFlags().isEnum() && getSuperClass().getFullName().equals(Consts.CLASS_ENUM); + return getAccessFlags().isEnum() + && getSuperClass() != null + && getSuperClass().getObject().equals(ArgType.ENUM.getObject()); } public boolean isAnonymous() { return clsInfo.isInner() - && getShortName().startsWith(Consts.ANONYMOUS_CLASS_PREFIX) + && clsInfo.getShortName().startsWith(Consts.ANONYMOUS_CLASS_PREFIX) && getDefaultConstructor() != null; } @@ -429,24 +433,34 @@ public class ClassNode extends LineAttrNode implements ILoadable { return dex; } + public String getRawName() { + return clsInfo.getRawName(); + } + + /** + * Internal class info (don't use in code generation and external api). + */ public ClassInfo getClassInfo() { return clsInfo; } + /** + * Class info for external usage (code generation and external api). + */ + public ClassInfo getAlias() { + return clsInfo.getAlias(); + } + public String getShortName() { - return clsInfo.getShortName(); + return clsInfo.getAlias().getShortName(); } public String getFullName() { - return clsInfo.getFullName(); + return clsInfo.getAlias().getFullName(); } public String getPackage() { - return clsInfo.getPackage(); - } - - public String getRawName() { - return clsInfo.getRawName(); + return clsInfo.getAlias().getPackage(); } public void setCode(CodeWriter code) { @@ -471,6 +485,6 @@ public class ClassNode extends LineAttrNode implements ILoadable { @Override public String toString() { - return getFullName(); + return clsInfo.getFullName(); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/DexNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/DexNode.java index 41fe3a9a3..faac0cc1a 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/DexNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/DexNode.java @@ -33,7 +33,10 @@ public class DexNode { private final RootNode root; private final Dex dexBuf; + private final InputFile file; + private final List classes = new ArrayList(); + private final Map clsMap = new HashMap(); private final Map constFields = new HashMap(); @@ -41,12 +44,36 @@ public class DexNode { public DexNode(RootNode root, InputFile input) { this.root = root; + this.file = input; this.dexBuf = input.getDexBuffer(); } public void loadClasses() throws DecodeException { for (ClassDef cls : dexBuf.classDefs()) { - classes.add(new ClassNode(this, cls)); + ClassNode clsNode = new ClassNode(this, cls); + classes.add(clsNode); + clsMap.put(clsNode.getClassInfo(), clsNode); + } + } + + void initInnerClasses() { + // move inner classes + List inner = new ArrayList(); + for (ClassNode cls : classes) { + if (cls.getClassInfo().isInner()) { + inner.add(cls); + } + } + for (ClassNode cls : inner) { + ClassInfo clsInfo = cls.getClassInfo(); + ClassNode parent = resolveClass(clsInfo.getParentClass()); + if (parent == null) { + clsMap.remove(clsInfo); + clsInfo.notInner(cls.dex()); + clsMap.put(clsInfo, cls); + } else { + parent.addInnerClass(cls); + } } } @@ -56,7 +83,7 @@ public class DexNode { @Nullable public ClassNode resolveClass(ClassInfo clsInfo) { - return root.resolveClass(clsInfo); + return clsMap.get(clsInfo); } @Nullable @@ -85,6 +112,10 @@ public class DexNode { return infoStorage; } + public InputFile getInputFile() { + return file; + } + // DexBuffer wrappers public String getString(int index) { diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java index 6f83c88d7..ec4d8e43a 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java @@ -597,7 +597,7 @@ public class MethodNode extends LineAttrNode implements ILoadable { @Override public String toString() { - return parentClass.getFullName() + "." + mthInfo.getName() + return parentClass + "." + mthInfo.getName() + "(" + Utils.listToString(mthInfo.getArgumentsTypes()) + "):" + retType; } 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 5fba46600..453f6416c 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 @@ -1,5 +1,6 @@ package jadx.core.dex.nodes; +import jadx.api.IJadxArgs; import jadx.api.ResourceFile; import jadx.api.ResourceType; import jadx.api.ResourcesLoader; @@ -27,19 +28,19 @@ import org.slf4j.LoggerFactory; public class RootNode { private static final Logger LOG = LoggerFactory.getLogger(RootNode.class); - private final Map names = new HashMap(); private final ErrorsCounter errorsCounter = new ErrorsCounter(); + private final IJadxArgs args; private List dexNodes; - - /** - * Resources * - */ private Map resourcesNames = new HashMap(); @Nullable private String appPackage; private ClassNode appResClass; + public RootNode(IJadxArgs args) { + this.args = args; + } + public void load(List dexFiles) throws DecodeException { dexNodes = new ArrayList(dexFiles.size()); for (InputFile dex : dexFiles) { @@ -54,21 +55,7 @@ public class RootNode { for (DexNode dexNode : dexNodes) { dexNode.loadClasses(); } - - List classes = new ArrayList(); - for (DexNode dexNode : dexNodes) { - for (ClassNode cls : dexNode.getClasses()) { - names.put(cls.getFullName(), cls); - } - classes.addAll(dexNode.getClasses()); - } - - try { - initClassPath(classes); - } catch (IOException e) { - throw new DecodeException("Error loading classpath", e); - } - initInnerClasses(classes); + initInnerClasses(); } public void loadResources(List resources) { @@ -99,55 +86,52 @@ public class RootNode { ResourceStorage resStorage = parser.getResStorage(); resourcesNames = resStorage.getResourcesNames(); + appPackage = resStorage.getAppPackage(); } public void initAppResClass() { - ClassNode resCls = null; - if (appPackage != null) { - resCls = searchClassByName(appPackage + ".R"); - } else { - for (ClassNode cls : names.values()) { - if (cls.getShortName().equals("R")) { - resCls = cls; - break; - } - } - } - if (resCls != null) { - appResClass = resCls; + ClassNode resCls; + if (appPackage == null) { + appResClass = makeClass("R"); return; } + String fullName = appPackage + ".R"; + resCls = searchClassByName(fullName); + if (resCls != null) { + appResClass = resCls; + } else { + appResClass = makeClass(fullName); + } + } + + private ClassNode makeClass(String clsName) { DexNode firstDex = dexNodes.get(0); - appResClass = new ClassNode(firstDex, ClassInfo.fromName(firstDex, "R")); + ClassInfo r = ClassInfo.fromName(firstDex, clsName); + return new ClassNode(firstDex, r); } - private static void initClassPath(List classes) throws IOException, DecodeException { - if (!ArgType.isClspSet()) { - ClspGraph clsp = new ClspGraph(); - clsp.load(); - clsp.addApp(classes); + public void initClassPath() throws DecodeException { + try { + if (!ArgType.isClspSet()) { + ClspGraph clsp = new ClspGraph(); + clsp.load(); - ArgType.setClsp(clsp); + List classes = new ArrayList(); + for (DexNode dexNode : dexNodes) { + classes.addAll(dexNode.getClasses()); + } + clsp.addApp(classes); + + ArgType.setClsp(clsp); + } + } catch (IOException e) { + throw new DecodeException("Error loading classpath", e); } } - private void initInnerClasses(List classes) { - // move inner classes - List inner = new ArrayList(); - for (ClassNode cls : classes) { - if (cls.getClassInfo().isInner()) { - inner.add(cls); - } - } - for (ClassNode cls : inner) { - ClassNode parent = resolveClass(cls.getClassInfo().getParentClass()); - if (parent == null) { - names.remove(cls.getFullName()); - cls.getClassInfo().notInner(cls.dex()); - names.put(cls.getFullName(), cls); - } else { - parent.addInnerClass(cls); - } + private void initInnerClasses() { + for (DexNode dexNode : dexNodes) { + dexNode.initInnerClasses(); } } @@ -168,12 +152,14 @@ public class RootNode { } public ClassNode searchClassByName(String fullName) { - return names.get(fullName); - } - - public ClassNode resolveClass(ClassInfo cls) { - String fullName = cls.getFullName(); - return searchClassByName(fullName); + for (DexNode dexNode : dexNodes) { + ClassInfo clsInfo = ClassInfo.fromName(dexNode, fullName); + ClassNode cls = dexNode.resolveClass(clsInfo); + if (cls != null) { + return cls; + } + } + return null; } public List getDexNodes() { @@ -196,4 +182,8 @@ public class RootNode { public ClassNode getAppResClass() { return appResClass; } + + public IJadxArgs getArgs() { + return args; + } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/AbstractVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/AbstractVisitor.java index bc8f4f1b0..98f844c84 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/AbstractVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/AbstractVisitor.java @@ -2,10 +2,15 @@ package jadx.core.dex.visitors; import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.MethodNode; +import jadx.core.dex.nodes.RootNode; import jadx.core.utils.exceptions.JadxException; public class AbstractVisitor implements IDexTreeVisitor { + @Override + public void init(RootNode root) throws JadxException { + } + @Override public boolean visit(ClassNode cls) throws JadxException { return true; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java index b1ac00e62..6885194a2 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java @@ -199,7 +199,7 @@ public class ClassModifier extends AbstractVisitor { */ private static void processStaticFieldAssign(ClassNode cls, IndexInsnNode insn) { FieldInfo field = (FieldInfo) insn.getIndex(); - String thisClass = cls.getFullName(); + String thisClass = cls.getClassInfo().getFullName(); if (field.getDeclClass().getFullName().equals(thisClass)) { FieldNode fn = cls.searchField(field); if (fn != null && fn.getAccessFlags().isFinal()) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/DependencyCollector.java b/jadx-core/src/main/java/jadx/core/dex/visitors/DependencyCollector.java index eca74014a..4b6ee94d7 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/DependencyCollector.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/DependencyCollector.java @@ -31,8 +31,8 @@ public class DependencyCollector extends AbstractVisitor { private static void processClass(ClassNode cls, DexNode dex, Set depList) { addDep(dex, depList, cls.getSuperClass()); - for (ClassInfo clsInfo : cls.getInterfaces()) { - addDep(dex, depList, clsInfo); + for (ArgType iType : cls.getInterfaces()) { + addDep(dex, depList, iType); } for (FieldNode fieldNode : cls.getFields()) { addDep(dex, depList, fieldNode.getType()); @@ -77,7 +77,7 @@ public class DependencyCollector extends AbstractVisitor { private static void addDep(DexNode dex, Set depList, ArgType type) { if (type != null) { if (type.isObject()) { - addDep(dex, depList, ClassInfo.fromName(type.getObject())); + addDep(dex, depList, ClassInfo.fromName(dex, type.getObject())); ArgType[] genericTypes = type.getGenericTypes(); if (type.isGeneric() && genericTypes != null) { for (ArgType argType : genericTypes) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java index 698f5a464..f95eb7220 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/DotGraphVisitor.java @@ -2,7 +2,6 @@ package jadx.core.dex.visitors; import jadx.core.codegen.CodeWriter; import jadx.core.codegen.MethodGen; -import jadx.core.deobf.Deobfuscator; import jadx.core.dex.attributes.IAttributeNode; import jadx.core.dex.instructions.IfNode; import jadx.core.dex.instructions.InsnType; @@ -68,7 +67,7 @@ public class DotGraphVisitor extends AbstractVisitor { public void process(MethodNode mth) { dot.startLine("digraph \"CFG for"); - dot.add(escape(mth.getParentClass().getFullName() + "." + mth.getMethodInfo().getShortId())); + dot.add(escape(mth.getParentClass() + "." + mth.getMethodInfo().getShortId())); dot.add("\" {"); if (useRegions) { @@ -85,7 +84,7 @@ public class DotGraphVisitor extends AbstractVisitor { dot.startLine("MethodNode[shape=record,label=\"{"); dot.add(escape(mth.getAccessFlags().makeString())); dot.add(escape(mth.getReturnType() + " " - + mth.getParentClass().getFullName() + "." + mth.getName() + + mth.getParentClass() + "." + mth.getName() + "(" + Utils.listToString(mth.getArguments(true)) + ") ")); String attrs = attributesString(mth); @@ -105,7 +104,7 @@ public class DotGraphVisitor extends AbstractVisitor { + (useRegions ? ".regions" : "") + (rawInsn ? ".raw" : "") + ".dot"; - dot.save(dir, Deobfuscator.instance().getClassFullPath(mth.getParentClass().getClassInfo()) + "_graphs", fileName); + dot.save(dir, mth.getParentClass().getClassInfo().getFullPath() + "_graphs", fileName); } private void processMethodRegion(MethodNode mth) { diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/IDexTreeVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/IDexTreeVisitor.java index 0e194a621..327056f82 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/IDexTreeVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/IDexTreeVisitor.java @@ -2,6 +2,7 @@ package jadx.core.dex.visitors; import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.MethodNode; +import jadx.core.dex.nodes.RootNode; import jadx.core.utils.exceptions.JadxException; /** @@ -9,6 +10,11 @@ import jadx.core.utils.exceptions.JadxException; */ public interface IDexTreeVisitor { + /** + * Called after loading dex tree, but before visitor traversal. + */ + void init(RootNode root) throws JadxException; + /** * Visit class * diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java new file mode 100644 index 000000000..22e524c5f --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java @@ -0,0 +1,34 @@ +package jadx.core.dex.visitors; + +import jadx.core.deobf.Deobfuscator; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.RootNode; +import jadx.core.utils.exceptions.JadxException; + +import java.io.File; + +import org.apache.commons.io.FilenameUtils; + +public class RenameVisitor extends AbstractVisitor { + + private Deobfuscator deobfuscator; + + @Override + public void init(RootNode root) { + String firstInputFileName = root.getDexNodes().get(0).getInputFile().getFile().getAbsolutePath(); + String inputPath = FilenameUtils.getFullPathNoEndSeparator(firstInputFileName); + String inputName = FilenameUtils.getBaseName(firstInputFileName); + + File deobfMapFile = new File(inputPath, inputName + ".jobf"); + deobfuscator = new Deobfuscator(root.getArgs(), root.getDexNodes(), deobfMapFile); + // TODO: check classes for case sensitive names (issue #24) + // TODO: sometimes can be used source file name from 'SourceFileAttr' + deobfuscator.execute(); + } + + @Override + public boolean visit(ClassNode cls) throws JadxException { + // TODO: rename fields and methods + return false; + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/SaveCode.java b/jadx-core/src/main/java/jadx/core/dex/visitors/SaveCode.java index 7cf9c13ab..9cabcb3ae 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/SaveCode.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/SaveCode.java @@ -2,7 +2,6 @@ package jadx.core.dex.visitors; import jadx.api.IJadxArgs; import jadx.core.codegen.CodeWriter; -import jadx.core.deobf.Deobfuscator; import jadx.core.dex.nodes.ClassNode; import jadx.core.utils.exceptions.CodegenException; @@ -25,7 +24,7 @@ public class SaveCode extends AbstractVisitor { public static void save(File dir, IJadxArgs args, ClassNode cls) { CodeWriter clsCode = cls.getCode(); - String fileName = Deobfuscator.instance().getClassFullPath(cls.getClassInfo()) + ".java"; + String fileName = cls.getClassInfo().getFullPath() + ".java"; if (args.isFallbackMode()) { fileName += ".jadx"; } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java index 2ad2cf946..17f7cb1de 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/SimplifyVisitor.java @@ -145,70 +145,70 @@ public class SimplifyVisitor extends AbstractVisitor { } private static InsnNode convertInvoke(MethodNode mth, InsnNode insn) { - MethodInfo callMth = ((InvokeNode) insn).getCallMth(); + MethodInfo callMth = ((InvokeNode) insn).getCallMth(); - // If this is a 'new StringBuilder(xxx).append(yyy).append(zzz).toString(), - // convert it to STRING_CONCAT pseudo instruction. - if (callMth.getDeclClass().getFullName().equals(Consts.CLASS_STRING_BUILDER) - && callMth.getShortId().equals(Consts.MTH_TOSTRING_SIGNATURE) - && insn.getArg(0).isInsnWrap()) { - try { - List chain = flattenInsnChain(insn); - int constrIndex = -1; //RAF - // Case where new StringBuilder() is called with NO args (the entire - // string is created using .append() calls: - if (chain.size()>1 && chain.get(0).getType()==InsnType.CONSTRUCTOR) { - constrIndex = 0; - } else if (chain.size()>2 && chain.get(1).getType()==InsnType.CONSTRUCTOR) { - //RAF Case where the first string element is String arg to the - // new StringBuilder("xxx") constructor - constrIndex = 1; - } else if (chain.size()>3 && chain.get(2).getType()==InsnType.CONSTRUCTOR) { - //RAF Case where the first string element is String.valueOf() arg - // to the new StringBuilder(String.valueOf(zzz)) constructor - constrIndex = 2; - } + // If this is a 'new StringBuilder(xxx).append(yyy).append(zzz).toString(), + // convert it to STRING_CONCAT pseudo instruction. + if (callMth.getDeclClass().getFullName().equals(Consts.CLASS_STRING_BUILDER) + && callMth.getShortId().equals(Consts.MTH_TOSTRING_SIGNATURE) + && insn.getArg(0).isInsnWrap()) { + try { + List chain = flattenInsnChain(insn); + int constrIndex = -1; //RAF + // Case where new StringBuilder() is called with NO args (the entire + // string is created using .append() calls: + if (chain.size() > 1 && chain.get(0).getType() == InsnType.CONSTRUCTOR) { + constrIndex = 0; + } else if (chain.size() > 2 && chain.get(1).getType() == InsnType.CONSTRUCTOR) { + //RAF Case where the first string element is String arg to the + // new StringBuilder("xxx") constructor + constrIndex = 1; + } else if (chain.size() > 3 && chain.get(2).getType() == InsnType.CONSTRUCTOR) { + //RAF Case where the first string element is String.valueOf() arg + // to the new StringBuilder(String.valueOf(zzz)) constructor + constrIndex = 2; + } - if (constrIndex != -1) { // If we found a CONSTRUCTOR, is it a StringBuilder? - ConstructorInsn constr = (ConstructorInsn) chain.get(constrIndex); - if (constr.getClassType().getFullName().equals(Consts.CLASS_STRING_BUILDER)) { - int len = chain.size(), argInd = 1; - InsnNode concatInsn = new InsnNode(InsnType.STR_CONCAT, len-1); - InsnNode argInsn; - if (constrIndex > 0) { // There was an arg to the StringBuilder constr - InsnWrapArg iwa; - if (constrIndex==2 - && (argInsn = chain.get(1)).getType()==InsnType.INVOKE - && ((InvokeNode)argInsn).getCallMth().getName().compareTo("valueOf")==0) { - // The argument of new StringBuilder() is a String.valueOf(chainElement0) - iwa = (InsnWrapArg)argInsn.getArg(0); - argInd = 3; // Cause for loop below to skip to after the constructor - } else { - InsnNode firstNode = chain.get(0); - if (firstNode instanceof ConstStringNode) { - ConstStringNode csn = (ConstStringNode) firstNode; - iwa = new InsnWrapArg(csn); - argInd = 2; // Cause for loop below to skip to after the constructor - } else { - return null; - } - } - concatInsn.addArg(iwa); - } + if (constrIndex != -1) { // If we found a CONSTRUCTOR, is it a StringBuilder? + ConstructorInsn constr = (ConstructorInsn) chain.get(constrIndex); + if (constr.getClassType().getFullName().equals(Consts.CLASS_STRING_BUILDER)) { + int len = chain.size(), argInd = 1; + InsnNode concatInsn = new InsnNode(InsnType.STR_CONCAT, len - 1); + InsnNode argInsn; + if (constrIndex > 0) { // There was an arg to the StringBuilder constr + InsnWrapArg iwa; + if (constrIndex == 2 + && (argInsn = chain.get(1)).getType() == InsnType.INVOKE + && ((InvokeNode) argInsn).getCallMth().getName().compareTo("valueOf") == 0) { + // The argument of new StringBuilder() is a String.valueOf(chainElement0) + iwa = (InsnWrapArg) argInsn.getArg(0); + argInd = 3; // Cause for loop below to skip to after the constructor + } else { + InsnNode firstNode = chain.get(0); + if (firstNode instanceof ConstStringNode) { + ConstStringNode csn = (ConstStringNode) firstNode; + iwa = new InsnWrapArg(csn); + argInd = 2; // Cause for loop below to skip to after the constructor + } else { + return null; + } + } + concatInsn.addArg(iwa); + } - for (; argInd < len; argInd++) { // Add the .append(xxx) arg string to concat - concatInsn.addArg(chain.get(argInd).getArg(1)); - } - concatInsn.setResult(insn.getResult()); - return concatInsn; - } // end of if constructor is for StringBuilder - } // end of if we found a constructor early in the chain + for (; argInd < len; argInd++) { // Add the .append(xxx) arg string to concat + concatInsn.addArg(chain.get(argInd).getArg(1)); + } + concatInsn.setResult(insn.getResult()); + return concatInsn; + } // end of if constructor is for StringBuilder + } // end of if we found a constructor early in the chain - } catch (Throwable e) { - LOG.debug("Can't convert string concatenation: {} insn: {}", mth, insn, e); - } - } - return null; + } catch (Throwable e) { + LOG.debug("Can't convert string concatenation: {} insn: {}", mth, insn, e); + } + } + return null; } private static InsnNode simplifyArith(InsnNode insn) { diff --git a/jadx-core/src/main/java/jadx/core/xmlgen/ParserStream.java b/jadx-core/src/main/java/jadx/core/xmlgen/ParserStream.java index 0ca7a399d..22454a588 100644 --- a/jadx-core/src/main/java/jadx/core/xmlgen/ParserStream.java +++ b/jadx-core/src/main/java/jadx/core/xmlgen/ParserStream.java @@ -4,6 +4,8 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; +import org.jetbrains.annotations.NotNull; + public class ParserStream { protected static final Charset STRING_CHARSET_UTF16 = Charset.forName("UTF-16LE"); @@ -15,7 +17,7 @@ public class ParserStream { private final InputStream input; private long readPos = 0; - public ParserStream(InputStream inputStream) { + public ParserStream(@NotNull InputStream inputStream) { this.input = inputStream; } diff --git a/jadx-core/src/test/java/jadx/tests/api/SmaliTest.java b/jadx-core/src/test/java/jadx/tests/api/SmaliTest.java index 2d4013eab..d28ad3478 100644 --- a/jadx-core/src/test/java/jadx/tests/api/SmaliTest.java +++ b/jadx-core/src/test/java/jadx/tests/api/SmaliTest.java @@ -1,6 +1,5 @@ package jadx.tests.api; -import jadx.core.Consts; import jadx.core.dex.nodes.ClassNode; import java.io.File; @@ -21,8 +20,7 @@ public class SmaliTest extends IntegrationTest { File smaliFile = getSmaliFile(clsName); File outDex = createTempFile(".dex"); compileSmali(smaliFile, outDex); - String fullClsName = Consts.DEFAULT_PACKAGE_NAME + "." + clsName; - return getClassNodeFromFile(outDex, fullClsName); + return getClassNodeFromFile(outDex, clsName); } private static File getSmaliFile(String clsName) { diff --git a/jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchReturnFromCase.java b/jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchReturnFromCase.java index 119c610cd..f495c1f3a 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchReturnFromCase.java +++ b/jadx-core/src/test/java/jadx/tests/integration/switches/TestSwitchReturnFromCase.java @@ -12,42 +12,42 @@ import static org.junit.Assert.assertThat; public class TestSwitchReturnFromCase extends IntegrationTest { - public static class TestCls { - public void test(int a) { - String s = null; - if (a > 1000) { - return; - } - switch (a % 4) { - case 1: - s = "1"; - break; - case 2: - s = "2"; - break; - case 3: - case 4: - s = "4"; - break; - case 5: - return; - } - s = "5"; - } - } + public static class TestCls { + public void test(int a) { + String s = null; + if (a > 1000) { + return; + } + switch (a % 4) { + case 1: + s = "1"; + break; + case 2: + s = "2"; + break; + case 3: + case 4: + s = "4"; + break; + case 5: + return; + } + s = "5"; + } + } - @Test - public void test() { - ClassNode cls = getClassNode(TestCls.class); - String code = cls.getCode().toString(); + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); - assertThat(code, containsString("switch (a % 4) {")); - assertEquals(5, count(code, "case ")); - assertEquals(3, count(code, "break;")); + assertThat(code, containsString("switch (a % 4) {")); + assertEquals(5, count(code, "case ")); + assertEquals(3, count(code, "break;")); - assertThat(code, containsOne("s = \"1\";")); - assertThat(code, containsOne("s = \"2\";")); - assertThat(code, containsOne("s = \"4\";")); - assertThat(code, containsOne("s = \"5\";")); - } + assertThat(code, containsOne("s = \"1\";")); + assertThat(code, containsOne("s = \"2\";")); + assertThat(code, containsOne("s = \"4\";")); + assertThat(code, containsOne("s = \"5\";")); + } }