Commit 79ccaada authored by Ahmed Ashour's avatar Ahmed Ashour Committed by skylot

fix: handle big .jar files (using multi-dex option) (#390) (PR #568)

parent ecaa87e7
package jadx.core.utils.files;
import static jadx.core.utils.files.FileUtils.isApkFile;
import static jadx.core.utils.files.FileUtils.isZipDexFile;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
......@@ -11,20 +16,18 @@ import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import com.android.dex.Dex;
import org.apache.commons.io.IOUtils;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.android.dex.Dex;
import jadx.core.utils.AsmUtils;
import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.exceptions.JadxRuntimeException;
import static jadx.core.utils.files.FileUtils.isApkFile;
import static jadx.core.utils.files.FileUtils.isZipDexFile;
public class InputFile {
private static final Logger LOG = LoggerFactory.getLogger(InputFile.class);
......@@ -52,7 +55,9 @@ public class InputFile {
return;
}
if (fileName.endsWith(".class")) {
addDexFile(loadFromClassFile(file));
for (Dex dex : loadFromClassFile(file)) {
addDexFile(dex);
}
return;
}
if (isApkFile(file) || isZipDexFile(file)) {
......@@ -65,7 +70,9 @@ public class InputFile {
return;
}
if (fileName.endsWith(".jar")) {
addDexFile(loadFromJar(file));
for (Dex dex : loadFromJar(file.toPath())) {
addDexFile(dex);
}
return;
}
if (fileName.endsWith(".aar")) {
......@@ -116,11 +123,11 @@ public class InputFile {
case ".jar":
index++;
File jarFile = FileUtils.createTempFile(entryName);
try (FileOutputStream fos = new FileOutputStream(jarFile)) {
IOUtils.copy(inputStream, fos);
Path jarFile = Files.createTempFile(entryName, ".jar");
Files.copy(inputStream, jarFile);
for (Dex dex : loadFromJar(jarFile)) {
addDexFile(entryName, dex);
}
addDexFile(entryName, loadFromJar(jarFile));
break;
default:
......@@ -155,15 +162,19 @@ public class InputFile {
}
}
private static Dex loadFromJar(File jarFile) throws DecodeException {
private static List<Dex> loadFromJar(Path jar) throws DecodeException {
JavaToDex j2d = new JavaToDex();
try {
LOG.info("converting to dex: {} ...", jarFile.getName());
byte[] ba = j2d.convert(jarFile.getAbsolutePath());
if (ba.length == 0) {
LOG.info("converting to dex: {} ...", jar.getFileName());
List<byte[]> byteList = j2d.convert(jar);
if (byteList.isEmpty()) {
throw new JadxException("Empty dx output");
}
return new Dex(ba);
List<Dex> dexList = new ArrayList<>(byteList.size());
for (byte[] b : byteList) {
dexList.add(new Dex(b));
}
return dexList;
} catch (Exception e) {
throw new DecodeException("java class to dex conversion error:\n " + e.getMessage(), e);
} finally {
......@@ -173,9 +184,9 @@ public class InputFile {
}
}
private static Dex loadFromClassFile(File file) throws IOException, DecodeException {
File outFile = FileUtils.createTempFile("cls.jar");
try (JarOutputStream jo = new JarOutputStream(new FileOutputStream(outFile))) {
private static List<Dex> loadFromClassFile(File file) throws IOException, DecodeException {
Path outFile = Files.createTempFile("cls", ".jar");
try (JarOutputStream jo = new JarOutputStream(Files.newOutputStream(outFile))) {
String clsName = AsmUtils.getNameFromClassFile(file);
if (clsName == null || !ZipSecurity.isValidZipEntryName(clsName)) {
throw new IOException("Can't read class name from file: " + file);
......
package jadx.core.utils.files;
import java.io.ByteArrayOutputStream;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import com.android.dx.command.dexer.DxContext;
import com.android.dx.command.dexer.Main;
......@@ -13,11 +18,12 @@ public class JavaToDex {
private static final String CHARSET_NAME = "UTF-8";
private static class DxArgs extends Arguments {
public DxArgs(DxContext context, String dexFile, String[] input) {
public DxArgs(DxContext context, String dexDir, String[] input) {
super(context);
outName = dexFile;
outName = dexDir;
fileNames = input;
jarOutput = false;
multiDex = true;
optimize = true;
localInfo = true;
......@@ -31,17 +37,29 @@ public class JavaToDex {
private String dxErrors;
public byte[] convert(String javaFile) throws JadxException {
public List<byte[]> convert(Path jar) throws JadxException {
try (ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayOutputStream errOut = new ByteArrayOutputStream()) {
DxContext context = new DxContext(out, errOut);
DxArgs args = new DxArgs(context, "-", new String[]{javaFile});
int result = (new Main(context)).runDx(args);
Path dir = Files.createTempDirectory("jadx");
DxArgs args = new DxArgs(
context,
dir.toAbsolutePath().toString(),
new String[]{jar.toAbsolutePath().toString()});
int result = new Main(context).runDx(args);
dxErrors = errOut.toString(CHARSET_NAME);
if (result != 0) {
throw new JadxException("Java to dex conversion error, code: " + result);
}
return out.toByteArray();
List<byte[]> list = new ArrayList<>();
try (DirectoryStream<Path> ds = Files.newDirectoryStream(dir)) {
for (Path child : ds) {
list.add(Files.readAllBytes(child));
Files.delete(child);
}
}
Files.delete(dir);
return list;
} catch (Exception e) {
throw new JadxException("dx exception: " + e.getMessage(), e);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment