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 0e8fb5f18d..39d5e7ceef 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 @@ -91,16 +91,16 @@ private enum Verbosity { NORMAL, VERBOSE, QUIET } .desc("Force delete destination directory.") .get(); + private static final Option decodeAllSrcOption = Option.builder("a") + .longOpt("all-src") + .desc("Decode all sources in the apk (includes unknown dex files).") + .get(); + private static final Option decodeNoSrcOption = Option.builder("s") .longOpt("no-src") .desc("Do not decode sources.") .get(); - private static final Option decodeOnlyMainClassesOption = Option.builder() - .longOpt("only-main-classes") - .desc("Only disassemble the main dex classes (classes[0-9]*.dex) in the root.") - .get(); - private static final Option decodeNoDebugInfoOption = Option.builder() .longOpt("no-debug-info") .desc("Do not include debug info in sources (.local, .param, .line, etc.)") @@ -242,11 +242,11 @@ private static void loadOptions(Options options, boolean advanced) { decodeOptions.addOption(jobsOption); decodeOptions.addOption(libOption); if (advanced) { + decodeOptions.addOption(decodeAllSrcOption); decodeOptions.addOption(decodeKeepBrokenResOption); decodeOptions.addOption(decodeMatchOriginalOption); decodeOptions.addOption(decodeNoAssetsOption); decodeOptions.addOption(decodeNoDebugInfoOption); - decodeOptions.addOption(decodeOnlyMainClassesOption); decodeOptions.addOption(decodeOnlyManifestOption); decodeOptions.addOption(decodeResResolveModeOption); } @@ -424,14 +424,14 @@ private static void cmdDecode(String[] args) throws AndrolibException { if (cli.hasOption(decodeForceOption)) { config.setForced(true); } - if (cli.hasOption(decodeNoSrcOption)) { - config.setDecodeSources(Config.DecodeSources.NONE); + if (cli.hasOption(decodeAllSrcOption)) { + config.setDecodeSources(Config.DecodeSources.FULL); } - if (cli.hasOption(decodeOnlyMainClassesOption)) { - if (cli.hasOption(decodeNoSrcOption)) { - printOptionConflict(decodeOnlyMainClassesOption, decodeNoSrcOption); + if (cli.hasOption(decodeNoSrcOption)) { + if (cli.hasOption(decodeAllSrcOption)) { + printOptionConflict(decodeNoSrcOption, decodeAllSrcOption); } else { - config.setDecodeSources(Config.DecodeSources.ONLY_MAIN_CLASSES); + config.setDecodeSources(Config.DecodeSources.NONE); } } if (cli.hasOption(decodeNoDebugInfoOption)) { 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 a06271cb4c..e3db99998c 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 @@ -50,13 +50,14 @@ public class ApkBuilder { private final AtomicReference mFirstError; private ApkInfo mApkInfo; - private int mMinSdkVersion; + private SmaliBuilder mSmaliBuilder; + private AaptInvoker mAaptInvoker; private BackgroundWorker mWorker; public ApkBuilder(ExtFile apkDir, Config config) { mApkDir = apkDir; mConfig = config; - mFirstError = new AtomicReference<>(null); + mFirstError = new AtomicReference<>(); } public void build(File outApk) throws AndrolibException { @@ -65,11 +66,10 @@ public void build(File outApk) throws AndrolibException { } try { mApkInfo = ApkInfo.load(mApkDir); - String minSdkVersion = mApkInfo.getSdkInfo().getMinSdkVersion(); - if (minSdkVersion != null) { - mMinSdkVersion = SdkInfo.parseSdkInt(minSdkVersion); - } + mSmaliBuilder = new SmaliBuilder(minSdkVersion != null + ? SdkInfo.parseSdkInt(minSdkVersion) : 0); + mAaptInvoker = new AaptInvoker(mApkInfo, mConfig); String apkName = mApkInfo.getApkFileName(); if (apkName == null) { @@ -141,7 +141,8 @@ private void buildSources(File outDir) throws AndrolibException { if (dirName.equals("smali")) { fileName = "classes.dex"; } else if (dirName.startsWith("smali_")) { - fileName = dirName.substring(dirName.indexOf('_') + 1) + ".dex"; + fileName = dirName.substring(dirName.indexOf('_') + 1) + .replace('@', File.separatorChar) + ".dex"; } else { continue; } @@ -157,11 +158,8 @@ private void buildSources(File outDir) throws AndrolibException { private void copySourcesRaw(File outDir, String fileName) throws AndrolibException { File inFile = new File(mApkDir, fileName); - if (!inFile.isFile()) { - return; - } - File outFile = new File(outDir, fileName); + if (!mConfig.isForced() && !isFileNewer(inFile, outFile)) { LOGGER.info("File " + fileName + " has not changed."); return; @@ -193,20 +191,15 @@ private void buildSourcesSmali(File outDir, String dirName, String fileName) thr private void buildSourcesSmaliJob(File outDir, String dirName, String fileName) throws AndrolibException { File smaliDir = new File(mApkDir, dirName); - if (!smaliDir.isDirectory()) { - return; - } - File dexFile = new File(outDir, fileName); + if (!mConfig.isForced() && !isFileNewer(smaliDir, dexFile)) { LOGGER.info("Sources in " + dirName + " have not changed."); return; } - OS.rmfile(dexFile); LOGGER.info("Smaling " + dirName + " folder into " + fileName + "..."); - SmaliBuilder builder = new SmaliBuilder(smaliDir, mMinSdkVersion); - builder.build(dexFile); + mSmaliBuilder.build(smaliDir, dexFile); } private void backupManifestFile(File manifest, File manifestOrig) throws AndrolibException { @@ -219,8 +212,6 @@ private void backupManifestFile(File manifest, File manifestOrig) throws Androli return; } - OS.rmfile(manifestOrig); - try { OS.cpfile(manifest, manifestOrig); ResXmlUtils.fixingPublicAttrsInProviderAttributes(manifest); @@ -272,40 +263,38 @@ private void copyResourcesRaw(File outDir, File manifest, File arscFile) throws } private void buildResourcesFull(File outDir, File manifest, File resDir) throws AndrolibException { - File resZip = new File(outDir.getParentFile(), "resources.zip"); - if (!mConfig.isForced() && resZip.isFile() + if (!mConfig.isForced() && !isFileNewer(manifest, new File(outDir, "AndroidManifest.xml")) && !isFileNewer(resDir, new File(outDir, "res"))) { LOGGER.info("Resources have not changed."); return; } - OS.rmfile(resZip); if (mConfig.isDebuggable()) { - LOGGER.info("Setting 'debuggable' attribute to 'true' in AndroidManifest.xml"); + LOGGER.info("Setting 'debuggable' attribute to 'true' in AndroidManifest.xml..."); ResXmlUtils.setApplicationDebugTagTrue(manifest); } if (mConfig.isNetSecConf()) { - String targetSdkVersion = mApkInfo.getSdkInfo().getTargetSdkVersion(); - if (targetSdkVersion != null && SdkInfo.parseSdkInt(targetSdkVersion) < ResConfig.SDK_NOUGAT) { - LOGGER.warning("Target SDK version is lower than 24! Network Security Configuration might be ignored!"); - } - + LOGGER.info("Adding permissive network security config in manifest..."); File netSecConfOrig = new File(mApkDir, "res/xml/network_security_config.xml"); OS.mkdir(netSecConfOrig.getParentFile()); ResXmlUtils.modNetworkSecurityConfig(netSecConfOrig); ResXmlUtils.setNetworkSecurityConfig(manifest); - LOGGER.info("Added permissive network security config in manifest"); + + String targetSdkVersion = mApkInfo.getSdkInfo().getTargetSdkVersion(); + if (targetSdkVersion != null && SdkInfo.parseSdkInt(targetSdkVersion) < ResConfig.SDK_NOUGAT) { + LOGGER.warning("Target SDK version is lower than 24, Network Security Configuration might be ignored!"); + } } ExtFile tmpFile; try { tmpFile = new ExtFile(File.createTempFile("APKTOOL", null)); + OS.rmfile(tmpFile); } catch (IOException ex) { throw new AndrolibException(ex); } - OS.rmfile(tmpFile); File npDir = new File(mApkDir, "9patch"); if (!npDir.isDirectory()) { @@ -314,8 +303,7 @@ private void buildResourcesFull(File outDir, File manifest, File resDir) throws LOGGER.info("Building resources with " + AaptManager.getBinaryName() + "..."); try { - AaptInvoker invoker = new AaptInvoker(mApkInfo, mConfig); - invoker.invoke(tmpFile, manifest, resDir, npDir, null, getIncludeFiles()); + mAaptInvoker.invoke(tmpFile, manifest, resDir, npDir, null, getIncludeFiles()); Directory tmpDir = tmpFile.getDirectory(); tmpDir.copyToDir(outDir, "AndroidManifest.xml", "resources.arsc", "res"); @@ -336,10 +324,10 @@ private void buildManifest(File outDir, File manifest) throws AndrolibException ExtFile tmpFile; try { tmpFile = new ExtFile(File.createTempFile("APKTOOL", null)); + OS.rmfile(tmpFile); } catch (IOException ex) { throw new AndrolibException(ex); } - OS.rmfile(tmpFile); File npDir = new File(mApkDir, "9patch"); if (!npDir.isDirectory()) { @@ -348,8 +336,7 @@ private void buildManifest(File outDir, File manifest) throws AndrolibException LOGGER.info("Building AndroidManifest.xml with " + AaptManager.getBinaryName() + "..."); try { - AaptInvoker invoker = new AaptInvoker(mApkInfo, mConfig); - invoker.invoke(tmpFile, manifest, null, npDir, null, getIncludeFiles()); + mAaptInvoker.invoke(tmpFile, manifest, null, npDir, null, getIncludeFiles()); Directory tmpDir = tmpFile.getDirectory(); tmpDir.copyToDir(outDir, "AndroidManifest.xml"); @@ -389,7 +376,7 @@ private void copyOriginalFiles(File outDir) throws AndrolibException { Directory in = originalDir.getDirectory(); for (String fileName : in.getFiles(true)) { - if (ApkInfo.ORIGINAL_FILENAMES_PATTERN.matcher(fileName).matches()) { + if (ApkInfo.ORIGINAL_FILES_PATTERN.matcher(fileName).matches()) { in.copyToDir(outDir, fileName); } } @@ -417,7 +404,7 @@ private void buildApkFile(File outDir, File outApk) throws AndrolibException { ZipUtils.zipDir(outDir, out, doNotCompress); // Zip standard raw files. - for (String dirName : ApkInfo.RAW_DIRNAMES) { + for (String dirName : ApkInfo.RAW_DIRS) { File rawDir = new File(mApkDir, dirName); if (rawDir.isDirectory()) { LOGGER.info("Importing " + dirName + "..."); 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 e06513d358..321c86deda 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 @@ -20,7 +20,7 @@ import brut.androlib.exceptions.InFileNotFoundException; import brut.androlib.exceptions.OutDirExistsException; import brut.androlib.meta.ApkInfo; -import brut.androlib.res.ResourcesDecoder; +import brut.androlib.res.ResDecoder; import brut.androlib.smali.SmaliDecoder; import brut.directory.Directory; import brut.directory.DirectoryException; @@ -47,14 +47,14 @@ public class ApkDecoder { private final AtomicReference mFirstError; private ApkInfo mApkInfo; - private ResourcesDecoder mResDecoder; - private volatile int mMinSdkVersion; + private SmaliDecoder mSmaliDecoder; + private ResDecoder mResDecoder; private BackgroundWorker mWorker; public ApkDecoder(ExtFile apkFile, Config config) { mApkFile = apkFile; mConfig = config; - mFirstError = new AtomicReference<>(null); + mFirstError = new AtomicReference<>(); } public ApkInfo decode(File outDir) throws AndrolibException { @@ -70,7 +70,8 @@ public ApkInfo decode(File outDir) throws AndrolibException { try { mApkInfo = new ApkInfo(mApkFile); mApkInfo.setVersion(mConfig.getVersion()); - mResDecoder = new ResourcesDecoder(mApkInfo, mConfig); + mSmaliDecoder = new SmaliDecoder(mApkFile, mConfig.isBaksmaliDebugMode()); + mResDecoder = new ResDecoder(mApkInfo, mConfig); OS.rmdir(outDir); OS.mkdir(outDir); @@ -113,27 +114,25 @@ private void decodeSources(File outDir) throws AndrolibException { try { Directory in = mApkFile.getDirectory(); + boolean allSrc = mConfig.getDecodeSources() == Config.DecodeSources.FULL; + boolean noSrc = mConfig.getDecodeSources() == Config.DecodeSources.NONE; - for (String fileName : in.getFiles(true)) { - if (!fileName.endsWith(".dex")) { + for (String fileName : in.getFiles(allSrc)) { + if (allSrc ? !fileName.endsWith(".dex") + : !ApkInfo.CLASSES_FILES_PATTERN.matcher(fileName).matches()) { continue; } - switch (mConfig.getDecodeSources()) { - case NONE: - copySourcesRaw(outDir, fileName); - break; - case FULL: - decodeSourcesSmali(outDir, fileName); - break; - case ONLY_MAIN_CLASSES: - if (fileName.startsWith("classes")) { - decodeSourcesSmali(outDir, fileName); - } else { - copySourcesRaw(outDir, fileName); - } - break; + if (noSrc) { + copySourcesRaw(outDir, fileName); + continue; } + + String dirName = "smali" + (!fileName.equals("classes.dex") + ? "_" + fileName.substring(0, fileName.lastIndexOf('.')) + .replace(in.separatorChar, '@') : ""); + + decodeSourcesSmali(new File(outDir, dirName), fileName); } } catch (DirectoryException ex) { throw new AndrolibException(ex); @@ -168,20 +167,8 @@ private void decodeSourcesSmali(File outDir, String fileName) throws AndrolibExc } private void decodeSourcesSmaliJob(File outDir, String fileName) throws AndrolibException { - File smaliDir = new File(outDir, "smali" + (!fileName.equals("classes.dex") - ? "_" + fileName.substring(0, fileName.indexOf('.')) : "")); - - OS.mkdir(smaliDir); - LOGGER.info("Baksmaling " + fileName + "..."); - SmaliDecoder decoder = new SmaliDecoder(mApkFile, fileName, mConfig.isBaksmaliDebugMode()); - decoder.decode(smaliDir); - - // Record minSdkVersion if there's no AndroidManifest.xml, i.e. JARs. - int minSdkVersion = decoder.getInferredApiLevel(); - if (mMinSdkVersion == 0 || mMinSdkVersion > minSdkVersion) { - mMinSdkVersion = minSdkVersion; - } + mSmaliDecoder.decode(fileName, outDir); } private void decodeResources(File outDir) throws AndrolibException { @@ -189,14 +176,10 @@ private void decodeResources(File outDir) throws AndrolibException { return; } - switch (mConfig.getDecodeResources()) { - case NONE: - case ONLY_MANIFEST: - copyResourcesRaw(outDir); - break; - case FULL: - mResDecoder.decodeResources(outDir); - break; + if (mConfig.getDecodeResources() == Config.DecodeResources.FULL) { + mResDecoder.decodeResources(outDir); + } else { + copyResourcesRaw(outDir); } } @@ -216,14 +199,10 @@ private void decodeManifest(File outDir) throws AndrolibException { return; } - switch (mConfig.getDecodeResources()) { - case NONE: - copyManifestRaw(outDir); - break; - case FULL: - case ONLY_MANIFEST: - mResDecoder.decodeManifest(outDir); - break; + if (mConfig.getDecodeResources() != Config.DecodeResources.NONE) { + mResDecoder.decodeManifest(outDir); + } else { + copyManifestRaw(outDir); } } @@ -240,20 +219,22 @@ private void copyManifestRaw(File outDir) throws AndrolibException { private void copyRawFiles(File outDir) throws AndrolibException { try { - Map resFileMapping = mResDecoder.getResFileMapping(); Directory in = mApkFile.getDirectory(); + Set dexFiles = mSmaliDecoder.getDexFiles(); + Map resFileMap = mResDecoder.getResFileMap(); + boolean noAssets = mConfig.getDecodeAssets() == Config.DecodeAssets.NONE; - for (String dirName : ApkInfo.RAW_DIRNAMES) { - if (!in.containsDir(dirName) || (mConfig.getDecodeAssets() == Config.DecodeAssets.NONE - && dirName.equals("assets"))) { + for (String dirName : ApkInfo.RAW_DIRS) { + if (!in.containsDir(dirName) || (noAssets && dirName.equals("assets"))) { continue; } LOGGER.info("Copying " + dirName + "..."); for (String fileName : in.getDir(dirName).getFiles(true)) { - fileName = dirName + "/" + fileName; - if (!ApkInfo.ORIGINAL_FILENAMES_PATTERN.matcher(fileName).matches() - && !resFileMapping.containsKey(fileName)) { + fileName = dirName + in.separator + fileName; + if (!ApkInfo.ORIGINAL_FILES_PATTERN.matcher(fileName).matches() + && !dexFiles.contains(fileName) + && !resFileMap.containsKey(fileName)) { in.copyToDir(outDir, fileName); } } @@ -271,7 +252,7 @@ private void copyOriginalFiles(File outDir) throws AndrolibException { Directory in = mApkFile.getDirectory(); for (String fileName : in.getFiles(true)) { - if (ApkInfo.ORIGINAL_FILENAMES_PATTERN.matcher(fileName).matches()) { + if (ApkInfo.ORIGINAL_FILES_PATTERN.matcher(fileName).matches()) { in.copyToDir(originalDir, fileName); } } @@ -285,12 +266,14 @@ private void copyUnknownFiles(File outDir) throws AndrolibException { LOGGER.info("Copying unknown files..."); try { - Map resFileMapping = mResDecoder.getResFileMapping(); Directory in = mApkFile.getDirectory(); + Set dexFiles = mSmaliDecoder.getDexFiles(); + Map resFileMap = mResDecoder.getResFileMap(); for (String fileName : in.getFiles(true)) { - if (!ApkInfo.STANDARD_FILENAMES_PATTERN.matcher(fileName).matches() - && !resFileMapping.containsKey(fileName)) { + if (!ApkInfo.STANDARD_FILES_PATTERN.matcher(fileName).matches() + && !dexFiles.contains(fileName) + && !resFileMap.containsKey(fileName)) { in.copyToDir(unknownDir, fileName); } } @@ -300,17 +283,21 @@ private void copyUnknownFiles(File outDir) throws AndrolibException { } private void writeApkInfo(File outDir) throws AndrolibException { - // In case we have no resources, we store the inferred dex opcode API level. - if (!mApkInfo.hasResources() && mMinSdkVersion > 0) { - mApkInfo.getSdkInfo().setMinSdkVersion(Integer.toString(mMinSdkVersion)); + // If we did not decode the manifest, store the inferred dex opcode API level. + if (!mApkInfo.hasManifest() + || mConfig.getDecodeResources() == Config.DecodeResources.NONE) { + int apiLevel = mSmaliDecoder.getInferredApiLevel(); + if (apiLevel > 0) { + mApkInfo.getSdkInfo().setMinSdkVersion(Integer.toString(apiLevel)); + } } // Record uncompressed files. try { - Map resFileMapping = mResDecoder.getResFileMapping(); + Directory in = mApkFile.getDirectory(); + Map resFileMap = mResDecoder.getResFileMap(); Set uncompressedExts = new HashSet<>(); Set uncompressedFiles = new HashSet<>(); - Directory in = mApkFile.getDirectory(); for (String fileName : in.getFiles(true)) { if (in.getCompressionLevel(fileName) == 0) { @@ -320,7 +307,7 @@ private void writeApkInfo(File outDir) throws AndrolibException { && NO_COMPRESS_EXT_PATTERN.matcher(ext).matches()) { uncompressedExts.add(ext); } else { - uncompressedFiles.add(resFileMapping.getOrDefault(fileName, fileName)); + uncompressedFiles.add(resFileMap.getOrDefault(fileName, fileName)); } } } 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 cbd516b57e..42d731035e 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 @@ -61,7 +61,7 @@ public Config(String version) { mVerbose = false; // Decode options - mDecodeSources = DecodeSources.FULL; + mDecodeSources = DecodeSources.ONLY_MAIN_CLASSES; mBaksmaliDebugMode = true; mDecodeResources = DecodeResources.FULL; mDecodeResolve = DecodeResolve.DEFAULT; diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/meta/ApkInfo.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/meta/ApkInfo.java index fe73de2e64..8c8c411be3 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/meta/ApkInfo.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/meta/ApkInfo.java @@ -22,7 +22,6 @@ import brut.yaml.*; import java.io.File; -import java.io.FileNotFoundException; import java.io.InputStream; import java.io.IOException; import java.nio.file.Files; @@ -33,14 +32,18 @@ import java.util.regex.Pattern; public class ApkInfo implements YamlSerializable { - public static final String[] RAW_DIRNAMES = { "assets", "lib" }; + public static final String[] RAW_DIRS = { "assets", "lib" }; - public static final Pattern ORIGINAL_FILENAMES_PATTERN = Pattern.compile( + public static final Pattern CLASSES_FILES_PATTERN = Pattern.compile( + "classes([2-9]|[1-9][0-9]+)?\\.dex"); + + public static final Pattern ORIGINAL_FILES_PATTERN = Pattern.compile( "AndroidManifest\\.xml|META-INF/[^/]+\\.(RSA|SF|MF)|stamp-cert-sha256"); - public static final Pattern STANDARD_FILENAMES_PATTERN = Pattern.compile( - "[^/]+\\.dex|resources\\.arsc|(" + String.join("|", RAW_DIRNAMES) + ")/.*|" - + ORIGINAL_FILENAMES_PATTERN.pattern()); + public static final Pattern STANDARD_FILES_PATTERN = Pattern.compile( + "resources\\.arsc|(" + String.join("|", RAW_DIRS) + ")/.*|" + + CLASSES_FILES_PATTERN.pattern() + "|" + + ORIGINAL_FILES_PATTERN.pattern()); private String mVersion; private String mApkFileName; diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AaptInvoker.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AaptInvoker.java index 85892e71f5..b8d0ba179b 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AaptInvoker.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AaptInvoker.java @@ -52,43 +52,43 @@ public void invoke(File apkFile, File manifest, File resDir, File rawDir, File a } } - List cmd; - File resourcesZip = null; + List cmd = new ArrayList<>(); + File resZip = null; if (resDir != null) { - resourcesZip = Paths.get(resDir.getParent(), "build", "resources.zip").toFile(); + resZip = Paths.get(resDir.getParent(), "build", "resources.zip").toFile(); + OS.rmfile(resZip); - if (!resourcesZip.exists()) { - // Compile the files into flat arsc files. - cmd = new ArrayList<>(); - cmd.add(aaptPath); - cmd.add("compile"); + // Compile the files into flat arsc files. + cmd.add(aaptPath); + cmd.add("compile"); - cmd.add("--dir"); - cmd.add(resDir.getAbsolutePath()); + cmd.add("--dir"); + cmd.add(resDir.getAbsolutePath()); - // Treats error that used to be valid in aapt1 as warnings in aapt2. - cmd.add("--legacy"); + // Treats error that used to be valid in aapt1 as warnings in aapt2. + cmd.add("--legacy"); - cmd.add("-o"); - cmd.add(resourcesZip.getAbsolutePath()); + cmd.add("-o"); + cmd.add(resZip.getAbsolutePath()); - if (mConfig.isVerbose()) { - cmd.add("-v"); - } + if (mConfig.isVerbose()) { + cmd.add("-v"); + } - if (mConfig.isNoCrunch()) { - cmd.add("--no-crunch"); - } + if (mConfig.isNoCrunch()) { + cmd.add("--no-crunch"); + } - try { - OS.exec(cmd.toArray(new String[0])); - LOGGER.fine("aapt2 compile command ran: "); - LOGGER.fine(cmd.toString()); - } catch (BrutException ex) { - throw new AndrolibException(ex); - } + try { + OS.exec(cmd.toArray(new String[0])); + LOGGER.fine("aapt2 compile command ran: "); + LOGGER.fine(cmd.toString()); + } catch (BrutException ex) { + throw new AndrolibException(ex); } + + cmd.clear(); } if (manifest == null) { @@ -96,7 +96,6 @@ public void invoke(File apkFile, File manifest, File resDir, File rawDir, File a } // Link resources to the final apk. - cmd = new ArrayList<>(); cmd.add(aaptPath); cmd.add("link"); @@ -180,8 +179,8 @@ public void invoke(File apkFile, File manifest, File resDir, File rawDir, File a if (mConfig.isVerbose()) { cmd.add("-v"); } - if (resourcesZip != null) { - cmd.add(resourcesZip.getAbsolutePath()); + if (resZip != null) { + cmd.add(resZip.getAbsolutePath()); } try { diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResourcesDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResDecoder.java similarity index 97% rename from brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResourcesDecoder.java rename to brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResDecoder.java index 7f7de99026..cafca7b71f 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/ResDecoder.java @@ -38,27 +38,27 @@ import java.util.*; import java.util.logging.Logger; -public class ResourcesDecoder { - private static final Logger LOGGER = Logger.getLogger(ResourcesDecoder.class.getName()); +public class ResDecoder { + private static final Logger LOGGER = Logger.getLogger(ResDecoder.class.getName()); private final ApkInfo mApkInfo; private final Config mConfig; private final ResTable mTable; - private final Map mResFileMapping; + private final Map mResFileMap; - public ResourcesDecoder(ApkInfo apkInfo, Config config) { + public ResDecoder(ApkInfo apkInfo, Config config) { mApkInfo = apkInfo; mConfig = config; mTable = new ResTable(apkInfo, config); - mResFileMapping = new HashMap<>(); + mResFileMap = new HashMap<>(); } public ResTable getTable() { return mTable; } - public Map getResFileMapping() { - return mResFileMapping; + public Map getResFileMap() { + return mResFileMap; } public void decodeResources(File apkDir) throws AndrolibException { @@ -98,7 +98,7 @@ public void decodeResources(File apkDir) throws AndrolibException { LOGGER.info("Decoding file resources..."); for (ResEntry entry : new ArrayList<>(pkg.listEntries())) { if (entry.getValue() instanceof ResFileReference) { - fileDecoder.decode(entry, inDir, outDir, mResFileMapping); + fileDecoder.decode(entry, inDir, outDir, mResFileMap); } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResFileDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResFileDecoder.java index 5e88fba6e2..bf6a9cd31a 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResFileDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResFileDecoder.java @@ -42,7 +42,7 @@ public ResFileDecoder(Map decoders) { mDecoders = decoders; } - public void decode(ResEntry entry, Directory inDir, Directory outDir, Map resFileMapping) + public void decode(ResEntry entry, Directory inDir, Directory outDir, Map resFileMap) throws AndrolibException { String inFileName = ((ResFileReference) entry.getValue()).getPath(); @@ -82,7 +82,7 @@ public void decode(ResEntry entry, Directory inDir, Directory outDir, Map 0 ? Opcodes.forApi(mApiLevel) : Opcodes.getDefault()); - for (String fileName : smaliDir.getDirectory().getFiles(true)) { + for (String fileName : dir.getDirectory().getFiles(true)) { if (!fileName.endsWith(".smali")) { LOGGER.warning("Unknown file type, ignoring: " + fileName); continue; } - buildFile(fileName, dexBuilder); + buildFile(smaliDir, fileName, dexBuilder); + } + + if (dexFile.exists()) { + OS.rmfile(dexFile); + } else { + File parentDir = dexFile.getParentFile(); + if (parentDir != null) { + OS.mkdir(parentDir); + } } dexBuilder.writeTo(new FileDataStore(dexFile)); } catch (DirectoryException | IOException | RuntimeException ex) { - throw new AndrolibException("Could not smali folder: " + mSmaliDir.getName(), ex); + throw new AndrolibException("Could not smali folder: " + smaliDir.getName(), ex); } } - private void buildFile(String fileName, DexBuilder dexBuilder) throws AndrolibException { + private void buildFile(File smaliDir, String dexName, DexBuilder dexBuilder) + throws AndrolibException { boolean success; Exception cause; try { - File smaliFile = new File(mSmaliDir, fileName); - success = buildFile(smaliFile, dexBuilder, mApiLevel); + File smaliFile = new File(smaliDir, dexName); + success = buildFile(smaliFile, dexBuilder); cause = null; } catch (Exception ex) { success = false; cause = ex; } if (!success) { - AndrolibException ex = new AndrolibException("Could not smali file: " + fileName); + AndrolibException ex = new AndrolibException("Could not smali file: " + dexName); if (cause != null) { ex.initCause(cause); } @@ -93,11 +102,11 @@ private void buildFile(String fileName, DexBuilder dexBuilder) throws AndrolibEx } } - private boolean buildFile(File smaliFile, DexBuilder dexBuilder, int apiLevel) + private boolean buildFile(File smaliFile, DexBuilder dexBuilder) throws IOException, RecognitionException { try (InputStreamReader reader = new InputStreamReader( Files.newInputStream(smaliFile.toPath()), StandardCharsets.UTF_8)) { - smaliFlexLexer lexer = new smaliFlexLexer(reader, apiLevel); + smaliFlexLexer lexer = new smaliFlexLexer(reader, mApiLevel); lexer.setSourceFile(smaliFile); CommonTokenStream tokens = new CommonTokenStream(lexer); @@ -112,7 +121,7 @@ private boolean buildFile(File smaliFile, DexBuilder dexBuilder, int apiLevel) } smaliParser parser = new smaliParser(tokens); - parser.setApiLevel(apiLevel); + parser.setApiLevel(mApiLevel); parser.setVerboseErrors(VERBOSE_ERRORS); smaliParser.smali_file_return result = parser.smali_file(); @@ -126,7 +135,7 @@ private boolean buildFile(File smaliFile, DexBuilder dexBuilder, int apiLevel) treeStream.setTokenStream(tokens); smaliTreeWalker treeWalker = new smaliTreeWalker(treeStream); - treeWalker.setApiLevel(apiLevel); + treeWalker.setApiLevel(mApiLevel); treeWalker.setVerboseErrors(VERBOSE_ERRORS); treeWalker.setDexBuilder(dexBuilder); treeWalker.smali_file(); diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/smali/SmaliDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/smali/SmaliDecoder.java index cbc4fd6a3b..cbd53b1644 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/smali/SmaliDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/smali/SmaliDecoder.java @@ -17,6 +17,7 @@ package brut.androlib.smali; import brut.androlib.exceptions.AndrolibException; +import brut.util.OS; import com.android.tools.smali.baksmali.Baksmali; import com.android.tools.smali.baksmali.BaksmaliOptions; import com.android.tools.smali.dexlib2.DexFileFactory; @@ -27,25 +28,32 @@ import com.android.tools.smali.dexlib2.iface.DexFile; import com.android.tools.smali.dexlib2.iface.MultiDexContainer; -import java.io.*; +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; public class SmaliDecoder { private final File mApkFile; - private final String mDexName; - private final boolean mBakDeb; + private final boolean mDebugMode; + private final Set mDexFiles; private int mInferredApiLevel; - public SmaliDecoder(File apkFile, String dexName, boolean bakDeb) { + public SmaliDecoder(File apkFile, boolean debugMode) { mApkFile = apkFile; - mDexName = dexName; - mBakDeb = bakDeb; + mDebugMode = debugMode; + mDexFiles = new HashSet<>(); + } + + public Set getDexFiles() { + return mDexFiles; } public int getInferredApiLevel() { return mInferredApiLevel; } - public void decode(File outDir) throws AndrolibException { + public void decode(String dexName, File smaliDir) throws AndrolibException { try { BaksmaliOptions options = new BaksmaliOptions(); options.deodex = false; @@ -53,7 +61,7 @@ public void decode(File outDir) throws AndrolibException { options.parameterRegisters = true; options.localsDirective = true; options.sequentialLabels = true; - options.debugInfo = mBakDeb; + options.debugInfo = mDebugMode; options.codeOffsets = false; options.accessorComments = false; options.registerInfo = 0; @@ -68,23 +76,20 @@ public void decode(File outDir) throws AndrolibException { // Create the container. MultiDexContainer container = DexFileFactory.loadDexContainer(mApkFile, null); - MultiDexContainer.DexEntry dexEntry; - DexBackedDexFile dexFile; // If we have 1 item, ignore the passed file. Pull the DexFile we need. - if (container.getDexEntryNames().size() == 1) { - dexEntry = container.getEntry(container.getDexEntryNames().get(0)); - } else { - dexEntry = container.getEntry(mDexName); - } + MultiDexContainer.DexEntry dexEntry = + container.getDexEntryNames().size() == 1 + ? container.getEntry(container.getDexEntryNames().get(0)) + : container.getEntry(dexName); // Double-check the passed param exists. if (dexEntry == null) { dexEntry = container.getEntry(container.getDexEntryNames().get(0)); + assert dexEntry != null; } - assert dexEntry != null; - dexFile = dexEntry.getDexFile(); + DexBackedDexFile dexFile = dexEntry.getDexFile(); if (dexFile.supportsOptimizedOpcodes()) { throw new AndrolibException("Could not disassemble an odex file without deodexing it."); @@ -95,11 +100,19 @@ public void decode(File outDir) throws AndrolibException { ((DexBackedOdexFile) dexFile).getOdexVersion()); } - Baksmali.disassembleDexFile(dexFile, outDir, jobs, options); + OS.mkdir(smaliDir); + Baksmali.disassembleDexFile(dexFile, smaliDir, jobs, options); + + synchronized (mDexFiles) { + int apiLevel = dexFile.getOpcodes().api; + if (mInferredApiLevel == 0 || mInferredApiLevel > apiLevel) { + mInferredApiLevel = apiLevel; + } - mInferredApiLevel = dexFile.getOpcodes().api; + mDexFiles.add(dexName); + } } catch (IOException ex) { - throw new AndrolibException("Could not baksmali file: " + mDexName, ex); + throw new AndrolibException("Could not baksmali file: " + dexName, ex); } } } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/AndResGuardTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/AndResGuardTest.java index fda4afa985..37ba073c28 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/AndResGuardTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/AndResGuardTest.java @@ -43,7 +43,6 @@ public void checkifAndResDecodeRemapsRFolder() throws BrutException { @Test public void checkIfAndResDecodeIgnoresRFolderInRawMode() throws BrutException { - sConfig.setForced(true); sConfig.setDecodeResources(Config.DecodeResources.NONE); ExtFile testApk = new ExtFile(sTmpDir, TEST_APK); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/DecodeResourcesTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/DecodeResourcesTest.java index 88f5374f5f..fc296e7370 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/DecodeResourcesTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/DecodeResourcesTest.java @@ -45,11 +45,10 @@ public static void beforeClass() throws Exception { @Test public void decodeResourcesNoneTest() throws BrutException, IOException { - sConfig.setForced(true); sConfig.setDecodeResources(Config.DecodeResources.NONE); ExtFile testApk = new ExtFile(sTmpDir, TEST_APK); - ExtFile testDir = new ExtFile(testApk + ".out"); + ExtFile testDir = new ExtFile(testApk + ".out.none"); new ApkDecoder(testApk, sConfig).decode(testDir); // assert that manifest is not XML @@ -61,11 +60,10 @@ public void decodeResourcesNoneTest() throws BrutException, IOException { @Test public void decodeResourcesFullTest() throws BrutException, IOException { - sConfig.setForced(true); sConfig.setDecodeResources(Config.DecodeResources.FULL); ExtFile testApk = new ExtFile(sTmpDir, TEST_APK); - ExtFile testDir = new ExtFile(testApk + ".out"); + ExtFile testDir = new ExtFile(testApk + ".out.full"); new ApkDecoder(testApk, sConfig).decode(testDir); // assert that manifest is XML @@ -77,11 +75,10 @@ public void decodeResourcesFullTest() throws BrutException, IOException { @Test public void decodeResourcesOnlyManifestTest() throws BrutException, IOException { - sConfig.setForced(true); sConfig.setDecodeResources(Config.DecodeResources.ONLY_MANIFEST); ExtFile testApk = new ExtFile(sTmpDir, TEST_APK); - ExtFile testDir = new ExtFile(testApk + ".out"); + ExtFile testDir = new ExtFile(testApk + ".out.manifest"); new ApkDecoder(testApk, sConfig).decode(testDir); // assert that manifest is XML diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/DuplicateDexTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/DynamicDexTest.java similarity index 78% rename from brut.apktool/apktool-lib/src/test/java/brut/androlib/DuplicateDexTest.java rename to brut.apktool/apktool-lib/src/test/java/brut/androlib/DynamicDexTest.java index b045f4de09..6becdce4be 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/DuplicateDexTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/DynamicDexTest.java @@ -23,20 +23,22 @@ import org.junit.*; import static org.junit.Assert.*; -public class DuplicateDexTest extends BaseTest { - private static final String TEST_APK = "duplicatedex.apk"; +public class DynamicDexTest extends BaseTest { + private static final String TEST_APK = "dynamic_dex.apk"; @BeforeClass public static void beforeClass() throws Exception { LOGGER.info("Unpacking " + TEST_APK + "..."); - TestUtils.copyResourceDir(DuplicateDexTest.class, "duplicatedex", sTmpDir); + TestUtils.copyResourceDir(DynamicDexTest.class, "dynamic_dex", sTmpDir); } - @Test(expected = AndrolibException.class) - public void decodeAllSourcesShouldThrowException() throws BrutException { + @Test + public void decodeOnlyMainClassesTest() throws BrutException { + sConfig.setDecodeSources(Config.DecodeSources.ONLY_MAIN_CLASSES); + LOGGER.info("Decoding " + TEST_APK + "..."); ExtFile testApk = new ExtFile(sTmpDir, TEST_APK); - ExtFile testDir = new ExtFile(testApk + ".out"); + ExtFile testDir = new ExtFile(testApk + ".out.main"); new ApkDecoder(testApk, sConfig).decode(testDir); LOGGER.info("Building " + TEST_APK + "..."); @@ -44,17 +46,15 @@ public void decodeAllSourcesShouldThrowException() throws BrutException { } @Test - public void decodeUsingOnlyMainClassesMode() throws BrutException { - sConfig.setForced(true); - sConfig.setDecodeSources(Config.DecodeSources.ONLY_MAIN_CLASSES); + public void decodeAllSourcesTest() throws BrutException { + sConfig.setDecodeSources(Config.DecodeSources.FULL); LOGGER.info("Decoding " + TEST_APK + "..."); ExtFile testApk = new ExtFile(sTmpDir, TEST_APK); - ExtFile testDir = new ExtFile(testApk + ".out.main"); + ExtFile testDir = new ExtFile(testApk + ".out.full"); new ApkDecoder(testApk, sConfig).decode(testDir); LOGGER.info("Building " + TEST_APK + "..."); new ApkBuilder(testDir, sConfig).build(null); } - } diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/Empty9PatchTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/Empty9PatchTest.java index 046229e62b..86df0570a1 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/Empty9PatchTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/Empty9PatchTest.java @@ -25,11 +25,11 @@ import static org.junit.Assert.*; public class Empty9PatchTest extends BaseTest { - private static final String TEST_APK = "empty9patch.apk"; + private static final String TEST_APK = "empty_9patch.apk"; @BeforeClass public static void beforeClass() throws Exception { - TestUtils.copyResourceDir(Empty9PatchTest.class, "empty9patch", sTmpDir); + TestUtils.copyResourceDir(Empty9PatchTest.class, "empty_9patch", sTmpDir); } @Test diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/MinifiedArscTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/MinifiedArscTest.java index 8094ea6194..84e9bc5e31 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/MinifiedArscTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/MinifiedArscTest.java @@ -34,8 +34,6 @@ public class MinifiedArscTest extends BaseTest { public static void beforeClass() throws Exception { TestUtils.copyResourceDir(MinifiedArscTest.class, "issue1157", sTmpDir); - sConfig.setForced(true); - ExtFile testApk = new ExtFile(sTmpDir, "issue1157.apk"); sTestNewDir = new ExtFile(testApk + ".out"); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/NonStandardPkgIdTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/NonStandardPkgIdTest.java index fc5cb883bd..b9f3615958 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/NonStandardPkgIdTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/NonStandardPkgIdTest.java @@ -17,7 +17,7 @@ package brut.androlib; import brut.androlib.meta.ApkInfo; -import brut.androlib.res.ResourcesDecoder; +import brut.androlib.res.ResDecoder; import brut.androlib.res.table.ResId; import brut.androlib.res.table.ResTable; import brut.common.BrutException; @@ -46,7 +46,7 @@ public static void beforeClass() throws Exception { LOGGER.info("Decoding pkgid8.apk..."); ApkInfo testInfo = new ApkInfo(testApk); - ResourcesDecoder resDecoder = new ResourcesDecoder(testInfo, sConfig); + ResDecoder resDecoder = new ResDecoder(testInfo, sConfig); OS.mkdir(sTestNewDir); resDecoder.decodeResources(sTestNewDir); resDecoder.decodeManifest(sTestNewDir); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/ParentDirectoryTraversalTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/ParentDirectoryTraversalTest.java index b3df066798..c1fa39d7a2 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/ParentDirectoryTraversalTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/ParentDirectoryTraversalTest.java @@ -32,7 +32,6 @@ public static void beforeClass() throws Exception { @Test public void checkIfDrawableFileDecodesProperly() throws BrutException { - sConfig.setForced(true); sConfig.setDecodeResources(Config.DecodeResources.NONE); ExtFile testApk = new ExtFile(sTmpDir, apk); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/ResourceDirectoryTraversalTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/ResourceDirectoryTraversalTest.java index 5c6407a77a..7fdf655171 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/ResourceDirectoryTraversalTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/ResourceDirectoryTraversalTest.java @@ -30,13 +30,11 @@ public class ResourceDirectoryTraversalTest extends BaseTest { @BeforeClass public static void beforeClass() throws Exception { - TestUtils.copyResourceDir(ResourceDirectoryTraversalTest.class, "arbitrary-write", sTmpDir); + TestUtils.copyResourceDir(ResourceDirectoryTraversalTest.class, "arbitrary_write", sTmpDir); } @Test public void checkIfMaliciousRawFileRenamed() throws BrutException { - sConfig.setForced(true); - ExtFile testApk = new ExtFile(sTmpDir, TEST_APK); ExtFile testDir = new ExtFile(testApk + ".out"); new ApkDecoder(testApk, sConfig).decode(testDir); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/SkipAssetTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/SkipAssetTest.java index 3bed460215..a3a1a13457 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/SkipAssetTest.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/SkipAssetTest.java @@ -34,11 +34,10 @@ public static void beforeClass() throws Exception { @Test public void checkIfEnablingSkipAssetWorks() throws BrutException { - sConfig.setForced(true); sConfig.setDecodeAssets(Config.DecodeAssets.NONE); ExtFile testApk = new ExtFile(sTmpDir, TEST_APK); - ExtFile testDir = new ExtFile(testApk + ".out"); + ExtFile testDir = new ExtFile(testApk + ".out.none"); new ApkDecoder(testApk, sConfig).decode(testDir); assertFalse(new File(testDir, "assets/kotlin.kotlin_builtins").isFile()); @@ -47,11 +46,10 @@ public void checkIfEnablingSkipAssetWorks() throws BrutException { @Test public void checkControl() throws BrutException { - sConfig.setForced(true); sConfig.setDecodeAssets(Config.DecodeAssets.FULL); ExtFile testApk = new ExtFile(sTmpDir, TEST_APK); - ExtFile testDir = new ExtFile(testApk + ".out"); + ExtFile testDir = new ExtFile(testApk + ".out.full"); new ApkDecoder(testApk, sConfig).decode(testDir); assertTrue(new File(testDir, "assets/kotlin.kotlin_builtins").isFile()); diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/TestUtils.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/TestUtils.java index 3bf58edfd5..4c11822cdc 100644 --- a/brut.apktool/apktool-lib/src/test/java/brut/androlib/TestUtils.java +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/TestUtils.java @@ -57,7 +57,7 @@ public static void copyResourceDir(Class clz, String dirPath, Directory out) } if (dirURL == null) { - String className = clz.getName().replace(".", "/") + ".class"; + String className = clz.getName().replace('.', '/') + ".class"; dirURL = clz.getClassLoader().getResource(className); } @@ -85,6 +85,6 @@ public static byte[] readHeaderOfFile(File file, int size) throws IOException { } public static String replaceNewlines(String value) { - return value.replace("\n", "").replace("\r", ""); + return value.replaceAll("[\n\r]", ""); } } diff --git a/brut.apktool/apktool-lib/src/test/resources/arbitrary-write/GHSA-2hqv-2xv4-5h5w.apk b/brut.apktool/apktool-lib/src/test/resources/arbitrary_write/GHSA-2hqv-2xv4-5h5w.apk similarity index 100% rename from brut.apktool/apktool-lib/src/test/resources/arbitrary-write/GHSA-2hqv-2xv4-5h5w.apk rename to brut.apktool/apktool-lib/src/test/resources/arbitrary_write/GHSA-2hqv-2xv4-5h5w.apk diff --git a/brut.apktool/apktool-lib/src/test/resources/duplicatedex/duplicatedex.apk b/brut.apktool/apktool-lib/src/test/resources/duplicatedex/duplicatedex.apk deleted file mode 100644 index 6e6144bf2d..0000000000 Binary files a/brut.apktool/apktool-lib/src/test/resources/duplicatedex/duplicatedex.apk and /dev/null differ diff --git a/brut.apktool/apktool-lib/src/test/resources/dynamic_dex/dynamic_dex.apk b/brut.apktool/apktool-lib/src/test/resources/dynamic_dex/dynamic_dex.apk new file mode 100644 index 0000000000..eb2345dea6 Binary files /dev/null and b/brut.apktool/apktool-lib/src/test/resources/dynamic_dex/dynamic_dex.apk differ diff --git a/brut.apktool/apktool-lib/src/test/resources/empty9patch/empty9patch.apk b/brut.apktool/apktool-lib/src/test/resources/empty_9patch/empty_9patch.apk similarity index 100% rename from brut.apktool/apktool-lib/src/test/resources/empty9patch/empty9patch.apk rename to brut.apktool/apktool-lib/src/test/resources/empty_9patch/empty_9patch.apk 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 fa7abc0964..ccfb0f34fa 100644 --- a/brut.j.dir/src/main/java/brut/directory/AbstractDirectory.java +++ b/brut.j.dir/src/main/java/brut/directory/AbstractDirectory.java @@ -310,7 +310,7 @@ private SubPath getSubPath(String path) throws PathNotExist { } private ParsedPath parsePath(String path) { - int pos = path.indexOf(separator); + int pos = path.indexOf(separatorChar); if (pos == -1) { return new ParsedPath(null, path); } diff --git a/brut.j.dir/src/main/java/brut/directory/Directory.java b/brut.j.dir/src/main/java/brut/directory/Directory.java index 299519d069..0a8c692b90 100644 --- a/brut.j.dir/src/main/java/brut/directory/Directory.java +++ b/brut.j.dir/src/main/java/brut/directory/Directory.java @@ -24,7 +24,8 @@ import java.util.Set; public interface Directory { - char separator = '/'; + String separator = "/"; + char separatorChar = '/'; Set getFiles(); 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 013f59719d..b4560f1d1f 100644 --- a/brut.j.dir/src/main/java/brut/directory/ZipRODirectory.java +++ b/brut.j.dir/src/main/java/brut/directory/ZipRODirectory.java @@ -141,7 +141,7 @@ private void loadAll() { String subname = name.substring(prefixLen); - int pos = subname.indexOf(separator); + int pos = subname.indexOf(separatorChar); if (pos == -1) { if (!entry.isDirectory()) { mFiles.add(subname);