core: change jadx args api for easier processing and validation
This commit is contained in:
@@ -1,12 +1,9 @@
|
||||
package jadx.cli;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
|
||||
public class JadxCLI {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JadxCLI.class);
|
||||
@@ -14,7 +11,7 @@ public class JadxCLI {
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
JadxCLIArgs jadxArgs = new JadxCLIArgs();
|
||||
if (processArgs(jadxArgs, args)) {
|
||||
if (jadxArgs.processArgs(args)) {
|
||||
processAndSave(jadxArgs);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -23,10 +20,9 @@ public class JadxCLI {
|
||||
}
|
||||
}
|
||||
|
||||
static void processAndSave(JadxCLIArgs jadxArgs) throws JadxException {
|
||||
JadxDecompiler jadx = new JadxDecompiler(jadxArgs);
|
||||
jadx.setOutputDir(jadxArgs.getOutDir());
|
||||
jadx.loadFiles(jadxArgs.getInput());
|
||||
static void processAndSave(JadxCLIArgs inputArgs) {
|
||||
JadxDecompiler jadx = new JadxDecompiler(inputArgs.toJadxArgs());
|
||||
jadx.load();
|
||||
jadx.save();
|
||||
if (jadx.getErrorsCount() != 0) {
|
||||
jadx.printErrorsReport();
|
||||
@@ -35,34 +31,4 @@ public class JadxCLI {
|
||||
LOG.info("done");
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
return false;
|
||||
}
|
||||
File outputDir = jadxArgs.getOutDir();
|
||||
if (outputDir == null) {
|
||||
String outDirName;
|
||||
File file = jadxArgs.getInput().get(0);
|
||||
String name = file.getName();
|
||||
int pos = name.lastIndexOf('.');
|
||||
if (pos != -1) {
|
||||
outDirName = name.substring(0, pos);
|
||||
} else {
|
||||
outDirName = name + "-jadx-out";
|
||||
}
|
||||
LOG.info("output directory: {}", outDirName);
|
||||
outputDir = new File(outDirName);
|
||||
jadxArgs.setOutputDir(outputDir);
|
||||
}
|
||||
if (outputDir.exists() && !outputDir.isDirectory()) {
|
||||
throw new JadxException("Output directory exists as file " + outputDir);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package jadx.cli;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import ch.qos.logback.core.Appender;
|
||||
@@ -18,28 +18,27 @@ import com.beust.jcommander.ParameterException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.IJadxArgs;
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
|
||||
public class JadxCLIArgs implements IJadxArgs {
|
||||
|
||||
protected static final int DEFAULT_THREADS_COUNT = Math.max(1, Runtime.getRuntime().availableProcessors() / 2);
|
||||
public class JadxCLIArgs {
|
||||
|
||||
@Parameter(description = "<input file> (.dex, .apk, .jar or .class)")
|
||||
protected List<String> files;
|
||||
protected List<String> files = new ArrayList<>(1);
|
||||
|
||||
@Parameter(names = {"-d", "--output-dir"}, description = "output directory")
|
||||
protected String outDirName;
|
||||
protected String outDir;
|
||||
|
||||
@Parameter(names = {"-ds", "--output-dir-src"}, description = "output directory for sources")
|
||||
protected String outDirNameSrc;
|
||||
protected String outDirSrc;
|
||||
|
||||
@Parameter(names = {"-dr", "--output-dir-res"}, description = "output directory for resources")
|
||||
protected String outDirNameRes;
|
||||
protected String outDirRes;
|
||||
|
||||
@Parameter(names = {"-j", "--threads-count"}, description = "processing threads count")
|
||||
protected int threadsCount = DEFAULT_THREADS_COUNT;
|
||||
protected int threadsCount = JadxArgs.DEFAULT_THREADS_COUNT;
|
||||
|
||||
@Parameter(names = {"-r", "--no-res"}, description = "do not decode resources")
|
||||
protected boolean skipResources = false;
|
||||
@@ -94,11 +93,6 @@ public class JadxCLIArgs implements IJadxArgs {
|
||||
@Parameter(names = {"-h", "--help"}, description = "print this help", help = true)
|
||||
protected boolean printHelp = false;
|
||||
|
||||
private final List<File> input = new ArrayList<>(1);
|
||||
private File outputDir;
|
||||
private File outputDirSrc;
|
||||
private File outputDirRes;
|
||||
|
||||
public boolean processArgs(String[] args) {
|
||||
return parse(args) && process();
|
||||
}
|
||||
@@ -123,35 +117,6 @@ public class JadxCLIArgs implements IJadxArgs {
|
||||
if (threadsCount <= 0) {
|
||||
throw new JadxException("Threads count must be positive, got: " + threadsCount);
|
||||
}
|
||||
if (files != null) {
|
||||
for (String fileName : files) {
|
||||
File file = new File(fileName);
|
||||
if (file.exists()) {
|
||||
input.add(file);
|
||||
} else {
|
||||
throw new JadxException("File not found: " + file);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (input.size() > 1) {
|
||||
throw new JadxException("Only one input file is supported");
|
||||
}
|
||||
if(outDirNameSrc != null) {
|
||||
outputDirSrc = new File(outDirNameSrc);
|
||||
}
|
||||
if(outDirNameRes != null) {
|
||||
outputDirRes = new File(outDirNameRes);
|
||||
}
|
||||
if (outDirName != null) {
|
||||
outputDir = new File(outDirName);
|
||||
if(outputDirSrc == null) {
|
||||
outputDirSrc = new File(outputDir, "source");
|
||||
}
|
||||
if(outputDirRes == null) {
|
||||
outputDirRes = new File(outputDir, "res");
|
||||
}
|
||||
}
|
||||
|
||||
if (isVerbose()) {
|
||||
ch.qos.logback.classic.Logger rootLogger =
|
||||
(ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
|
||||
@@ -212,6 +177,31 @@ public class JadxCLIArgs implements IJadxArgs {
|
||||
}
|
||||
}
|
||||
|
||||
public JadxArgs toJadxArgs() {
|
||||
JadxArgs args = new JadxArgs();
|
||||
args.setInputFiles(files.stream().map(FileUtils::toFile).collect(Collectors.toList()));
|
||||
args.setOutDir(FileUtils.toFile(outDir));
|
||||
args.setOutDirSrc(FileUtils.toFile(outDirSrc));
|
||||
args.setOutDirRes(FileUtils.toFile(outDirRes));
|
||||
args.setThreadsCount(threadsCount);
|
||||
args.setSkipSources(skipSources);
|
||||
args.setSkipResources(skipResources);
|
||||
args.setFallbackMode(fallbackMode);
|
||||
args.setShowInconsistentCode(showInconsistentCode);
|
||||
args.setCfgOutput(cfgOutput);
|
||||
args.setRawCFGOutput(rawCfgOutput);
|
||||
args.setReplaceConsts(replaceConsts);
|
||||
args.setDeobfuscationOn(deobfuscationOn);
|
||||
args.setDeobfuscationForceSave(deobfuscationForceSave);
|
||||
args.setDeobfuscationMinLength(deobfuscationMinLength);
|
||||
args.setDeobfuscationMaxLength(deobfuscationMaxLength);
|
||||
args.setUseSourceNameAsClassAlias(deobfuscationUseSourceNameAsAlias);
|
||||
args.setEscapeUnicode(escapeUnicode);
|
||||
args.setExportAsGradleProject(exportAsGradleProject);
|
||||
args.setUseImports(useImports);
|
||||
return args;
|
||||
}
|
||||
|
||||
public static class InvertedBooleanConverter implements IStringConverter<Boolean> {
|
||||
@Override
|
||||
public Boolean convert(String value) {
|
||||
@@ -219,114 +209,90 @@ public class JadxCLIArgs implements IJadxArgs {
|
||||
}
|
||||
}
|
||||
|
||||
public List<File> getInput() {
|
||||
return input;
|
||||
public List<String> getFiles() {
|
||||
return files;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getOutDir() {
|
||||
return outputDir;
|
||||
public String getOutDir() {
|
||||
return outDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getOutDirSrc() {
|
||||
return outputDirSrc;
|
||||
public String getOutDirSrc() {
|
||||
return outDirSrc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getOutDirRes() {
|
||||
return outputDirRes;
|
||||
}
|
||||
|
||||
public void setOutputDir(File outputDir) {
|
||||
this.outputDir = outputDir;
|
||||
public String getOutDirRes() {
|
||||
return outDirRes;
|
||||
}
|
||||
|
||||
public boolean isPrintHelp() {
|
||||
return printHelp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipResources() {
|
||||
return skipResources;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipSources() {
|
||||
return skipSources;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThreadsCount() {
|
||||
return threadsCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCFGOutput() {
|
||||
return cfgOutput;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRawCFGOutput() {
|
||||
return rawCfgOutput;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFallbackMode() {
|
||||
return fallbackMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShowInconsistentCode() {
|
||||
return showInconsistentCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUsingImports() {
|
||||
public boolean isUseImports() {
|
||||
return useImports;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVerbose() {
|
||||
return verbose;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeobfuscationOn() {
|
||||
return deobfuscationOn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDeobfuscationMinLength() {
|
||||
return deobfuscationMinLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDeobfuscationMaxLength() {
|
||||
return deobfuscationMaxLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeobfuscationForceSave() {
|
||||
return deobfuscationForceSave;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useSourceNameAsClassAlias() {
|
||||
public boolean isDeobfuscationUseSourceNameAsAlias() {
|
||||
return deobfuscationUseSourceNameAsAlias;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean escapeUnicode() {
|
||||
return escapeUnicode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReplaceConsts() {
|
||||
return replaceConsts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExportAsGradleProject() {
|
||||
return exportAsGradleProject;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import static org.junit.Assert.assertThat;
|
||||
public class JadxCLIArgsTest {
|
||||
|
||||
@Test
|
||||
public void testInvertedBooleanOption() throws Exception {
|
||||
public void testInvertedBooleanOption() {
|
||||
assertThat(parse("--no-replace-consts").isReplaceConsts(), is(false));
|
||||
assertThat(parse("").isReplaceConsts(), is(true));
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
package jadx.api;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public interface IJadxArgs {
|
||||
File getOutDir();
|
||||
|
||||
File getOutDirSrc();
|
||||
|
||||
File getOutDirRes();
|
||||
|
||||
int getThreadsCount();
|
||||
|
||||
boolean isCFGOutput();
|
||||
|
||||
boolean isRawCFGOutput();
|
||||
|
||||
boolean isFallbackMode();
|
||||
|
||||
boolean isShowInconsistentCode();
|
||||
|
||||
boolean isUsingImports();
|
||||
|
||||
boolean isVerbose();
|
||||
|
||||
boolean isSkipResources();
|
||||
|
||||
boolean isSkipSources();
|
||||
|
||||
boolean isDeobfuscationOn();
|
||||
|
||||
int getDeobfuscationMinLength();
|
||||
|
||||
int getDeobfuscationMaxLength();
|
||||
|
||||
boolean isDeobfuscationForceSave();
|
||||
|
||||
boolean useSourceNameAsClassAlias();
|
||||
|
||||
boolean escapeUnicode();
|
||||
|
||||
/**
|
||||
* Replace constant values with static final fields with same value
|
||||
*/
|
||||
boolean isReplaceConsts();
|
||||
|
||||
/**
|
||||
* Save as gradle project
|
||||
*/
|
||||
boolean isExportAsGradleProject();
|
||||
}
|
||||
@@ -1,21 +1,31 @@
|
||||
package jadx.api;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class JadxArgs implements IJadxArgs {
|
||||
public class JadxArgs {
|
||||
|
||||
private File outDir = new File("jadx-output");
|
||||
private File outDirSrc = new File(outDir, "source");
|
||||
private File outDirRes = new File(outDir, "res");
|
||||
private int threadsCount = Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
|
||||
public static final int DEFAULT_THREADS_COUNT = Math.max(1, Runtime.getRuntime().availableProcessors() / 2);
|
||||
|
||||
public static final String DEFAULT_OUT_DIR = "jadx-output";
|
||||
public static final String DEFAULT_SRC_DIR = "sources";
|
||||
public static final String DEFAULT_RES_DIR = "resources";
|
||||
|
||||
private List<File> inputFiles = new ArrayList<>(1);
|
||||
|
||||
private File outDir;
|
||||
private File outDirSrc;
|
||||
private File outDirRes;
|
||||
|
||||
private int threadsCount = DEFAULT_THREADS_COUNT;
|
||||
|
||||
private boolean cfgOutput = false;
|
||||
private boolean rawCFGOutput = false;
|
||||
|
||||
private boolean isVerbose = false;
|
||||
private boolean fallbackMode = false;
|
||||
private boolean showInconsistentCode = false;
|
||||
|
||||
|
||||
private boolean useImports = true;
|
||||
|
||||
private boolean isSkipResources = false;
|
||||
@@ -32,7 +42,24 @@ public class JadxArgs implements IJadxArgs {
|
||||
private boolean replaceConsts = true;
|
||||
private boolean exportAsGradleProject = false;
|
||||
|
||||
@Override
|
||||
public JadxArgs() {
|
||||
// use default options
|
||||
}
|
||||
|
||||
public void setRootDir(File rootDir) {
|
||||
setOutDir(rootDir);
|
||||
setOutDirSrc(new File(rootDir, DEFAULT_SRC_DIR));
|
||||
setOutDirRes(new File(rootDir, DEFAULT_RES_DIR));
|
||||
}
|
||||
|
||||
public List<File> getInputFiles() {
|
||||
return inputFiles;
|
||||
}
|
||||
|
||||
public void setInputFiles(List<File> inputFiles) {
|
||||
this.inputFiles = inputFiles;
|
||||
}
|
||||
|
||||
public File getOutDir() {
|
||||
return outDir;
|
||||
}
|
||||
@@ -41,7 +68,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.outDir = outDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getOutDirSrc() {
|
||||
return outDirSrc;
|
||||
}
|
||||
@@ -50,7 +76,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.outDirSrc = outDirSrc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getOutDirRes() {
|
||||
return outDirRes;
|
||||
}
|
||||
@@ -59,7 +84,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.outDirRes = outDirRes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThreadsCount() {
|
||||
return threadsCount;
|
||||
}
|
||||
@@ -68,8 +92,7 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.threadsCount = threadsCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCFGOutput() {
|
||||
public boolean isCfgOutput() {
|
||||
return cfgOutput;
|
||||
}
|
||||
|
||||
@@ -77,7 +100,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.cfgOutput = cfgOutput;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRawCFGOutput() {
|
||||
return rawCFGOutput;
|
||||
}
|
||||
@@ -86,7 +108,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.rawCFGOutput = rawCFGOutput;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFallbackMode() {
|
||||
return fallbackMode;
|
||||
}
|
||||
@@ -95,7 +116,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.fallbackMode = fallbackMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShowInconsistentCode() {
|
||||
return showInconsistentCode;
|
||||
}
|
||||
@@ -104,8 +124,7 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.showInconsistentCode = showInconsistentCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUsingImports() {
|
||||
public boolean isUseImports() {
|
||||
return useImports;
|
||||
}
|
||||
|
||||
@@ -113,16 +132,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.useImports = useImports;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVerbose() {
|
||||
return isVerbose;
|
||||
}
|
||||
|
||||
public void setVerbose(boolean verbose) {
|
||||
isVerbose = verbose;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipResources() {
|
||||
return isSkipResources;
|
||||
}
|
||||
@@ -131,7 +140,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
isSkipResources = skipResources;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSkipSources() {
|
||||
return isSkipSources;
|
||||
}
|
||||
@@ -140,7 +148,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
isSkipSources = skipSources;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeobfuscationOn() {
|
||||
return isDeobfuscationOn;
|
||||
}
|
||||
@@ -149,7 +156,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
isDeobfuscationOn = deobfuscationOn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeobfuscationForceSave() {
|
||||
return isDeobfuscationForceSave;
|
||||
}
|
||||
@@ -158,8 +164,7 @@ public class JadxArgs implements IJadxArgs {
|
||||
isDeobfuscationForceSave = deobfuscationForceSave;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useSourceNameAsClassAlias() {
|
||||
public boolean isUseSourceNameAsClassAlias() {
|
||||
return useSourceNameAsClassAlias;
|
||||
}
|
||||
|
||||
@@ -167,7 +172,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.useSourceNameAsClassAlias = useSourceNameAsClassAlias;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDeobfuscationMinLength() {
|
||||
return deobfuscationMinLength;
|
||||
}
|
||||
@@ -176,7 +180,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.deobfuscationMinLength = deobfuscationMinLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDeobfuscationMaxLength() {
|
||||
return deobfuscationMaxLength;
|
||||
}
|
||||
@@ -185,8 +188,7 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.deobfuscationMaxLength = deobfuscationMaxLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean escapeUnicode() {
|
||||
public boolean isEscapeUnicode() {
|
||||
return escapeUnicode;
|
||||
}
|
||||
|
||||
@@ -194,7 +196,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.escapeUnicode = escapeUnicode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReplaceConsts() {
|
||||
return replaceConsts;
|
||||
}
|
||||
@@ -203,7 +204,6 @@ public class JadxArgs implements IJadxArgs {
|
||||
this.replaceConsts = replaceConsts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExportAsGradleProject() {
|
||||
return exportAsGradleProject;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package jadx.api;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
public class JadxArgsValidator {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JadxArgsValidator.class);
|
||||
|
||||
public static void validate(JadxArgs args) {
|
||||
if (args.getInputFiles().isEmpty()) {
|
||||
throw new JadxRuntimeException("Please specify input file");
|
||||
}
|
||||
for (File file : args.getInputFiles()) {
|
||||
checkFile(file);
|
||||
}
|
||||
validateOutDirs(args);
|
||||
}
|
||||
|
||||
private static void validateOutDirs(JadxArgs args) {
|
||||
File outDir = args.getOutDir();
|
||||
File srcDir = args.getOutDirSrc();
|
||||
File resDir = args.getOutDirRes();
|
||||
if (outDir == null) {
|
||||
if (srcDir != null) {
|
||||
outDir = srcDir;
|
||||
} else if (resDir != null) {
|
||||
outDir = resDir;
|
||||
} else {
|
||||
outDir = makeDirFromInput(args);
|
||||
}
|
||||
}
|
||||
args.setOutDir(outDir);
|
||||
setFromOut(args);
|
||||
|
||||
checkDir(args.getOutDir());
|
||||
checkDir(args.getOutDirSrc());
|
||||
checkDir(args.getOutDirRes());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static File makeDirFromInput(JadxArgs args) {
|
||||
File outDir;
|
||||
String outDirName;
|
||||
File file = args.getInputFiles().get(0);
|
||||
String name = file.getName();
|
||||
int pos = name.lastIndexOf('.');
|
||||
if (pos != -1) {
|
||||
outDirName = name.substring(0, pos);
|
||||
} else {
|
||||
outDirName = name + "-" + JadxArgs.DEFAULT_OUT_DIR;
|
||||
}
|
||||
LOG.info("output directory: {}", outDirName);
|
||||
outDir = new File(outDirName);
|
||||
return outDir;
|
||||
}
|
||||
|
||||
private static void setFromOut(JadxArgs args) {
|
||||
if (args.getOutDirSrc() == null) {
|
||||
args.setOutDirSrc(new File(args.getOutDir(), JadxArgs.DEFAULT_SRC_DIR));
|
||||
}
|
||||
if (args.getOutDirRes() == null) {
|
||||
args.setOutDirRes(new File(args.getOutDir(), JadxArgs.DEFAULT_RES_DIR));
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkFile(File file) {
|
||||
if (!file.exists()) {
|
||||
throw new JadxRuntimeException("File not found " + file.getAbsolutePath());
|
||||
}
|
||||
if (file.isDirectory()) {
|
||||
throw new JadxRuntimeException("Expected file but found directory instead: " + file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkDir(File dir) {
|
||||
if (dir != null && dir.exists() && !dir.isDirectory()) {
|
||||
throw new JadxRuntimeException("Output directory exists as file " + dir);
|
||||
}
|
||||
}
|
||||
|
||||
private JadxArgsValidator() {
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,6 @@
|
||||
package jadx.api;
|
||||
|
||||
import jadx.core.Jadx;
|
||||
import jadx.core.ProcessClass;
|
||||
import jadx.core.codegen.CodeGen;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.dex.visitors.IDexTreeVisitor;
|
||||
import jadx.core.dex.visitors.SaveCode;
|
||||
import jadx.core.export.ExportGradleProject;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.utils.files.InputFile;
|
||||
import jadx.core.xmlgen.BinaryXMLParser;
|
||||
import jadx.core.xmlgen.ResourcesSaver;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@@ -33,16 +15,35 @@ import java.util.concurrent.TimeUnit;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.Jadx;
|
||||
import jadx.core.ProcessClass;
|
||||
import jadx.core.codegen.CodeGen;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.dex.visitors.IDexTreeVisitor;
|
||||
import jadx.core.dex.visitors.SaveCode;
|
||||
import jadx.core.export.ExportGradleProject;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.utils.files.InputFile;
|
||||
import jadx.core.xmlgen.BinaryXMLParser;
|
||||
import jadx.core.xmlgen.ResourcesSaver;
|
||||
|
||||
/**
|
||||
* Jadx API usage example:
|
||||
* <pre><code>
|
||||
* JadxDecompiler jadx = new JadxDecompiler();
|
||||
* jadx.loadFile(new File("classes.dex"));
|
||||
* jadx.setOutputDir(new File("out"));
|
||||
* jadx.save();
|
||||
* JadxArgs args = new JadxArgs();
|
||||
* args.getInputFiles().add(new File("test.apk"));
|
||||
* args.setOutDir(new File("jadx-test-output"));
|
||||
*
|
||||
* JadxDecompiler jadx = new JadxDecompiler(args);
|
||||
* jadx.load();
|
||||
* jadx.save();
|
||||
* </code></pre>
|
||||
* <p/>
|
||||
* Instead of 'save()' you can get list of decompiled classes:
|
||||
* Instead of 'save()' you can iterate over decompiled classes:
|
||||
* <pre><code>
|
||||
* for(JavaClass cls : jadx.getClasses()) {
|
||||
* System.out.println(cls.getCode());
|
||||
@@ -52,12 +53,9 @@ import org.slf4j.LoggerFactory;
|
||||
public final class JadxDecompiler {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JadxDecompiler.class);
|
||||
|
||||
private final IJadxArgs args;
|
||||
private final List<InputFile> inputFiles = new ArrayList<>();
|
||||
private JadxArgs args;
|
||||
|
||||
private File outDir;
|
||||
private File outDirRes;
|
||||
private File outDirSrc;
|
||||
private final List<InputFile> inputFiles = new ArrayList<>();
|
||||
|
||||
private RootNode root;
|
||||
private List<IDexTreeVisitor> passes;
|
||||
@@ -72,54 +70,35 @@ public final class JadxDecompiler {
|
||||
private Map<MethodNode, JavaMethod> methodsMap = new ConcurrentHashMap<>();
|
||||
private Map<FieldNode, JavaField> fieldsMap = new ConcurrentHashMap<>();
|
||||
|
||||
public JadxDecompiler() throws JadxException {
|
||||
public JadxDecompiler() {
|
||||
this(new JadxArgs());
|
||||
}
|
||||
|
||||
public JadxDecompiler(IJadxArgs jadxArgs) throws JadxException {
|
||||
this.args = jadxArgs;
|
||||
this.outDir = jadxArgs.getOutDir();
|
||||
this.outDirSrc = jadxArgs.getOutDirSrc();
|
||||
this.outDirRes = jadxArgs.getOutDirRes();
|
||||
public JadxDecompiler(JadxArgs args) {
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public void load() {
|
||||
reset();
|
||||
JadxArgsValidator.validate(args);
|
||||
init();
|
||||
LOG.info("loading ...");
|
||||
|
||||
loadFiles(args.getInputFiles());
|
||||
|
||||
root = new RootNode(args);
|
||||
root.load(inputFiles);
|
||||
|
||||
root.initClassPath();
|
||||
root.loadResources(getResources());
|
||||
root.initAppResClass();
|
||||
|
||||
initVisitors();
|
||||
}
|
||||
|
||||
public void setOutputDir(File outDir) throws JadxException {
|
||||
this.outDir = outDir;
|
||||
init();
|
||||
}
|
||||
|
||||
public void setOutputDirSrc(File outDirSrc) throws JadxException {
|
||||
this.outDirSrc = outDirSrc;
|
||||
init();
|
||||
}
|
||||
|
||||
public void setOutputDirRes(File outDirRes) throws JadxException {
|
||||
this.outDirRes = outDirRes;
|
||||
init();
|
||||
}
|
||||
|
||||
void init() throws JadxException {
|
||||
if(outDir == null && outDirSrc == null) {
|
||||
outDirSrc = new JadxArgs().getOutDirSrc();
|
||||
}
|
||||
if(outDir == null && outDirRes == null) {
|
||||
outDirRes = new JadxArgs().getOutDirRes();
|
||||
}
|
||||
if (outDir == null) {
|
||||
outDir = new JadxArgs().getOutDir();
|
||||
}
|
||||
else {
|
||||
if(outDirSrc == null && outDirRes != null && !args.isSkipSources()) {
|
||||
throw new JadxException("--output-dir-src must be specified");
|
||||
}
|
||||
if(outDirSrc != null && outDirRes == null && !args.isSkipResources()) {
|
||||
throw new JadxException("--output-dir-res must be specified");
|
||||
}
|
||||
}
|
||||
this.passes = Jadx.getPassesList(args, outDir);
|
||||
this.codeGen = new CodeGen(args);
|
||||
void init() {
|
||||
this.passes = Jadx.getPassesList(args);
|
||||
this.codeGen = new CodeGen();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
@@ -135,23 +114,18 @@ public final class JadxDecompiler {
|
||||
return Jadx.getVersion();
|
||||
}
|
||||
|
||||
public void loadFile(File file) throws JadxException {
|
||||
loadFiles(Collections.singletonList(file));
|
||||
}
|
||||
|
||||
public void loadFiles(List<File> files) throws JadxException {
|
||||
private void loadFiles(List<File> files) {
|
||||
if (files.isEmpty()) {
|
||||
throw new JadxException("Empty file list");
|
||||
throw new JadxRuntimeException("Empty file list");
|
||||
}
|
||||
inputFiles.clear();
|
||||
for (File file : files) {
|
||||
try {
|
||||
InputFile.addFilesFrom(file, inputFiles, args.isSkipSources());
|
||||
} catch (IOException e) {
|
||||
throw new JadxException("Error load file: " + file, e);
|
||||
} catch (Exception e) {
|
||||
throw new JadxRuntimeException("Error load file: " + file, e);
|
||||
}
|
||||
}
|
||||
parse();
|
||||
}
|
||||
|
||||
public void save() {
|
||||
@@ -194,13 +168,13 @@ public final class JadxDecompiler {
|
||||
File sourcesOutDir;
|
||||
File resOutDir;
|
||||
if (args.isExportAsGradleProject()) {
|
||||
ExportGradleProject export = new ExportGradleProject(root, outDir);
|
||||
ExportGradleProject export = new ExportGradleProject(root, args.getOutDir());
|
||||
export.init();
|
||||
sourcesOutDir = export.getSrcOutDir();
|
||||
resOutDir = export.getResOutDir();
|
||||
} else {
|
||||
sourcesOutDir = outDirSrc;
|
||||
resOutDir = outDirRes;
|
||||
sourcesOutDir = args.getOutDirSrc();
|
||||
resOutDir = args.getOutDirRes();
|
||||
}
|
||||
if (saveSources) {
|
||||
appendSourcesSave(executor, sourcesOutDir);
|
||||
@@ -294,21 +268,6 @@ public final class JadxDecompiler {
|
||||
root.getErrorsCounter().printReport();
|
||||
}
|
||||
|
||||
void parse() throws JadxException {
|
||||
reset();
|
||||
init();
|
||||
|
||||
root = new RootNode(args);
|
||||
LOG.info("loading ...");
|
||||
root.load(inputFiles);
|
||||
|
||||
root.initClassPath();
|
||||
root.loadResources(getResources());
|
||||
root.initAppResClass();
|
||||
|
||||
initVisitors();
|
||||
}
|
||||
|
||||
private void initVisitors() {
|
||||
for (IDexTreeVisitor pass : passes) {
|
||||
try {
|
||||
@@ -346,7 +305,7 @@ public final class JadxDecompiler {
|
||||
return fieldsMap;
|
||||
}
|
||||
|
||||
public IJadxArgs getArgs() {
|
||||
public JadxArgs getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
@@ -354,5 +313,4 @@ public final class JadxDecompiler {
|
||||
public String toString() {
|
||||
return "jadx decompiler " + getVersion();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
package jadx.core;
|
||||
|
||||
import jadx.api.IJadxArgs;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.core.dex.visitors.ClassModifier;
|
||||
import jadx.core.dex.visitors.CodeShrinker;
|
||||
import jadx.core.dex.visitors.ConstInlineVisitor;
|
||||
@@ -33,16 +42,6 @@ import jadx.core.dex.visitors.ssa.SSATransform;
|
||||
import jadx.core.dex.visitors.typeinference.FinishTypeInference;
|
||||
import jadx.core.dex.visitors.typeinference.TypeInference;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Jadx {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Jadx.class);
|
||||
|
||||
@@ -55,7 +54,7 @@ public class Jadx {
|
||||
}
|
||||
}
|
||||
|
||||
public static List<IDexTreeVisitor> getPassesList(IJadxArgs args, File outDir) {
|
||||
public static List<IDexTreeVisitor> getPassesList(JadxArgs args) {
|
||||
List<IDexTreeVisitor> passes = new ArrayList<>();
|
||||
if (args.isFallbackMode()) {
|
||||
passes.add(new FallbackModeVisitor());
|
||||
@@ -71,7 +70,7 @@ public class Jadx {
|
||||
passes.add(new TypeInference());
|
||||
|
||||
if (args.isRawCFGOutput()) {
|
||||
passes.add(DotGraphVisitor.dumpRaw(outDir));
|
||||
passes.add(DotGraphVisitor.dumpRaw());
|
||||
}
|
||||
|
||||
passes.add(new ConstInlineVisitor());
|
||||
@@ -83,8 +82,8 @@ public class Jadx {
|
||||
passes.add(new CodeShrinker());
|
||||
passes.add(new ReSugarCode());
|
||||
|
||||
if (args.isCFGOutput()) {
|
||||
passes.add(DotGraphVisitor.dump(outDir));
|
||||
if (args.isCfgOutput()) {
|
||||
passes.add(DotGraphVisitor.dump());
|
||||
}
|
||||
|
||||
passes.add(new RegionMakerVisitor());
|
||||
@@ -95,8 +94,8 @@ public class Jadx {
|
||||
passes.add(new SimplifyVisitor());
|
||||
passes.add(new CheckRegions());
|
||||
|
||||
if (args.isCFGOutput()) {
|
||||
passes.add(DotGraphVisitor.dumpRegions(outDir));
|
||||
if (args.isCfgOutput()) {
|
||||
passes.add(DotGraphVisitor.dumpRegions());
|
||||
}
|
||||
|
||||
passes.add(new MethodInlineVisitor());
|
||||
|
||||
@@ -12,7 +12,7 @@ import java.util.Set;
|
||||
|
||||
import com.android.dx.rop.code.AccessFlags;
|
||||
|
||||
import jadx.api.IJadxArgs;
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.AttrNode;
|
||||
@@ -48,8 +48,8 @@ public class ClassGen {
|
||||
private final Set<ClassInfo> imports = new HashSet<>();
|
||||
private int clsDeclLine;
|
||||
|
||||
public ClassGen(ClassNode cls, IJadxArgs jadxArgs) {
|
||||
this(cls, null, jadxArgs.isUsingImports(), jadxArgs.isFallbackMode(), jadxArgs.isShowInconsistentCode());
|
||||
public ClassGen(ClassNode cls, JadxArgs jadxArgs) {
|
||||
this(cls, null, jadxArgs.isUseImports(), jadxArgs.isFallbackMode(), jadxArgs.isShowInconsistentCode());
|
||||
}
|
||||
|
||||
public ClassGen(ClassNode cls, ClassGen parentClsGen) {
|
||||
|
||||
@@ -1,25 +1,17 @@
|
||||
package jadx.core.codegen;
|
||||
|
||||
import jadx.api.IJadxArgs;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.visitors.AbstractVisitor;
|
||||
import jadx.core.utils.exceptions.CodegenException;
|
||||
|
||||
public class CodeGen extends AbstractVisitor {
|
||||
|
||||
private final IJadxArgs args;
|
||||
|
||||
public CodeGen(IJadxArgs args) {
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(ClassNode cls) throws CodegenException {
|
||||
ClassGen clsGen = new ClassGen(cls, args);
|
||||
ClassGen clsGen = new ClassGen(cls, cls.root().getArgs());
|
||||
CodeWriter clsCode = clsGen.makeClass();
|
||||
clsCode.finish();
|
||||
cls.setCode(clsCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package jadx.core.deobf;
|
||||
|
||||
import jadx.api.IJadxArgs;
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
@@ -34,7 +34,7 @@ public class Deobfuscator {
|
||||
public static final String CLASS_NAME_SEPARATOR = ".";
|
||||
public static final String INNER_CLASS_SEPARATOR = "$";
|
||||
|
||||
private final IJadxArgs args;
|
||||
private final JadxArgs args;
|
||||
@NotNull
|
||||
private final List<DexNode> dexNodes;
|
||||
private final DeobfPresets deobfPresets;
|
||||
@@ -58,13 +58,13 @@ public class Deobfuscator {
|
||||
private int fldIndex = 0;
|
||||
private int mthIndex = 0;
|
||||
|
||||
public Deobfuscator(IJadxArgs args, @NotNull List<DexNode> dexNodes, File deobfMapFile) {
|
||||
public Deobfuscator(JadxArgs args, @NotNull List<DexNode> dexNodes, File deobfMapFile) {
|
||||
this.args = args;
|
||||
this.dexNodes = dexNodes;
|
||||
|
||||
this.minLength = args.getDeobfuscationMinLength();
|
||||
this.maxLength = args.getDeobfuscationMaxLength();
|
||||
this.useSourceNameAsAlias = args.useSourceNameAsClassAlias();
|
||||
this.useSourceNameAsAlias = args.isUseSourceNameAsClassAlias();
|
||||
|
||||
this.deobfPresets = new DeobfPresets(this, deobfMapFile);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.api.IJadxArgs;
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.instructions.args.LiteralArg;
|
||||
import jadx.core.dex.instructions.args.PrimitiveType;
|
||||
@@ -60,7 +60,7 @@ public class ConstStorage {
|
||||
|
||||
private Map<Integer, String> resourcesNames = new HashMap<>();
|
||||
|
||||
public ConstStorage(IJadxArgs args) {
|
||||
public ConstStorage(JadxArgs args) {
|
||||
this.replaceEnabled = args.isReplaceConsts();
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
// cache maps
|
||||
private Map<MethodInfo, MethodNode> mthInfoMap = Collections.emptyMap();
|
||||
|
||||
public ClassNode(DexNode dex, ClassDef cls) throws DecodeException {
|
||||
public ClassNode(DexNode dex, ClassDef cls) {
|
||||
this.dex = dex;
|
||||
this.clsInfo = ClassInfo.fromDex(dex, cls.getTypeIndex());
|
||||
try {
|
||||
@@ -128,7 +128,7 @@ public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
|
||||
|
||||
buildCache();
|
||||
} catch (Exception e) {
|
||||
throw new DecodeException("Error decode class: " + clsInfo, e);
|
||||
throw new JadxRuntimeException("Error decode class: " + clsInfo, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ public class DexNode implements IDexNode {
|
||||
this.dexId = dexId;
|
||||
}
|
||||
|
||||
public void loadClasses() throws DecodeException {
|
||||
public void loadClasses() {
|
||||
for (ClassDef cls : dexBuf.classDefs()) {
|
||||
ClassNode clsNode = new ClassNode(this, cls);
|
||||
classes.add(clsNode);
|
||||
|
||||
@@ -1,24 +1,5 @@
|
||||
package jadx.core.dex.nodes;
|
||||
|
||||
import jadx.api.IJadxArgs;
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.api.ResourceType;
|
||||
import jadx.api.ResourcesLoader;
|
||||
import jadx.core.clsp.ClspGraph;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.info.ConstStorage;
|
||||
import jadx.core.dex.info.InfoStorage;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.StringUtils;
|
||||
import jadx.core.utils.android.AndroidResourcesUtils;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.core.utils.files.DexFile;
|
||||
import jadx.core.utils.files.InputFile;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
import jadx.core.xmlgen.ResTableParser;
|
||||
import jadx.core.xmlgen.ResourceStorage;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
@@ -28,11 +9,30 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.api.ResourceType;
|
||||
import jadx.api.ResourcesLoader;
|
||||
import jadx.core.clsp.ClspGraph;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.info.ConstStorage;
|
||||
import jadx.core.dex.info.InfoStorage;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.StringUtils;
|
||||
import jadx.core.utils.android.AndroidResourcesUtils;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.utils.files.DexFile;
|
||||
import jadx.core.utils.files.InputFile;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
import jadx.core.xmlgen.ResTableParser;
|
||||
import jadx.core.xmlgen.ResourceStorage;
|
||||
|
||||
public class RootNode {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RootNode.class);
|
||||
|
||||
private final ErrorsCounter errorsCounter = new ErrorsCounter();
|
||||
private final IJadxArgs args;
|
||||
private final JadxArgs args;
|
||||
private final StringUtils stringUtils;
|
||||
private final ConstStorage constValues;
|
||||
private final InfoStorage infoStorage = new InfoStorage();
|
||||
@@ -43,13 +43,13 @@ public class RootNode {
|
||||
private ClassNode appResClass;
|
||||
private ClspGraph clsp;
|
||||
|
||||
public RootNode(IJadxArgs args) {
|
||||
public RootNode(JadxArgs args) {
|
||||
this.args = args;
|
||||
this.stringUtils = new StringUtils(args);
|
||||
this.constValues = new ConstStorage(args);
|
||||
}
|
||||
|
||||
public void load(List<InputFile> inputFiles) throws DecodeException {
|
||||
public void load(List<InputFile> inputFiles) {
|
||||
dexNodes = new ArrayList<>();
|
||||
for (InputFile input : inputFiles) {
|
||||
for (DexFile dexFile : input.getDexFiles()) {
|
||||
@@ -58,7 +58,7 @@ public class RootNode {
|
||||
DexNode dexNode = new DexNode(this, dexFile, dexNodes.size());
|
||||
dexNodes.add(dexNode);
|
||||
} catch (Exception e) {
|
||||
throw new DecodeException("Error decode file: " + dexFile, e);
|
||||
throw new JadxRuntimeException("Error decode file: " + dexFile, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,22 +103,22 @@ public class RootNode {
|
||||
appResClass = AndroidResourcesUtils.searchAppResClass(this);
|
||||
}
|
||||
|
||||
public void initClassPath() throws DecodeException {
|
||||
public void initClassPath() {
|
||||
try {
|
||||
if (this.clsp == null) {
|
||||
ClspGraph clsp = new ClspGraph();
|
||||
clsp.load();
|
||||
ClspGraph newClsp = new ClspGraph();
|
||||
newClsp.load();
|
||||
|
||||
List<ClassNode> classes = new ArrayList<>();
|
||||
for (DexNode dexNode : dexNodes) {
|
||||
classes.addAll(dexNode.getClasses());
|
||||
}
|
||||
clsp.addApp(classes);
|
||||
newClsp.addApp(classes);
|
||||
|
||||
this.clsp = clsp;
|
||||
this.clsp = newClsp;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new DecodeException("Error loading classpath", e);
|
||||
} catch (Exception e) {
|
||||
throw new JadxRuntimeException("Error loading classpath", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,10 +188,6 @@ public class RootNode {
|
||||
return appResClass;
|
||||
}
|
||||
|
||||
public IJadxArgs getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
public StringUtils getStringUtils() {
|
||||
return stringUtils;
|
||||
}
|
||||
@@ -204,4 +200,7 @@ public class RootNode {
|
||||
return infoStorage;
|
||||
}
|
||||
|
||||
public JadxArgs getArgs() {
|
||||
return args;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,42 +23,33 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class DotGraphVisitor extends AbstractVisitor {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DotGraphVisitor.class);
|
||||
|
||||
private static final String NL = "\\l";
|
||||
private static final boolean PRINT_DOMINATORS = false;
|
||||
|
||||
private final File dir;
|
||||
private final boolean useRegions;
|
||||
private final boolean rawInsn;
|
||||
|
||||
public static DotGraphVisitor dump(File outDir) {
|
||||
return new DotGraphVisitor(outDir, false, false);
|
||||
public static DotGraphVisitor dump() {
|
||||
return new DotGraphVisitor(false, false);
|
||||
}
|
||||
|
||||
public static DotGraphVisitor dumpRaw(File outDir) {
|
||||
return new DotGraphVisitor(outDir, false, true);
|
||||
public static DotGraphVisitor dumpRaw() {
|
||||
return new DotGraphVisitor(false, true);
|
||||
}
|
||||
|
||||
public static DotGraphVisitor dumpRegions(File outDir) {
|
||||
return new DotGraphVisitor(outDir, true, false);
|
||||
public static DotGraphVisitor dumpRegions() {
|
||||
return new DotGraphVisitor(true, false);
|
||||
}
|
||||
|
||||
public static DotGraphVisitor dumpRawRegions(File outDir) {
|
||||
return new DotGraphVisitor(outDir, true, true);
|
||||
public static DotGraphVisitor dumpRawRegions() {
|
||||
return new DotGraphVisitor(true, true);
|
||||
}
|
||||
|
||||
private DotGraphVisitor(File outDir, boolean useRegions, boolean rawInsn) {
|
||||
this.dir = outDir;
|
||||
private DotGraphVisitor(boolean useRegions, boolean rawInsn) {
|
||||
this.useRegions = useRegions;
|
||||
this.rawInsn = rawInsn;
|
||||
LOG.debug("DOT {}{}graph dump dir: {}",
|
||||
useRegions ? "regions " : "", rawInsn ? "raw " : "", outDir.getAbsolutePath());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,12 +57,25 @@ public class DotGraphVisitor extends AbstractVisitor {
|
||||
if (mth.isNoCode()) {
|
||||
return;
|
||||
}
|
||||
new DumpDotGraph().process(mth);
|
||||
File outRootDir = mth.root().getArgs().getOutDir();
|
||||
new DumpDotGraph(outRootDir).process(mth);
|
||||
}
|
||||
|
||||
public void save(File dir, MethodNode mth) {
|
||||
if (mth.isNoCode()) {
|
||||
return;
|
||||
}
|
||||
new DumpDotGraph(dir).process(mth);
|
||||
}
|
||||
|
||||
private class DumpDotGraph {
|
||||
private final CodeWriter dot = new CodeWriter();
|
||||
private final CodeWriter conn = new CodeWriter();
|
||||
private final File dir;
|
||||
|
||||
public DumpDotGraph(File dir) {
|
||||
this.dir = dir;
|
||||
}
|
||||
|
||||
public void process(MethodNode mth) {
|
||||
dot.startLine("digraph \"CFG for");
|
||||
|
||||
@@ -7,7 +7,7 @@ import java.util.Set;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import jadx.api.IJadxArgs;
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.codegen.TypeGen;
|
||||
import jadx.core.deobf.Deobfuscator;
|
||||
@@ -31,8 +31,6 @@ public class RenameVisitor extends AbstractVisitor {
|
||||
|
||||
@Override
|
||||
public void init(RootNode root) {
|
||||
IJadxArgs args = root.getArgs();
|
||||
|
||||
List<DexNode> dexNodes = root.getDexNodes();
|
||||
if (dexNodes.isEmpty()) {
|
||||
return;
|
||||
@@ -43,6 +41,7 @@ public class RenameVisitor extends AbstractVisitor {
|
||||
String inputName = FilenameUtils.getBaseName(firstInputFileName);
|
||||
|
||||
File deobfMapFile = new File(inputPath, inputName + ".jobf");
|
||||
JadxArgs args = root.getArgs();
|
||||
deobfuscator = new Deobfuscator(args, dexNodes, deobfMapFile);
|
||||
boolean deobfuscationOn = args.isDeobfuscationOn();
|
||||
if (deobfuscationOn) {
|
||||
|
||||
@@ -2,7 +2,7 @@ package jadx.core.dex.visitors;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import jadx.api.IJadxArgs;
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.core.codegen.CodeWriter;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
|
||||
@@ -10,7 +10,7 @@ public class SaveCode {
|
||||
|
||||
private SaveCode() {}
|
||||
|
||||
public static void save(File dir, IJadxArgs args, ClassNode cls) {
|
||||
public static void save(File dir, JadxArgs args, ClassNode cls) {
|
||||
CodeWriter clsCode = cls.getCode();
|
||||
String fileName = cls.getClassInfo().getFullPath() + ".java";
|
||||
if (args.isFallbackMode()) {
|
||||
|
||||
@@ -44,9 +44,9 @@ public class DebugUtils {
|
||||
|
||||
public static void dump(MethodNode mth, String desc) {
|
||||
File out = new File("test-graph" + desc + "-tmp");
|
||||
DotGraphVisitor.dump(out).visit(mth);
|
||||
DotGraphVisitor.dumpRaw(out).visit(mth);
|
||||
DotGraphVisitor.dumpRegions(out).visit(mth);
|
||||
DotGraphVisitor.dump().save(out, mth);
|
||||
DotGraphVisitor.dumpRaw().save(out, mth);
|
||||
DotGraphVisitor.dumpRegions().save(out, mth);
|
||||
}
|
||||
|
||||
public static void printRegionsWithBlock(MethodNode mth, BlockNode block) {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package jadx.core.utils;
|
||||
|
||||
import jadx.api.IJadxArgs;
|
||||
import jadx.api.JadxArgs;
|
||||
|
||||
public class StringUtils {
|
||||
|
||||
private final boolean escapeUnicode;
|
||||
|
||||
public StringUtils(IJadxArgs args) {
|
||||
this.escapeUnicode = args.escapeUnicode();
|
||||
public StringUtils(JadxArgs args) {
|
||||
this.escapeUnicode = args.isEscapeUnicode();
|
||||
}
|
||||
|
||||
public String unescapeString(String str) {
|
||||
|
||||
@@ -214,4 +214,12 @@ public class FileUtils {
|
||||
}
|
||||
return IOCase.SYSTEM.isCaseSensitive();
|
||||
}
|
||||
|
||||
public static File toFile(String path) {
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
return new File(path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
package jadx.tests
|
||||
|
||||
import jadx.api.IJadxArgs
|
||||
import jadx.api.JadxDecompiler
|
||||
import jadx.core.utils.exceptions.JadxException
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException
|
||||
import spock.lang.Specification
|
||||
|
||||
class TestAPI extends Specification {
|
||||
|
||||
def "no loaded files"() {
|
||||
setup:
|
||||
def d = new JadxDecompiler()
|
||||
when:
|
||||
def classes = d.getClasses()
|
||||
def packages = d.getPackages()
|
||||
then:
|
||||
notThrown(NullPointerException)
|
||||
classes?.isEmpty()
|
||||
packages?.isEmpty()
|
||||
}
|
||||
|
||||
def "save with no loaded files"() {
|
||||
when:
|
||||
new JadxDecompiler().save()
|
||||
then:
|
||||
def e = thrown(JadxRuntimeException)
|
||||
e.message == "No loaded files"
|
||||
}
|
||||
|
||||
def "load empty files list"() {
|
||||
when:
|
||||
new JadxDecompiler().loadFiles(Collections.emptyList())
|
||||
then:
|
||||
def e = thrown(JadxException)
|
||||
e.message == "Empty file list"
|
||||
}
|
||||
|
||||
def "load null"() {
|
||||
when:
|
||||
new JadxDecompiler().loadFile(null)
|
||||
then:
|
||||
thrown(NullPointerException)
|
||||
}
|
||||
|
||||
def "load missing file"() {
|
||||
when:
|
||||
new JadxDecompiler().loadFile(new File("_.dex"))
|
||||
then:
|
||||
def e = thrown(JadxException)
|
||||
e.message == "Error load file: _.dex"
|
||||
e.cause.class == IOException
|
||||
}
|
||||
|
||||
def "pass decompiler args"() {
|
||||
setup:
|
||||
def args = Mock(IJadxArgs)
|
||||
when:
|
||||
new JadxDecompiler(args)
|
||||
then:
|
||||
noExceptionThrown()
|
||||
}
|
||||
|
||||
def "get errors count for new decompiler"() {
|
||||
expect:
|
||||
new JadxDecompiler().getErrorsCount() == 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package jadx.api;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
|
||||
import static jadx.core.utils.files.FileUtils.toFile;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class JadxArgsValidatorOutDirsTest {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JadxArgsValidatorOutDirsTest.class);
|
||||
public JadxArgs args;
|
||||
|
||||
@Test
|
||||
public void checkAllSet() {
|
||||
setOutDirs("r", "s", "r");
|
||||
checkOutDirs("r", "s", "r");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkRootOnly() {
|
||||
setOutDirs("out", null, null);
|
||||
checkOutDirs("out", "out/" + JadxArgs.DEFAULT_SRC_DIR, "out/" + JadxArgs.DEFAULT_RES_DIR);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkSrcOnly() {
|
||||
setOutDirs(null, "src", null);
|
||||
checkOutDirs("src", "src", "src/" + JadxArgs.DEFAULT_RES_DIR);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkResOnly() {
|
||||
setOutDirs(null, null, "res");
|
||||
checkOutDirs("res", "res/" + JadxArgs.DEFAULT_SRC_DIR, "res");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkNone() {
|
||||
setOutDirs(null, null, null);
|
||||
String inputFileBase = args.getInputFiles().get(0).getName().replace(".apk", "");
|
||||
checkOutDirs(inputFileBase,
|
||||
inputFileBase + "/" + JadxArgs.DEFAULT_SRC_DIR,
|
||||
inputFileBase + "/" + JadxArgs.DEFAULT_RES_DIR);
|
||||
}
|
||||
|
||||
private void setOutDirs(String outDir, String srcDir, String resDir) {
|
||||
args = makeArgs();
|
||||
args.setOutDir(toFile(outDir));
|
||||
args.setOutDirSrc(toFile(srcDir));
|
||||
args.setOutDirRes(toFile(resDir));
|
||||
LOG.debug("Set dirs: out={}, src={}, res={}", outDir, srcDir, resDir);
|
||||
}
|
||||
|
||||
private void checkOutDirs(String outDir, String srcDir, String resDir) {
|
||||
JadxArgsValidator.validate(args);
|
||||
LOG.debug("Got dirs: out={}, src={}, res={}", args.getOutDir(), args.getOutDirSrc(), args.getOutDirRes());
|
||||
assertThat(args.getOutDir(), is(toFile(outDir)));
|
||||
assertThat(args.getOutDirSrc(), is(toFile(srcDir)));
|
||||
assertThat(args.getOutDirRes(), is(toFile(resDir)));
|
||||
}
|
||||
|
||||
private JadxArgs makeArgs() {
|
||||
JadxArgs args = new JadxArgs();
|
||||
args.getInputFiles().add(FileUtils.createTempFile("some.apk"));
|
||||
return args;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package jadx.api;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
public class JadxDecompilerTest {
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testExampleUsage() {
|
||||
JadxArgs args = new JadxArgs();
|
||||
args.getInputFiles().add(new File("test.apk"));
|
||||
args.setOutDir(new File("jadx-test-output"));
|
||||
|
||||
JadxDecompiler jadx = new JadxDecompiler(args);
|
||||
jadx.load();
|
||||
jadx.save();
|
||||
}
|
||||
|
||||
// TODO make more tests
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.jar.JarOutputStream;
|
||||
@@ -29,7 +28,6 @@ import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.dex.visitors.DepthTraversal;
|
||||
import jadx.core.dex.visitors.IDexTreeVisitor;
|
||||
import jadx.core.utils.exceptions.CodegenException;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.tests.api.compiler.DynamicCompiler;
|
||||
import jadx.tests.api.compiler.StaticCompiler;
|
||||
import jadx.tests.api.utils.TestUtils;
|
||||
@@ -65,6 +63,7 @@ public abstract class IntegrationTest extends TestUtils {
|
||||
|
||||
public IntegrationTest() {
|
||||
args = new JadxArgs();
|
||||
args.setOutDir(new File(outDir));
|
||||
args.setShowInconsistentCode(true);
|
||||
args.setThreadsCount(1);
|
||||
args.setSkipResources(true);
|
||||
@@ -84,9 +83,10 @@ public abstract class IntegrationTest extends TestUtils {
|
||||
public ClassNode getClassNodeFromFile(File file, String clsName) {
|
||||
JadxDecompiler d = null;
|
||||
try {
|
||||
args.setInputFiles(Collections.singletonList(file));
|
||||
d = new JadxDecompiler(args);
|
||||
d.loadFile(file);
|
||||
} catch (JadxException e) {
|
||||
d.load();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
fail(e.getMessage());
|
||||
}
|
||||
@@ -114,18 +114,18 @@ public abstract class IntegrationTest extends TestUtils {
|
||||
}
|
||||
|
||||
private void decompile(JadxDecompiler jadx, ClassNode cls) {
|
||||
List<IDexTreeVisitor> passes = Jadx.getPassesList(jadx.getArgs(), new File(outDir));
|
||||
ProcessClass.process(cls, passes, new CodeGen(jadx.getArgs()));
|
||||
List<IDexTreeVisitor> passes = Jadx.getPassesList(jadx.getArgs());
|
||||
ProcessClass.process(cls, passes, new CodeGen());
|
||||
}
|
||||
|
||||
private void decompileWithoutUnload(JadxDecompiler d, ClassNode cls) {
|
||||
cls.load();
|
||||
List<IDexTreeVisitor> passes = Jadx.getPassesList(d.getArgs(), new File(outDir));
|
||||
List<IDexTreeVisitor> passes = Jadx.getPassesList(d.getArgs());
|
||||
for (IDexTreeVisitor visitor : passes) {
|
||||
DepthTraversal.visit(visitor, cls);
|
||||
}
|
||||
try {
|
||||
new CodeGen(d.getArgs()).visit(cls);
|
||||
new CodeGen().visit(cls);
|
||||
} catch (CodegenException e) {
|
||||
e.printStackTrace();
|
||||
fail(e.getMessage());
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package jadx.tests.functional;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -24,7 +23,7 @@ public class JadxVisitorsOrderTest {
|
||||
|
||||
@Test
|
||||
public void testOrder() {
|
||||
List<IDexTreeVisitor> passes = Jadx.getPassesList(new JadxArgs(), new File("out"));
|
||||
List<IDexTreeVisitor> passes = Jadx.getPassesList(new JadxArgs());
|
||||
|
||||
List<String> errors = check(passes);
|
||||
for (String str : errors) {
|
||||
|
||||
@@ -5,7 +5,6 @@ import javax.swing.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.settings.JadxSettingsAdapter;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
@@ -17,24 +16,17 @@ public class JadxGUI {
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
LogCollector.register();
|
||||
final JadxSettings jadxArgs = JadxSettingsAdapter.load();
|
||||
final JadxSettings settings = JadxSettingsAdapter.load();
|
||||
// overwrite loaded settings by command line arguments
|
||||
if (!jadxArgs.processArgs(args)) {
|
||||
if (!settings.processArgs(args)) {
|
||||
return;
|
||||
}
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
MainWindow window = new MainWindow(jadxArgs);
|
||||
window.open();
|
||||
}
|
||||
catch(JadxException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
MainWindow window = new MainWindow(settings);
|
||||
window.open();
|
||||
});
|
||||
} catch (Throwable e) {
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error: {}", e.getMessage(), e);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
@@ -2,38 +2,38 @@ package jadx.gui;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.IJadxArgs;
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.api.JavaClass;
|
||||
import jadx.api.JavaPackage;
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
|
||||
public class JadxWrapper {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JadxWrapper.class);
|
||||
|
||||
private final JadxDecompiler decompiler;
|
||||
private final JadxSettings settings;
|
||||
private JadxDecompiler decompiler;
|
||||
private File openFile;
|
||||
|
||||
public JadxWrapper(IJadxArgs jadxArgs) throws JadxException {
|
||||
this.decompiler = new JadxDecompiler(jadxArgs);
|
||||
public JadxWrapper(JadxSettings settings) {
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
public void openFile(File file) {
|
||||
this.openFile = file;
|
||||
try {
|
||||
this.decompiler.loadFile(file);
|
||||
} catch (DecodeException e) {
|
||||
LOG.error("Error decode file: {}", file, e);
|
||||
} catch (JadxException e) {
|
||||
LOG.error("Error open file: {}", file, e);
|
||||
this.decompiler = new JadxDecompiler(settings.toJadxArgs());
|
||||
this.decompiler.getArgs().setInputFiles(Collections.singletonList(file));
|
||||
this.decompiler.load();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error load file: {}", file, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public class JadxWrapper {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
decompiler.setOutputDir(dir);
|
||||
decompiler.getArgs().setRootDir(dir);
|
||||
ThreadPoolExecutor ex = (ThreadPoolExecutor) decompiler.getSaveExecutor();
|
||||
ex.shutdown();
|
||||
while (ex.isTerminating()) {
|
||||
@@ -53,7 +53,7 @@ public class JadxWrapper {
|
||||
}
|
||||
progressMonitor.close();
|
||||
LOG.info("done");
|
||||
} catch (InterruptedException|JadxException e) {
|
||||
} catch (InterruptedException e) {
|
||||
LOG.error("Save interrupted", e);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.util.Set;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.cli.JadxCLIArgs;
|
||||
|
||||
public class JadxSettings extends JadxCLIArgs {
|
||||
@@ -44,7 +45,7 @@ public class JadxSettings extends JadxCLIArgs {
|
||||
|
||||
public void fixOnLoad() {
|
||||
if (threadsCount <= 0) {
|
||||
threadsCount = DEFAULT_THREADS_COUNT;
|
||||
threadsCount = JadxArgs.DEFAULT_THREADS_COUNT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,8 +166,8 @@ public class JadxSettings extends JadxCLIArgs {
|
||||
this.deobfuscationForceSave = deobfuscationForceSave;
|
||||
}
|
||||
|
||||
public void setUseSourceNameAsClassAlias(boolean useSourceNameAsAlias) {
|
||||
this.deobfuscationUseSourceNameAsAlias = useSourceNameAsAlias;
|
||||
public void setDeobfuscationUseSourceNameAsAlias(boolean deobfuscationUseSourceNameAsAlias) {
|
||||
this.deobfuscationUseSourceNameAsAlias = deobfuscationUseSourceNameAsAlias;
|
||||
}
|
||||
|
||||
public void setEscapeUnicode(boolean escapeUnicode) {
|
||||
|
||||
@@ -145,10 +145,10 @@ public class JadxSettingsWindow extends JDialog {
|
||||
});
|
||||
|
||||
JCheckBox deobfSourceAlias = new JCheckBox();
|
||||
deobfSourceAlias.setSelected(settings.useSourceNameAsClassAlias());
|
||||
deobfSourceAlias.setSelected(settings.isDeobfuscationUseSourceNameAsAlias());
|
||||
deobfSourceAlias.addItemListener(new ItemListener() {
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
settings.setUseSourceNameAsClassAlias(e.getStateChange() == ItemEvent.SELECTED);
|
||||
settings.setDeobfuscationUseSourceNameAsAlias(e.getStateChange() == ItemEvent.SELECTED);
|
||||
needReload();
|
||||
}
|
||||
});
|
||||
@@ -219,6 +219,7 @@ public class JadxSettingsWindow extends JDialog {
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
settings.setThreadsCount((Integer) threadsCount.getValue());
|
||||
needReload();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -27,12 +27,13 @@ import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.gui.JadxWrapper;
|
||||
import jadx.gui.jobs.BackgroundWorker;
|
||||
import jadx.gui.jobs.DecompileJob;
|
||||
@@ -101,7 +102,7 @@ public class MainWindow extends JFrame {
|
||||
private transient ProgressPanel progressPane;
|
||||
private transient BackgroundWorker backgroundWorker;
|
||||
|
||||
public MainWindow(JadxSettings settings) throws JadxException {
|
||||
public MainWindow(JadxSettings settings) {
|
||||
this.wrapper = new JadxWrapper(settings);
|
||||
this.settings = settings;
|
||||
this.cacheObject = new CacheObject();
|
||||
@@ -119,10 +120,10 @@ public class MainWindow extends JFrame {
|
||||
setLocationRelativeTo(null);
|
||||
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
||||
|
||||
if (settings.getInput().isEmpty()) {
|
||||
if (settings.getFiles().isEmpty()) {
|
||||
openFile();
|
||||
} else {
|
||||
openFile(settings.getInput().get(0));
|
||||
openFile(new File(settings.getFiles().get(0)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -689,5 +690,4 @@ public class MainWindow extends JFrame {
|
||||
public void menuCanceled(MenuEvent e) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import jadx.api.Factory;
|
||||
import jadx.api.IJadxArgs;
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.api.JavaClass;
|
||||
import jadx.api.JavaPackage;
|
||||
@@ -30,7 +30,7 @@ public class JSourcesTest {
|
||||
when(root.isFlatPackages()).thenReturn(false);
|
||||
JadxWrapper wrapper = mock(JadxWrapper.class);
|
||||
sources = new JSources(root, wrapper);
|
||||
decompiler = new JadxDecompiler(mock(IJadxArgs.class));
|
||||
decompiler = new JadxDecompiler(new JadxArgs());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user