From d79724549c8a2eeb928d5786dc0c7c38db599a6a Mon Sep 17 00:00:00 2001 From: Daniel Malmqvist Date: Wed, 3 Apr 2019 15:50:43 +0200 Subject: [PATCH] Added new option to skip non classes[0-9]*.dex files --- .../src/main/java/brut/apktool/Main.java | 9 ++ .../main/java/brut/androlib/ApkDecoder.java | 11 ++- .../androlib/decode/DuplicateDexTest.java | 79 ++++++++++++++++++ .../decode/duplicatedex/duplicatedex.apk | Bin 0 -> 13091 bytes 4 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/DuplicateDexTest.java create mode 100644 brut.apktool/apktool-lib/src/test/resources/decode/duplicatedex/duplicatedex.apk diff --git a/brut.apktool/apktool-cli/src/main/java/brut/apktool/Main.java b/brut.apktool/apktool-cli/src/main/java/brut/apktool/Main.java index d94ccfb4..ff8a4529 100644 --- a/brut.apktool/apktool-cli/src/main/java/brut/apktool/Main.java +++ b/brut.apktool/apktool-cli/src/main/java/brut/apktool/Main.java @@ -112,6 +112,9 @@ public class Main { if (cli.hasOption("s") || cli.hasOption("no-src")) { decoder.setDecodeSources(ApkDecoder.DECODE_SOURCES_NONE); } + if (cli.hasOption("onlymainclasses")) { + decoder.setDecodeSources(ApkDecoder.DECODE_SOURCES_SMALI_ONLY_MAIN_CLASSES); + } if (cli.hasOption("d") || cli.hasOption("debug")) { System.err.println("SmaliDebugging has been removed in 2.1.0 onward. Please see: https://github.com/iBotPeaches/Apktool/issues/1061"); System.exit(1); @@ -309,6 +312,11 @@ public class Main { .desc("Do not decode sources.") .build(); + Option onlyMainClassesOption = Option.builder("onlymainclasses") + .longOpt("only-main-classes") + .desc("Only decompile the main dex classes (classes[0-9]*.dex) in the root.") + .build(); + Option noResOption = Option.builder("r") .longOpt("no-res") .desc("Do not decode resources.") @@ -468,6 +476,7 @@ public class Main { DecodeOptions.addOption(frameDirOption); DecodeOptions.addOption(forceDecOption); DecodeOptions.addOption(noSrcOption); + DecodeOptions.addOption(onlyMainClassesOption); DecodeOptions.addOption(noResOption); // add basic build options diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java index 4a7e1fc6..ce6939fa 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java @@ -144,6 +144,7 @@ public class ApkDecoder { mAndrolib.decodeSourcesRaw(mApkFile, outDir, "classes.dex"); break; case DECODE_SOURCES_SMALI: + case DECODE_SOURCES_SMALI_ONLY_MAIN_CLASSES: mAndrolib.decodeSourcesSmali(mApkFile, outDir, "classes.dex", mBakDeb, mApi); break; } @@ -162,6 +163,13 @@ public class ApkDecoder { case DECODE_SOURCES_SMALI: mAndrolib.decodeSourcesSmali(mApkFile, outDir, file, mBakDeb, mApi); break; + case DECODE_SOURCES_SMALI_ONLY_MAIN_CLASSES: + if (file.startsWith("classes") && file.endsWith(".dex")) { + mAndrolib.decodeSourcesSmali(mApkFile, outDir, file, mBakDeb, mApi); + } else { + mAndrolib.decodeSourcesRaw(mApkFile, outDir, file); + } + break; } } } @@ -184,7 +192,7 @@ public class ApkDecoder { } public void setDecodeSources(short mode) throws AndrolibException { - if (mode != DECODE_SOURCES_NONE && mode != DECODE_SOURCES_SMALI) { + if (mode != DECODE_SOURCES_NONE && mode != DECODE_SOURCES_SMALI && mode != DECODE_SOURCES_SMALI_ONLY_MAIN_CLASSES) { throw new AndrolibException("Invalid decode sources mode: " + mode); } mDecodeSources = mode; @@ -316,6 +324,7 @@ public class ApkDecoder { public final static short DECODE_SOURCES_NONE = 0x0000; public final static short DECODE_SOURCES_SMALI = 0x0001; + public final static short DECODE_SOURCES_SMALI_ONLY_MAIN_CLASSES = 0x0010; public final static short DECODE_RESOURCES_NONE = 0x0100; public final static short DECODE_RESOURCES_FULL = 0x0101; diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/DuplicateDexTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/DuplicateDexTest.java new file mode 100644 index 00000000..304fedf6 --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/DuplicateDexTest.java @@ -0,0 +1,79 @@ +/** + * Copyright (C) 2018 Ryszard Wiśniewski + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package brut.androlib.decode; + +import brut.androlib.*; +import brut.androlib.aapt1.AndroidOreoSparseTest; +import brut.androlib.aapt2.BuildAndDecodeTest; +import brut.androlib.meta.MetaInfo; +import brut.common.BrutException; +import brut.directory.ExtFile; +import brut.util.OS; +import org.apache.commons.lang3.StringUtils; +import org.junit.*; + +import java.io.File; +import java.io.IOException; + +import static org.junit.Assert.assertTrue; + +public class DuplicateDexTest extends BaseTest { + + @Before + public void beforeClass() throws Exception { + TestUtils.cleanFrameworkFile(); + sTmpDir = new ExtFile(OS.createTempDirectory()); + sTestOrigDir = new ExtFile(sTmpDir, "duplicatedex-orig"); + sTestNewDir = new ExtFile(sTmpDir, "duplicatedex-new"); + LOGGER.info("Unpacking sparse.apk..."); + TestUtils.copyResourceDir(DuplicateDexTest.class, "decode/duplicatedex", sTestOrigDir); + } + + @After + public void afterClass() throws BrutException { + OS.rmdir(sTmpDir); + } + + @Test(expected = AndrolibException.class) + public void decodeAllSourcesShouldThrowException() throws BrutException, IOException { + File testApk = new File(sTestOrigDir, "duplicatedex.apk"); + + LOGGER.info("Decoding duplicatedex.apk..."); + ApkDecoder apkDecoder = new ApkDecoder(testApk); + apkDecoder.setOutDir(sTestNewDir); + apkDecoder.decode(); + + LOGGER.info("Building duplicatedex.apk..."); + ApkOptions apkOptions = new ApkOptions(); + new Androlib(apkOptions).build(sTestNewDir, testApk); + } + + @Test + public void decodeUsingOnlyMainClassesMode() throws BrutException, IOException { + File testApk = new File(sTestOrigDir, "duplicatedex.apk"); + + LOGGER.info("Decoding duplicatedex.apk..."); + ApkDecoder apkDecoder = new ApkDecoder(testApk); + apkDecoder.setDecodeSources(ApkDecoder.DECODE_SOURCES_SMALI_ONLY_MAIN_CLASSES); + apkDecoder.setOutDir(sTestNewDir); + apkDecoder.decode(); + + LOGGER.info("Building duplicatedex.apk..."); + ApkOptions apkOptions = new ApkOptions(); + new Androlib(apkOptions).build(sTestNewDir, testApk); + } + +} \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/resources/decode/duplicatedex/duplicatedex.apk b/brut.apktool/apktool-lib/src/test/resources/decode/duplicatedex/duplicatedex.apk new file mode 100644 index 0000000000000000000000000000000000000000..6e6144bf2d877fa847fb9e68f73a2d440c70fd5c GIT binary patch literal 13091 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 literal 0 HcmV?d00001