refactor: use own dex parser instead deprecated dx lib

This commit is contained in:
Skylot
2020-04-29 17:24:44 +01:00
parent 09e267f8bc
commit 0d69e0ac97
181 changed files with 6779 additions and 3072 deletions
@@ -0,0 +1,67 @@
package jadx.plugins.input.javaconvert;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ConvertResult {
private static final Logger LOG = LoggerFactory.getLogger(ConvertResult.class);
private final List<Path> converted = new ArrayList<>();
private final List<Path> tmpPaths = new ArrayList<>();
public List<Path> getConverted() {
return converted;
}
public void addConvertedFiles(List<Path> paths) {
converted.addAll(paths);
}
public void addTempPath(Path path) {
tmpPaths.add(path);
}
public boolean isEmpty() {
return converted.isEmpty();
}
public void deleteTemp() {
for (Path tmpPath : tmpPaths) {
try {
delete(tmpPath);
} catch (Exception e) {
LOG.warn("Failed to delete temp path: {}", tmpPath, e);
}
}
}
@SuppressWarnings("ResultOfMethodCallIgnored")
private static void delete(Path path) throws IOException {
if (Files.isRegularFile(path)) {
Files.delete(path);
return;
}
if (Files.isDirectory(path)) {
try (Stream<Path> pathStream = Files.walk(path)) {
pathStream
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
}
}
}
@Override
public String toString() {
return "ConvertResult{converted=" + converted + ", tmpPaths=" + tmpPaths + '}';
}
}
@@ -0,0 +1,53 @@
package jadx.plugins.input.javaconvert;
import java.nio.file.Path;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.D8;
import com.android.tools.r8.D8Command;
import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.DiagnosticsHandler;
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 {
D8Command d8Command = D8Command.builder(new LogHandler())
.addProgramFiles(path)
.setOutput(tempDirectory, OutputMode.DexIndexed)
.setMode(CompilationMode.DEBUG)
.setMinApiLevel(30)
.setIntermediate(true)
.setEnableDesugaring(false)
.build();
D8.run(d8Command);
}
private static class LogHandler implements DiagnosticsHandler {
@Override
public void error(Diagnostic diagnostic) {
LOG.error("D8 error: {}", format(diagnostic));
}
@Override
public void warning(Diagnostic diagnostic) {
LOG.warn("D8 warning: {}", format(diagnostic));
}
@Override
public void info(Diagnostic diagnostic) {
LOG.info("D8 info: {}", format(diagnostic));
}
public static String format(Diagnostic diagnostic) {
return diagnostic.getDiagnosticMessage()
+ ", origin: " + diagnostic.getOrigin()
+ ", position: " + diagnostic.getPosition();
}
}
}
@@ -0,0 +1,49 @@
package jadx.plugins.input.javaconvert;
import java.io.ByteArrayOutputStream;
import java.nio.file.Path;
import com.android.dx.command.dexer.DxContext;
import com.android.dx.command.dexer.Main;
public class DxConverter {
private static final String CHARSET_NAME = "UTF-8";
private static class DxArgs extends com.android.dx.command.dexer.Main.Arguments {
public DxArgs(DxContext context, String dexDir, String[] input) {
super(context);
outName = dexDir;
fileNames = input;
jarOutput = false;
multiDex = true;
optimize = true;
localInfo = true;
coreLibrary = true;
debug = true;
warnings = true;
minSdkVersion = 28;
}
}
public static void run(Path path, Path tempDirectory) {
int result;
String dxErrors;
try (ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayOutputStream errOut = new ByteArrayOutputStream()) {
DxContext context = new DxContext(out, errOut);
DxArgs args = new DxArgs(
context,
tempDirectory.toAbsolutePath().toString(),
new String[] { path.toAbsolutePath().toString() });
result = new Main(context).runDx(args);
dxErrors = errOut.toString(CHARSET_NAME);
} catch (Exception e) {
throw new RuntimeException("dx exception: " + e.getMessage(), e);
}
if (result != 0) {
throw new RuntimeException("Java to dex conversion error, code: " + result + "\n errors: " + dxErrors);
}
}
}
@@ -0,0 +1,52 @@
package jadx.plugins.input.javaconvert;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class JavaConvertLoader {
private static final Logger LOG = LoggerFactory.getLogger(JavaConvertLoader.class);
public static ConvertResult process(List<Path> input) {
ConvertResult result = new ConvertResult();
for (Path path : input) {
if (isJavaFile(path)) {
try {
convert(result, path);
} catch (Exception e) {
LOG.error("Failed to convert file: " + path.toAbsolutePath(), e);
}
}
}
return result;
}
private static boolean isJavaFile(Path path) {
String fileName = path.getFileName().toString();
return fileName.endsWith(".jar")
|| fileName.endsWith(".class");
}
private static void convert(ConvertResult result, Path path) throws Exception {
Path tempDirectory = Files.createTempDirectory("jadx-");
result.addTempPath(tempDirectory);
// D8Converter.run(path, tempDirectory);
DxConverter.run(path, tempDirectory);
LOG.debug("Converted to dex: {}", path.toAbsolutePath());
result.addConvertedFiles(collectFilesInDir(tempDirectory));
}
private static List<Path> collectFilesInDir(Path tempDirectory) throws IOException {
try (Stream<Path> pathStream = Files.walk(tempDirectory)) {
return pathStream.collect(Collectors.toList());
}
}
}
@@ -0,0 +1,38 @@
package jadx.plugins.input.javaconvert;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
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.plugins.input.dex.DexFileLoader;
import jadx.plugins.input.dex.DexLoadResult;
import jadx.plugins.input.dex.DexReader;
public class JavaConvertPlugin implements JadxInputPlugin {
@Override
public JadxPluginInfo getPluginInfo() {
return new JadxPluginInfo("java-convert", "JavaConvert", "Convert .jar and .class files to dex");
}
@Override
public ILoadResult loadFiles(List<Path> input) {
ConvertResult result = JavaConvertLoader.process(input);
if (result.isEmpty()) {
result.deleteTemp();
return EmptyLoadResult.INSTANCE;
}
List<DexReader> dexReaders = DexFileLoader.collectDexFiles(result.getConverted());
return new DexLoadResult(dexReaders) {
@Override
public void close() throws IOException {
super.close();
result.deleteTemp();
}
};
}
}
@@ -0,0 +1 @@
jadx.plugins.input.javaconvert.JavaConvertPlugin