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 00000000..6e6144bf Binary files /dev/null and b/brut.apktool/apktool-lib/src/test/resources/decode/duplicatedex/duplicatedex.apk differ