From 4c03a4245b7d6ce4a08a3802aeac2d8946187a7f Mon Sep 17 00:00:00 2001 From: Skylot Date: Sun, 8 Sep 2013 16:51:17 +0400 Subject: [PATCH] improve jadx api --- jadx-cli/src/main/java/jadx/cli/JadxCLI.java | 24 ++- .../src/main/java/jadx/cli/JadxCLIArgs.java | 20 +-- .../src/main/java/jadx/api/Decompiler.java | 144 +++++++++++------- .../main/java/jadx/api/DefaultJadxArgs.java | 29 ++++ .../src/main/java/jadx/api/IJadxArgs.java | 9 -- jadx-core/src/main/java/jadx/core/Jadx.java | 9 +- .../src/main/java/jadx/gui/JadxWrapper.java | 7 +- 7 files changed, 153 insertions(+), 89 deletions(-) create mode 100644 jadx-core/src/main/java/jadx/api/DefaultJadxArgs.java diff --git a/jadx-cli/src/main/java/jadx/cli/JadxCLI.java b/jadx-cli/src/main/java/jadx/cli/JadxCLI.java index 14bfa8009..35205e1fb 100644 --- a/jadx-cli/src/main/java/jadx/cli/JadxCLI.java +++ b/jadx-cli/src/main/java/jadx/cli/JadxCLI.java @@ -1,6 +1,7 @@ package jadx.cli; import jadx.api.Decompiler; +import jadx.core.utils.ErrorsCounter; import jadx.core.utils.exceptions.JadxException; import java.io.File; @@ -15,15 +16,30 @@ public class JadxCLI { try { JadxCLIArgs jadxArgs = new JadxCLIArgs(args); checkArgs(jadxArgs); - Decompiler jadx = new Decompiler(jadxArgs); - jadx.processAndSaveAll(); - System.exit(jadx.getErrorsCount()); - } catch (Throwable e) { + processAndSave(jadxArgs); + } catch (JadxException e) { LOG.error(e.getMessage()); System.exit(1); } } + private static void processAndSave(JadxCLIArgs jadxArgs) { + 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); + } + int errorsCount = ErrorsCounter.getErrorCount(); + if (errorsCount != 0) { + ErrorsCounter.printReport(); + } + System.exit(errorsCount); + } + private static void checkArgs(JadxCLIArgs jadxArgs) throws JadxException { if (jadxArgs.getInput().isEmpty()) throw new JadxException("Please specify input file"); diff --git a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java index 86aef002f..999826ced 100644 --- a/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java +++ b/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java @@ -141,7 +141,10 @@ public final class JadxCLIArgs implements IJadxArgs { str.append(' '); } - @Override + public List getInput() { + return input; + } + public File getOutDir() { return outputDir; } @@ -150,6 +153,10 @@ public final class JadxCLIArgs implements IJadxArgs { this.outputDir = outputDir; } + public boolean isPrintHelp() { + return printHelp; + } + @Override public int getThreadsCount() { return threadsCount; @@ -165,11 +172,6 @@ public final class JadxCLIArgs implements IJadxArgs { return rawCfgOutput; } - @Override - public List getInput() { - return input; - } - @Override public boolean isFallbackMode() { return fallbackMode; @@ -179,10 +181,4 @@ public final class JadxCLIArgs implements IJadxArgs { public boolean isVerbose() { return verbose; } - - @Override - public boolean isPrintHelp() { - return printHelp; - } - } diff --git a/jadx-core/src/main/java/jadx/api/Decompiler.java b/jadx-core/src/main/java/jadx/api/Decompiler.java index 278247895..9ccc3d3db 100644 --- a/jadx-core/src/main/java/jadx/api/Decompiler.java +++ b/jadx-core/src/main/java/jadx/api/Decompiler.java @@ -10,11 +10,13 @@ import jadx.core.dex.visitors.SaveCode; import jadx.core.utils.ErrorsCounter; import jadx.core.utils.exceptions.CodegenException; import jadx.core.utils.exceptions.DecodeException; +import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.core.utils.files.InputFile; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -28,40 +30,106 @@ import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * Jadx API usage example: + *

+ *  Decompiler jadx = new Decompiler();
+ *  jadx.loadFile(new File("classes.dex"));
+ *  jadx.setOutputDir(new File("out"));
+ *  jadx.save();
+ * 
+ *

+ * Instead of 'save()' you can get list of decompiled classes: + *


+ *  for(JavaClass cls : jadx.getClasses()) {
+ *      System.out.println(cls.getCode());
+ *  }
+ * 
+ */ public final class Decompiler { private static final Logger LOG = LoggerFactory.getLogger(Decompiler.class); private final IJadxArgs args; private final List inputFiles = new ArrayList(); + private File outDir; + private RootNode root; private List passes; - private int errorsCount; + + public Decompiler() { + this.args = new DefaultJadxArgs(); + init(); + } public Decompiler(IJadxArgs jadxArgs) { this.args = jadxArgs; - this.passes = Jadx.getPassesList(args); + init(); } - public void processAndSaveAll() { - try { - loadInput(); - parseDex(); - ExecutorService ex = saveAll(args.getOutDir()); - ex.awaitTermination(100, TimeUnit.DAYS); - LOG.info("done"); - } catch (Throwable e) { - LOG.error("jadx error:", e); - } finally { - errorsCount = ErrorsCounter.getErrorCount(); - if (errorsCount != 0) - ErrorsCounter.printReport(); + public void setOutputDir(File outDir) { + this.outDir = outDir; + init(); + } + + void init() { + if (outDir == null) { + outDir = new File("jadx-output"); } + this.passes = Jadx.getPassesList(args, outDir); } public void loadFile(File file) throws IOException, DecodeException { - setInput(file); - parseDex(); + loadFiles(Arrays.asList(file)); + } + + public void loadFiles(List files) throws IOException, DecodeException { + if (files.isEmpty()) { + throw new JadxRuntimeException("Empty file list"); + } + inputFiles.clear(); + for (File file : files) { + inputFiles.add(new InputFile(file)); + } + parse(); + } + + public void save() { + try { + ExecutorService ex = getSaveExecutor(); + ex.awaitTermination(1, TimeUnit.DAYS); + } catch (InterruptedException e) { + LOG.error("Save interrupted", e); + } + } + + public ThreadPoolExecutor getSaveExecutor() { + if (root == null) { + throw new JadxRuntimeException("No loaded files"); + } + int threadsCount = args.getThreadsCount(); + LOG.debug("processing threads count: {}", threadsCount); + + ArrayList passList = new ArrayList(passes); + SaveCode savePass = new SaveCode(outDir, args); + passList.add(savePass); + + LOG.info("processing ..."); + ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threadsCount); + for (ClassNode cls : root.getClasses(false)) { + if (cls.getCode() == null) { + ProcessClass job = new ProcessClass(cls, passList); + executor.execute(job); + } else { + try { + savePass.visit(cls); + } catch (CodegenException e) { + LOG.error("Can't save class {}", cls, e); + } + } + } + executor.shutdown(); + return executor; } public List getClasses() { @@ -102,48 +170,10 @@ public final class Decompiler { } public int getErrorsCount() { - return errorsCount; + return ErrorsCounter.getErrorCount(); } - public ThreadPoolExecutor saveAll(File dir) { - int threadsCount = args.getThreadsCount(); - LOG.debug("processing threads count: {}", threadsCount); - - ArrayList passList = new ArrayList(passes); - SaveCode savePass = new SaveCode(dir, args); - passList.add(savePass); - - LOG.info("processing ..."); - ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threadsCount); - for (ClassNode cls : root.getClasses(false)) { - if (cls.getCode() == null) { - ProcessClass job = new ProcessClass(cls, passList); - executor.execute(job); - } else { - try { - savePass.visit(cls); - } catch (CodegenException e) { - LOG.error("Can't save class {}", cls, e); - } - } - } - executor.shutdown(); - return executor; - } - - private void loadInput() throws IOException, DecodeException { - inputFiles.clear(); - for (File file : args.getInput()) { - inputFiles.add(new InputFile(file)); - } - } - - private void setInput(File file) throws IOException, DecodeException { - inputFiles.clear(); - inputFiles.add(new InputFile(file)); - } - - private void parseDex() throws DecodeException { + void parse() throws DecodeException { ClassInfo.clearCache(); ErrorsCounter.reset(); diff --git a/jadx-core/src/main/java/jadx/api/DefaultJadxArgs.java b/jadx-core/src/main/java/jadx/api/DefaultJadxArgs.java new file mode 100644 index 000000000..65b8b8ef8 --- /dev/null +++ b/jadx-core/src/main/java/jadx/api/DefaultJadxArgs.java @@ -0,0 +1,29 @@ +package jadx.api; + +public class DefaultJadxArgs implements IJadxArgs { + + @Override + public int getThreadsCount() { + return Runtime.getRuntime().availableProcessors(); + } + + @Override + public boolean isCFGOutput() { + return false; + } + + @Override + public boolean isRawCFGOutput() { + return false; + } + + @Override + public boolean isFallbackMode() { + return false; + } + + @Override + public boolean isVerbose() { + return false; + } +} diff --git a/jadx-core/src/main/java/jadx/api/IJadxArgs.java b/jadx-core/src/main/java/jadx/api/IJadxArgs.java index a21f38a0c..ce2c3f2a2 100644 --- a/jadx-core/src/main/java/jadx/api/IJadxArgs.java +++ b/jadx-core/src/main/java/jadx/api/IJadxArgs.java @@ -1,13 +1,6 @@ package jadx.api; -import java.io.File; -import java.util.List; - public interface IJadxArgs { - List getInput(); - - File getOutDir(); - int getThreadsCount(); boolean isCFGOutput(); @@ -17,6 +10,4 @@ public interface IJadxArgs { boolean isFallbackMode(); boolean isVerbose(); - - boolean isPrintHelp(); } diff --git a/jadx-core/src/main/java/jadx/core/Jadx.java b/jadx-core/src/main/java/jadx/core/Jadx.java index 27ead0533..4866aea6b 100644 --- a/jadx-core/src/main/java/jadx/core/Jadx.java +++ b/jadx-core/src/main/java/jadx/core/Jadx.java @@ -20,6 +20,7 @@ import jadx.core.dex.visitors.regions.RegionMakerVisitor; import jadx.core.dex.visitors.typeresolver.FinishTypeResolver; import jadx.core.dex.visitors.typeresolver.TypeResolver; +import java.io.File; import java.util.ArrayList; import java.util.List; @@ -36,7 +37,7 @@ public class Jadx { LOG.info("assertions enabled"); } - public static List getPassesList(IJadxArgs args) { + public static List getPassesList(IJadxArgs args, File outDir) { List passes = new ArrayList(); if (args.isFallbackMode()) { passes.add(new FallbackModeVisitor()); @@ -48,13 +49,13 @@ public class Jadx { passes.add(new FinishTypeResolver()); if (args.isRawCFGOutput()) - passes.add(new DotGraphVisitor(args.getOutDir(), false, true)); + passes.add(new DotGraphVisitor(outDir, false, true)); passes.add(new ModVisitor()); passes.add(new EnumVisitor()); if (args.isCFGOutput()) - passes.add(new DotGraphVisitor(args.getOutDir(), false)); + passes.add(new DotGraphVisitor(outDir, false)); passes.add(new RegionMakerVisitor()); passes.add(new PostRegionVisitor()); @@ -63,7 +64,7 @@ public class Jadx { passes.add(new ProcessVariables()); passes.add(new CheckRegions()); if (args.isCFGOutput()) - passes.add(new DotGraphVisitor(args.getOutDir(), true)); + passes.add(new DotGraphVisitor(outDir, true)); passes.add(new MethodInlinerVisitor()); passes.add(new ClassModifier()); diff --git a/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java b/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java index 2361e42a8..a1b5ba620 100644 --- a/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java +++ b/jadx-gui/src/main/java/jadx/gui/JadxWrapper.java @@ -30,9 +30,9 @@ public class JadxWrapper { try { this.decompiler.loadFile(file); } catch (IOException e) { - e.printStackTrace(); + LOG.error("Error open file: " + file, e); } catch (DecodeException e) { - e.printStackTrace(); + LOG.error("Error decode file: " + file, e); } } @@ -41,7 +41,8 @@ public class JadxWrapper { @Override public void run() { try { - ThreadPoolExecutor ex = decompiler.saveAll(dir); + decompiler.setOutputDir(dir); + ThreadPoolExecutor ex = decompiler.getSaveExecutor(); while (ex.isTerminating()) { long total = ex.getTaskCount(); long done = ex.getCompletedTaskCount();