improve jadx api
This commit is contained in:
@@ -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");
|
||||
|
||||
@@ -141,7 +141,10 @@ public final class JadxCLIArgs implements IJadxArgs {
|
||||
str.append(' ');
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<File> 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<File> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
* <pre><code>
|
||||
* Decompiler jadx = new Decompiler();
|
||||
* jadx.loadFile(new File("classes.dex"));
|
||||
* jadx.setOutputDir(new File("out"));
|
||||
* jadx.save();
|
||||
* </code></pre>
|
||||
* <p/>
|
||||
* Instead of 'save()' you can get list of decompiled classes:
|
||||
* <pre><code>
|
||||
* for(JavaClass cls : jadx.getClasses()) {
|
||||
* System.out.println(cls.getCode());
|
||||
* }
|
||||
* </code></pre>
|
||||
*/
|
||||
public final class Decompiler {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Decompiler.class);
|
||||
|
||||
private final IJadxArgs args;
|
||||
private final List<InputFile> inputFiles = new ArrayList<InputFile>();
|
||||
|
||||
private File outDir;
|
||||
|
||||
private RootNode root;
|
||||
private List<IDexTreeVisitor> 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<File> 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<IDexTreeVisitor> passList = new ArrayList<IDexTreeVisitor>(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<JavaClass> 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<IDexTreeVisitor> passList = new ArrayList<IDexTreeVisitor>(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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,6 @@
|
||||
package jadx.api;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
public interface IJadxArgs {
|
||||
List<File> getInput();
|
||||
|
||||
File getOutDir();
|
||||
|
||||
int getThreadsCount();
|
||||
|
||||
boolean isCFGOutput();
|
||||
@@ -17,6 +10,4 @@ public interface IJadxArgs {
|
||||
boolean isFallbackMode();
|
||||
|
||||
boolean isVerbose();
|
||||
|
||||
boolean isPrintHelp();
|
||||
}
|
||||
|
||||
@@ -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<IDexTreeVisitor> getPassesList(IJadxArgs args) {
|
||||
public static List<IDexTreeVisitor> getPassesList(IJadxArgs args, File outDir) {
|
||||
List<IDexTreeVisitor> passes = new ArrayList<IDexTreeVisitor>();
|
||||
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());
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user