diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResourcesDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResourcesDecoder.java index f065257ee0..6e28df64c2 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResourcesDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/ResourcesDecoder.java @@ -136,9 +136,7 @@ private void decodeManifest(ResTable resTable, ExtFile apkFile, File outDir) // Set ResAttrDecoder duo.m2.setAttrDecoder(new ResAttrDecoder()); ResAttrDecoder attrDecoder = duo.m2.getAttrDecoder(); - - // Fake ResPackage - attrDecoder.setCurrentPackage(new ResPackage(resTable, 0, null)); + attrDecoder.setResTable(resTable); Directory inApk, out; try { @@ -159,8 +157,7 @@ private void decodeManifestWithResources(ResTable resTable, ExtFile apkFile, Fil Duo duo = getManifestFileDecoder(true); ResFileDecoder fileDecoder = duo.m1; ResAttrDecoder attrDecoder = duo.m2.getAttrDecoder(); - - attrDecoder.setCurrentPackage(resTable.listMainPackages().iterator().next()); + attrDecoder.setResTable(resTable); Directory inApk, out; try { @@ -259,8 +256,7 @@ private void decodeResources(ResTable resTable, ExtFile apkFile, File outDir) Duo duo = getResFileDecoder(); ResFileDecoder fileDecoder = duo.m1; ResAttrDecoder attrDecoder = duo.m2.getAttrDecoder(); - - attrDecoder.setCurrentPackage(resTable.listMainPackages().iterator().next()); + attrDecoder.setResTable(resTable); Directory in, out; try { @@ -273,7 +269,6 @@ private void decodeResources(ResTable resTable, ExtFile apkFile, File outDir) ExtMXSerializer xmlSerializer = getResXmlSerializer(); for (ResPackage pkg : resTable.listMainPackages()) { - attrDecoder.setCurrentPackage(pkg); LOGGER.info("Decoding file-resources..."); for (ResResource res : pkg.listFiles()) { diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResID.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResID.java index abc3203996..6b118fece0 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResID.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResID.java @@ -17,22 +17,21 @@ package brut.androlib.res.data; public class ResID { - public final int package_; + public final int pkgId; public final int type; public final int entry; - public final int id; - public ResID(int package_, int type, int entry) { - this(package_, type, entry, (package_ << 24) + (type << 16) + entry); + public ResID(int pkgId, int type, int entry) { + this(pkgId, type, entry, (pkgId << 24) + (type << 16) + entry); } public ResID(int id) { this((id >> 24) & 0xff, (id >> 16) & 0x000000ff, id & 0x0000ffff, id); } - public ResID(int package_, int type, int entry, int id) { - this.package_ = (package_ == 0) ? 2 : package_; + public ResID(int pkgId, int type, int entry, int id) { + this.pkgId = (pkgId == 0) ? 2 : pkgId; this.type = type; this.entry = entry; this.id = id; diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java index 7ed16ffd1c..0c83b282f4 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/data/ResTable.java @@ -82,7 +82,7 @@ public ResResSpec getResSpec(int resID) throws AndrolibException { } public ResResSpec getResSpec(ResID resID) throws AndrolibException { - return getPackage(resID.package_).getResSpec(resID); + return getPackage(resID.pkgId).getResSpec(resID); } public Set listMainPackages() { diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java index 075a6c090f..ca314fa862 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/AXmlResourceParser.java @@ -282,8 +282,8 @@ public String getAttributeNamespace(int index) { String value = m_strings.getString(namespace); if (value == null || value.length() == 0) { - ResID resourceId = new ResID(getAttributeNameResource(index)); - if (resourceId.package_ == PRIVATE_PKG_ID) { + ResID resId = new ResID(getAttributeNameResource(index)); + if (resId.pkgId == PRIVATE_PKG_ID) { value = getNonDefaultNamespaceUri(offset); } else { value = android_ns; @@ -327,29 +327,32 @@ public String getAttributeName(int index) { return ""; } - String value = m_strings.getString(name); - String namespace = getAttributeNamespace(index); + String resourceMapValue; + String stringBlockValue = m_strings.getString(name); + int resourceId = getAttributeNameResource(index); - // If attribute name is lacking or a private namespace emerges, - // retrieve the exact attribute name by its id. - if (value == null || value.length() == 0) { - try { - value = mAttrDecoder.decodeManifestAttr(getAttributeNameResource(index)); - if (value == null) { - value = ""; - } - } catch (AndrolibException e) { - value = ""; - } - } else if (! namespace.equals(android_ns)) { - try { - String obfuscatedName = mAttrDecoder.decodeManifestAttr(getAttributeNameResource(index)); - if (! (obfuscatedName == null || obfuscatedName.equals(value))) { - value = obfuscatedName; - } - } catch (AndrolibException ignored) {} + try { + resourceMapValue = mAttrDecoder.decodeFromResourceId(resourceId); + } catch (AndrolibException ignored) { + resourceMapValue = null; } - return value; + + // Android prefers the resource map value over what the String block has. + // This can be seen quite often in obfuscated apps where values such as: + // + // Are improperly decoded when trusting the String block. + // Leveraging the resource map allows us to get the proper value. + // + if (resourceMapValue != null) { + return resourceMapValue; + } + + if (stringBlockValue != null) { + return stringBlockValue; + } + + // In this case we have a bogus resource. If it was not found in either. + return "APKTOOL_MISSING_" + Integer.toHexString(resourceId); } @Override @@ -383,18 +386,22 @@ public String getAttributeValue(int index) { if (mAttrDecoder != null) { try { - String value = valueRaw == -1 ? null : ResXmlEncoders.escapeXmlChars(m_strings.getString(valueRaw)); - String obfuscatedValue = mAttrDecoder.decodeManifestAttr(valueData); + String stringBlockValue = valueRaw == -1 ? null : ResXmlEncoders.escapeXmlChars(m_strings.getString(valueRaw)); + String resourceMapValue = mAttrDecoder.decodeFromResourceId(valueData); + String value = stringBlockValue; - if (! (value == null || obfuscatedValue == null)) { - int slashPos = value.lastIndexOf("/"); + if (stringBlockValue != null && resourceMapValue != null) { + int slashPos = stringBlockValue.lastIndexOf("/"); + int colonPos = stringBlockValue.lastIndexOf(":"); + // Handle a value with a format of "@yyy/xxx", but avoid "@yyy/zzz:xxx" if (slashPos != -1) { - // Handle a value with a format of "@yyy/xxx" - String dir = value.substring(0, slashPos); - value = dir + "/"+ obfuscatedValue; - } else if (! value.equals(obfuscatedValue)) { - value = obfuscatedValue; + if (colonPos == -1) { + String type = stringBlockValue.substring(0, slashPos); + value = type + "/" + resourceMapValue; + } + } else if (! stringBlockValue.equals(resourceMapValue)) { + value = resourceMapValue; } } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResAttrDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResAttrDecoder.java index 2ce60dfea6..b30c347e01 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResAttrDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/ResAttrDecoder.java @@ -17,72 +17,58 @@ package brut.androlib.res.decoder; import brut.androlib.exceptions.AndrolibException; +import brut.androlib.exceptions.CantFindFrameworkResException; import brut.androlib.exceptions.UndefinedResObjectException; import brut.androlib.res.data.ResID; -import brut.androlib.res.data.ResPackage; import brut.androlib.res.data.ResResSpec; +import brut.androlib.res.data.ResTable; import brut.androlib.res.data.value.ResAttr; import brut.androlib.res.data.value.ResScalarValue; public class ResAttrDecoder { public String decode(int type, int value, String rawValue, int attrResId) - throws AndrolibException { - ResScalarValue resValue = mCurrentPackage.getValueFactory().factory( - type, value, rawValue); + throws AndrolibException { + ResScalarValue resValue = mResTable.getCurrentResPackage().getValueFactory().factory(type, value, rawValue); String decoded = null; if (attrResId > 0) { try { - ResAttr attr = (ResAttr) getCurrentPackage().getResTable() - .getResSpec(attrResId).getDefaultResource().getValue(); + ResAttr attr = (ResAttr) mResTable.getResSpec(attrResId).getDefaultResource().getValue(); decoded = attr.convertToResXmlFormat(resValue); - } catch (UndefinedResObjectException | ClassCastException ex) { - // ignored - } + } catch (UndefinedResObjectException | ClassCastException ignored) {} } return decoded != null ? decoded : resValue.encodeAsResXmlAttr(); } - public String decodeManifestAttr(int attrResId) + public String decodeFromResourceId(int attrResId) throws AndrolibException { if (attrResId != 0) { - int attrId = attrResId; - - // See also: brut.androlib.res.data.ResTable.getResSpec - if (attrId >> 24 == 0) { - ResPackage pkg = getCurrentPackage(); - int packageId = pkg.getId(); - int pkgId = (packageId == 0 ? 2 : packageId); - attrId = (0xFF000000 & (pkgId << 24)) | attrId; - } + ResID resId = new ResID(attrResId); - // Retrieve the ResSpec in a package by id - ResID resId = new ResID(attrId); - ResPackage pkg = getCurrentPackage(); - if (pkg.hasResSpec(resId)) { - ResResSpec resResSpec = pkg.getResSpec(resId); + try { + ResResSpec resResSpec = mResTable.getResSpec(resId); if (resResSpec != null) { return resResSpec.getName(); } - } + } catch (UndefinedResObjectException | CantFindFrameworkResException ignored) {} } return null; } - public ResPackage getCurrentPackage() throws AndrolibException { - if (mCurrentPackage == null) { - throw new AndrolibException("Current package not set"); + public ResTable getResTable() throws AndrolibException { + if (mResTable == null) { + throw new AndrolibException("Res Table not set"); } - return mCurrentPackage; + return mResTable; } - public void setCurrentPackage(ResPackage currentPackage) { - mCurrentPackage = currentPackage; + public void setResTable(ResTable resTable) { + mResTable = resTable; } - private ResPackage mCurrentPackage; + private ResTable mResTable; } diff --git a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/XmlPullStreamDecoder.java b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/XmlPullStreamDecoder.java index 8bff042f5b..b8bd8256cb 100644 --- a/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/XmlPullStreamDecoder.java +++ b/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/decoder/XmlPullStreamDecoder.java @@ -45,7 +45,7 @@ public void decode(InputStream in, OutputStream out) try { XmlPullWrapperFactory factory = XmlPullWrapperFactory.newInstance(); XmlPullParserWrapper par = factory.newPullParserWrapper(mParser); - final ResTable resTable = ((AXmlResourceParser) mParser).getAttrDecoder().getCurrentPackage().getResTable(); + final ResTable resTable = ((AXmlResourceParser) mParser).getAttrDecoder().getResTable(); XmlSerializerWrapper ser = new StaticXmlSerializerWrapper(mSerial, factory) { boolean hideSdkInfo = false;