Support malformed resource names/namespaces by falling back to resId (#2886)

* Correct falsely-referenced attribute into an entity which is an obfuscated name

* style: correct syntax for custom attributes

* fix: remove unused import

Co-authored-by: MyAnoneNeko <MyAnoneNeko@users.noreply.github.com>
This commit is contained in:
Connor Tumbleson 2022-09-19 16:27:43 -04:00 committed by GitHub
parent 5d6c6c04d2
commit 3fff2f128e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 69 additions and 28 deletions

View File

@ -336,16 +336,26 @@ public class AXmlResourceParser implements XmlResourceParser {
}
String value = m_strings.getString(name);
String namespace = getAttributeNamespace(index);
// some attributes will return "", we must rely on the resource_id and refer to the frameworks
// to match the resource id to the name. ex: 0x101021C = versionName
if (value.length() == 0 || android_ns.equals(getAttributeNamespace(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 {
int resourceId = getAttributeNameResource(index);
if (resourceId != 0) {
value = mAttrDecoder.decodeManifestAttr(getAttributeNameResource(index));
value = mAttrDecoder.decodeManifestAttr(getAttributeNameResource(index));
if (value == null) {
value = "";
}
} catch (AndrolibException | NullPointerException ignored) {}
} 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) {}
}
return value;
}
@ -381,11 +391,27 @@ public class AXmlResourceParser implements XmlResourceParser {
if (mAttrDecoder != null) {
try {
String value = valueRaw == -1 ? null : ResXmlEncoders.escapeXmlChars(m_strings.getString(valueRaw));
String obfuscatedValue = mAttrDecoder.decodeManifestAttr(valueData);
if (! (value == null || obfuscatedValue == null)) {
int slashPos = value.lastIndexOf("/");
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;
}
}
return mAttrDecoder.decode(
valueType,
valueData,
valueRaw == -1 ? null : ResXmlEncoders.escapeXmlChars(m_strings.getString(valueRaw)),
getAttributeNameResource(index));
valueType,
valueData,
value,
getAttributeNameResource(index)
);
} catch (AndrolibException ex) {
setFirstError(ex);
LOGGER.log(Level.WARNING, String.format("Could not decode attr value, using undecoded value "
@ -807,9 +833,9 @@ public class AXmlResourceParser implements XmlResourceParser {
if (m_strings == null) {
m_reader.skipCheckInt(CHUNK_AXML_FILE, CHUNK_AXML_FILE_BROKEN);
/*
* chunkSize
*/
/*
* chunkSize
*/
m_reader.skipInt();
m_strings = StringBlock.read(m_reader);
m_namespaces.increaseDepth();
@ -863,9 +889,9 @@ public class AXmlResourceParser implements XmlResourceParser {
}
// Common header.
/* chunkSize */m_reader.skipInt();
/* chunkSize */m_reader.skipInt();
int lineNumber = m_reader.readInt();
/* 0xFFFFFFFF */m_reader.skipInt();
/* 0xFFFFFFFF */m_reader.skipInt();
if (chunkType == CHUNK_XML_START_NAMESPACE || chunkType == CHUNK_XML_END_NAMESPACE) {
if (chunkType == CHUNK_XML_START_NAMESPACE) {
@ -873,8 +899,8 @@ public class AXmlResourceParser implements XmlResourceParser {
int uri = m_reader.readInt();
m_namespaces.push(prefix, uri);
} else {
/* prefix */m_reader.skipInt();
/* uri */m_reader.skipInt();
/* prefix */m_reader.skipInt();
/* uri */m_reader.skipInt();
m_namespaces.pop();
}
continue;
@ -885,7 +911,7 @@ public class AXmlResourceParser implements XmlResourceParser {
if (chunkType == CHUNK_XML_START_TAG) {
m_namespaceUri = m_reader.readInt();
m_name = m_reader.readInt();
/* flags? */m_reader.skipInt();
/* flags? */m_reader.skipInt();
int attributeCount = m_reader.readInt();
m_idAttribute = (attributeCount >>> 16) - 1;
attributeCount &= 0xFFFF;
@ -912,8 +938,8 @@ public class AXmlResourceParser implements XmlResourceParser {
if (chunkType == CHUNK_XML_TEXT) {
m_name = m_reader.readInt();
/* ? */m_reader.skipInt();
/* ? */m_reader.skipInt();
/* ? */m_reader.skipInt();
/* ? */m_reader.skipInt();
m_event = TEXT;
break;
}
@ -927,10 +953,10 @@ public class AXmlResourceParser implements XmlResourceParser {
}
// ///////////////////////////////// data
/*
* All values are essentially indices, e.g. m_name is an index of name in
* m_strings.
*/
/*
* All values are essentially indices, e.g. m_name is an index of name in
* m_strings.
*/
private ExtDataInput m_reader;
private ResAttrDecoder mAttrDecoder;
private AndrolibException mFirstError;

View File

@ -18,6 +18,7 @@ package brut.androlib.res.decoder;
import brut.androlib.AndrolibException;
import brut.androlib.err.UndefinedResObjectException;
import brut.androlib.res.data.ResID;
import brut.androlib.res.data.ResPackage;
import brut.androlib.res.data.ResResSpec;
import brut.androlib.res.data.value.ResAttr;
@ -48,10 +49,24 @@ public class ResAttrDecoder {
throws AndrolibException {
if (attrResId != 0) {
ResResSpec resResSpec = getCurrentPackage().getResTable().getResSpec(attrResId);
int attrId = attrResId;
if (resResSpec != null) {
return resResSpec.getName();
// 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;
}
// 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);
if (resResSpec != null) {
return resResSpec.getName();
}
}
}