feat: add options for java-convert plugin

This commit is contained in:
Skylot
2022-03-02 13:43:16 +00:00
parent c54dd77f35
commit 94fb91cec6
10 changed files with 238 additions and 61 deletions
@@ -16,14 +16,14 @@ import com.android.tools.r8.OutputMode;
public class D8Converter {
private static final Logger LOG = LoggerFactory.getLogger(D8Converter.class);
public static void run(Path path, Path tempDirectory) throws CompilationFailedException {
public static void run(Path path, Path tempDirectory, JavaConvertOptions options) throws CompilationFailedException {
D8Command d8Command = D8Command.builder(new LogHandler())
.addProgramFiles(path)
.setOutput(tempDirectory, OutputMode.DexIndexed)
.setMode(CompilationMode.DEBUG)
.setMinApiLevel(30)
.setIntermediate(true)
.setDisableDesugaring(true)
.setDisableDesugaring(!options.isD8Desugar())
.build();
D8.run(d8Command);
}
@@ -23,7 +23,13 @@ import jadx.api.plugins.utils.ZipSecurity;
public class JavaConvertLoader {
private static final Logger LOG = LoggerFactory.getLogger(JavaConvertLoader.class);
public static ConvertResult process(List<Path> input) {
private final JavaConvertOptions options;
public JavaConvertLoader(JavaConvertOptions options) {
this.options = options;
}
public ConvertResult process(List<Path> input) {
ConvertResult result = new ConvertResult();
processJars(input, result);
processAars(input, result);
@@ -31,7 +37,7 @@ public class JavaConvertLoader {
return result;
}
private static void processJars(List<Path> input, ConvertResult result) {
private void processJars(List<Path> input, ConvertResult result) {
PathMatcher jarMatcher = FileSystems.getDefault().getPathMatcher("glob:**.jar");
input.stream()
.filter(jarMatcher::matches)
@@ -44,7 +50,7 @@ public class JavaConvertLoader {
});
}
private static void processClassFiles(List<Path> input, ConvertResult result) {
private void processClassFiles(List<Path> input, ConvertResult result) {
PathMatcher jarMatcher = FileSystems.getDefault().getPathMatcher("glob:**.class");
List<Path> clsFiles = input.stream()
.filter(jarMatcher::matches)
@@ -72,7 +78,7 @@ public class JavaConvertLoader {
}
}
private static void processAars(List<Path> input, ConvertResult result) {
private void processAars(List<Path> input, ConvertResult result) {
PathMatcher aarMatcher = FileSystems.getDefault().getPathMatcher("glob:**.aar");
input.stream()
.filter(aarMatcher::matches)
@@ -91,14 +97,14 @@ public class JavaConvertLoader {
}));
}
private static void convertJar(ConvertResult result, Path path) throws Exception {
private void convertJar(ConvertResult result, Path path) throws Exception {
if (repackAndConvertJar(result, path)) {
return;
}
convertSimpleJar(result, path);
}
private static boolean repackAndConvertJar(ConvertResult result, Path path) throws Exception {
private boolean repackAndConvertJar(ConvertResult result, Path path) throws Exception {
// check if jar need a full repackage
Boolean repackNeeded = ZipSecurity.visitZipEntries(path.toFile(), (zipFile, zipEntry) -> {
String entryName = zipEntry.getName();
@@ -154,25 +160,50 @@ public class JavaConvertLoader {
return true;
}
private static void convertSimpleJar(ConvertResult result, Path path) throws Exception {
private void convertSimpleJar(ConvertResult result, Path path) throws Exception {
Path tempDirectory = Files.createTempDirectory("jadx-");
result.addTempPath(tempDirectory);
LOG.debug("Converting to dex ...");
try {
DxConverter.run(path, tempDirectory);
} catch (Throwable e) {
LOG.warn("DX convert failed, trying D8, path: {}", path);
try {
D8Converter.run(path, tempDirectory);
} catch (Throwable ex) {
LOG.error("D8 convert failed: {}", ex.getMessage());
}
}
convert(path, tempDirectory);
List<Path> dexFiles = collectFilesInDir(tempDirectory);
LOG.debug("Converted {} to {} dex", path.toAbsolutePath(), dexFiles.size());
result.addConvertedFiles(dexFiles);
}
private void convert(Path path, Path tempDirectory) {
JavaConvertOptions.Mode mode = options.getMode();
switch (mode) {
case DX:
try {
DxConverter.run(path, tempDirectory);
} catch (Throwable e) {
LOG.error("DX convert failed, path: {}", path, e);
}
break;
case D8:
try {
D8Converter.run(path, tempDirectory, options);
} catch (Throwable e) {
LOG.error("D8 convert failed, path: {}", path, e);
}
break;
case BOTH:
try {
DxConverter.run(path, tempDirectory);
} catch (Throwable e) {
LOG.warn("DX convert failed, trying D8, path: {}", path);
try {
D8Converter.run(path, tempDirectory, options);
} catch (Throwable ex) {
LOG.error("D8 convert failed: {}", ex.getMessage());
}
}
break;
}
}
private static List<Path> collectFilesInDir(Path tempDirectory) throws IOException {
PathMatcher dexMatcher = FileSystems.getDefault().getPathMatcher("glob:**.dex");
try (Stream<Path> pathStream = Files.walk(tempDirectory, 1)) {
@@ -0,0 +1,50 @@
package jadx.plugins.input.javaconvert;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import jadx.api.plugins.options.OptionDescription;
import jadx.api.plugins.options.impl.BaseOptionsParser;
import jadx.api.plugins.options.impl.JadxOptionDescription;
public class JavaConvertOptions extends BaseOptionsParser {
private static final String MODE_OPT = JavaConvertPlugin.PLUGIN_ID + ".mode";
private static final String D8_DESUGAR_OPT = JavaConvertPlugin.PLUGIN_ID + ".d8-desugar";
public enum Mode {
DX, D8, BOTH
}
private Mode mode = Mode.BOTH;
private boolean d8Desugar = false;
public void apply(Map<String, String> options) {
mode = getOption(options, MODE_OPT, name -> Mode.valueOf(name.toUpperCase(Locale.ROOT)), Mode.BOTH);
d8Desugar = getBooleanOption(options, D8_DESUGAR_OPT, false);
}
public List<OptionDescription> buildOptionsDescriptions() {
return Arrays.asList(
new JadxOptionDescription(
MODE_OPT,
"Convert mode",
"both",
Arrays.asList("dx", "d8", "both")),
new JadxOptionDescription(
D8_DESUGAR_OPT,
"Use desugar in d8",
"no",
Arrays.asList("yes", "no")));
}
public Mode getMode() {
return mode;
}
public boolean isD8Desugar() {
return d8Desugar;
}
}
@@ -2,21 +2,28 @@ package jadx.plugins.input.javaconvert;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import jadx.api.plugins.JadxPluginInfo;
import jadx.api.plugins.input.JadxInputPlugin;
import jadx.api.plugins.input.data.ILoadResult;
import jadx.api.plugins.input.data.impl.EmptyLoadResult;
import jadx.api.plugins.options.JadxPluginOptions;
import jadx.api.plugins.options.OptionDescription;
import jadx.plugins.input.dex.DexInputPlugin;
public class JavaConvertPlugin implements JadxInputPlugin {
public class JavaConvertPlugin implements JadxInputPlugin, JadxPluginOptions {
public static final String PLUGIN_ID = "java-convert";
private final DexInputPlugin dexInput = new DexInputPlugin();
private final JavaConvertOptions options = new JavaConvertOptions();
private final JavaConvertLoader loader = new JavaConvertLoader(options);
@Override
public JadxPluginInfo getPluginInfo() {
return new JadxPluginInfo(
"java-convert",
PLUGIN_ID,
"JavaConvert",
"Convert .jar and .class files to dex",
"java-input");
@@ -24,11 +31,21 @@ public class JavaConvertPlugin implements JadxInputPlugin {
@Override
public ILoadResult loadFiles(List<Path> input) {
ConvertResult result = JavaConvertLoader.process(input);
ConvertResult result = loader.process(input);
if (result.isEmpty()) {
result.close();
return EmptyLoadResult.INSTANCE;
}
return dexInput.loadFiles(result.getConverted(), result);
}
@Override
public void setOptions(Map<String, String> options) {
this.options.apply(options);
}
@Override
public List<OptionDescription> getOptionsDescriptions() {
return this.options.buildOptionsDescriptions();
}
}