From 6ddb71e21fe0fbbf6df8b6d57c3828b1ef13444b Mon Sep 17 00:00:00 2001 From: Skylot Date: Thu, 1 Aug 2013 18:19:54 +0400 Subject: [PATCH] core: add classpath for precise class types resolving --- .gitignore | 3 +- build.gradle | 1 - jadx-core/build.gradle | 5 + jadx-core/clsp-data/android-4.3.jar | Bin 0 -> 66905 bytes .../src/main/java/jadx/api/Decompiler.java | 12 +- .../src/main/java/jadx/core/clsp/ClsSet.java | 218 ++++++++++++++++++ .../main/java/jadx/core/clsp/ClspGraph.java | 115 +++++++++ .../java/jadx/core/clsp/ConvertToClsSet.java | 67 ++++++ .../src/main/java/jadx/core/clsp/NClass.java | 55 +++++ .../java/jadx/core/codegen/CodeWriter.java | 13 +- .../java/jadx/core/codegen/MethodGen.java | 6 +- .../java/jadx/core/dex/info/ClassInfo.java | 4 + .../core/dex/instructions/args/ArgType.java | 44 ++-- .../java/jadx/core/dex/nodes/ClassNode.java | 6 +- .../java/jadx/core/dex/nodes/RootNode.java | 77 ++++--- .../src/main/java/jadx/core/utils/Utils.java | 13 ++ .../test/java/jadx/tests/TypeMergeTest.java | 39 +++- jadx-gui/build.gradle | 1 + .../src/main/java/jadx/gui/utils/Utils.java | 3 + .../main/java/jadx/samples/TestTryCatch.java | 22 +- 20 files changed, 620 insertions(+), 84 deletions(-) create mode 100644 jadx-core/clsp-data/android-4.3.jar create mode 100644 jadx-core/src/main/java/jadx/core/clsp/ClsSet.java create mode 100644 jadx-core/src/main/java/jadx/core/clsp/ClspGraph.java create mode 100644 jadx-core/src/main/java/jadx/core/clsp/ConvertToClsSet.java create mode 100644 jadx-core/src/main/java/jadx/core/clsp/NClass.java diff --git a/.gitignore b/.gitignore index 117777baf..3065caeca 100644 --- a/.gitignore +++ b/.gitignore @@ -17,8 +17,9 @@ idea/ .gradle/ gradle.properties +*-tmp/ + *.dex -*.jar *.class *.dump *.log diff --git a/build.gradle b/build.gradle index 432a7c19b..0479a2482 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,6 @@ subprojects { dependencies { compile 'org.slf4j:slf4j-api:1.7.5' - compile 'ch.qos.logback:logback-classic:1.0.13' testCompile 'junit:junit:4.11' } diff --git a/jadx-core/build.gradle b/jadx-core/build.gradle index 6a2f8a945..4d905fe6c 100644 --- a/jadx-core/build.gradle +++ b/jadx-core/build.gradle @@ -1,4 +1,9 @@ +ext.jadxClasspath = 'clsp-data/android-4.3.jar' + dependencies { compile 'com.google.android.tools:dx:1.7' + compile 'ch.qos.logback:logback-classic:1.0.13' + + runtime files(jadxClasspath) } diff --git a/jadx-core/clsp-data/android-4.3.jar b/jadx-core/clsp-data/android-4.3.jar new file mode 100644 index 0000000000000000000000000000000000000000..d5b7e6299b6a0668fe6342466351d6ebe17e0bda GIT binary patch literal 66905 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 L008|20RR91aJ4QS literal 0 HcmV?d00001 diff --git a/jadx-core/src/main/java/jadx/api/Decompiler.java b/jadx-core/src/main/java/jadx/api/Decompiler.java index 2e1c3d117..9acc7cdd0 100644 --- a/jadx-core/src/main/java/jadx/api/Decompiler.java +++ b/jadx-core/src/main/java/jadx/api/Decompiler.java @@ -64,8 +64,9 @@ public final class Decompiler { } public List getClasses() { - List classes = new ArrayList(root.getClasses().size()); - for (ClassNode classNode : root.getClasses()) { + List classNodeList = root.getClasses(false); + List classes = new ArrayList(classNodeList.size()); + for (ClassNode classNode : classNodeList) { classes.add(new JavaClass(this, classNode)); } return Collections.unmodifiableList(classes); @@ -101,7 +102,7 @@ public final class Decompiler { LOG.info("processing ..."); ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threadsCount); - for (ClassNode cls : root.getClasses()) { + for (ClassNode cls : root.getClasses(false)) { if (cls.getCode() == null) { ProcessClass job = new ProcessClass(cls, passList); executor.execute(job); @@ -133,10 +134,9 @@ public final class Decompiler { ClassInfo.clearCache(); ErrorsCounter.reset(); - root = new RootNode(args, inputFiles); + root = new RootNode(); LOG.info("loading ..."); - root.load(); - root.init(); + root.load(inputFiles); } void processClass(ClassNode cls) { diff --git a/jadx-core/src/main/java/jadx/core/clsp/ClsSet.java b/jadx-core/src/main/java/jadx/core/clsp/ClsSet.java new file mode 100644 index 000000000..7b151a2af --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/clsp/ClsSet.java @@ -0,0 +1,218 @@ +package jadx.core.clsp; + +import jadx.core.dex.info.ClassInfo; +import jadx.core.dex.nodes.ClassNode; +import jadx.core.dex.nodes.RootNode; +import jadx.core.utils.Utils; +import jadx.core.utils.exceptions.DecodeException; + +import java.io.BufferedOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Classes list for import into classpath graph + */ +public class ClsSet { + private static final Logger LOG = LoggerFactory.getLogger(ClsSet.class); + + private static final String CLST_EXTENSION = ".jcst"; + private static final String CLST_FILENAME = "core" + CLST_EXTENSION; + private static final String CLST_PKG_PATH = ClsSet.class.getPackage().getName().replace('.', '/'); + + private static final String JADX_CLS_SET_HEADER = "jadx-cst"; + private static final int VERSION = 1; + + private NClass[] classes; + + public void load(RootNode root) { + List list = root.getClasses(true); + Map names = new HashMap(list.size()); + int k = 0; + for (ClassNode cls : list) { + String clsRawName = cls.getRawName(); + if (cls.getAccessFlags().isPublic()) { + NClass nClass = new NClass(clsRawName, k); + if (names.put(clsRawName, nClass) != null) { + throw new RuntimeException("Duplicate class: " + clsRawName); + } + k++; + } else { + names.put(clsRawName, null); + } + } + classes = new NClass[k]; + k = 0; + for (ClassNode cls : list) { + if (cls.getAccessFlags().isPublic()) { + NClass nClass = getCls(cls.getRawName(), names); + nClass.setParents(makeParentsArray(cls, names)); + classes[k] = nClass; + k++; + } + } + } + + public static NClass[] makeParentsArray(ClassNode cls, Map names) { + List parents = new ArrayList(1 + cls.getInterfaces().size()); + ClassInfo superClass = cls.getSuperClass(); + if (superClass != null) { + NClass c = getCls(superClass.getRawName(), names); + if (c != null) { + parents.add(c); + } + } + for (ClassInfo iface : cls.getInterfaces()) { + NClass c = getCls(iface.getRawName(), names); + if (c != null) { + parents.add(c); + } + } + return parents.toArray(new NClass[parents.size()]); + } + + private static NClass getCls(String fullName, Map names) { + NClass id = names.get(fullName); + if (id == null && !names.containsKey(fullName)) { + LOG.warn("Class not found: " + fullName); + } + return id; + } + + void save(File output) throws IOException { + Utils.makeDirsForFile(output); + + BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(output)); + String outputName = output.getName(); + if (outputName.endsWith(CLST_EXTENSION)) { + save(outputStream); + } else if (outputName.endsWith(".jar")) { + ZipOutputStream out = new ZipOutputStream(outputStream); + try { + out.putNextEntry(new ZipEntry(CLST_PKG_PATH + "/" + CLST_FILENAME)); + save(out); + out.closeEntry(); + } finally { + out.close(); + outputStream.close(); + } + } else { + throw new RuntimeException("Unknown file format: " + outputName); + } + } + + public void save(OutputStream output) throws IOException { + DataOutputStream out = new DataOutputStream(output); + out.writeBytes(JADX_CLS_SET_HEADER); + out.writeByte(VERSION); + + LOG.info("Classes count: " + classes.length); + out.writeInt(classes.length); + for (NClass cls : classes) { + writeString(out, cls.getName()); + } + for (NClass cls : classes) { + NClass[] parents = cls.getParents(); + out.writeByte(parents.length); + for (NClass parent : parents) { + out.writeInt(parent.getId()); + } + } + } + + public void load() throws IOException, DecodeException { + InputStream input = getClass().getResourceAsStream(CLST_FILENAME); + if (input == null) { + throw new RuntimeException("Can't load classpath file: " + CLST_FILENAME); + } + load(input); + } + + public void load(File input) throws IOException, DecodeException { + String name = input.getName(); + InputStream inputStream = new FileInputStream(input); + if (name.endsWith(CLST_EXTENSION)) { + load(inputStream); + } else if (name.endsWith(".jar")) { + ZipInputStream in = new ZipInputStream(inputStream); + try { + ZipEntry entry = in.getNextEntry(); + while (entry != null) { + if (entry.getName().endsWith(CLST_EXTENSION)) { + load(in); + } + entry = in.getNextEntry(); + } + } finally { + in.close(); + } + } else { + throw new RuntimeException("Unknown file format: " + name); + } + } + + public void load(InputStream input) throws IOException, DecodeException { + DataInputStream in = new DataInputStream(input); + byte[] header = new byte[JADX_CLS_SET_HEADER.length()]; + in.read(header); + int version = in.readByte(); + if (!JADX_CLS_SET_HEADER.equals(new String(header)) || version != VERSION) { + throw new DecodeException("Wrong jadx class set header"); + } + int count = in.readInt(); + classes = new NClass[count]; + for (int i = 0; i < count; i++) { + String name = readString(in); + classes[i] = new NClass(name, i); + } + for (int i = 0; i < count; i++) { + int pCount = in.readByte(); + NClass[] parents = new NClass[pCount]; + for (int j = 0; j < pCount; j++) { + parents[j] = classes[in.readInt()]; + } + classes[i].setParents(parents); + } + } + + private void writeString(DataOutputStream out, String name) throws IOException { + byte[] bytes = name.getBytes(); + out.writeByte(bytes.length); + out.write(bytes); + } + + private static String readString(DataInputStream in) throws IOException { + int len = in.readByte(); + byte[] bytes = new byte[len]; + int count = in.read(bytes); + while (count != len) { + int res = in.read(bytes, count, len - count); + if (res == -1) { + throw new IOException("String read error"); + } else { + count += res; + } + } + return new String(bytes); + } + + public NClass[] getClasses() { + return classes; + } +} diff --git a/jadx-core/src/main/java/jadx/core/clsp/ClspGraph.java b/jadx-core/src/main/java/jadx/core/clsp/ClspGraph.java new file mode 100644 index 000000000..6a880977f --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/clsp/ClspGraph.java @@ -0,0 +1,115 @@ +package jadx.core.clsp; + +import jadx.core.dex.nodes.ClassNode; +import jadx.core.utils.exceptions.DecodeException; + +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Classes hierarchy graph + */ +public class ClspGraph { + private static final Logger LOG = LoggerFactory.getLogger(ClspGraph.class); + + private Map nameMap; + private Map> ancestorCache = new WeakHashMap>(); + + public void load() throws IOException, DecodeException { + ClsSet set = new ClsSet(); + set.load(); + addClasspath(set); + } + + public void addClasspath(ClsSet set) { + NClass[] arr = set.getClasses(); + if (nameMap == null) { + nameMap = new HashMap(arr.length); + for (NClass cls : arr) { + nameMap.put(cls.getName(), cls); + } + } else { + throw new RuntimeException("Classpath already loaded"); + } + } + + public void addApp(List classes) { + if (nameMap == null) { + throw new RuntimeException("Classpath must be loaded first"); + } + int size = classes.size(); + NClass[] nClasses = new NClass[size]; + for (int i = 0; i < size; i++) { + ClassNode cls = classes.get(i); + NClass nClass = new NClass(cls.getRawName(), -1); + nClasses[i] = nClass; + nameMap.put(cls.getRawName(), nClass); + } + for (int i = 0; i < size; i++) { + nClasses[i].setParents(ClsSet.makeParentsArray(classes.get(i), nameMap)); + } + } + + public boolean isImplements(String clsName, String implClsName) { + Set anc = getAncestors(clsName); + return anc.contains(implClsName); + } + + public String getCommonAncestor(String clsName, String implClsName) { + if (isImplements(clsName, implClsName)) { + return implClsName; + } + Set anc = getAncestors(clsName); + NClass cls = nameMap.get(implClsName); + if(cls != null) { + return searchCommonParent(anc, cls); + } else { + LOG.debug("Missing class: {}", implClsName); + return null; + } + } + + private String searchCommonParent(Set anc, NClass cls) { + for (NClass p : cls.getParents()) { + String name = p.getName(); + if (anc.contains(name)) { + return name; + } else { + String r = searchCommonParent(anc, p); + if (r != null) + return r; + } + } + return null; + } + + private Set getAncestors(String clsName) { + Set result = ancestorCache.get(clsName); + if (result == null) { + result = new HashSet(); + ancestorCache.put(clsName, result); + NClass cls = nameMap.get(clsName); + if(cls != null) { + addAncestorsNames(cls, result); + } else { + LOG.debug("Missing class: {}", clsName); + } + } + return result; + } + + private void addAncestorsNames(NClass cls, Set result) { + result.add(cls.getName()); + for (NClass p : cls.getParents()) { + addAncestorsNames(p, result); + } + } +} diff --git a/jadx-core/src/main/java/jadx/core/clsp/ConvertToClsSet.java b/jadx-core/src/main/java/jadx/core/clsp/ConvertToClsSet.java new file mode 100644 index 000000000..9ea88a360 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/clsp/ConvertToClsSet.java @@ -0,0 +1,67 @@ +package jadx.core.clsp; + +import jadx.core.dex.nodes.RootNode; +import jadx.core.utils.exceptions.DecodeException; +import jadx.core.utils.files.InputFile; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Utility class for convert dex or jar to jadx classes set (.jcst) + */ +public class ConvertToClsSet { + private static final Logger LOG = LoggerFactory.getLogger(ConvertToClsSet.class); + + public static void usage() { + LOG.info(" "); + } + + public static void main(String[] args) throws IOException, DecodeException { + if (args.length < 2) { + usage(); + System.exit(1); + } + File output = new File(args[0]); + + List inputFiles = new ArrayList(args.length - 1); + for (int i = 1; i < args.length; i++) { + File f = new File(args[i]); + if (f.isDirectory()) { + addFilesFromDirectory(f, inputFiles); + } else { + inputFiles.add(new InputFile(f)); + } + } + for (InputFile inputFile : inputFiles) { + LOG.info("Loaded: " + inputFile.getFile()); + } + + RootNode root = new RootNode(); + root.load(inputFiles); + + ClsSet set = new ClsSet(); + set.load(root); + set.save(output); + LOG.info("Output: " + output); + LOG.info("done"); + } + + private static void addFilesFromDirectory(File dir, List inputFiles) throws IOException, DecodeException { + File[] files = dir.listFiles(); + for (File file : files) { + if (file.isDirectory()) { + addFilesFromDirectory(file, inputFiles); + } + if (file.getName().endsWith(".dex")) { + inputFiles.add(new InputFile(file)); + } + } + } + +} diff --git a/jadx-core/src/main/java/jadx/core/clsp/NClass.java b/jadx-core/src/main/java/jadx/core/clsp/NClass.java new file mode 100644 index 000000000..5f3487b1a --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/clsp/NClass.java @@ -0,0 +1,55 @@ +package jadx.core.clsp; + +/** + * Class node in classpath graph + */ +public class NClass { + + private final String name; + private NClass[] parents; + private int id; + + public NClass(String name, int id) { + this.name = name; + this.id = id; + } + + public String getName() { + return name; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public NClass[] getParents() { + return parents; + } + + public void setParents(NClass[] parents) { + this.parents = parents; + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NClass nClass = (NClass) o; + if (!name.equals(nClass.name)) return false; + return true; + } + + @Override + public String toString() { + return name; + } +} diff --git a/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java b/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java index 88cb1c0c9..37df2686a 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java +++ b/jadx-core/src/main/java/jadx/core/codegen/CodeWriter.java @@ -1,7 +1,7 @@ package jadx.core.codegen; import jadx.core.dex.attributes.LineAttrNode; -import jadx.core.utils.exceptions.JadxRuntimeException; +import jadx.core.utils.Utils; import java.io.File; import java.io.PrintWriter; @@ -214,7 +214,7 @@ public class CodeWriter { PrintWriter out = null; try { - makeDirsForFile(file); + Utils.makeDirsForFile(file); out = new PrintWriter(file, "UTF-8"); String code = buf.toString(); code = removeFirstEmptyLine(code); @@ -227,13 +227,4 @@ public class CodeWriter { } } - private void makeDirsForFile(File file) { - File dir = file.getParentFile(); - if (!dir.exists()) { - // if directory already created in other thread mkdirs will return false, - // so check dir existence again - if (!dir.mkdirs() && !dir.exists()) - throw new JadxRuntimeException("Can't create directory " + dir); - } - } } diff --git a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java index c459e77de..5993df22b 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java @@ -172,7 +172,11 @@ public class MethodGen { else return name; } else { - return base + "_" + Utils.escape(TypeGen.translate(classGen, arg.getType())); + ArgType type = arg.getType(); + if (type.isPrimitive()) + return base + type.getPrimitiveType().getShortName().toLowerCase(); + else + return base + "_" + Utils.escape(TypeGen.translate(classGen, arg.getType())); } } } diff --git a/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java b/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java index bfcea7b95..48a1112eb 100644 --- a/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java +++ b/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java @@ -105,6 +105,10 @@ public final class ClassInfo { return name; } + public String getRawName() { + return type.getObject(); + } + public String getPackage() { return pkg; } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java index f0c077e20..bc5e9ce6d 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/ArgType.java @@ -1,6 +1,7 @@ package jadx.core.dex.instructions.args; import jadx.core.Consts; +import jadx.core.clsp.ClspGraph; import jadx.core.utils.Utils; import java.util.ArrayList; @@ -43,6 +44,16 @@ public abstract class ArgType { protected int hash; + private static ClspGraph clsp; + + public static ClspGraph getClsp() { + return clsp; + } + + public static void setClsp(ClspGraph clsp) { + ArgType.clsp = clsp; + } + private static ArgType primitive(PrimitiveType stype) { return new PrimitiveArg(stype); } @@ -344,35 +355,30 @@ public abstract class ArgType { } } } else { - if(a.isGenericType()) - return a; - if(b.isGenericType()) - return b; + if (a.isGenericType()) return a; + if (b.isGenericType()) return b; if (a.isObject() && b.isObject()) { - if (a.getObject().equals(b.getObject())) { - if (a.getGenericTypes() != null) - return a; - else - return b; - } else if (a.getObject().equals(OBJECT.getObject())) + String aObj = a.getObject(); + String bObj = b.getObject(); + if (aObj.equals(bObj)) { + return (a.getGenericTypes() != null ? a : b); + } else if (aObj.equals(Consts.CLASS_OBJECT)) { return b; - else if (b.getObject().equals(OBJECT.getObject())) + } else if (bObj.equals(Consts.CLASS_OBJECT)) { return a; - else + } else { // different objects - return null; + String obj = clsp.getCommonAncestor(aObj, bObj); + return (obj == null ? null : object(obj)); + } } - if (a.isArray() && b.isArray()) { ArgType res = merge(a.getArrayElement(), b.getArrayElement()); return (res == null ? null : ArgType.array(res)); } - - if (a.isPrimitive() && b.isPrimitive()) { - if (a.getRegCount() == b.getRegCount()) - // return primitive(PrimitiveType.getWidest(a.getPrimitiveType(), b.getPrimitiveType())); - return primitive(PrimitiveType.getSmaller(a.getPrimitiveType(), b.getPrimitiveType())); + if (a.isPrimitive() && b.isPrimitive() && a.getRegCount() == b.getRegCount()) { + return primitive(PrimitiveType.getSmaller(a.getPrimitiveType(), b.getPrimitiveType())); } } return null; diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java index 84efa9144..469c9db51 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java @@ -240,7 +240,7 @@ public class ClassNode extends LineAttrNode implements ILoadable { public FieldNode getConstField(Object o) { FieldNode field = constFields.get(o); - if(field == null) + if (field == null) field = dex.getConstFields().get(o); return field; } @@ -329,6 +329,10 @@ public class ClassNode extends LineAttrNode implements ILoadable { return clsInfo.getPackage(); } + public String getRawName() { + return clsInfo.getRawName(); + } + public void setCode(CodeWriter code) { this.code = code; } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java index c7670957d..7eebd5c73 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java @@ -1,34 +1,23 @@ package jadx.core.dex.nodes; -import jadx.api.IJadxArgs; +import jadx.core.clsp.ClspGraph; import jadx.core.dex.info.ClassInfo; +import jadx.core.dex.instructions.args.ArgType; import jadx.core.utils.exceptions.DecodeException; import jadx.core.utils.files.InputFile; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class RootNode { - private static final Logger LOG = LoggerFactory.getLogger(RootNode.class); - - private final IJadxArgs args; - private final List dexFiles; - - private List dexNodes; - private final List classes = new ArrayList(); private final Map names = new HashMap(); + private List dexNodes; + private ClspGraph clsp; - public RootNode(IJadxArgs args, List dexFiles) { - this.args = args; - this.dexFiles =dexFiles; - } - - public void load() throws DecodeException { + public void load(List dexFiles) throws DecodeException { dexNodes = new ArrayList(dexFiles.size()); for (InputFile dex : dexFiles) { DexNode dexNode; @@ -39,37 +28,63 @@ public class RootNode { } dexNodes.add(dexNode); } - - for (DexNode dexNode : dexNodes) - dexNode.loadClasses(); - for (DexNode dexNode : dexNodes) { - for (ClassNode cls : dexNode.getClasses()) + dexNode.loadClasses(); + } + + List classes = new ArrayList(); + 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 init() { + private void initClassPath(List classes) throws IOException, DecodeException { + clsp = new ClspGraph(); + clsp.load(); + clsp.addApp(classes); + + ArgType.setClsp(clsp); + } + + private void initInnerClasses(List classes) { // move inner classes List inner = new ArrayList(); - for (ClassNode cls : getClasses()) { + for (ClassNode cls : classes) { if (cls.getClassInfo().isInner()) inner.add(cls); } - for (ClassNode cls : inner) { ClassNode parent = resolveClass(cls.getClassInfo().getParentClass()); if (parent == null) { cls.getClassInfo().notInner(); } else { parent.addInnerClass(cls); - getClasses().remove(cls); } } } - public List getClasses() { + public List getClasses(boolean includeInner) { + List classes = new ArrayList(); + for (DexNode dexNode : dexNodes) { + for (ClassNode cls : dexNode.getClasses()) { + if (includeInner) { + classes.add(cls); + } else { + if (!cls.getClassInfo().isInner()) + classes.add(cls); + } + } + } return classes; } @@ -82,12 +97,4 @@ public class RootNode { ClassNode rCls = searchClassByName(fullName); return rCls; } - - public List getDexNodes() { - return dexNodes; - } - - public IJadxArgs getJadxArgs() { - return args; - } } diff --git a/jadx-core/src/main/java/jadx/core/utils/Utils.java b/jadx-core/src/main/java/jadx/core/utils/Utils.java index 3e0cfd90f..5288429b7 100644 --- a/jadx-core/src/main/java/jadx/core/utils/Utils.java +++ b/jadx-core/src/main/java/jadx/core/utils/Utils.java @@ -1,5 +1,8 @@ package jadx.core.utils; +import jadx.core.utils.exceptions.JadxRuntimeException; + +import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; @@ -138,4 +141,14 @@ public class Utils { } return "dev"; } + + public static void makeDirsForFile(File file) { + File dir = file.getParentFile(); + if (!dir.exists()) { + // if directory already created in other thread mkdirs will return false, + // so check dir existence again + if (!dir.mkdirs() && !dir.exists()) + throw new JadxRuntimeException("Can't create directory " + dir); + } + } } diff --git a/jadx-core/src/test/java/jadx/tests/TypeMergeTest.java b/jadx-core/src/test/java/jadx/tests/TypeMergeTest.java index 041cd4aac..7554e231d 100644 --- a/jadx-core/src/test/java/jadx/tests/TypeMergeTest.java +++ b/jadx-core/src/test/java/jadx/tests/TypeMergeTest.java @@ -1,9 +1,13 @@ package jadx.tests; +import jadx.core.clsp.ClspGraph; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.PrimitiveType; +import jadx.core.utils.exceptions.DecodeException; import junit.framework.TestCase; +import java.io.IOException; + import static jadx.core.dex.instructions.args.ArgType.BOOLEAN; import static jadx.core.dex.instructions.args.ArgType.CHAR; import static jadx.core.dex.instructions.args.ArgType.INT; @@ -12,12 +16,21 @@ import static jadx.core.dex.instructions.args.ArgType.NARROW; import static jadx.core.dex.instructions.args.ArgType.OBJECT; import static jadx.core.dex.instructions.args.ArgType.UNKNOWN; import static jadx.core.dex.instructions.args.ArgType.UNKNOWN_OBJECT; +import static jadx.core.dex.instructions.args.ArgType.genericType; import static jadx.core.dex.instructions.args.ArgType.object; import static jadx.core.dex.instructions.args.ArgType.unknown; public class TypeMergeTest extends TestCase { - public void testMerge() { + private void initClsp() throws IOException, DecodeException { + ClspGraph clsp = new ClspGraph(); + clsp.load(); + ArgType.setClsp(clsp); + } + + public void testMerge() throws IOException, DecodeException { + initClsp(); + first(INT, INT); first(BOOLEAN, INT); reject(INT, LONG); @@ -27,21 +40,33 @@ public class TypeMergeTest extends TestCase { first(INT, NARROW); first(CHAR, INT); - merge(unknown(PrimitiveType.INT, PrimitiveType.BOOLEAN, PrimitiveType.FLOAT), + check(unknown(PrimitiveType.INT, PrimitiveType.BOOLEAN, PrimitiveType.FLOAT), unknown(PrimitiveType.INT, PrimitiveType.BOOLEAN), unknown(PrimitiveType.INT, PrimitiveType.BOOLEAN)); - merge(unknown(PrimitiveType.INT, PrimitiveType.FLOAT), + check(unknown(PrimitiveType.INT, PrimitiveType.FLOAT), unknown(PrimitiveType.INT, PrimitiveType.BOOLEAN), INT); - merge(unknown(PrimitiveType.INT, PrimitiveType.OBJECT), + check(unknown(PrimitiveType.INT, PrimitiveType.OBJECT), unknown(PrimitiveType.OBJECT, PrimitiveType.ARRAY), unknown(PrimitiveType.OBJECT)); - first(object("Lsomeobj;"), object("Lsomeobj;")); - merge(object("Lsomeobj;"), object("Lotherobj;"), null); - first(object("Lsomeobj;"), OBJECT); + ArgType objExc = object("java.lang.Exception"); + ArgType objThr = object("java.lang.Throwable"); + ArgType objIO = object("java.io.IOException"); + ArgType objArr = object("java.lang.ArrayIndexOutOfBoundsException"); + ArgType objList = object("java.util.List"); + + first(objExc, objExc); + check(objExc, objList, OBJECT); + first(objExc, OBJECT); + + check(objExc, objThr, objThr); + check(objIO, objArr, objExc); + + ArgType generic = genericType("T"); + first(generic, objExc); } private void first(ArgType t1, ArgType t2) { diff --git a/jadx-gui/build.gradle b/jadx-gui/build.gradle index 922723e43..a41e8d166 100644 --- a/jadx-gui/build.gradle +++ b/jadx-gui/build.gradle @@ -6,6 +6,7 @@ dependencies { compile(project(":jadx-core")) compile(project(":jadx-cli")) compile 'com.fifesoft:rsyntaxtextarea:2.0.7' + compile 'ch.qos.logback:logback-classic:1.0.13' } startScripts { diff --git a/jadx-gui/src/main/java/jadx/gui/utils/Utils.java b/jadx-gui/src/main/java/jadx/gui/utils/Utils.java index f31cd7399..7ea06b921 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/Utils.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/Utils.java @@ -37,6 +37,9 @@ public class Utils { } public static String typeStr(ArgType type) { + if (type == null) { + return "null"; + } if (type.isObject()) { String cls = type.getObject(); int dot = cls.lastIndexOf('.'); diff --git a/jadx-samples/src/main/java/jadx/samples/TestTryCatch.java b/jadx-samples/src/main/java/jadx/samples/TestTryCatch.java index a556181d4..e3ef44758 100644 --- a/jadx-samples/src/main/java/jadx/samples/TestTryCatch.java +++ b/jadx-samples/src/main/java/jadx/samples/TestTryCatch.java @@ -168,6 +168,20 @@ public class TestTryCatch extends AbstractTest { return b; } + public int catchInLoop(int i, int j) { + while (true) { + try { + while (i < j) + i = j++ / i; + } catch (RuntimeException e) { + i = 10; + continue; + } + break; + } + return j; + } + @Override public boolean testRun() throws Exception { Object obj = new Object(); @@ -180,8 +194,8 @@ public class TestTryCatch extends AbstractTest { assertTrue(test6(obj)); assertTrue(test7()); - assertTrue(testSynchronize(obj) == true); - assertTrue(testSynchronize("str") == false); + assertTrue(testSynchronize(obj)); + assertFalse(testSynchronize("str")); assertTrue(testSynchronize2("str")); assertTrue(testSynchronize3()); @@ -191,6 +205,10 @@ public class TestTryCatch extends AbstractTest { assertTrue(test8a("a")); assertTrue(test8a(null)); + + assertEquals(catchInLoop(1, 0), 0); + assertEquals(catchInLoop(0, 1), 2); + assertEquals(catchInLoop(788, 100), 100); return true; }