diff --git a/jadx-core/src/main/java/jadx/core/xmlgen/ParserConstants.java b/jadx-core/src/main/java/jadx/core/xmlgen/ParserConstants.java index bef8dc58e00..1cb307e0f82 100644 --- a/jadx-core/src/main/java/jadx/core/xmlgen/ParserConstants.java +++ b/jadx-core/src/main/java/jadx/core/xmlgen/ParserConstants.java @@ -145,8 +145,20 @@ protected ParserConstants() { protected static final int SORTED_FLAG = 1; protected static final int UTF8_FLAG = 1 << 8; + /** + * ResTable_type + */ protected static final int NO_ENTRY = 0xFFFFFFFF; + // If set, the entry is sparse, and encodes both the entry ID and offset into each entry, + // and a binary search is used to find the key. Only available on platforms >= O. + // Mark any types that use this with a v26 qualifier to prevent runtime issues on older + // platforms. + protected static final int FLAG_SPARSE = 0x01; + // If set, the offsets to the entries are encoded in 16-bit, real_offset = offset * 4u + // An 16-bit offset of 0xffffu means a NO_ENTRY + protected static final int FLAG_OFFSET16 = 0x02; + /** * ResTable_entry */ @@ -158,6 +170,9 @@ protected ParserConstants() { // If set, this is a weak resource and may be overridden by strong resources of the same name/type. // This is only useful during linking with other resource tables. protected static final int FLAG_WEAK = 0x0004; + // If set, this is a compact entry with data type and value directly + // encoded in the entry, see ResTable_entry::compact + protected static final int FLAG_COMPACT = 0x0008; /** * ResTable_map diff --git a/jadx-core/src/main/java/jadx/core/xmlgen/ResTableBinaryParser.java b/jadx-core/src/main/java/jadx/core/xmlgen/ResTableBinaryParser.java index 04c0b3eca34..f06e4208cf6 100644 --- a/jadx-core/src/main/java/jadx/core/xmlgen/ResTableBinaryParser.java +++ b/jadx-core/src/main/java/jadx/core/xmlgen/ResTableBinaryParser.java @@ -274,8 +274,14 @@ private void parseTypeChunk(long start, PackageChunk pkg) throws IOException { // The type identifier this chunk is holding. Type IDs start at 1 (corresponding // to the value of the type bits in a resource identifier). 0 is invalid. int id = is.readInt8(); - int flags = is.readInt8(); // 0 or 1 - boolean flagSparse = flags == 1; + + int flags = is.readInt8(); + boolean isSparse = (flags & FLAG_SPARSE) != 0; + boolean isOffset16 = (flags & FLAG_OFFSET16) != 0; + + if (isOffset16) { + throw new JadxRuntimeException("16-bit entry offsets are not supported yet"); + } is.checkInt16(0, "type chunk, reserved"); int entryCount = is.readInt32(); @@ -289,7 +295,7 @@ private void parseTypeChunk(long start, PackageChunk pkg) throws IOException { } Map entryOffsetMap = new LinkedHashMap<>(entryCount); - if (flagSparse) { + if (isSparse) { for (int i = 0; i < entryCount; i++) { int idx = is.readInt16(); int offset = is.readInt16() * 4; // The offset in ResTable_sparseTypeEntry::offset is stored divided by 4. @@ -357,7 +363,15 @@ private void parseStagedAliasChunk(long chunkStart) throws IOException { private void parseEntry(PackageChunk pkg, int typeId, int entryId, String config) throws IOException { int size = is.readInt16(); + int flags = is.readInt16(); + boolean isComplex = (flags & FLAG_COMPLEX) != 0; + boolean isCompact = (flags & FLAG_COMPACT) != 0; + + if (isCompact) { + throw new JadxRuntimeException("Compact resource entries are not supported yet"); + } + int key = is.readInt32(); if (key == -1) { return; @@ -368,7 +382,7 @@ private void parseEntry(PackageChunk pkg, int typeId, int entryId, String config String origKeyName = pkg.getKeyStrings().get(key); ResourceEntry newResEntry = buildResourceEntry(pkg, config, resRef, typeName, origKeyName); - if ((flags & FLAG_COMPLEX) != 0 || size == 16) { + if (isComplex || size == 16) { int parentRef = is.readInt32(); int count = is.readInt32(); newResEntry.setParentRef(parentRef);