Commit 30138f7a authored by NeoSpb's avatar NeoSpb

core: added deobfuscation feature (basic functionality)

parent 883429fa
......@@ -50,6 +50,18 @@ public final class JadxCLIArgs implements IJadxArgs {
@Parameter(names = {"-v", "--verbose"}, description = "verbose output")
protected boolean verbose = false;
@Parameter(names = {"--deobf"}, description = "activate deobfuscation")
protected boolean deobfuscationOn = false;
@Parameter(names = {"--deobf-min"}, description = "min length of name")
protected int deobfuscationMinLength = 2;
@Parameter(names = {"--deobf-max"}, description = "max length of name")
protected int deobfuscationMaxLength = 40;
@Parameter(names = {"--deobf-rewrite-cfg"}, description = "force to save deobfuscation map")
protected boolean deobfuscationForceSave = false;
@Parameter(names = {"-h", "--help"}, description = "print this help", help = true)
protected boolean printHelp = false;
......@@ -209,4 +221,24 @@ public final class JadxCLIArgs implements IJadxArgs {
public boolean isVerbose() {
return verbose;
}
@Override
public boolean isDeobfuscationOn() {
return deobfuscationOn;
}
@Override
public int getDeobfuscationMinLength() {
return deobfuscationMinLength;
}
@Override
public int getDeobfuscationMaxLength() {
return deobfuscationMaxLength;
}
@Override
public boolean isDeobfuscationForceSave() {
return deobfuscationForceSave;
}
}
......@@ -6,6 +6,7 @@ dependencies {
runtime files(jadxClasspath)
compile files('lib/dx-1.10.jar')
compile 'commons-io:commons-io:2.4'
compile 'org.ow2.asm:asm:5.0.3'
compile 'com.intellij:annotations:12.0'
......
......@@ -48,4 +48,24 @@ public class DefaultJadxArgs implements IJadxArgs {
public boolean isSkipSources() {
return false;
}
@Override
public boolean isDeobfuscationOn() {
return false;
}
@Override
public int getDeobfuscationMinLength() {
return Integer.MIN_VALUE+1;
}
@Override
public int getDeobfuscationMaxLength() {
return Integer.MAX_VALUE-1;
}
@Override
public boolean isDeobfuscationForceSave() {
return false;
}
}
......@@ -20,4 +20,12 @@ public interface IJadxArgs {
boolean isSkipResources();
boolean isSkipSources();
boolean isDeobfuscationOn();
int getDeobfuscationMinLength();
int getDeobfuscationMaxLength();
boolean isDeobfuscationForceSave();
}
......@@ -3,6 +3,8 @@ package jadx.api;
import jadx.core.Jadx;
import jadx.core.ProcessClass;
import jadx.core.codegen.CodeWriter;
import jadx.core.deobf.DefaultDeobfuscator;
import jadx.core.deobf.Deobfuscator;
import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.RootNode;
......@@ -253,6 +255,51 @@ public final class JadxDecompiler {
root = new RootNode();
LOG.info("loading ...");
root.load(inputFiles);
if (args.isDeobfuscationOn()) {
final String firstInputFileName = inputFiles.get(0).getFile().getAbsolutePath();
final String inputPath = org.apache.commons.io.FilenameUtils.getFullPathNoEndSeparator(
firstInputFileName);
final String inputName = org.apache.commons.io.FilenameUtils.getBaseName(firstInputFileName);
final File deobfuscationMapFile = new File(inputPath, inputName + ".jobf");
DefaultDeobfuscator deobfuscator = new DefaultDeobfuscator();
if (deobfuscationMapFile.exists()) {
try {
deobfuscator.load(deobfuscationMapFile);
} catch (IOException e) {
LOG.error("Failed to load deobfuscation map file '{}'",
deobfuscationMapFile.getAbsolutePath());
}
}
deobfuscator.setInputData(root.getDexNodes());
deobfuscator.setMinNameLength(args.getDeobfuscationMinLength());
deobfuscator.setMaxNameLength(args.getDeobfuscationMaxLength());
deobfuscator.process();
try {
if (deobfuscationMapFile.exists()) {
if (args.isDeobfuscationForceSave()) {
deobfuscator.save(deobfuscationMapFile);
} else {
LOG.warn("Deobfuscation map file '{}' exists. Use command line option '--deobf=rewrite-cfg'" +
" to rewrite it", deobfuscationMapFile.getAbsolutePath());
}
} else {
deobfuscator.save(deobfuscationMapFile);
}
} catch (IOException e) {
LOG.error("Failed to load deobfuscation map file '{}'",
deobfuscationMapFile.getAbsolutePath());
}
Deobfuscator.setDeobfuscator(deobfuscator);
}
root.loadResources(getResources());
root.initAppResClass();
}
......
package jadx.api;
import jadx.core.codegen.CodeWriter;
import jadx.core.deobf.Deobfuscator;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.nodes.LineAttrNode;
import jadx.core.dex.info.AccessInfo;
......@@ -150,16 +151,16 @@ public final class JavaClass implements JavaNode {
@Override
public String getName() {
return cls.getShortName();
return Deobfuscator.instance().getClassShortName(cls);
}
@Override
public String getFullName() {
return cls.getFullName();
return Deobfuscator.instance().getClassFullName(cls);
}
public String getPackage() {
return cls.getPackage();
return Deobfuscator.instance().getPackageName(cls.getPackage());
}
@Override
......@@ -202,6 +203,6 @@ public final class JavaClass implements JavaNode {
@Override
public String toString() {
return getFullName();
return cls.getFullName() + "[ " + getFullName() + " ]";
}
}
package jadx.api;
import jadx.core.deobf.Deobfuscator;
import java.util.List;
import org.jetbrains.annotations.NotNull;
......@@ -9,7 +11,7 @@ public final class JavaPackage implements JavaNode, Comparable<JavaPackage> {
private final List<JavaClass> classes;
JavaPackage(String name, List<JavaClass> classes) {
this.name = name;
this.name = Deobfuscator.instance().getPackageName(name);
this.classes = classes;
}
......
......@@ -2,6 +2,7 @@ package jadx.core.codegen;
import jadx.api.IJadxArgs;
import jadx.core.Consts;
import jadx.core.deobf.Deobfuscator;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.AttrNode;
......@@ -80,14 +81,14 @@ public class ClassGen {
CodeWriter clsCode = new CodeWriter();
if (!"".equals(cls.getPackage())) {
clsCode.add("package ").add(cls.getPackage()).add(';');
clsCode.add("package ").add(Deobfuscator.instance().getPackageName(cls.getPackage())).add(';');
clsCode.newLine();
}
int importsCount = imports.size();
if (importsCount != 0) {
List<String> sortImports = new ArrayList<String>(importsCount);
for (ClassInfo ic : imports) {
sortImports.add(ic.getFullName());
sortImports.add(Deobfuscator.instance().getClassFullName(ic));
}
Collections.sort(sortImports);
......@@ -142,7 +143,7 @@ public class ClassGen {
} else {
clsCode.add("class ");
}
clsCode.add(cls.getShortName());
clsCode.add(Deobfuscator.instance().getClassShortName(cls));
addGenericMap(clsCode, cls.getGenericMap());
clsCode.add(' ');
......@@ -453,7 +454,8 @@ public class ClassGen {
if (fallback) {
return fullName;
}
String shortName = classInfo.getShortName();
fullName = Deobfuscator.instance().getClassFullName(classInfo);
String shortName = Deobfuscator.instance().getClassShortName(classInfo);
if (classInfo.getPackage().equals("java.lang") && classInfo.getParentClass() == null) {
return shortName;
} else {
......@@ -474,7 +476,7 @@ public class ClassGen {
return fullName;
}
if (classInfo.getPackage().equals(useCls.getPackage())) {
fullName = classInfo.getNameWithoutPackage();
fullName = Deobfuscator.instance().getClassName(classInfo);
}
for (ClassInfo importCls : getImports()) {
if (!importCls.equals(classInfo)
......
package jadx.core.codegen;
import jadx.core.deobf.Deobfuscator;
import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.annotations.MethodParameters;
......@@ -85,7 +86,7 @@ public class MethodGen {
code.add(' ');
}
if (mth.getAccessFlags().isConstructor()) {
code.add(classGen.getClassNode().getShortName()); // constructor
code.add(Deobfuscator.instance().getClassShortName(classGen.getClassNode())); // constructor
} else {
classGen.useType(code, mth.getReturnType());
code.add(' ');
......
package jadx.core.deobf;
public class Deobfuscator {
private static final StubDeobfuscator stubDeobfuscator;
private static IDeobfuscator deobfuscatorInstance;
static {
stubDeobfuscator = new StubDeobfuscator();
deobfuscatorInstance = stubDeobfuscator;
}
/**
* Gets instance of active deobfuscator
*
* @return deobfuscator instance
*/
public static IDeobfuscator instance() {
return deobfuscatorInstance;
}
/**
* Sets active deobfuscator
*
* @param deobfuscator object that makes deobfuscation or {@code null}
* to set stub deobfuscator
*
*/
public static void setDeobfuscator(IDeobfuscator deobfuscator) {
if (deobfuscator != null) {
deobfuscatorInstance = deobfuscator;
} else {
deobfuscatorInstance = stubDeobfuscator;
}
}
}
package jadx.core.deobf;
import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.nodes.ClassNode;
public interface IDeobfuscator {
public String getPackageName(String packageName);
public String getClassShortName(ClassNode cls);
public String getClassShortName(ClassInfo clsInfo);
public String getClassName(ClassNode cls);
public String getClassName(ClassInfo clsInfo);
public String getClassFullName(ClassNode cls);
public String getClassFullName(ClassInfo clsInfo);
public String getClassFullPath(ClassInfo clsInfo);
}
package jadx.core.deobf;
import jadx.core.dex.nodes.ClassNode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
public class PackageNode {
private PackageNode parentPackage;
private List<ClassNode> innerClasses = Collections.emptyList();
private List<PackageNode> innerPackages = Collections.emptyList();
public static final char separatorChar = '.';
private String packageName;
private String packageAlias;
private String cachedPackageFullName;
private String cachedPackageFullAlias;
public PackageNode(String packageName) {
this.packageName = packageName;
this.parentPackage = this;
}
public String getName() {
return packageName;
}
public String getFullName() {
if (cachedPackageFullName == null) {
Stack<PackageNode> pp = getParentPackages();
StringBuilder result = new StringBuilder();
result.append(pp.pop().getName());
while (pp.size() > 0) {
result.append(separatorChar);
result.append(pp.pop().getName());
}
cachedPackageFullName = result.toString();
}
return cachedPackageFullName;
}
public String getAlias() {
if (packageAlias != null) {
return packageAlias;
}
return packageName;
}
public void setAlias(String alias) {
packageAlias = alias;
}
public boolean hasAlias() {
return (packageAlias != null);
}
public String getFullAlias() {
if (cachedPackageFullAlias == null) {
Stack<PackageNode> pp = getParentPackages();
StringBuilder result = new StringBuilder();
result.append(pp.pop().getAlias());
while (pp.size() > 0) {
result.append(separatorChar);
result.append(pp.pop().getAlias());
}
cachedPackageFullAlias = result.toString();
}
return cachedPackageFullAlias;
}
public PackageNode getParentPackage() {
return parentPackage;
}
public List<PackageNode> getInnerPackages() {
return innerPackages;
}
public List<ClassNode> getInnerClasses() {
return innerClasses;
}
public void addInnerClass(ClassNode cls) {
if (innerClasses.isEmpty()) {
innerClasses = new ArrayList<ClassNode>();
}
innerClasses.add(cls);
}
public void addInnerPackage(PackageNode pkg) {
if (innerPackages.isEmpty()) {
innerPackages = new ArrayList<PackageNode>();
}
innerPackages.add(pkg);
pkg.parentPackage = this;
}
/**
* Gets inner package node by name
*
* @param name inner package name
*
* @return package node or {@code null}
*/
public PackageNode getInnerPackageByName(String name) {
PackageNode result = null;
for (PackageNode p : innerPackages) {
if (p.getName().equals(name)) {
result = p;
break;
}
}
return result;
}
/**
* Fills stack with parent packages exclude root node
*
* @return stack with parent packages
*/
private Stack<PackageNode> getParentPackages() {
Stack<PackageNode> pp = new Stack<PackageNode>();
PackageNode currentP = this;
PackageNode parentP = currentP.getParentPackage();
while (currentP != parentP) {
pp.push(currentP);
currentP = parentP;
parentP = currentP.getParentPackage();
}
return pp;
}
}
package jadx.core.deobf;
import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.nodes.ClassNode;
public class StubDeobfuscator implements IDeobfuscator {
@Override
public String getPackageName(String packageName) {
return packageName;
}
@Override
public String getClassShortName(ClassNode cls) {
return cls.getShortName();
}
@Override
public String getClassShortName(ClassInfo clsInfo) {
return clsInfo.getShortName();
}
@Override
public String getClassName(ClassNode cls) {
return cls.getClassInfo().getNameWithoutPackage();
}
@Override
public String getClassName(ClassInfo clsInfo) {
return clsInfo.getNameWithoutPackage();
}
@Override
public String getClassFullName(ClassNode cls) {
return cls.getFullName();
}
@Override
public String getClassFullName(ClassInfo clsInfo) {
return clsInfo.getFullName();
}
@Override
public String getClassFullPath(ClassInfo clsInfo) {
return clsInfo.getFullPath();
}
}
......@@ -2,6 +2,7 @@ package jadx.core.dex.visitors;
import jadx.core.codegen.CodeWriter;
import jadx.core.codegen.MethodGen;
import jadx.core.deobf.Deobfuscator;
import jadx.core.dex.attributes.IAttributeNode;
import jadx.core.dex.instructions.IfNode;
import jadx.core.dex.instructions.InsnType;
......@@ -104,7 +105,7 @@ public class DotGraphVisitor extends AbstractVisitor {
+ (useRegions ? ".regions" : "")
+ (rawInsn ? ".raw" : "")
+ ".dot";
dot.save(dir, mth.getParentClass().getClassInfo().getFullPath() + "_graphs", fileName);
dot.save(dir, Deobfuscator.instance().getClassFullPath(mth.getParentClass().getClassInfo()) + "_graphs", fileName);
}
private void processMethodRegion(MethodNode mth) {
......
......@@ -2,6 +2,7 @@ package jadx.core.dex.visitors;
import jadx.api.IJadxArgs;
import jadx.core.codegen.CodeWriter;
import jadx.core.deobf.Deobfuscator;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.utils.exceptions.CodegenException;
......@@ -24,7 +25,7 @@ public class SaveCode extends AbstractVisitor {
public static void save(File dir, IJadxArgs args, ClassNode cls) {
CodeWriter clsCode = cls.getCode();
String fileName = cls.getClassInfo().getFullPath() + ".java";
String fileName = Deobfuscator.instance().getClassFullPath(cls.getClassInfo()) + ".java";
if (args.isFallbackMode()) {
fileName += ".jadx";
}
......
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