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 94967f2b92..da8d52dd44 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 @@ -88,24 +88,34 @@ public static void main(String[] args) throws BrutException { boolean cmdFound = false; for (String opt : commandLine.getArgs()) { - if (opt.equalsIgnoreCase("d") || opt.equalsIgnoreCase("decode")) { - cmdDecode(commandLine, config); - cmdFound = true; - } else if (opt.equalsIgnoreCase("b") || opt.equalsIgnoreCase("build")) { - cmdBuild(commandLine, config); - cmdFound = true; - } else if (opt.equalsIgnoreCase("if") || opt.equalsIgnoreCase("install-framework")) { - cmdInstallFramework(commandLine, config); - cmdFound = true; - } else if (opt.equalsIgnoreCase("empty-framework-dir")) { - cmdEmptyFrameworkDirectory(commandLine, config); - cmdFound = true; - } else if (opt.equalsIgnoreCase("list-frameworks")) { - cmdListFrameworks(commandLine, config); - cmdFound = true; - } else if (opt.equalsIgnoreCase("publicize-resources")) { - cmdPublicizeResources(commandLine, config); - cmdFound = true; + switch (opt) { + case "d": + case "decode": + cmdDecode(commandLine, config); + cmdFound = true; + break; + case "b": + case "build": + cmdBuild(commandLine, config); + cmdFound = true; + break; + case "if": + case "install-framework": + cmdInstallFramework(commandLine, config); + cmdFound = true; + break; + case "empty-framework-dir": + cmdEmptyFrameworkDirectory(commandLine, config); + cmdFound = true; + break; + case "list-frameworks": + cmdListFrameworks(commandLine, config); + cmdFound = true; + break; + case "publicize-resources": + cmdPublicizeResources(commandLine, config); + cmdFound = true; + break; } } @@ -214,8 +224,7 @@ private static void cmdDecode(CommandLine cli, Config config) throws AndrolibExc } ExtFile apkFile = new ExtFile(apkName); - ApkDecoder decoder = new ApkDecoder(config, apkFile); - + ApkDecoder decoder = new ApkDecoder(apkFile, config); try { decoder.decode(outDir); } catch (OutDirExistsException ex) { @@ -235,18 +244,12 @@ private static void cmdDecode(CommandLine cli, Config config) throws AndrolibExc + ". You must install proper " + "framework files, see project website for more info."); System.exit(1); - } catch (IOException ex) { - System.err.println("Could not modify file. Please ensure you have permission."); - System.exit(1); - } catch (DirectoryException ex) { - System.err.println("Could not modify internal dex files. Please ensure you have permission."); - System.exit(1); } } - private static void cmdBuild(CommandLine cli, Config config) { + private static void cmdBuild(CommandLine cli, Config config) throws AndrolibException { String[] args = cli.getArgs(); - String appDirName = args.length < 2 ? "." : args[1]; + String apkDirName = args.length < 2 ? "." : args[1]; // check for build options if (cli.hasOption("f") || cli.hasOption("force-all")) { @@ -262,7 +265,32 @@ private static void cmdBuild(CommandLine cli, Config config) { config.verbose = true; } if (cli.hasOption("a") || cli.hasOption("aapt")) { - config.aaptPath = cli.getOptionValue("a"); + if (cli.hasOption("use-aapt1") || cli.hasOption("use-aapt2")) { + System.err.println("You can only use one of -a/--aapt or --use-aapt1 or --use-aapt2."); + System.exit(1); + } + + try { + String aaptPath = cli.getOptionValue("a"); + int aaptVersion = AaptManager.getAaptVersion(aaptPath); + if (aaptVersion < AaptManager.AAPT_VERSION_MIN && aaptVersion > AaptManager.AAPT_VERSION_MAX) { + System.err.println("AAPT version " + aaptVersion + " is not supported"); + System.exit(1); + } + + config.aaptPath = aaptPath; + config.aaptVersion = aaptVersion; + } catch (BrutException ex) { + System.err.println(ex.getMessage()); + System.exit(1); + } + } else if (cli.hasOption("use-aapt1")) { + if (cli.hasOption("use-aapt2")) { + System.err.println("You can only use one of --use-aapt1 or --use-aapt2."); + System.exit(1); + } + + config.aaptVersion = 1; } if (cli.hasOption("c") || cli.hasOption("copy-original")) { config.copyOriginalFiles = true; @@ -270,13 +298,8 @@ private static void cmdBuild(CommandLine cli, Config config) { if (cli.hasOption("nc") || cli.hasOption("no-crunch")) { config.noCrunch = true; } - if (cli.hasOption("use-aapt1")) { - config.useAapt2 = false; - } - - if (cli.hasOption("use-aapt1") && cli.hasOption("use-aapt2")) { - System.err.println("You can only use one of --use-aapt1 or --use-aapt2."); - System.exit(1); + if (cli.hasOption("na") || cli.hasOption("no-apk")) { + config.noApk = true; } File outFile; @@ -286,21 +309,14 @@ private static void cmdBuild(CommandLine cli, Config config) { outFile = null; } - if (config.netSecConf && !config.useAapt2) { - System.err.println("-n / --net-sec-conf is only supported with --use-aapt2."); + if (config.netSecConf && config.aaptVersion == 1) { + System.err.println("-n / --net-sec-conf is not supported with legacy AAPT."); System.exit(1); } - // try and build apk - try { - if (cli.hasOption("a") || cli.hasOption("aapt")) { - config.aaptVersion = AaptManager.getAaptVersion(cli.getOptionValue("a")); - } - new ApkBuilder(config, new ExtFile(appDirName)).build(outFile); - } catch (BrutException ex) { - System.err.println(ex.getMessage()); - System.exit(1); - } + ExtFile apkDir = new ExtFile(apkDirName); + ApkBuilder builder = new ApkBuilder(apkDir, config); + builder.build(outFile); } private static void cmdInstallFramework(CommandLine cli, Config config) throws AndrolibException { @@ -462,13 +478,13 @@ private static void _options() { .build(); Option aapt1Option = Option.builder() - .longOpt("use-aapt1") - .desc("Use aapt binary instead of aapt2 during the build step.") - .build(); + .longOpt("use-aapt1") + .desc("Use aapt binary instead of aapt2 during the build step.") + .build(); Option aapt2Option = Option.builder() .longOpt("use-aapt2") - .desc("Use aapt2 binary instead of aapt during the build step.") + .desc("Use aapt2 binary instead of aapt during the build step. (default)") .build(); Option originalOption = Option.builder("c") @@ -481,6 +497,11 @@ private static void _options() { .desc("Disable crunching of resource files during the build step.") .build(); + Option noApkOption = Option.builder("na") + .longOpt("no-apk") + .desc("Disable repacking of the built files into a new apk.") + .build(); + Option tagOption = Option.builder("t") .longOpt("tag") .desc("Tag frameworks using .") @@ -530,6 +551,7 @@ private static void _options() { buildOptions.addOption(originalOption); buildOptions.addOption(aapt1Option); buildOptions.addOption(noCrunchOption); + buildOptions.addOption(noApkOption); } // add global options @@ -591,6 +613,7 @@ private static void _options() { allOptions.addOption(aapt1Option); allOptions.addOption(aapt2Option); allOptions.addOption(noCrunchOption); + allOptions.addOption(noApkOption); allOptions.addOption(onlyMainClassesOption); } @@ -675,8 +698,8 @@ public String format(LogRecord record) { } } } - } catch (Exception exception) { - reportError(null, exception, ErrorManager.FORMAT_FAILURE); + } catch (Exception ex) { + reportError(null, ex, ErrorManager.FORMAT_FAILURE); } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/AaptInvoker.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/AaptInvoker.java index db9afc783e..675265edb9 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/AaptInvoker.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/AaptInvoker.java @@ -16,8 +16,8 @@ */ package brut.androlib; -import brut.androlib.exceptions.AndrolibException; import brut.androlib.apk.ApkInfo; +import brut.androlib.exceptions.AndrolibException; import brut.common.BrutException; import brut.util.AaptManager; import brut.util.OS; @@ -39,39 +39,39 @@ public AaptInvoker(Config config, ApkInfo apkInfo) { private File getAaptBinaryFile() throws AndrolibException { try { - if (getAaptVersion() == 2) { - return AaptManager.getAapt2(); + switch (mConfig.aaptVersion) { + case 2: + return AaptManager.getAapt2(); + default: + return AaptManager.getAapt1(); } - return AaptManager.getAapt1(); } catch (BrutException ex) { throw new AndrolibException(ex); } } - private int getAaptVersion() { - return mConfig.isAapt2() ? 2 : 1; - } + public void invokeAapt(File apkFile, File manifest, File resDir, File rawDir, File assetDir, File[] include) + throws AndrolibException { - private File createDoNotCompressExtensionsFile(ApkInfo apkInfo) throws AndrolibException { - if (apkInfo.doNotCompress == null || apkInfo.doNotCompress.isEmpty()) { - return null; - } + String aaptPath = mConfig.aaptPath; + boolean customAapt = !aaptPath.isEmpty(); + List cmd = new ArrayList<>(); - File doNotCompressFile; try { - doNotCompressFile = File.createTempFile("APKTOOL", null); - doNotCompressFile.deleteOnExit(); - - BufferedWriter fileWriter = new BufferedWriter(new FileWriter(doNotCompressFile)); - for (String extension : apkInfo.doNotCompress) { - fileWriter.write(extension); - fileWriter.newLine(); - } - fileWriter.close(); + String aaptCommand = AaptManager.getAaptExecutionCommand(aaptPath, getAaptBinaryFile()); + cmd.add(aaptCommand); + } catch (BrutException ex) { + LOGGER.warning("aapt: " + ex.getMessage() + " (defaulting to $PATH binary)"); + cmd.add(AaptManager.getAaptBinaryName(mConfig.aaptVersion)); + } - return doNotCompressFile; - } catch (IOException ex) { - throw new AndrolibException(ex); + switch (mConfig.aaptVersion) { + case 2: + invokeAapt2(apkFile, manifest, resDir, rawDir, assetDir, include, cmd, customAapt); + break; + default: + invokeAapt1(apkFile, manifest, resDir, rawDir, assetDir, include, cmd, customAapt); + break; } } @@ -131,7 +131,9 @@ private void invokeAapt2(File apkFile, File manifest, File resDir, File rawDir, cmd.add("-o"); cmd.add(apkFile.getAbsolutePath()); - if (mApkInfo.packageInfo.forcedPackageId != null && ! mApkInfo.sharedLibrary) { + if (mApkInfo.packageInfo.forcedPackageId != null && !mApkInfo.packageInfo.forcedPackageId.equals("1") + && !mApkInfo.sharedLibrary) { + cmd.add("--allow-reserved-package-id"); cmd.add("--package-id"); cmd.add(mApkInfo.packageInfo.forcedPackageId); } @@ -174,8 +176,6 @@ private void invokeAapt2(File apkFile, File manifest, File resDir, File rawDir, cmd.add("--no-version-transitions"); cmd.add("--no-resource-deduping"); - cmd.add("--allow-reserved-package-id"); - cmd.add("--no-compile-sdk-metadata"); // #3427 - Ignore stricter parsing during aapt2 @@ -189,25 +189,6 @@ private void invokeAapt2(File apkFile, File manifest, File resDir, File rawDir, cmd.add("-x"); } - if (mApkInfo.doNotCompress != null && !customAapt) { - // Use custom -e option to avoid limits on commandline length. - // Can only be used when custom aapt binary is not used. - String extensionsFilePath = - Objects.requireNonNull(createDoNotCompressExtensionsFile(mApkInfo)).getAbsolutePath(); - cmd.add("-e"); - cmd.add(extensionsFilePath); - } else if (mApkInfo.doNotCompress != null) { - for (String file : mApkInfo.doNotCompress) { - cmd.add("-0"); - cmd.add(file); - } - } - - if (!mApkInfo.resourcesAreCompressed) { - cmd.add("-0"); - cmd.add("arsc"); - } - if (include != null) { for (File file : include) { cmd.add("-I"); @@ -264,7 +245,7 @@ private void invokeAapt1(File apkFile, File manifest, File resDir, File rawDir, } // force package id so that some frameworks build with correct id // disable if user adds own aapt (can't know if they have this feature) - if (mApkInfo.packageInfo.forcedPackageId != null && ! customAapt && ! mApkInfo.sharedLibrary) { + if (mApkInfo.packageInfo.forcedPackageId != null && !mApkInfo.sharedLibrary && !customAapt) { cmd.add("--forced-package-id"); cmd.add(mApkInfo.packageInfo.forcedPackageId); } @@ -311,25 +292,6 @@ private void invokeAapt1(File apkFile, File manifest, File resDir, File rawDir, cmd.add("-x"); } - if (mApkInfo.doNotCompress != null && !customAapt) { - // Use custom -e option to avoid limits on commandline length. - // Can only be used when custom aapt binary is not used. - String extensionsFilePath = - Objects.requireNonNull(createDoNotCompressExtensionsFile(mApkInfo)).getAbsolutePath(); - cmd.add("-e"); - cmd.add(extensionsFilePath); - } else if (mApkInfo.doNotCompress != null) { - for (String file : mApkInfo.doNotCompress) { - cmd.add("-0"); - cmd.add(file); - } - } - - if (!mApkInfo.resourcesAreCompressed) { - cmd.add("-0"); - cmd.add("arsc"); - } - if (include != null) { for (File file : include) { cmd.add("-I"); @@ -359,26 +321,4 @@ private void invokeAapt1(File apkFile, File manifest, File resDir, File rawDir, throw new AndrolibException(ex); } } - - public void invokeAapt(File apkFile, File manifest, File resDir, File rawDir, File assetDir, File[] include) - throws AndrolibException { - - String aaptPath = mConfig.aaptPath; - boolean customAapt = !aaptPath.isEmpty(); - List cmd = new ArrayList<>(); - - try { - String aaptCommand = AaptManager.getAaptExecutionCommand(aaptPath, getAaptBinaryFile()); - cmd.add(aaptCommand); - } catch (BrutException ex) { - LOGGER.warning("aapt: " + ex.getMessage() + " (defaulting to $PATH binary)"); - cmd.add(AaptManager.getAaptBinaryName(getAaptVersion())); - } - - if (mConfig.isAapt2()) { - invokeAapt2(apkFile, manifest, resDir, rawDir, assetDir, include, cmd, customAapt); - return; - } - invokeAapt1(apkFile, manifest, resDir, rawDir, assetDir, include, cmd, customAapt); - } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkBuilder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkBuilder.java index 5c9e3de4a2..92da81539e 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkBuilder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkBuilder.java @@ -31,6 +31,7 @@ import brut.directory.DirectoryException; import brut.directory.ExtFile; import brut.directory.ZipUtils; +import brut.util.AaptManager; import brut.util.BrutIO; import brut.util.OS; import org.apache.commons.io.FileUtils; @@ -44,131 +45,145 @@ import java.util.*; import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Logger; -import java.util.zip.CRC32; -import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class ApkBuilder { private final static Logger LOGGER = Logger.getLogger(ApkBuilder.class.getName()); - private final AtomicReference mBuildError = new AtomicReference<>(null); - private final Config mConfig; private final ExtFile mApkDir; - private BackgroundWorker mWorker; + private final Config mConfig; private ApkInfo mApkInfo; private int mMinSdkVersion = 0; - - private final static String APK_DIRNAME = "build/apk"; - private final static String UNK_DIRNAME = "unknown"; - private final static String[] APK_RESOURCES_FILENAMES = new String[] { - "resources.arsc", "AndroidManifest.xml", "res", "r", "R" }; - private final static String[] APK_RESOURCES_WITHOUT_RES_FILENAMES = new String[] { - "resources.arsc", "AndroidManifest.xml" }; - private final static String[] APP_RESOURCES_FILENAMES = new String[] { - "AndroidManifest.xml", "res" }; - private final static String[] APK_MANIFEST_FILENAMES = new String[] { - "AndroidManifest.xml" }; + private BackgroundWorker mWorker; + private final AtomicReference mBuildError = new AtomicReference<>(null); public ApkBuilder(ExtFile apkDir) { - this(Config.getDefaultConfig(), apkDir); + this(apkDir, Config.getDefaultConfig()); } - public ApkBuilder(Config config, ExtFile apkDir) { - mConfig = config; + public ApkBuilder(ExtFile apkDir, Config config) { mApkDir = apkDir; + mConfig = config; } - public void build(File outFile) throws BrutException { - LOGGER.info("Using Apktool " + ApktoolProperties.getVersion() + " with " + mConfig.jobs + " thread(s)."); + public void build(File outApk) throws AndrolibException { + if (mConfig.jobs > 1) { + mWorker = new BackgroundWorker(mConfig.jobs - 1); + } try { - mWorker = new BackgroundWorker(mConfig.jobs); mApkInfo = ApkInfo.load(mApkDir); - if (mApkInfo.getSdkInfo() != null && mApkInfo.getSdkInfo().get("minSdkVersion") != null) { - String minSdkVersion = mApkInfo.getSdkInfo().get("minSdkVersion"); + String minSdkVersion = mApkInfo.getMinSdkVersion(); + if (minSdkVersion != null) { mMinSdkVersion = mApkInfo.getMinSdkVersionFromAndroidCodename(minSdkVersion); } - if (outFile == null) { + if (outApk == null) { String outFileName = mApkInfo.apkFileName; - outFile = new File(mApkDir, "dist" + File.separator + (outFileName == null ? "out.apk" : outFileName)); + if (outFileName == null) { + outFileName = "out.apk"; + } + outApk = new File(mApkDir, "dist" + File.separator + outFileName); } + File outDir = new File(mApkDir, "build" + File.separator + "apk"); //noinspection ResultOfMethodCallIgnored - new File(mApkDir, APK_DIRNAME).mkdirs(); + outDir.mkdirs(); + File manifest = new File(mApkDir, "AndroidManifest.xml"); - File manifestOriginal = new File(mApkDir, "AndroidManifest.xml.orig"); - - scheduleBuildDexFiles(); - backupManifestFile(manifest, manifestOriginal); - buildResources(); - copyLibs(); - copyOriginalFilesIfEnabled(); - mWorker.waitForFinish(); - if (mBuildError.get() != null) { - throw mBuildError.get(); + File manifestOrig = new File(mApkDir, "AndroidManifest.xml.orig"); + + LOGGER.info("Using Apktool " + ApktoolProperties.getVersion() + " on " + outApk.getName() + + (mWorker != null ? " with " + mConfig.jobs + " threads" : "")); + + buildSources(outDir); + backupManifestFile(manifest, manifestOrig); + buildResources(outDir, manifest); + + if (mWorker != null) { + mWorker.waitForFinish(); + if (mBuildError.get() != null) { + throw mBuildError.get(); + } } - buildApk(outFile); + if (!mConfig.noApk) { + if (outApk.exists()) { + //noinspection ResultOfMethodCallIgnored + outApk.delete(); + } else { + File parentDir = outApk.getParentFile(); + if (parentDir != null && !parentDir.exists()) { + //noinspection ResultOfMethodCallIgnored + parentDir.mkdirs(); + } + } + + copyOriginalFiles(outDir); + + LOGGER.info("Building apk file..."); + + try (ZipOutputStream outStream = new ZipOutputStream(Files.newOutputStream(outApk.toPath()))) { + // zip aapt output files + try { + ZipUtils.zipDir(outDir, outStream, mApkInfo.doNotCompress); + } catch (IOException ex) { + throw new AndrolibException(ex); + } + + // zip remaining standard files + importRawFiles(outStream); + + // zip unknown files + importUnknownFiles(outStream); + } catch (IOException ex) { + throw new AndrolibException(ex); + } + + LOGGER.info("Built apk into: " + outApk.getPath()); + } // we copied the AndroidManifest.xml to AndroidManifest.xml.orig so we can edit it // lets restore the unedited one, to not change the original - if (manifest.isFile() && manifest.exists() && manifestOriginal.isFile()) { + if (manifest.isFile() && manifestOrig.isFile()) { try { - if (new File(mApkDir, "AndroidManifest.xml").delete()) { - FileUtils.moveFile(manifestOriginal, manifest); + if (manifest.delete()) { + FileUtils.moveFile(manifestOrig, manifest); } } catch (IOException ex) { - throw new AndrolibException(ex.getMessage()); + throw new AndrolibException(ex); } } - LOGGER.info("Built apk into: " + outFile.getPath()); } finally { - mWorker.shutdownNow(); + if (mWorker != null) { + mWorker.shutdownNow(); + } } } - private void backupManifestFile(File manifest, File manifestOriginal) throws AndrolibException { - // If we decoded in "raw", we cannot patch AndroidManifest - if (new File(mApkDir, "resources.arsc").exists()) { - return; - } - if (manifest.isFile() && manifest.exists()) { - try { - if (manifestOriginal.exists()) { - //noinspection ResultOfMethodCallIgnored - manifestOriginal.delete(); - } - FileUtils.copyFile(manifest, manifestOriginal); - ResXmlPatcher.fixingPublicAttrsInProviderAttributes(manifest); - } catch (IOException ex) { - throw new AndrolibException(ex.getMessage()); - } + private void buildSources(File outDir) throws AndrolibException { + if (!copySourcesRaw(outDir, "classes.dex")) { + buildSourcesSmali(outDir, "smali", "classes.dex"); } - } - private void scheduleBuildDexFiles() throws AndrolibException { try { - mWorker.submit(() -> scheduleDexBuild("classes.dex", "smali")); + Directory in = mApkDir.getDirectory(); // loop through any smali_ directories for multi-dex apks - Map dirs = mApkDir.getDirectory().getDirs(); - for (Map.Entry directory : dirs.entrySet()) { - String name = directory.getKey(); - if (name.startsWith("smali_")) { - String filename = name.substring(name.indexOf("_") + 1) + ".dex"; - mWorker.submit(() -> scheduleDexBuild(filename, name)); + for (String dirName : in.getDirs().keySet()) { + if (dirName.startsWith("smali_")) { + String fileName = dirName.substring(dirName.indexOf("_") + 1) + ".dex"; + if (!copySourcesRaw(outDir, fileName)) { + buildSourcesSmali(outDir, dirName, fileName); + } } } // loop through any classes#.dex files for multi-dex apks - File[] dexFiles = mApkDir.listFiles(); - if (dexFiles != null) { - for (File dex : dexFiles) { - // skip classes.dex because we have handled it in buildSources() - if (dex.getName().endsWith(".dex") && !dex.getName().equalsIgnoreCase("classes.dex")) { - buildSourcesRaw(dex.getName()); - } + for (String fileName : in.getFiles()) { + // skip classes.dex because we have handled it + if (fileName.endsWith(".dex") && !fileName.equals("classes.dex")) { + copySourcesRaw(outDir, fileName); } } } catch (DirectoryException ex) { @@ -176,338 +191,309 @@ private void scheduleBuildDexFiles() throws AndrolibException { } } - private void scheduleDexBuild(String filename, String smali) { + private boolean copySourcesRaw(File outDir, String fileName) throws AndrolibException { + File working = new File(mApkDir, fileName); + if (!working.isFile()) { + return false; + } + + File stored = new File(outDir, fileName); + if (!mConfig.forceBuildAll && !isModified(working, stored)) { + return true; + } + + LOGGER.info("Copying raw " + fileName + " file..."); try { - if (mBuildError.get() != null) { - return; - } - if (!buildSourcesRaw(filename) && !buildSourcesSmali(smali, filename)) { - LOGGER.warning("Could not find sources"); - } - } catch (AndrolibException e) { - mBuildError.compareAndSet(null, e); + BrutIO.copyAndClose(Files.newInputStream(working.toPath()), Files.newOutputStream(stored.toPath())); + } catch (IOException ex) { + throw new AndrolibException(ex); } + return true; } - private boolean buildSourcesRaw(String filename) throws AndrolibException { - File working = new File(mApkDir, filename); - if (!working.exists()) { - return false; + private void buildSourcesSmali(File outDir, String dirName, String fileName) throws AndrolibException { + if (mWorker != null) { + mWorker.submit(() -> { + if (mBuildError.get() == null) { + try { + buildSourcesSmaliJob(outDir, dirName, fileName); + } catch (AndrolibException ex) { + mBuildError.compareAndSet(null, ex); + } + } + }); + } else { + buildSourcesSmaliJob(outDir, dirName, fileName); } - File stored = new File(mApkDir, APK_DIRNAME + "/" + filename); - if (mConfig.forceBuildAll || isModified(working, stored)) { - LOGGER.info("Copying " + mApkDir.toString() + " " + filename + " file..."); - try { - BrutIO.copyAndClose(Files.newInputStream(working.toPath()), Files.newOutputStream(stored.toPath())); - return true; - } catch (IOException ex) { - throw new AndrolibException(ex); + } + + private void buildSourcesSmaliJob(File outDir, String dirName, String fileName) throws AndrolibException { + File smaliDir = new File(mApkDir, dirName); + if (!smaliDir.isDirectory()) { + return; + } + + File dex = new File(outDir, fileName); + if (!mConfig.forceBuildAll) { + LOGGER.info("Checking whether sources have changed..."); + if (!isModified(smaliDir, dex)) { + return; } } - return true; + //noinspection ResultOfMethodCallIgnored + dex.delete(); + + int apiLevel = mConfig.apiLevel > 0 ? mConfig.apiLevel : mMinSdkVersion; + + LOGGER.info("Smaling " + dirName + " folder into " + fileName + "..."); + SmaliBuilder.build(smaliDir, dex, apiLevel); } - private boolean buildSourcesSmali(String folder, String filename) throws AndrolibException { - ExtFile smaliDir = new ExtFile(mApkDir, folder); - if (!smaliDir.exists()) { - return false; + private void backupManifestFile(File manifest, File manifestOrig) throws AndrolibException { + // if we decoded in "raw", we cannot patch AndroidManifest + if (new File(mApkDir, "resources.arsc").isFile()) { + return; } - File dex = new File(mApkDir, APK_DIRNAME + "/" + filename); - if (!mConfig.forceBuildAll) { - LOGGER.info("Checking whether sources has changed..."); + + if (!manifest.isFile()) { + return; } - if (mConfig.forceBuildAll || isModified(smaliDir, dex)) { - LOGGER.info("Smaling " + folder + " folder into " + filename + "..."); + + if (manifestOrig.exists()) { //noinspection ResultOfMethodCallIgnored - dex.delete(); - SmaliBuilder.build(smaliDir, dex, mConfig.apiLevel > 0 ? mConfig.apiLevel : mMinSdkVersion); + manifestOrig.delete(); + } + + try { + FileUtils.copyFile(manifest, manifestOrig); + ResXmlPatcher.fixingPublicAttrsInProviderAttributes(manifest); + } catch (IOException ex) { + throw new AndrolibException(ex); } - return true; } - private void buildResources() throws BrutException { - // create res folder, manifest file and resources.arsc - if (!buildResourcesRaw() && !buildResourcesFull() && !buildManifest()) { - LOGGER.warning("Could not find resources"); + private void buildResources(File outDir, File manifest) throws AndrolibException { + if (!manifest.isFile()) { + LOGGER.fine("Could not find AndroidManifest.xml"); + return; + } + + if (new File(mApkDir, "resources.arsc").isFile()) { + copyResourcesRaw(outDir, manifest); + } else if (new File(mApkDir, "res").isDirectory()) { + buildResourcesFull(outDir, manifest); + } else { + LOGGER.fine("Could not find resources"); + buildManifest(outDir, manifest); } } - private boolean buildResourcesRaw() throws AndrolibException { - try { - if (!new File(mApkDir, "resources.arsc").exists()) { - return false; - } - File apkDir = new File(mApkDir, APK_DIRNAME); - if (!mConfig.forceBuildAll) { - LOGGER.info("Checking whether resources has changed..."); - } - if (mConfig.forceBuildAll || isModified(newFiles(APK_RESOURCES_FILENAMES, mApkDir), - newFiles(APK_RESOURCES_FILENAMES, apkDir))) { - LOGGER.info("Copying raw resources..."); - mApkDir.getDirectory().copyToDir(apkDir, APK_RESOURCES_FILENAMES); + private void copyResourcesRaw(File outDir, File manifest) throws AndrolibException { + if (!mConfig.forceBuildAll) { + LOGGER.info("Checking whether resources have changed..."); + if (!isModified(manifest, new File(outDir, "AndroidManifest.xml")) + && !isModified(new File(mApkDir, "resources.arsc"), new File(outDir, "resources.arsc")) + && !isModified(newFiles(mApkDir, ApkInfo.RESOURCES_DIRNAMES), + newFiles(outDir, ApkInfo.RESOURCES_DIRNAMES))) { + return; } - return true; + } + + LOGGER.info("Copying raw resources..."); + try { + Directory in = mApkDir.getDirectory(); + + in.copyToDir(outDir, "AndroidManifest.xml"); + in.copyToDir(outDir, "resources.arsc"); + in.copyToDir(outDir, ApkInfo.RESOURCES_DIRNAMES); } catch (DirectoryException ex) { throw new AndrolibException(ex); } } - private boolean buildResourcesFull() throws AndrolibException { - try { - if (!new File(mApkDir, "res").exists()) { - return false; - } - if (!mConfig.forceBuildAll) { - LOGGER.info("Checking whether resources has changed..."); + private void buildResourcesFull(File outDir, File manifest) throws AndrolibException { + File resourcesFile = new File(outDir.getParentFile(), "resources.zip"); + if (!mConfig.forceBuildAll) { + LOGGER.info("Checking whether resources have changed..."); + if (!isModified(manifest, new File(outDir, "AndroidManifest.xml")) + && !isModified(newFiles(mApkDir, ApkInfo.RESOURCES_DIRNAMES), + newFiles(outDir, ApkInfo.RESOURCES_DIRNAMES)) + && (mConfig.aaptVersion == 1 || resourcesFile.isFile())) { + return; } - File apkDir = new File(mApkDir, APK_DIRNAME); - File resourceFile = new File(apkDir.getParent(), "resources.zip"); - - if (mConfig.forceBuildAll || isModified(newFiles(APP_RESOURCES_FILENAMES, mApkDir), - newFiles(APK_RESOURCES_FILENAMES, apkDir)) || (mConfig.isAapt2() && !isFile(resourceFile))) { - LOGGER.info("Building resources..."); - - if (mConfig.debugMode) { - if (mConfig.isAapt2()) { - LOGGER.info("Using aapt2 - setting 'debuggable' attribute to 'true' in AndroidManifest.xml"); - ResXmlPatcher.setApplicationDebugTagTrue(new File(mApkDir, "AndroidManifest.xml")); - } else { - ResXmlPatcher.removeApplicationDebugTag(new File(mApkDir, "AndroidManifest.xml")); - } - } + } + //noinspection ResultOfMethodCallIgnored + resourcesFile.delete(); - if (mConfig.netSecConf) { - ApkInfo meta = ApkInfo.load(new ExtFile(mApkDir)); - if (meta.getSdkInfo() != null && meta.getSdkInfo().get("targetSdkVersion") != null) { - if (Integer.parseInt(meta.getSdkInfo().get("targetSdkVersion")) < ResConfigFlags.SDK_NOUGAT) { - LOGGER.warning("Target SDK version is lower than 24! Network Security Configuration might be ignored!"); - } - } - File netSecConfOrig = new File(mApkDir, "res/xml/network_security_config.xml"); - if (netSecConfOrig.exists()) { - LOGGER.info("Replacing existing network_security_config.xml!"); - //noinspection ResultOfMethodCallIgnored - netSecConfOrig.delete(); - } - ResXmlPatcher.modNetworkSecurityConfig(netSecConfOrig); - ResXmlPatcher.setNetworkSecurityConfig(new File(mApkDir, "AndroidManifest.xml")); - LOGGER.info("Added permissive network security config in manifest"); + try { + if (mConfig.debugMode) { + if (mConfig.aaptVersion == 2) { + LOGGER.info("Using aapt2 - setting 'debuggable' attribute to 'true' in AndroidManifest.xml"); + ResXmlPatcher.setApplicationDebugTagTrue(manifest); + } else { + ResXmlPatcher.removeApplicationDebugTag(manifest); } + } - File apkFile = File.createTempFile("APKTOOL", null); - //noinspection ResultOfMethodCallIgnored - apkFile.delete(); - //noinspection ResultOfMethodCallIgnored - resourceFile.delete(); - - File ninePatch = new File(mApkDir, "9patch"); - if (!ninePatch.exists()) { - ninePatch = null; + if (mConfig.netSecConf) { + String targetSdkVersion = mApkInfo.getTargetSdkVersion(); + if (targetSdkVersion != null) { + if (Integer.parseInt(targetSdkVersion) < ResConfigFlags.SDK_NOUGAT) { + LOGGER.warning("Target SDK version is lower than 24! Network Security Configuration might be ignored!"); + } } - AaptInvoker invoker = new AaptInvoker(mConfig, mApkInfo); - invoker.invokeAapt(apkFile, new File(mApkDir, "AndroidManifest.xml"), - new File(mApkDir, "res"), ninePatch, null, getIncludeFiles()); - ExtFile tmpExtFile = new ExtFile(apkFile); - Directory tmpDir = tmpExtFile.getDirectory(); - - // Sometimes an application is built with a resources.arsc file with no resources, - // Apktool assumes it will have a rebuilt arsc file, when it doesn't. So if we - // encounter a copy error, move to a warning and continue on. (#1730) - try { - tmpDir.copyToDir(apkDir, - tmpDir.containsDir("res") ? APK_RESOURCES_FILENAMES - : APK_RESOURCES_WITHOUT_RES_FILENAMES); - } catch (DirectoryException ex) { - LOGGER.warning(ex.getMessage()); - } finally { - tmpExtFile.close(); + File netSecConfOrig = new File(mApkDir, "res/xml/network_security_config.xml"); + if (netSecConfOrig.exists()) { + LOGGER.info("Replacing existing network_security_config.xml!"); + //noinspection ResultOfMethodCallIgnored + netSecConfOrig.delete(); } - // delete tmpDir - //noinspection ResultOfMethodCallIgnored - apkFile.delete(); + ResXmlPatcher.modNetworkSecurityConfig(netSecConfOrig); + ResXmlPatcher.setNetworkSecurityConfig(manifest); + LOGGER.info("Added permissive network security config in manifest"); } - return true; - } catch (IOException | BrutException | ParserConfigurationException | TransformerException | SAXException ex) { + } catch (IOException | ParserConfigurationException | TransformerException | SAXException ex) { throw new AndrolibException(ex); } - } - private boolean buildManifestRaw() throws AndrolibException { + ExtFile tmpFile; try { - File apkDir = new File(mApkDir, APK_DIRNAME); - LOGGER.info("Copying raw AndroidManifest.xml..."); - mApkDir.getDirectory().copyToDir(apkDir, APK_MANIFEST_FILENAMES); - return true; - } catch (DirectoryException ex) { + tmpFile = new ExtFile(File.createTempFile("APKTOOL", null)); + } catch (IOException ex) { throw new AndrolibException(ex); } - } + //noinspection ResultOfMethodCallIgnored + tmpFile.delete(); + + File resDir = new File(mApkDir, "res"); + File ninePatch = new File(mApkDir, "9patch"); + if (!ninePatch.isDirectory()) { + ninePatch = null; + } + + LOGGER.info("Building resources with " + AaptManager.getAaptBinaryName(mConfig.aaptVersion) + "..."); - private boolean buildManifest() throws BrutException { try { - if (!new File(mApkDir, "AndroidManifest.xml").exists()) { - return false; - } - if (!mConfig.forceBuildAll) { - LOGGER.info("Checking whether resources has changed..."); - } + AaptInvoker invoker = new AaptInvoker(mConfig, mApkInfo); + invoker.invokeAapt(tmpFile, manifest, resDir, ninePatch, null, getIncludeFiles()); - File apkDir = new File(mApkDir, APK_DIRNAME); + Directory tmpDir = tmpFile.getDirectory(); + tmpDir.copyToDir(outDir, "AndroidManifest.xml"); + tmpDir.copyToDir(outDir, "resources.arsc"); + tmpDir.copyToDir(outDir, ApkInfo.RESOURCES_DIRNAMES); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } finally { + //noinspection ResultOfMethodCallIgnored + tmpFile.delete(); + } + } - if (mConfig.forceBuildAll || isModified(newFiles(APK_MANIFEST_FILENAMES, mApkDir), - newFiles(APK_MANIFEST_FILENAMES, apkDir))) { - LOGGER.info("Building AndroidManifest.xml..."); + private void buildManifest(File outDir, File manifest) throws AndrolibException { + if (!mConfig.forceBuildAll) { + LOGGER.info("Checking whether AndroidManifest.xml has changed..."); + if (!isModified(manifest, new File(outDir, "AndroidManifest.xml"))) { + return; + } + } - File apkFile = File.createTempFile("APKTOOL", null); - //noinspection ResultOfMethodCallIgnored - apkFile.delete(); + ExtFile tmpFile; + try { + tmpFile = new ExtFile(File.createTempFile("APKTOOL", null)); + } catch (IOException ex) { + throw new AndrolibException(ex); + } + //noinspection ResultOfMethodCallIgnored + tmpFile.delete(); - File ninePatch = new File(mApkDir, "9patch"); - if (!ninePatch.exists()) { - ninePatch = null; - } + File ninePatch = new File(mApkDir, "9patch"); + if (!ninePatch.isDirectory()) { + ninePatch = null; + } - AaptInvoker invoker = new AaptInvoker(mConfig, mApkInfo); - invoker.invokeAapt(apkFile, new File(mApkDir, "AndroidManifest.xml"), - null, ninePatch, null, getIncludeFiles()); + LOGGER.info("Building AndroidManifest.xml with " + AaptManager.getAaptBinaryName(mConfig.aaptVersion) + "..."); - Directory tmpDir = new ExtFile(apkFile).getDirectory(); - tmpDir.copyToDir(apkDir, APK_MANIFEST_FILENAMES); + try { + AaptInvoker invoker = new AaptInvoker(mConfig, mApkInfo); + invoker.invokeAapt(tmpFile, manifest, null, ninePatch, null, getIncludeFiles()); - //noinspection ResultOfMethodCallIgnored - apkFile.delete(); - } - return true; - } catch (IOException | DirectoryException ex) { + Directory tmpDir = tmpFile.getDirectory(); + tmpDir.copyToDir(outDir, "AndroidManifest.xml"); + } catch (DirectoryException ex) { throw new AndrolibException(ex); } catch (AndrolibException ex) { LOGGER.warning("Parse AndroidManifest.xml failed, treat it as raw file."); - return buildManifestRaw(); + copyManifestRaw(outDir); + } finally { + //noinspection ResultOfMethodCallIgnored + tmpFile.delete(); } } - private void copyLibs() throws AndrolibException { - buildLibrary("lib"); - buildLibrary("libs"); - buildLibrary("kotlin"); - buildLibrary("META-INF/services"); - } + private void copyManifestRaw(File outDir) throws AndrolibException { + LOGGER.info("Copying raw manifest..."); + try { + Directory in = mApkDir.getDirectory(); - private void buildLibrary(String folder) throws AndrolibException { - File working = new File(mApkDir, folder); + in.copyToDir(outDir, "AndroidManifest.xml"); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } - if (!working.exists()) { + private void copyOriginalFiles(File outDir) throws AndrolibException { + if (!mConfig.copyOriginalFiles) { return; } - File stored = new File(mApkDir, APK_DIRNAME + "/" + folder); - if (mConfig.forceBuildAll || isModified(working, stored)) { - LOGGER.info("Copying libs... (/" + folder + ")"); - try { - OS.rmdir(stored); - OS.cpdir(working, stored); - } catch (BrutException ex) { - throw new AndrolibException(ex); - } + ExtFile originalDir = new ExtFile(mApkDir, "original"); + if (!originalDir.isDirectory()) { + return; } - } - private void copyOriginalFilesIfEnabled() throws AndrolibException { - if (mConfig.copyOriginalFiles) { - File originalDir = new File(mApkDir, "original"); - if (originalDir.exists()) { - try { - LOGGER.info("Copy original files..."); - Directory in = (new ExtFile(originalDir)).getDirectory(); - if (in.containsFile("AndroidManifest.xml")) { - LOGGER.info("Copy AndroidManifest.xml..."); - in.copyToDir(new File(mApkDir, APK_DIRNAME), "AndroidManifest.xml"); - } - if (in.containsFile("stamp-cert-sha256")) { - LOGGER.info("Copy stamp-cert-sha256..."); - in.copyToDir(new File(mApkDir, APK_DIRNAME), "stamp-cert-sha256"); - } - if (in.containsDir("META-INF")) { - LOGGER.info("Copy META-INF..."); - in.copyToDir(new File(mApkDir, APK_DIRNAME), "META-INF"); - } - } catch (DirectoryException ex) { - throw new AndrolibException(ex); + LOGGER.info("Copying original files..."); + try { + Directory in = originalDir.getDirectory(); + + for (String fileName : in.getFiles(true)) { + if (ApkInfo.ORIGINAL_FILENAMES_PATTERN.matcher(fileName).matches()) { + in.copyToDir(outDir, fileName); } } + } catch (DirectoryException ex) { + throw new AndrolibException(ex); } } - private void buildApk(File outApk) throws AndrolibException { - LOGGER.info("Building apk file..."); - if (outApk.exists()) { - //noinspection ResultOfMethodCallIgnored - outApk.delete(); - } else { - File outDir = outApk.getParentFile(); - if (outDir != null && !outDir.exists()) { - //noinspection ResultOfMethodCallIgnored - outDir.mkdirs(); + private void importRawFiles(ZipOutputStream outStream) throws AndrolibException { + for (String dirName : ApkInfo.RAW_DIRNAMES) { + File rawDir = new File(mApkDir, dirName); + if (!rawDir.isDirectory()) { + continue; } - } - File assetDir = new File(mApkDir, "assets"); - if (!assetDir.exists()) { - assetDir = null; - } - try (ZipOutputStream zipOutputStream = new ZipOutputStream(Files.newOutputStream(outApk.toPath()))) { - // zip all AAPT-generated files - ZipUtils.zipFoldersPreserveStream(new File(mApkDir, APK_DIRNAME), zipOutputStream, assetDir, mApkInfo.doNotCompress); - // we must copy some files manually - // this is because Aapt won't add files it doesn't know (ex unknown files) - if (mApkInfo.unknownFiles != null) { - LOGGER.info("Copying unknown files/dir..."); - copyUnknownFiles(zipOutputStream, mApkInfo.unknownFiles); + LOGGER.info("Importing " + dirName + "..."); + try { + ZipUtils.zipDir(mApkDir, dirName, outStream, mApkInfo.doNotCompress); + } catch (IOException ex) { + throw new AndrolibException(ex); } - } catch (IOException | BrutException e) { - throw new AndrolibException(e); } } - private void copyUnknownFiles(ZipOutputStream outputFile, Map files) - throws BrutException, IOException { - File unknownFileDir = new File(mApkDir, UNK_DIRNAME); - - // loop through unknown files - for (Map.Entry unknownFileInfo : files.entrySet()) { - File inputFile; - - try { - inputFile = new File(unknownFileDir, BrutIO.sanitizeFilepath(unknownFileDir, unknownFileInfo.getKey())); - } catch (RootUnknownFileException | InvalidUnknownFileException | TraversalUnknownFileException exception) { - LOGGER.warning(String.format("Skipping file %s (%s)", unknownFileInfo.getKey(), exception.getMessage())); - continue; - } - - if (inputFile.isDirectory()) { - continue; - } - - ZipEntry newEntry = new ZipEntry(unknownFileInfo.getKey()); - int method = Integer.parseInt(unknownFileInfo.getValue()); - LOGGER.fine(String.format("Copying unknown file %s with method %d", unknownFileInfo.getKey(), method)); - if (method == ZipEntry.STORED) { - newEntry.setMethod(ZipEntry.STORED); - newEntry.setSize(inputFile.length()); - newEntry.setCompressedSize(-1); - BufferedInputStream unknownFile = new BufferedInputStream(Files.newInputStream(inputFile.toPath())); - CRC32 crc = BrutIO.calculateCrc(unknownFile); - newEntry.setCrc(crc.getValue()); - unknownFile.close(); - } else { - newEntry.setMethod(ZipEntry.DEFLATED); - } - outputFile.putNextEntry(newEntry); + private void importUnknownFiles(ZipOutputStream outStream) throws AndrolibException { + File unknownDir = new File(mApkDir, "unknown"); + if (!unknownDir.isDirectory()) { + return; + } - BrutIO.copy(inputFile, outputFile); - outputFile.closeEntry(); + LOGGER.info("Importing unknown files..."); + try { + ZipUtils.zipDir(unknownDir, outStream, mApkInfo.doNotCompress); + } catch (IOException ex) { + throw new AndrolibException(ex); } } @@ -533,11 +519,7 @@ private File[] getIncludeFiles() throws AndrolibException { } private boolean isModified(File working, File stored) { - return !stored.exists() || BrutIO.recursiveModifiedTime(working) > BrutIO .recursiveModifiedTime(stored); - } - - private boolean isFile(File working) { - return working.exists(); + return !stored.exists() || BrutIO.recursiveModifiedTime(working) > BrutIO.recursiveModifiedTime(stored); } private boolean isModified(File[] working, File[] stored) { @@ -549,29 +531,11 @@ private boolean isModified(File[] working, File[] stored) { return BrutIO.recursiveModifiedTime(working) > BrutIO.recursiveModifiedTime(stored); } - private File[] newFiles(String[] names, File dir) { + private File[] newFiles(File dir, String[] names) { File[] files = new File[names.length]; for (int i = 0; i < names.length; i++) { files[i] = new File(dir, names[i]); } return files; } - - public boolean detectWhetherAppIsFramework() throws AndrolibException { - File publicXml = new File(mApkDir, "res/values/public.xml"); - if (!publicXml.exists()) { - return false; - } - - Iterator it; - try { - it = IOUtils.lineIterator(new FileReader(new File(mApkDir, "res/values/public.xml"))); - } catch (FileNotFoundException ex) { - throw new AndrolibException( - "Could not detect whether app is framework one", ex); - } - it.next(); - it.next(); - return it.next().contains("0x01"); - } } 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 1f328fbefc..b0e8437868 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 @@ -39,53 +39,41 @@ public class ApkDecoder { private final static Logger LOGGER = Logger.getLogger(ApkDecoder.class.getName()); - private final AtomicReference mBuildError = new AtomicReference<>(null); + // extensions of files that are often packed uncompressed + private final static Pattern NO_COMPRESS_EXT_PATTERN = Pattern.compile( + "dex|arsc|so|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"); + + private final ExtFile mApkFile; private final Config mConfig; - private final ApkInfo mApkInfo; + private ApkInfo mApkInfo; + private ResourcesDecoder mResDecoder; private volatile int mMinSdkVersion = 0; private BackgroundWorker mWorker; - - private final static String SMALI_DIRNAME = "smali"; - private final static String UNK_DIRNAME = "unknown"; - 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", "stamp-cert-sha256" }; - 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)$"); - - public ApkDecoder(File apkFile) { - this(Config.getDefaultConfig(), new ExtFile(apkFile)); - } + private final AtomicReference mBuildError = new AtomicReference<>(null); public ApkDecoder(ExtFile apkFile) { - this(Config.getDefaultConfig(), apkFile); + this(apkFile, Config.getDefaultConfig()); } - public ApkDecoder(Config config, File apkFile) { - this(config, new ExtFile(apkFile)); - } - - public ApkDecoder(Config config, ExtFile apkFile) { + public ApkDecoder(ExtFile apkFile, Config config) { + mApkFile = apkFile; mConfig = config; - mApkInfo = new ApkInfo(apkFile); } - public ApkInfo decode(File outDir) throws AndrolibException, IOException, DirectoryException { - ExtFile apkFile = mApkInfo.getApkFile(); + public ApkInfo decode(File outDir) throws AndrolibException { + if (!mConfig.forceDelete && outDir.exists()) { + throw new OutDirExistsException(); + } + if (!mApkFile.isFile() || !mApkFile.canRead()) { + throw new InFileNotFoundException(); + } + if (mConfig.jobs > 1) { + mWorker = new BackgroundWorker(mConfig.jobs - 1); + } try { - mWorker = new BackgroundWorker(mConfig.jobs); - if (!mConfig.forceDelete && outDir.exists()) { - throw new OutDirExistsException(); - } - - if (!apkFile.isFile() || !apkFile.canRead()) { - throw new InFileNotFoundException(); - } + mApkInfo = new ApkInfo(mApkFile); + mResDecoder = new ResourcesDecoder(mConfig, mApkInfo); try { OS.rmdir(outDir); @@ -95,213 +83,197 @@ public ApkInfo decode(File outDir) throws AndrolibException, IOException, Direct //noinspection ResultOfMethodCallIgnored outDir.mkdirs(); - LOGGER.info("Using Apktool " + ApktoolProperties.getVersion() + " on " + mApkInfo.apkFileName + - " with " + mConfig.jobs + " thread(s)."); - - if (mApkInfo.hasSources()) { - switch (mConfig.decodeSources) { - case Config.DECODE_SOURCES_NONE: - copySourcesRaw(outDir, "classes.dex"); - break; - case Config.DECODE_SOURCES_SMALI: - case Config.DECODE_SOURCES_SMALI_ONLY_MAIN_CLASSES: - scheduleDecodeSourcesSmali(outDir, "classes.dex"); - break; - } - } - - if (mApkInfo.hasMultipleSources()) { - // foreach unknown dex file in root, lets disassemble it - Set files = apkFile.getDirectory().getFiles(true); - for (String file : files) { - if (file.endsWith(".dex")) { - if (!file.equalsIgnoreCase("classes.dex")) { - switch(mConfig.decodeSources) { - case Config.DECODE_SOURCES_NONE: - copySourcesRaw(outDir, file); - break; - case Config.DECODE_SOURCES_SMALI: - scheduleDecodeSourcesSmali(outDir, file); - break; - case Config.DECODE_SOURCES_SMALI_ONLY_MAIN_CLASSES: - if (file.startsWith("classes") && file.endsWith(".dex")) { - scheduleDecodeSourcesSmali(outDir, file); - } else { - copySourcesRaw(outDir, file); - } - break; - } - } - } - } - } - - ResourcesDecoder resourcesDecoder = new ResourcesDecoder(mConfig, mApkInfo); + LOGGER.info("Using Apktool " + ApktoolProperties.getVersion() + " on " + mApkFile.getName() + + (mWorker != null ? " with " + mConfig.jobs + " threads" : "")); - if (mApkInfo.hasResources()) { - switch (mConfig.decodeResources) { - case Config.DECODE_RESOURCES_NONE: - copyResourcesRaw(outDir); - break; - case Config.DECODE_RESOURCES_FULL: - resourcesDecoder.decodeResources(outDir); - break; - } - } + decodeSources(outDir); + decodeResources(outDir); + decodeManifest(outDir); - if (mApkInfo.hasManifest()) { - if (mConfig.decodeResources == Config.DECODE_RESOURCES_FULL || - mConfig.forceDecodeManifest == Config.FORCE_DECODE_MANIFEST_FULL) { - resourcesDecoder.decodeManifest(outDir); - } - else { - copyManifestRaw(outDir); + if (mWorker != null) { + mWorker.waitForFinish(); + if (mBuildError.get() != null) { + throw mBuildError.get(); } } - resourcesDecoder.updateApkInfo(outDir); + copyOriginalFiles(outDir); copyRawFiles(outDir); copyUnknownFiles(outDir); - recordUncompressedFiles(resourcesDecoder.getResFileMapping()); - copyOriginalFiles(outDir); - mWorker.waitForFinish(); - if (mBuildError.get() != null) { - throw mBuildError.get(); - } - - // In case we have no resources. We should store the minSdk we pulled from the source opcode api level - if (!mApkInfo.hasResources() && mMinSdkVersion > 0) { - mApkInfo.setSdkInfoField("minSdkVersion", Integer.toString(mMinSdkVersion)); - } - writeApkInfo(outDir); return mApkInfo; } finally { - mWorker.shutdownNow(); + if (mWorker != null) { + mWorker.shutdownNow(); + } try { - apkFile.close(); + mApkFile.close(); } catch (IOException ignored) {} } } - private void writeApkInfo(File outDir) throws AndrolibException { - mApkInfo.save(new File(outDir, "apktool.yml")); - } + private void decodeSources(File outDir) throws AndrolibException { + if (!mApkInfo.hasSources()) { + return; + } - private void copyManifestRaw(File outDir) throws AndrolibException { - try { - LOGGER.info("Copying raw manifest..."); - mApkInfo.getApkFile().getDirectory().copyToDir(outDir, APK_MANIFEST_FILENAMES); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); + switch (mConfig.decodeSources) { + case Config.DECODE_SOURCES_NONE: + copySourcesRaw(outDir, "classes.dex"); + break; + case Config.DECODE_SOURCES_SMALI: + case Config.DECODE_SOURCES_SMALI_ONLY_MAIN_CLASSES: + decodeSourcesSmali(outDir, "classes.dex"); + break; } - } - private void copyResourcesRaw(File outDir) throws AndrolibException { try { - LOGGER.info("Copying raw resources..."); - mApkInfo.getApkFile().getDirectory().copyToDir(outDir, APK_RESOURCES_FILENAMES); + Directory in = mApkFile.getDirectory(); + + // foreach unknown dex file in root, lets disassemble it + for (String fileName : in.getFiles(true)) { + if (fileName.endsWith(".dex") && !fileName.equals("classes.dex")) { + switch (mConfig.decodeSources) { + case Config.DECODE_SOURCES_NONE: + copySourcesRaw(outDir, fileName); + break; + case Config.DECODE_SOURCES_SMALI: + decodeSourcesSmali(outDir, fileName); + break; + case Config.DECODE_SOURCES_SMALI_ONLY_MAIN_CLASSES: + if (fileName.startsWith("classes")) { + decodeSourcesSmali(outDir, fileName); + } else { + copySourcesRaw(outDir, fileName); + } + break; + } + } + } } catch (DirectoryException ex) { throw new AndrolibException(ex); } } - private void copySourcesRaw(File outDir, String filename) throws AndrolibException { + private void copySourcesRaw(File outDir, String fileName) throws AndrolibException { + LOGGER.info("Copying raw " + fileName + " file..."); try { - LOGGER.info("Copying raw " + filename + " file..."); - mApkInfo.getApkFile().getDirectory().copyToDir(outDir, filename); + Directory in = mApkFile.getDirectory(); + + in.copyToDir(outDir, fileName); } catch (DirectoryException ex) { throw new AndrolibException(ex); } } - private void scheduleDecodeSourcesSmali(File outDir, String filename) { - mWorker.submit(() -> { - try { - decodeSourcesSmali(outDir, filename); - } catch (AndrolibException e) { - mBuildError.compareAndSet(null, new RuntimeException(e)); - } - }); + private void decodeSourcesSmali(File outDir, String fileName) throws AndrolibException { + if (mWorker != null) { + mWorker.submit(() -> { + if (mBuildError.get() == null) { + try { + decodeSourcesSmaliJob(outDir, fileName); + } catch (AndrolibException ex) { + mBuildError.compareAndSet(null, ex); + } + } + }); + } else { + decodeSourcesSmaliJob(outDir, fileName); + } } - private void decodeSourcesSmali(File outDir, String filename) throws AndrolibException { + private void decodeSourcesSmaliJob(File outDir, String fileName) throws AndrolibException { + File smaliDir; + if (fileName.equals("classes.dex")) { + smaliDir = new File(outDir, "smali"); + } else { + smaliDir = new File(outDir, "smali_" + fileName.substring(0, fileName.indexOf("."))); + } try { - File smaliDir; - if (filename.equalsIgnoreCase("classes.dex")) { - smaliDir = new File(outDir, SMALI_DIRNAME); - } else { - smaliDir = new File(outDir, SMALI_DIRNAME + "_" + filename.substring(0, filename.indexOf("."))); - } OS.rmdir(smaliDir); - //noinspection ResultOfMethodCallIgnored - smaliDir.mkdirs(); - LOGGER.info("Baksmaling " + filename + "..."); - DexFile dexFile = SmaliDecoder.decode(mApkInfo.getApkFile(), smaliDir, filename, - mConfig.baksmaliDebugMode, mConfig.apiLevel); - int minSdkVersion = dexFile.getOpcodes().api; - if (mMinSdkVersion == 0 || mMinSdkVersion > minSdkVersion) { - mMinSdkVersion = minSdkVersion; - } } catch (BrutException ex) { throw new AndrolibException(ex); } + //noinspection ResultOfMethodCallIgnored + smaliDir.mkdirs(); + + LOGGER.info("Baksmaling " + fileName + "..."); + DexFile dexFile = SmaliDecoder.decode(mApkFile, smaliDir, fileName, + mConfig.baksmaliDebugMode, mConfig.apiLevel); + + // record minSdkVersion for jars + int minSdkVersion = dexFile.getOpcodes().api; + if (mMinSdkVersion == 0 || mMinSdkVersion > minSdkVersion) { + mMinSdkVersion = minSdkVersion; + } } - private void copyRawFiles(File outDir) throws AndrolibException { - LOGGER.info("Copying assets and libs..."); + private void decodeResources(File outDir) throws AndrolibException { + if (!mApkInfo.hasResources()) { + return; + } + + switch (mConfig.decodeResources) { + case Config.DECODE_RESOURCES_NONE: + copyResourcesRaw(outDir); + break; + case Config.DECODE_RESOURCES_FULL: + mResDecoder.decodeResources(outDir); + break; + } + } + + private void copyResourcesRaw(File outDir) throws AndrolibException { + LOGGER.info("Copying raw resources..."); try { - Directory in = mApkInfo.getApkFile().getDirectory(); + Directory in = mApkFile.getDirectory(); - if (mConfig.decodeAssets == Config.DECODE_ASSETS_FULL) { - if (in.containsDir("assets")) { - in.copyToDir(outDir, "assets"); - } - } - if (in.containsDir("lib")) { - in.copyToDir(outDir, "lib"); - } - if (in.containsDir("libs")) { - in.copyToDir(outDir, "libs"); - } - if (in.containsDir("kotlin")) { - in.copyToDir(outDir, "kotlin"); - } + in.copyToDir(outDir, "resources.arsc"); + in.copyToDir(outDir, ApkInfo.RESOURCES_DIRNAMES); } catch (DirectoryException ex) { throw new AndrolibException(ex); } } - private boolean isAPKFileNames(String file) { - for (String apkFile : APK_STANDARD_ALL_FILENAMES) { - if (file.startsWith("classes") && file.endsWith(".dex")) { - return true; - } + private void decodeManifest(File outDir) throws AndrolibException { + if (!mApkInfo.hasManifest()) { + return; + } - if (apkFile.equals(file) || file.startsWith(apkFile + "/")) { - return true; - } + if (mConfig.decodeResources == Config.DECODE_RESOURCES_FULL + || mConfig.forceDecodeManifest == Config.FORCE_DECODE_MANIFEST_FULL) { + mResDecoder.decodeManifest(outDir); + } else { + copyManifestRaw(outDir); } - return false; } - private void copyUnknownFiles(File outDir) throws AndrolibException { - LOGGER.info("Copying unknown files..."); - File unknownOut = new File(outDir, UNK_DIRNAME); + private void copyManifestRaw(File outDir) throws AndrolibException { + LOGGER.info("Copying raw manifest..."); + try { + Directory in = mApkFile.getDirectory(); + + in.copyToDir(outDir, "AndroidManifest.xml"); + } catch (DirectoryException ex) { + throw new AndrolibException(ex); + } + } + + private void copyRawFiles(File outDir) throws AndrolibException { try { - Directory unk = mApkInfo.getApkFile().getDirectory(); - - // loop all items in container recursively, ignoring any that are pre-defined by aapt - Set files = unk.getFiles(true); - for (String file : files) { - if (!isAPKFileNames(file) && !file.endsWith(".dex")) { - - // copy file out of archive into special "unknown" folder - unk.copyToDir(unknownOut, file); - // let's record the name of the file, and its compression type - // so that we may re-include it the same way - mApkInfo.addUnknownFileInfo(file, String.valueOf(unk.getCompressionLevel(file))); + Directory in = mApkFile.getDirectory(); + + for (String dirName : ApkInfo.RAW_DIRNAMES) { + if ((mConfig.decodeAssets == Config.DECODE_ASSETS_FULL || !dirName.equals("assets")) + && in.containsDir(dirName)) { + LOGGER.info("Copying " + dirName + "..."); + for (String fileName : in.getDir(dirName).getFiles(true)) { + fileName = dirName + "/" + fileName; + if (!ApkInfo.ORIGINAL_FILENAMES_PATTERN.matcher(fileName).matches()) { + in.copyToDir(outDir, fileName); + } + } } } } catch (DirectoryException ex) { @@ -311,29 +283,29 @@ private void copyUnknownFiles(File outDir) throws AndrolibException { private void copyOriginalFiles(File outDir) throws AndrolibException { LOGGER.info("Copying original files..."); - File originalDir = new File(outDir, "original"); - if (!originalDir.exists()) { - //noinspection ResultOfMethodCallIgnored - originalDir.mkdirs(); + try { + Directory in = mApkFile.getDirectory(); + File originalDir = new File(outDir, "original"); + + for (String fileName : in.getFiles(true)) { + if (ApkInfo.ORIGINAL_FILENAMES_PATTERN.matcher(fileName).matches()) { + in.copyToDir(originalDir, fileName); + } + } + } catch (DirectoryException ex) { + throw new AndrolibException(ex); } + } + private void copyUnknownFiles(File outDir) throws AndrolibException { + LOGGER.info("Copying unknown files..."); try { - Directory in = mApkInfo.getApkFile().getDirectory(); - if (in.containsFile("AndroidManifest.xml")) { - in.copyToDir(originalDir, "AndroidManifest.xml"); - } - if (in.containsFile("stamp-cert-sha256")) { - in.copyToDir(originalDir, "stamp-cert-sha256"); - } - if (in.containsDir("META-INF")) { - in.copyToDir(originalDir, "META-INF"); - - if (in.containsDir("META-INF/services")) { - // If the original APK contains the folder META-INF/services folder - // that is used for service locators (like coroutines on android), - // copy it to the destination folder, so it does not get dropped. - LOGGER.info("Copying META-INF/services directory"); - in.copyToDir(outDir, "META-INF/services"); + Directory in = mApkFile.getDirectory(); + File unknownDir = new File(outDir, "unknown"); + + for (String fileName : in.getFiles(true)) { + if (!ApkInfo.STANDARD_FILENAMES_PATTERN.matcher(fileName).matches()) { + in.copyToDir(unknownDir, fileName); } } } catch (DirectoryException ex) { @@ -341,36 +313,69 @@ private void copyOriginalFiles(File outDir) throws AndrolibException { } } - private void recordUncompressedFiles(Map resFileMapping) throws AndrolibException { + private void writeApkInfo(File outDir) throws AndrolibException { + mResDecoder.updateApkInfo(outDir); + + // in case we have no resources, we should store the minSdk we pulled from the source opcode api level + if (!mApkInfo.hasResources() && mMinSdkVersion > 0) { + mApkInfo.setMinSdkVersion(Integer.toString(mMinSdkVersion)); + } + + // record uncompressed files try { - List uncompressedFilesOrExts = new ArrayList<>(); - Directory unk = mApkInfo.getApkFile().getDirectory(); - Set files = unk.getFiles(true); - - for (String file : files) { - if (isAPKFileNames(file) && unk.getCompressionLevel(file) == 0) { - String extOrFile = ""; - if (unk.getSize(file) != 0) { - extOrFile = FilenameUtils.getExtension(file); + Map resFileMapping = mResDecoder.getResFileMapping(); + Set uncompressedExts = new HashSet<>(); + Set uncompressedFiles = new HashSet<>(); + Directory in = mApkFile.getDirectory(); + + for (String fileName : in.getFiles(true)) { + if (in.getCompressionLevel(fileName) == 0) { + String ext; + if (in.getSize(fileName) > 0 + && !(ext = FilenameUtils.getExtension(fileName)).isEmpty() + && NO_COMPRESS_EXT_PATTERN.matcher(ext).matches()) { + uncompressedExts.add(ext); + } else { + uncompressedFiles.add(resFileMapping.getOrDefault(fileName, fileName)); } + } + } - if (extOrFile.isEmpty() || !NO_COMPRESS_PATTERN.matcher(extOrFile).find()) { - extOrFile = file; - if (resFileMapping.containsKey(extOrFile)) { - extOrFile = resFileMapping.get(extOrFile); - } - } - if (!uncompressedFilesOrExts.contains(extOrFile)) { - uncompressedFilesOrExts.add(extOrFile); + // exclude files with an already recorded extenstion + if (!uncompressedExts.isEmpty() && !uncompressedFiles.isEmpty()) { + Iterator it = uncompressedFiles.iterator(); + while (it.hasNext()) { + String fileName = it.next(); + String ext = FilenameUtils.getExtension(fileName); + if (uncompressedExts.contains(ext)) { + it.remove(); } } } + // update apk info - if (!uncompressedFilesOrExts.isEmpty()) { - mApkInfo.doNotCompress = uncompressedFilesOrExts; + int doNotCompressSize = uncompressedExts.size() + uncompressedFiles.size(); + if (doNotCompressSize > 0) { + List doNotCompress = new ArrayList<>(doNotCompressSize); + if (!uncompressedExts.isEmpty()) { + List uncompressedExtsList = new ArrayList<>(uncompressedExts); + uncompressedExtsList.sort(null); + doNotCompress.addAll(uncompressedExtsList); + } + if (!uncompressedFiles.isEmpty()) { + List uncompressedFilesList = new ArrayList<>(uncompressedFiles); + uncompressedFilesList.sort(null); + doNotCompress.addAll(uncompressedFilesList); + } + if (!doNotCompress.isEmpty()) { + mApkInfo.doNotCompress = doNotCompress; + } } } catch (DirectoryException ex) { throw new AndrolibException(ex); } + + // write apk info to file + mApkInfo.save(new File(outDir, "apktool.yml")); } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApktoolProperties.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApktoolProperties.java index 10fcf31a34..b9caea6fde 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApktoolProperties.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApktoolProperties.java @@ -65,7 +65,7 @@ private static void loadProps() { properties.load(templateStream); version = properties.getProperty("application.version"); templateStream.close(); - } catch (IOException ignored) { } + } catch (IOException ignored) {} } sProps.put("baksmaliVersion", version); @@ -83,7 +83,7 @@ private static void loadProps() { properties.load(templateStream); version = properties.getProperty("application.version"); templateStream.close(); - } catch (IOException ignored) { } + } catch (IOException ignored) {} } sProps.put("smaliVersion", version); } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/BackgroundWorker.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/BackgroundWorker.java index b30d715af2..1b702a2745 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/BackgroundWorker.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/BackgroundWorker.java @@ -34,8 +34,8 @@ public void waitForFinish() { for (Future future : mWorkerFutures) { try { future.get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); + } catch (InterruptedException | ExecutionException ex) { + throw new RuntimeException(ex); } } mWorkerFutures.clear(); diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/Config.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/Config.java index 63d3cc9114..85e71a1c69 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/Config.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/Config.java @@ -51,8 +51,8 @@ public class Config { public boolean verbose = false; public boolean copyOriginalFiles = false; public boolean updateFiles = false; - public boolean useAapt2 = true; public boolean noCrunch = false; + public boolean noApk = false; // Decode options public short decodeSources = DECODE_SOURCES_SMALI; @@ -71,13 +71,9 @@ public class Config { public String frameworkDirectory = null; public String frameworkTag = null; public String aaptPath = ""; - public int aaptVersion = 1; // default to v1 + public int aaptVersion = 2; // default to v2 // Utility functions - public boolean isAapt2() { - return this.useAapt2 || this.aaptVersion == 2; - } - public boolean isDecodeResolveModeUsingDummies() { return decodeResolveMode == DECODE_RES_RESOLVE_DUMMY; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/ApkInfo.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/ApkInfo.java index 9f8ff7dbd5..6b6991d84e 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/ApkInfo.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/ApkInfo.java @@ -25,32 +25,39 @@ import java.io.*; import java.util.*; +import java.util.regex.Pattern; public class ApkInfo implements YamlSerializable { + public final static String[] RESOURCES_DIRNAMES = new String[] { "res", "r", "R" }; + public final static String[] RAW_DIRNAMES = new String[] { "assets", "lib", "libs", "kotlin", "META-INF/services" }; + + public final static Pattern ORIGINAL_FILENAMES_PATTERN = Pattern.compile( + "AndroidManifest\\.xml|META-INF/[^/]+\\.(RSA|SF|MF)|stamp-cert-sha256"); + + public final static Pattern STANDARD_FILENAMES_PATTERN = Pattern.compile( + "[^/]+\\.dex|resources\\.arsc|(" + String.join("|", RESOURCES_DIRNAMES) + "|" + + String.join("|", RAW_DIRNAMES) + ")/.*|" + ORIGINAL_FILENAMES_PATTERN.pattern()); + + // only set when loaded from a file (not a stream) private transient ExtFile mApkFile; public String version; public String apkFileName; public boolean isFrameworkApk; public UsesFramework usesFramework; - private Map sdkInfo = new LinkedHashMap<>(); + public Map sdkInfo = new LinkedHashMap<>(); public PackageInfo packageInfo = new PackageInfo(); public VersionInfo versionInfo = new VersionInfo(); - public boolean resourcesAreCompressed; public boolean sharedLibrary; public boolean sparseResources; - public Map unknownFiles = new LinkedHashMap<>(); - public List doNotCompress; - - /** @deprecated use {@link #resourcesAreCompressed} */ - public boolean compressionType; + public List doNotCompress = new ArrayList<>(); public ApkInfo() { this(null); } public ApkInfo(ExtFile apkFile) { - this.version = ApktoolProperties.getVersion(); + version = ApktoolProperties.getVersion(); if (apkFile != null) { setApkFile(apkFile); } @@ -62,68 +69,44 @@ public ExtFile getApkFile() { public void setApkFile(ExtFile apkFile) { mApkFile = apkFile; - if (this.apkFileName == null) { - this.apkFileName = apkFile.getName(); - } - } - - public boolean hasManifest() throws AndrolibException { - if (mApkFile == null) { - return false; - } - try { - return mApkFile.getDirectory().containsFile("AndroidManifest.xml"); - } catch (DirectoryException ex) { - throw new AndrolibException(ex); + if (apkFileName == null) { + apkFileName = apkFile.getName(); } } - public boolean hasResources() throws AndrolibException { + public boolean hasSources() throws AndrolibException { if (mApkFile == null) { return false; } try { - return mApkFile.getDirectory().containsFile("resources.arsc"); + return mApkFile.getDirectory().containsFile("classes.dex"); } catch (DirectoryException ex) { throw new AndrolibException(ex); } } - public boolean hasSources() throws AndrolibException { + public boolean hasManifest() throws AndrolibException { if (mApkFile == null) { return false; } try { - return mApkFile.getDirectory().containsFile("classes.dex"); + return mApkFile.getDirectory().containsFile("AndroidManifest.xml"); } catch (DirectoryException ex) { throw new AndrolibException(ex); } } - public boolean hasMultipleSources() throws AndrolibException { + public boolean hasResources() throws AndrolibException { if (mApkFile == null) { return false; } try { - Set files = mApkFile.getDirectory().getFiles(false); - for (String file : files) { - if (file.endsWith(".dex")) { - if (!file.equalsIgnoreCase("classes.dex")) { - return true; - } - } - } - - return false; + return mApkFile.getDirectory().containsFile("resources.arsc"); } catch (DirectoryException ex) { throw new AndrolibException(ex); } } - public void addUnknownFileInfo(String file, String value) { - unknownFiles.put(file, value); - } - public String checkTargetSdkVersionBounds() { int target = mapSdkShorthandToVersion(getTargetSdkVersion()); @@ -135,35 +118,35 @@ public String checkTargetSdkVersionBounds() { return Integer.toString(target); } - public Map getSdkInfo() { - return sdkInfo; - } - - public void setSdkInfo(Map sdkInfo) { - this.sdkInfo = sdkInfo; - } - - public void setSdkInfoField(String key, String value) { - sdkInfo.put(key, value); - } - public String getMinSdkVersion() { return sdkInfo.get("minSdkVersion"); } + public void setMinSdkVersion(String minSdkVersion) { + sdkInfo.put("minSdkVersion", minSdkVersion); + } + public String getMaxSdkVersion() { return sdkInfo.get("maxSdkVersion"); } + public void setMaxSdkVersion(String maxSdkVersion) { + sdkInfo.put("maxSdkVersion", maxSdkVersion); + } + public String getTargetSdkVersion() { return sdkInfo.get("targetSdkVersion"); } + public void setTargetSdkVersion(String targetSdkVersion) { + sdkInfo.put("targetSdkVersion", targetSdkVersion); + } + public int getMinSdkVersionFromAndroidCodename(String sdkVersion) { int sdkNumber = mapSdkShorthandToVersion(sdkVersion); if (sdkNumber == ResConfigFlags.SDK_BASE) { - return Integer.parseInt(sdkInfo.get("minSdkVersion")); + return Integer.parseInt(getMinSdkVersion()); } return sdkNumber; } @@ -205,15 +188,15 @@ private int mapSdkShorthandToVersion(String sdkVersion) { public void save(File file) throws AndrolibException { try (YamlWriter writer = new YamlWriter(new FileOutputStream(file))) { write(writer); - } catch (FileNotFoundException e) { + } catch (FileNotFoundException ex) { throw new AndrolibException("File not found"); - } catch (Exception e) { - throw new AndrolibException(e); + } catch (Exception ex) { + throw new AndrolibException(ex); } } - public static ApkInfo load(InputStream is) throws AndrolibException { - YamlReader reader = new YamlReader(is); + public static ApkInfo load(InputStream in) throws AndrolibException { + YamlReader reader = new YamlReader(in); ApkInfo apkInfo = new ApkInfo(); reader.readRoot(apkInfo); return apkInfo; @@ -234,56 +217,47 @@ public void readItem(YamlReader reader) throws AndrolibException { YamlLine line = reader.getLine(); switch (line.getKey()) { case "version": { - this.version = line.getValue(); + version = line.getValue(); break; } case "apkFileName": { - this.apkFileName = line.getValue(); + apkFileName = line.getValue(); break; } case "isFrameworkApk": { - this.isFrameworkApk = line.getValueBool(); + isFrameworkApk = line.getValueBool(); break; } case "usesFramework": { - this.usesFramework = new UsesFramework(); + usesFramework = new UsesFramework(); reader.readObject(usesFramework); break; } case "sdkInfo": { + sdkInfo.clear(); reader.readMap(sdkInfo); break; } case "packageInfo": { - this.packageInfo = new PackageInfo(); + packageInfo = new PackageInfo(); reader.readObject(packageInfo); break; } case "versionInfo": { - this.versionInfo = new VersionInfo(); + versionInfo = new VersionInfo(); reader.readObject(versionInfo); break; } - case "compressionType": - case "resourcesAreCompressed": { - this.resourcesAreCompressed = line.getValueBool(); - break; - } case "sharedLibrary": { - this.sharedLibrary = line.getValueBool(); + sharedLibrary = line.getValueBool(); break; } case "sparseResources": { - this.sparseResources = line.getValueBool(); - break; - } - case "unknownFiles": { - this.unknownFiles = new LinkedHashMap<>(); - reader.readMap(unknownFiles); + sparseResources = line.getValueBool(); break; } case "doNotCompress": { - this.doNotCompress = new ArrayList<>(); + doNotCompress.clear(); reader.readStringList(doNotCompress); break; } @@ -299,12 +273,10 @@ public void write(YamlWriter writer) { writer.writeStringMap("sdkInfo", sdkInfo); writer.writeObject("packageInfo", packageInfo); writer.writeObject("versionInfo", versionInfo); - writer.writeBool("resourcesAreCompressed", resourcesAreCompressed); writer.writeBool("sharedLibrary", sharedLibrary); writer.writeBool("sparseResources", sparseResources); - if (unknownFiles.size() > 0) { - writer.writeStringMap("unknownFiles", unknownFiles); + if (!doNotCompress.isEmpty()) { + writer.writeList("doNotCompress", doNotCompress); } - writer.writeList("doNotCompress", doNotCompress); } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/YamlWriter.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/YamlWriter.java index 902b96e8a6..1e4e76ae6e 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/YamlWriter.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/apk/YamlWriter.java @@ -89,9 +89,9 @@ public void writeList(String key, List list) { } writeIndent(); mWriter.println(escape(key) + ":"); - for (T item: list) { + for (T item : list) { writeIndent(); - mWriter.println("- " + item); + mWriter.println("- " + item); } } @@ -102,7 +102,7 @@ public void writeStringMap(String key, Map map) { writeIndent(); mWriter.println(escape(key) + ":"); nextIndent(); - for (String mapKey: map.keySet()) { + for (String mapKey : map.keySet()) { writeString(mapKey, map.get(mapKey)); } prevIndent(); diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/mod/SmaliMod.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/mod/SmaliMod.java index 26652e5857..2e1e65f9a1 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/mod/SmaliMod.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/mod/SmaliMod.java @@ -33,56 +33,47 @@ public class SmaliMod { public static boolean assembleSmaliFile(File smaliFile, DexBuilder dexBuilder, int apiLevel, boolean verboseErrors, boolean printTokens) throws IOException, RecognitionException { - - CommonTokenStream tokens; - smaliFlexLexer lexer; - - InputStream is = Files.newInputStream(smaliFile.toPath()); - InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8); - - lexer = new smaliFlexLexer(reader, apiLevel); - (lexer).setSourceFile(smaliFile); - tokens = new CommonTokenStream(lexer); - - if (printTokens) { - tokens.getTokens(); - - for (int i=0; i 0 || lexer.getNumberOfSyntaxErrors() > 0) { - is.close(); - reader.close(); - return false; - } + smaliParser.smali_file_return result = parser.smali_file(); - CommonTree t = result.getTree(); + if (parser.getNumberOfSyntaxErrors() > 0 || lexer.getNumberOfSyntaxErrors() > 0) { + return false; + } - CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t); - treeStream.setTokenStream(tokens); + CommonTree t = result.getTree(); - smaliTreeWalker dexGen = new smaliTreeWalker(treeStream); - dexGen.setApiLevel(apiLevel); - dexGen.setVerboseErrors(verboseErrors); - dexGen.setDexBuilder(dexBuilder); - dexGen.smali_file(); + CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t); + treeStream.setTokenStream(tokens); - is.close(); - reader.close(); + smaliTreeWalker dexGen = new smaliTreeWalker(treeStream); + dexGen.setApiLevel(apiLevel); + dexGen.setVerboseErrors(verboseErrors); + dexGen.setDexBuilder(dexBuilder); + dexGen.smali_file(); - return dexGen.getNumberOfSyntaxErrors() == 0; + return dexGen.getNumberOfSyntaxErrors() == 0; + } } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/Framework.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/Framework.java index 80ab4b28f3..55b8076fc6 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/Framework.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/Framework.java @@ -52,7 +52,7 @@ public void installFramework(File frameFile) throws AndrolibException { public void installFramework(File frameFile, String tag) throws AndrolibException { InputStream in = null; ZipOutputStream out = null; - try(ZipFile zip = new ZipFile(frameFile)) { + try (ZipFile zip = new ZipFile(frameFile)) { ZipEntry entry = zip.getEntry("resources.arsc"); if (entry == null) { @@ -247,8 +247,8 @@ public void emptyFrameworkDirectory() throws AndrolibException { } } } - } catch (NullPointerException e) { - throw new AndrolibException(e); + } catch (NullPointerException ex) { + throw new AndrolibException(ex); } } } 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 f74f6a9312..a1749e3370 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 @@ -75,7 +75,7 @@ public void decodeManifest(File outDir) throws AndrolibException { } AXmlResourceParser axmlParser = new AndroidManifestResourceParser(mResTable); - XmlPullStreamDecoder fileDecoder = new XmlPullStreamDecoder(axmlParser, getResXmlSerializer()); + ResStreamDecoder fileDecoder = new AndroidManifestPullStreamDecoder(axmlParser, getResXmlSerializer()); Directory inApk, out; InputStream inputStream = null; @@ -91,7 +91,7 @@ public void decodeManifest(File outDir) throws AndrolibException { } inputStream = inApk.getFileInput("AndroidManifest.xml"); outputStream = out.getFileOutput("AndroidManifest.xml"); - fileDecoder.decodeManifest(inputStream, outputStream); + fileDecoder.decode(inputStream, outputStream); } catch (DirectoryException ex) { throw new AndrolibException(ex); @@ -136,8 +136,8 @@ private void adjustPackageManifest(String filePath) throws AndrolibException { // 2) Check if pkgRenamed is null // 3) Check if pkgOriginal === mPackageRenamed // 4) Check if pkgOriginal is ignored via IGNORED_PACKAGES - if (pkgOriginal == null || pkgRenamed == null || pkgOriginal.equalsIgnoreCase(pkgRenamed) - || (Arrays.asList(IGNORED_PACKAGES).contains(pkgOriginal))) { + if (pkgOriginal == null || pkgRenamed == null || pkgOriginal.equals(pkgRenamed) + || (Arrays.asList(IGNORED_PACKAGES).contains(pkgOriginal))) { LOGGER.info("Regular manifest package..."); } else { LOGGER.info("Renamed manifest package found! Replacing " + pkgRenamed + " with " + pkgOriginal); @@ -157,7 +157,7 @@ public void decodeResources(File outDir) throws AndrolibException { decoders.setDecoder("9patch", new Res9patchStreamDecoder()); AXmlResourceParser axmlParser = new AXmlResourceParser(mResTable); - decoders.setDecoder("xml", new XmlPullStreamDecoder(axmlParser, getResXmlSerializer())); + decoders.setDecoder("xml", new ResXmlPullStreamDecoder(axmlParser, getResXmlSerializer())); ResFileDecoder fileDecoder = new ResFileDecoder(decoders); Directory in, out, outRes; @@ -220,7 +220,7 @@ private void generateValuesFile(ResValuesFile valuesFile, Directory out, serial.endDocument(); serial.flush(); outStream.close(); - } catch (IOException | DirectoryException ex) { + } catch (DirectoryException | IOException ex) { throw new AndrolibException("Could not generate: " + valuesFile.getPath(), ex); } } @@ -245,7 +245,7 @@ private void generatePublicXml(ResPackage pkg, Directory out, serial.endDocument(); serial.flush(); outStream.close(); - } catch (IOException | DirectoryException ex) { + } catch (DirectoryException | IOException ex) { throw new AndrolibException("Could not generate public.xml file", ex); } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java index 26bd7bd797..940fb549f3 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResConfigFlags.java @@ -501,7 +501,7 @@ private String getLocaleString() { private String toUpper(char[] character) { StringBuilder sb = new StringBuilder(); - for (char ch: character) { + for (char ch : character) { sb.append(Character.toUpperCase(ch)); } return sb.toString(); diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java index 1c29ef4fb2..24cc6c592d 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java @@ -118,7 +118,7 @@ private ResPackage selectPkgWithMostResSpecs(ResPackage[] pkgs) { for (int i = 0; i < pkgs.length; i++) { ResPackage resPackage = pkgs[i]; - if (resPackage.getResSpecCount() > value && ! resPackage.getName().equalsIgnoreCase("android")) { + if (resPackage.getResSpecCount() > value && ! resPackage.getName().equals("android")) { value = resPackage.getResSpecCount(); id = resPackage.getId(); index = i; @@ -178,8 +178,8 @@ private ResPackage loadFrameworkPkg(int id) throws AndrolibException { private ResPackage[] loadResPackagesFromApk(ExtFile apkFile, boolean keepBrokenResources) throws AndrolibException { try { Directory dir = apkFile.getDirectory(); - try (BufferedInputStream bfi = new BufferedInputStream(dir.getFileInput("resources.arsc"))) { - return ARSCDecoder.decode(bfi, false, keepBrokenResources, this).getPackages(); + try (BufferedInputStream bis = new BufferedInputStream(dir.getFileInput("resources.arsc"))) { + return ARSCDecoder.decode(bis, false, keepBrokenResources, this).getPackages(); } } catch (DirectoryException | IOException ex) { throw new AndrolibException("Could not load resources.arsc from file: " + apkFile, ex); @@ -190,7 +190,7 @@ public ResPackage getHighestSpecPackage() throws AndrolibException { int id = 0; int value = 0; for (ResPackage resPackage : mPackagesById.values()) { - if (resPackage.getResSpecCount() > value && !resPackage.getName().equalsIgnoreCase("android")) { + if (resPackage.getResSpecCount() > value && !resPackage.getName().equals("android")) { value = resPackage.getResSpecCount(); id = resPackage.getId(); } @@ -267,11 +267,11 @@ public void setSparseResources(boolean flag) { } public void clearSdkInfo() { - mApkInfo.getSdkInfo().clear(); + mApkInfo.sdkInfo.clear(); } public void addSdkInfo(String key, String value) { - mApkInfo.getSdkInfo().put(key, value); + mApkInfo.sdkInfo.put(key, value); } public void setVersionName(String versionName) { @@ -310,7 +310,7 @@ private boolean isFrameworkApk() { public void initApkInfo(ApkInfo apkInfo, File outDir) throws AndrolibException { apkInfo.isFrameworkApk = isFrameworkApk(); apkInfo.usesFramework = getUsesFramework(); - if (!mApkInfo.getSdkInfo().isEmpty()) { + if (!mApkInfo.sdkInfo.isEmpty()) { updateSdkInfoFromResources(outDir); } initPackageInfo(); @@ -331,24 +331,25 @@ private UsesFramework getUsesFramework() { } private void updateSdkInfoFromResources(File outDir) { - String refValue; - Map sdkInfo = mApkInfo.getSdkInfo(); - if (sdkInfo.get("minSdkVersion") != null) { - refValue = ResXmlPatcher.pullValueFromIntegers(outDir, sdkInfo.get("minSdkVersion")); + String minSdkVersion = mApkInfo.getMinSdkVersion(); + if (minSdkVersion != null) { + String refValue = ResXmlPatcher.pullValueFromIntegers(outDir, minSdkVersion); if (refValue != null) { - sdkInfo.put("minSdkVersion", refValue); + mApkInfo.setMinSdkVersion(refValue); } } - if (sdkInfo.get("targetSdkVersion") != null) { - refValue = ResXmlPatcher.pullValueFromIntegers(outDir, sdkInfo.get("targetSdkVersion")); + String targetSdkVersion = mApkInfo.getTargetSdkVersion(); + if (targetSdkVersion != null) { + String refValue = ResXmlPatcher.pullValueFromIntegers(outDir, targetSdkVersion); if (refValue != null) { - sdkInfo.put("targetSdkVersion", refValue); + mApkInfo.setTargetSdkVersion(refValue); } } - if (sdkInfo.get("maxSdkVersion") != null) { - refValue = ResXmlPatcher.pullValueFromIntegers(outDir, sdkInfo.get("maxSdkVersion")); + String maxSdkVersion = mApkInfo.getMaxSdkVersion(); + if (maxSdkVersion != null) { + String refValue = ResXmlPatcher.pullValueFromIntegers(outDir, maxSdkVersion); if (refValue != null) { - sdkInfo.put("maxSdkVersion", refValue); + mApkInfo.setMaxSdkVersion(refValue); } } } @@ -367,7 +368,7 @@ private void initPackageInfo() throws AndrolibException { } // only put rename-manifest-package into apktool.yml, if the change will be required - if (renamed != null && !renamed.equalsIgnoreCase(original)) { + if (renamed != null && !renamed.equals(original)) { mApkInfo.packageInfo.renameManifestPackage = renamed; } mApkInfo.packageInfo.forcedPackageId = String.valueOf(id); diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTypeSpec.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTypeSpec.java index f1b0cd243d..57e99e00ee 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTypeSpec.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTypeSpec.java @@ -48,7 +48,7 @@ public int getId() { } public boolean isString() { - return mName.equalsIgnoreCase(RES_TYPE_NAME_STRING); + return mName.equals(RES_TYPE_NAME_STRING); } public ResResSpec getResSpec(String name) throws AndrolibException { diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResArrayValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResArrayValue.java index 2b1e2046b3..d4d34beb36 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResArrayValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResArrayValue.java @@ -41,8 +41,8 @@ public ResArrayValue(ResReferenceValue parent, ResScalarValue[] items) { } @Override - public void serializeToResValuesXml(XmlSerializer serializer, - ResResource res) throws IOException, AndrolibException { + public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) + throws IOException, AndrolibException { String type = getType(); type = (type == null ? "" : type + "-") + "array"; serializer.startTag(null, type); diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResScalarValue.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResScalarValue.java index 30ee763b59..a2f72632b3 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResScalarValue.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResScalarValue.java @@ -73,7 +73,7 @@ public void serializeToResValuesXml(XmlSerializer serializer, String body = encodeAsResXmlValue(); // check for resource reference - if (!type.equalsIgnoreCase("color")) { + if (!type.equals("color")) { if (body.contains("@")) { if (!res.getFilePath().contains("string")) { item = true; @@ -89,7 +89,7 @@ public void serializeToResValuesXml(XmlSerializer serializer, // Android does not allow values (false) for ids.xml anymore // https://issuetracker.google.com/issues/80475496 // But it decodes as a ResBoolean, which makes no sense. So force it to empty - if (type.equalsIgnoreCase("id") && !body.isEmpty()) { + if (type.equals("id") && !body.isEmpty()) { body = ""; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValueFactory.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValueFactory.java index 1d20c79fc2..a6af2a48ce 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValueFactory.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/value/ResValueFactory.java @@ -79,7 +79,7 @@ public ResIntBasedValue factory(String value, int rawValue) { } public ResBagValue bagFactory(int parent, Duo[] items, ResTypeSpec resTypeSpec) - throws AndrolibException { + throws AndrolibException { ResReferenceValue parentVal = newReference(parent, null); if (items.length == 0) { diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java index 24bfe96437..25ea53f339 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ARSCDecoder.java @@ -317,7 +317,7 @@ private ResType readTableType() throws IOException, AndrolibException { int entriesStartAligned = mHeader.startPosition + entriesStart; if (mIn.position() < entriesStartAligned) { long bytesSkipped = mIn.skip(entriesStartAligned - mIn.position()); - LOGGER.fine("Skipping: " + bytesSkipped + " byte(s) to align with ResTable_entry start."); + LOGGER.fine(String.format("Skipping: %d byte(s) to align with ResTable_entry start.", bytesSkipped)); } for (int i : entryOffsetMap.keySet()) { @@ -377,7 +377,7 @@ private EntryData readEntryData() throws IOException, AndrolibException { byte type = (byte) ((flags >> 8) & 0xFF); value = readCompactValue(type, specNamesId); - // To keep code below happy - we know if compact that the size has the key index encoded. + // To keep code below happy - we know if compact then the size has the key index encoded. specNamesId = size; } else if (isComplex) { value = readComplexEntry(); @@ -469,7 +469,7 @@ private ResIntBasedValue readCompactValue(byte type, int data) throws AndrolibEx } private ResIntBasedValue readValue() throws IOException, AndrolibException { - int size = mIn.readShort(); + short size = mIn.readShort(); if (size < 8) { return null; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java index a2e01d4057..23891e156d 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java @@ -89,9 +89,9 @@ public int next() throws XmlPullParserException, IOException { try { doNext(); return mEvent; - } catch (IOException e) { + } catch (IOException ex) { close(); - throw e; + throw ex; } } @@ -348,10 +348,10 @@ public String getAttributeName(int index) { String resourceMapValue; String stringBlockValue = mStringBlock.getString(name); - int resourceId = getAttributeNameResource(index); + int attrResId = getAttributeNameResource(index); try { - resourceMapValue = decodeFromResourceId(resourceId); + resourceMapValue = decodeFromResourceId(attrResId); } catch (AndrolibException ignored) { resourceMapValue = null; } @@ -371,7 +371,7 @@ public String getAttributeName(int index) { } // In this case we have a bogus resource. If it was not found in either. - return "APKTOOL_MISSING_" + Integer.toHexString(resourceId); + return "APKTOOL_MISSING_" + Integer.toHexString(attrResId); } @Override @@ -409,8 +409,10 @@ public String getAttributeValue(int index) { // Ensure we only track down obfuscated values for reference/attribute type values. Otherwise, we might // spam lookups against resource table for invalid ids. - if (valueType == TypedValue.TYPE_REFERENCE || valueType == TypedValue.TYPE_DYNAMIC_REFERENCE || - valueType == TypedValue.TYPE_ATTRIBUTE || valueType == TypedValue.TYPE_DYNAMIC_ATTRIBUTE) { + if (valueType == TypedValue.TYPE_REFERENCE + || valueType == TypedValue.TYPE_DYNAMIC_REFERENCE + || valueType == TypedValue.TYPE_ATTRIBUTE + || valueType == TypedValue.TYPE_DYNAMIC_ATTRIBUTE) { resourceMapValue = decodeFromResourceId(valueData); } String value = getPreferredString(stringBlockValue, resourceMapValue); diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/XmlPullStreamDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AndroidManifestPullStreamDecoder.java similarity index 82% rename from brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/XmlPullStreamDecoder.java rename to brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AndroidManifestPullStreamDecoder.java index de7aff98da..1e088093ad 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/XmlPullStreamDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AndroidManifestPullStreamDecoder.java @@ -30,8 +30,8 @@ import java.io.*; -public class XmlPullStreamDecoder implements ResStreamDecoder { - public XmlPullStreamDecoder(AXmlResourceParser parser, +public class AndroidManifestPullStreamDecoder implements ResStreamDecoder { + public AndroidManifestPullStreamDecoder(AXmlResourceParser parser, ExtXmlSerializer serializer) { this.mParser = parser; this.mSerial = serializer; @@ -55,11 +55,11 @@ public void event(XmlPullParser pp) int type = pp.getEventType(); if (type == XmlPullParser.START_TAG) { - if ("manifest".equalsIgnoreCase(pp.getName())) { + if ("manifest".equals(pp.getName())) { try { hidePackageInfo = parseManifest(pp); } catch (AndrolibException ignored) {} - } else if ("uses-sdk".equalsIgnoreCase(pp.getName())) { + } else if ("uses-sdk".equals(pp.getName())) { try { hideSdkInfo = parseAttr(pp); if (hideSdkInfo) { @@ -68,10 +68,10 @@ public void event(XmlPullParser pp) } catch (AndrolibException ignored) {} } } else if (hideSdkInfo && type == XmlPullParser.END_TAG - && "uses-sdk".equalsIgnoreCase(pp.getName())) { + && "uses-sdk".equals(pp.getName())) { return; } else if (hidePackageInfo && type == XmlPullParser.END_TAG - && "manifest".equalsIgnoreCase(pp.getName())) { + && "manifest".equals(pp.getName())) { super.event(pp); return; } @@ -86,11 +86,11 @@ private boolean parseManifest(XmlPullParser pp) for (int i = 0; i < pp.getAttributeCount(); i++) { attr_name = pp.getAttributeName(i); - if (attr_name.equalsIgnoreCase(("package"))) { + if (attr_name.equals(("package"))) { resTable.setPackageRenamed(pp.getAttributeValue(i)); - } else if (attr_name.equalsIgnoreCase("versionCode")) { + } else if (attr_name.equals("versionCode")) { resTable.setVersionCode(pp.getAttributeValue(i)); - } else if (attr_name.equalsIgnoreCase("versionName")) { + } else if (attr_name.equals("versionName")) { resTable.setVersionName(pp.getAttributeValue(i)); } } @@ -103,14 +103,14 @@ private boolean parseAttr(XmlPullParser pp) final String a_ns = "http://schemas.android.com/apk/res/android"; String ns = pp.getAttributeNamespace(i); - if (a_ns.equalsIgnoreCase(ns)) { + if (a_ns.equals(ns)) { String name = pp.getAttributeName(i); String value = pp.getAttributeValue(i); if (name != null && value != null) { - if (name.equalsIgnoreCase("minSdkVersion") - || name.equalsIgnoreCase("targetSdkVersion") - || name.equalsIgnoreCase("maxSdkVersion") - || name.equalsIgnoreCase("compileSdkVersion")) { + if (name.equals("minSdkVersion") + || name.equals("targetSdkVersion") + || name.equals("maxSdkVersion") + || name.equals("compileSdkVersion")) { resTable.addSdkInfo(name, value); } else { resTable.clearSdkInfo(); @@ -144,11 +144,6 @@ private boolean parseAttr(XmlPullParser pp) } } - public void decodeManifest(InputStream in, OutputStream out) - throws AndrolibException { - decode(in, out); - } - private final AXmlResourceParser mParser; private final ExtXmlSerializer mSerial; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AndroidManifestResourceParser.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AndroidManifestResourceParser.java index 64562b4c03..8fd38da587 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AndroidManifestResourceParser.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AndroidManifestResourceParser.java @@ -58,9 +58,9 @@ public String getAttributeValue(int index) { } private boolean isNumericStringMetadataAttributeValue(int index, String value) { - return "meta-data".equalsIgnoreCase(super.getName()) - && "value".equalsIgnoreCase(super.getAttributeName(index)) - && super.getAttributeValueType(index) == TypedValue.TYPE_STRING - && PATTERN_NUMERIC_STRING.matcher(value).matches(); + return "meta-data".equals(super.getName()) + && "value".equals(super.getAttributeName(index)) + && super.getAttributeValueType(index) == TypedValue.TYPE_STRING + && PATTERN_NUMERIC_STRING.matcher(value).matches(); } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoder.java index d2f63ae069..d0825d1c9c 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResStreamDecoder.java @@ -20,6 +20,5 @@ import java.io.*; public interface ResStreamDecoder { - void decode(InputStream in, OutputStream out) - throws AndrolibException; + void decode(InputStream in, OutputStream out) throws AndrolibException; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResXmlPullStreamDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResXmlPullStreamDecoder.java new file mode 100644 index 0000000000..1719b20c3d --- /dev/null +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResXmlPullStreamDecoder.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2010 Ryszard Wiśniewski + * Copyright (C) 2010 Connor Tumbleson + * + * 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 + * + * https://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.res.decoder; + +import brut.androlib.exceptions.AndrolibException; +import brut.androlib.exceptions.AXmlDecodingException; +import brut.androlib.exceptions.RawXmlEncounteredException; +import brut.androlib.res.util.ExtXmlSerializer; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.wrapper.XmlPullParserWrapper; +import org.xmlpull.v1.wrapper.XmlPullWrapperFactory; +import org.xmlpull.v1.wrapper.XmlSerializerWrapper; +import org.xmlpull.v1.wrapper.classic.StaticXmlSerializerWrapper; + +import java.io.*; + +public class ResXmlPullStreamDecoder implements ResStreamDecoder { + public ResXmlPullStreamDecoder(AXmlResourceParser parser, + ExtXmlSerializer serializer) { + this.mParser = parser; + this.mSerial = serializer; + } + + @Override + public void decode(InputStream in, OutputStream out) + throws AndrolibException { + try { + XmlPullWrapperFactory factory = XmlPullWrapperFactory.newInstance(); + XmlPullParserWrapper par = factory.newPullParserWrapper(mParser); + XmlSerializerWrapper ser = new StaticXmlSerializerWrapper(mSerial, factory); + + par.setInput(in, null); + ser.setOutput(out, null); + + while (par.nextToken() != XmlPullParser.END_DOCUMENT) { + ser.event(par); + } + ser.flush(); + } catch (XmlPullParserException ex) { + throw new AXmlDecodingException("Could not decode XML", ex); + } catch (IOException ex) { + throw new RawXmlEncounteredException("Could not decode XML", ex); + } + } + + private final AXmlResourceParser mParser; + private final ExtXmlSerializer mSerial; +} diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/StringBlock.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/StringBlock.java index 34a95598d4..91237325b2 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/StringBlock.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/StringBlock.java @@ -25,7 +25,6 @@ import java.nio.ByteBuffer; import java.nio.charset.*; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.logging.Logger; @@ -139,7 +138,7 @@ public String getHTML(int index) { for (int i = 0; i < style.length; i += 3) { spans.add(new StyledString.Span(getString(style[i]), style[i + 1], style[i + 2])); } - Collections.sort(spans); + spans.sort(null); StyledString styledString = new StyledString(text, spans); return styledString.toString(); @@ -240,7 +239,7 @@ String decodeString(int offset, int length) { // in some places, Android uses 3-byte UTF-8 sequences instead of 4-bytes. // If decoding failed, we try to use CESU-8 decoder, which is closer to what Android actually uses. return CESU8_DECODER.decode(wrappedBufferRetry).toString(); - } catch (CharacterCodingException e) { + } catch (CharacterCodingException ex) { LOGGER.warning("Failed to decode a string with CESU-8 decoder."); return null; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlPatcher.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlPatcher.java index 0acd9328f4..80a049aeb1 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlPatcher.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlPatcher.java @@ -41,24 +41,25 @@ public final class ResXmlPatcher { * @throws AndrolibException Error reading Manifest file */ public static void removeApplicationDebugTag(File file) throws AndrolibException { - if (file.exists()) { - try { - Document doc = loadDocument(file); - Node application = doc.getElementsByTagName("application").item(0); - - // load attr - NamedNodeMap attr = application.getAttributes(); - Node debugAttr = attr.getNamedItem("android:debuggable"); - - // remove application:debuggable - if (debugAttr != null) { - attr.removeNamedItem("android:debuggable"); - } + if (!file.exists()) { + return; + } + try { + Document doc = loadDocument(file); + Node application = doc.getElementsByTagName("application").item(0); - saveDocument(file, doc); + // load attr + NamedNodeMap attr = application.getAttributes(); + Node debugAttr = attr.getNamedItem("android:debuggable"); - } catch (SAXException | ParserConfigurationException | IOException | TransformerException ignored) { + // remove application:debuggable + if (debugAttr != null) { + attr.removeNamedItem("android:debuggable"); } + + saveDocument(file, doc); + + } catch (SAXException | ParserConfigurationException | IOException | TransformerException ignored) { } } @@ -68,27 +69,28 @@ public static void removeApplicationDebugTag(File file) throws AndrolibException * @param file AndroidManifest file */ public static void setApplicationDebugTagTrue(File file) { - if (file.exists()) { - try { - Document doc = loadDocument(file); - Node application = doc.getElementsByTagName("application").item(0); - - // load attr - NamedNodeMap attr = application.getAttributes(); - Node debugAttr = attr.getNamedItem("android:debuggable"); - - if (debugAttr == null) { - debugAttr = doc.createAttribute("android:debuggable"); - attr.setNamedItem(debugAttr); - } + if (!file.exists()) { + return; + } + try { + Document doc = loadDocument(file); + Node application = doc.getElementsByTagName("application").item(0); - // set application:debuggable to 'true - debugAttr.setNodeValue("true"); + // load attr + NamedNodeMap attr = application.getAttributes(); + Node debugAttr = attr.getNamedItem("android:debuggable"); - saveDocument(file, doc); - - } catch (SAXException | ParserConfigurationException | IOException | TransformerException ignored) { + if (debugAttr == null) { + debugAttr = doc.createAttribute("android:debuggable"); + attr.setNamedItem(debugAttr); } + + // set application:debuggable to 'true + debugAttr.setNodeValue("true"); + + saveDocument(file, doc); + + } catch (SAXException | ParserConfigurationException | IOException | TransformerException ignored) { } } @@ -98,28 +100,29 @@ public static void setApplicationDebugTagTrue(File file) { * @param file AndroidManifest file */ public static void setNetworkSecurityConfig(File file) { - if (file.exists()) { - try { - Document doc = loadDocument(file); - Node application = doc.getElementsByTagName("application").item(0); - - // load attr - NamedNodeMap attr = application.getAttributes(); - Node netSecConfAttr = attr.getNamedItem("android:networkSecurityConfig"); - - if (netSecConfAttr == null) { - // there is not an already existing network security configuration - netSecConfAttr = doc.createAttribute("android:networkSecurityConfig"); - attr.setNamedItem(netSecConfAttr); - } - - // whether it already existed or it was created now set it to the proper value - netSecConfAttr.setNodeValue("@xml/network_security_config"); + if (!file.exists()) { + return; + } + try { + Document doc = loadDocument(file); + Node application = doc.getElementsByTagName("application").item(0); - saveDocument(file, doc); + // load attr + NamedNodeMap attr = application.getAttributes(); + Node netSecConfAttr = attr.getNamedItem("android:networkSecurityConfig"); - } catch (SAXException | ParserConfigurationException | IOException | TransformerException ignored) { + if (netSecConfAttr == null) { + // there is not an already existing network security configuration + netSecConfAttr = doc.createAttribute("android:networkSecurityConfig"); + attr.setNamedItem(netSecConfAttr); } + + // whether it already existed or it was created now set it to the proper value + netSecConfAttr.setNodeValue("@xml/network_security_config"); + + saveDocument(file, doc); + + } catch (SAXException | ParserConfigurationException | IOException | TransformerException ignored) { } } @@ -173,50 +176,52 @@ public static void modNetworkSecurityConfig(File file) * @param file File for AndroidManifest.xml */ public static void fixingPublicAttrsInProviderAttributes(File file) { - boolean saved = false; - if (file.exists()) { - try { - Document doc = loadDocument(file); - XPath xPath = XPathFactory.newInstance().newXPath(); - XPathExpression expression = xPath.compile("/manifest/application/provider"); - - Object result = expression.evaluate(doc, XPathConstants.NODESET); - NodeList nodes = (NodeList) result; - - for (int i = 0; i < nodes.getLength(); i++) { - Node node = nodes.item(i); - NamedNodeMap attrs = node.getAttributes(); - Node provider = attrs.getNamedItem("android:authorities"); - - if (provider != null) { - saved = isSaved(file, saved, provider); - } - } + if (!file.exists()) { + return; + } + try { + Document doc = loadDocument(file); + XPath xPath = XPathFactory.newInstance().newXPath(); + XPathExpression expression = xPath.compile("/manifest/application/provider"); - // android:scheme - xPath = XPathFactory.newInstance().newXPath(); - expression = xPath.compile("/manifest/application/activity/intent-filter/data"); + Object result = expression.evaluate(doc, XPathConstants.NODESET); + NodeList nodes = (NodeList) result; - result = expression.evaluate(doc, XPathConstants.NODESET); - nodes = (NodeList) result; + boolean saved = false; - for (int i = 0; i < nodes.getLength(); i++) { - Node node = nodes.item(i); - NamedNodeMap attrs = node.getAttributes(); - Node provider = attrs.getNamedItem("android:scheme"); + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + NamedNodeMap attrs = node.getAttributes(); + Node provider = attrs.getNamedItem("android:authorities"); - if (provider != null) { - saved = isSaved(file, saved, provider); - } + if (provider != null) { + saved = isSaved(file, saved, provider); } + } - if (saved) { - saveDocument(file, doc); + // android:scheme + xPath = XPathFactory.newInstance().newXPath(); + expression = xPath.compile("/manifest/application/activity/intent-filter/data"); + + result = expression.evaluate(doc, XPathConstants.NODESET); + nodes = (NodeList) result; + + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + NamedNodeMap attrs = node.getAttributes(); + Node provider = attrs.getNamedItem("android:scheme"); + + if (provider != null) { + saved = isSaved(file, saved, provider); } + } - } catch (SAXException | ParserConfigurationException | IOException | - XPathExpressionException | TransformerException ignored) { + if (saved) { + saveDocument(file, doc); } + + } catch (SAXException | ParserConfigurationException | IOException + | XPathExpressionException | TransformerException ignored) { } } @@ -254,23 +259,20 @@ public static String pullValueFromStrings(File directory, String key) { File file = new File(directory, "/res/values/strings.xml"); key = key.replace("@string/", ""); - if (file.exists()) { - try { - Document doc = loadDocument(file); - XPath xPath = XPathFactory.newInstance().newXPath(); - XPathExpression expression = xPath.compile("/resources/string[@name=" + '"' + key + "\"]/text()"); - - Object result = expression.evaluate(doc, XPathConstants.STRING); + if (!file.exists()) { + return null; + } + try { + Document doc = loadDocument(file); + XPath xPath = XPathFactory.newInstance().newXPath(); + XPathExpression expression = xPath.compile("/resources/string[@name=\"" + key + "\"]/text()"); - if (result != null) { - return (String) result; - } + Object result = expression.evaluate(doc, XPathConstants.STRING); + return result != null ? (String) result : null; - } catch (SAXException | ParserConfigurationException | IOException | XPathExpressionException ignored) { - } + } catch (SAXException | ParserConfigurationException | IOException | XPathExpressionException ignored) { + return null; } - - return null; } /** @@ -288,23 +290,20 @@ public static String pullValueFromIntegers(File directory, String key) { File file = new File(directory, "/res/values/integers.xml"); key = key.replace("@integer/", ""); - if (file.exists()) { - try { - Document doc = loadDocument(file); - XPath xPath = XPathFactory.newInstance().newXPath(); - XPathExpression expression = xPath.compile("/resources/integer[@name=" + '"' + key + "\"]/text()"); - - Object result = expression.evaluate(doc, XPathConstants.STRING); + if (!file.exists()) { + return null; + } + try { + Document doc = loadDocument(file); + XPath xPath = XPathFactory.newInstance().newXPath(); + XPathExpression expression = xPath.compile("/resources/integer[@name=\"" + key + "\"]/text()"); - if (result != null) { - return (String) result; - } + Object result = expression.evaluate(doc, XPathConstants.STRING); + return result != null ? (String) result : null; - } catch (SAXException | ParserConfigurationException | IOException | XPathExpressionException ignored) { - } + } catch (SAXException | ParserConfigurationException | IOException | XPathExpressionException ignored) { + return null; } - - return null; } /** @@ -313,24 +312,25 @@ public static String pullValueFromIntegers(File directory, String key) { * @param file File representing AndroidManifest.xml */ public static void removeManifestVersions(File file) { - if (file.exists()) { - try { - Document doc = loadDocument(file); - Node manifest = doc.getFirstChild(); - NamedNodeMap attr = manifest.getAttributes(); - Node vCode = attr.getNamedItem("android:versionCode"); - Node vName = attr.getNamedItem("android:versionName"); - - if (vCode != null) { - attr.removeNamedItem("android:versionCode"); - } - if (vName != null) { - attr.removeNamedItem("android:versionName"); - } - saveDocument(file, doc); + if (!file.exists()) { + return; + } + try { + Document doc = loadDocument(file); + Node manifest = doc.getFirstChild(); + NamedNodeMap attr = manifest.getAttributes(); + Node vCode = attr.getNamedItem("android:versionCode"); + Node vName = attr.getNamedItem("android:versionName"); - } catch (SAXException | ParserConfigurationException | IOException | TransformerException ignored) { + if (vCode != null) { + attr.removeNamedItem("android:versionCode"); } + if (vName != null) { + attr.removeNamedItem("android:versionName"); + } + saveDocument(file, doc); + + } catch (SAXException | ParserConfigurationException | IOException | TransformerException ignored) { } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliBuilder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliBuilder.java index 6880c1557a..d7d7bb83ff 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliBuilder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliBuilder.java @@ -20,7 +20,6 @@ import brut.androlib.mod.SmaliMod; import brut.directory.DirectoryException; import brut.directory.ExtFile; -import org.antlr.runtime.RecognitionException; import com.android.tools.smali.dexlib2.Opcodes; import com.android.tools.smali.dexlib2.writer.builder.DexBuilder; import com.android.tools.smali.dexlib2.writer.io.FileDataStore; @@ -31,12 +30,12 @@ public class SmaliBuilder { - public static void build(ExtFile smaliDir, File dexFile, int apiLevel) throws AndrolibException { + public static void build(File smaliDir, File dexFile, int apiLevel) throws AndrolibException { new SmaliBuilder(smaliDir, dexFile, apiLevel).build(); } - private SmaliBuilder(ExtFile smaliDir, File dexFile, int apiLevel) { - mSmaliDir = smaliDir; + private SmaliBuilder(File smaliDir, File dexFile, int apiLevel) { + mSmaliDir = new ExtFile(smaliDir); mDexFile = dexFile; mApiLevel = apiLevel; } @@ -53,29 +52,35 @@ private void build() throws AndrolibException { for (String fileName : mSmaliDir.getDirectory().getFiles(true)) { buildFile(fileName, dexBuilder); } - dexBuilder.writeTo(new FileDataStore( new File(mDexFile.getAbsolutePath()))); - } catch (IOException | DirectoryException ex) { - throw new AndrolibException(ex); + dexBuilder.writeTo(new FileDataStore(new File(mDexFile.getAbsolutePath()))); + } catch (DirectoryException | IOException | RuntimeException ex) { + throw new AndrolibException("Could not smali folder: " + mSmaliDir.getName(), ex); } } - private void buildFile(String fileName, DexBuilder dexBuilder) - throws AndrolibException, IOException { - File inFile = new File(mSmaliDir, fileName); - InputStream inStream = Files.newInputStream(inFile.toPath()); + private void buildFile(String fileName, DexBuilder dexBuilder) throws AndrolibException { + if (!fileName.endsWith(".smali")) { + LOGGER.warning("Unknown file type, ignoring: " + fileName); + return; + } - if (fileName.endsWith(".smali")) { - try { - if (!SmaliMod.assembleSmaliFile(inFile, dexBuilder, mApiLevel, false, false)) { - throw new AndrolibException("Could not smali file: " + fileName); - } - } catch (IOException | RecognitionException ex) { - throw new AndrolibException(ex); + boolean success; + Exception cause; + try { + File inFile = new File(mSmaliDir, fileName); + success = SmaliMod.assembleSmaliFile(inFile, dexBuilder, mApiLevel, false, false); + cause = null; + } catch (Exception ex) { + success = false; + cause = ex; + } + if (!success) { + AndrolibException ex = new AndrolibException("Could not smali file: " + fileName); + if (cause != null) { + ex.initCause(cause); } - } else { - LOGGER.warning("Unknown file type, ignoring: " + inFile); + throw ex; } - inStream.close(); } private final ExtFile mSmaliDir; diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java index 688025b693..b3a52a5d43 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/src/SmaliDecoder.java @@ -39,7 +39,7 @@ public static DexFile decode(File apkFile, File outDir, String dexName, boolean private SmaliDecoder(File apkFile, File outDir, String dexName, boolean bakDeb, int apiLevel) { mApkFile = apkFile; mOutDir = outDir; - mDexFile = dexName; + mDexName = dexName; mBakDeb = bakDeb; mApiLevel = apiLevel; } @@ -76,7 +76,7 @@ private DexFile decode() throws AndrolibException { if (container.getDexEntryNames().size() == 1) { dexEntry = container.getEntry(container.getDexEntryNames().get(0)); } else { - dexEntry = container.getEntry(mDexFile); + dexEntry = container.getEntry(mDexName); } // Double-check the passed param exists @@ -100,13 +100,13 @@ private DexFile decode() throws AndrolibException { return dexFile; } catch (IOException ex) { - throw new AndrolibException(ex); + throw new AndrolibException("Could not baksmali file: " + mDexName, ex); } } private final File mApkFile; private final File mOutDir; - private final String mDexFile; + private final String mDexName; private final boolean mBakDeb; private final int mApiLevel; } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/BaseTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/BaseTest.java index 12d3a351f9..555e0c7c99 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/BaseTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/BaseTest.java @@ -38,22 +38,6 @@ public class BaseTest { - protected void compareUnknownFiles() throws BrutException { - ApkInfo control = ApkInfo.load(sTestOrigDir); - ApkInfo test = ApkInfo.load(sTestNewDir); - assertNotNull(control.unknownFiles); - assertNotNull(test.unknownFiles); - - Map controlFiles = control.unknownFiles; - Map testFiles = test.unknownFiles; - assertEquals(controlFiles.size(), testFiles.size()); - - // Make sure that the compression methods are still the same - for (Map.Entry controlEntry : controlFiles.entrySet()) { - assertEquals(controlEntry.getValue(), testFiles.get(controlEntry.getKey())); - } - } - protected void compareBinaryFolder(String path, boolean res) throws BrutException, IOException { boolean exists = true; @@ -67,10 +51,10 @@ protected void compareBinaryFolder(String path, boolean res) throws BrutExceptio FileDirectory fileDirectory = new FileDirectory(sTestOrigDir, location); Set files = fileDirectory.getFiles(true); - for (String filename : files) { + for (String fileName : files) { - File control = new File((sTestOrigDir + location), filename); - File test = new File((sTestNewDir + location), filename); + File control = new File((sTestOrigDir + location), fileName); + File test = new File((sTestNewDir + location), fileName); if (! test.isFile() || ! control.isFile()) { exists = false; @@ -92,6 +76,10 @@ protected void compareAssetsFolder(String path) throws BrutException, IOExceptio compareBinaryFolder(File.separatorChar + "assets" + File.separatorChar + path, false); } + protected void compareUnknownFiles() throws BrutException, IOException { + compareBinaryFolder(File.separatorChar + "unknown", false); + } + protected void compareValuesFiles(String path) throws BrutException { compareXmlFiles("res/" + path, new ElementNameAndAttributeQualifier("name")); } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/AndroidOreoNotSparseTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/AndroidOreoNotSparseTest.java index e13deccdd4..275d338ed2 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/AndroidOreoNotSparseTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/AndroidOreoNotSparseTest.java @@ -39,7 +39,7 @@ public static void beforeClass() throws Exception { LOGGER.info("Unpacking not_sparse.apk..."); TestUtils.copyResourceDir(AndroidOreoNotSparseTest.class, "aapt1/issue1594", sTestOrigDir); - File testApk = new File(sTestOrigDir, "not_sparse.apk"); + ExtFile testApk = new ExtFile(sTestOrigDir, "not_sparse.apk"); LOGGER.info("Decoding not_sparse.apk..."); ApkDecoder apkDecoder = new ApkDecoder(testApk); @@ -47,8 +47,8 @@ public static void beforeClass() throws Exception { LOGGER.info("Building not_sparse.apk..."); Config config = Config.getDefaultConfig(); - config.useAapt2 = false; - new ApkBuilder(config, sTestNewDir).build(testApk); + config.aaptVersion = 1; + new ApkBuilder(sTestNewDir, config).build(testApk); } @AfterClass diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/AndroidOreoSparseTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/AndroidOreoSparseTest.java index 9461916780..a8d4cad53a 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/AndroidOreoSparseTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/AndroidOreoSparseTest.java @@ -39,7 +39,7 @@ public static void beforeClass() throws Exception { LOGGER.info("Unpacking sparse.apk..."); TestUtils.copyResourceDir(AndroidOreoSparseTest.class, "aapt1/issue1594", sTestOrigDir); - File testApk = new File(sTestOrigDir, "sparse.apk"); + ExtFile testApk = new ExtFile(sTestOrigDir, "sparse.apk"); LOGGER.info("Decoding sparse.apk..."); ApkDecoder apkDecoder = new ApkDecoder(testApk); @@ -47,8 +47,8 @@ public static void beforeClass() throws Exception { LOGGER.info("Building sparse.apk..."); Config config = Config.getDefaultConfig(); - config.useAapt2 = false; - new ApkBuilder(config, sTestNewDir).build(testApk); + config.aaptVersion = 1; + new ApkBuilder(sTestNewDir, config).build(testApk); } @AfterClass diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/BuildAndDecodeJarTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/BuildAndDecodeJarTest.java index 2de35510e6..812e74d846 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/BuildAndDecodeJarTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/BuildAndDecodeJarTest.java @@ -40,10 +40,10 @@ public static void beforeClass() throws Exception { TestUtils.copyResourceDir(BuildAndDecodeJarTest.class, "aapt1/testjar/", sTestOrigDir); LOGGER.info("Building testjar.jar..."); - File testJar = new File(sTmpDir, "testjar.jar"); + ExtFile testJar = new ExtFile(sTmpDir, "testjar.jar"); Config config = Config.getDefaultConfig(); - config.useAapt2 = false; - new ApkBuilder(config, sTestOrigDir).build(testJar); + config.aaptVersion = 1; + new ApkBuilder(sTestOrigDir, config).build(testJar); LOGGER.info("Decoding testjar.jar..."); ApkDecoder apkDecoder = new ApkDecoder(testJar); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/BuildAndDecodeTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/BuildAndDecodeTest.java index f5385d651e..49ab1b9616 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/BuildAndDecodeTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/BuildAndDecodeTest.java @@ -48,10 +48,10 @@ public static void beforeClass() throws Exception { TestUtils.copyResourceDir(BuildAndDecodeTest.class, "aapt1/testapp/", sTestOrigDir); LOGGER.info("Building testapp.apk..."); - File testApk = new File(sTmpDir, "testapp.apk"); + ExtFile testApk = new ExtFile(sTmpDir, "testapp.apk"); Config config = Config.getDefaultConfig(); - config.useAapt2 = false; - new ApkBuilder(config, sTestOrigDir).build(testApk); + config.aaptVersion = 1; + new ApkBuilder(sTestOrigDir, config).build(testApk); LOGGER.info("Decoding testapp.apk..."); ApkDecoder apkDecoder = new ApkDecoder(testApk); @@ -540,7 +540,7 @@ public void libsTest() throws BrutException, IOException { } @Test - public void unknownFolderTest() throws BrutException { + public void unknownFolderTest() throws BrutException, IOException { compareUnknownFiles(); } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/DebugTagRetainedTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/DebugTagRetainedTest.java index 9c63aa2904..2aafb45e51 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/DebugTagRetainedTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/DebugTagRetainedTest.java @@ -48,11 +48,11 @@ public static void beforeClass() throws Exception { LOGGER.info("Building issue1235.apk..."); Config config = Config.getDefaultConfig(); - config.useAapt2 = false; + config.aaptVersion = 1; config.debugMode = true; - File testApk = new File(sTmpDir, "issue1235.apk"); - new ApkBuilder(config, sTestOrigDir).build(testApk); + ExtFile testApk = new ExtFile(sTmpDir, "issue1235.apk"); + new ApkBuilder(sTestOrigDir, config).build(testApk); LOGGER.info("Decoding issue1235.apk..."); ApkDecoder apkDecoder = new ApkDecoder(testApk); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/DefaultBaksmaliVariableTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/DefaultBaksmaliVariableTest.java index ccdd9b1c32..c3a63ed6d7 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/DefaultBaksmaliVariableTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/DefaultBaksmaliVariableTest.java @@ -42,10 +42,10 @@ public static void beforeClass() throws Exception { TestUtils.copyResourceDir(DefaultBaksmaliVariableTest.class, "aapt1/issue1481/", sTestOrigDir); LOGGER.info("Building issue1481.jar..."); - File testJar = new File(sTmpDir, "issue1481.jar"); + ExtFile testJar = new ExtFile(sTmpDir, "issue1481.jar"); Config config = Config.getDefaultConfig(); - config.useAapt2 = false; - new ApkBuilder(config, sTestOrigDir).build(testJar); + config.aaptVersion = 1; + new ApkBuilder(sTestOrigDir, config).build(testJar); LOGGER.info("Decoding issue1481.jar..."); ApkDecoder apkDecoder = new ApkDecoder(testJar); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/EmptyResourcesArscTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/EmptyResourcesArscTest.java index 240d304076..4a46b79f28 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/EmptyResourcesArscTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/EmptyResourcesArscTest.java @@ -42,7 +42,7 @@ public static void beforeClass() throws Exception { LOGGER.info("Unpacking issue1730.apk..."); TestUtils.copyResourceDir(EmptyResourcesArscTest.class, "aapt1/issue1730", sTestOrigDir); - File testApk = new File(sTestOrigDir, "issue1730.apk"); + ExtFile testApk = new ExtFile(sTestOrigDir, "issue1730.apk"); LOGGER.info("Decoding issue1730.apk..."); ApkDecoder apkDecoder = new ApkDecoder(testApk); @@ -50,8 +50,8 @@ public static void beforeClass() throws Exception { LOGGER.info("Building issue1730.apk..."); Config config = Config.getDefaultConfig(); - config.useAapt2 = false; - new ApkBuilder(config, sTestNewDir).build(testApk); + config.aaptVersion = 1; + new ApkBuilder(sTestNewDir, config).build(testApk); } @AfterClass diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/ExternalEntityTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/ExternalEntityTest.java index 4c673958f8..f24400971d 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/ExternalEntityTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/ExternalEntityTest.java @@ -39,10 +39,10 @@ public static void beforeClass() throws Exception { TestUtils.copyResourceDir(ExternalEntityTest.class, "decode/doctype/", sTestOrigDir); LOGGER.info("Building doctype.apk..."); - File testApk = new File(sTestOrigDir, "doctype.apk"); + ExtFile testApk = new ExtFile(sTestOrigDir, "doctype.apk"); Config config = Config.getDefaultConfig(); - config.useAapt2 = false; - new ApkBuilder(config, sTestOrigDir).build(testApk); + config.aaptVersion = 1; + new ApkBuilder(sTestOrigDir, config).build(testApk); LOGGER.info("Decoding doctype.apk..."); ApkDecoder apkDecoder = new ApkDecoder(testApk); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/LargeIntsInManifestTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/LargeIntsInManifestTest.java index c8e4574c53..e670b795f5 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/LargeIntsInManifestTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/LargeIntsInManifestTest.java @@ -44,7 +44,7 @@ public void checkIfLargeIntsAreHandledTest() throws BrutException, IOException { String apk = "issue767.apk"; // decode issue767.apk - ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + apk)); sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + ".out"); File outDir = new File(sTmpDir + File.separator + apk + ".out"); @@ -52,13 +52,13 @@ public void checkIfLargeIntsAreHandledTest() throws BrutException, IOException { // build issue767 Config config = Config.getDefaultConfig(); - config.useAapt2 = false; + config.aaptVersion = 1; ExtFile testApk = new ExtFile(sTmpDir, apk + ".out"); - new ApkBuilder(config, testApk).build(null); + new ApkBuilder(testApk, config).build(null); String newApk = apk + ".out" + File.separator + "dist" + File.separator + apk; // decode issue767 again - apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + newApk)); + apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + newApk)); sTestNewDir = new ExtFile(sTmpDir + File.separator + apk + ".out.two"); outDir = new File(sTmpDir + File.separator + apk + ".out.two"); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/ProviderAttributeTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/ProviderAttributeTest.java index 35a9ba9a6d..f618cdd206 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/ProviderAttributeTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/ProviderAttributeTest.java @@ -53,20 +53,20 @@ public void isProviderStringReplacementWorking() throws BrutException, IOExcepti String apk = "issue636.apk"; // decode issue636.apk - ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + apk)); File outDir = new File(sTmpDir + File.separator + apk + ".out"); apkDecoder.decode(outDir); // build issue636 ExtFile testApk = new ExtFile(sTmpDir, apk + ".out"); Config config = Config.getDefaultConfig(); - config.useAapt2 = false; - new ApkBuilder(config, testApk).build(null); + config.aaptVersion = 1; + new ApkBuilder(testApk, config).build(null); String newApk = apk + ".out" + File.separator + "dist" + File.separator + apk; assertTrue(fileExists(newApk)); // decode issues636 again - apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + newApk)); + apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + newApk)); outDir = new File(sTmpDir + File.separator + apk + ".out.two"); apkDecoder.decode(outDir); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/SharedLibraryTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/SharedLibraryTest.java index bc7b44f231..fa5f08ab5f 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/SharedLibraryTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/SharedLibraryTest.java @@ -81,30 +81,30 @@ public void isSharedResourceDecodingAndRebuildingWorking() throws IOException, B Config config = Config.getDefaultConfig(); config.frameworkDirectory = sTmpDir.getAbsolutePath(); config.frameworkTag = "shared"; - config.useAapt2 = false; + config.aaptVersion = 1; // install library/framework new Framework(config).installFramework(new File(sTmpDir + File.separator + library)); assertTrue(fileExists("2-shared.apk")); // decode client.apk - ApkDecoder apkDecoder = new ApkDecoder(config, new ExtFile(sTmpDir + File.separator + client)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + client), config); File outDir = new File(sTmpDir + File.separator + client + ".out"); apkDecoder.decode(outDir); // decode library.apk - ApkDecoder libraryDecoder = new ApkDecoder(config, new ExtFile(sTmpDir + File.separator + library)); + ApkDecoder libraryDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + library), config); outDir = new File(sTmpDir + File.separator + library + ".out"); libraryDecoder.decode(outDir); // build client.apk ExtFile clientApk = new ExtFile(sTmpDir, client + ".out"); - new ApkBuilder(config, clientApk).build(null); + new ApkBuilder(clientApk, config).build(null); assertTrue(fileExists(client + ".out" + File.separator + "dist" + File.separator + client)); // build library.apk (shared library) ExtFile libraryApk = new ExtFile(sTmpDir, library + ".out"); - new ApkBuilder(config, libraryApk).build(null); + new ApkBuilder(libraryApk, config).build(null); assertTrue(fileExists(library + ".out" + File.separator + "dist" + File.separator + library)); } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/SkipAssetTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/SkipAssetTest.java index f6d02f3b41..1e375304b5 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/SkipAssetTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/SkipAssetTest.java @@ -52,7 +52,7 @@ public void checkIfEnablingSkipAssetWorks() throws BrutException, IOException { config.forceDelete = true; // decode issue1605.apk - ApkDecoder apkDecoder = new ApkDecoder(config, new ExtFile(sTmpDir + File.separator + apk)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + apk), config); sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + ".out"); apkDecoder.decode(sTestOrigDir); @@ -69,7 +69,7 @@ public void checkControl() throws BrutException, IOException { config.forceDelete = true; // decode issue1605.apk - ApkDecoder apkDecoder = new ApkDecoder(config, new ExtFile(sTmpDir + File.separator + apk)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + apk), config); sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + ".out"); apkDecoder.decode(sTestOrigDir); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/UnknownCompressionTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/UnknownCompressionTest.java index 5e087db2fa..930b41c438 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/UnknownCompressionTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt1/UnknownCompressionTest.java @@ -42,7 +42,7 @@ public static void beforeClass() throws Exception { String apk = "deflated_unknowns.apk"; Config config = Config.getDefaultConfig(); config.frameworkDirectory = sTmpDir.getAbsolutePath(); - config.useAapt2 = false; + config.aaptVersion = 1; sTestOrigDir = new ExtFile(sTmpDir, apk); @@ -54,7 +54,7 @@ public static void beforeClass() throws Exception { // build deflated_unknowns ExtFile clientApkFolder = new ExtFile(sTestOrigDir.getAbsolutePath() + ".out"); - new ApkBuilder(config, clientApkFolder).build(null); + new ApkBuilder(clientApkFolder, config).build(null); sTestNewDir = new ExtFile(clientApkFolder, "dist" + File.separator + apk); } @@ -71,7 +71,7 @@ public void pkmExtensionDeflatedTest() throws BrutException, IOException { // Check that control = rebuilt (both deflated) // Add extra check for checking not equal to 0, just in case control gets broken assertEquals(control, rebuilt); - assertNotSame(0, rebuilt); + assertNotSame(Integer.valueOf(0), rebuilt); } @Test @@ -95,11 +95,11 @@ public void confirmJsonFileIsDeflatedTest() throws BrutException, IOException { } @Test - public void confirmPngFileIsCorrectlyDeflatedTest() throws BrutException, IOException { + public void confirmPngFileIsStoredTest() throws BrutException, IOException { Integer control = sTestOrigDir.getDirectory().getCompressionLevel("950x150.png"); Integer rebuilt = sTestNewDir.getDirectory().getCompressionLevel("950x150.png"); - assertEquals(control, rebuilt); - assertEquals(Integer.valueOf(8), rebuilt); + assertNotSame(control, rebuilt); + assertEquals(Integer.valueOf(0), rebuilt); } } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/BuildAndDecodeTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/BuildAndDecodeTest.java index 14014adcf6..ab008d4238 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/BuildAndDecodeTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/BuildAndDecodeTest.java @@ -49,12 +49,11 @@ public static void beforeClass() throws Exception { TestUtils.copyResourceDir(BuildAndDecodeTest.class, "aapt2/testapp/", sTestOrigDir); Config config = Config.getDefaultConfig(); - config.useAapt2 = true; config.verbose = true; LOGGER.info("Building testapp.apk..."); - File testApk = new File(sTmpDir, "testapp.apk"); - new ApkBuilder(config, sTestOrigDir).build(testApk); + ExtFile testApk = new ExtFile(sTmpDir, "testapp.apk"); + new ApkBuilder(sTestOrigDir, config).build(testApk); LOGGER.info("Decoding testapp.apk..."); ApkDecoder apkDecoder = new ApkDecoder(testApk); @@ -135,7 +134,7 @@ public void leadingDollarSignResourceNameTest() throws BrutException { } @Test - public void samsungQmgFilesHandledTest() throws IOException, BrutException { + public void samsungQmgFilesHandledTest() throws BrutException, IOException { compareBinaryFolder("drawable-xhdpi", true); } @@ -175,7 +174,7 @@ public void singleDexTest() throws BrutException, IOException { } @Test - public void unknownFolderTest() throws BrutException { + public void unknownFolderTest() throws BrutException, IOException { compareUnknownFiles(); } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/DebuggableFalseChangeToTrueTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/DebuggableFalseChangeToTrueTest.java index 8ca7f8d6f3..4c30f86431 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/DebuggableFalseChangeToTrueTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/DebuggableFalseChangeToTrueTest.java @@ -49,11 +49,10 @@ public static void beforeClass() throws Exception { LOGGER.info("Building issue2328-debuggable-flase.apk..."); Config config = Config.getDefaultConfig(); config.debugMode = true; - config.useAapt2 = true; config.verbose = true; - File testApk = new File(sTmpDir, "issue2328-debuggable-flase.apk"); - new ApkBuilder(config, sTestOrigDir).build(testApk); + ExtFile testApk = new ExtFile(sTmpDir, "issue2328-debuggable-flase.apk"); + new ApkBuilder(sTestOrigDir, config).build(testApk); LOGGER.info("Decoding issue2328-debuggable-flase.apk..."); ApkDecoder apkDecoder = new ApkDecoder(testApk); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/DebuggableTrueAddedTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/DebuggableTrueAddedTest.java index 9cab0376ba..8cce9d8239 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/DebuggableTrueAddedTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/DebuggableTrueAddedTest.java @@ -49,11 +49,10 @@ public static void beforeClass() throws Exception { LOGGER.info("Building issue2328-debuggable-missing.apk..."); Config config = Config.getDefaultConfig(); config.debugMode = true; - config.useAapt2 = true; config.verbose = true; - File testApk = new File(sTmpDir, "issue2328-debuggable-missing.apk"); - new ApkBuilder(config, sTestOrigDir).build(testApk); + ExtFile testApk = new ExtFile(sTmpDir, "issue2328-debuggable-missing.apk"); + new ApkBuilder(sTestOrigDir, config).build(testApk); LOGGER.info("Decoding issue2328-debuggable-missing.apk..."); ApkDecoder apkDecoder = new ApkDecoder(testApk); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/DebuggableTrueRetainedTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/DebuggableTrueRetainedTest.java index d85822f483..321129a070 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/DebuggableTrueRetainedTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/DebuggableTrueRetainedTest.java @@ -49,11 +49,10 @@ public static void beforeClass() throws Exception { LOGGER.info("Building issue2328-debuggable-true.apk..."); Config config = Config.getDefaultConfig(); config.debugMode = true; - config.useAapt2 = true; config.verbose = true; - File testApk = new File(sTmpDir, "issue2328-debuggable-true.apk"); - new ApkBuilder(config, sTestOrigDir).build(testApk); + ExtFile testApk = new ExtFile(sTmpDir, "issue2328-debuggable-true.apk"); + new ApkBuilder(sTestOrigDir, config).build(testApk); LOGGER.info("Decoding issue2328-debuggable-true.apk..."); ApkDecoder apkDecoder = new ApkDecoder(testApk); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/NetworkConfigTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/NetworkConfigTest.java index 51be370612..f9789bc1c3 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/NetworkConfigTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/NetworkConfigTest.java @@ -53,9 +53,8 @@ public static void beforeClass() throws Exception { LOGGER.info("Building testapp.apk..."); Config config = Config.getDefaultConfig(); config.netSecConf = true; - config.useAapt2 = true; - File testApk = new File(sTmpDir, "testapp.apk"); - new ApkBuilder(config, sTestOrigDir).build(testApk); + ExtFile testApk = new ExtFile(sTmpDir, "testapp.apk"); + new ApkBuilder(sTestOrigDir, config).build(testApk); LOGGER.info("Decoding testapp.apk..."); ApkDecoder apkDecoder = new ApkDecoder(testApk); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/NoNetworkConfigTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/NoNetworkConfigTest.java index d19d3f1d14..d483daba7b 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/NoNetworkConfigTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/NoNetworkConfigTest.java @@ -55,9 +55,8 @@ public static void beforeClass() throws Exception { LOGGER.info("Building testapp.apk..."); Config config = Config.getDefaultConfig(); config.netSecConf = true; - config.useAapt2 = true; - File testApk = new File(sTmpDir, "testapp.apk"); - new ApkBuilder(config, sTestOrigDir).build(testApk); + ExtFile testApk = new ExtFile(sTmpDir, "testapp.apk"); + new ApkBuilder(sTestOrigDir, config).build(testApk); LOGGER.info("Decoding testapp.apk..."); ApkDecoder apkDecoder = new ApkDecoder(testApk); 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 34ea0c6b2e..4158f51b5f 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 @@ -45,12 +45,11 @@ public static void beforeClass() throws Exception { TestUtils.copyResourceDir(BuildAndDecodeTest.class, "aapt2/pkgid8/", sTestOrigDir); Config config = Config.getDefaultConfig(); - config.useAapt2 = true; config.verbose = true; LOGGER.info("Building pkgid8.apk..."); ExtFile testApk = new ExtFile(sTmpDir, "pkgid8.apk"); - new ApkBuilder(config, sTestOrigDir).build(testApk); + new ApkBuilder(sTestOrigDir, config).build(testApk); LOGGER.info("Decoding pkgid8.apk..."); ApkInfo testInfo = new ApkInfo(testApk); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/ApkInfoReaderTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/ApkInfoReaderTest.java index 4e3b1c2ebd..3dda5b1c30 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/ApkInfoReaderTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/ApkInfoReaderTest.java @@ -25,17 +25,16 @@ public class ApkInfoReaderTest { private void checkStandard(ApkInfo apkInfo) { assertEquals("standard.apk", apkInfo.apkFileName); - assertFalse(apkInfo.resourcesAreCompressed); assertEquals(1, apkInfo.doNotCompress.size()); - assertEquals("resources.arsc", apkInfo.doNotCompress.iterator().next()); + assertEquals("arsc", apkInfo.doNotCompress.iterator().next()); assertFalse(apkInfo.isFrameworkApk); assertNotNull(apkInfo.packageInfo); assertEquals("127", apkInfo.packageInfo.forcedPackageId); assertNull(apkInfo.packageInfo.renameManifestPackage); - assertNotNull(apkInfo.getSdkInfo()); - assertEquals(2, apkInfo.getSdkInfo().size()); - assertEquals("25", apkInfo.getSdkInfo().get("minSdkVersion")); - assertEquals("30", apkInfo.getSdkInfo().get("targetSdkVersion")); + assertNotNull(apkInfo.sdkInfo); + assertEquals(2, apkInfo.sdkInfo.size()); + assertEquals("25", apkInfo.sdkInfo.get("minSdkVersion")); + assertEquals("30", apkInfo.sdkInfo.get("targetSdkVersion")); assertFalse(apkInfo.sharedLibrary); assertFalse(apkInfo.sparseResources); assertNotNull(apkInfo.usesFramework); @@ -95,22 +94,13 @@ public void testUnknownFiles() throws AndrolibException { assertNotNull(apkInfo.versionInfo); assertEquals("1", apkInfo.versionInfo.versionCode); assertEquals("1.0", apkInfo.versionInfo.versionName); - assertFalse(apkInfo.resourcesAreCompressed); assertNotNull(apkInfo.doNotCompress); - assertEquals(4, apkInfo.doNotCompress.size()); + assertEquals(5, apkInfo.doNotCompress.size()); assertEquals("assets/0byte_file.jpg", apkInfo.doNotCompress.get(0)); assertEquals("arsc", apkInfo.doNotCompress.get(1)); assertEquals("png", apkInfo.doNotCompress.get(2)); assertEquals("mp3", apkInfo.doNotCompress.get(3)); - assertNotNull(apkInfo.unknownFiles); - assertEquals(7, apkInfo.unknownFiles.size()); - assertEquals("8", apkInfo.unknownFiles.get("AssetBundle/assets/a.txt")); - assertEquals("8", apkInfo.unknownFiles.get("AssetBundle/b.txt")); - assertEquals("8", apkInfo.unknownFiles.get("hidden.file")); - assertEquals("8", apkInfo.unknownFiles.get("non\u007Fprintable.file")); - assertEquals("0", apkInfo.unknownFiles.get("stored.file")); - assertEquals("8", apkInfo.unknownFiles.get("unk_folder/unknown_file")); - assertEquals("8", apkInfo.unknownFiles.get("lib_bug603/bug603")); + assertEquals("stored.file", apkInfo.doNotCompress.get(4)); } @Test @@ -127,22 +117,19 @@ public void testUlist_with_indent() throws AndrolibException { assertNotNull(apkInfo.packageInfo); assertEquals("127", apkInfo.packageInfo.forcedPackageId); assertEquals("com.test.basic", apkInfo.packageInfo.renameManifestPackage); - assertNotNull(apkInfo.getSdkInfo()); - assertEquals(3, apkInfo.getSdkInfo().size()); - assertEquals("4", apkInfo.getSdkInfo().get("minSdkVersion")); - assertEquals("30", apkInfo.getSdkInfo().get("maxSdkVersion")); - assertEquals("22", apkInfo.getSdkInfo().get("targetSdkVersion")); + assertNotNull(apkInfo.sdkInfo); + assertEquals(3, apkInfo.sdkInfo.size()); + assertEquals("4", apkInfo.sdkInfo.get("minSdkVersion")); + assertEquals("30", apkInfo.sdkInfo.get("maxSdkVersion")); + assertEquals("22", apkInfo.sdkInfo.get("targetSdkVersion")); assertFalse(apkInfo.sharedLibrary); assertTrue(apkInfo.sparseResources); - assertNotNull(apkInfo.unknownFiles); - assertEquals(1, apkInfo.unknownFiles.size()); - assertEquals("1", apkInfo.unknownFiles.get("hidden.file")); assertNotNull(apkInfo.versionInfo); assertEquals("71", apkInfo.versionInfo.versionCode); assertEquals("1.0.70", apkInfo.versionInfo.versionName); assertNotNull(apkInfo.doNotCompress); assertEquals(2, apkInfo.doNotCompress.size()); - assertEquals("resources.arsc", apkInfo.doNotCompress.get(0)); + assertEquals("arsc", apkInfo.doNotCompress.get(0)); assertEquals("png", apkInfo.doNotCompress.get(1)); } } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/ApkInfoSerializationTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/ApkInfoSerializationTest.java index 295493a06a..fc15a9081a 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/ApkInfoSerializationTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/ApkInfoSerializationTest.java @@ -37,7 +37,7 @@ public void checkApkInfoSerialization() throws IOException, AndrolibException { this.getClass().getResourceAsStream("/apk/unknown_files.yml")); check(control); - File savedApkInfo = folder.newFile( "saved.yml" ); + File savedApkInfo = folder.newFile("saved.yml"); control.save(savedApkInfo); try (FileInputStream fis = new FileInputStream(savedApkInfo)) { ApkInfo saved = ApkInfo.load(fis); @@ -57,21 +57,12 @@ private void check(ApkInfo apkInfo) { assertNotNull(apkInfo.versionInfo); assertEquals("1", apkInfo.versionInfo.versionCode); assertEquals("1.0", apkInfo.versionInfo.versionName); - assertFalse(apkInfo.resourcesAreCompressed); assertNotNull(apkInfo.doNotCompress); - assertEquals(4, apkInfo.doNotCompress.size()); + assertEquals(5, apkInfo.doNotCompress.size()); assertEquals("assets/0byte_file.jpg", apkInfo.doNotCompress.get(0)); assertEquals("arsc", apkInfo.doNotCompress.get(1)); assertEquals("png", apkInfo.doNotCompress.get(2)); assertEquals("mp3", apkInfo.doNotCompress.get(3)); - assertNotNull(apkInfo.unknownFiles); - assertEquals(7, apkInfo.unknownFiles.size()); - assertEquals("8", apkInfo.unknownFiles.get("AssetBundle/assets/a.txt")); - assertEquals("8", apkInfo.unknownFiles.get("AssetBundle/b.txt")); - assertEquals("8", apkInfo.unknownFiles.get("hidden.file")); - assertEquals("8", apkInfo.unknownFiles.get("non\u007Fprintable.file")); - assertEquals("0", apkInfo.unknownFiles.get("stored.file")); - assertEquals("8", apkInfo.unknownFiles.get("unk_folder/unknown_file")); - assertEquals("8", apkInfo.unknownFiles.get("lib_bug603/bug603")); + assertEquals("stored.file", apkInfo.doNotCompress.get(4)); } } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/ConsistentPropertyTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/ConsistentPropertyTest.java index bada5313e7..8bd82fab89 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/ConsistentPropertyTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/ConsistentPropertyTest.java @@ -40,11 +40,8 @@ public void testAssertingAllKnownApkInfoProperties() throws AndrolibException { assertEquals("com.test.basic", apkInfo.packageInfo.renameManifestPackage); assertEquals("71", apkInfo.versionInfo.versionCode); assertEquals("1.0.70", apkInfo.versionInfo.versionName); - assertFalse(apkInfo.resourcesAreCompressed); assertFalse(apkInfo.sharedLibrary); assertTrue(apkInfo.sparseResources); - assertEquals(1, apkInfo.unknownFiles.size()); assertEquals(2, apkInfo.doNotCompress.size()); - assertFalse(apkInfo.compressionType); } } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/InvalidSdkBoundingTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/InvalidSdkBoundingTest.java index 9c6800bc7f..7555cb7c0f 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/InvalidSdkBoundingTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/apk/InvalidSdkBoundingTest.java @@ -34,7 +34,7 @@ public void checkIfInvalidValuesPass() { sdkInfo.put("targetSdkVersion", "25"); sdkInfo.put("maxSdkVersion", "19"); - apkInfo.setSdkInfo(sdkInfo); + apkInfo.sdkInfo = sdkInfo; assertEquals("19", apkInfo.checkTargetSdkVersionBounds()); } @@ -46,7 +46,7 @@ public void checkIfMissingMinPasses() { sdkInfo.put("targetSdkVersion", "25"); sdkInfo.put("maxSdkVersion", "19"); - apkInfo.setSdkInfo(sdkInfo); + apkInfo.sdkInfo = sdkInfo; assertEquals("19", apkInfo.checkTargetSdkVersionBounds()); } @@ -58,7 +58,7 @@ public void checkIfMissingMaxPasses() { sdkInfo.put("minSdkVersion", "15"); sdkInfo.put("targetSdkVersion", "25"); - apkInfo.setSdkInfo(sdkInfo); + apkInfo.sdkInfo = sdkInfo; assertEquals("25", apkInfo.checkTargetSdkVersionBounds()); } @@ -69,7 +69,7 @@ public void checkIfMissingBothPasses() { Map sdkInfo = new LinkedHashMap<>(); sdkInfo.put("targetSdkVersion", "25"); - apkInfo.setSdkInfo(sdkInfo); + apkInfo.sdkInfo = sdkInfo; assertEquals("25", apkInfo.checkTargetSdkVersionBounds()); } @@ -80,7 +80,7 @@ public void checkForShortHandSTag() { Map sdkInfo = new LinkedHashMap<>(); sdkInfo.put("targetSdkVersion", "S"); - apkInfo.setSdkInfo(sdkInfo); + apkInfo.sdkInfo = sdkInfo; assertEquals("31", apkInfo.checkTargetSdkVersionBounds()); } @@ -91,7 +91,7 @@ public void checkForShortHandSdkTag() { Map sdkInfo = new LinkedHashMap<>(); sdkInfo.put("targetSdkVersion", "O"); - apkInfo.setSdkInfo(sdkInfo); + apkInfo.sdkInfo = sdkInfo; assertEquals("26", apkInfo.checkTargetSdkVersionBounds()); } @@ -102,7 +102,7 @@ public void checkForSdkDevelopmentInsaneTestValue() { Map sdkInfo = new LinkedHashMap<>(); sdkInfo.put("targetSdkVersion", "SDK_CUR_DEVELOPMENT"); - apkInfo.setSdkInfo(sdkInfo); + apkInfo.sdkInfo = sdkInfo; assertEquals("10000", apkInfo.checkTargetSdkVersionBounds()); } } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/AndResGuardTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/AndResGuardTest.java index fe6d3dc3fd..39518bd4c0 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/AndResGuardTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/AndResGuardTest.java @@ -48,7 +48,7 @@ public void checkifAndResDecodeRemapsRFolder() throws BrutException, IOException String apk = "issue1170.apk"; // decode issue1170.apk - ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + apk)); sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + ".out"); File outDir = new File(sTmpDir + File.separator + apk + ".out"); @@ -65,7 +65,7 @@ public void checkIfAndResDecodeRemapsRFolderInRawMode() throws BrutException, IO config.forceDelete = true; config.decodeResources = Config.DECODE_RESOURCES_NONE; String apk = "issue1170.apk"; - ApkDecoder apkDecoder = new ApkDecoder(config, new File(sTmpDir + File.separator + apk)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + apk), config); sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + ".raw.out"); File outDir = new File(sTmpDir + File.separator + apk + ".raw.out"); apkDecoder.decode(outDir); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/CompactResourceTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/CompactResourceTest.java index 5b4ad679d5..c7f6a5835b 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/CompactResourceTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/CompactResourceTest.java @@ -48,7 +48,7 @@ public static void afterClass() throws BrutException { @Test public void checkIfDecodeSucceeds() throws BrutException, IOException, ParserConfigurationException, SAXException { String apk = "issue3366.apk"; - File testApk = new File(sTmpDir, apk); + ExtFile testApk = new ExtFile(sTmpDir, apk); // decode issue3366.apk ApkDecoder apkDecoder = new ApkDecoder(testApk); @@ -62,6 +62,6 @@ public void checkIfDecodeSucceeds() throws BrutException, IOException, ParserCon Config config = Config.getDefaultConfig(); LOGGER.info("Building duplicatedex.apk..."); - new ApkBuilder(config, sTestOrigDir).build(testApk); + new ApkBuilder(sTestOrigDir, config).build(testApk); } } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/DecodeKotlinCoroutinesTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/DecodeKotlinCoroutinesTest.java index d52b35556f..96561eac2c 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/DecodeKotlinCoroutinesTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/DecodeKotlinCoroutinesTest.java @@ -55,7 +55,7 @@ public void kotlinCoroutinesDecodeTest() throws IOException, AndrolibException, Config config = Config.getDefaultConfig(); config.forceDelete = true; // decode kotlin coroutines - ApkDecoder apkDecoder = new ApkDecoder(config, new File(sTmpDir + File.separator + apk)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + apk), config); File outDir = new File(sTmpDir + File.separator + apk + ".out"); apkDecoder.decode(outDir); File coroutinesExceptionHandler = new File(sTmpDir + File.separator + apk + ".out" + File.separator + "META-INF" + File.separator + "services", "kotlinx.coroutines.CoroutineExceptionHandler"); @@ -66,23 +66,23 @@ public void kotlinCoroutinesDecodeTest() throws IOException, AndrolibException, } @Test - public void kotlinCoroutinesEncodeAfterDecodeTest() throws IOException, BrutException { + public void kotlinCoroutinesEncodeAfterDecodeTest() throws BrutException, IOException { Config config = Config.getDefaultConfig(); config.forceDelete = true; // decode kotlin coroutines - ApkDecoder apkDecoder = new ApkDecoder(config, new File(sTmpDir + File.separator + apk)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + apk), config); File outDir = new File(sTmpDir + File.separator + apk + ".out"); apkDecoder.decode(outDir); // build kotlin coroutines ExtFile testApk = new ExtFile(sTmpDir, apk + ".out"); - new ApkBuilder(config, testApk).build(null); + new ApkBuilder(testApk, config).build(null); String newApk = apk + ".out" + File.separator + "dist" + File.separator + apk; assertTrue(fileExists(newApk)); // decode kotlin coroutines again - apkDecoder = new ApkDecoder(config, new File(sTmpDir + File.separator + newApk)); + apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + newApk), config); outDir = new File(sTmpDir + File.separator + apk + ".out.two"); apkDecoder.decode(outDir); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/DecodeKotlinTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/DecodeKotlinTest.java index 415138e7c9..2dc515ad11 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/DecodeKotlinTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/DecodeKotlinTest.java @@ -43,7 +43,7 @@ public static void beforeClass() throws Exception { String apk = "testkotlin.apk"; // decode testkotlin.apk - ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + apk)); sTestNewDir = new ExtFile(sTmpDir + File.separator + apk + ".out"); File outDir = new File(sTmpDir + File.separator + apk + ".out"); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/DoubleExtensionUnknownFileTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/DoubleExtensionUnknownFileTest.java index 29c68a1b9b..b56dc48a22 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/DoubleExtensionUnknownFileTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/DoubleExtensionUnknownFileTest.java @@ -52,7 +52,7 @@ public void multipleExtensionUnknownFileTest() throws BrutException, IOException String apk = "issue1244.apk"; // decode issue1244.apk - ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + apk)); ExtFile decodedApk = new ExtFile(sTmpDir + File.separator + apk + ".out"); File outDir = new File(sTmpDir + File.separator + apk + ".out"); apkDecoder.decode(outDir); @@ -60,7 +60,7 @@ public void multipleExtensionUnknownFileTest() throws BrutException, IOException ApkInfo apkInfo = ApkInfo.load(decodedApk); for (String string : apkInfo.doNotCompress) { if (StringUtils.countMatches(string, ".") > 1) { - assertTrue(string.equalsIgnoreCase("assets/bin/Data/sharedassets1.assets.split0")); + assertTrue(string.equals("assets/bin/Data/sharedassets1.assets.split0")); } } } 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 index 9af4216f54..a4ab7491e8 100644 --- 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 @@ -45,7 +45,7 @@ public void afterClass() throws BrutException { @Test(expected = AndrolibException.class) public void decodeAllSourcesShouldThrowException() throws BrutException, IOException { - File testApk = new File(sTestOrigDir, "duplicatedex.apk"); + ExtFile testApk = new ExtFile(sTestOrigDir, "duplicatedex.apk"); LOGGER.info("Decoding duplicatedex.apk..."); ApkDecoder apkDecoder = new ApkDecoder(testApk); @@ -53,22 +53,22 @@ public void decodeAllSourcesShouldThrowException() throws BrutException, IOExcep LOGGER.info("Building duplicatedex.apk..."); Config config = Config.getDefaultConfig(); - new ApkBuilder(config, sTestNewDir).build(testApk); + new ApkBuilder(sTestNewDir, config).build(testApk); } @Test public void decodeUsingOnlyMainClassesMode() throws BrutException, IOException { - File testApk = new File(sTestOrigDir, "duplicatedex.apk"); + ExtFile testApk = new ExtFile(sTestOrigDir, "duplicatedex.apk"); LOGGER.info("Decoding duplicatedex.apk..."); Config config = Config.getDefaultConfig(); config.decodeSources = Config.DECODE_SOURCES_SMALI_ONLY_MAIN_CLASSES; - ApkDecoder apkDecoder = new ApkDecoder(config, testApk); + ApkDecoder apkDecoder = new ApkDecoder(testApk, config); apkDecoder.decode(sTestNewDir); LOGGER.info("Building duplicatedex.apk..."); - new ApkBuilder(config, sTestNewDir).build(testApk); + new ApkBuilder(sTestNewDir, config).build(testApk); } } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/Empty9PatchTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/Empty9PatchTest.java index d9789e2953..da4d9f0968 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/Empty9PatchTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/Empty9PatchTest.java @@ -51,7 +51,7 @@ public void decodeWithEmpty9PatchFile() throws BrutException, IOException { String apk = "empty9patch.apk"; // decode empty9patch.apk - ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + apk)); sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + ".out"); File outDir = new File(sTmpDir + File.separator + apk + ".out"); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/EmptyArscTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/EmptyArscTest.java index 4a7916d67d..75de4d6beb 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/EmptyArscTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/EmptyArscTest.java @@ -50,7 +50,7 @@ public void decodeWithEmptyArscFile() throws BrutException, IOException { String apk = "test.apk"; // decode test.apk - ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + apk)); sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + ".out"); File outDir = new File(sTmpDir + File.separator + apk + ".out"); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ForceManifestDecodeNoResourcesTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ForceManifestDecodeNoResourcesTest.java index 3ce0491bfc..da1acfed73 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ForceManifestDecodeNoResourcesTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ForceManifestDecodeNoResourcesTest.java @@ -139,7 +139,7 @@ private void decodeFile(String apk, short decodeResources, short decodeManifest, config.forceDelete = true; config.decodeResources = decodeResources; config.forceDecodeManifest = decodeManifest; - ApkDecoder apkDecoder = new ApkDecoder(config, new File(apk)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(apk), config); apkDecoder.decode(new File(output)); } } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/MinifiedArscTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/MinifiedArscTest.java index f4e1207742..168bc9b98d 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/MinifiedArscTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/MinifiedArscTest.java @@ -48,7 +48,7 @@ public static void beforeClass() throws Exception { Config config = Config.getDefaultConfig(); config.forceDelete = true; // decode issue1157.apk - ApkDecoder apkDecoder = new ApkDecoder(config, new ExtFile(sTmpDir, apk)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTmpDir, apk), config); // this should not raise an exception: apkDecoder.decode(sTestNewDir); } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/MissingVersionManifestTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/MissingVersionManifestTest.java index 55eaf149a4..a6002f68e7 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/MissingVersionManifestTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/MissingVersionManifestTest.java @@ -51,7 +51,7 @@ public void missingVersionParsesCorrectlyTest() throws BrutException, IOExceptio String apk = "issue1264.apk"; // decode issue1264.apk - ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + apk)); ExtFile decodedApk = new ExtFile(sTmpDir + File.separator + apk + ".out"); File outDir = new File(sTmpDir + File.separator + apk + ".out"); apkDecoder.decode(outDir); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/OutsideOfDirectoryEntryTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/OutsideOfDirectoryEntryTest.java index 80b4c4f5bc..2978b3aab5 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/OutsideOfDirectoryEntryTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/OutsideOfDirectoryEntryTest.java @@ -42,7 +42,7 @@ public static void beforeClass() throws Exception { String apk = "issue1589.apk"; // decode issue1589.apk - ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + apk)); sTestNewDir = new ExtFile(sTmpDir + File.separator + apk + ".out"); File outDir = new File(sTmpDir + File.separator + apk + ".out"); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ParentDirectoryTraversalTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ParentDirectoryTraversalTest.java index 1805a27eac..f1bb0917d4 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ParentDirectoryTraversalTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ParentDirectoryTraversalTest.java @@ -52,7 +52,7 @@ public void checkIfDrawableFileDecodesProperly() throws BrutException, IOExcepti config.forceDelete = true; config.decodeResources = Config.DECODE_RESOURCES_NONE; // decode issue1498.apk - ApkDecoder apkDecoder = new ApkDecoder(config, new File(sTmpDir + File.separator + apk)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + apk), config); File outDir = new File(sTmpDir + File.separator + apk + ".out"); // this should not raise an exception: apkDecoder.decode(outDir); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ProtectedApkTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ProtectedApkTest.java index 2c979766b4..c1f419bbc6 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ProtectedApkTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ProtectedApkTest.java @@ -47,7 +47,7 @@ public void checkIfDecodeWorksWithoutCrash() throws BrutException, IOException { String apk = "protected-v1.apk"; // decode protected-v1.apk - ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + apk)); sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + ".out"); File outDir = new File(sTmpDir + File.separator + apk + ".out"); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ResourceDirectoryTraversalTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ResourceDirectoryTraversalTest.java index 44ec664cb9..a9245b66bf 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ResourceDirectoryTraversalTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ResourceDirectoryTraversalTest.java @@ -52,7 +52,7 @@ public void checkIfMaliciousRawFileIsDisassembledProperly() throws BrutException Config config = Config.getDefaultConfig(); config.forceDelete = true; - ApkDecoder apkDecoder = new ApkDecoder(config, new File(sTmpDir + File.separator + apk)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + apk), config); File outDir = new File(sTmpDir + File.separator + apk + ".out"); apkDecoder.decode(outDir); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ResourceModeTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ResourceModeTest.java index 1fbe855dd9..e0af62f454 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ResourceModeTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/ResourceModeTest.java @@ -53,7 +53,7 @@ public void checkDecodingModeAsRemove() throws BrutException, IOException { config.setDecodeResolveMode(Config.DECODE_RES_RESOLVE_REMOVE); // decode issue2836.apk - ApkDecoder apkDecoder = new ApkDecoder(config, new File(sTmpDir + File.separator + apk)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + apk), config); sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + "remove.out"); File outDir = new File(sTmpDir + File.separator + apk + "remove.out"); @@ -84,7 +84,7 @@ public void checkDecodingModeAsDummies() throws BrutException, IOException { config.setDecodeResolveMode(Config.DECODE_RES_RESOLVE_DUMMY); // decode issue2836.apk - ApkDecoder apkDecoder = new ApkDecoder(config, new File(sTmpDir + File.separator + apk)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + apk), config); sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + "dummies.out"); File outDir = new File(sTmpDir + File.separator + apk + "dummies.out"); @@ -115,7 +115,7 @@ public void checkDecodingModeAsLeave() throws BrutException, IOException { config.setDecodeResolveMode(Config.DECODE_RES_RESOLVE_RETAIN); // decode issue2836.apk - ApkDecoder apkDecoder = new ApkDecoder(config, new File(sTmpDir + File.separator + apk)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + apk), config); sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + "leave.out"); File outDir = new File(sTmpDir + File.separator + apk + "leave.out"); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/SparseFlagTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/SparseFlagTest.java index c3959250f4..1667b28006 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/SparseFlagTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/SparseFlagTest.java @@ -50,35 +50,35 @@ public void afterClass() throws BrutException { @Test public void decodeWithExpectationOfSparseResources() throws BrutException, IOException { - File testApk = new File(sTestOrigDir, "sparse.apk"); + ExtFile testApk = new ExtFile(sTestOrigDir, "sparse.apk"); LOGGER.info("Decoding sparse.apk..."); Config config = Config.getDefaultConfig(); config.frameworkTag = "issue-3298"; - ApkDecoder apkDecoder = new ApkDecoder(config, testApk); + ApkDecoder apkDecoder = new ApkDecoder(testApk, config); ApkInfo apkInfo = apkDecoder.decode(sTestNewDir); assertTrue("Expecting sparse resources", apkInfo.sparseResources); LOGGER.info("Building sparse.apk..."); - new ApkBuilder(config, sTestNewDir).build(testApk); + new ApkBuilder(sTestNewDir, config).build(testApk); } @Test public void decodeWithExpectationOfNoSparseResources() throws BrutException, IOException { - File testApk = new File(sTestOrigDir, "not-sparse.apk"); + ExtFile testApk = new ExtFile(sTestOrigDir, "not-sparse.apk"); LOGGER.info("Decoding not-sparse.apk..."); Config config = Config.getDefaultConfig(); config.frameworkTag = "issue-3298"; - ApkDecoder apkDecoder = new ApkDecoder(config, testApk); + ApkDecoder apkDecoder = new ApkDecoder(testApk, config); ApkInfo apkInfo = apkDecoder.decode(sTestNewDir); assertFalse("Expecting not-sparse resources", apkInfo.sparseResources); LOGGER.info("Building not-sparse.apk..."); - new ApkBuilder(config, sTestNewDir).build(testApk); + new ApkBuilder(sTestNewDir, config).build(testApk); } } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/VectorDrawableTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/VectorDrawableTest.java index 255775a8d3..5f6c07061b 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/VectorDrawableTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/decode/VectorDrawableTest.java @@ -50,7 +50,7 @@ public void checkIfDrawableFileDecodesProperly() throws BrutException, IOExcepti String apk = "issue1456.apk"; // decode issue1456.apk - ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(sTmpDir + File.separator + apk)); sTestOrigDir = new ExtFile(sTmpDir + File.separator + apk + ".out"); File outDir = new File(sTmpDir + File.separator + apk + ".out"); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/res/src/DexStaticFieldValueTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/res/src/DexStaticFieldValueTest.java index e8f8dd50f6..4007f5802d 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/res/src/DexStaticFieldValueTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/res/src/DexStaticFieldValueTest.java @@ -49,11 +49,11 @@ public static void beforeClass() throws Exception { LOGGER.info("Building issue2543.apk..."); File testApk = new File(sTmpDir, "issue2543.apk"); - new ApkBuilder(config, sTestOrigDir).build(testApk); + new ApkBuilder(sTestOrigDir, config).build(testApk); LOGGER.info("Decoding issue2543.apk..."); config.baksmaliDebugMode = false; - ApkDecoder apkDecoder = new ApkDecoder(config, new ExtFile(testApk)); + ApkDecoder apkDecoder = new ApkDecoder(new ExtFile(testApk), config); apkDecoder.decode(sTestNewDir); } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/util/UnknownDirectoryTraversalTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/util/UnknownDirectoryTraversalTest.java index 269b3fc07e..27ddc960af 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/util/UnknownDirectoryTraversalTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/util/UnknownDirectoryTraversalTest.java @@ -50,32 +50,32 @@ public static void afterClass() throws BrutException { } @Test - public void validFileTest() throws IOException, BrutException { - String validFilename = BrutIO.sanitizeFilepath(sTmpDir, "file"); - assertEquals(validFilename, "file"); + public void validFileTest() throws BrutException, IOException { + String validFileName = BrutIO.sanitizePath(sTmpDir, "file"); + assertEquals(validFileName, "file"); - File validFile = new File(sTmpDir, validFilename); + File validFile = new File(sTmpDir, validFileName); assertTrue(validFile.isFile()); } @Test(expected = TraversalUnknownFileException.class) - public void invalidBackwardFileTest() throws IOException, BrutException { - BrutIO.sanitizeFilepath(sTmpDir, "../file"); + public void invalidBackwardFileTest() throws BrutException, IOException { + BrutIO.sanitizePath(sTmpDir, "../file"); } @Test(expected = RootUnknownFileException.class) - public void invalidRootFileTest() throws IOException, BrutException { + public void invalidRootFileTest() throws BrutException, IOException { String rootLocation = OSDetection.isWindows() ? "C:/" : File.separator; - BrutIO.sanitizeFilepath(sTmpDir, rootLocation + "file"); + BrutIO.sanitizePath(sTmpDir, rootLocation + "file"); } @Test(expected = InvalidUnknownFileException.class) - public void noFilePassedTest() throws IOException, BrutException { - BrutIO.sanitizeFilepath(sTmpDir, ""); + public void noFilePassedTest() throws BrutException, IOException { + BrutIO.sanitizePath(sTmpDir, ""); } @Test(expected = TraversalUnknownFileException.class) - public void invalidBackwardPathOnWindows() throws IOException, BrutException { + public void invalidBackwardPathOnWindows() throws BrutException, IOException { String invalidPath; if (! OSDetection.isWindows()) { invalidPath = "../../app"; @@ -83,12 +83,12 @@ public void invalidBackwardPathOnWindows() throws IOException, BrutException { invalidPath = "..\\..\\app.exe"; } - BrutIO.sanitizeFilepath(sTmpDir, invalidPath); + BrutIO.sanitizePath(sTmpDir, invalidPath); } @Test - public void validDirectoryFileTest() throws IOException, BrutException { - String validFilename = BrutIO.sanitizeFilepath(sTmpDir, "dir" + File.separator + "file"); - assertEquals("dir" + File.separator + "file", validFilename); + public void validDirectoryFileTest() throws BrutException, IOException { + String validFileName = BrutIO.sanitizePath(sTmpDir, "dir" + File.separator + "file"); + assertEquals("dir" + File.separator + "file", validFileName); } } diff --git a/brut.apktool/apktool-lib/src/test/resources/aapt1/issue1235/apktool.yml b/brut.apktool/apktool-lib/src/test/resources/aapt1/issue1235/apktool.yml index 9daf7e741f..96426c320e 100644 --- a/brut.apktool/apktool-lib/src/test/resources/aapt1/issue1235/apktool.yml +++ b/brut.apktool/apktool-lib/src/test/resources/aapt1/issue1235/apktool.yml @@ -8,5 +8,4 @@ packageInfo: forcedPackageId: '127' versionInfo: versionCode: '1' - versionName: '1.0' -compressionType: false \ No newline at end of file + versionName: '1.0' \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/resources/aapt1/testapp/apktool.yml b/brut.apktool/apktool-lib/src/test/resources/aapt1/testapp/apktool.yml index 8362ef054a..87d8da1839 100644 --- a/brut.apktool/apktool-lib/src/test/resources/aapt1/testapp/apktool.yml +++ b/brut.apktool/apktool-lib/src/test/resources/aapt1/testapp/apktool.yml @@ -9,17 +9,9 @@ packageInfo: versionInfo: versionCode: '1' versionName: '1.0' -compressionType: false doNotCompress: - assets/0byte_file.jpg - arsc - png - mp3 -unknownFiles: - AssetBundle/assets/a.txt: '8' - AssetBundle/b.txt: '8' - hidden.file: '8' - non\u007Fprintable.file: '8' - stored.file: '0' - unk_folder/unknown_file: '8' - lib_bug603/bug603: '8' +- stored.file diff --git a/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-false/apktool.yml b/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-false/apktool.yml index 73522b6244..ecc612a206 100644 --- a/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-false/apktool.yml +++ b/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-false/apktool.yml @@ -8,5 +8,4 @@ packageInfo: forcedPackageId: '127' versionInfo: versionCode: '1' - versionName: '1.0' -compressionType: false \ No newline at end of file + versionName: '1.0' \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-missing/apktool.yml b/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-missing/apktool.yml index 73522b6244..ecc612a206 100644 --- a/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-missing/apktool.yml +++ b/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-missing/apktool.yml @@ -8,5 +8,4 @@ packageInfo: forcedPackageId: '127' versionInfo: versionCode: '1' - versionName: '1.0' -compressionType: false \ No newline at end of file + versionName: '1.0' \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-true/apktool.yml b/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-true/apktool.yml index 73522b6244..ecc612a206 100644 --- a/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-true/apktool.yml +++ b/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-true/apktool.yml @@ -8,5 +8,4 @@ packageInfo: forcedPackageId: '127' versionInfo: versionCode: '1' - versionName: '1.0' -compressionType: false \ No newline at end of file + versionName: '1.0' \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/resources/aapt2/pkgid8/apktool.yml b/brut.apktool/apktool-lib/src/test/resources/aapt2/pkgid8/apktool.yml index 7487500608..dc7aef41a4 100644 --- a/brut.apktool/apktool-lib/src/test/resources/aapt2/pkgid8/apktool.yml +++ b/brut.apktool/apktool-lib/src/test/resources/aapt2/pkgid8/apktool.yml @@ -8,5 +8,4 @@ packageInfo: forcedPackageId: '128' versionInfo: versionCode: '1' - versionName: '1.0' -compressionType: false \ No newline at end of file + versionName: '1.0' \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/apktool.yml b/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/apktool.yml index e83d2e54c2..f11e206fa0 100644 --- a/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/apktool.yml +++ b/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/apktool.yml @@ -9,10 +9,6 @@ packageInfo: versionInfo: versionCode: '1' versionName: '1.0' -compressionType: false doNotCompress: - assets/0byte_file.jpg sparseResources: false -unknownFiles: - AssetBundle/assets/a.txt: '8' - AssetBundle/b.txt: '8' diff --git a/brut.apktool/apktool-lib/src/test/resources/apk/basic.yml b/brut.apktool/apktool-lib/src/test/resources/apk/basic.yml index b51612c6bd..c1ecc383bf 100644 --- a/brut.apktool/apktool-lib/src/test/resources/apk/basic.yml +++ b/brut.apktool/apktool-lib/src/test/resources/apk/basic.yml @@ -1,8 +1,7 @@ !!brut.androlib.meta.MetaInfo apkFileName: basic.apk -compressionType: false doNotCompress: - - resources.arsc + - arsc - png isFrameworkApk: false packageInfo: @@ -14,8 +13,6 @@ sdkInfo: targetSdkVersion: '22' sharedLibrary: false sparseResources: true -unknownFiles: - hidden.file: 1 usesFramework: ids: - 1 diff --git a/brut.apktool/apktool-lib/src/test/resources/apk/cve20220476.yml b/brut.apktool/apktool-lib/src/test/resources/apk/cve20220476.yml index 2006ae81db..a705c8c7c5 100644 --- a/brut.apktool/apktool-lib/src/test/resources/apk/cve20220476.yml +++ b/brut.apktool/apktool-lib/src/test/resources/apk/cve20220476.yml @@ -1,9 +1,8 @@ !!brut.androlib.meta.MetaInfo apkFileName: cve20220476.apk -compressionType: false some_var: !!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["https://127.0.0.1:8000"]]]] doNotCompress: -- resources.arsc +- arsc isFrameworkApk: false packageInfo: forcedPackageId: '127' diff --git a/brut.apktool/apktool-lib/src/test/resources/apk/first_incorrect_indent.yml b/brut.apktool/apktool-lib/src/test/resources/apk/first_incorrect_indent.yml index 49e9b66901..08f35ab74e 100644 --- a/brut.apktool/apktool-lib/src/test/resources/apk/first_incorrect_indent.yml +++ b/brut.apktool/apktool-lib/src/test/resources/apk/first_incorrect_indent.yml @@ -1,9 +1,8 @@ !!brut.androlib.meta.MetaInfo version: 2.0.0 apkFileName: standard.apk -compressionType: false doNotCompress: -- resources.arsc +- arsc isFrameworkApk: false packageInfo: forcedPackageId: '127' diff --git a/brut.apktool/apktool-lib/src/test/resources/apk/list_with_indent.yml b/brut.apktool/apktool-lib/src/test/resources/apk/list_with_indent.yml index b51612c6bd..c1ecc383bf 100644 --- a/brut.apktool/apktool-lib/src/test/resources/apk/list_with_indent.yml +++ b/brut.apktool/apktool-lib/src/test/resources/apk/list_with_indent.yml @@ -1,8 +1,7 @@ !!brut.androlib.meta.MetaInfo apkFileName: basic.apk -compressionType: false doNotCompress: - - resources.arsc + - arsc - png isFrameworkApk: false packageInfo: @@ -14,8 +13,6 @@ sdkInfo: targetSdkVersion: '22' sharedLibrary: false sparseResources: true -unknownFiles: - hidden.file: 1 usesFramework: ids: - 1 diff --git a/brut.apktool/apktool-lib/src/test/resources/apk/skip_incorrect_indent.yml b/brut.apktool/apktool-lib/src/test/resources/apk/skip_incorrect_indent.yml index cfdc2d7838..f135689727 100644 --- a/brut.apktool/apktool-lib/src/test/resources/apk/skip_incorrect_indent.yml +++ b/brut.apktool/apktool-lib/src/test/resources/apk/skip_incorrect_indent.yml @@ -1,9 +1,8 @@ !!brut.androlib.meta.MetaInfo apkFileName: standard.apk version: 2.0.0 -compressionType: false doNotCompress: -- resources.arsc +- arsc isFrameworkApk: false packageInfo: forcedPackageId: '127' diff --git a/brut.apktool/apktool-lib/src/test/resources/apk/standard.yml b/brut.apktool/apktool-lib/src/test/resources/apk/standard.yml index 6aa78ecb55..deb61b225b 100644 --- a/brut.apktool/apktool-lib/src/test/resources/apk/standard.yml +++ b/brut.apktool/apktool-lib/src/test/resources/apk/standard.yml @@ -1,8 +1,7 @@ !!brut.androlib.meta.MetaInfo apkFileName: standard.apk -compressionType: false doNotCompress: -- resources.arsc +- arsc isFrameworkApk: false packageInfo: forcedPackageId: '127' diff --git a/brut.apktool/apktool-lib/src/test/resources/apk/unknown_fields.yml b/brut.apktool/apktool-lib/src/test/resources/apk/unknown_fields.yml index 876e9fcaef..dd90de4075 100644 --- a/brut.apktool/apktool-lib/src/test/resources/apk/unknown_fields.yml +++ b/brut.apktool/apktool-lib/src/test/resources/apk/unknown_fields.yml @@ -1,9 +1,8 @@ !!brut.androlib.meta.MetaInfo apkFileName: standard.apk -compressionType: false test: test doNotCompress: -- resources.arsc +- arsc isFrameworkApk: false packageInfo: forcedPackageId: '127' diff --git a/brut.apktool/apktool-lib/src/test/resources/apk/unknown_files.yml b/brut.apktool/apktool-lib/src/test/resources/apk/unknown_files.yml index 8362ef054a..87d8da1839 100644 --- a/brut.apktool/apktool-lib/src/test/resources/apk/unknown_files.yml +++ b/brut.apktool/apktool-lib/src/test/resources/apk/unknown_files.yml @@ -9,17 +9,9 @@ packageInfo: versionInfo: versionCode: '1' versionName: '1.0' -compressionType: false doNotCompress: - assets/0byte_file.jpg - arsc - png - mp3 -unknownFiles: - AssetBundle/assets/a.txt: '8' - AssetBundle/b.txt: '8' - hidden.file: '8' - non\u007Fprintable.file: '8' - stored.file: '0' - unk_folder/unknown_file: '8' - lib_bug603/bug603: '8' +- stored.file diff --git a/brut.apktool/apktool-lib/src/test/resources/decode/doctype/apktool.yml b/brut.apktool/apktool-lib/src/test/resources/decode/doctype/apktool.yml index 1e3c552c84..fd49b569cc 100644 --- a/brut.apktool/apktool-lib/src/test/resources/decode/doctype/apktool.yml +++ b/brut.apktool/apktool-lib/src/test/resources/decode/doctype/apktool.yml @@ -8,5 +8,4 @@ packageInfo: forcedPackageId: '127' versionInfo: versionCode: '1' - versionName: '1.0' -compressionType: false \ No newline at end of file + versionName: '1.0' \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/resources/decode/issue2543/apktool.yml b/brut.apktool/apktool-lib/src/test/resources/decode/issue2543/apktool.yml index 7e576b377a..2fa938a15d 100644 --- a/brut.apktool/apktool-lib/src/test/resources/decode/issue2543/apktool.yml +++ b/brut.apktool/apktool-lib/src/test/resources/decode/issue2543/apktool.yml @@ -8,5 +8,4 @@ packageInfo: forcedPackageId: '127' versionInfo: versionCode: '1' - versionName: '1.0' -compressionType: false \ No newline at end of file + versionName: '1.0' \ No newline at end of file diff --git a/brut.j.dir/src/main/java/brut/directory/AbstractDirectory.java b/brut.j.dir/src/main/java/brut/directory/AbstractDirectory.java index ecd5da77b9..a39aa69b6d 100644 --- a/brut.j.dir/src/main/java/brut/directory/AbstractDirectory.java +++ b/brut.j.dir/src/main/java/brut/directory/AbstractDirectory.java @@ -59,7 +59,7 @@ public boolean containsFile(String path) { SubPath subpath; try { subpath = getSubPath(path); - } catch (PathNotExist e) { + } catch (PathNotExist ex) { return false; } @@ -74,7 +74,7 @@ public boolean containsDir(String path) { SubPath subpath; try { subpath = getSubPath(path); - } catch (PathNotExist e) { + } catch (PathNotExist ex) { return false; } @@ -117,10 +117,9 @@ public OutputStream getFileOutput(String path) throws DirectoryException { } Directory dir; - // IMPOSSIBLE_EXCEPTION try { dir = createDir(parsed.dir); - } catch (PathAlreadyExists e) { + } catch (PathAlreadyExists ex) { dir = getAbstractDirs().get(parsed.dir); } return dir.getFileOutput(parsed.subpath); @@ -165,7 +164,7 @@ public boolean removeFile(String path) { SubPath subpath; try { subpath = getSubPath(path); - } catch (PathNotExist e) { + } catch (PathNotExist ex) { return false; } @@ -226,7 +225,7 @@ protected Map getAbstractDirs(boolean recursive) { } Map dirs = new LinkedHashMap<>(mDirs); - for (Map.Entry dir : getAbstractDirs().entrySet()) { + for (Map.Entry dir : mDirs.entrySet()) { for (Map.Entry subdir : dir.getValue().getAbstractDirs( true).entrySet()) { dirs.put(dir.getKey() + separator + subdir.getKey(), diff --git a/brut.j.dir/src/main/java/brut/directory/DirUtil.java b/brut.j.dir/src/main/java/brut/directory/DirUtil.java index 906fdac256..c6e96794f1 100644 --- a/brut.j.dir/src/main/java/brut/directory/DirUtil.java +++ b/brut.j.dir/src/main/java/brut/directory/DirUtil.java @@ -22,6 +22,7 @@ import brut.common.TraversalUnknownFileException; import brut.util.BrutIO; import brut.util.OS; + import java.io.*; import java.nio.file.FileSystemException; import java.nio.file.Files; @@ -34,15 +35,13 @@ private DirUtil() { // Private constructor for utility class } - public static void copyToDir(Directory in, Directory out) - throws DirectoryException { + public static void copyToDir(Directory in, Directory out) throws DirectoryException { for (String fileName : in.getFiles(true)) { copyToDir(in, out, fileName); } } - public static void copyToDir(Directory in, Directory out, - String[] fileNames) throws DirectoryException { + public static void copyToDir(Directory in, Directory out, String[] fileNames) throws DirectoryException { for (String fileName : fileNames) { copyToDir(in, out, fileName); } @@ -84,23 +83,24 @@ public static void copyToDir(Directory in, File out, String fileName) throws DirectoryException { try { if (in.containsDir(fileName)) { - OS.rmdir(new File(out, fileName)); - in.getDir(fileName).copyToDir(new File(out, fileName)); - } else if (!in.containsDir(fileName) && !in.containsFile(fileName)) { - // Skip copies of directories/files not found. - } else { - String cleanedFilename = BrutIO.sanitizeFilepath(out, fileName); - if (! cleanedFilename.isEmpty()) { - File outFile = new File(out, cleanedFilename); + File outDir = new File(out, fileName); + OS.rmdir(outDir); + in.getDir(fileName).copyToDir(outDir); + } else if (in.containsFile(fileName)) { + String validFileName = BrutIO.sanitizePath(out, fileName); + if (!validFileName.isEmpty()) { + File outFile = new File(out, validFileName); //noinspection ResultOfMethodCallIgnored outFile.getParentFile().mkdirs(); BrutIO.copyAndClose(in.getFileInput(fileName), Files.newOutputStream(outFile.toPath())); } + } else { + // Skip if directory/file not found } - } catch (FileSystemException exception) { - LOGGER.warning(String.format("Skipping file %s (%s)", fileName, exception.getReason())); - } catch (RootUnknownFileException | InvalidUnknownFileException | TraversalUnknownFileException | IOException exception) { - LOGGER.warning(String.format("Skipping file %s (%s)", fileName, exception.getMessage())); + } catch (FileSystemException ex) { + LOGGER.warning(String.format("Skipping file %s (%s)", fileName, ex.getReason())); + } catch (RootUnknownFileException | InvalidUnknownFileException | TraversalUnknownFileException | IOException ex) { + LOGGER.warning(String.format("Skipping file %s (%s)", fileName, ex.getMessage())); } catch (BrutException ex) { throw new DirectoryException("Error copying file: " + fileName, ex); } diff --git a/brut.j.dir/src/main/java/brut/directory/ExtFile.java b/brut.j.dir/src/main/java/brut/directory/ExtFile.java index 575ea9b338..5572848795 100644 --- a/brut.j.dir/src/main/java/brut/directory/ExtFile.java +++ b/brut.j.dir/src/main/java/brut/directory/ExtFile.java @@ -58,5 +58,14 @@ public void close() throws IOException { } } + @Override + public boolean delete() { + try { + close(); + } catch (IOException ignored) {} + + return super.delete(); + } + private Directory mDirectory; } diff --git a/brut.j.dir/src/main/java/brut/directory/FileDirectory.java b/brut.j.dir/src/main/java/brut/directory/FileDirectory.java index fd6e88a5e0..33b7fd59c7 100644 --- a/brut.j.dir/src/main/java/brut/directory/FileDirectory.java +++ b/brut.j.dir/src/main/java/brut/directory/FileDirectory.java @@ -70,8 +70,8 @@ protected AbstractDirectory createDirLocal(String name) throws DirectoryExceptio protected InputStream getFileInputLocal(String name) throws DirectoryException { try { return new FileInputStream(generatePath(name)); - } catch (FileNotFoundException e) { - throw new DirectoryException(e); + } catch (FileNotFoundException ex) { + throw new DirectoryException(ex); } } @@ -79,8 +79,8 @@ protected InputStream getFileInputLocal(String name) throws DirectoryException { protected OutputStream getFileOutputLocal(String name) throws DirectoryException { try { return new FileOutputStream(generatePath(name)); - } catch (FileNotFoundException e) { - throw new DirectoryException(e); + } catch (FileNotFoundException ex) { + throw new DirectoryException(ex); } } @@ -115,7 +115,6 @@ private void loadAll() { if (file.isFile()) { mFiles.add(file.getName()); } else { - // IMPOSSIBLE_EXCEPTION try { mDirs.put(file.getName(), new FileDirectory(file)); } catch (DirectoryException ignored) {} diff --git a/brut.j.dir/src/main/java/brut/directory/ZipRODirectory.java b/brut.j.dir/src/main/java/brut/directory/ZipRODirectory.java index b6072abe40..4eb8bc7600 100644 --- a/brut.j.dir/src/main/java/brut/directory/ZipRODirectory.java +++ b/brut.j.dir/src/main/java/brut/directory/ZipRODirectory.java @@ -51,8 +51,8 @@ public ZipRODirectory(File zipFile, String path) throws DirectoryException { super(); try { mZipFile = new ZipFile(zipFile); - } catch (IOException e) { - throw new DirectoryException(e); + } catch (IOException ex) { + throw new DirectoryException(ex); } mPath = path; } @@ -73,8 +73,8 @@ protected InputStream getFileInputLocal(String name) throws DirectoryException { try { return getZipFile().getInputStream(new ZipEntry(getPath() + name)); - } catch (IOException e) { - throw new PathNotExist(name, e); + } catch (IOException ex) { + throw new PathNotExist(name, ex); } } diff --git a/brut.j.dir/src/main/java/brut/directory/ZipUtils.java b/brut.j.dir/src/main/java/brut/directory/ZipUtils.java index db9dcdf36b..d632e3fb54 100644 --- a/brut.j.dir/src/main/java/brut/directory/ZipUtils.java +++ b/brut.j.dir/src/main/java/brut/directory/ZipUtils.java @@ -17,6 +17,9 @@ package brut.directory; import brut.common.BrutException; +import brut.common.InvalidUnknownFileException; +import brut.common.RootUnknownFileException; +import brut.common.TraversalUnknownFileException; import brut.util.BrutIO; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; @@ -24,64 +27,89 @@ import java.io.*; import java.nio.file.Files; import java.util.Collection; +import java.util.function.Predicate; +import java.util.logging.Logger; import java.util.zip.CRC32; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class ZipUtils { - - private static Collection mDoNotCompress; + private static final Logger LOGGER = Logger.getLogger(""); private ZipUtils() { // Private constructor for utility class } - public static void zipFoldersPreserveStream(final File folder, final ZipOutputStream zipOutputStream, final File assets, final Collection doNotCompress) - throws BrutException, IOException { + public static void zipDir(File dir, ZipOutputStream out, Collection doNotCompress) + throws IOException { + zipDir(dir, null, out, doNotCompress); + } + + public static void zipDir(File baseDir, String dirName, ZipOutputStream out, Collection doNotCompress) + throws IOException { + File dir; + if (dirName == null || dirName.isEmpty()) { + dir = baseDir; + } else { + dir = new File(baseDir, dirName); + } + if (!dir.isDirectory()) { + return; + } - mDoNotCompress = doNotCompress; - zipFolders(folder, zipOutputStream); + for (File file : dir.listFiles()) { + String fileName = baseDir.toURI().relativize(file.toURI()).getPath(); - // We manually set the assets because we need to retain the folder structure - if (assets != null) { - processFolder(assets, zipOutputStream, assets.getPath().length() - 6); + if (file.isDirectory()) { + zipDir(baseDir, fileName, out, doNotCompress); + } else if (file.isFile()) { + zipFile(baseDir, fileName, out, doNotCompress != null && !doNotCompress.isEmpty() + ? entryName -> doNotCompress.contains(entryName) + || doNotCompress.contains(FilenameUtils.getExtension(entryName)) + : entryName -> false); + } } } - private static void zipFolders(final File folder, final ZipOutputStream outputStream) - throws BrutException, IOException { - processFolder(folder, outputStream, folder.getPath().length() + 1); + public static void zipFile(File baseDir, String fileName, ZipOutputStream out, boolean doNotCompress) + throws IOException { + zipFile(baseDir, fileName, out, entryName -> doNotCompress); } - private static void processFolder(final File folder, final ZipOutputStream zipOutputStream, final int prefixLength) - throws BrutException, IOException { - for (final File file : folder.listFiles()) { - if (file.isFile()) { - final String cleanedPath = BrutIO.sanitizeFilepath(folder, file.getPath().substring(prefixLength)); - final ZipEntry zipEntry = new ZipEntry(BrutIO.adaptSeparatorToUnix(cleanedPath)); + private static void zipFile(File baseDir, String fileName, ZipOutputStream out, Predicate doNotCompress) + throws IOException { + try { + String validFileName = BrutIO.sanitizePath(baseDir, fileName); + if (validFileName.isEmpty()) { + return; + } + + File file = new File(baseDir, validFileName); + if (!file.isFile()) { + return; + } - // aapt binary by default takes in parameters via -0 arsc to list extensions that shouldn't be - // compressed. We will replicate that behavior - final String extension = FilenameUtils.getExtension(file.getAbsolutePath()); - if (mDoNotCompress != null && (mDoNotCompress.contains(extension) || mDoNotCompress.contains(zipEntry.getName()))) { - zipEntry.setMethod(ZipEntry.STORED); - zipEntry.setSize(file.length()); - BufferedInputStream unknownFile = new BufferedInputStream(Files.newInputStream(file.toPath())); - CRC32 crc = BrutIO.calculateCrc(unknownFile); + String entryName = BrutIO.adaptSeparatorToUnix(validFileName); + ZipEntry zipEntry = new ZipEntry(entryName); + + if (doNotCompress.test(entryName)) { + zipEntry.setMethod(ZipEntry.STORED); + zipEntry.setSize(file.length()); + try (BufferedInputStream bis = new BufferedInputStream(Files.newInputStream(file.toPath()))) { + CRC32 crc = BrutIO.calculateCrc(bis); zipEntry.setCrc(crc.getValue()); - unknownFile.close(); - } else { - zipEntry.setMethod(ZipEntry.DEFLATED); } + } else { + zipEntry.setMethod(ZipEntry.DEFLATED); + } - zipOutputStream.putNextEntry(zipEntry); - try (FileInputStream inputStream = new FileInputStream(file)) { - IOUtils.copy(inputStream, zipOutputStream); - } - zipOutputStream.closeEntry(); - } else if (file.isDirectory()) { - processFolder(file, zipOutputStream, prefixLength); + out.putNextEntry(zipEntry); + try (InputStream in = Files.newInputStream(file.toPath())) { + IOUtils.copy(in, out); } + out.closeEntry(); + } catch (RootUnknownFileException | InvalidUnknownFileException | TraversalUnknownFileException ex) { + LOGGER.warning(String.format("Skipping file %s (%s)", fileName, ex.getMessage())); } } } diff --git a/brut.j.util/src/main/java/brut/util/AaptManager.java b/brut.j.util/src/main/java/brut/util/AaptManager.java index b1375b15d9..e62d33ead3 100644 --- a/brut.j.util/src/main/java/brut/util/AaptManager.java +++ b/brut.j.util/src/main/java/brut/util/AaptManager.java @@ -22,6 +22,8 @@ import java.util.List; public class AaptManager { + public static final int AAPT_VERSION_MIN = 1; + public static final int AAPT_VERSION_MAX = 2; public static File getAapt2() throws BrutException { return getAapt(2); @@ -31,59 +33,71 @@ public static File getAapt1() throws BrutException { return getAapt(1); } - private static File getAapt(Integer version) throws BrutException { - File aaptBinary; - String aaptVersion = getAaptBinaryName(version); + private static File getAapt(int version) throws BrutException { + String aaptName = getAaptBinaryName(version); - if (! OSDetection.is64Bit() && OSDetection.isMacOSX()) { - throw new BrutException("32 bit OS detected. No 32 bit binaries available."); + if (!OSDetection.is64Bit() && OSDetection.isMacOSX()) { + throw new BrutException(aaptName + " binaries are not available for 32-bit platform: " + OSDetection.returnOS()); } - // Set the 64 bit flag - aaptVersion += OSDetection.is64Bit() ? "_64" : ""; - - try { - if (OSDetection.isMacOSX()) { - aaptBinary = Jar.getResourceAsFile("/prebuilt/macosx/" + aaptVersion, AaptManager.class); - } else if (OSDetection.isUnix()) { - aaptBinary = Jar.getResourceAsFile("/prebuilt/linux/" + aaptVersion, AaptManager.class); - } else if (OSDetection.isWindows()) { - aaptBinary = Jar.getResourceAsFile("/prebuilt/windows/" + aaptVersion + ".exe", AaptManager.class); - } else { - throw new BrutException("Could not identify platform: " + OSDetection.returnOS()); - } - } catch (BrutException ex) { - throw new BrutException(ex); + StringBuilder aaptPath = new StringBuilder("/prebuilt/"); + if (OSDetection.isUnix()) { + aaptPath.append("linux"); + } else if (OSDetection.isMacOSX()) { + aaptPath.append("macosx"); + } else if (OSDetection.isWindows()) { + aaptPath.append("windows"); + } else { + throw new BrutException("Could not identify platform: " + OSDetection.returnOS()); + } + aaptPath.append("/"); + aaptPath.append(aaptName); + if (OSDetection.is64Bit()) { + aaptPath.append("_64"); + } + if (OSDetection.isWindows()) { + aaptPath.append(".exe"); } - if (aaptBinary.setExecutable(true)) { - return aaptBinary; + File aaptBinary = Jar.getResourceAsFile(aaptPath.toString(), AaptManager.class); + if (!aaptBinary.setExecutable(true)) { + throw new BrutException("Can't set aapt binary as executable"); } - throw new BrutException("Can't set aapt binary as executable"); + return aaptBinary; } - public static String getAaptExecutionCommand(String aaptPath, File aapt) throws BrutException { - if (! aaptPath.isEmpty()) { - File aaptFile = new File(aaptPath); - if (aaptFile.canRead() && aaptFile.exists()) { - //noinspection ResultOfMethodCallIgnored - aaptFile.setExecutable(true); - return aaptFile.getPath(); - } else { - throw new BrutException("binary could not be read: " + aaptFile.getAbsolutePath()); - } - } else { - return aapt.getAbsolutePath(); + public static String getAaptBinaryName(int version) { + switch (version) { + case 2: + return "aapt2"; + default: + return "aapt"; } } - public static int getAaptVersion(String aaptLocation) throws BrutException { - return getAaptVersion(new File(aaptLocation)); + public static int getAaptVersion(String aaptPath) throws BrutException { + return getAaptVersion(new File(aaptPath)); } - public static String getAaptBinaryName(Integer version) { - return "aapt" + (version == 2 ? "2" : ""); + public static int getAaptVersion(File aaptBinary) throws BrutException { + if (!aaptBinary.isFile() || !aaptBinary.canRead()) { + throw new BrutException("Can't read aapt binary: " + aaptBinary.getAbsolutePath()); + } + if (!aaptBinary.setExecutable(true)) { + throw new BrutException("Can't set aapt binary as executable: " + aaptBinary.getAbsolutePath()); + } + + List cmd = new ArrayList<>(); + cmd.add(aaptBinary.getAbsolutePath()); + cmd.add("version"); + + String version = OS.execAndReturn(cmd.toArray(new String[0])); + if (version == null) { + throw new BrutException("Could not execute aapt binary at location: " + aaptBinary.getAbsolutePath()); + } + + return getAppVersionFromString(version); } public static int getAppVersionFromString(String version) throws BrutException { @@ -98,23 +112,19 @@ public static int getAppVersionFromString(String version) throws BrutException { throw new BrutException("aapt version could not be identified: " + version); } - public static int getAaptVersion(File aapt) throws BrutException { - if (!aapt.isFile()) { - throw new BrutException("Could not identify aapt binary as executable."); + public static String getAaptExecutionCommand(String aaptPath, File aaptBinary) throws BrutException { + if (aaptPath.isEmpty()) { + return aaptBinary.getAbsolutePath(); } - //noinspection ResultOfMethodCallIgnored - aapt.setExecutable(true); - - List cmd = new ArrayList<>(); - cmd.add(aapt.getAbsolutePath()); - cmd.add("version"); - String version = OS.execAndReturn(cmd.toArray(new String[0])); - - if (version == null) { - throw new BrutException("Could not execute aapt binary at location: " + aapt.getAbsolutePath()); + aaptBinary = new File(aaptPath); + if (!aaptBinary.isFile() || !aaptBinary.canRead()) { + throw new BrutException("Can't read aapt binary: " + aaptBinary.getAbsolutePath()); + } + if (!aaptBinary.setExecutable(true)) { + throw new BrutException("Can't set aapt binary as executable: " + aaptBinary.getAbsolutePath()); } - return getAppVersionFromString(version); + return aaptBinary.getPath(); } } diff --git a/brut.j.util/src/main/java/brut/util/BrutIO.java b/brut.j.util/src/main/java/brut/util/BrutIO.java index e56867dc3d..8f319a4d94 100644 --- a/brut.j.util/src/main/java/brut/util/BrutIO.java +++ b/brut.j.util/src/main/java/brut/util/BrutIO.java @@ -24,13 +24,9 @@ import java.io.*; import java.util.zip.CRC32; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; -import java.util.zip.ZipOutputStream; public class BrutIO { - public static void copyAndClose(InputStream in, OutputStream out) - throws IOException { + public static void copyAndClose(InputStream in, OutputStream out) throws IOException { try { IOUtils.copy(in, out); } finally { @@ -68,23 +64,25 @@ public static CRC32 calculateCrc(InputStream input) throws IOException { CRC32 crc = new CRC32(); int bytesRead; byte[] buffer = new byte[8192]; - while((bytesRead = input.read(buffer)) != -1) { + while ((bytesRead = input.read(buffer)) != -1) { crc.update(buffer, 0, bytesRead); } return crc; } - public static String sanitizeFilepath(final File directory, final String entry) throws IOException, BrutException { - if (entry.isEmpty()) { + public static String sanitizePath(File baseDir, String path) + throws InvalidUnknownFileException, RootUnknownFileException, + TraversalUnknownFileException, IOException { + if (path.isEmpty()) { throw new InvalidUnknownFileException("Invalid Unknown File"); } - if (new File(entry).isAbsolute()) { + if (new File(path).isAbsolute()) { throw new RootUnknownFileException("Absolute Unknown Files is not allowed"); } - final String canonicalDirPath = directory.getCanonicalPath() + File.separator; - final String canonicalEntryPath = new File(directory, entry).getCanonicalPath(); + String canonicalDirPath = baseDir.getCanonicalPath() + File.separator; + String canonicalEntryPath = new File(baseDir, path).getCanonicalPath(); if (!canonicalEntryPath.startsWith(canonicalDirPath)) { throw new TraversalUnknownFileException("Directory Traversal is not allowed"); @@ -94,8 +92,11 @@ public static String sanitizeFilepath(final File directory, final String entry) return canonicalEntryPath.substring(canonicalDirPath.length()); } - public static boolean detectPossibleDirectoryTraversal(String entry) { - return entry.contains("../") || entry.contains("/..") || entry.contains("..\\") || entry.contains("\\.."); + public static boolean detectPossibleDirectoryTraversal(String path) { + return path.contains("../") + || path.contains("/..") + || path.contains("..\\") + || path.contains("\\.."); } public static String adaptSeparatorToUnix(String path) { @@ -107,17 +108,4 @@ public static String adaptSeparatorToUnix(String path) { return path; } - - public static void copy(File inputFile, ZipOutputStream outputFile) throws IOException { - try (FileInputStream fis = new FileInputStream(inputFile)) { - IOUtils.copy(fis, outputFile); - } - } - - public static void copy(ZipFile inputFile, ZipOutputStream outputFile, ZipEntry entry) throws IOException { - try (InputStream is = inputFile.getInputStream(entry)) { - IOUtils.copy(is, outputFile); - } - } - } diff --git a/brut.j.util/src/main/java/brut/util/OS.java b/brut.j.util/src/main/java/brut/util/OS.java index 58f3c53137..354db5207e 100644 --- a/brut.j.util/src/main/java/brut/util/OS.java +++ b/brut.j.util/src/main/java/brut/util/OS.java @@ -126,7 +126,7 @@ public static String execAndReturn(String[] cmd) { System.err.println("Stream collector did not terminate."); } return collector.get(); - } catch (IOException | InterruptedException e) { + } catch (IOException | InterruptedException ex) { return null; } } @@ -149,8 +149,8 @@ public static File createTempDirectory() throws BrutException { static class StreamForwarder extends Thread { - StreamForwarder(InputStream is, String type) { - mIn = is; + StreamForwarder(InputStream in, String type) { + mIn = in; mType = type; } diff --git a/brut.j.util/src/main/java/brut/util/OSDetection.java b/brut.j.util/src/main/java/brut/util/OSDetection.java index 2f7eae5c5c..63a5d5ea14 100644 --- a/brut.j.util/src/main/java/brut/util/OSDetection.java +++ b/brut.j.util/src/main/java/brut/util/OSDetection.java @@ -39,7 +39,7 @@ public static boolean is64Bit() { return arch != null && arch.endsWith("64") || wow64Arch != null && wow64Arch.endsWith("64"); } - return BIT.equalsIgnoreCase("64"); + return BIT.equals("64"); } public static String returnOS() {