diff --git a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/DexFileLoader.java b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/DexFileLoader.java index b2d76e868..f1dcbb905 100644 --- a/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/DexFileLoader.java +++ b/jadx-plugins/jadx-dex-input/src/main/java/jadx/plugins/input/dex/DexFileLoader.java @@ -13,9 +13,11 @@ import java.util.Collections; import java.util.List; import java.util.stream.Collectors; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import jadx.api.plugins.utils.CommonFileUtils; import jadx.api.plugins.utils.ZipSecurity; import jadx.plugins.input.dex.sections.DexConsts; import jadx.plugins.input.dex.utils.DexCheckSum; @@ -45,31 +47,34 @@ public class DexFileLoader { private List loadDexFromFile(File file) { try (InputStream inputStream = new FileInputStream(file)) { - return checkFileMagic(file, inputStream, file.getAbsolutePath()); + return load(file, inputStream, file.getAbsolutePath()); } catch (Exception e) { LOG.error("File open error: {}", file.getAbsolutePath(), e); return Collections.emptyList(); } } - private List checkFileMagic(File file, InputStream inputStream, String inputFileName) throws IOException { + private List load(@Nullable File file, InputStream inputStream, String fileName) throws IOException { try (InputStream in = inputStream.markSupported() ? inputStream : new BufferedInputStream(inputStream)) { byte[] magic = new byte[DexConsts.MAX_MAGIC_SIZE]; in.mark(magic.length); if (in.read(magic) != magic.length) { return Collections.emptyList(); } - if (isStartWithBytes(magic, DexConsts.DEX_FILE_MAGIC)) { + if (isStartWithBytes(magic, DexConsts.DEX_FILE_MAGIC) || fileName.endsWith(".dex")) { in.reset(); byte[] content = readAllBytes(in); if (options.isVerifyChecksum()) { DexCheckSum.verify(content); } - DexReader dexReader = new DexReader(getNextUniqId(), inputFileName, content); + DexReader dexReader = new DexReader(getNextUniqId(), fileName, content); return Collections.singletonList(dexReader); } - if (file != null && isStartWithBytes(magic, DexConsts.ZIP_FILE_MAGIC)) { - return collectDexFromZip(file); + if (file != null) { + // allow only top level zip files + if (isStartWithBytes(magic, DexConsts.ZIP_FILE_MAGIC) || CommonFileUtils.isZipFileExt(fileName)) { + return collectDexFromZip(file); + } } return Collections.emptyList(); } @@ -80,7 +85,7 @@ public class DexFileLoader { try { ZipSecurity.readZipEntries(file, (entry, in) -> { try { - result.addAll(checkFileMagic(null, in, entry.getName())); + result.addAll(load(null, in, entry.getName())); } catch (Exception e) { LOG.error("Failed to read zip entry: {}", entry, e); } diff --git a/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/JavaFileLoader.java b/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/JavaFileLoader.java index 532b4d0fb..073b079e6 100644 --- a/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/JavaFileLoader.java +++ b/jadx-plugins/jadx-java-input/src/main/java/jadx/plugins/input/java/JavaFileLoader.java @@ -52,18 +52,20 @@ public class JavaFileLoader { if (in.read(magic) != magic.length) { return Collections.emptyList(); } - if (isStartWithBytes(magic, JAVA_CLASS_FILE_MAGIC)) { + if (isStartWithBytes(magic, JAVA_CLASS_FILE_MAGIC) || name.endsWith(".class")) { byte[] data = CommonFileUtils.loadBytes(magic, in); String source = concatSource(parentFileName, name); JavaClassReader reader = new JavaClassReader(getNextUniqId(), source, data); return Collections.singletonList(reader); } - if (isStartWithBytes(magic, ZIP_FILE_MAGIC)) { + if (isStartWithBytes(magic, ZIP_FILE_MAGIC) || CommonFileUtils.isZipFileExt(name)) { if (file != null) { return collectFromZip(file, name); } File zipFile = CommonFileUtils.saveToTempFile(magic, in, ".zip").toFile(); - return collectFromZip(zipFile, concatSource(parentFileName, name)); + List readers = collectFromZip(zipFile, concatSource(parentFileName, name)); + CommonFileUtils.safeDeleteFile(zipFile); + return readers; } return Collections.emptyList(); } diff --git a/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/utils/CommonFileUtils.java b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/utils/CommonFileUtils.java index 8ddc1b614..7ffa18627 100644 --- a/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/utils/CommonFileUtils.java +++ b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/utils/CommonFileUtils.java @@ -7,9 +7,15 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Set; + +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class CommonFileUtils { + private static final Logger LOG = LoggerFactory.getLogger(CommonFileUtils.class); public static final File CWD = getCWD(); public static final Path CWD_PATH = CWD.toPath(); @@ -38,6 +44,15 @@ public class CommonFileUtils { return tempFile; } + public static boolean safeDeleteFile(File file) { + try { + return file.delete(); + } catch (Exception e) { + LOG.warn("Failed to delete file: {}", file, e); + return false; + } + } + public static byte[] loadBytes(InputStream input) throws IOException { return loadBytes(null, input); } @@ -56,7 +71,7 @@ public class CommonFileUtils { } public static void copyStream(InputStream input, OutputStream output) throws IOException { - byte[] buffer = new byte[8 * 1024]; + byte[] buffer = new byte[8192]; while (true) { int count = input.read(buffer); if (count == -1) { @@ -65,4 +80,23 @@ public class CommonFileUtils { output.write(buffer, 0, count); } } + + @Nullable + public static String getFileExtension(String fileName) { + int dotIndex = fileName.lastIndexOf('.'); + if (dotIndex == -1) { + return null; + } + return fileName.substring(dotIndex + 1); + } + + private static final Set ZIP_FILE_EXTS = Utils.constSet("zip", "jar", "apk"); + + public static boolean isZipFileExt(String fileName) { + String ext = getFileExtension(fileName); + if (ext == null) { + return false; + } + return ZIP_FILE_EXTS.contains(ext); + } } diff --git a/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/utils/Utils.java b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/utils/Utils.java index 447a4ee06..0af05e9bb 100644 --- a/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/utils/Utils.java +++ b/jadx-plugins/jadx-plugins-api/src/main/java/jadx/api/plugins/utils/Utils.java @@ -1,8 +1,10 @@ package jadx.api.plugins.utils; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; @@ -87,4 +89,9 @@ public class Utils { public static String formatOffset(int offset) { return String.format("0x%04x", offset); } + + @SafeVarargs + public static Set constSet(T... arr) { + return Collections.unmodifiableSet(new HashSet<>(Arrays.asList(arr))); + } }