From c3f7a049d829b8e08db9cd5a5636771789e3b5b2 Mon Sep 17 00:00:00 2001 From: Skylot Date: Mon, 8 Jul 2019 12:24:54 +0300 Subject: [PATCH] fix: ignore incorrect dex files in apk (#700) --- .../java/jadx/core/utils/files/InputFile.java | 52 +++++++++++------- .../jadx/core/utils/files/InputFileTest.java | 37 +++++++++++++ .../test-samples/app-with-fake-dex.apk | Bin 0 -> 10030 bytes 3 files changed, 69 insertions(+), 20 deletions(-) create mode 100644 jadx-core/src/test/java/jadx/core/utils/files/InputFileTest.java create mode 100644 jadx-core/src/test/resources/test-samples/app-with-fake-dex.apk 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 e5b22039e9e..beefc7826d1 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 @@ -20,6 +20,7 @@ import org.slf4j.LoggerFactory; import com.android.dex.Dex; +import com.android.dex.DexException; import jadx.core.utils.AsmUtils; import jadx.core.utils.exceptions.DecodeException; @@ -52,7 +53,7 @@ private void searchDexFiles(boolean skipSources) throws IOException, DecodeExcep String fileName = file.getName(); if (fileName.endsWith(".dex")) { - addDexFile(fileName, new Dex(file), file.toPath()); + addDexFile(fileName, file.toPath()); return; } if (fileName.endsWith(".smali")) { @@ -60,12 +61,12 @@ private void searchDexFiles(boolean skipSources) throws IOException, DecodeExcep SmaliOptions options = new SmaliOptions(); options.outputDexFile = output.toAbsolutePath().toString(); Smali.assemble(options, file.getAbsolutePath()); - addDexFile("", new Dex(output.toFile()), output); + addDexFile(fileName, output); return; } if (fileName.endsWith(".class")) { for (Path path : loadFromClassFile(file)) { - addDexFile(path); + addDexFile(fileName, path); } return; } @@ -80,7 +81,7 @@ private void searchDexFiles(boolean skipSources) throws IOException, DecodeExcep } if (fileName.endsWith(".jar")) { for (Path path : loadFromJar(file.toPath())) { - addDexFile(path); + addDexFile(fileName, path); } return; } @@ -96,18 +97,6 @@ private void searchDexFiles(boolean skipSources) throws IOException, DecodeExcep LOG.warn("No dex files found in {}", file); } - private void addDexFile(Path path) throws IOException { - addDexFile(path.getFileName().toString(), path); - } - - private void addDexFile(String fileName, Path path) throws IOException { - addDexFile(fileName, new Dex(Files.readAllBytes(path)), path); - } - - private void addDexFile(String fileName, Dex dexBuf, Path path) { - dexFiles.add(new DexFile(this, fileName, dexBuf, path)); - } - private boolean loadFromZip(String ext) throws IOException, DecodeException { int index = 0; try (ZipFile zf = new ZipFile(file)) { @@ -127,9 +116,8 @@ private boolean loadFromZip(String ext) throws IOException, DecodeException { || entryName.endsWith(instantRunDexSuffix)) { switch (ext) { case ".dex": - Path path = makeDexBuf(entryName, inputStream); - if (path != null) { - addDexFile(entryName, path); + Path path = copyToTmpDex(entryName, inputStream); + if (addDexFile(entryName, path)) { index++; } break; @@ -163,8 +151,32 @@ private boolean loadFromZip(String ext) throws IOException, DecodeException { return index > 0; } + private boolean addDexFile(String entryName, @Nullable Path filePath) { + if (filePath == null) { + return false; + } + Dex dexBuf = loadDexBufFromPath(filePath, entryName); + if (dexBuf == null) { + return false; + } + dexFiles.add(new DexFile(this, entryName, dexBuf, filePath)); + return true; + } + + @Nullable + private Dex loadDexBufFromPath(Path path, String entryName) { + try { + return new Dex(Files.readAllBytes(path)); + } catch (DexException e) { + LOG.error("Failed to load dex file: {}, error: {}", entryName, e.getMessage()); + } catch (Exception e) { + LOG.error("Failed to load dex file: {}, error: {}", entryName, e.getMessage(), e); + } + return null; + } + @Nullable - private Path makeDexBuf(String entryName, InputStream inputStream) { + private Path copyToTmpDex(String entryName, InputStream inputStream) { try { Path path = FileUtils.createTempFile(".dex"); Files.copy(inputStream, path, StandardCopyOption.REPLACE_EXISTING); diff --git a/jadx-core/src/test/java/jadx/core/utils/files/InputFileTest.java b/jadx-core/src/test/java/jadx/core/utils/files/InputFileTest.java new file mode 100644 index 00000000000..7f4f60fa5f2 --- /dev/null +++ b/jadx-core/src/test/java/jadx/core/utils/files/InputFileTest.java @@ -0,0 +1,37 @@ +package jadx.core.utils.files; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import jadx.core.utils.exceptions.DecodeException; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.notNullValue; + +class InputFileTest { + private static final String TEST_SAMPLES_DIR = "test-samples/"; + + @Test + public void testApkWithFakeDex() throws IOException, DecodeException { + File sample = getFileFromSampleDir("app-with-fake-dex.apk"); + + List list = new ArrayList<>(); + InputFile.addFilesFrom(sample, list, false); + assertThat(list, hasSize(1)); + InputFile inputFile = list.get(0); + assertThat(inputFile.getDexFiles(), hasSize(1)); + } + + private static File getFileFromSampleDir(String fileName) { + URL resource = InputFileTest.class.getClassLoader().getResource(TEST_SAMPLES_DIR + fileName); + assertThat(resource, notNullValue()); + String pathStr = resource.getFile(); + return new File(pathStr); + } +} diff --git a/jadx-core/src/test/resources/test-samples/app-with-fake-dex.apk b/jadx-core/src/test/resources/test-samples/app-with-fake-dex.apk new file mode 100644 index 0000000000000000000000000000000000000000..0a6969ad69c712a10b945a720ba7c24b6060ca50 GIT binary patch literal 10030 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 literal 0 HcmV?d00001