Commit 3314de8d authored by Skylot's avatar Skylot

core: rename fields and methods in deobfuscation pass.

parent 8dab9b83
......@@ -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
......
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;
}
}
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;
}
}
......@@ -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();
......
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());
}
}
}
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();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment