Skip to content

resources.arsc parser does not support sparse ResTable_type entries #9

@phplego

Description

@phplego

Summary

ResourceTable.GetResourceEntry fails on APKs whose resources.arsc contains sparse ResTable_type chunks (FLAG_SPARSE = 0x01). The parser currently treats the entry index table as a dense uint32[] for every type chunk, so sparse entries are interpreted as huge invalid offsets.

Observed behavior

For one APK in a private test corpus, resolving resource id 0x7f110001 fails with:

Invalid entry 0x0001 offset: 262238!

This happens through calls like:

rt, err := apkparser.ParseResourceTable(reader)
entry, err := rt.GetResourceEntry(0x7f110001)

I cannot attach the APK, but the failing resource table chunk has this shape:

resource id: 0x7f110001
package id: 0x7f
type chunk id: 0x11
entry id: 0x0001
ResTable_type flags: 0x01
entryCount: 2
entriesStart: 92
headerSize: 84

The sparse index table bytes decode as pairs:

(idx=1, offset=0) -> actual entry offset = entriesStart + 0*4 = 92
(idx=2, offset=4) -> actual entry offset = entriesStart + 4*4 = 108

But the current code reads the second pair as a dense uint32 offset for entry 1:

bytes: 02 00 04 00
uint32 little-endian: 0x00040002 = 262146
computed offset: entriesStart + 262146 = 262238

That produces the invalid offset error above.

Likely cause

In parseType, the second byte after Id is read as Res0 and discarded:

vals := struct {
    Id   uint8
    Res0 uint8
    Res1 uint16

    EntryCount   uint32
    EntriesStart uint32
}{...}

For ResTable_type, this byte is the type chunk flags. When flags & 0x01 != 0, the entry index table is sparse and should be read as sorted (uint16 idx, uint16 offsetDiv4) pairs instead of dense uint32 offsets.

getEntryConfigs currently always does dense lookup:

r.Seek(int64(thisType.indexesStart+entry*4), io.SeekStart)
var thisOffset uint32
binary.Read(r, binary.LittleEndian, &thisOffset)
offset := thisType.entriesStart + thisOffset

Expected behavior

For sparse type chunks, getEntryConfigs should search the sparse index table for the requested entry id and calculate:

offset = entriesStart + offsetDiv4*4

If the entry id is not present in the sparse table, it should behave like a missing entry/config rather than interpreting unrelated bytes as a dense offset.

Notes

This affects normal resource references such as launcher icons stored as android:icon="@7f...". Android and Google Play resolve the resource correctly, but apkparser currently fails before the resource value can be read.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions