From 42eb3197518e4bccbc74ac8c30c52b1fa6670ae9 Mon Sep 17 00:00:00 2001 From: Skylot Date: Sat, 10 May 2014 14:44:49 +0400 Subject: [PATCH] fix issues reported by Coverity --- README.md | 9 +- build.gradle | 2 +- jadx-cli/src/main/java/jadx/cli/JadxCLI.java | 29 ++-- .../src/main/java/jadx/cli/JadxCLIArgs.java | 17 ++- jadx-core/src/main/java/jadx/core/Jadx.java | 18 ++- .../src/main/java/jadx/core/clsp/ClsSet.java | 144 ++++++++++-------- .../java/jadx/core/clsp/ConvertToClsSet.java | 3 + .../main/java/jadx/core/codegen/InsnGen.java | 10 +- .../java/jadx/core/codegen/MethodGen.java | 1 + .../jadx/core/dex/instructions/ArithNode.java | 17 +++ .../core/dex/instructions/ConstClassNode.java | 19 ++- .../dex/instructions/ConstStringNode.java | 19 ++- .../core/dex/instructions/FillArrayNode.java | 19 ++- .../jadx/core/dex/instructions/GotoNode.java | 17 +++ .../jadx/core/dex/instructions/IfNode.java | 17 +++ .../core/dex/instructions/IndexInsnNode.java | 17 +++ .../core/dex/instructions/InvokeNode.java | 20 +++ .../core/dex/instructions/SwitchNode.java | 23 +++ .../core/dex/instructions/args/FieldArg.java | 26 ++++ .../dex/instructions/args/RegisterArg.java | 3 + .../instructions/mods/ConstructorInsn.java | 23 +++ .../dex/instructions/mods/TernaryInsn.java | 17 +++ .../java/jadx/core/dex/nodes/BlockNode.java | 14 +- .../jadx/core/dex/nodes/parser/LocalVar.java | 10 ++ .../dex/nodes/parser/SignatureParser.java | 9 ++ .../jadx/core/dex/visitors/ClassModifier.java | 5 +- .../src/main/java/jadx/core/utils/Utils.java | 2 +- .../java/jadx/core/utils/files/InputFile.java | 13 +- .../java/jadx/core/utils/files/JavaToDex.java | 20 ++- .../jadx/tests/TestSignatureParser.groovy | 9 +- jadx-gui/src/main/java/jadx/gui/JadxGUI.java | 7 +- .../src/main/java/jadx/gui/ui/MainWindow.java | 7 +- .../src/main/java/jadx/gui/ui/SearchBar.java | 14 +- 33 files changed, 455 insertions(+), 125 deletions(-) diff --git a/README.md b/README.md index 8b2bdd52..96188241 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ -## JADX +## JADX [![Build Status](https://travis-ci.org/skylot/jadx.png?branch=master)](https://travis-ci.org/skylot/jadx) [![Build Status](https://drone.io/github.com/skylot/jadx/status.png)](https://drone.io/github.com/skylot/jadx/latest) [![Coverage Status](https://coveralls.io/repos/skylot/jadx/badge.png)](https://coveralls.io/r/skylot/jadx) +[![Coverity Scan Build Status](https://scan.coverity.com/projects/2166/badge.svg)](https://scan.coverity.com/projects/2166) **jadx** - Dex to Java decompiler @@ -12,14 +13,14 @@ Command line and GUI tools for produce Java source code from Android Dex and Apk ### Downloads - [unstable](https://drone.io/github.com/skylot/jadx/files) - from [github](https://github.com/skylot/jadx/releases) -- from [sourceforge](http://sourceforge.net/projects/jadx/files/) +- from [sourceforge](http://sourceforge.net/projects/jadx/files/) -### Building from source +### Building from source git clone https://github.com/skylot/jadx.git cd jadx ./gradlew dist - + (on Windows, use `gradlew.bat` instead of `./gradlew`) Scripts for run jadx will be placed in `build/jadx/bin` diff --git a/build.gradle b/build.gradle index b864c6ee..66ff65be 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ subprojects { gradle.projectsEvaluated { tasks.withType(Compile) { - if (!"${it}".contains(':jadx-samples:')) { + if (!"$it".contains(':jadx-samples:')) { options.compilerArgs << '-Xlint' << '-Xlint:unchecked' << '-Xlint:deprecation' } } diff --git a/jadx-cli/src/main/java/jadx/cli/JadxCLI.java b/jadx-cli/src/main/java/jadx/cli/JadxCLI.java index 72b13ee0..282c35cb 100644 --- a/jadx-cli/src/main/java/jadx/cli/JadxCLI.java +++ b/jadx-cli/src/main/java/jadx/cli/JadxCLI.java @@ -14,37 +14,41 @@ public class JadxCLI { public static void main(String[] args) { try { - JadxCLIArgs jadxArgs = new JadxCLIArgs(args); - checkArgs(jadxArgs); - processAndSave(jadxArgs); - } catch (Exception e) { + JadxCLIArgs jadxArgs = new JadxCLIArgs(); + if (processArgs(jadxArgs, args)) { + processAndSave(jadxArgs); + } + } catch (JadxException e) { LOG.error(e.getMessage()); System.exit(1); } } - private static void processAndSave(JadxCLIArgs jadxArgs) { + static void processAndSave(JadxCLIArgs jadxArgs) throws JadxException { try { Decompiler jadx = new Decompiler(jadxArgs); jadx.loadFiles(jadxArgs.getInput()); jadx.setOutputDir(jadxArgs.getOutDir()); jadx.save(); - LOG.info("done"); } catch (Throwable e) { - LOG.error("jadx error:", e); + throw new JadxException("jadx error: " + e.getMessage(), e); } - int errorsCount = ErrorsCounter.getErrorCount(); - if (errorsCount != 0) { + if (ErrorsCounter.getErrorCount() != 0) { ErrorsCounter.printReport(); + throw new JadxException("finished with errors"); } - System.exit(errorsCount); + LOG.info("done"); + } - private static void checkArgs(JadxCLIArgs jadxArgs) throws JadxException { + static boolean processArgs(JadxCLIArgs jadxArgs, String[] args) throws JadxException { + if (!jadxArgs.processArgs(args)) { + return false; + } if (jadxArgs.getInput().isEmpty()) { LOG.error("Please specify input file"); jadxArgs.printUsage(); - System.exit(1); + return false; } File outputDir = jadxArgs.getOutDir(); if (outputDir == null) { @@ -64,5 +68,6 @@ public class JadxCLI { if (outputDir.exists() && !outputDir.isDirectory()) { throw new JadxException("Output directory exists as file " + outputDir); } + return true; } } diff --git a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java index 1f5bafa6..d8721b7e 100644 --- a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java +++ b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java @@ -47,25 +47,25 @@ public final class JadxCLIArgs implements IJadxArgs { private final List input = new ArrayList(1); private File outputDir; - public JadxCLIArgs(String[] args) { - parse(args); - processArgs(); + public boolean processArgs(String[] args) { + return parse(args) && process(); } - private void parse(String[] args) { + private boolean parse(String[] args) { try { new JCommander(this, args); + return true; } catch (ParameterException e) { System.err.println("Arguments parse error: " + e.getMessage()); printUsage(); - System.exit(1); + return false; } } - public void processArgs() { + private boolean process() { if (isPrintHelp()) { printUsage(); - System.exit(0); + return false; } try { if (threadsCount <= 0) { @@ -95,8 +95,9 @@ public final class JadxCLIArgs implements IJadxArgs { } catch (JadxException e) { System.err.println("ERROR: " + e.getMessage()); printUsage(); - System.exit(1); + return false; } + return true; } public void printUsage() { diff --git a/jadx-core/src/main/java/jadx/core/Jadx.java b/jadx-core/src/main/java/jadx/core/Jadx.java index 6b63f498..912067d8 100644 --- a/jadx-core/src/main/java/jadx/core/Jadx.java +++ b/jadx-core/src/main/java/jadx/core/Jadx.java @@ -25,7 +25,6 @@ import jadx.core.dex.visitors.typeinference.TypeInference; import jadx.core.utils.Utils; import java.io.File; -import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; @@ -95,15 +94,18 @@ public class Jadx { public static String getVersion() { try { - Enumeration resources = Utils.class.getClassLoader().getResources("META-INF/MANIFEST.MF"); - while (resources.hasMoreElements()) { - Manifest manifest = new Manifest(resources.nextElement().openStream()); - String ver = manifest.getMainAttributes().getValue("jadx-version"); - if (ver != null) { - return ver; + ClassLoader classLoader = Utils.class.getClassLoader(); + if (classLoader != null) { + Enumeration resources = classLoader.getResources("META-INF/MANIFEST.MF"); + while (resources.hasMoreElements()) { + Manifest manifest = new Manifest(resources.nextElement().openStream()); + String ver = manifest.getMainAttributes().getValue("jadx-version"); + if (ver != null) { + return ver; + } } } - } catch (IOException e) { + } catch (Exception e) { LOG.error("Can't get manifest file", e); } return "dev"; diff --git a/jadx-core/src/main/java/jadx/core/clsp/ClsSet.java b/jadx-core/src/main/java/jadx/core/clsp/ClsSet.java index d3967250..9043df9e 100644 --- a/jadx-core/src/main/java/jadx/core/clsp/ClsSet.java +++ b/jadx-core/src/main/java/jadx/core/clsp/ClsSet.java @@ -65,6 +65,9 @@ public class ClsSet { for (ClassNode cls : list) { if (cls.getAccessFlags().isPublic()) { NClass nClass = getCls(cls.getRawName(), names); + if (nClass == null) { + throw new JadxRuntimeException("Missing class: " + cls); + } nClass.setParents(makeParentsArray(cls, names)); classes[k] = nClass; k++; @@ -102,40 +105,47 @@ public class ClsSet { 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(); + try { + 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(); + } + } else { + throw new JadxRuntimeException("Unknown file format: " + outputName); } - } else { - throw new JadxRuntimeException("Unknown file format: " + outputName); + } finally { + outputStream.close(); } } public void save(OutputStream output) throws IOException { DataOutputStream out = new DataOutputStream(output); - out.writeBytes(JADX_CLS_SET_HEADER); - out.writeByte(VERSION); + try { + 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()); + 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()); + } + } + } finally { + out.close(); } } @@ -144,55 +154,67 @@ public class ClsSet { if (input == null) { throw new JadxRuntimeException("Can't load classpath file: " + CLST_FILENAME); } - load(input); + try { + load(input); + } finally { + input.close(); + } } 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); + try { + 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(); } - entry = in.getNextEntry(); + } finally { + in.close(); } - } finally { - in.close(); + } else { + throw new JadxRuntimeException("Unknown file format: " + name); } - } else { - throw new JadxRuntimeException("Unknown file format: " + name); + } finally { + inputStream.close(); } } public void load(InputStream input) throws IOException, DecodeException { DataInputStream in = new DataInputStream(input); - byte[] header = new byte[JADX_CLS_SET_HEADER.length()]; - int readHeaderLength = in.read(header); - int version = in.readByte(); - if (readHeaderLength != JADX_CLS_SET_HEADER.length() - || !JADX_CLS_SET_HEADER.equals(new String(header, STRING_CHARSET)) - || 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()]; + try { + byte[] header = new byte[JADX_CLS_SET_HEADER.length()]; + int readHeaderLength = in.read(header); + int version = in.readByte(); + if (readHeaderLength != JADX_CLS_SET_HEADER.length() + || !JADX_CLS_SET_HEADER.equals(new String(header, STRING_CHARSET)) + || version != VERSION) { + throw new DecodeException("Wrong jadx class set header"); } - classes[i].setParents(parents); + 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); + } + } finally { + in.close(); } } diff --git a/jadx-core/src/main/java/jadx/core/clsp/ConvertToClsSet.java b/jadx-core/src/main/java/jadx/core/clsp/ConvertToClsSet.java index 9ea88a36..020f69a9 100644 --- a/jadx-core/src/main/java/jadx/core/clsp/ConvertToClsSet.java +++ b/jadx-core/src/main/java/jadx/core/clsp/ConvertToClsSet.java @@ -54,6 +54,9 @@ public class ConvertToClsSet { private static void addFilesFromDirectory(File dir, List inputFiles) throws IOException, DecodeException { File[] files = dir.listFiles(); + if (files == null) { + return; + } for (File file : files) { if (file.isDirectory()) { addFilesFromDirectory(file, inputFiles); diff --git a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java index 2e6df4a4..e707abc0 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -555,10 +555,12 @@ public class InsnGen { } cls.getAttributes().add(AttributeFlag.DONT_GENERATE); MethodNode defCtr = cls.getDefaultConstructor(); - if (RegionUtils.notEmpty(defCtr.getRegion())) { - defCtr.getAttributes().add(AttributeFlag.ANONYMOUS_CONSTRUCTOR); - } else { - defCtr.getAttributes().add(AttributeFlag.DONT_GENERATE); + if (defCtr != null) { + if (RegionUtils.notEmpty(defCtr.getRegion())) { + defCtr.getAttributes().add(AttributeFlag.ANONYMOUS_CONSTRUCTOR); + } else { + defCtr.getAttributes().add(AttributeFlag.DONT_GENERATE); + } } code.add("new ").add(parent == null ? "Object" : useClass(parent)).add("() "); new ClassGen(cls, mgen.getClassGen().getParentGen(), fallback).addClassBody(code); 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 1b318ed0..76fccdc1 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java @@ -312,6 +312,7 @@ public class MethodGen { } } } catch (CodegenException e) { + LOG.debug("Error generate fallback instruction: ", e.getCause()); code.startLine("// error: " + insn); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/ArithNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/ArithNode.java index 2a7bb2fb..2dfcaef7 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/ArithNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/ArithNode.java @@ -61,6 +61,23 @@ public class ArithNode extends InsnNode { return op; } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof ArithNode) || !super.equals(obj)) { + return false; + } + ArithNode that = (ArithNode) obj; + return op == that.op; + } + + @Override + public int hashCode() { + return 31 * super.hashCode() + op.hashCode(); + } + @Override public String toString() { return InsnUtils.formatOffset(offset) + ": " diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/ConstClassNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/ConstClassNode.java index 0a2cfb29..e5fa45f3 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/ConstClassNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/ConstClassNode.java @@ -3,7 +3,7 @@ package jadx.core.dex.instructions; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.nodes.InsnNode; -public class ConstClassNode extends InsnNode { +public final class ConstClassNode extends InsnNode { private final ArgType clsType; @@ -16,6 +16,23 @@ public class ConstClassNode extends InsnNode { return clsType; } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof ConstClassNode) || !super.equals(obj)) { + return false; + } + ConstClassNode that = (ConstClassNode) obj; + return clsType.equals(that.clsType); + } + + @Override + public int hashCode() { + return 31 * super.hashCode() + clsType.hashCode(); + } + @Override public String toString() { return super.toString() + " " + clsType; diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/ConstStringNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/ConstStringNode.java index 0c191017..222f14ba 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/ConstStringNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/ConstStringNode.java @@ -2,7 +2,7 @@ package jadx.core.dex.instructions; import jadx.core.dex.nodes.InsnNode; -public class ConstStringNode extends InsnNode { +public final class ConstStringNode extends InsnNode { private final String str; @@ -15,6 +15,23 @@ public class ConstStringNode extends InsnNode { return str; } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof ConstStringNode) || !super.equals(obj)) { + return false; + } + ConstStringNode that = (ConstStringNode) obj; + return str.equals(that.str); + } + + @Override + public int hashCode() { + return 31 * super.hashCode() + str.hashCode(); + } + @Override public String toString() { return super.toString() + " \"" + str + "\""; diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/FillArrayNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/FillArrayNode.java index 9a8e1be7..d6a8b6ed 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/FillArrayNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/FillArrayNode.java @@ -8,7 +8,7 @@ import jadx.core.utils.exceptions.JadxRuntimeException; import com.android.dx.io.instructions.FillArrayDataPayloadDecodedInstruction; -public class FillArrayNode extends InsnNode { +public final class FillArrayNode extends InsnNode { private final Object data; private ArgType elemType; @@ -53,4 +53,21 @@ public class FillArrayNode extends InsnNode { elemType = r; } } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof FillArrayNode) || !super.equals(obj)) { + return false; + } + FillArrayNode that = (FillArrayNode) obj; + return elemType.equals(that.elemType) && data == that.data; + } + + @Override + public int hashCode() { + return 31 * super.hashCode() + elemType.hashCode() + data.hashCode(); + } } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/GotoNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/GotoNode.java index be6f6316..8caf2ee2 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/GotoNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/GotoNode.java @@ -20,6 +20,23 @@ public class GotoNode extends InsnNode { return target; } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof GotoNode) || !super.equals(obj)) { + return false; + } + GotoNode gotoNode = (GotoNode) obj; + return target == gotoNode.target; + } + + @Override + public int hashCode() { + return 31 * super.hashCode() + target; + } + @Override public String toString() { return super.toString() + "-> " + InsnUtils.formatOffset(target); diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/IfNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/IfNode.java index 602baa2d..98d8538f 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/IfNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/IfNode.java @@ -70,6 +70,23 @@ public class IfNode extends GotoNode { return elseBlock; } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof IfNode) || !super.equals(obj)) { + return false; + } + IfNode ifNode = (IfNode) obj; + return op == ifNode.op; + } + + @Override + public int hashCode() { + return 31 * super.hashCode() + op.hashCode(); + } + @Override public String toString() { return InsnUtils.formatOffset(offset) + ": " diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/IndexInsnNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/IndexInsnNode.java index a6a3f74c..f897001c 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/IndexInsnNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/IndexInsnNode.java @@ -16,6 +16,23 @@ public class IndexInsnNode extends InsnNode { return index; } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof IndexInsnNode) || !super.equals(obj)) { + return false; + } + IndexInsnNode that = (IndexInsnNode) obj; + return index == null ? that.index == null : index.equals(that.index); + } + + @Override + public int hashCode() { + return 31 * super.hashCode() + (index != null ? index.hashCode() : 0); + } + @Override public String toString() { return super.toString() + " " + InsnUtils.indexToString(index); diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeNode.java index e653d7d1..07f30ca8 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/InvokeNode.java @@ -44,6 +44,26 @@ public class InvokeNode extends InsnNode { return mth; } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof InvokeNode) || !super.equals(obj)) { + return false; + } + InvokeNode that = (InvokeNode) obj; + return type == that.type && mth.equals(that.mth); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + type.hashCode(); + result = 31 * result + mth.hashCode(); + return result; + } + @Override public String toString() { return InsnUtils.formatOffset(offset) + ": " diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/SwitchNode.java b/jadx-core/src/main/java/jadx/core/dex/instructions/SwitchNode.java index 3afbc794..20c4834e 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/SwitchNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/SwitchNode.java @@ -36,6 +36,29 @@ public class SwitchNode extends InsnNode { return def; } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof SwitchNode) || !super.equals(obj)) { + return false; + } + SwitchNode that = (SwitchNode) obj; + return def == that.def + && Arrays.equals(keys, that.keys) + && Arrays.equals(targets, that.targets); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + Arrays.hashCode(keys); + result = 31 * result + Arrays.hashCode(targets); + result = 31 * result + def; + return result; + } + @Override public String toString() { StringBuilder targ = new StringBuilder(); diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/FieldArg.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/FieldArg.java index e1f7bfa8..3ca5a099 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/FieldArg.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/FieldArg.java @@ -42,6 +42,32 @@ public final class FieldArg extends RegisterArg { this.type = type; } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof FieldArg) || !super.equals(obj)) { + return false; + } + FieldArg fieldArg = (FieldArg) obj; + if (!field.equals(fieldArg.field)) { + return false; + } + if (regArg != null ? !regArg.equals(fieldArg.regArg) : fieldArg.regArg != null) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + field.hashCode(); + result = 31 * result + (regArg != null ? regArg.hashCode() : 0); + return result; + } + @Override public String toString() { return "(" + field + ")"; diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java index 218bd917..939a966f 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/RegisterArg.java @@ -126,6 +126,9 @@ public class RegisterArg extends InsnArg implements Named { } public InsnNode getAssignInsn() { + if (sVar == null) { + return null; + } RegisterArg assign = sVar.getAssign(); if (assign != null) { return assign.getParentInsn(); diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java b/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java index ea4a5f16..31f60f98 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/mods/ConstructorInsn.java @@ -75,6 +75,29 @@ public class ConstructorInsn extends InsnNode { return callType == CallType.SELF; } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ConstructorInsn) || !super.equals(o)) { + return false; + } + ConstructorInsn that = (ConstructorInsn) o; + return callMth.equals(that.callMth) + && callType == that.callType + && instanceArg.equals(that.instanceArg); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + callMth.hashCode(); + result = 31 * result + callType.hashCode(); + result = 31 * result + instanceArg.hashCode(); + return result; + } + @Override public String toString() { return super.toString() + " " + callMth + " " + callType; diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/mods/TernaryInsn.java b/jadx-core/src/main/java/jadx/core/dex/instructions/mods/TernaryInsn.java index f408f800..69570e5d 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/mods/TernaryInsn.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/mods/TernaryInsn.java @@ -33,6 +33,23 @@ public class TernaryInsn extends InsnNode { return condition; } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof TernaryInsn) || !super.equals(obj)) { + return false; + } + TernaryInsn that = (TernaryInsn) obj; + return condition.equals(that.condition); + } + + @Override + public int hashCode() { + return 31 * super.hashCode() + condition.hashCode(); + } + @Override public String toString() { return InsnUtils.formatOffset(offset) + ": TERNARY" diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/BlockNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/BlockNode.java index 085ed817..c6cd9b67 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/BlockNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/BlockNode.java @@ -61,9 +61,17 @@ public class BlockNode extends AttrNode implements IBlock { } public void lock() { - cleanSuccessors = Collections.unmodifiableList(cleanSuccessors); - successors = Collections.unmodifiableList(successors); - predecessors = Collections.unmodifiableList(predecessors); + cleanSuccessors = lockList(cleanSuccessors); + successors = lockList(successors); + predecessors = lockList(predecessors); + dominatesOn = lockList(dominatesOn); + } + + List lockList(List list) { + if (list.isEmpty()) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(list); } /** diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/parser/LocalVar.java b/jadx-core/src/main/java/jadx/core/dex/nodes/parser/LocalVar.java index d8d4c611..6a03fc64 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/parser/LocalVar.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/parser/LocalVar.java @@ -85,6 +85,16 @@ final class LocalVar extends RegisterArg { return endAddr; } + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + + @Override + public int hashCode() { + return super.hashCode(); + } + @Override public String toString() { return super.toString() + " " + (isEnd diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/parser/SignatureParser.java b/jadx-core/src/main/java/jadx/core/dex/nodes/parser/SignatureParser.java index 2305d237..4df105ce 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/parser/SignatureParser.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/parser/SignatureParser.java @@ -13,7 +13,12 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class SignatureParser { + + private static final Logger LOG = LoggerFactory.getLogger(SignatureParser.class); private static final char STOP_CHAR = 0; private final String sign; @@ -217,6 +222,10 @@ public class SignatureParser { break; } String id = consumeUntil(':'); + if (id == null) { + LOG.error("Can't parse generic map: {}", sign); + return Collections.emptyMap(); + } tryConsume(':'); List types = consumeExtendsTypesList(); map.put(ArgType.genericType(id), types); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java b/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java index 3e3246a8..c09217e4 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/ClassModifier.java @@ -73,7 +73,7 @@ public class ClassModifier extends AbstractVisitor { } private static boolean removeFieldUsageFromConstructor(MethodNode mth, FieldNode field, ClassNode fieldsCls) { - if (!mth.getAccessFlags().isConstructor()) { + if (mth.isNoCode() || !mth.getAccessFlags().isConstructor()) { return false; } List args = mth.getArguments(false); @@ -114,6 +114,9 @@ public class ClassModifier extends AbstractVisitor { private static void removeSyntheticMethods(ClassNode cls) { for (MethodNode mth : cls.getMethods()) { + if (mth.isNoCode()) { + continue; + } AccessInfo af = mth.getAccessFlags(); // remove bridge methods if (af.isBridge() && af.isSynthetic() && !isMethodUniq(cls, mth)) { 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 ae36280d..9e69a803 100644 --- a/jadx-core/src/main/java/jadx/core/utils/Utils.java +++ b/jadx-core/src/main/java/jadx/core/utils/Utils.java @@ -97,7 +97,7 @@ public class Utils { public static void makeDirsForFile(File file) { File dir = file.getParentFile(); - if (!dir.exists()) { + if (dir != null && !dir.exists()) { // if directory already created in other thread mkdirs will return false, // so check dir existence again if (!dir.mkdirs() && !dir.exists()) { diff --git a/jadx-core/src/main/java/jadx/core/utils/files/InputFile.java b/jadx-core/src/main/java/jadx/core/utils/files/InputFile.java index 16c43b1c..74531abb 100644 --- a/jadx-core/src/main/java/jadx/core/utils/files/InputFile.java +++ b/jadx-core/src/main/java/jadx/core/utils/files/InputFile.java @@ -2,6 +2,7 @@ package jadx.core.utils.files; import jadx.core.utils.exceptions.DecodeException; import jadx.core.utils.exceptions.JadxException; +import jadx.core.utils.exceptions.JadxRuntimeException; import java.io.ByteArrayOutputStream; import java.io.File; @@ -56,17 +57,23 @@ public class InputFile { private byte[] openDexFromApk(File file) throws IOException { ZipFile zf = new ZipFile(file); ZipEntry dex = zf.getEntry("classes.dex"); + if (dex == null) { + zf.close(); + throw new JadxRuntimeException("File 'classes.dex' not found in apk file: " + file); + } ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); + InputStream in = null; try { - InputStream in = zf.getInputStream(dex); - + in = zf.getInputStream(dex); byte[] buffer = new byte[8192]; int count; while ((count = in.read(buffer)) != -1) { bytesOut.write(buffer, 0, count); } - in.close(); } finally { + if (in != null) { + in.close(); + } zf.close(); } return bytesOut.toByteArray(); diff --git a/jadx-core/src/main/java/jadx/core/utils/files/JavaToDex.java b/jadx-core/src/main/java/jadx/core/utils/files/JavaToDex.java index 6d7d9301..8bc1f09d 100644 --- a/jadx-core/src/main/java/jadx/core/utils/files/JavaToDex.java +++ b/jadx-core/src/main/java/jadx/core/utils/files/JavaToDex.java @@ -4,6 +4,7 @@ import jadx.core.utils.exceptions.JadxException; import java.io.ByteArrayOutputStream; import java.io.PrintStream; +import java.io.UnsupportedEncodingException; import com.android.dx.command.DxConsole; import com.android.dx.command.dexer.Main; @@ -11,6 +12,8 @@ import com.android.dx.command.dexer.Main.Arguments; public class JavaToDex { + private static final String CHARSET_NAME = "UTF-8"; + public static class DxArgs extends Arguments { public DxArgs(String dexFile, String[] input) { outName = dexFile; @@ -27,12 +30,15 @@ public class JavaToDex { public byte[] convert(String javaFile) throws JadxException { ByteArrayOutputStream errOut = new ByteArrayOutputStream(); - DxConsole.err = new PrintStream(errOut); - + try { + DxConsole.err = new PrintStream(errOut, true, CHARSET_NAME); + } catch (UnsupportedEncodingException e) { + throw new JadxException(e.getMessage(), e); + } PrintStream oldOut = System.out; ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { - System.setOut(new PrintStream(baos)); + System.setOut(new PrintStream(baos, true, CHARSET_NAME)); DxArgs args = new DxArgs("-", new String[]{javaFile}); Main.run(args); baos.close(); @@ -41,8 +47,12 @@ public class JavaToDex { } finally { System.setOut(oldOut); } - // errOut also contains warnings - dxErrors = errOut.toString(); + try { + // errOut also contains warnings + dxErrors = errOut.toString(CHARSET_NAME); + } catch (UnsupportedEncodingException e) { + throw new JadxException("Can't save error output", e); + } return baos.toByteArray(); } diff --git a/jadx-core/src/test/groovy/jadx/tests/TestSignatureParser.groovy b/jadx-core/src/test/groovy/jadx/tests/TestSignatureParser.groovy index db3b6b52..9e67b492 100644 --- a/jadx-core/src/test/groovy/jadx/tests/TestSignatureParser.groovy +++ b/jadx-core/src/test/groovy/jadx/tests/TestSignatureParser.groovy @@ -1,5 +1,4 @@ package jadx.tests - import jadx.core.dex.instructions.args.ArgType import jadx.core.dex.nodes.parser.SignatureParser import spock.lang.Specification @@ -84,4 +83,12 @@ class TestSignatureParser extends Specification { argTypes.size() == 1 argTypes.get(0) == generic("Ljava/util/List;", wildcard()) } + + def "generic map: bad signature"() { + when: + def map = new SignatureParser("