diff --git a/jadx-core/src/main/java/jadx/api/JavaField.java b/jadx-core/src/main/java/jadx/api/JavaField.java index 7da2487a1..559b40db5 100644 --- a/jadx-core/src/main/java/jadx/api/JavaField.java +++ b/jadx-core/src/main/java/jadx/api/JavaField.java @@ -16,12 +16,12 @@ public final class JavaField implements JavaNode { @Override public String getName() { - return field.getName(); + return field.getAlias(); } @Override public String getFullName() { - return parent.getFullName() + "." + field.getName(); + return parent.getFullName() + "." + getName(); } @Override diff --git a/jadx-core/src/main/java/jadx/api/JavaMethod.java b/jadx-core/src/main/java/jadx/api/JavaMethod.java index 44a4f34cd..ef91413d2 100644 --- a/jadx-core/src/main/java/jadx/api/JavaMethod.java +++ b/jadx-core/src/main/java/jadx/api/JavaMethod.java @@ -17,7 +17,7 @@ public final class JavaMethod implements JavaNode { @Override public String getName() { - return mth.getName(); + return mth.getAlias(); } @Override diff --git a/jadx-core/src/main/java/jadx/core/Jadx.java b/jadx-core/src/main/java/jadx/core/Jadx.java index e832b22ae..4dcda7a6d 100644 --- a/jadx-core/src/main/java/jadx/core/Jadx.java +++ b/jadx-core/src/main/java/jadx/core/Jadx.java @@ -108,9 +108,7 @@ public class Jadx { passes.add(new DependencyCollector()); - if (args.isDeobfuscationOn()) { - passes.add(new RenameVisitor()); - } + 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 bb81cc3ab..373399a83 100644 --- a/jadx-core/src/main/java/jadx/core/ProcessClass.java +++ b/jadx-core/src/main/java/jadx/core/ProcessClass.java @@ -53,10 +53,6 @@ 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/codegen/ClassGen.java b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java index 9e5e28cf4..762652407 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/ClassGen.java @@ -337,7 +337,7 @@ public class ClassGen { code.startLine(f.getAccessFlags().makeString()); useType(code, f.getType()); code.add(' '); - code.add(f.getName()); + code.add(f.getAlias()); FieldValueAttr fv = f.get(AType.FIELD_VALUE); if (fv != null) { code.add(" = "); 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 f8458258c..603fd258d 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -157,7 +157,7 @@ public class InsnGen { if (fieldNode != null) { code.attachAnnotation(fieldNode); } - code.add(field.getName()); + code.add(field.getAlias()); } public static void makeStaticFieldAccess(CodeWriter code, FieldInfo field, ClassGen clsGen) { @@ -179,7 +179,7 @@ public class InsnGen { if (fieldNode != null) { code.attachAnnotation(fieldNode); } - code.add(field.getName()); + code.add(field.getAlias()); } protected void staticField(CodeWriter code, FieldInfo field) { @@ -612,7 +612,7 @@ public class InsnGen { if (callMthNode != null) { code.attachAnnotation(callMthNode); } - code.add(callMth.getName()); + code.add(callMth.getAlias()); generateMethodArguments(code, insn, k, callMthNode); } 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 55ffc4bef..5589cfb30 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java @@ -90,7 +90,7 @@ public class MethodGen { } else { classGen.useType(code, mth.getReturnType()); code.add(' '); - code.add(mth.getName()); + code.add(mth.getAlias()); } code.add('('); diff --git a/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java b/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java index 5bfedec2c..41b801794 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java @@ -245,7 +245,7 @@ public class RegionGen extends InsnGen { if (k instanceof FieldNode) { FieldNode fn = (FieldNode) k; if (fn.getParentClass().isEnum()) { - code.add(fn.getName()); + code.add(fn.getAlias()); } else { staticField(code, fn.getFieldInfo()); // print original value, sometimes replace with incorrect field diff --git a/jadx-core/src/main/java/jadx/core/deobf/DeobfClsInfo.java b/jadx-core/src/main/java/jadx/core/deobf/DeobfClsInfo.java new file mode 100644 index 000000000..4fa823892 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/deobf/DeobfClsInfo.java @@ -0,0 +1,50 @@ +package jadx.core.deobf; + +import jadx.core.dex.nodes.ClassNode; + +class DeobfClsInfo { + private final Deobfuscator deobfuscator; + private final ClassNode cls; + private final PackageNode pkg; + private final String alias; + + public DeobfClsInfo(Deobfuscator deobfuscator, ClassNode cls, PackageNode pkg, String alias) { + this.deobfuscator = deobfuscator; + this.cls = cls; + this.pkg = pkg; + this.alias = alias; + } + + public String makeNameWithoutPkg() { + String prefix; + ClassNode parentClass = cls.getParentClass(); + if (parentClass != cls) { + DeobfClsInfo parentDeobfClsInfo = deobfuscator.getClsMap().get(parentClass.getClassInfo()); + if (parentDeobfClsInfo != null) { + prefix = parentDeobfClsInfo.makeNameWithoutPkg(); + } else { + prefix = deobfuscator.getNameWithoutPackage(parentClass.getClassInfo()); + } + prefix += Deobfuscator.INNER_CLASS_SEPARATOR; + } else { + prefix = ""; + } + return prefix + (this.alias != null ? this.alias : this.cls.getShortName()); + } + + public String getFullName() { + return pkg.getFullAlias() + Deobfuscator.CLASS_NAME_SEPARATOR + makeNameWithoutPkg(); + } + + public ClassNode getCls() { + return cls; + } + + public PackageNode getPkg() { + return pkg; + } + + public String getAlias() { + return alias; + } +} diff --git a/jadx-core/src/main/java/jadx/core/deobf/DeobfPresets.java b/jadx-core/src/main/java/jadx/core/deobf/DeobfPresets.java new file mode 100644 index 000000000..5918204ed --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/deobf/DeobfPresets.java @@ -0,0 +1,167 @@ +package jadx.core.deobf; + +import jadx.core.dex.info.ClassInfo; +import jadx.core.dex.info.FieldInfo; +import jadx.core.dex.info.MethodInfo; + +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 org.apache.commons.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class DeobfPresets { + private static final Logger LOG = LoggerFactory.getLogger(DeobfPresets.class); + + private static final String MAP_FILE_CHARSET = "UTF-8"; + + private final Deobfuscator deobfuscator; + private final File deobfMapFile; + + private final Map clsPresetMap = new HashMap(); + private final Map fldPresetMap = new HashMap(); + private final Map mthPresetMap = new HashMap(); + + public DeobfPresets(Deobfuscator deobfuscator, File deobfMapFile) { + this.deobfuscator = deobfuscator; + this.deobfMapFile = deobfMapFile; + } + + /** + * Loads deobfuscator presets + */ + public void load() { + if (!deobfMapFile.exists()) { + return; + } + LOG.info("Loading obfuscation map from: {}", deobfMapFile.getAbsoluteFile()); + try { + List lines = FileUtils.readLines(deobfMapFile, MAP_FILE_CHARSET); + for (String l : lines) { + l = l.trim(); + if (l.isEmpty() || l.startsWith("#")) { + continue; + } + String[] va = splitAndTrim(l); + if (va.length != 2) { + continue; + } + String origName = va[0]; + String alias = va[1]; + if (l.startsWith("p ")) { + deobfuscator.addPackagePreset(origName, alias); + } else if (l.startsWith("c ")) { + clsPresetMap.put(origName, alias); + } else if (l.startsWith("f ")) { + fldPresetMap.put(origName, alias); + } else if (l.startsWith("m ")) { + mthPresetMap.put(origName, alias); + } + } + } catch (IOException e) { + LOG.error("Failed to load deobfuscation map file '{}'", deobfMapFile.getAbsolutePath(), e); + } + } + + private static String[] splitAndTrim(String str) { + String[] v = str.substring(2).split("="); + for (int i = 0; i < v.length; i++) { + v[i] = v[i].trim(); + } + return v; + } + + public void save(boolean forceSave) { + try { + if (deobfMapFile.exists()) { + if (forceSave) { + dumpMapping(); + } else { + LOG.warn("Deobfuscation map file '{}' exists. Use command line option '--deobf-rewrite-cfg' to rewrite it", + deobfMapFile.getAbsolutePath()); + } + } else { + dumpMapping(); + } + } catch (IOException e) { + LOG.error("Failed to load deobfuscation map file '{}'", deobfMapFile.getAbsolutePath(), e); + } + } + + /** + * Saves DefaultDeobfuscator presets + */ + private void dumpMapping() throws IOException { + List list = new ArrayList(); + // packages + for (PackageNode p : deobfuscator.getRootPackage().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 : deobfuscator.getClsMap().values()) { + if (deobfClsInfo.getAlias() != null) { + list.add(String.format("c %s = %s", + deobfClsInfo.getCls().getClassInfo().getFullName(), deobfClsInfo.getAlias())); + } + } + for (FieldInfo fld : deobfuscator.getFldMap().keySet()) { + list.add(String.format("f %s = %s", fld.getFullId(), fld.getAlias())); + } + for (MethodInfo mth : deobfuscator.getMthMap().keySet()) { + list.add(String.format("m %s = %s", mth.getFullId(), mth.getAlias())); + } + Collections.sort(list); + FileUtils.writeLines(deobfMapFile, MAP_FILE_CHARSET, list); + list.clear(); + } + + 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())); + } + } + + public String getForCls(ClassInfo cls) { + return clsPresetMap.get(cls.getFullName()); + } + + public String getForFld(FieldInfo fld) { + return fldPresetMap.get(fld.getFullId()); + } + + public String getForMth(MethodInfo mth) { + return mthPresetMap.get(mth.getFullId()); + } + + public void clear() { + clsPresetMap.clear(); + fldPresetMap.clear(); + mthPresetMap.clear(); + } + + public Map getClsPresetMap() { + return clsPresetMap; + } + + public Map getFldPresetMap() { + return fldPresetMap; + } + + public Map getMthPresetMap() { + return mthPresetMap; + } +} 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 44d3d593b..90a5f4c9b 100644 --- a/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java +++ b/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java @@ -4,21 +4,22 @@ import jadx.api.IJadxArgs; import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.nodes.SourceFileAttr; import jadx.core.dex.info.ClassInfo; +import jadx.core.dex.info.FieldInfo; +import jadx.core.dex.info.MethodInfo; import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.DexNode; +import jadx.core.dex.nodes.FieldNode; +import jadx.core.dex.nodes.MethodNode; 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.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,56 +28,60 @@ public class Deobfuscator { private static final boolean DEBUG = false; - private static final String MAP_FILE_CHARSET = "UTF-8"; - private static final String CLASS_NAME_SEPARATOR = "."; - private static final String INNER_CLASS_SEPARATOR = "$"; + public static final String CLASS_NAME_SEPARATOR = "."; + public static final String INNER_CLASS_SEPARATOR = "$"; - private final Map clsMap = new HashMap(); private final IJadxArgs args; - private final File deobfMapFile; @NotNull private final List dexNodes; + private final DeobfPresets deobfPresets; + + private final Map clsMap = new HashMap(); + private final Map fldMap = new HashMap(); + private final Map mthMap = new HashMap(); + + private final PackageNode rootPackage = new PackageNode(""); + private final Set pkgSet = new TreeSet(); private final int maxLength; private final int minLength; private int pkgIndex = 0; private int clsIndex = 0; - - private final PackageNode rootPackage = new PackageNode(""); - private final Set pkgSet = new TreeSet(); - private Map preLoadClsMap = Collections.emptyMap(); + private int fldIndex = 0; + private int mthIndex = 0; public Deobfuscator(IJadxArgs args, @NotNull List dexNodes, File deobfMapFile) { this.args = args; this.dexNodes = dexNodes; - this.deobfMapFile = deobfMapFile; this.minLength = args.getDeobfuscationMinLength(); this.maxLength = args.getDeobfuscationMaxLength(); + + this.deobfPresets = new DeobfPresets(this, deobfMapFile); } public void execute() { - if (deobfMapFile.exists() && !args.isDeobfuscationForceSave()) { - try { - load(); - } catch (IOException e) { - LOG.error("Failed to load deobfuscation map file '{}'", deobfMapFile.getAbsolutePath(), e); - } + if (!args.isDeobfuscationForceSave()) { + deobfPresets.load(); + initIndexes(); } 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(); + deobfPresets.save(args.isDeobfuscationForceSave()); + clear(); + } + + private void initIndexes() { + pkgIndex = pkgSet.size(); + clsIndex = deobfPresets.getClsPresetMap().size(); + fldIndex = deobfPresets.getFldPresetMap().size(); + mthIndex = deobfPresets.getMthPresetMap().size(); + } + + private void preProcess() { + for (DexNode dexNode : dexNodes) { + for (ClassNode cls : dexNode.getClasses()) { + doClass(cls); } - } catch (IOException e) { - LOG.error("Failed to load deobfuscation map file '{}'", deobfMapFile.getAbsolutePath(), e); } } @@ -85,20 +90,47 @@ public class Deobfuscator { 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); - if (!fullName.equals(clsInfo.getFullName())) { - clsInfo.rename(dexNode, fullName); - } + for (ClassNode cls : dexNode.getClasses()) { + processClass(dexNode, cls); } } } + void clear() { + deobfPresets.clear(); + clsMap.clear(); + fldMap.clear(); + mthMap.clear(); + } + + private void processClass(DexNode dex, ClassNode cls) { + ClassInfo clsInfo = cls.getClassInfo(); + String fullName = getClassFullName(clsInfo); + if (!fullName.equals(clsInfo.getFullName())) { + clsInfo.rename(dex, fullName); + } + for (FieldNode field : cls.getFields()) { + FieldInfo fieldInfo = field.getFieldInfo(); + String alias = getFieldAlias(field); + if (alias != null) { + fieldInfo.setAlias(alias); + } + } + for (MethodNode mth : cls.getMethods()) { + MethodInfo methodInfo = mth.getMethodInfo(); + String alias = getMethodAlias(mth); + if (alias != null) { + methodInfo.setAlias(alias); + } + } + } + + public void addPackagePreset(String origPkgName, String pkgAlias) { + PackageNode pkg = getPackageNode(origPkgName, true); + pkg.setAlias(pkgAlias); + } + /** * Gets package node for full package name * @@ -134,41 +166,7 @@ public class Deobfuscator { return result; } - private final class DeobfClsInfo { - public final ClassNode cls; - public final PackageNode pkg; - public final String alias; - - public DeobfClsInfo(ClassNode cls, PackageNode pkg, String alias) { - this.cls = cls; - this.pkg = pkg; - this.alias = alias; - } - - 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 += INNER_CLASS_SEPARATOR; - } else { - prefix = ""; - } - - return prefix + (this.alias != null ? this.alias : this.cls.getShortName()); - } - - public String getFullName() { - return pkg.getFullAlias() + CLASS_NAME_SEPARATOR + makeNameWithoutPkg(); - } - } - - private String getNameWithoutPackage(ClassInfo clsInfo) { + String getNameWithoutPackage(ClassInfo clsInfo) { String prefix; ClassInfo parentClsInfo = clsInfo.getParentClass(); if (parentClsInfo != null) { @@ -191,18 +189,17 @@ public class Deobfuscator { PackageNode pkg = getPackageNode(pkgFullName, true); doPkg(pkg, pkgFullName); - String fullName = classInfo.getFullName(); - if (preLoadClsMap.containsKey(fullName)) { - String alias = preLoadClsMap.get(fullName); - clsMap.put(classInfo, new DeobfClsInfo(cls, pkg, alias)); + String alias = deobfPresets.getForCls(classInfo); + if (alias != null) { + clsMap.put(classInfo, new DeobfClsInfo(this, cls, pkg, alias)); return; } if (clsMap.containsKey(classInfo)) { return; } if (shouldRename(classInfo.getShortName())) { - String alias = makeClsAlias(cls); - clsMap.put(classInfo, new DeobfClsInfo(cls, pkg, alias)); + alias = makeClsAlias(cls); + clsMap.put(classInfo, new DeobfClsInfo(this, cls, pkg, alias)); } } @@ -224,6 +221,54 @@ public class Deobfuscator { return String.format("C%04d%s", clsIndex++, makeName(clsName)); } + @Nullable + public String getFieldAlias(FieldNode field) { + FieldInfo fieldInfo = field.getFieldInfo(); + String alias = fldMap.get(fieldInfo); + if (alias != null) { + return alias; + } + alias = deobfPresets.getForFld(fieldInfo); + if (alias != null) { + fldMap.put(fieldInfo, alias); + return alias; + } + if (shouldRename(field.getName())) { + return makeFieldAlias(field); + } + return null; + } + + @Nullable + public String getMethodAlias(MethodNode mth) { + MethodInfo methodInfo = mth.getMethodInfo(); + String alias = mthMap.get(methodInfo); + if (alias != null) { + return alias; + } + alias = deobfPresets.getForMth(methodInfo); + if (alias != null) { + mthMap.put(methodInfo, alias); + return alias; + } + if (shouldRename(mth.getName())) { + return makeMethodAlias(mth); + } + return null; + } + + public String makeFieldAlias(FieldNode field) { + String alias = String.format("f%d%s", fldIndex++, makeName(field.getName())); + fldMap.put(field.getFieldInfo(), alias); + return alias; + } + + public String makeMethodAlias(MethodNode mth) { + String alias = String.format("m%d%s", mthIndex++, makeName(mth.getName())); + mthMap.put(mth.getMethodInfo(), alias); + return alias; + } + private void doPkg(PackageNode pkg, String fullName) { if (pkgSet.contains(fullName)) { return; @@ -246,14 +291,6 @@ public class Deobfuscator { } } - private void preProcess() { - for (DexNode dexNode : dexNodes) { - for (ClassNode cls : dexNode.getClasses()) { - doClass(cls); - } - } - } - private boolean shouldRename(String s) { return s.length() > maxLength || s.length() < minLength @@ -305,80 +342,6 @@ public class Deobfuscator { } } - /** - * Loads deobfuscator presets - * - * @throws IOException - */ - private void load() throws IOException { - if (!deobfMapFile.exists()) { - return; - } - LOG.info("Loading obfuscation map from: {}", deobfMapFile.getAbsoluteFile()); - List lines = FileUtils.readLines(deobfMapFile, MAP_FILE_CHARSET); - for (String l : lines) { - l = l.trim(); - if (l.startsWith("p ")) { - String[] va = splitAndTrim(l); - if (va.length == 2) { - PackageNode pkg = getPackageNode(va[0], true); - pkg.setAlias(va[1]); - } - } else if (l.startsWith("c ")) { - String[] va = splitAndTrim(l); - if (va.length == 2) { - if (preLoadClsMap.isEmpty()) { - preLoadClsMap = new HashMap(); - } - preLoadClsMap.put(va[0], va[1]); - } - } - } - } - - private static String[] splitAndTrim(String str) { - String[] v = str.substring(2).split("="); - for (int i = 0; i < v.length; i++) { - v[i] = v[i].trim(); - } - return v; - } - - 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 - */ - private 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.getClassInfo().getFullName(), deobfClsInfo.alias)); - } - } - Collections.sort(list); - FileUtils.writeLines(deobfMapFile, MAP_FILE_CHARSET, list); - list.clear(); - } - private String getPackageName(String packageName) { final PackageNode pkg = getPackageNode(packageName, false); if (pkg != null) { @@ -406,4 +369,20 @@ public class Deobfuscator { } return getPackageName(clsInfo.getPackage()) + CLASS_NAME_SEPARATOR + getClassName(clsInfo); } + + public Map getClsMap() { + return clsMap; + } + + public Map getFldMap() { + return fldMap; + } + + public Map getMthMap() { + return mthMap; + } + + public PackageNode getRootPackage() { + return rootPackage; + } } diff --git a/jadx-core/src/main/java/jadx/core/deobf/NameMapper.java b/jadx-core/src/main/java/jadx/core/deobf/NameMapper.java index 15f789168..1571a3604 100644 --- a/jadx-core/src/main/java/jadx/core/deobf/NameMapper.java +++ b/jadx-core/src/main/java/jadx/core/deobf/NameMapper.java @@ -88,10 +88,11 @@ public class NameMapper { } public static boolean isAllCharsPrintable(String str) { - for (int i = 0; i < str.length(); i++) { - if (!isPrintableChar(str.charAt(i))) { - return false; - } + int len = str.length(); + for (int i = 0; i < len; i++) { + if (!isPrintableChar(str.charAt(i))) { + return false; + } } return true; } 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 8028bbcf9..51897f914 100644 --- a/jadx-core/src/main/java/jadx/core/deobf/PackageNode.java +++ b/jadx-core/src/main/java/jadx/core/deobf/PackageNode.java @@ -7,11 +7,11 @@ import java.util.Stack; public class PackageNode { + private static final char SEPARATOR_CHAR = '.'; + private PackageNode parentPackage; private List innerPackages = Collections.emptyList(); - private static final char separatorChar = '.'; - private final String packageName; private String packageAlias; @@ -34,7 +34,7 @@ public class PackageNode { StringBuilder result = new StringBuilder(); result.append(pp.pop().getName()); while (pp.size() > 0) { - result.append(separatorChar); + result.append(SEPARATOR_CHAR); result.append(pp.pop().getName()); } cachedPackageFullName = result.toString(); @@ -63,7 +63,7 @@ public class PackageNode { StringBuilder result = new StringBuilder(); result.append(pp.pop().getAlias()); while (pp.size() > 0) { - result.append(separatorChar); + result.append(SEPARATOR_CHAR); result.append(pp.pop().getAlias()); } cachedPackageFullAlias = result.toString(); 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 5073831b8..ba1060910 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 @@ -1,5 +1,6 @@ package jadx.core.dex.info; +import jadx.core.codegen.TypeGen; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.nodes.DexNode; @@ -10,11 +11,13 @@ public final class FieldInfo { private final ClassInfo declClass; private final String name; private final ArgType type; + private String alias; private FieldInfo(ClassInfo declClass, String name, ArgType type) { this.declClass = declClass; this.name = name; this.type = type; + this.alias = name; } public static FieldInfo from(DexNode dex, ClassInfo declClass, String name, ArgType type) { @@ -42,6 +45,22 @@ public final class FieldInfo { return declClass; } + public String getAlias() { + return alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + public String getFullId() { + return declClass.getFullName() + "." + name + ":" + TypeGen.signature(type); + } + + public boolean isRenamed() { + return !name.equals(alias); + } + @Override public boolean equals(Object o) { if (this == o) { 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 7859e87c9..e6a8e5fb2 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 @@ -17,10 +17,12 @@ public final class MethodInfo { private final List args; private final ClassInfo declClass; private final String shortId; + private String alias; private MethodInfo(DexNode dex, int mthIndex) { MethodId mthId = dex.getMethodId(mthIndex); name = dex.getString(mthId.getNameIndex()); + alias = name; declClass = ClassInfo.fromDex(dex, mthId.getDeclaringClassIndex()); ProtoId proto = dex.getProtoId(mthId.getProtoIndex()); @@ -91,6 +93,18 @@ public final class MethodInfo { return name.equals(""); } + public String getAlias() { + return alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + public boolean isRenamed() { + return !name.equals(alias); + } + @Override public int hashCode() { int result = declClass.hashCode(); diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/FieldNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/FieldNode.java index d73e3aee3..a19f49ffb 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/FieldNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/FieldNode.java @@ -42,6 +42,10 @@ public class FieldNode extends LineAttrNode { return fieldInfo.getName(); } + public String getAlias() { + return fieldInfo.getAlias(); + } + public ArgType getType() { return type; } 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 3b2a578cd..10a026183 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 @@ -369,6 +369,10 @@ public class MethodNode extends LineAttrNode implements ILoadable { return mthInfo.getName(); } + public String getAlias() { + return mthInfo.getAlias(); + } + public ClassNode getParentClass() { return parentClass; } 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 96a9f79a2..cad4ec626 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 @@ -2,6 +2,9 @@ package jadx.core.dex.visitors; import jadx.core.dex.attributes.AType; import jadx.core.dex.info.ClassInfo; +import jadx.core.dex.info.FieldInfo; +import jadx.core.dex.instructions.IndexInsnNode; +import jadx.core.dex.instructions.InvokeNode; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.InsnWrapArg; @@ -73,6 +76,21 @@ public class DependencyCollector extends AbstractVisitor { addDep(dex, depList, arg.getType()); } } + processCustomInsn(dex, depList, insnNode); + } + + private static void processCustomInsn(DexNode dex, Set depList, InsnNode insn) { + if (insn instanceof IndexInsnNode) { + Object index = ((IndexInsnNode) insn).getIndex(); + if (index instanceof FieldInfo) { + addDep(dex, depList, ((FieldInfo) index).getDeclClass()); + } else if (index instanceof ArgType) { + addDep(dex, depList, (ArgType) index); + } + } else if (insn instanceof InvokeNode) { + ClassInfo declClass = ((InvokeNode) insn).getCallMth().getDeclClass(); + addDep(dex, depList, declClass); + } } private static void addDep(DexNode dex, Set depList, ArgType type) { @@ -94,15 +112,14 @@ public class DependencyCollector extends AbstractVisitor { private static void addDep(DexNode dex, Set depList, ClassInfo clsInfo) { if (clsInfo != null) { ClassNode node = dex.resolveClass(clsInfo); - if (node != null) { - depList.add(node); - } + addDep(dex, depList, node); } } private static void addDep(DexNode dex, Set depList, ClassNode clsNode) { if (clsNode != null) { - depList.add(clsNode); + // add only top classes + depList.add(clsNode.getTopParentClass()); } } } 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 index 22e524c5f..f69b193dc 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java @@ -1,34 +1,82 @@ package jadx.core.dex.visitors; +import jadx.api.IJadxArgs; +import jadx.core.codegen.TypeGen; import jadx.core.deobf.Deobfuscator; +import jadx.core.dex.attributes.AFlag; +import jadx.core.dex.info.FieldInfo; +import jadx.core.dex.info.MethodInfo; +import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.FieldNode; +import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.RootNode; import jadx.core.utils.exceptions.JadxException; import java.io.File; +import java.util.HashSet; +import java.util.Set; -import org.apache.commons.io.FilenameUtils; +import org.jetbrains.annotations.NotNull; public class RenameVisitor extends AbstractVisitor { + @NotNull 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(); + IJadxArgs args = root.getArgs(); + File deobfMapFile = new File(args.getOutDir(), "deobf_map.jobf"); + deobfuscator = new Deobfuscator(args, root.getDexNodes(), deobfMapFile); + if (args.isDeobfuscationOn()) { + // TODO: check classes for case sensitive names (issue #24) + deobfuscator.execute(); + } } @Override public boolean visit(ClassNode cls) throws JadxException { - // TODO: rename fields and methods + checkFields(cls); + checkMethods(cls); + for (ClassNode inner : cls.getInnerClasses()) { + visit(inner); + } return false; } + + private void checkFields(ClassNode cls) { + Set names = new HashSet(); + for (FieldNode field : cls.getFields()) { + FieldInfo fieldInfo = field.getFieldInfo(); + if (!names.add(fieldInfo.getAlias())) { + fieldInfo.setAlias(deobfuscator.makeFieldAlias(field)); + } + } + } + + private void checkMethods(ClassNode cls) { + Set names = new HashSet(); + for (MethodNode mth : cls.getMethods()) { + if (mth.contains(AFlag.DONT_GENERATE)) { + continue; + } + MethodInfo methodInfo = mth.getMethodInfo(); + String signature = makeMethodSignature(methodInfo); + if (!names.add(signature)) { + methodInfo.setAlias(deobfuscator.makeMethodAlias(mth)); + } + } + } + + private static String makeMethodSignature(MethodInfo methodInfo) { + StringBuilder signature = new StringBuilder(); + signature.append(methodInfo.getAlias()); + signature.append('('); + for (ArgType arg : methodInfo.getArgumentsTypes()) { + signature.append(TypeGen.signature(arg)); + } + signature.append(')'); + return signature.toString(); + } }