core: rename fields and methods in deobfuscation pass.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -17,7 +17,7 @@ public final class JavaMethod implements JavaNode {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return mth.getName();
|
||||
return mth.getAlias();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -108,9 +108,7 @@ public class Jadx {
|
||||
|
||||
passes.add(new DependencyCollector());
|
||||
|
||||
if (args.isDeobfuscationOn()) {
|
||||
passes.add(new RenameVisitor());
|
||||
}
|
||||
passes.add(new RenameVisitor());
|
||||
}
|
||||
return passes;
|
||||
}
|
||||
|
||||
@@ -53,10 +53,6 @@ public final class ProcessClass {
|
||||
|
||||
static void processDependencies(ClassNode cls, List<IDexTreeVisitor> passes) {
|
||||
for (ClassNode depCls : cls.getDependencies()) {
|
||||
if (cls.getTopParentClass() == cls) {
|
||||
// ignore inner classes of this class
|
||||
continue;
|
||||
}
|
||||
process(depCls, passes, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(" = ");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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('(');
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<String, String> clsPresetMap = new HashMap<String, String>();
|
||||
private final Map<String, String> fldPresetMap = new HashMap<String, String>();
|
||||
private final Map<String, String> mthPresetMap = new HashMap<String, String>();
|
||||
|
||||
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<String> 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<String> list = new ArrayList<String>();
|
||||
// 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<String> 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<String, String> getClsPresetMap() {
|
||||
return clsPresetMap;
|
||||
}
|
||||
|
||||
public Map<String, String> getFldPresetMap() {
|
||||
return fldPresetMap;
|
||||
}
|
||||
|
||||
public Map<String, String> getMthPresetMap() {
|
||||
return mthPresetMap;
|
||||
}
|
||||
}
|
||||
@@ -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<ClassInfo, DeobfClsInfo> clsMap = new HashMap<ClassInfo, DeobfClsInfo>();
|
||||
private final IJadxArgs args;
|
||||
private final File deobfMapFile;
|
||||
@NotNull
|
||||
private final List<DexNode> dexNodes;
|
||||
private final DeobfPresets deobfPresets;
|
||||
|
||||
private final Map<ClassInfo, DeobfClsInfo> clsMap = new HashMap<ClassInfo, DeobfClsInfo>();
|
||||
private final Map<FieldInfo, String> fldMap = new HashMap<FieldInfo, String>();
|
||||
private final Map<MethodInfo, String> mthMap = new HashMap<MethodInfo, String>();
|
||||
|
||||
private final PackageNode rootPackage = new PackageNode("");
|
||||
private final Set<String> pkgSet = new TreeSet<String>();
|
||||
|
||||
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<String> pkgSet = new TreeSet<String>();
|
||||
private Map<String, String> preLoadClsMap = Collections.emptyMap();
|
||||
private int fldIndex = 0;
|
||||
private int mthIndex = 0;
|
||||
|
||||
public Deobfuscator(IJadxArgs args, @NotNull List<DexNode> 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<String> 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<String, String>();
|
||||
}
|
||||
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<String> 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<String> list = new ArrayList<String>();
|
||||
// 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<ClassInfo, DeobfClsInfo> getClsMap() {
|
||||
return clsMap;
|
||||
}
|
||||
|
||||
public Map<FieldInfo, String> getFldMap() {
|
||||
return fldMap;
|
||||
}
|
||||
|
||||
public Map<MethodInfo, String> getMthMap() {
|
||||
return mthMap;
|
||||
}
|
||||
|
||||
public PackageNode getRootPackage() {
|
||||
return rootPackage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -7,11 +7,11 @@ import java.util.Stack;
|
||||
|
||||
public class PackageNode {
|
||||
|
||||
private static final char SEPARATOR_CHAR = '.';
|
||||
|
||||
private PackageNode parentPackage;
|
||||
private List<PackageNode> 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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -17,10 +17,12 @@ public final class MethodInfo {
|
||||
private final List<ArgType> 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("<clinit>");
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@@ -42,6 +42,10 @@ public class FieldNode extends LineAttrNode {
|
||||
return fieldInfo.getName();
|
||||
}
|
||||
|
||||
public String getAlias() {
|
||||
return fieldInfo.getAlias();
|
||||
}
|
||||
|
||||
public ArgType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<ClassNode> 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<ClassNode> depList, ArgType type) {
|
||||
@@ -94,15 +112,14 @@ public class DependencyCollector extends AbstractVisitor {
|
||||
private static void addDep(DexNode dex, Set<ClassNode> 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<ClassNode> depList, ClassNode clsNode) {
|
||||
if (clsNode != null) {
|
||||
depList.add(clsNode);
|
||||
// add only top classes
|
||||
depList.add(clsNode.getTopParentClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> names = new HashSet<String>();
|
||||
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<String> names = new HashSet<String>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user