Commit bc8ad4df authored by skylot's avatar skylot

Merge pull request #64 from NeoSpb/fix_deobfuscator

Fix deobfuscator
parents c923d19b 53ac3ec5
......@@ -64,6 +64,9 @@ public class JadxCLIArgs implements IJadxArgs {
@Parameter(names = {"--deobf-rewrite-cfg"}, description = "force to save deobfuscation map")
protected boolean deobfuscationForceSave = false;
@Parameter(names = {"--deobf-use-sourcename"}, description = "use source file name as class name alias")
protected boolean deobfuscationUseSourceNameAsAlias = false;
@Parameter(names = {"-h", "--help"}, description = "print this help", help = true)
protected boolean printHelp = false;
......@@ -242,4 +245,9 @@ public class JadxCLIArgs implements IJadxArgs {
public boolean isDeobfuscationForceSave() {
return deobfuscationForceSave;
}
@Override
public boolean useSourceNameAsClassAlias() {
return deobfuscationUseSourceNameAsAlias;
}
}
......@@ -68,4 +68,9 @@ public class DefaultJadxArgs implements IJadxArgs {
public boolean isDeobfuscationForceSave() {
return false;
}
@Override
public boolean useSourceNameAsClassAlias() {
return false;
}
}
......@@ -28,4 +28,6 @@ public interface IJadxArgs {
int getDeobfuscationMaxLength();
boolean isDeobfuscationForceSave();
boolean useSourceNameAsClassAlias();
}
......@@ -6,13 +6,17 @@ 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.instructions.args.ArgType;
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.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
......@@ -40,11 +44,16 @@ public class Deobfuscator {
private final Map<FieldInfo, String> fldMap = new HashMap<FieldInfo, String>();
private final Map<MethodInfo, String> mthMap = new HashMap<MethodInfo, String>();
private final Map<MethodInfo, OverridedMethodsNode> ovrdMap = new HashMap<MethodInfo, OverridedMethodsNode>();
private final List<OverridedMethodsNode> ovrd = new ArrayList<OverridedMethodsNode>();
private final PackageNode rootPackage = new PackageNode("");
private final Set<String> pkgSet = new TreeSet<String>();
private final int maxLength;
private final int minLength;
private final boolean useSourceNameAsAlias;
private int pkgIndex = 0;
private int clsIndex = 0;
private int fldIndex = 0;
......@@ -56,6 +65,7 @@ public class Deobfuscator {
this.minLength = args.getDeobfuscationMinLength();
this.maxLength = args.getDeobfuscationMaxLength();
this.useSourceNameAsAlias = args.useSourceNameAsClassAlias();
this.deobfPresets = new DeobfPresets(this, deobfMapFile);
}
......@@ -95,6 +105,32 @@ public class Deobfuscator {
processClass(dexNode, cls);
}
}
postProcess();
}
private void postProcess() {
int id = 1;
for (OverridedMethodsNode o : ovrd) {
Iterator<MethodInfo> it = o.getMethods().iterator();
if (it.hasNext()) {
MethodInfo mth = it.next();
if (mth.isRenamed() && !mth.isAliasFromPreset()) {
mth.setAlias(String.format("mo%d%s", id, makeName(mth.getName())));
}
String firstMethodAlias = mth.getAlias();
while (it.hasNext()) {
mth = it.next();
if (!mth.getAlias().equals(firstMethodAlias)) {
mth.setAlias(firstMethodAlias);
}
}
}
id++;
}
}
void clear() {
......@@ -102,6 +138,98 @@ public class Deobfuscator {
clsMap.clear();
fldMap.clear();
mthMap.clear();
ovrd.clear();
ovrdMap.clear();
}
@Nullable
private static ClassNode resolveOverridingInternal(DexNode dex, ClassNode cls, String signature,
Set<MethodInfo> overrideSet, ClassNode rootClass) {
ClassNode result = null;
for (MethodNode m : cls.getMethods()) {
if (m.getMethodInfo().getShortId().startsWith(signature)) {
result = cls;
if (!overrideSet.contains(m.getMethodInfo())) {
overrideSet.add(m.getMethodInfo());
}
break;
}
}
ArgType superClass = cls.getSuperClass();
if (superClass != null) {
ClassNode superNode = dex.resolveClass(superClass);
if (superNode != null) {
ClassNode clsWithMth = resolveOverridingInternal(dex, superNode, signature, overrideSet, rootClass);
if (clsWithMth != null) {
if ((result != null) && (result != cls)) {
if (clsWithMth != result) {
LOG.warn(String.format("Multiple overriding '%s' from '%s' and '%s' in '%s'",
signature,
result.getFullName(), clsWithMth.getFullName(),
rootClass.getFullName()));
}
} else {
result = clsWithMth;
}
}
}
}
for (ArgType iFaceType : cls.getInterfaces()) {
ClassNode iFaceNode = dex.resolveClass(iFaceType);
if (iFaceNode != null) {
ClassNode clsWithMth = resolveOverridingInternal(dex, iFaceNode, signature, overrideSet, rootClass);
if (clsWithMth != null) {
if ((result != null) && (result != cls)) {
if (clsWithMth != result) {
LOG.warn(String.format("Multiple overriding '%s' from '%s' and '%s' in '%s'",
signature,
result.getFullName(), clsWithMth.getFullName(),
rootClass.getFullName()));
}
} else {
result = clsWithMth;
}
}
}
}
return result;
}
private void resolveOverriding(DexNode dex, ClassNode cls, MethodNode mth) {
Set<MethodInfo> overrideSet = new HashSet<MethodInfo>();
resolveOverridingInternal(dex, cls, mth.getMethodInfo().makeSignature(false), overrideSet, cls);
if (overrideSet.size() > 1) {
OverridedMethodsNode overrideNode = null;
for (MethodInfo _mth : overrideSet) {
if (ovrdMap.containsKey(_mth)) {
overrideNode = ovrdMap.get(_mth);
break;
}
}
if (overrideNode == null) {
overrideNode = new OverridedMethodsNode(overrideSet);
ovrd.add(overrideNode);
}
for (MethodInfo _mth : overrideSet) {
if (!ovrdMap.containsKey(_mth)) {
ovrdMap.put(_mth, overrideNode);
if (!overrideNode.contains(_mth)) {
overrideNode.add(_mth);
}
}
}
} else {
overrideSet.clear();
overrideSet = null;
}
}
private void processClass(DexNode dex, ClassNode cls) {
......@@ -123,6 +251,10 @@ public class Deobfuscator {
if (alias != null) {
methodInfo.setAlias(alias);
}
if (mth.isVirtual()) {
resolveOverriding(dex, cls, mth);
}
}
}
......@@ -212,7 +344,12 @@ public class Deobfuscator {
private String makeClsAlias(ClassNode cls) {
ClassInfo classInfo = cls.getClassInfo();
String alias = getAliasFromSourceFile(cls);
String alias = null;
if (this.useSourceNameAsAlias) {
alias = getAliasFromSourceFile(cls);
}
if (alias == null) {
String clsName = classInfo.getShortName();
alias = String.format("C%04d%s", clsIndex++, makeName(clsName));
......@@ -269,6 +406,7 @@ public class Deobfuscator {
alias = deobfPresets.getForMth(methodInfo);
if (alias != null) {
mthMap.put(methodInfo, alias);
methodInfo.setAliasFromPreset(true);
return alias;
}
if (shouldRename(mth.getName())) {
......
package jadx.core.deobf;
import jadx.core.dex.info.MethodInfo;
import java.util.Set;
/* package */ class OverridedMethodsNode {
private Set<MethodInfo> methods;
public OverridedMethodsNode(Set<MethodInfo> methodsSet) {
methods = methodsSet;
}
public boolean contains(MethodInfo mth) {
return methods.contains(mth);
}
public void add(MethodInfo mth) {
methods.add(mth);
}
public Set<MethodInfo> getMethods() {
return methods;
}
}
......@@ -18,11 +18,13 @@ public final class MethodInfo {
private final ClassInfo declClass;
private final String shortId;
private String alias;
private boolean aliasFromPreset;
private MethodInfo(DexNode dex, int mthIndex) {
MethodId mthId = dex.getMethodId(mthIndex);
name = dex.getString(mthId.getNameIndex());
alias = name;
aliasFromPreset = false;
declClass = ClassInfo.fromDex(dex, mthId.getDeclaringClassIndex());
ProtoId proto = dex.getProtoId(mthId.getProtoIndex());
......@@ -109,6 +111,14 @@ public final class MethodInfo {
return !name.equals(alias);
}
public boolean isAliasFromPreset() {
return aliasFromPreset;
}
public void setAliasFromPreset(boolean value) {
aliasFromPreset = value;
}
@Override
public int hashCode() {
int result = declClass.hashCode();
......
......@@ -86,10 +86,10 @@ public class ClassNode extends LineAttrNode implements ILoadable {
fields = new ArrayList<FieldNode>(fieldsCount);
for (Method mth : clsData.getDirectMethods()) {
methods.add(new MethodNode(this, mth));
methods.add(new MethodNode(this, mth, false));
}
for (Method mth : clsData.getVirtualMethods()) {
methods.add(new MethodNode(this, mth));
methods.add(new MethodNode(this, mth, true));
}
for (Field f : clsData.getStaticFields()) {
......
......@@ -56,6 +56,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
private int codeSize;
private int debugInfoOffset;
private boolean noCode;
private boolean methodIsVirtual;
private ArgType retType;
private RegisterArg thisArg;
......@@ -71,12 +72,13 @@ public class MethodNode extends LineAttrNode implements ILoadable {
private List<ExceptionHandler> exceptionHandlers = Collections.emptyList();
private List<LoopInfo> loops = Collections.emptyList();
public MethodNode(ClassNode classNode, Method mthData) {
public MethodNode(ClassNode classNode, Method mthData, boolean isVirtual) {
this.mthInfo = MethodInfo.fromDex(classNode.dex(), mthData.getMethodIndex());
this.parentClass = classNode;
this.accFlags = new AccessInfo(mthData.getAccessFlags(), AFType.METHOD);
this.noCode = mthData.getCodeOffset() == 0;
this.methodData = noCode ? null : mthData;
this.methodIsVirtual = isVirtual;
}
@Override
......@@ -538,6 +540,10 @@ public class MethodNode extends LineAttrNode implements ILoadable {
return result;
}
public boolean isVirtual() {
return methodIsVirtual;
}
public int getRegsCount() {
return regsCount;
}
......
......@@ -30,7 +30,13 @@ public class RenameVisitor extends AbstractVisitor {
@Override
public void init(RootNode root) {
IJadxArgs args = root.getArgs();
File deobfMapFile = new File(args.getOutDir(), "deobf_map.jobf");
final String firstInputFileName = root.getDexNodes().get(0).getInputFile().getFile().getAbsolutePath();
final String inputPath = org.apache.commons.io.FilenameUtils.getFullPathNoEndSeparator(
firstInputFileName);
final String inputName = org.apache.commons.io.FilenameUtils.getBaseName(firstInputFileName);
File deobfMapFile = new File(inputPath, inputName + ".jobf");
deobfuscator = new Deobfuscator(args, root.getDexNodes(), deobfMapFile);
boolean deobfuscationOn = args.isDeobfuscationOn();
if (deobfuscationOn) {
......
......@@ -132,6 +132,10 @@ public class JadxSettings extends JadxCLIArgs {
this.deobfuscationForceSave = deobfuscationForceSave;
}
public void setUseSourceNameAsClassAlias(boolean useSourceNameAsAlias) {
this.deobfuscationUseSourceNameAsAlias = useSourceNameAsAlias;
}
public Font getFont() {
if (fontStr.isEmpty()) {
return DEFAULT_FONT;
......
......@@ -141,11 +141,21 @@ public class JadxSettingsWindow extends JDialog {
}
});
JCheckBox deobfSourceAlias = new JCheckBox();
deobfSourceAlias.setSelected(settings.useSourceNameAsClassAlias());
deobfSourceAlias.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
settings.setUseSourceNameAsClassAlias(e.getStateChange() == ItemEvent.SELECTED);
needReload();
}
});
SettingsGroup deobfGroup = new SettingsGroup(NLS.str("preferences.deobfuscation"));
deobfGroup.addRow(NLS.str("preferences.deobfuscation_on"), deobfOn);
deobfGroup.addRow(NLS.str("preferences.deobfuscation_force"), deobfForce);
deobfGroup.addRow(NLS.str("preferences.deobfuscation_min_len"), minLen);
deobfGroup.addRow(NLS.str("preferences.deobfuscation_max_len"), maxLen);
deobfGroup.addRow(NLS.str("preferences.deobfuscation_source_alias"), deobfSourceAlias);
deobfGroup.end();
return deobfGroup;
}
......
......@@ -62,6 +62,7 @@ preferences.deobfuscation_on=Enable deobfuscation
preferences.deobfuscation_force=Force rewrite deobfuscation map file
preferences.deobfuscation_min_len=Minimum name length
preferences.deobfuscation_max_len=Maximum name length
preferences.deobfuscation_source_alias=Use source file name as class name alias
preferences.save=Save
preferences.cancel=Cancel
......
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