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