From 8d59882e5f23408a380c76253e532f5e169dc837 Mon Sep 17 00:00:00 2001 From: Cody Lund Date: Tue, 17 Aug 2021 05:01:08 -0600 Subject: [PATCH] Fix: numeric string meta-data value corruption (#2612) * Fix: handle numeric strings in manifest meta-data value * fix regex * scoped solution * improve comment Co-authored-by: Cody Lund --- .../brut/androlib/res/AndrolibResources.java | 12 ++-- .../AndroidManifestResourceParser.java | 58 +++++++++++++++++++ .../aapt1/testapp/AndroidManifest.xml | 4 ++ .../aapt2/testapp/AndroidManifest.xml | 4 ++ 4 files changed, 73 insertions(+), 5 deletions(-) create mode 100644 brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AndroidManifestResourceParser.java 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 58ffd0d4..82b32878 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 @@ -138,7 +138,7 @@ final public class AndrolibResources { public void decodeManifest(ResTable resTable, ExtFile apkFile, File outDir) throws AndrolibException { - Duo duo = getManifestFileDecoder(); + Duo duo = getManifestFileDecoder(false); ResFileDecoder fileDecoder = duo.m1; // Set ResAttrDecoder @@ -185,7 +185,7 @@ final public class AndrolibResources { public void decodeManifestWithResources(ResTable resTable, ExtFile apkFile, File outDir) throws AndrolibException { - Duo duo = getResFileDecoder(); + Duo duo = getManifestFileDecoder(true); ResFileDecoder fileDecoder = duo.m1; ResAttrDecoder attrDecoder = duo.m2.getAttrDecoder(); @@ -708,11 +708,13 @@ final public class AndrolibResources { return new Duo(new ResFileDecoder(decoders), axmlParser); } - public Duo getManifestFileDecoder() { + public Duo getManifestFileDecoder(boolean withResources) { ResStreamDecoderContainer decoders = new ResStreamDecoderContainer(); - AXmlResourceParser axmlParser = new AXmlResourceParser(); - + AXmlResourceParser axmlParser = new AndroidManifestResourceParser(); + if (withResources) { + axmlParser.setAttrDecoder(new ResAttrDecoder()); + } decoders.setDecoder("xml", new XmlPullStreamDecoder(axmlParser,getResXmlSerializer())); return new Duo(new ResFileDecoder(decoders), axmlParser); diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AndroidManifestResourceParser.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AndroidManifestResourceParser.java new file mode 100644 index 00000000..7501b39c --- /dev/null +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AndroidManifestResourceParser.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2010 Ryszard Wiśniewski + * Copyright (C) 2010 Connor Tumbleson + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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.decoder; + +import android.util.TypedValue; + +import java.util.regex.Pattern; + +/** + * AXmlResourceParser specifically for parsing encoded AndroidManifest.xml. + */ +public class AndroidManifestResourceParser extends AXmlResourceParser { + + /** + * Pattern for matching numeric string meta-data values. aapt automatically infers the + * type for a manifest meta-data value based on the string in the unencoded XML. However, + * some apps intentionally coerce integers to be strings by prepending an escaped space. + * For details/discussion, see https://stackoverflow.com/questions/2154945/how-to-force-a-meta-data-value-to-type-string + * With aapt1, the escaped space is dropped when encoded. For aapt2, the escaped space is preserved. + */ + private static final Pattern PATTERN_NUMERIC_STRING = Pattern.compile("\\s?\\d+"); + + @Override + public String getAttributeValue(int index) { + String value = super.getAttributeValue(index); + + if (!isNumericStringMetadataAttributeValue(index, value)) { + return value; + } + + // Patch the numeric string value by prefixing it with an escaped space. + // Otherwise, when the decoded app is rebuilt, aapt will incorrectly encode + // the value as an int or float (depending on aapt version), breaking the original + // app functionality. + return "\\ " + super.getAttributeValue(index).trim(); + } + + private boolean isNumericStringMetadataAttributeValue(int index, String value) { + return "meta-data".equalsIgnoreCase(super.getName()) + && "value".equalsIgnoreCase(super.getAttributeName(index)) + && super.getAttributeValueType(index) == TypedValue.TYPE_STRING + && PATTERN_NUMERIC_STRING.matcher(value).matches(); + } +} \ No newline at end of file diff --git a/brut.apktool/apktool-lib/src/test/resources/aapt1/testapp/AndroidManifest.xml b/brut.apktool/apktool-lib/src/test/resources/aapt1/testapp/AndroidManifest.xml index 5e76c23f..c3c8fac2 100644 --- a/brut.apktool/apktool-lib/src/test/resources/aapt1/testapp/AndroidManifest.xml +++ b/brut.apktool/apktool-lib/src/test/resources/aapt1/testapp/AndroidManifest.xml @@ -2,4 +2,8 @@ + + + + diff --git a/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/AndroidManifest.xml b/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/AndroidManifest.xml index 508d0453..40895a7d 100644 --- a/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/AndroidManifest.xml +++ b/brut.apktool/apktool-lib/src/test/resources/aapt2/testapp/AndroidManifest.xml @@ -1,3 +1,7 @@ + + + +