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 53f831a224..ba1379c6fc 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 @@ -22,6 +22,7 @@ import brut.androlib.apk.ApkInfo; import brut.androlib.res.ResourcesDecoder; import brut.androlib.res.data.*; +import brut.androlib.res.xml.ResXmlPatcher; import brut.androlib.src.SmaliDecoder; import brut.directory.Directory; import brut.directory.ExtFile; @@ -50,6 +51,10 @@ public class ApkDecoder { private final static String[] APK_STANDARD_ALL_FILENAMES = new String[] { "classes.dex", "AndroidManifest.xml", "resources.arsc", "res", "r", "R", "lib", "libs", "assets", "META-INF", "kotlin" }; + private final static String[] APK_RESOURCES_FILENAMES = new String[] { + "resources.arsc", "res", "r", "R" }; + private final static String[] APK_MANIFEST_FILENAMES = new String[] { + "AndroidManifest.xml" }; private final static Pattern NO_COMPRESS_PATTERN = Pattern.compile("(" + "jpg|jpeg|png|gif|wav|mp2|mp3|ogg|aac|mpg|mpeg|mid|midi|smf|jet|rtttl|imy|xmf|mp4|" + "m4a|m4v|3gp|3gpp|3g2|3gpp2|amr|awb|wma|wmv|webm|webp|mkv)$"); @@ -92,8 +97,27 @@ public ApkInfo decode(File outDir) throws AndrolibException, IOException, Direct LOGGER.info("Using Apktool " + ApktoolProperties.getVersion() + " on " + mApkFile.getName()); ResourcesDecoder resourcesDecoder = new ResourcesDecoder(mConfig, mApkFile); - resourcesDecoder.decodeManifest(outDir); - resourcesDecoder.decodeResources(outDir); + if (hasResources()) { + switch (mConfig.decodeResources) { + case Config.DECODE_RESOURCES_NONE: + copyResourcesRaw(outDir); + break; + case Config.DECODE_RESOURCES_FULL: + resourcesDecoder.decodeResources(outDir); + break; + } + } + + if (hasManifest()) { + if (mConfig.decodeResources == Config.DECODE_RESOURCES_FULL || + mConfig.forceDecodeManifest == Config.FORCE_DECODE_MANIFEST_FULL) { + resourcesDecoder.decodeManifest(outDir); + } + else { + copyManifestRaw(outDir); + } + } + resourcesDecoder.updateApkInfo(outDir); if (hasSources()) { switch (mConfig.decodeSources) { @@ -135,7 +159,7 @@ public ApkInfo decode(File outDir) throws AndrolibException, IOException, Direct // In case we have no resources. We should store the minSdk we pulled from the source opcode api level ApkInfo apkInfo = resourcesDecoder.getApkInfo(); - if (! resourcesDecoder.hasResources() && mMinSdkVersion > 0) { + if (!hasResources() && mMinSdkVersion > 0) { apkInfo.setSdkInfoField("minSdkVersion", Integer.toString(mMinSdkVersion)); } @@ -154,6 +178,22 @@ public ApkInfo decode(File outDir) throws AndrolibException, IOException, Direct } } + private boolean hasManifest() throws AndrolibException { + try { + return mApkFile.getDirectory().containsFile("AndroidManifest.xml"); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } + + private boolean hasResources() throws AndrolibException { + try { + return mApkFile.getDirectory().containsFile("resources.arsc"); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } + private boolean hasSources() throws AndrolibException { try { return mApkFile.getDirectory().containsFile("classes.dex"); @@ -187,6 +227,26 @@ private void writeApkInfo(ApkInfo apkInfo, File outDir) throws AndrolibException } } + private void copyManifestRaw(File outDir) + throws AndrolibException { + try { + LOGGER.info("Copying raw manifest..."); + mApkFile.getDirectory().copyToDir(outDir, APK_MANIFEST_FILENAMES); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } + + private void copyResourcesRaw(File outDir) + throws AndrolibException { + try { + LOGGER.info("Copying raw resources..."); + mApkFile.getDirectory().copyToDir(outDir, APK_RESOURCES_FILENAMES); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } + private void copySourcesRaw(File outDir, String filename) throws AndrolibException { try { diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResourcesDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResourcesDecoder.java index 59abe33484..77a8efb1c3 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResourcesDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResourcesDecoder.java @@ -29,7 +29,6 @@ import brut.directory.DirectoryException; import brut.directory.ExtFile; import brut.directory.FileDirectory; -import brut.util.Duo; import org.xmlpull.v1.XmlSerializer; import java.io.File; @@ -48,10 +47,6 @@ public class ResourcesDecoder { private final ApkInfo mApkInfo; private final Map mResFileMapping = new HashMap<>(); - private final static String[] APK_RESOURCES_FILENAMES = new String[] { - "resources.arsc", "res", "r", "R" }; - private final static String[] APK_MANIFEST_FILENAMES = new String[] { - "AndroidManifest.xml" }; private final static String[] IGNORED_PACKAGES = new String[] { "android", "com.htc", "com.lge", "com.lge.internal", "yi", "flyme", "air.com.adobe.appentry", "FFFFFFFFFFFFFFFFFFFFFF" }; @@ -85,9 +80,6 @@ public ResTable getResTable() throws AndrolibException { throw new AndrolibException( "Apk doesn't contain either AndroidManifest.xml file or resources.arsc file"); } - if (hasResources() && !mResTable.isMainPkgLoaded()) { - mResTable.loadMainPkg(mApkFile); - } return mResTable; } @@ -101,38 +93,31 @@ public Map getResFileMapping() { public void decodeManifest(File outDir) throws AndrolibException { if (hasManifest()) { - if (mConfig.decodeResources == Config.DECODE_RESOURCES_FULL || - mConfig.forceDecodeManifest == Config.FORCE_DECODE_MANIFEST_FULL) { - decodeManifest(getResTable(), mApkFile, outDir); - if (hasResources()) { - if (!mConfig.analysisMode) { - // Remove versionName / versionCode (aapt API 16) - // - // check for a mismatch between resources.arsc package and the package listed in AndroidManifest - // also remove the android::versionCode / versionName from manifest for rebuild - // this is a required change to prevent aapt warning about conflicting versions - // it will be passed as a parameter to aapt like "--min-sdk-version" via apktool.yml - adjustPackageManifest(getResTable(), outDir.getAbsolutePath() + File.separator + "AndroidManifest.xml"); - - ResXmlPatcher.removeManifestVersions(new File( - outDir.getAbsolutePath() + File.separator + "AndroidManifest.xml")); - - // update apk info - mApkInfo.packageInfo.forcedPackageId = String.valueOf(mResTable.getPackageId()); - } - } - } - else { - try { - LOGGER.info("Copying raw manifest..."); - mApkFile.getDirectory().copyToDir(outDir, APK_MANIFEST_FILENAMES); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); + decodeManifest(getResTable(), mApkFile, outDir); + if (hasResources()) { + if (!mConfig.analysisMode) { + // Remove versionName / versionCode (aapt API 16) + // + // check for a mismatch between resources.arsc package and the package listed in AndroidManifest + // also remove the android::versionCode / versionName from manifest for rebuild + // this is a required change to prevent aapt warning about conflicting versions + // it will be passed as a parameter to aapt like "--min-sdk-version" via apktool.yml + adjustPackageManifest(getResTable(), outDir.getAbsolutePath() + File.separator + "AndroidManifest.xml"); + + ResXmlPatcher.removeManifestVersions(new File( + outDir.getAbsolutePath() + File.separator + "AndroidManifest.xml")); + + // update apk info + mApkInfo.packageInfo.forcedPackageId = String.valueOf(mResTable.getPackageId()); } } } } + public void updateApkInfo(File outDir) throws AndrolibException { + mResTable.initApkInfo(mApkInfo, outDir); + } + private void decodeManifest(ResTable resTable, ExtFile apkFile, File outDir) throws AndrolibException { @@ -191,22 +176,14 @@ private ExtMXSerializer getResXmlSerializer() { return serial; } + public void loadMainPkg() throws AndrolibException { + mResTable.loadMainPkg(mApkFile); + } + public ResTable decodeResources(File outDir) throws AndrolibException { if (hasResources()) { - switch (mConfig.decodeResources) { - case Config.DECODE_RESOURCES_NONE: - try { - LOGGER.info("Copying raw resources..."); - mApkFile.getDirectory().copyToDir(outDir, APK_RESOURCES_FILENAMES); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); - } - break; - case Config.DECODE_RESOURCES_FULL: - decodeResources(getResTable(), mApkFile, outDir); - break; - } - mResTable.initApkInfo(mApkInfo, outDir); + loadMainPkg(); + decodeResources(getResTable(), mApkFile, outDir); } return mResTable; } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/NonStandardPkgIdTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/NonStandardPkgIdTest.java index a43e408e4a..7568a646b2 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/NonStandardPkgIdTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/NonStandardPkgIdTest.java @@ -58,8 +58,8 @@ public static void beforeClass() throws Exception { Config.getDefaultConfig(), new ExtFile(testApk)); sTestNewDir.mkdirs(); - resourcesDecoder.decodeManifest(sTestNewDir); mResTable = resourcesDecoder.decodeResources(sTestNewDir); + resourcesDecoder.decodeManifest(sTestNewDir); } @AfterClass diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/DecodeArrayTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/DecodeArrayTest.java index 9c0741ac77..68b1104f62 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/DecodeArrayTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/DecodeArrayTest.java @@ -57,6 +57,7 @@ public void decodeStringArray() throws BrutException { Config.getDefaultConfig(), new ExtFile(sTmpDir + File.separator + apk)); + resourcesDecoder.loadMainPkg(); ResTable resTable = resourcesDecoder.getResTable(); ResValue value = resTable.getResSpec(0x7f020001).getDefaultResource().getValue(); @@ -70,6 +71,7 @@ public void decodeArray() throws BrutException { Config.getDefaultConfig(), new ExtFile(sTmpDir + File.separator + apk)); + resourcesDecoder.loadMainPkg(); ResTable resTable = resourcesDecoder.getResTable(); ResValue value = resTable.getResSpec(0x7f020000).getDefaultResource().getValue();