From 79ccaadaff18de7443491dbc333e71cfa6e3e3d5 Mon Sep 17 00:00:00 2001 From: Ahmed Ashour Date: Mon, 8 Apr 2019 16:20:04 +0200 Subject: [PATCH] fix: handle big .jar files (using multi-dex option) (#390) (PR #568) --- .../java/jadx/core/utils/files/InputFile.java | 47 ++++++++++++------- .../java/jadx/core/utils/files/JavaToDex.java | 30 +++++++++--- 2 files changed, 53 insertions(+), 24 deletions(-) diff --git a/jadx-core/src/main/java/jadx/core/utils/files/InputFile.java b/jadx-core/src/main/java/jadx/core/utils/files/InputFile.java index 470d26118..e27a7791d 100644 --- a/jadx-core/src/main/java/jadx/core/utils/files/InputFile.java +++ b/jadx-core/src/main/java/jadx/core/utils/files/InputFile.java @@ -1,9 +1,14 @@ 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 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 byteList = j2d.convert(jar); + if (byteList.isEmpty()) { throw new JadxException("Empty dx output"); } - return new Dex(ba); + List 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 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); diff --git a/jadx-core/src/main/java/jadx/core/utils/files/JavaToDex.java b/jadx-core/src/main/java/jadx/core/utils/files/JavaToDex.java index c4898b4be..ed2294e33 100644 --- a/jadx-core/src/main/java/jadx/core/utils/files/JavaToDex.java +++ b/jadx-core/src/main/java/jadx/core/utils/files/JavaToDex.java @@ -1,6 +1,11 @@ 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 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 list = new ArrayList<>(); + try (DirectoryStream 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); }