Commit a532287d authored by Skylot's avatar Skylot

core: refactor deobfuscator

parent 7844e554
...@@ -56,12 +56,12 @@ public class DefaultJadxArgs implements IJadxArgs { ...@@ -56,12 +56,12 @@ public class DefaultJadxArgs implements IJadxArgs {
@Override @Override
public int getDeobfuscationMinLength() { public int getDeobfuscationMinLength() {
return Integer.MIN_VALUE+1; return Integer.MIN_VALUE + 1;
} }
@Override @Override
public int getDeobfuscationMaxLength() { public int getDeobfuscationMaxLength() {
return Integer.MAX_VALUE-1; return Integer.MAX_VALUE - 1;
} }
@Override @Override
......
...@@ -4,8 +4,6 @@ import jadx.core.Jadx; ...@@ -4,8 +4,6 @@ import jadx.core.Jadx;
import jadx.core.ProcessClass; import jadx.core.ProcessClass;
import jadx.core.codegen.CodeGen; import jadx.core.codegen.CodeGen;
import jadx.core.codegen.CodeWriter; import jadx.core.codegen.CodeWriter;
import jadx.core.deobf.DefaultDeobfuscator;
import jadx.core.deobf.Deobfuscator;
import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.RootNode; import jadx.core.dex.nodes.RootNode;
import jadx.core.dex.visitors.IDexTreeVisitor; import jadx.core.dex.visitors.IDexTreeVisitor;
...@@ -93,6 +91,8 @@ public final class JadxDecompiler { ...@@ -93,6 +91,8 @@ public final class JadxDecompiler {
resources = null; resources = null;
xmlParser = null; xmlParser = null;
root = null; root = null;
passes = null;
codeGen = null;
} }
public static String getVersion() { public static String getVersion() {
...@@ -254,56 +254,27 @@ public final class JadxDecompiler { ...@@ -254,56 +254,27 @@ public final class JadxDecompiler {
void parse() throws DecodeException { void parse() throws DecodeException {
reset(); reset();
root = new RootNode(); init();
root = new RootNode(args);
LOG.info("loading ..."); LOG.info("loading ...");
root.load(inputFiles); root.load(inputFiles);
if (args.isDeobfuscationOn()) { root.initClassPath();
final String firstInputFileName = inputFiles.get(0).getFile().getAbsolutePath(); root.loadResources(getResources());
final String inputPath = org.apache.commons.io.FilenameUtils.getFullPathNoEndSeparator( root.initAppResClass();
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(); initVisitors();
}
private void initVisitors() {
for (IDexTreeVisitor pass : passes) {
try { try {
if (deobfuscationMapFile.exists()) { pass.init(root);
if (args.isDeobfuscationForceSave()) { } catch (Exception e) {
deobfuscator.save(deobfuscationMapFile); LOG.error("Visitor init failed: {}", pass.getClass().getSimpleName(), e);
} 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();
} }
void processClass(ClassNode cls) { void processClass(ClassNode cls) {
......
package jadx.api; package jadx.api;
import jadx.core.codegen.CodeWriter; import jadx.core.codegen.CodeWriter;
import jadx.core.deobf.Deobfuscator;
import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.nodes.LineAttrNode; import jadx.core.dex.attributes.nodes.LineAttrNode;
import jadx.core.dex.info.AccessInfo; import jadx.core.dex.info.AccessInfo;
...@@ -116,6 +115,9 @@ public final class JavaClass implements JavaNode { ...@@ -116,6 +115,9 @@ public final class JavaClass implements JavaNode {
public CodePosition getDefinitionPosition(int line, int offset) { public CodePosition getDefinitionPosition(int line, int offset) {
Map<CodePosition, Object> map = getCodeAnnotations(); Map<CodePosition, Object> map = getCodeAnnotations();
if (map.isEmpty()) {
return null;
}
Object obj = map.get(new CodePosition(line, offset)); Object obj = map.get(new CodePosition(line, offset));
if (!(obj instanceof LineAttrNode)) { if (!(obj instanceof LineAttrNode)) {
return null; return null;
...@@ -151,16 +153,16 @@ public final class JavaClass implements JavaNode { ...@@ -151,16 +153,16 @@ public final class JavaClass implements JavaNode {
@Override @Override
public String getName() { public String getName() {
return Deobfuscator.instance().getClassShortName(cls); return cls.getShortName();
} }
@Override @Override
public String getFullName() { public String getFullName() {
return Deobfuscator.instance().getClassFullName(cls); return cls.getFullName();
} }
public String getPackage() { public String getPackage() {
return Deobfuscator.instance().getPackageName(cls.getPackage()); return cls.getPackage();
} }
@Override @Override
......
package jadx.api; package jadx.api;
import jadx.core.deobf.Deobfuscator;
import java.util.List; import java.util.List;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
...@@ -11,7 +9,7 @@ public final class JavaPackage implements JavaNode, Comparable<JavaPackage> { ...@@ -11,7 +9,7 @@ public final class JavaPackage implements JavaNode, Comparable<JavaPackage> {
private final List<JavaClass> classes; private final List<JavaClass> classes;
JavaPackage(String name, List<JavaClass> classes) { JavaPackage(String name, List<JavaClass> classes) {
this.name = Deobfuscator.instance().getPackageName(name); this.name = name;
this.classes = classes; this.classes = classes;
} }
......
...@@ -14,6 +14,7 @@ import jadx.core.dex.visitors.MethodInlineVisitor; ...@@ -14,6 +14,7 @@ import jadx.core.dex.visitors.MethodInlineVisitor;
import jadx.core.dex.visitors.ModVisitor; import jadx.core.dex.visitors.ModVisitor;
import jadx.core.dex.visitors.PrepareForCodeGen; import jadx.core.dex.visitors.PrepareForCodeGen;
import jadx.core.dex.visitors.ReSugarCode; import jadx.core.dex.visitors.ReSugarCode;
import jadx.core.dex.visitors.RenameVisitor;
import jadx.core.dex.visitors.SimplifyVisitor; import jadx.core.dex.visitors.SimplifyVisitor;
import jadx.core.dex.visitors.blocksmaker.BlockExceptionHandler; import jadx.core.dex.visitors.blocksmaker.BlockExceptionHandler;
import jadx.core.dex.visitors.blocksmaker.BlockFinallyExtract; import jadx.core.dex.visitors.blocksmaker.BlockFinallyExtract;
...@@ -106,6 +107,10 @@ public class Jadx { ...@@ -106,6 +107,10 @@ public class Jadx {
passes.add(new ProcessVariables()); passes.add(new ProcessVariables());
passes.add(new DependencyCollector()); passes.add(new DependencyCollector());
if (args.isDeobfuscationOn()) {
passes.add(new RenameVisitor());
}
} }
return passes; return passes;
} }
......
...@@ -33,9 +33,7 @@ public final class ProcessClass { ...@@ -33,9 +33,7 @@ public final class ProcessClass {
for (IDexTreeVisitor visitor : passes) { for (IDexTreeVisitor visitor : passes) {
DepthTraversal.visit(visitor, cls); DepthTraversal.visit(visitor, cls);
} }
for (ClassNode clsNode : cls.getDependencies()) { processDependencies(cls, passes);
process(clsNode, passes, null);
}
cls.setState(PROCESSED); cls.setState(PROCESSED);
} }
if (cls.getState() == PROCESSED && codeGen != null) { if (cls.getState() == PROCESSED && codeGen != null) {
...@@ -52,4 +50,14 @@ public final class ProcessClass { ...@@ -52,4 +50,14 @@ 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);
}
}
} }
package jadx.core.clsp; package jadx.core.clsp;
import jadx.core.dex.info.ClassInfo; import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.RootNode; import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.exceptions.DecodeException; import jadx.core.utils.exceptions.DecodeException;
...@@ -77,15 +77,15 @@ public class ClsSet { ...@@ -77,15 +77,15 @@ public class ClsSet {
public static NClass[] makeParentsArray(ClassNode cls, Map<String, NClass> names) { public static NClass[] makeParentsArray(ClassNode cls, Map<String, NClass> names) {
List<NClass> parents = new ArrayList<NClass>(1 + cls.getInterfaces().size()); List<NClass> parents = new ArrayList<NClass>(1 + cls.getInterfaces().size());
ClassInfo superClass = cls.getSuperClass(); ArgType superClass = cls.getSuperClass();
if (superClass != null) { if (superClass != null) {
NClass c = getCls(superClass.getRawName(), names); NClass c = getCls(superClass.getObject(), names);
if (c != null) { if (c != null) {
parents.add(c); parents.add(c);
} }
} }
for (ClassInfo iface : cls.getInterfaces()) { for (ArgType iface : cls.getInterfaces()) {
NClass c = getCls(iface.getRawName(), names); NClass c = getCls(iface.getObject(), names);
if (c != null) { if (c != null) {
parents.add(c); parents.add(c);
} }
......
...@@ -45,16 +45,10 @@ public class ClspGraph { ...@@ -45,16 +45,10 @@ public class ClspGraph {
throw new JadxRuntimeException("Classpath must be loaded first"); throw new JadxRuntimeException("Classpath must be loaded first");
} }
int size = classes.size(); int size = classes.size();
for (ClassNode cls : classes) {
size += cls.getInnerClasses().size();
}
NClass[] nClasses = new NClass[size]; NClass[] nClasses = new NClass[size];
int k = 0; int k = 0;
for (ClassNode cls : classes) { for (ClassNode cls : classes) {
nClasses[k++] = addClass(cls); nClasses[k++] = addClass(cls);
for (ClassNode inner : cls.getInnerClasses()) {
nClasses[k++] = addClass(inner);
}
} }
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
nClasses[i].setParents(ClsSet.makeParentsArray(classes.get(i), nameMap)); nClasses[i].setParents(ClsSet.makeParentsArray(classes.get(i), nameMap));
...@@ -62,8 +56,9 @@ public class ClspGraph { ...@@ -62,8 +56,9 @@ public class ClspGraph {
} }
private NClass addClass(ClassNode cls) { private NClass addClass(ClassNode cls) {
NClass nClass = new NClass(cls.getRawName(), -1); String rawName = cls.getRawName();
nameMap.put(cls.getRawName(), nClass); NClass nClass = new NClass(rawName, -1);
nameMap.put(rawName, nClass);
return nClass; return nClass;
} }
......
package jadx.core.clsp; package jadx.core.clsp;
import jadx.api.DefaultJadxArgs;
import jadx.core.dex.nodes.RootNode; import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.exceptions.DecodeException; import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.files.InputFile; import jadx.core.utils.files.InputFile;
...@@ -42,7 +43,7 @@ public class ConvertToClsSet { ...@@ -42,7 +43,7 @@ public class ConvertToClsSet {
LOG.info("Loaded: {}", inputFile.getFile()); LOG.info("Loaded: {}", inputFile.getFile());
} }
RootNode root = new RootNode(); RootNode root = new RootNode(new DefaultJadxArgs());
root.load(inputFiles); root.load(inputFiles);
ClsSet set = new ClsSet(); ClsSet set = new ClsSet();
......
package jadx.core.codegen; package jadx.core.codegen;
import jadx.api.IJadxArgs; import jadx.api.IJadxArgs;
import jadx.core.Consts;
import jadx.core.deobf.Deobfuscator;
import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.AttrNode; import jadx.core.dex.attributes.AttrNode;
...@@ -81,14 +79,14 @@ public class ClassGen { ...@@ -81,14 +79,14 @@ public class ClassGen {
CodeWriter clsCode = new CodeWriter(); CodeWriter clsCode = new CodeWriter();
if (!"".equals(cls.getPackage())) { if (!"".equals(cls.getPackage())) {
clsCode.add("package ").add(Deobfuscator.instance().getPackageName(cls.getPackage())).add(';'); clsCode.add("package ").add(cls.getPackage()).add(';');
clsCode.newLine(); clsCode.newLine();
} }
int importsCount = imports.size(); int importsCount = imports.size();
if (importsCount != 0) { if (importsCount != 0) {
List<String> sortImports = new ArrayList<String>(importsCount); List<String> sortImports = new ArrayList<String>(importsCount);
for (ClassInfo ic : imports) { for (ClassInfo ic : imports) {
sortImports.add(Deobfuscator.instance().getClassFullName(ic)); sortImports.add(ic.getAlias().getFullName());
} }
Collections.sort(sortImports); Collections.sort(sortImports);
...@@ -126,7 +124,7 @@ public class ClassGen { ...@@ -126,7 +124,7 @@ public class ClassGen {
} }
// 'static' modifier not allowed for top classes (not inner) // 'static' modifier not allowed for top classes (not inner)
if (!cls.getClassInfo().isInner()) { if (!cls.getAlias().isInner()) {
af = af.remove(AccessFlags.ACC_STATIC); af = af.remove(AccessFlags.ACC_STATIC);
} }
...@@ -143,15 +141,15 @@ public class ClassGen { ...@@ -143,15 +141,15 @@ public class ClassGen {
} else { } else {
clsCode.add("class "); clsCode.add("class ");
} }
clsCode.add(Deobfuscator.instance().getClassShortName(cls)); clsCode.add(cls.getShortName());
addGenericMap(clsCode, cls.getGenericMap()); addGenericMap(clsCode, cls.getGenericMap());
clsCode.add(' '); clsCode.add(' ');
ClassInfo sup = cls.getSuperClass(); ArgType sup = cls.getSuperClass();
if (sup != null if (sup != null
&& !sup.getFullName().equals(Consts.CLASS_OBJECT) && !sup.equals(ArgType.OBJECT)
&& !sup.getFullName().equals(Consts.CLASS_ENUM)) { && !sup.getObject().equals(ArgType.ENUM.getObject())) {
clsCode.add("extends "); clsCode.add("extends ");
useClass(clsCode, sup); useClass(clsCode, sup);
clsCode.add(' '); clsCode.add(' ');
...@@ -163,8 +161,8 @@ public class ClassGen { ...@@ -163,8 +161,8 @@ public class ClassGen {
} else { } else {
clsCode.add("implements "); clsCode.add("implements ");
} }
for (Iterator<ClassInfo> it = cls.getInterfaces().iterator(); it.hasNext(); ) { for (Iterator<ArgType> it = cls.getInterfaces().iterator(); it.hasNext(); ) {
ClassInfo interf = it.next(); ArgType interf = it.next();
useClass(clsCode, interf); useClass(clsCode, interf);
if (it.hasNext()) { if (it.hasNext()) {
clsCode.add(", "); clsCode.add(", ");
...@@ -192,7 +190,7 @@ public class ClassGen { ...@@ -192,7 +190,7 @@ public class ClassGen {
if (type.isGenericType()) { if (type.isGenericType()) {
code.add(type.getObject()); code.add(type.getObject());
} else { } else {
useClass(code, ClassInfo.fromType(cls.dex(), type)); useClass(code, type);
} }
if (list != null && !list.isEmpty()) { if (list != null && !list.isEmpty()) {
code.add(" extends "); code.add(" extends ");
...@@ -201,7 +199,7 @@ public class ClassGen { ...@@ -201,7 +199,7 @@ public class ClassGen {
if (g.isGenericType()) { if (g.isGenericType()) {
code.add(g.getObject()); code.add(g.getObject());
} else { } else {
useClass(code, ClassInfo.fromType(cls.dex(), g)); useClass(code, g);
} }
if (it.hasNext()) { if (it.hasNext()) {
code.add(" & "); code.add(" & ");
...@@ -407,7 +405,7 @@ public class ClassGen { ...@@ -407,7 +405,7 @@ public class ClassGen {
if (type.isGenericType()) { if (type.isGenericType()) {
code.add(type.getObject()); code.add(type.getObject());
} else { } else {
useClass(code, ClassInfo.fromType(cls.dex(), type)); useClass(code, type);
} }
} else if (stype == PrimitiveType.ARRAY) { } else if (stype == PrimitiveType.ARRAY) {
useType(code, type.getArrayElement()); useType(code, type.getArrayElement());
...@@ -417,14 +415,9 @@ public class ClassGen { ...@@ -417,14 +415,9 @@ public class ClassGen {
} }
} }
public void useClass(CodeWriter code, ClassInfo classInfo) { public void useClass(CodeWriter code, ArgType type) {
ClassNode classNode = cls.dex().resolveClass(classInfo); useClass(code, ClassInfo.extCls(cls.dex(), type));
if (classNode != null) { ArgType[] generics = type.getGenericTypes();
code.attachAnnotation(classNode);
}
String baseClass = useClassInternal(cls.getClassInfo(), classInfo);
code.add(baseClass);
ArgType[] generics = classInfo.getType().getGenericTypes();
if (generics != null) { if (generics != null) {
code.add('<'); code.add('<');
int len = generics.length; int len = generics.length;
...@@ -449,54 +442,63 @@ public class ClassGen { ...@@ -449,54 +442,63 @@ public class ClassGen {
} }
} }
private String useClassInternal(ClassInfo useCls, ClassInfo classInfo) { public void useClass(CodeWriter code, ClassInfo classInfo) {
String fullName = classInfo.getFullName(); ClassNode classNode = cls.dex().resolveClass(classInfo);
if (classNode != null) {
code.attachAnnotation(classNode);
}
String baseClass = useClassInternal(cls.getAlias(), classInfo.getAlias());
code.add(baseClass);
}
private String useClassInternal(ClassInfo useCls, ClassInfo extClsInfo) {
String fullName = extClsInfo.getFullName();
if (fallback) { if (fallback) {
return fullName; return fullName;
} }
fullName = Deobfuscator.instance().getClassFullName(classInfo); fullName = extClsInfo.getFullName();
String shortName = Deobfuscator.instance().getClassShortName(classInfo); String shortName = extClsInfo.getShortName();
if (classInfo.getPackage().equals("java.lang") && classInfo.getParentClass() == null) { if (extClsInfo.getPackage().equals("java.lang") && extClsInfo.getParentClass() == null) {
return shortName; return shortName;
} else { } else {
// don't add import if this class inner for current class // don't add import if this class inner for current class
if (isClassInnerFor(classInfo, useCls)) { if (isClassInnerFor(extClsInfo, useCls)) {
return shortName; return shortName;
} }
// don't add import if this class from same package // don't add import if this class from same package
if (classInfo.getPackage().equals(useCls.getPackage()) && !classInfo.isInner()) { if (extClsInfo.getPackage().equals(useCls.getPackage()) && !extClsInfo.isInner()) {
return shortName; return shortName;
} }
// don't add import if class not public (must be accessed using inheritance) // don't add import if class not public (must be accessed using inheritance)
ClassNode classNode = cls.dex().resolveClass(classInfo); ClassNode classNode = cls.dex().resolveClass(extClsInfo);
if (classNode != null && !classNode.getAccessFlags().isPublic()) { if (classNode != null && !classNode.getAccessFlags().isPublic()) {
return shortName; return shortName;
} }
if (searchCollision(cls.dex(), useCls, classInfo)) { if (searchCollision(cls.dex(), useCls, extClsInfo)) {
return fullName; return fullName;
} }
if (classInfo.getPackage().equals(useCls.getPackage())) { if (extClsInfo.getPackage().equals(useCls.getPackage())) {
fullName = Deobfuscator.instance().getClassName(classInfo); fullName = extClsInfo.getNameWithoutPackage();
} }
for (ClassInfo importCls : getImports()) { for (ClassInfo importCls : getImports()) {
if (!importCls.equals(classInfo) if (!importCls.equals(extClsInfo)
&& importCls.getShortName().equals(shortName)) { && importCls.getShortName().equals(shortName)) {
if (classInfo.isInner()) { if (extClsInfo.isInner()) {
String parent = useClassInternal(useCls, classInfo.getParentClass()); String parent = useClassInternal(useCls, extClsInfo.getParentClass().getAlias());
return parent + "." + shortName; return parent + "." + shortName;
} else { } else {
return fullName; return fullName;
} }
} }
} }
addImport(classInfo); addImport(extClsInfo);
return shortName; return shortName;
} }
} }
private void addImport(ClassInfo classInfo) { private void addImport(ClassInfo classInfo) {
if (parentGen != null) { if (parentGen != null) {
parentGen.addImport(classInfo); parentGen.addImport(classInfo.getAlias());
} else { } else {
imports.add(classInfo); imports.add(classInfo);
} }
...@@ -530,7 +532,7 @@ public class ClassGen { ...@@ -530,7 +532,7 @@ public class ClassGen {
if (classNode != null) { if (classNode != null) {
for (ClassNode inner : classNode.getInnerClasses()) { for (ClassNode inner : classNode.getInnerClasses()) {
if (inner.getShortName().equals(shortName) if (inner.getShortName().equals(shortName)
&& !inner.getClassInfo().equals(searchCls)) { && !inner.getAlias().equals(searchCls)) {
return true; return true;
} }
} }
......
...@@ -162,14 +162,14 @@ public class InsnGen { ...@@ -162,14 +162,14 @@ public class InsnGen {
public static void makeStaticFieldAccess(CodeWriter code, FieldInfo field, ClassGen clsGen) { public static void makeStaticFieldAccess(CodeWriter code, FieldInfo field, ClassGen clsGen) {
ClassInfo declClass = field.getDeclClass(); ClassInfo declClass = field.getDeclClass();
boolean fieldFromThisClass = clsGen.getClassNode().getFullName().startsWith(declClass.getFullName()); boolean fieldFromThisClass = clsGen.getClassNode().getClassInfo().equals(declClass);
if (!fieldFromThisClass) { if (!fieldFromThisClass) {
// Android specific resources class handler // Android specific resources class handler
ClassInfo parentClass = declClass.getParentClass(); ClassInfo parentClass = declClass.getParentClass();
if (parentClass != null && parentClass.getShortName().equals("R")) { if (parentClass != null && parentClass.getShortName().equals("R")) {
clsGen.useClass(code, parentClass); clsGen.useClass(code, parentClass);
code.add('.'); code.add('.');
code.add(declClass.getShortName()); code.add(declClass.getAlias().getShortName());
} else { } else {
clsGen.useClass(code, declClass); clsGen.useClass(code, declClass);
} }
...@@ -186,6 +186,10 @@ public class InsnGen { ...@@ -186,6 +186,10 @@ public class InsnGen {
makeStaticFieldAccess(code, field, mgen.getClassGen()); makeStaticFieldAccess(code, field, mgen.getClassGen());
} }
public void useClass(CodeWriter code, ArgType type) {
mgen.getClassGen().useClass(code, type);
}
public void useClass(CodeWriter code, ClassInfo cls) { public void useClass(CodeWriter code, ClassInfo cls) {
mgen.getClassGen().useClass(code, cls); mgen.getClassGen().useClass(code, cls);
} }
...@@ -200,9 +204,6 @@ public class InsnGen { ...@@ -200,9 +204,6 @@ public class InsnGen {
protected boolean makeInsn(InsnNode insn, CodeWriter code, Flags flag) throws CodegenException { protected boolean makeInsn(InsnNode insn, CodeWriter code, Flags flag) throws CodegenException {
try { try {
if (insn.getType() == InsnType.NOP) {
return false;
}
Set<Flags> state = EnumSet.noneOf(Flags.class); Set<Flags> state = EnumSet.noneOf(Flags.class);
if (flag == Flags.BODY_ONLY || flag == Flags.BODY_ONLY_NOWRAP) { if (flag == Flags.BODY_ONLY || flag == Flags.BODY_ONLY_NOWRAP) {
state.add(flag); state.add(flag);
...@@ -531,7 +532,7 @@ public class InsnGen { ...@@ -531,7 +532,7 @@ public class InsnGen {
ClassNode cls = mth.dex().resolveClass(insn.getClassType()); ClassNode cls = mth.dex().resolveClass(insn.getClassType());
if (cls != null && cls.isAnonymous() && !fallback) { if (cls != null && cls.isAnonymous() && !fallback) {
// anonymous class construction // anonymous class construction
ClassInfo parent; ArgType parent;
if (cls.getInterfaces().size() == 1) { if (cls.getInterfaces().size() == 1) {
parent = cls.getInterfaces().get(0); parent = cls.getInterfaces().get(0);
} else { } else {
...@@ -600,7 +601,7 @@ public class InsnGen { ...@@ -600,7 +601,7 @@ public class InsnGen {
break; break;
case STATIC: case STATIC:
ClassInfo insnCls = mth.getParentClass().getClassInfo(); ClassInfo insnCls = mth.getParentClass().getAlias();
ClassInfo declClass = callMth.getDeclClass(); ClassInfo declClass = callMth.getDeclClass();
if (!insnCls.equals(declClass)) { if (!insnCls.equals(declClass)) {
useClass(code, declClass); useClass(code, declClass);
......
package jadx.core.codegen; package jadx.core.codegen;
import jadx.core.deobf.Deobfuscator;
import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.annotations.MethodParameters; import jadx.core.dex.attributes.annotations.MethodParameters;
import jadx.core.dex.attributes.nodes.JadxErrorAttr; import jadx.core.dex.attributes.nodes.JadxErrorAttr;
import jadx.core.dex.info.AccessInfo; import jadx.core.dex.info.AccessInfo;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.InsnNode;
...@@ -86,7 +86,7 @@ public class MethodGen { ...@@ -86,7 +86,7 @@ public class MethodGen {
code.add(' '); code.add(' ');
} }
if (mth.getAccessFlags().isConstructor()) { if (mth.getAccessFlags().isConstructor()) {
code.add(Deobfuscator.instance().getClassShortName(classGen.getClassNode())); // constructor code.add(classGen.getClassNode().getShortName()); // constructor
} else { } else {
classGen.useType(code, mth.getReturnType()); classGen.useType(code, mth.getReturnType());
code.add(' '); code.add(' ');
...@@ -209,7 +209,7 @@ public class MethodGen { ...@@ -209,7 +209,7 @@ public class MethodGen {
public static void addFallbackInsns(CodeWriter code, MethodNode mth, InsnNode[] insnArr, boolean addLabels) { public static void addFallbackInsns(CodeWriter code, MethodNode mth, InsnNode[] insnArr, boolean addLabels) {
InsnGen insnGen = new InsnGen(getFallbackMethodGen(mth), true); InsnGen insnGen = new InsnGen(getFallbackMethodGen(mth), true);
for (InsnNode insn : insnArr) { for (InsnNode insn : insnArr) {
if (insn == null) { if (insn == null || insn.getType() == InsnType.NOP) {
continue; continue;
} }
if (addLabels && (insn.contains(AType.JUMP) || insn.contains(AType.EXC_HANDLER))) { if (addLabels && (insn.contains(AType.JUMP) || insn.contains(AType.EXC_HANDLER))) {
......
...@@ -158,8 +158,8 @@ public class NameGen { ...@@ -158,8 +158,8 @@ public class NameGen {
if (alias != null) { if (alias != null) {
return alias; return alias;
} }
ClassInfo clsInfo = ClassInfo.fromType(mth.dex(), type); ClassInfo extClsInfo = ClassInfo.extCls(mth.dex(), type);
String shortName = clsInfo.getShortName(); String shortName = extClsInfo.getShortName();
String vName = fromName(shortName); String vName = fromName(shortName);
if (vName != null) { if (vName != null) {
return vName; return vName;
...@@ -223,12 +223,12 @@ public class NameGen { ...@@ -223,12 +223,12 @@ public class NameGen {
return null; return null;
} }
private static String makeNameFromInvoke(MethodInfo callMth) { private String makeNameFromInvoke(MethodInfo callMth) {
String name = callMth.getName(); String name = callMth.getName();
if (name.startsWith("get") || name.startsWith("set")) { if (name.startsWith("get") || name.startsWith("set")) {
return fromName(name.substring(3)); return fromName(name.substring(3));
} }
ArgType declType = callMth.getDeclClass().getType(); ArgType declType = callMth.getDeclClass().getAlias().getType();
if ("iterator".equals(name)) { if ("iterator".equals(name)) {
return "it"; return "it";
} }
......
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);
}
...@@ -14,7 +14,7 @@ public class PackageNode { ...@@ -14,7 +14,7 @@ public class PackageNode {
private List<PackageNode> innerPackages = Collections.emptyList(); private List<PackageNode> innerPackages = Collections.emptyList();
public static final char separatorChar = '.'; public static final char separatorChar = '.';
private String packageName; private String packageName;
private String packageAlias; private String packageAlias;
...@@ -51,14 +51,14 @@ public class PackageNode { ...@@ -51,14 +51,14 @@ public class PackageNode {
if (packageAlias != null) { if (packageAlias != null) {
return packageAlias; return packageAlias;
} }
return packageName; return packageName;
} }
public void setAlias(String alias) { public void setAlias(String alias) {
packageAlias = alias; packageAlias = alias;
} }
public boolean hasAlias() { public boolean hasAlias() {
return (packageAlias != null); return (packageAlias != null);
} }
...@@ -108,9 +108,8 @@ public class PackageNode { ...@@ -108,9 +108,8 @@ public class PackageNode {
/** /**
* Gets inner package node by name * Gets inner package node by name
* *
* @param name inner package name * @param name inner package name
*
* @return package node or {@code null} * @return package node or {@code null}
*/ */
public PackageNode getInnerPackageByName(String name) { public PackageNode getInnerPackageByName(String name) {
...@@ -127,7 +126,7 @@ public class PackageNode { ...@@ -127,7 +126,7 @@ public class PackageNode {
/** /**
* Fills stack with parent packages exclude root node * Fills stack with parent packages exclude root node
* *
* @return stack with parent packages * @return stack with parent packages
*/ */
private Stack<PackageNode> getParentPackages() { private Stack<PackageNode> getParentPackages() {
......
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();
}
}
...@@ -16,17 +16,27 @@ public final class ClassInfo { ...@@ -16,17 +16,27 @@ public final class ClassInfo {
private String fullName; private String fullName;
// for inner class not equals null // for inner class not equals null
private ClassInfo parentClass; private ClassInfo parentClass;
// class info after rename (deobfuscation)
private ClassInfo alias;
private ClassInfo(DexNode dex, ArgType type) { private ClassInfo(DexNode dex, ArgType type) {
if (!type.isObject()) { this(dex, type, true);
}
private ClassInfo(DexNode dex, ArgType type, boolean inner) {
if (!type.isObject() || type.isGeneric()) {
throw new JadxRuntimeException("Not class type: " + type); throw new JadxRuntimeException("Not class type: " + type);
} }
this.type = type; this.type = type;
this.alias = this;
splitNames(dex, true); splitNames(dex, inner);
} }
public static ClassInfo fromType(DexNode dex, ArgType type) { public static ClassInfo fromType(DexNode dex, ArgType type) {
if (type.isArray()) {
type = ArgType.OBJECT;
}
ClassInfo cls = dex.getInfoStorage().getCls(type); ClassInfo cls = dex.getInfoStorage().getCls(type);
if (cls != null) { if (cls != null) {
return cls; return cls;
...@@ -39,26 +49,32 @@ public final class ClassInfo { ...@@ -39,26 +49,32 @@ public final class ClassInfo {
if (clsIndex == DexNode.NO_INDEX) { if (clsIndex == DexNode.NO_INDEX) {
return null; return null;
} }
ArgType type = dex.getType(clsIndex); return fromType(dex, dex.getType(clsIndex));
if (type.isArray()) {
type = ArgType.OBJECT;
}
return fromType(dex, type);
} }
public static ClassInfo fromName(DexNode dex, String clsName) { public static ClassInfo fromName(DexNode dex, String clsName) {
return fromType(dex, ArgType.object(clsName)); return fromType(dex, ArgType.object(clsName));
} }
public static ClassInfo extCls(DexNode dex, ArgType type) {
ClassInfo classInfo = fromName(dex, type.getObject());
return classInfo.alias;
}
public void rename(DexNode dex, String fullName) {
this.alias = new ClassInfo(dex, ArgType.object(fullName), isInner());
}
public ClassInfo getAlias() {
return alias;
}
private void splitNames(DexNode dex, boolean canBeInner) { private void splitNames(DexNode dex, boolean canBeInner) {
String fullObjectName = type.getObject(); String fullObjectName = type.getObject();
assert fullObjectName.indexOf('/') == -1 : "Raw type: " + type;
String clsName; String clsName;
int dot = fullObjectName.lastIndexOf('.'); int dot = fullObjectName.lastIndexOf('.');
if (dot == -1) { if (dot == -1) {
// rename default package if it used from class with package (often for obfuscated apps), pkg = "";
pkg = Consts.DEFAULT_PACKAGE_NAME;
clsName = fullObjectName; clsName = fullObjectName;
} else { } else {
pkg = fullObjectName.substring(0, dot); pkg = fullObjectName.substring(0, dot);
...@@ -83,8 +99,12 @@ public final class ClassInfo { ...@@ -83,8 +99,12 @@ public final class ClassInfo {
if (NameMapper.isReserved(clsName)) { if (NameMapper.isReserved(clsName)) {
clsName += "_"; clsName += "_";
} }
this.fullName = (parentClass != null ? parentClass.getFullName() : pkg) + "." + clsName;
this.name = clsName; this.name = clsName;
if (parentClass != null) {
this.fullName = parentClass.fullName + "." + clsName;
} else {
this.fullName = pkg.isEmpty() ? clsName : pkg + "." + clsName;
}
} }
public String getFullPath() { public String getFullPath() {
...@@ -97,28 +117,23 @@ public final class ClassInfo { ...@@ -97,28 +117,23 @@ public final class ClassInfo {
return fullName; return fullName;
} }
public boolean isObject() {
return fullName.equals(Consts.CLASS_OBJECT);
}
public String getShortName() { public String getShortName() {
return name; return name;
} }
public String getRawName() {
return type.getObject();
}
public String getPackage() { public String getPackage() {
return pkg; return pkg;
} }
public boolean isPackageDefault() { public String getRawName() {
return pkg.isEmpty() || pkg.equals(Consts.DEFAULT_PACKAGE_NAME); return type.getObject();
} }
public String getNameWithoutPackage() { public String getNameWithoutPackage() {
return (parentClass != null ? parentClass.getNameWithoutPackage() + "." : "") + name; if (parentClass == null) {
return name;
}
return parentClass.getNameWithoutPackage() + "." + name;
} }
public ClassInfo getParentClass() { public ClassInfo getParentClass() {
...@@ -154,7 +169,7 @@ public final class ClassInfo { ...@@ -154,7 +169,7 @@ public final class ClassInfo {
} }
if (obj instanceof ClassInfo) { if (obj instanceof ClassInfo) {
ClassInfo other = (ClassInfo) obj; ClassInfo other = (ClassInfo) obj;
return this.getFullName().equals(other.getFullName()); return this.type.equals(other.type);
} }
return false; return false;
} }
......
...@@ -5,7 +5,7 @@ import jadx.core.dex.nodes.DexNode; ...@@ -5,7 +5,7 @@ import jadx.core.dex.nodes.DexNode;
import com.android.dex.FieldId; import com.android.dex.FieldId;
public class FieldInfo { public final class FieldInfo {
private final ClassInfo declClass; private final ClassInfo declClass;
private final String name; private final String name;
......
...@@ -621,12 +621,10 @@ public class InsnDecoder { ...@@ -621,12 +621,10 @@ public class InsnDecoder {
regs[i] = InsnArg.reg(regNum, elType, typeImmutable); regs[i] = InsnArg.reg(regNum, elType, typeImmutable);
} }
} }
InsnNode node = new FilledNewArrayNode(elType, regs == null ? 0 : regs.length); InsnNode node = new FilledNewArrayNode(elType, regs.length);
node.setResult(resReg == -1 ? null : InsnArg.reg(resReg, arrType)); node.setResult(resReg == -1 ? null : InsnArg.reg(resReg, arrType));
if (regs != null) { for (InsnArg arg : regs) {
for (InsnArg arg : regs) { node.addArg(arg);
node.addArg(arg);
}
} }
return node; return node;
} }
......
...@@ -26,6 +26,7 @@ public abstract class ArgType { ...@@ -26,6 +26,7 @@ public abstract class ArgType {
public static final ArgType OBJECT = object(Consts.CLASS_OBJECT); public static final ArgType OBJECT = object(Consts.CLASS_OBJECT);
public static final ArgType CLASS = object(Consts.CLASS_CLASS); public static final ArgType CLASS = object(Consts.CLASS_CLASS);
public static final ArgType STRING = object(Consts.CLASS_STRING); public static final ArgType STRING = object(Consts.CLASS_STRING);
public static final ArgType ENUM = object(Consts.CLASS_ENUM);
public static final ArgType THROWABLE = object(Consts.CLASS_THROWABLE); public static final ArgType THROWABLE = object(Consts.CLASS_THROWABLE);
public static final ArgType UNKNOWN = unknown(PrimitiveType.values()); public static final ArgType UNKNOWN = unknown(PrimitiveType.values());
......
...@@ -30,6 +30,7 @@ import java.util.List; ...@@ -30,6 +30,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly; import org.jetbrains.annotations.TestOnly;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -46,8 +47,8 @@ public class ClassNode extends LineAttrNode implements ILoadable { ...@@ -46,8 +47,8 @@ public class ClassNode extends LineAttrNode implements ILoadable {
private final DexNode dex; private final DexNode dex;
private final ClassInfo clsInfo; private final ClassInfo clsInfo;
private final AccessInfo accessFlags; private final AccessInfo accessFlags;
private ClassInfo superClass; private ArgType superClass;
private List<ClassInfo> interfaces; private List<ArgType> interfaces;
private Map<ArgType, List<ArgType>> genericMap; private Map<ArgType, List<ArgType>> genericMap;
private final List<MethodNode> methods; private final List<MethodNode> methods;
...@@ -70,11 +71,11 @@ public class ClassNode extends LineAttrNode implements ILoadable { ...@@ -70,11 +71,11 @@ public class ClassNode extends LineAttrNode implements ILoadable {
if (cls.getSupertypeIndex() == DexNode.NO_INDEX) { if (cls.getSupertypeIndex() == DexNode.NO_INDEX) {
this.superClass = null; this.superClass = null;
} else { } else {
this.superClass = ClassInfo.fromDex(dex, cls.getSupertypeIndex()); this.superClass = dex.getType(cls.getSupertypeIndex());
} }
this.interfaces = new ArrayList<ClassInfo>(cls.getInterfaces().length); this.interfaces = new ArrayList<ArgType>(cls.getInterfaces().length);
for (short interfaceIdx : cls.getInterfaces()) { for (short interfaceIdx : cls.getInterfaces()) {
this.interfaces.add(ClassInfo.fromDex(dex, interfaceIdx)); this.interfaces.add(dex.getType(interfaceIdx));
} }
if (cls.getClassDataOffset() != 0) { if (cls.getClassDataOffset() != 0) {
ClassData clsData = dex.readClassData(cls); ClassData clsData = dex.readClassData(cls);
...@@ -111,7 +112,7 @@ public class ClassNode extends LineAttrNode implements ILoadable { ...@@ -111,7 +112,7 @@ public class ClassNode extends LineAttrNode implements ILoadable {
int sfIdx = cls.getSourceFileIndex(); int sfIdx = cls.getSourceFileIndex();
if (sfIdx != DexNode.NO_INDEX) { if (sfIdx != DexNode.NO_INDEX) {
String fileName = dex.getString(sfIdx); String fileName = dex.getString(sfIdx);
if (!this.getFullName().contains(fileName.replace(".java", "")) if (!clsInfo.getFullName().contains(fileName.replace(".java", ""))
&& !fileName.equals("SourceFile")) { && !fileName.equals("SourceFile")) {
this.addAttr(new SourceFileAttr(fileName)); this.addAttr(new SourceFileAttr(fileName));
LOG.debug("Class '{}' compiled from '{}'", this, fileName); LOG.debug("Class '{}' compiled from '{}'", this, fileName);
...@@ -129,7 +130,7 @@ public class ClassNode extends LineAttrNode implements ILoadable { ...@@ -129,7 +130,7 @@ public class ClassNode extends LineAttrNode implements ILoadable {
this.accessFlags = new AccessInfo(accFlagsValue, AFType.CLASS); this.accessFlags = new AccessInfo(accFlagsValue, AFType.CLASS);
} catch (Exception e) { } catch (Exception e) {
throw new DecodeException("Error decode class: " + getFullName(), e); throw new DecodeException("Error decode class: " + clsInfo, e);
} }
} }
...@@ -191,12 +192,12 @@ public class ClassNode extends LineAttrNode implements ILoadable { ...@@ -191,12 +192,12 @@ public class ClassNode extends LineAttrNode implements ILoadable {
// parse class generic map // parse class generic map
genericMap = sp.consumeGenericMap(); genericMap = sp.consumeGenericMap();
// parse super class signature // parse super class signature
superClass = ClassInfo.fromType(dex, sp.consumeType()); superClass = sp.consumeType();
// parse interfaces signatures // parse interfaces signatures
for (int i = 0; i < interfaces.size(); i++) { for (int i = 0; i < interfaces.size(); i++) {
ArgType type = sp.consumeType(); ArgType type = sp.consumeType();
if (type != null) { if (type != null) {
interfaces.set(i, ClassInfo.fromType(dex, type)); interfaces.set(i, type);
} else { } else {
break; break;
} }
...@@ -247,11 +248,12 @@ public class ClassNode extends LineAttrNode implements ILoadable { ...@@ -247,11 +248,12 @@ public class ClassNode extends LineAttrNode implements ILoadable {
} }
} }
public ClassInfo getSuperClass() { @Nullable
public ArgType getSuperClass() {
return superClass; return superClass;
} }
public List<ClassInfo> getInterfaces() { public List<ArgType> getInterfaces() {
return interfaces; return interfaces;
} }
...@@ -403,12 +405,14 @@ public class ClassNode extends LineAttrNode implements ILoadable { ...@@ -403,12 +405,14 @@ public class ClassNode extends LineAttrNode implements ILoadable {
} }
public boolean isEnum() { public boolean isEnum() {
return getAccessFlags().isEnum() && getSuperClass().getFullName().equals(Consts.CLASS_ENUM); return getAccessFlags().isEnum()
&& getSuperClass() != null
&& getSuperClass().getObject().equals(ArgType.ENUM.getObject());
} }
public boolean isAnonymous() { public boolean isAnonymous() {
return clsInfo.isInner() return clsInfo.isInner()
&& getShortName().startsWith(Consts.ANONYMOUS_CLASS_PREFIX) && clsInfo.getShortName().startsWith(Consts.ANONYMOUS_CLASS_PREFIX)
&& getDefaultConstructor() != null; && getDefaultConstructor() != null;
} }
...@@ -429,24 +433,34 @@ public class ClassNode extends LineAttrNode implements ILoadable { ...@@ -429,24 +433,34 @@ public class ClassNode extends LineAttrNode implements ILoadable {
return dex; return dex;
} }
public String getRawName() {
return clsInfo.getRawName();
}
/**
* Internal class info (don't use in code generation and external api).
*/
public ClassInfo getClassInfo() { public ClassInfo getClassInfo() {
return clsInfo; return clsInfo;
} }
/**
* Class info for external usage (code generation and external api).
*/
public ClassInfo getAlias() {
return clsInfo.getAlias();
}
public String getShortName() { public String getShortName() {
return clsInfo.getShortName(); return clsInfo.getAlias().getShortName();
} }
public String getFullName() { public String getFullName() {
return clsInfo.getFullName(); return clsInfo.getAlias().getFullName();
} }
public String getPackage() { public String getPackage() {
return clsInfo.getPackage(); return clsInfo.getAlias().getPackage();
}
public String getRawName() {
return clsInfo.getRawName();
} }
public void setCode(CodeWriter code) { public void setCode(CodeWriter code) {
...@@ -471,6 +485,6 @@ public class ClassNode extends LineAttrNode implements ILoadable { ...@@ -471,6 +485,6 @@ public class ClassNode extends LineAttrNode implements ILoadable {
@Override @Override
public String toString() { public String toString() {
return getFullName(); return clsInfo.getFullName();
} }
} }
...@@ -33,7 +33,10 @@ public class DexNode { ...@@ -33,7 +33,10 @@ public class DexNode {
private final RootNode root; private final RootNode root;
private final Dex dexBuf; private final Dex dexBuf;
private final InputFile file;
private final List<ClassNode> classes = new ArrayList<ClassNode>(); private final List<ClassNode> classes = new ArrayList<ClassNode>();
private final Map<ClassInfo, ClassNode> clsMap = new HashMap<ClassInfo, ClassNode>();
private final Map<Object, FieldNode> constFields = new HashMap<Object, FieldNode>(); private final Map<Object, FieldNode> constFields = new HashMap<Object, FieldNode>();
...@@ -41,12 +44,36 @@ public class DexNode { ...@@ -41,12 +44,36 @@ public class DexNode {
public DexNode(RootNode root, InputFile input) { public DexNode(RootNode root, InputFile input) {
this.root = root; this.root = root;
this.file = input;
this.dexBuf = input.getDexBuffer(); this.dexBuf = input.getDexBuffer();
} }
public void loadClasses() throws DecodeException { public void loadClasses() throws DecodeException {
for (ClassDef cls : dexBuf.classDefs()) { for (ClassDef cls : dexBuf.classDefs()) {
classes.add(new ClassNode(this, cls)); ClassNode clsNode = new ClassNode(this, cls);
classes.add(clsNode);
clsMap.put(clsNode.getClassInfo(), clsNode);
}
}
void initInnerClasses() {
// move inner classes
List<ClassNode> inner = new ArrayList<ClassNode>();
for (ClassNode cls : classes) {
if (cls.getClassInfo().isInner()) {
inner.add(cls);
}
}
for (ClassNode cls : inner) {
ClassInfo clsInfo = cls.getClassInfo();
ClassNode parent = resolveClass(clsInfo.getParentClass());
if (parent == null) {
clsMap.remove(clsInfo);
clsInfo.notInner(cls.dex());
clsMap.put(clsInfo, cls);
} else {
parent.addInnerClass(cls);
}
} }
} }
...@@ -56,7 +83,7 @@ public class DexNode { ...@@ -56,7 +83,7 @@ public class DexNode {
@Nullable @Nullable
public ClassNode resolveClass(ClassInfo clsInfo) { public ClassNode resolveClass(ClassInfo clsInfo) {
return root.resolveClass(clsInfo); return clsMap.get(clsInfo);
} }
@Nullable @Nullable
...@@ -85,6 +112,10 @@ public class DexNode { ...@@ -85,6 +112,10 @@ public class DexNode {
return infoStorage; return infoStorage;
} }
public InputFile getInputFile() {
return file;
}
// DexBuffer wrappers // DexBuffer wrappers
public String getString(int index) { public String getString(int index) {
......
...@@ -597,7 +597,7 @@ public class MethodNode extends LineAttrNode implements ILoadable { ...@@ -597,7 +597,7 @@ public class MethodNode extends LineAttrNode implements ILoadable {
@Override @Override
public String toString() { public String toString() {
return parentClass.getFullName() + "." + mthInfo.getName() return parentClass + "." + mthInfo.getName()
+ "(" + Utils.listToString(mthInfo.getArgumentsTypes()) + "):" + "(" + Utils.listToString(mthInfo.getArgumentsTypes()) + "):"
+ retType; + retType;
} }
......
package jadx.core.dex.nodes; package jadx.core.dex.nodes;
import jadx.api.IJadxArgs;
import jadx.api.ResourceFile; import jadx.api.ResourceFile;
import jadx.api.ResourceType; import jadx.api.ResourceType;
import jadx.api.ResourcesLoader; import jadx.api.ResourcesLoader;
...@@ -27,19 +28,19 @@ import org.slf4j.LoggerFactory; ...@@ -27,19 +28,19 @@ import org.slf4j.LoggerFactory;
public class RootNode { public class RootNode {
private static final Logger LOG = LoggerFactory.getLogger(RootNode.class); private static final Logger LOG = LoggerFactory.getLogger(RootNode.class);
private final Map<String, ClassNode> names = new HashMap<String, ClassNode>();
private final ErrorsCounter errorsCounter = new ErrorsCounter(); private final ErrorsCounter errorsCounter = new ErrorsCounter();
private final IJadxArgs args;
private List<DexNode> dexNodes; private List<DexNode> dexNodes;
/**
* Resources *
*/
private Map<Integer, String> resourcesNames = new HashMap<Integer, String>(); private Map<Integer, String> resourcesNames = new HashMap<Integer, String>();
@Nullable @Nullable
private String appPackage; private String appPackage;
private ClassNode appResClass; private ClassNode appResClass;
public RootNode(IJadxArgs args) {
this.args = args;
}
public void load(List<InputFile> dexFiles) throws DecodeException { public void load(List<InputFile> dexFiles) throws DecodeException {
dexNodes = new ArrayList<DexNode>(dexFiles.size()); dexNodes = new ArrayList<DexNode>(dexFiles.size());
for (InputFile dex : dexFiles) { for (InputFile dex : dexFiles) {
...@@ -54,21 +55,7 @@ public class RootNode { ...@@ -54,21 +55,7 @@ public class RootNode {
for (DexNode dexNode : dexNodes) { for (DexNode dexNode : dexNodes) {
dexNode.loadClasses(); dexNode.loadClasses();
} }
initInnerClasses();
List<ClassNode> classes = new ArrayList<ClassNode>();
for (DexNode dexNode : dexNodes) {
for (ClassNode cls : dexNode.getClasses()) {
names.put(cls.getFullName(), cls);
}
classes.addAll(dexNode.getClasses());
}
try {
initClassPath(classes);
} catch (IOException e) {
throw new DecodeException("Error loading classpath", e);
}
initInnerClasses(classes);
} }
public void loadResources(List<ResourceFile> resources) { public void loadResources(List<ResourceFile> resources) {
...@@ -99,55 +86,52 @@ public class RootNode { ...@@ -99,55 +86,52 @@ public class RootNode {
ResourceStorage resStorage = parser.getResStorage(); ResourceStorage resStorage = parser.getResStorage();
resourcesNames = resStorage.getResourcesNames(); resourcesNames = resStorage.getResourcesNames();
appPackage = resStorage.getAppPackage();
} }
public void initAppResClass() { public void initAppResClass() {
ClassNode resCls = null; ClassNode resCls;
if (appPackage != null) { if (appPackage == null) {
resCls = searchClassByName(appPackage + ".R"); appResClass = makeClass("R");
} else { return;
for (ClassNode cls : names.values()) {
if (cls.getShortName().equals("R")) {
resCls = cls;
break;
}
}
} }
String fullName = appPackage + ".R";
resCls = searchClassByName(fullName);
if (resCls != null) { if (resCls != null) {
appResClass = resCls; appResClass = resCls;
return; } else {
appResClass = makeClass(fullName);
} }
}
private ClassNode makeClass(String clsName) {
DexNode firstDex = dexNodes.get(0); DexNode firstDex = dexNodes.get(0);
appResClass = new ClassNode(firstDex, ClassInfo.fromName(firstDex, "R")); ClassInfo r = ClassInfo.fromName(firstDex, clsName);
return new ClassNode(firstDex, r);
} }
private static void initClassPath(List<ClassNode> classes) throws IOException, DecodeException { public void initClassPath() throws DecodeException {
if (!ArgType.isClspSet()) { try {
ClspGraph clsp = new ClspGraph(); if (!ArgType.isClspSet()) {
clsp.load(); ClspGraph clsp = new ClspGraph();
clsp.addApp(classes); clsp.load();
ArgType.setClsp(clsp); List<ClassNode> classes = new ArrayList<ClassNode>();
} for (DexNode dexNode : dexNodes) {
} classes.addAll(dexNode.getClasses());
}
clsp.addApp(classes);
private void initInnerClasses(List<ClassNode> classes) { ArgType.setClsp(clsp);
// move inner classes
List<ClassNode> inner = new ArrayList<ClassNode>();
for (ClassNode cls : classes) {
if (cls.getClassInfo().isInner()) {
inner.add(cls);
} }
} catch (IOException e) {
throw new DecodeException("Error loading classpath", e);
} }
for (ClassNode cls : inner) { }
ClassNode parent = resolveClass(cls.getClassInfo().getParentClass());
if (parent == null) { private void initInnerClasses() {
names.remove(cls.getFullName()); for (DexNode dexNode : dexNodes) {
cls.getClassInfo().notInner(cls.dex()); dexNode.initInnerClasses();
names.put(cls.getFullName(), cls);
} else {
parent.addInnerClass(cls);
}
} }
} }
...@@ -168,12 +152,14 @@ public class RootNode { ...@@ -168,12 +152,14 @@ public class RootNode {
} }
public ClassNode searchClassByName(String fullName) { public ClassNode searchClassByName(String fullName) {
return names.get(fullName); for (DexNode dexNode : dexNodes) {
} ClassInfo clsInfo = ClassInfo.fromName(dexNode, fullName);
ClassNode cls = dexNode.resolveClass(clsInfo);
public ClassNode resolveClass(ClassInfo cls) { if (cls != null) {
String fullName = cls.getFullName(); return cls;
return searchClassByName(fullName); }
}
return null;
} }
public List<DexNode> getDexNodes() { public List<DexNode> getDexNodes() {
...@@ -196,4 +182,8 @@ public class RootNode { ...@@ -196,4 +182,8 @@ public class RootNode {
public ClassNode getAppResClass() { public ClassNode getAppResClass() {
return appResClass; return appResClass;
} }
public IJadxArgs getArgs() {
return args;
}
} }
...@@ -2,11 +2,16 @@ package jadx.core.dex.visitors; ...@@ -2,11 +2,16 @@ package jadx.core.dex.visitors;
import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.exceptions.JadxException; import jadx.core.utils.exceptions.JadxException;
public class AbstractVisitor implements IDexTreeVisitor { public class AbstractVisitor implements IDexTreeVisitor {
@Override @Override
public void init(RootNode root) throws JadxException {
}
@Override
public boolean visit(ClassNode cls) throws JadxException { public boolean visit(ClassNode cls) throws JadxException {
return true; return true;
} }
......
...@@ -199,7 +199,7 @@ public class ClassModifier extends AbstractVisitor { ...@@ -199,7 +199,7 @@ public class ClassModifier extends AbstractVisitor {
*/ */
private static void processStaticFieldAssign(ClassNode cls, IndexInsnNode insn) { private static void processStaticFieldAssign(ClassNode cls, IndexInsnNode insn) {
FieldInfo field = (FieldInfo) insn.getIndex(); FieldInfo field = (FieldInfo) insn.getIndex();
String thisClass = cls.getFullName(); String thisClass = cls.getClassInfo().getFullName();
if (field.getDeclClass().getFullName().equals(thisClass)) { if (field.getDeclClass().getFullName().equals(thisClass)) {
FieldNode fn = cls.searchField(field); FieldNode fn = cls.searchField(field);
if (fn != null && fn.getAccessFlags().isFinal()) { if (fn != null && fn.getAccessFlags().isFinal()) {
......
...@@ -31,8 +31,8 @@ public class DependencyCollector extends AbstractVisitor { ...@@ -31,8 +31,8 @@ public class DependencyCollector extends AbstractVisitor {
private static void processClass(ClassNode cls, DexNode dex, Set<ClassNode> depList) { private static void processClass(ClassNode cls, DexNode dex, Set<ClassNode> depList) {
addDep(dex, depList, cls.getSuperClass()); addDep(dex, depList, cls.getSuperClass());
for (ClassInfo clsInfo : cls.getInterfaces()) { for (ArgType iType : cls.getInterfaces()) {
addDep(dex, depList, clsInfo); addDep(dex, depList, iType);
} }
for (FieldNode fieldNode : cls.getFields()) { for (FieldNode fieldNode : cls.getFields()) {
addDep(dex, depList, fieldNode.getType()); addDep(dex, depList, fieldNode.getType());
...@@ -77,7 +77,7 @@ public class DependencyCollector extends AbstractVisitor { ...@@ -77,7 +77,7 @@ public class DependencyCollector extends AbstractVisitor {
private static void addDep(DexNode dex, Set<ClassNode> depList, ArgType type) { private static void addDep(DexNode dex, Set<ClassNode> depList, ArgType type) {
if (type != null) { if (type != null) {
if (type.isObject()) { if (type.isObject()) {
addDep(dex, depList, ClassInfo.fromName(type.getObject())); addDep(dex, depList, ClassInfo.fromName(dex, type.getObject()));
ArgType[] genericTypes = type.getGenericTypes(); ArgType[] genericTypes = type.getGenericTypes();
if (type.isGeneric() && genericTypes != null) { if (type.isGeneric() && genericTypes != null) {
for (ArgType argType : genericTypes) { for (ArgType argType : genericTypes) {
......
...@@ -2,7 +2,6 @@ package jadx.core.dex.visitors; ...@@ -2,7 +2,6 @@ package jadx.core.dex.visitors;
import jadx.core.codegen.CodeWriter; import jadx.core.codegen.CodeWriter;
import jadx.core.codegen.MethodGen; import jadx.core.codegen.MethodGen;
import jadx.core.deobf.Deobfuscator;
import jadx.core.dex.attributes.IAttributeNode; import jadx.core.dex.attributes.IAttributeNode;
import jadx.core.dex.instructions.IfNode; import jadx.core.dex.instructions.IfNode;
import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.InsnType;
...@@ -68,7 +67,7 @@ public class DotGraphVisitor extends AbstractVisitor { ...@@ -68,7 +67,7 @@ public class DotGraphVisitor extends AbstractVisitor {
public void process(MethodNode mth) { public void process(MethodNode mth) {
dot.startLine("digraph \"CFG for"); dot.startLine("digraph \"CFG for");
dot.add(escape(mth.getParentClass().getFullName() + "." + mth.getMethodInfo().getShortId())); dot.add(escape(mth.getParentClass() + "." + mth.getMethodInfo().getShortId()));
dot.add("\" {"); dot.add("\" {");
if (useRegions) { if (useRegions) {
...@@ -85,7 +84,7 @@ public class DotGraphVisitor extends AbstractVisitor { ...@@ -85,7 +84,7 @@ public class DotGraphVisitor extends AbstractVisitor {
dot.startLine("MethodNode[shape=record,label=\"{"); dot.startLine("MethodNode[shape=record,label=\"{");
dot.add(escape(mth.getAccessFlags().makeString())); dot.add(escape(mth.getAccessFlags().makeString()));
dot.add(escape(mth.getReturnType() + " " dot.add(escape(mth.getReturnType() + " "
+ mth.getParentClass().getFullName() + "." + mth.getName() + mth.getParentClass() + "." + mth.getName()
+ "(" + Utils.listToString(mth.getArguments(true)) + ") ")); + "(" + Utils.listToString(mth.getArguments(true)) + ") "));
String attrs = attributesString(mth); String attrs = attributesString(mth);
...@@ -105,7 +104,7 @@ public class DotGraphVisitor extends AbstractVisitor { ...@@ -105,7 +104,7 @@ public class DotGraphVisitor extends AbstractVisitor {
+ (useRegions ? ".regions" : "") + (useRegions ? ".regions" : "")
+ (rawInsn ? ".raw" : "") + (rawInsn ? ".raw" : "")
+ ".dot"; + ".dot";
dot.save(dir, Deobfuscator.instance().getClassFullPath(mth.getParentClass().getClassInfo()) + "_graphs", fileName); dot.save(dir, mth.getParentClass().getClassInfo().getFullPath() + "_graphs", fileName);
} }
private void processMethodRegion(MethodNode mth) { private void processMethodRegion(MethodNode mth) {
......
...@@ -2,6 +2,7 @@ package jadx.core.dex.visitors; ...@@ -2,6 +2,7 @@ package jadx.core.dex.visitors;
import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.exceptions.JadxException; import jadx.core.utils.exceptions.JadxException;
/** /**
...@@ -10,6 +11,11 @@ import jadx.core.utils.exceptions.JadxException; ...@@ -10,6 +11,11 @@ import jadx.core.utils.exceptions.JadxException;
public interface IDexTreeVisitor { public interface IDexTreeVisitor {
/** /**
* Called after loading dex tree, but before visitor traversal.
*/
void init(RootNode root) throws JadxException;
/**
* Visit class * Visit class
* *
* @return false for disable child methods and inner classes traversal * @return false for disable child methods and inner classes traversal
......
package jadx.core.dex.visitors;
import jadx.core.deobf.Deobfuscator;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.exceptions.JadxException;
import java.io.File;
import org.apache.commons.io.FilenameUtils;
public class RenameVisitor extends AbstractVisitor {
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();
}
@Override
public boolean visit(ClassNode cls) throws JadxException {
// TODO: rename fields and methods
return false;
}
}
...@@ -2,7 +2,6 @@ package jadx.core.dex.visitors; ...@@ -2,7 +2,6 @@ package jadx.core.dex.visitors;
import jadx.api.IJadxArgs; import jadx.api.IJadxArgs;
import jadx.core.codegen.CodeWriter; import jadx.core.codegen.CodeWriter;
import jadx.core.deobf.Deobfuscator;
import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.ClassNode;
import jadx.core.utils.exceptions.CodegenException; import jadx.core.utils.exceptions.CodegenException;
...@@ -25,7 +24,7 @@ public class SaveCode extends AbstractVisitor { ...@@ -25,7 +24,7 @@ public class SaveCode extends AbstractVisitor {
public static void save(File dir, IJadxArgs args, ClassNode cls) { public static void save(File dir, IJadxArgs args, ClassNode cls) {
CodeWriter clsCode = cls.getCode(); CodeWriter clsCode = cls.getCode();
String fileName = Deobfuscator.instance().getClassFullPath(cls.getClassInfo()) + ".java"; String fileName = cls.getClassInfo().getFullPath() + ".java";
if (args.isFallbackMode()) { if (args.isFallbackMode()) {
fileName += ".jadx"; fileName += ".jadx";
} }
......
...@@ -145,70 +145,70 @@ public class SimplifyVisitor extends AbstractVisitor { ...@@ -145,70 +145,70 @@ public class SimplifyVisitor extends AbstractVisitor {
} }
private static InsnNode convertInvoke(MethodNode mth, InsnNode insn) { private static InsnNode convertInvoke(MethodNode mth, InsnNode insn) {
MethodInfo callMth = ((InvokeNode) insn).getCallMth(); MethodInfo callMth = ((InvokeNode) insn).getCallMth();
// If this is a 'new StringBuilder(xxx).append(yyy).append(zzz).toString(), // If this is a 'new StringBuilder(xxx).append(yyy).append(zzz).toString(),
// convert it to STRING_CONCAT pseudo instruction. // convert it to STRING_CONCAT pseudo instruction.
if (callMth.getDeclClass().getFullName().equals(Consts.CLASS_STRING_BUILDER) if (callMth.getDeclClass().getFullName().equals(Consts.CLASS_STRING_BUILDER)
&& callMth.getShortId().equals(Consts.MTH_TOSTRING_SIGNATURE) && callMth.getShortId().equals(Consts.MTH_TOSTRING_SIGNATURE)
&& insn.getArg(0).isInsnWrap()) { && insn.getArg(0).isInsnWrap()) {
try { try {
List<InsnNode> chain = flattenInsnChain(insn); List<InsnNode> chain = flattenInsnChain(insn);
int constrIndex = -1; //RAF int constrIndex = -1; //RAF
// Case where new StringBuilder() is called with NO args (the entire // Case where new StringBuilder() is called with NO args (the entire
// string is created using .append() calls: // string is created using .append() calls:
if (chain.size()>1 && chain.get(0).getType()==InsnType.CONSTRUCTOR) { if (chain.size() > 1 && chain.get(0).getType() == InsnType.CONSTRUCTOR) {
constrIndex = 0; constrIndex = 0;
} else if (chain.size()>2 && chain.get(1).getType()==InsnType.CONSTRUCTOR) { } else if (chain.size() > 2 && chain.get(1).getType() == InsnType.CONSTRUCTOR) {
//RAF Case where the first string element is String arg to the //RAF Case where the first string element is String arg to the
// new StringBuilder("xxx") constructor // new StringBuilder("xxx") constructor
constrIndex = 1; constrIndex = 1;
} else if (chain.size()>3 && chain.get(2).getType()==InsnType.CONSTRUCTOR) { } else if (chain.size() > 3 && chain.get(2).getType() == InsnType.CONSTRUCTOR) {
//RAF Case where the first string element is String.valueOf() arg //RAF Case where the first string element is String.valueOf() arg
// to the new StringBuilder(String.valueOf(zzz)) constructor // to the new StringBuilder(String.valueOf(zzz)) constructor
constrIndex = 2; constrIndex = 2;
} }
if (constrIndex != -1) { // If we found a CONSTRUCTOR, is it a StringBuilder? if (constrIndex != -1) { // If we found a CONSTRUCTOR, is it a StringBuilder?
ConstructorInsn constr = (ConstructorInsn) chain.get(constrIndex); ConstructorInsn constr = (ConstructorInsn) chain.get(constrIndex);
if (constr.getClassType().getFullName().equals(Consts.CLASS_STRING_BUILDER)) { if (constr.getClassType().getFullName().equals(Consts.CLASS_STRING_BUILDER)) {
int len = chain.size(), argInd = 1; int len = chain.size(), argInd = 1;
InsnNode concatInsn = new InsnNode(InsnType.STR_CONCAT, len-1); InsnNode concatInsn = new InsnNode(InsnType.STR_CONCAT, len - 1);
InsnNode argInsn; InsnNode argInsn;
if (constrIndex > 0) { // There was an arg to the StringBuilder constr if (constrIndex > 0) { // There was an arg to the StringBuilder constr
InsnWrapArg iwa; InsnWrapArg iwa;
if (constrIndex==2 if (constrIndex == 2
&& (argInsn = chain.get(1)).getType()==InsnType.INVOKE && (argInsn = chain.get(1)).getType() == InsnType.INVOKE
&& ((InvokeNode)argInsn).getCallMth().getName().compareTo("valueOf")==0) { && ((InvokeNode) argInsn).getCallMth().getName().compareTo("valueOf") == 0) {
// The argument of new StringBuilder() is a String.valueOf(chainElement0) // The argument of new StringBuilder() is a String.valueOf(chainElement0)
iwa = (InsnWrapArg)argInsn.getArg(0); iwa = (InsnWrapArg) argInsn.getArg(0);
argInd = 3; // Cause for loop below to skip to after the constructor argInd = 3; // Cause for loop below to skip to after the constructor
} else { } else {
InsnNode firstNode = chain.get(0); InsnNode firstNode = chain.get(0);
if (firstNode instanceof ConstStringNode) { if (firstNode instanceof ConstStringNode) {
ConstStringNode csn = (ConstStringNode) firstNode; ConstStringNode csn = (ConstStringNode) firstNode;
iwa = new InsnWrapArg(csn); iwa = new InsnWrapArg(csn);
argInd = 2; // Cause for loop below to skip to after the constructor argInd = 2; // Cause for loop below to skip to after the constructor
} else { } else {
return null; return null;
} }
} }
concatInsn.addArg(iwa); concatInsn.addArg(iwa);
} }
for (; argInd < len; argInd++) { // Add the .append(xxx) arg string to concat for (; argInd < len; argInd++) { // Add the .append(xxx) arg string to concat
concatInsn.addArg(chain.get(argInd).getArg(1)); concatInsn.addArg(chain.get(argInd).getArg(1));
} }
concatInsn.setResult(insn.getResult()); concatInsn.setResult(insn.getResult());
return concatInsn; return concatInsn;
} // end of if constructor is for StringBuilder } // end of if constructor is for StringBuilder
} // end of if we found a constructor early in the chain } // end of if we found a constructor early in the chain
} catch (Throwable e) { } catch (Throwable e) {
LOG.debug("Can't convert string concatenation: {} insn: {}", mth, insn, e); LOG.debug("Can't convert string concatenation: {} insn: {}", mth, insn, e);
} }
} }
return null; return null;
} }
private static InsnNode simplifyArith(InsnNode insn) { private static InsnNode simplifyArith(InsnNode insn) {
......
...@@ -4,6 +4,8 @@ import java.io.IOException; ...@@ -4,6 +4,8 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import org.jetbrains.annotations.NotNull;
public class ParserStream { public class ParserStream {
protected static final Charset STRING_CHARSET_UTF16 = Charset.forName("UTF-16LE"); protected static final Charset STRING_CHARSET_UTF16 = Charset.forName("UTF-16LE");
...@@ -15,7 +17,7 @@ public class ParserStream { ...@@ -15,7 +17,7 @@ public class ParserStream {
private final InputStream input; private final InputStream input;
private long readPos = 0; private long readPos = 0;
public ParserStream(InputStream inputStream) { public ParserStream(@NotNull InputStream inputStream) {
this.input = inputStream; this.input = inputStream;
} }
......
package jadx.tests.api; package jadx.tests.api;
import jadx.core.Consts;
import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.ClassNode;
import java.io.File; import java.io.File;
...@@ -21,8 +20,7 @@ public class SmaliTest extends IntegrationTest { ...@@ -21,8 +20,7 @@ public class SmaliTest extends IntegrationTest {
File smaliFile = getSmaliFile(clsName); File smaliFile = getSmaliFile(clsName);
File outDex = createTempFile(".dex"); File outDex = createTempFile(".dex");
compileSmali(smaliFile, outDex); compileSmali(smaliFile, outDex);
String fullClsName = Consts.DEFAULT_PACKAGE_NAME + "." + clsName; return getClassNodeFromFile(outDex, clsName);
return getClassNodeFromFile(outDex, fullClsName);
} }
private static File getSmaliFile(String clsName) { private static File getSmaliFile(String clsName) {
......
...@@ -12,42 +12,42 @@ import static org.junit.Assert.assertThat; ...@@ -12,42 +12,42 @@ import static org.junit.Assert.assertThat;
public class TestSwitchReturnFromCase extends IntegrationTest { public class TestSwitchReturnFromCase extends IntegrationTest {
public static class TestCls { public static class TestCls {
public void test(int a) { public void test(int a) {
String s = null; String s = null;
if (a > 1000) { if (a > 1000) {
return; return;
} }
switch (a % 4) { switch (a % 4) {
case 1: case 1:
s = "1"; s = "1";
break; break;
case 2: case 2:
s = "2"; s = "2";
break; break;
case 3: case 3:
case 4: case 4:
s = "4"; s = "4";
break; break;
case 5: case 5:
return; return;
} }
s = "5"; s = "5";
} }
} }
@Test @Test
public void test() { public void test() {
ClassNode cls = getClassNode(TestCls.class); ClassNode cls = getClassNode(TestCls.class);
String code = cls.getCode().toString(); String code = cls.getCode().toString();
assertThat(code, containsString("switch (a % 4) {")); assertThat(code, containsString("switch (a % 4) {"));
assertEquals(5, count(code, "case ")); assertEquals(5, count(code, "case "));
assertEquals(3, count(code, "break;")); assertEquals(3, count(code, "break;"));
assertThat(code, containsOne("s = \"1\";")); assertThat(code, containsOne("s = \"1\";"));
assertThat(code, containsOne("s = \"2\";")); assertThat(code, containsOne("s = \"2\";"));
assertThat(code, containsOne("s = \"4\";")); assertThat(code, containsOne("s = \"4\";"));
assertThat(code, containsOne("s = \"5\";")); assertThat(code, containsOne("s = \"5\";"));
} }
} }
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