From 306959a6fbcfbf383adfe2c4fe7a87c78f545d33 Mon Sep 17 00:00:00 2001 From: Comnir Date: Wed, 20 May 2020 13:20:41 +0300 Subject: [PATCH] fix the usage of debug flag (-d) when using aapt2 - when using aapt and debug flag, we remove the 'debuggable' attribute from AndroidManifest and aapt adds it back. (see issue 1621) - when using aapt2 and debug flag, we add and set the value of 'debuggable' attribute ourselves. --- .../src/main/java/brut/androlib/Androlib.java | 7 +- .../brut/androlib/res/xml/ResXmlPatcher.java | 36 ++++++- .../DebuggableFalseChangeToTrueTest.java | 93 +++++++++++++++++++ .../aapt2/DebuggableTrueAddedTest.java | 93 +++++++++++++++++++ .../aapt2/DebuggableTrueRetainedTest.java | 93 +++++++++++++++++++ .../debuggable-false/AndroidManifest.xml | 4 + .../issue2328/debuggable-false/apktool.yml | 12 +++ .../debuggable-false/res/values/strings.xml | 4 + .../debuggable-missing/AndroidManifest.xml | 4 + .../issue2328/debuggable-missing/apktool.yml | 12 +++ .../debuggable-missing/res/values/strings.xml | 4 + .../debuggable-true/AndroidManifest.xml | 4 + .../issue2328/debuggable-true/apktool.yml | 12 +++ .../debuggable-true/res/values/strings.xml | 4 + 14 files changed, 377 insertions(+), 5 deletions(-) create mode 100644 brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/DebuggableFalseChangeToTrueTest.java create mode 100644 brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/DebuggableTrueAddedTest.java create mode 100644 brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/DebuggableTrueRetainedTest.java create mode 100644 brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-false/AndroidManifest.xml create mode 100644 brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-false/apktool.yml create mode 100644 brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-false/res/values/strings.xml create mode 100644 brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-missing/AndroidManifest.xml create mode 100644 brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-missing/apktool.yml create mode 100644 brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-missing/res/values/strings.xml create mode 100644 brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-true/AndroidManifest.xml create mode 100644 brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-true/apktool.yml create mode 100644 brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-true/res/values/strings.xml 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 0985df8c..df3ec28c 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 @@ -468,7 +468,12 @@ public class Androlib { LOGGER.info("Building resources..."); if (apkOptions.debugMode) { - ResXmlPatcher.removeApplicationDebugTag(new File(appDir, "AndroidManifest.xml")); + if (apkOptions.isAapt2()) { + LOGGER.info("Using aapt2 - setting 'debuggable' attribute to 'true' in AndroidManifest.xml"); + ResXmlPatcher.setApplicationDebugTagTrue(new File(appDir, "AndroidManifest.xml")); + } else { + ResXmlPatcher.removeApplicationDebugTag(new File(appDir, "AndroidManifest.xml")); + } } File apkFile = File.createTempFile("APKTOOL", 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 9361052a..c980c0fd 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 @@ -35,10 +35,7 @@ import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; -import org.w3c.dom.Document; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; +import org.w3c.dom.*; import org.xml.sax.SAXException; import brut.androlib.AndrolibException; @@ -76,6 +73,37 @@ public final class ResXmlPatcher { } } + /** + * Sets "debug" tag in the file to true + * + * @param file AndroidManifest file + * @throws AndrolibException + */ + public static void setApplicationDebugTagTrue(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"); + + 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) { + } + } + } + /** * 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 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 new file mode 100644 index 00000000..139b126e --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/DebuggableFalseChangeToTrueTest.java @@ -0,0 +1,93 @@ +/** + * Copyright (C) 2019 Ryszard Wiśniewski + * Copyright (C) 2019 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.aapt2; + +import brut.androlib.*; +import brut.common.BrutException; +import brut.directory.ExtFile; +import brut.util.OS; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import org.custommonkey.xmlunit.XMLUnit; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.xml.sax.SAXException; + +import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; +import static org.junit.Assert.assertTrue; + +/** + * @author Connor Tumbleson + */ +public class DebuggableFalseChangeToTrueTest extends BaseTest { + + @BeforeClass + public static void beforeClass() throws Exception { + TestUtils.cleanFrameworkFile(); + sTmpDir = new ExtFile(OS.createTempDirectory()); + sTestOrigDir = new ExtFile(sTmpDir, "issue2328-debuggable-false-orig"); + sTestNewDir = new ExtFile(sTmpDir, "issue2328-debuggable-flase-new"); + LOGGER.info("Unpacking issue2328-debuggable-flase..."); + TestUtils.copyResourceDir(DebuggableFalseChangeToTrueTest.class, "aapt2/issue2328/debuggable-false", sTestOrigDir); + + LOGGER.info("Building issue2328-debuggable-flase.apk..."); + ApkOptions apkOptions = new ApkOptions(); + apkOptions.debugMode = true; + apkOptions.useAapt2 = true; + apkOptions.verbose = true; + + File testApk = new File(sTmpDir, "issue2328-debuggable-flase.apk"); + new Androlib(apkOptions).build(sTestOrigDir, testApk); + + LOGGER.info("Decoding issue2328-debuggable-flase.apk..."); + ApkDecoder apkDecoder = new ApkDecoder(testApk); + apkDecoder.setOutDir(sTestNewDir); + apkDecoder.decode(); + } + + @AfterClass + public static void afterClass() throws BrutException { + OS.rmdir(sTmpDir); + } + + @Test + public void buildAndDecodeTest() { + assertTrue(sTestNewDir.isDirectory()); + } + + @Test + public void DebugIsTruePriorToBeingFalseTest() throws IOException, SAXException { + String apk = "issue2328-debuggable-flase-new"; + + String expected = TestUtils.replaceNewlines("" + + " "); + + byte[] encoded = Files.readAllBytes(Paths.get(sTmpDir + File.separator + apk + File.separator + "AndroidManifest.xml")); + String obtained = TestUtils.replaceNewlines(new String(encoded)); + + XMLUnit.setIgnoreWhitespace(true); + XMLUnit.setIgnoreAttributeOrder(true); + XMLUnit.setCompareUnmatched(false); + + assertXMLEqual(expected, obtained); + } +} \ No newline at end of file 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 new file mode 100644 index 00000000..533fe343 --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/DebuggableTrueAddedTest.java @@ -0,0 +1,93 @@ +/** + * Copyright (C) 2019 Ryszard Wiśniewski + * Copyright (C) 2019 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.aapt2; + +import brut.androlib.*; +import brut.common.BrutException; +import brut.directory.ExtFile; +import brut.util.OS; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import org.custommonkey.xmlunit.XMLUnit; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.xml.sax.SAXException; + +import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; +import static org.junit.Assert.assertTrue; + +/** + * @author Connor Tumbleson + */ +public class DebuggableTrueAddedTest extends BaseTest { + + @BeforeClass + public static void beforeClass() throws Exception { + TestUtils.cleanFrameworkFile(); + sTmpDir = new ExtFile(OS.createTempDirectory()); + sTestOrigDir = new ExtFile(sTmpDir, "issue2328-debuggable-missing-orig"); + sTestNewDir = new ExtFile(sTmpDir, "issue2328-debuggable-missing-new"); + LOGGER.info("Unpacking issue2328-debuggable-missing..."); + TestUtils.copyResourceDir(DebuggableTrueAddedTest.class, "aapt2/issue2328/debuggable-missing", sTestOrigDir); + + LOGGER.info("Building issue2328-debuggable-missing.apk..."); + ApkOptions apkOptions = new ApkOptions(); + apkOptions.debugMode = true; + apkOptions.useAapt2 = true; + apkOptions.verbose = true; + + File testApk = new File(sTmpDir, "issue2328-debuggable-missing.apk"); + new Androlib(apkOptions).build(sTestOrigDir, testApk); + + LOGGER.info("Decoding issue2328-debuggable-missing.apk..."); + ApkDecoder apkDecoder = new ApkDecoder(testApk); + apkDecoder.setOutDir(sTestNewDir); + apkDecoder.decode(); + } + + @AfterClass + public static void afterClass() throws BrutException { + OS.rmdir(sTmpDir); + } + + @Test + public void buildAndDecodeTest() { + assertTrue(sTestNewDir.isDirectory()); + } + + @Test + public void DebugIsTruePriorToBeingFalseTest() throws IOException, SAXException { + String apk = "issue2328-debuggable-missing-new"; + + String expected = TestUtils.replaceNewlines("" + + " "); + + byte[] encoded = Files.readAllBytes(Paths.get(sTmpDir + File.separator + apk + File.separator + "AndroidManifest.xml")); + String obtained = TestUtils.replaceNewlines(new String(encoded)); + + XMLUnit.setIgnoreWhitespace(true); + XMLUnit.setIgnoreAttributeOrder(true); + XMLUnit.setCompareUnmatched(false); + + assertXMLEqual(expected, obtained); + } +} \ No newline at end of file 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 new file mode 100644 index 00000000..1dd6e9a5 --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/java/brut/androlib/aapt2/DebuggableTrueRetainedTest.java @@ -0,0 +1,93 @@ +/** + * Copyright (C) 2019 Ryszard Wiśniewski + * Copyright (C) 2019 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.aapt2; + +import brut.androlib.*; +import brut.common.BrutException; +import brut.directory.ExtFile; +import brut.util.OS; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import org.custommonkey.xmlunit.XMLUnit; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.xml.sax.SAXException; + +import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; +import static org.junit.Assert.assertTrue; + +/** + * @author Connor Tumbleson + */ +public class DebuggableTrueRetainedTest extends BaseTest { + + @BeforeClass + public static void beforeClass() throws Exception { + TestUtils.cleanFrameworkFile(); + sTmpDir = new ExtFile(OS.createTempDirectory()); + sTestOrigDir = new ExtFile(sTmpDir, "issue2328-debuggable-true-orig"); + sTestNewDir = new ExtFile(sTmpDir, "issue2328-debuggable-true-new"); + LOGGER.info("Unpacking issue2328-debuggable-true..."); + TestUtils.copyResourceDir(DebuggableTrueRetainedTest.class, "aapt2/issue2328/debuggable-true", sTestOrigDir); + + LOGGER.info("Building issue2328-debuggable-true.apk..."); + ApkOptions apkOptions = new ApkOptions(); + apkOptions.debugMode = true; + apkOptions.useAapt2 = true; + apkOptions.verbose = true; + + File testApk = new File(sTmpDir, "issue2328-debuggable-true.apk"); + new Androlib(apkOptions).build(sTestOrigDir, testApk); + + LOGGER.info("Decoding issue2328-debuggable-true.apk..."); + ApkDecoder apkDecoder = new ApkDecoder(testApk); + apkDecoder.setOutDir(sTestNewDir); + apkDecoder.decode(); + } + + @AfterClass + public static void afterClass() throws BrutException { + OS.rmdir(sTmpDir); + } + + @Test + public void buildAndDecodeTest() { + assertTrue(sTestNewDir.isDirectory()); + } + + @Test + public void DebugIsTruePriorToBeingFalseTest() throws IOException, SAXException { + String apk = "issue2328-debuggable-true-new"; + + String expected = TestUtils.replaceNewlines("" + + " "); + + byte[] encoded = Files.readAllBytes(Paths.get(sTmpDir + File.separator + apk + File.separator + "AndroidManifest.xml")); + String obtained = TestUtils.replaceNewlines(new String(encoded)); + + XMLUnit.setIgnoreWhitespace(true); + XMLUnit.setIgnoreAttributeOrder(true); + XMLUnit.setCompareUnmatched(false); + + assertXMLEqual(expected, obtained); + } +} \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-false/AndroidManifest.xml b/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-false/AndroidManifest.xml new file mode 100644 index 00000000..d7449da0 --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-false/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + 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 new file mode 100644 index 00000000..9daf7e74 --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-false/apktool.yml @@ -0,0 +1,12 @@ +version: 2.0.0 +apkFileName: issue1235.apk +isFrameworkApk: false +usesFramework: + ids: + - 1 +packageInfo: + forcedPackageId: '127' +versionInfo: + versionCode: '1' + versionName: '1.0' +compressionType: false \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-false/res/values/strings.xml b/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-false/res/values/strings.xml new file mode 100644 index 00000000..9999efc0 --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-false/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Hello World + diff --git a/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-missing/AndroidManifest.xml b/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-missing/AndroidManifest.xml new file mode 100644 index 00000000..9869c90d --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-missing/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + 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 new file mode 100644 index 00000000..9daf7e74 --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-missing/apktool.yml @@ -0,0 +1,12 @@ +version: 2.0.0 +apkFileName: issue1235.apk +isFrameworkApk: false +usesFramework: + ids: + - 1 +packageInfo: + forcedPackageId: '127' +versionInfo: + versionCode: '1' + versionName: '1.0' +compressionType: false \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-missing/res/values/strings.xml b/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-missing/res/values/strings.xml new file mode 100644 index 00000000..9999efc0 --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-missing/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Hello World + diff --git a/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-true/AndroidManifest.xml b/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-true/AndroidManifest.xml new file mode 100644 index 00000000..7faa4d8a --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-true/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + 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 new file mode 100644 index 00000000..9daf7e74 --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-true/apktool.yml @@ -0,0 +1,12 @@ +version: 2.0.0 +apkFileName: issue1235.apk +isFrameworkApk: false +usesFramework: + ids: + - 1 +packageInfo: + forcedPackageId: '127' +versionInfo: + versionCode: '1' + versionName: '1.0' +compressionType: false \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-true/res/values/strings.xml b/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-true/res/values/strings.xml new file mode 100644 index 00000000..9999efc0 --- /dev/null +++ b/brut.apktool/apktool-lib/src/test/resources/aapt2/issue2328/debuggable-true/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Hello World +