From addbf8336da7d3e1e1285ceffb416cf64b302060 Mon Sep 17 00:00:00 2001 From: Connor Tumbleson Date: Thu, 16 Apr 2015 07:24:27 -0500 Subject: [PATCH 1/4] [WIP] Wires up rewriter of @string references in provider attrs - finds all in manifest - finds corresponding @string in res/values/strings.xml - does reference replacement w/ literal value - fixes #636 --- .../src/main/java/brut/androlib/Androlib.java | 2 + .../brut/androlib/res/AndrolibResources.java | 60 +++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java index 8b4a9b4b..91111665 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java @@ -280,7 +280,9 @@ public class Androlib { new File(appDir, APK_DIRNAME).mkdirs(); buildSources(appDir); buildNonDefaultSources(appDir); + mAndRes.fixing_public_attrs_in_providers(new File(appDir, "AndroidManifest.xml")); buildResources(appDir, (Map) meta.get("usesFramework")); + buildLib(appDir); buildCopyOriginalFiles(appDir); buildApk(appDir, outFile); diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java index 0bfa0efe..21eea782 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java @@ -41,12 +41,14 @@ import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.*; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; +import javax.xml.xpath.*; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.apache.commons.io.IOUtils; +import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import org.xmlpull.v1.XmlSerializer; @@ -227,6 +229,64 @@ final public class AndrolibResources { } } + public void fixing_public_attrs_in_providers(File file) throws AndrolibException { + if (file.exists()) { + try { + Document doc = loadDocument(file.getAbsolutePath()); + 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(); + + if (attrs != null) { + Node provider = attrs.getNamedItem("android:authorities"); + + if (provider != null) { + String reference = provider.getNodeValue(); + String replacement = pull_value_from_strings(file.getParentFile(), reference); + + if (replacement != null) { + provider.setNodeValue(replacement); + saveDocument(file.getAbsolutePath(), doc); + } + } + } + } + + } catch (SAXException | ParserConfigurationException | IOException | + XPathExpressionException | TransformerException ignored) { + } + } + } + + public String pull_value_from_strings(File directory, String key) throws AndrolibException { + File file = new File(directory, "/res/values/strings.xml"); + key = key.replace("@string/", ""); + + if (file.exists()) { + try { + Document doc = loadDocument(file.getAbsolutePath()); + XPath xPath = XPathFactory.newInstance().newXPath(); + XPathExpression expression = xPath.compile("/resources/string[@name=" + '"' + key + "\"]/text()"); + + Object result = expression.evaluate(doc, XPathConstants.STRING); + + if (result != null) { + return (String) result; + } + + } catch (SAXException | ParserConfigurationException | IOException | XPathExpressionException ignored) { + } + } + + return null; + } + public void remove_manifest_versions(String filePath) throws AndrolibException { From 3208624bf683a982ea8d23aa7d05a249e14c8c3c Mon Sep 17 00:00:00 2001 From: Connor Tumbleson Date: Thu, 16 Apr 2015 08:02:24 -0500 Subject: [PATCH 2/4] Moves all XML parsing related functions to new class: ResXmlPatcher --- .../src/main/java/brut/androlib/Androlib.java | 5 +- .../brut/androlib/res/AndrolibResources.java | 197 ++------------- .../brut/androlib/res/xml/ResXmlPatcher.java | 238 ++++++++++++++++++ 3 files changed, 263 insertions(+), 177 deletions(-) create mode 100644 brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlPatcher.java diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java index 91111665..88f8f64a 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java @@ -22,6 +22,7 @@ import brut.androlib.res.data.ResPackage; import brut.androlib.res.data.ResTable; import brut.androlib.res.data.ResUnknownFiles; import brut.androlib.res.util.ExtFile; +import brut.androlib.res.xml.ResXmlPatcher; import brut.androlib.src.SmaliBuilder; import brut.androlib.src.SmaliDecoder; import brut.common.BrutException; @@ -280,7 +281,7 @@ public class Androlib { new File(appDir, APK_DIRNAME).mkdirs(); buildSources(appDir); buildNonDefaultSources(appDir); - mAndRes.fixing_public_attrs_in_providers(new File(appDir, "AndroidManifest.xml")); + ResXmlPatcher.fixingPublicAttrsInProviderAttributes(new File(appDir, "AndroidManifest.xml")); buildResources(appDir, (Map) meta.get("usesFramework")); buildLib(appDir); @@ -483,7 +484,7 @@ public class Androlib { File apkDir = new File(appDir, APK_DIRNAME); if (apkOptions.debugMode) { - mAndRes.remove_application_debug(new File(apkDir, "AndroidManifest.xml").getAbsolutePath()); + ResXmlPatcher.removeApplicationDebugTag(new File(apkDir,"AndroidManifest.xml")); } if (apkOptions.forceBuildAll || isModified(newFiles(APK_MANIFEST_FILENAMES, appDir), diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java index 21eea782..478632ea 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/AndrolibResources.java @@ -25,6 +25,7 @@ import brut.androlib.res.decoder.ARSCDecoder.ARSCData; import brut.androlib.res.decoder.ARSCDecoder.FlagsOffset; import brut.androlib.res.util.*; import brut.androlib.res.xml.ResValuesXmlSerializable; +import brut.androlib.res.xml.ResXmlPatcher; import brut.common.BrutException; import brut.directory.*; import brut.util.*; @@ -35,21 +36,8 @@ import java.util.zip.*; import java.io.File; import java.io.IOException; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.*; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import javax.xml.xpath.*; - -import org.w3c.dom.Document; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; import org.apache.commons.io.IOUtils; -import org.w3c.dom.NodeList; -import org.xml.sax.SAXException; import org.xmlpull.v1.XmlSerializer; /** @@ -169,173 +157,30 @@ final public class AndrolibResources { } } - public void remove_application_debug(String filePath) - throws AndrolibException { - - // change application:debug to true - try { - Document doc = loadDocument(filePath); - Node application = doc.getElementById("application"); - - // load attr - NamedNodeMap attr = application.getAttributes(); - Node debugAttr = attr.getNamedItem("debug"); - - // remove application:debug - if (debugAttr != null) { - attr.removeNamedItem("debug"); - } - - saveDocument(filePath, doc); - - } catch (SAXException | ParserConfigurationException | IOException | TransformerException ignored) { - } - } - - public void adjust_package_manifest(ResTable resTable, String filePath) + public void adjustPackageManifest(ResTable resTable, String filePath) throws AndrolibException { // compare resources.arsc package name to the one present in AndroidManifest ResPackage resPackage = resTable.getCurrentResPackage(); - mPackageOriginal = resPackage.getName(); + String packageOriginal = resPackage.getName(); mPackageRenamed = resTable.getPackageRenamed(); resTable.setPackageId(resPackage.getId()); - resTable.setPackageOriginal(mPackageOriginal); + resTable.setPackageOriginal(packageOriginal); - // 1) Check if mPackageOriginal === mPackageRenamed - // 2) Check if mPackageOriginal is ignored via IGNORED_PACKAGES + // 1) Check if packageOriginal === mPackageRenamed + // 2) Check if packageOriginal is ignored via IGNORED_PACKAGES // 2a) If its ignored, make sure the mPackageRenamed isn't explicitly allowed - if (mPackageOriginal.equalsIgnoreCase(mPackageRenamed) || - (Arrays.asList(IGNORED_PACKAGES).contains(mPackageOriginal) && + if (packageOriginal.equalsIgnoreCase(mPackageRenamed) || + (Arrays.asList(IGNORED_PACKAGES).contains(packageOriginal) && ! Arrays.asList(ALLOWED_PACKAGES).contains(mPackageRenamed))) { LOGGER.info("Regular manifest package..."); } else { - try { - LOGGER.info("Renamed manifest package found! Replacing " + mPackageRenamed + " with " + mPackageOriginal); - Document doc = loadDocument(filePath); - - // Get the manifest line - Node manifest = doc.getFirstChild(); - - // update package attribute - NamedNodeMap attr = manifest.getAttributes(); - Node nodeAttr = attr.getNamedItem("package"); - nodeAttr.setNodeValue(mPackageOriginal); - saveDocument(filePath, doc); - - } catch (SAXException | ParserConfigurationException | IOException | TransformerException ignored) { - } + LOGGER.info("Renamed manifest package found! Replacing " + mPackageRenamed + " with " + packageOriginal); + ResXmlPatcher.renameManifestPackage(new File(filePath), packageOriginal); } } - public void fixing_public_attrs_in_providers(File file) throws AndrolibException { - if (file.exists()) { - try { - Document doc = loadDocument(file.getAbsolutePath()); - 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(); - - if (attrs != null) { - Node provider = attrs.getNamedItem("android:authorities"); - - if (provider != null) { - String reference = provider.getNodeValue(); - String replacement = pull_value_from_strings(file.getParentFile(), reference); - - if (replacement != null) { - provider.setNodeValue(replacement); - saveDocument(file.getAbsolutePath(), doc); - } - } - } - } - - } catch (SAXException | ParserConfigurationException | IOException | - XPathExpressionException | TransformerException ignored) { - } - } - } - - public String pull_value_from_strings(File directory, String key) throws AndrolibException { - File file = new File(directory, "/res/values/strings.xml"); - key = key.replace("@string/", ""); - - if (file.exists()) { - try { - Document doc = loadDocument(file.getAbsolutePath()); - XPath xPath = XPathFactory.newInstance().newXPath(); - XPathExpression expression = xPath.compile("/resources/string[@name=" + '"' + key + "\"]/text()"); - - Object result = expression.evaluate(doc, XPathConstants.STRING); - - if (result != null) { - return (String) result; - } - - } catch (SAXException | ParserConfigurationException | IOException | XPathExpressionException ignored) { - } - } - - return null; - } - - public void remove_manifest_versions(String filePath) - throws AndrolibException { - - File f = new File(filePath); - - if (f.exists()) { - try { - Document doc = loadDocument(filePath); - Node manifest = doc.getFirstChild(); - - // load attr - NamedNodeMap attr = manifest.getAttributes(); - Node vCode = attr.getNamedItem("android:versionCode"); - Node vName = attr.getNamedItem("android:versionName"); - - // remove versionCode - if (vCode != null) { - attr.removeNamedItem("android:versionCode"); - } - if (vName != null) { - attr.removeNamedItem("android:versionName"); - } - saveDocument(filePath, doc); - - } catch (SAXException | ParserConfigurationException | IOException | TransformerException ignored) { - } - } - } - - private Document loadDocument(String filename) - throws IOException, SAXException, ParserConfigurationException { - - DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); - DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); - return docBuilder.parse(filename); - } - - private void saveDocument(String filename, Document doc) - throws IOException, SAXException, ParserConfigurationException, TransformerException { - - TransformerFactory transformerFactory = TransformerFactory.newInstance(); - Transformer transformer = transformerFactory.newTransformer(); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - transformer.setOutputProperty(OutputKeys.STANDALONE,"yes"); - DOMSource source = new DOMSource(doc); - StreamResult result = new StreamResult(new File(filename)); - transformer.transform(source, result); - } - public void decodeManifestWithResources(ResTable resTable, ExtFile apkFile, File outDir) throws AndrolibException { @@ -360,8 +205,11 @@ final public class AndrolibResources { // also remove the android::versionCode / versionName from manifest for rebuild // this is a required change to prevent aapt warning about conflicting versions // it will be passed as a parameter to aapt like "--min-sdk-version" via apktool.yml - adjust_package_manifest(resTable, outDir.getAbsolutePath() + File.separator + "AndroidManifest.xml"); - remove_manifest_versions(outDir.getAbsolutePath() + File.separator + "AndroidManifest.xml"); + adjustPackageManifest(resTable, outDir.getAbsolutePath() + File.separator + "AndroidManifest.xml"); + + ResXmlPatcher.removeManifestVersions(new File( + outDir.getAbsolutePath() + File.separator + "AndroidManifest.xml")); + mPackageId = String.valueOf(resTable.getPackageId()); } } catch (DirectoryException ex) { @@ -856,13 +704,15 @@ final public class AndrolibResources { * @throws AndrolibException */ public File getAaptBinaryFile() throws AndrolibException { + File aaptBinary; + try { if (OSDetection.isMacOSX()) { - mAaptBinary = Jar.getResourceAsFile("/prebuilt/aapt/macosx/aapt"); + aaptBinary = Jar.getResourceAsFile("/prebuilt/aapt/macosx/aapt"); } else if (OSDetection.isUnix()) { - mAaptBinary = Jar.getResourceAsFile("/prebuilt/aapt/linux/aapt"); + aaptBinary = Jar.getResourceAsFile("/prebuilt/aapt/linux/aapt"); } else if (OSDetection.isWindows()) { - mAaptBinary = Jar.getResourceAsFile("/prebuilt/aapt/windows/aapt.exe"); + aaptBinary = Jar.getResourceAsFile("/prebuilt/aapt/windows/aapt.exe"); } else { LOGGER.warning("Unknown Operating System: " + OSDetection.returnOS()); return null; @@ -870,8 +720,8 @@ final public class AndrolibResources { } catch (BrutException ex) { throw new AndrolibException(ex); } - if (mAaptBinary.setExecutable(true)) { - return mAaptBinary; + if (aaptBinary.setExecutable(true)) { + return aaptBinary; } System.err.println("Can't set aapt binary as executable"); @@ -901,13 +751,10 @@ final public class AndrolibResources { private String mVersionCode = null; private String mVersionName = null; private String mPackageRenamed = null; - private String mPackageOriginal = null; private String mPackageId = null; private boolean mSharedLibrary = false; - private File mAaptBinary = null; - private final static String[] IGNORED_PACKAGES = new String[] { "android", "com.htc", "miui", "com.lge", "com.lge.internal", "yi" }; 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 new file mode 100644 index 00000000..ef71e962 --- /dev/null +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlPatcher.java @@ -0,0 +1,238 @@ +/** + * Copyright 2014 Ryszard Wiśniewski + * Copyright 2015 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package brut.androlib.res.xml; + +import brut.androlib.AndrolibException; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.xpath.*; +import java.io.File; +import java.io.IOException; + +/** + * @author Connor Tumbleson + */ +public final class ResXmlPatcher { + + /** + * Removes "debug" tag from file + * + * @param file AndroidManifest file + * @throws AndrolibException + */ + public static void removeApplicationDebugTag(File file) throws AndrolibException { + if (file.exists()) { + try { + Document doc = loadDocument(file); + Node application = doc.getElementById("application"); + + // load attr + NamedNodeMap attr = application.getAttributes(); + Node debugAttr = attr.getNamedItem("debug"); + + // remove application:debug + if (debugAttr != null) { + attr.removeNamedItem("debug"); + } + + saveDocument(file, doc); + + } catch (SAXException | ParserConfigurationException | IOException | TransformerException ignored) { + } + } + } + + /** + * Any @string reference in a value in AndroidManifest.xml will break on + * build, thus preventing the application from installing. This is from a bug/error + * in AOSP where public resources cannot be part of an authorities attribute within + * a tag. + * + * This finds any reference and replaces it with the literal value found in the + * res/values/strings.xml file. + * + * @param file File for AndroidManifest.xml + * @throws AndrolibException + */ + public static void fixingPublicAttrsInProviderAttributes(File file) throws AndrolibException { + 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(); + + if (attrs != null) { + Node provider = attrs.getNamedItem("android:authorities"); + + if (provider != null) { + String reference = provider.getNodeValue(); + String replacement = pullValueFromStrings(file.getParentFile(), reference); + + if (replacement != null) { + provider.setNodeValue(replacement); + saveDocument(file, doc); + } + } + } + } + + } catch (SAXException | ParserConfigurationException | IOException | + XPathExpressionException | TransformerException ignored) { + } + } + } + + /** + * Finds key in strings.xml file and returns text value + * + * @param directory Root directory of apk + * @param key String reference (ie @string/foo) + * @return String|null + * @throws AndrolibException + */ + public static String pullValueFromStrings(File directory, String key) throws AndrolibException { + 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 (result != null) { + return (String) result; + } + + } catch (SAXException | ParserConfigurationException | IOException | XPathExpressionException ignored) { + } + } + + return null; + } + + /** + * Removes attributes like "versionCode" and "versionName" from file. + * + * @param file File representing AndroidManifest.xml + * @throws AndrolibException + */ + public static void removeManifestVersions(File file) throws AndrolibException { + 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); + + } catch (SAXException | ParserConfigurationException | IOException | TransformerException ignored) { + } + } + } + + /** + * Replaces package value with passed packageOriginal string + * + * @param file File for AndroidManifest.xml + * @param packageOriginal Package name to replace + * @throws AndrolibException + */ + public static void renameManifestPackage(File file, String packageOriginal) throws AndrolibException { + try { + Document doc = loadDocument(file); + + // Get the manifest line + Node manifest = doc.getFirstChild(); + + // update package attribute + NamedNodeMap attr = manifest.getAttributes(); + Node nodeAttr = attr.getNamedItem("package"); + nodeAttr.setNodeValue(packageOriginal); + saveDocument(file, doc); + + } catch (SAXException | ParserConfigurationException | IOException | TransformerException ignored) { + } + } + + /** + * + * @param file File to load into Document + * @return Document + * @throws IOException + * @throws SAXException + * @throws ParserConfigurationException + */ + private static Document loadDocument(File file) + throws IOException, SAXException, ParserConfigurationException { + + DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); + return docBuilder.parse(file); + } + + /** + * + * @param file File to save Document to (ie AndroidManifest.xml) + * @param doc Document being saved + * @throws IOException + * @throws SAXException + * @throws ParserConfigurationException + * @throws TransformerException + */ + private static void saveDocument(File file, Document doc) + throws IOException, SAXException, ParserConfigurationException, TransformerException { + + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty(OutputKeys.STANDALONE,"yes"); + DOMSource source = new DOMSource(doc); + StreamResult result = new StreamResult(file); + transformer.transform(source, result); + } +} From 5d405fa842e22e7516df493c7091392ed94fe374 Mon Sep 17 00:00:00 2001 From: Connor Tumbleson Date: Sat, 18 Apr 2015 08:44:25 -0500 Subject: [PATCH 3/4] add a unit-test for provider attr changing --- .../brut/androlib/ProviderAttributeTest.java | 91 ++++++++++++++++++ .../brut/apktool/issue636/issue636.apk | Bin 0 -> 23374 bytes 2 files changed, 91 insertions(+) create mode 100644 brut.apktool/apktool-lib/src/test/java/brut/androlib/ProviderAttributeTest.java create mode 100644 brut.apktool/apktool-lib/src/test/resources/brut/apktool/issue636/issue636.apk diff --git a/brut.apktool/apktool-lib/src/test/java/brut/androlib/ProviderAttributeTest.java b/brut.apktool/apktool-lib/src/test/java/brut/androlib/ProviderAttributeTest.java new file mode 100644 index 00000000..ad1c1624 --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/ProviderAttributeTest.java @@ -0,0 +1,91 @@ +/** + * Copyright 2014 Ryszard Wiśniewski + * Copyright 2014 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package brut.androlib; + +import brut.androlib.res.util.ExtFile; +import brut.common.BrutException; +import brut.directory.DirectoryException; +import brut.util.OS; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class ProviderAttributeTest { + + @BeforeClass + public static void beforeClass() throws BrutException { + sTmpDir = new ExtFile(OS.createTempDirectory()); + TestUtils.copyResourceDir(ProviderAttributeTest.class, "brut/apktool/issue636/", sTmpDir); + } + + @AfterClass + public static void afterClass() throws BrutException { + OS.rmdir(sTmpDir); + } + + @Test + public void isProviderStringReplacementWorking() throws BrutException, IOException { + String apk = "issue636.apk"; + + // decode issue636.apk + ApkDecoder apkDecoder = new ApkDecoder(new File(sTmpDir + File.separator + apk)); + apkDecoder.setOutDir(new File(sTmpDir + File.separator + apk + ".out")); + apkDecoder.decode(); + + // build issue636 + ExtFile testApk = new ExtFile(sTmpDir, apk + ".out"); + new Androlib().build(testApk, 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.setOutDir(new File(sTmpDir + File.separator + apk + ".out.two")); + apkDecoder.decode(); + + String expected = replaceNewlines("\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""); + + + byte[] encoded = Files.readAllBytes(Paths.get(sTmpDir + File.separator + apk + ".out.two" + File.separator + "AndroidManifest.xml")); + String obtained = replaceNewlines(new String(encoded)); + assertEquals(expected, obtained); + } + + private boolean fileExists(String filepath) { + return Files.exists(Paths.get(sTmpDir.getAbsolutePath() + File.separator + filepath)); + } + + private String replaceNewlines(String value) { + return value.replace("\n", "").replace("\r", ""); + } + + private static ExtFile sTmpDir; +} diff --git a/brut.apktool/apktool-lib/src/test/resources/brut/apktool/issue636/issue636.apk b/brut.apktool/apktool-lib/src/test/resources/brut/apktool/issue636/issue636.apk new file mode 100644 index 0000000000000000000000000000000000000000..1a1f76efe4ee176ef1cbf0b7a5f83ce239a10240 GIT binary patch literal 23374 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 Date: Sun, 19 Apr 2015 07:44:57 -0500 Subject: [PATCH 4/4] If passed string isn't a reference, don't look for it. --- .../src/main/java/brut/androlib/res/xml/ResXmlPatcher.java | 4 ++++ 1 file changed, 4 insertions(+) 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 ef71e962..b2d4efbd 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 @@ -125,6 +125,10 @@ public final class ResXmlPatcher { * @throws AndrolibException */ public static String pullValueFromStrings(File directory, String key) throws AndrolibException { + if (! key.contains("@")) { + return null; + } + File file = new File(directory, "/res/values/strings.xml"); key = key.replace("@string/", "");