background

Arsc resources. Arsc is the product generated after Android compilation, mainly used to establish resource mapping relationship, in order to clearly understand the mapping logic, it is necessary to analyze the structure of resources.

Structure analysis

Resources.arsc is a binary file whose internal structure is defined in the resourcetypes.h file. The resourcetypes.h file is a binary file whose internal structure is defined in the resourcetypes.h file.

  • RES_TABLE_TYPE
  • RES_STRING_POOL_TYPE
  • RES_TABLE_PACKAGE_TYPE
  • RES_TABLE_TYPE_SPEC_TYPE
  • RES_TABLE_TYPE_TYPE

The above structure is dissected in detail below.

Index structure

The index refers to the resource ID generated in the R.java file, as follows:

public final class R { public static final class attr { } public static final class drawable { public static final int icon=0x7f020000; } public static final class layout { public static final int main=0x7f030000; } public static final class string { public static final int app_name=0x7f040001; public static final int hello=0x7f040000; }}Copy the code

The format is an 8-bit hexadecimal :0xPPTTEEEE

  • PP:Package ID, the Package namespace, the value range is [0x01, 0x7F]. Generally, third-party applications are 7F. During the plug-in, we need to modify the Package namespace of the resources in the plug-in to prevent the resource IDS in the host and the plug-in from repeating
  • TT: indicates the resource type. Resources include attR, Drawable, Layout, and String. These two characters represent the resource type, which is dynamically generated instead of fixed
  • EEEE: indicates the structure of the resource index based on the value in the offset array for a certain type of resource

Chunk Header

The chunk Header is a structure common to all types of blocks and is mainly used to describe the structure information of chunk:

struct ResChunk_header
{
    // Type identifier for this chunk.  The meaning of this value depends
    // on the containing chunk.
    uint16_t type;

    // Size of the chunk header (in bytes).  Adding this value to
    // the address of the chunk allows you to find its associated data
    // (if any).
    uint16_t headerSize;

    // Total size of this chunk (in bytes).  This is the chunkSize plus
    // the size of any data associated with the chunk.  Adding this value
    // to the chunk allows you to completely skip its contents (including
    // any child chunks).  If this value is the same as chunkSize, there is
    // no data associated with the chunk.
    uint32_t size;
};
Copy the code

1. Type: indicates the resource type, which is used to distinguish the type of each chunk. It is defined as follows:

enum {
    RES_STRING_POOL_TYPE        = 0x0001,
    RES_TABLE_TYPE              = 0x0002,
    RES_TABLE_PACKAGE_TYPE      = 0x0200,
    RES_TABLE_TYPE_TYPE         = 0x0201,
    RES_TABLE_TYPE_SPEC_TYPE    = 0x0202,
    RES_TABLE_LIBRARY_TYPE      = 0x0203
};
Copy the code

2. HeaderSize: Indicates the header size of each chunk

3. Size: The size of each chunk

RES_TABLE_TYPE

RES_TABLE_TYPE describes the properties of the entire resources.arsc:

struct ResTable_header
{
    struct ResChunk_header header;

    // The number of ResTable_package structures.
    uint32_t packageCount;
};
Copy the code

PackageCount: resources. How many arsc ResTable_package, usually only one

RES_STRING_POOL_TYPE

A string resource pool is used to store strings. Note that the string resource pool does not include the resource type or name.

<string name="app_name">Demo</string>
Copy the code

Only Demo strings will be stored, and the resource types string and app_name will be in PACKAGE Chunk, which will be described later. The structure of a string resource pool is as follows:

struct ResStringPool_header
{
    struct ResChunk_header header;
    
    // Number of strings in this pool (number of uint32_t indices that follow
    // in the data).
    uint32_t stringCount;

    // Number of style span arrays in the pool (number of uint32_t indices
    // follow the string indices).
    uint32_t styleCount;

    // Flags.
    enum {
        // If set, the string index is sorted by the string values (based
        // on strcmp16()).
        SORTED_FLAG = 1<<0,

        // String pool is encoded in UTF-8
        UTF8_FLAG = 1<<8
    };
    uint32_t flags;

    // Index from header of the string data.
    uint32_t stringsStart;

    // Index from header of the style data.
    uint32_t stylesStart;
};
Copy the code

StringCount: indicates the number of strings in chunk

StyleCount: The number of styles in a string

Flags: string encoding, utF-8 or UTF-16

StringsStart: The offset of the string data block within the current block. This is where the string is read from

StylesStart: String style data block offset within the current block

Here are some important points to note when reading strings:

  • ResStringPool_header is followed by two arrays, string offset array and string style offset array, of type int, stringCount and styleCount of header size. Once the header is read, we need to populate both arrays
  • A string cannot be read directly from the offset array. The first address of each string is:
int offset = chunkoffset + stringsStart + stringIndex[i]
Copy the code

Offset: is the starting position of each string read

Chunkoffset: the starting position of the current chunk in the entire Resources.arSC

StringsStart: Offset of the string in the header

StringIndex: Array of string offsets read above

  • The length of the string is determined by the two bytes at the beginning of the string reading position. The calculation of the length is related to the string encoding. The calculated length code is as follows:
    int len = data[0];
    if (flags == UTF8_FLAG) {
        if ((data[0] & 0x80) != 0) {
            len  = ((data[0] & 0x7F) << 8) | data[1];
        }
    } else {
        if((len & 0x8000) ! = 0) { len = ((len & 0x7FFF) << 16) | data[1]; } len = len * 2; }Copy the code

When the length of the string is obtained, the corresponding length of bytes can be directly read and converted into a string according to the encoding format

RES_TABLE_PACKAGE_TYPE

RES_TABLE_PACKAGE_TYPE is a package concept. This structure contains the following RES_TABLE_TYPE_SPEC_TYPE and RES_TABLE_TYPE_TYPE as well as the string resource pool.

struct ResTable_package
{
    struct ResChunk_header header;

    // If this is a base package, its ID.  Package IDs start
    // at 1 (corresponding to the value of the package bits in a
    // resource identifier).  0 means this is not a base package.
    uint32_t id;

    // Actual name of this package, \0-terminated.
    uint16_t name[128];

    // Offset to a ResStringPool_header defining the resource
    // type symbol table.  If zero, this package is inheriting from
    // another base package (overriding specific values in it).
    uint32_t typeStrings;

    // Last index into typeStrings that is for public use by others.
    uint32_t lastPublicType;

    // Offset to a ResStringPool_header defining the resource
    // key symbol table.  If zero, this package is inheriting from
    // another base package (overriding specific values in it).
    uint32_t keyStrings;

    // Last index into keyStrings that is forpublic use by others. uint32_t lastPublicKey; 0xPPTTEEEE uint32_ttypeIdOffset;
};
Copy the code

Id: Package ID, usually 7F

Name: the package name

TypeStrings: type string pool offset (attr,drawable,layout)

KeyStrings: keyword string pool offset, where the string pool stores keys such as r.string.appName, where appName is stored

There are a few other properties that I didn’t use in parsing, so I didn’t look at them very carefully

RES_TABLE_TYPE_SPEC_TYPE

RES_TABLE_TYPE_SPEC_TYPE represents the resource type. Android resources include ATTR,drawable,layout, etc. Each type has such a structure, so there are more than one in the PACKAGE. Each RES_TABLE_TYPE_SPEC_TYPE structure will be followed by an array of RES_TABLE_TYPE_TYPE. If drawable has multiple sizes, there will be as many RES_TABLE_TYPE_TYPE blocks as there are sizes, as shown below:

struct ResTable_typeSpec
{
    struct ResChunk_header header;

    // 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.
    uint8_t id;
    
    // Must be 0.
    uint8_t res0;
    // Must be 0.
    uint16_t res1;
    
    // Number of uint32_t entry configuration masks that follow.
    uint32_t entryCount;

    enum : uint32_t {
        // Additional flag indicating an entry is public.
        SPEC_PUBLIC = 0x40000000u,

        // Additional flag indicating an entry is overlayable at runtime.
        // Added in Android-P.
        SPEC_OVERLAYABLE = 0x80000000u,
    };
};
Copy the code

EntryCount: indicates the number of RES_TABLE_TYPE_TYPE values. This value is not used for RES_TABLE_TYPE_TYPE

RES_TABLE_TYPE_TYPE

RES_TABLE_TYPE_TYPE represents the resource data.

Here are a few points:

  • Each RES_TABLE_TYPE_TYPE contains an array of Restable_entries, the specific contents of each resource
  • Note that there is an offset array of ResTable_entry in the figure above, which is EEEE in 0xPPTTEEEE
  • Resources can be classified into bag and non-bag types. The value of bag type is determined, and the value of non-bag type is multiple, as follows:
<string name="app_name">Demo</string>
Copy the code

The following types are non-BAG:

<resources>
    <attr name="custom_orientation">
        <enum name="custom_vertical" value="100" />
        <enum name="custom_horizontal" value="200" />
    </attr>
</resources>
Copy the code
  • A resource of type BAG has a structure called ResTableEntry, and a resource of type non-bag has a structure called ResTableMapEntry, which has multiple data values
  • RES_TABLE_TYPE_TYPE has multiple causes due to ResTable_config, such as screen regions

Let’s take a look at the structure:

struct ResTable_type
{
    struct ResChunk_header header;

    enum {
        NO_ENTRY = 0xFFFFFFFF
    };
    
    // 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.
    uint8_t id;
    
    enum {
        // 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. FLAG_SPARSE = 0x01, }; uint8_t flags; // Must be 0. uint16_t reserved; // Number of uint32_t entry indices that follow. uint32_t entryCount; // Offset from headerwhere ResTable_entry data starts.
    uint32_t entriesStart;

    // Configuration this collection of entries is designed for. This must always be last.
    ResTable_config config;
};
Copy the code

Id: type id, if the value is NO_ENTRY = 0 XFFFFFFFF, then no current configuration type entryCount: behind the number of ResTableEntry entriesStart: ResTableEntry offset, ResTable_config: specifies the configuration information (language, screen size, etc.) of ResTableEntry. After the header is read, it is the offset array of ResTableEntry. Here’s the structure of ResTableEntry:

struct ResTable_entry
{
    // Number of bytes in this structure.
    uint16_t size;

    enum {
        // If set, this is a complex entry, holding a set of name/value
        // mappings.  It is followed by an array of ResTable_map structures.
        FLAG_COMPLEX = 0x0001,
        // If set, this resource has been declared public, so libraries
        // are allowed to reference it.
        FLAG_PUBLIC = 0x0002,
        // If set. this is a weak resource and may be overriden by strong // resources of the same name/type. This is only useful during //  linking with other resource tables. FLAG_WEAK = 0x0004 }; uint16_t flags; // Reference into ResTable_package::keyStrings identifying this entry. struct ResStringPool_ref key; };Copy the code

Size: indicates the size of the current structure

Flags: checks whether the current type is BAG or non-BAG

ResStringPool_ref: This structure contains an int value that is the index of the current keyword string pool. For example, r.string.appname, the int is the index of appName in the keyword string pool

ResTable_entry is followed by ResValue. If ResTable_entry is of type BAG, ResValue is followed by array ResValue.

struct Res_value
{
    // Number of bytes in this structure.
    uint16_t size;

    // Always set to 0.
    uint8_t res0;
        
    // Type of the data value.
    enum : uint8_t {
        // The 'data' is either 0 or 1, specifying this resource is either
        // undefined or empty, respectively.
        TYPE_NULL = 0x00,
        // The 'data' holds a ResTable_ref, a reference to another resource
        // table entry.
        TYPE_REFERENCE = 0x01,
        // The 'data' holds an attribute resource identifier.
        TYPE_ATTRIBUTE = 0x02,
        // The 'data' holds an index into the containing resource table's // global value string pool. TYPE_STRING = 0x03, // The 'data' holds a single-precision floating point number. TYPE_FLOAT = 0x04, // The 'data' holds a complex number encoding a dimension value, // such as "100in". TYPE_DIMENSION = 0x05, // The 'data' holds a complex number encoding a fraction of a // container. TYPE_FRACTION = 0x06, // The 'data' holds a dynamic ResTable_ref, which needs to be // resolved before it can be used like a TYPE_REFERENCE. TYPE_DYNAMIC_REFERENCE = 0x07, // The 'data' holds an attribute resource identifier, which needs to be resolved // before it can be used like a TYPE_ATTRIBUTE. TYPE_DYNAMIC_ATTRIBUTE = 0x08, // Beginning of integer flavors... TYPE_FIRST_INT = 0x10, // The 'data' is a raw integer value of the form n.. n. TYPE_INT_DEC = 0x10, // The 'data' is a raw integer value of the form 0xn.. n. TYPE_INT_HEX = 0x11, // The 'data' is either 0 or 1, for input "false" or "true" respectively. TYPE_INT_BOOLEAN = 0x12, // Beginning of color integer flavors... TYPE_FIRST_COLOR_INT = 0x1c, // The 'data' is a raw integer value of the form #aarrggbb. TYPE_INT_COLOR_ARGB8 = 0x1c, // The 'data' is a raw integer value of the form #rrggbb. TYPE_INT_COLOR_RGB8 = 0x1d, // The 'data' is a raw integer value of the form #argb. TYPE_INT_COLOR_ARGB4 = 0x1e, // The 'data' is a raw integer value of the form #rgb. TYPE_INT_COLOR_RGB4 = 0x1f, // ... end of integer flavors. TYPE_LAST_COLOR_INT = 0x1f, // ... end of integer flavors. TYPE_LAST_INT = 0x1f }; uint8_t dataType; // The data for this item, as interpreted according to dataType. typedef uint32_t data_type; data_type data; };Copy the code

DataType: indicates the type of the current data, which is defined above. This is important. Data: indicates the data, depending on the above data type

The structure of Resources.arSC has been analyzed, and some details have not been further studied, which does not affect the overall structure analysis.

Index analysis practice

Arsc = resources. Arsc = resources. Arsc = resources. Arsc = resources.

<? xml version="1.0" encoding="utf-8"? > <resources> <publictype="color" name="colorAccent" id="2130771968" />
    <public type="color" name="colorPrimary" id="2130771969" />
    <public type="color" name="colorPrimaryDark" id="2130771970" />
    <public type="drawable" name="$ic_launcher_foreground__0" id="2130837504" />
    <public type="drawable" name="ic_launcher_background" id="2130837505" />
    <public type="drawable" name="ic_launcher_foreground" id="2130837506" />
    <public type="id" name="btn_start_service" id="2130903040" />
    <public type="id" name="btn_uInit" id="2130903041" />
    <public type="layout" name="activity_main" id="2130968576" />
    <public type="layout" name="second" id="2130968577" />
    <public type="mipmap" name="ic_launcher" id="2131034112" />
    <public type="mipmap" name="ic_launcher_round" id="2131034113" />
    <public type="string" name="app_name" id="2131099648" />
</resources>
Copy the code

App_name change id=2131099648 to hexadecimal 7f060000 packageID:7f TT:06 EEEE:0000

ResTableHeader{chunkHeader=ResChunkHeader{type=2, headerSize=12, size=4204}, packageCount=1}
ResStringPoolHeader{chunkHeader=ResChunkHeader{type=1, headerSize=28, size=1148}, stringCount=24, styleCount=0, flags=256, stringsStart=124, stylesStart=0}
---------value ==Demo---index:0
---------value ==res/drawable-anydpi-v21/ic_launcher_background.xml---index:1
---------value ==res/drawable-hdpi-v4/ic_launcher_background.png---index:2
---------value ==res/drawable-ldpi-v4/ic_launcher_background.png---index:3
---------value ==res/drawable-mdpi-v4/ic_launcher_background.png---index:4
---------value ==res/drawable-v24/$ic_launcher_foreground__0.xml---index:5 ---------value ==res/drawable-v24/ic_launcher_foreground.xml---index:6 ---------value ==res/drawable-xhdpi-v4/ic_launcher_background.png---index:7 ---------value ==res/drawable-xxhdpi-v4/ic_launcher_background.png---index:8 ---------value ==res/drawable-xxxhdpi-v4/ic_launcher_background.png---index:9 ---------value ==res/layout/activity_main.xml---index:10 ---------value ==res/layout/second.xml---index:11 ---------value ==res/mipmap-anydpi-v26/ic_launcher.xml---index:12 ---------value ==res/mipmap-anydpi-v26/ic_launcher_round.xml---index:13 ---------value ==res/mipmap-hdpi-v4/ic_launcher.png---index:14 ---------value ==res/mipmap-hdpi-v4/ic_launcher_round.png---index:15 ---------value ==res/mipmap-mdpi-v4/ic_launcher.png---index:16 ---------value ==res/mipmap-mdpi-v4/ic_launcher_round.png---index:17 ---------value ==res/mipmap-xhdpi-v4/ic_launcher.png---index:18 ---------value ==res/mipmap-xhdpi-v4/ic_launcher_round.png---index:19 ---------value ==res/mipmap-xxhdpi-v4/ic_launcher.png---index:20 ---------value ==res/mipmap-xxhdpi-v4/ic_launcher_round.png---index:21  ---------value ==res/mipmap-xxxhdpi-v4/ic_launcher.png---index:22 ---------value ==res/mipmap-xxxhdpi-v4/ic_launcher_round.png---index:23 start parse string style ResTablePackage{chunkHeader=ResChunkHeader{type=512, headerSize=288, size=3044}, id=7f, name=[c, , o, , m, , ., , e, , x, , a, , m, , p, , l, , e, , ., , t, , e, , c, , h, , a, , i, , n, , h, , o, , s, , t, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , . . ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,typeStrings=288, lastPublicType=0, keyStrings=432, lastPublicKey=0, typeIdOffset=0}
ResStringPoolHeader{chunkHeader=ResChunkHeader{type=1, headerSize=28, size=144}, stringCount=6, styleCount=0, flags=0, stringsStart=52, stylesStart=0} ---------value ==color---index:0 ---------value ==drawable---index:1 ---------value ==id---index:2 ---------value ==layout---index:3 ---------value ==mipmap---index:4 ---------value ==string---index:5 start parse string  style ResStringPoolHeader{chunkHeader=ResChunkHeader{type=1, headerSize=28, size=312}, stringCount=13, styleCount=0, flags=256, stringsStart=80, stylesStart=0}
---------value ==colorAccent---index:0
---------value ==colorPrimary---index:1
---------value ==colorPrimaryDark---index:2
---------value ==$ic_launcher_foreground__0---index:3
---------value ==ic_launcher_background---index:4
---------value ==ic_launcher_foreground---index:5
---------value ==btn_start_service---index:6
---------value ==btn_uInit---index:7
---------value ==activity_main---index:8
---------value ==second---index:9
---------value ==ic_launcher---index:10
---------value ==ic_launcher_round---index:11
---------value ==app_name---index:12
start parse string style
ResTableTypeSpec{chunkHeader=ResChunkHeader{type=514, headerSize=16, size=28}, id=1, res0=0, res1=0, entryCount=3, resTableTypes=[], spec=[0, 0, 0]}
ResTableType{chunkHeader=ResChunkHeader{type=513, headerSize=84, size=144}, id=1, flags=0, reserved=0, entryCount=3, entriesStart=96, entrys=[0, 10, 20], resTableEntries=[ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=0}, resValue=ResValue{size=8, res0=0, dataType=29, data=-2614432}}, ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=1}, resValue=ResValue{size=8, res0=0, dataType=29, data=-16743049}}, ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=2}, resValue=ResValue{size=8, res0=0, dataType=29, data=-16754869}}]}
ResTableTypeSpec{chunkHeader=ResChunkHeader{type=514, headerSize=16, size=28}, id=2, res0=0, res1=0, entryCount=3, resTableTypes=[], spec=[0, 1280, 0]}
ResTableType{chunkHeader=ResChunkHeader{type=513, headerSize=84, size=128}, id=2, flags=0, reserved=0, entryCount=3, entriesStart=96, entrys=[0, ffffffff, 10], resTableEntries=[ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=3}, resValue=ResValue{size=8, res0=0, dataType=3, data=5}}, null, ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=5}, resValue=ResValue{size=8, res0=0, dataType=3, data=6}}]}
ResTableType{chunkHeader=ResChunkHeader{type=513, headerSize=84, size=112}, id=2, flags=0, reserved=0, entryCount=3, entriesStart=96, entrys=[ffffffff, 0, ffffffff], resTableEntries=[null, ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=4}, resValue=ResValue{size=8, res0=0, dataType=3, data=3}}, null]}
ResTableType{chunkHeader=ResChunkHeader{type=513, headerSize=84, size=112}, id=2, flags=0, reserved=0, entryCount=3, entriesStart=96, entrys=[ffffffff, 0, ffffffff], resTableEntries=[null, ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=4}, resValue=ResValue{size=8, res0=0, dataType=3, data=4}}, null]}
ResTableType{chunkHeader=ResChunkHeader{type=513, headerSize=84, size=112}, id=2, flags=0, reserved=0, entryCount=3, entriesStart=96, entrys=[ffffffff, 0, ffffffff], resTableEntries=[null, ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=4}, resValue=ResValue{size=8, res0=0, dataType=3, data=2}}, null]}
ResTableType{chunkHeader=ResChunkHeader{type=513, headerSize=84, size=112}, id=2, flags=0, reserved=0, entryCount=3, entriesStart=96, entrys=[ffffffff, 0, ffffffff], resTableEntries=[null, ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=4}, resValue=ResValue{size=8, res0=0, dataType=3, data=7}}, null]}
ResTableType{chunkHeader=ResChunkHeader{type=513, headerSize=84, size=112}, id=2, flags=0, reserved=0, entryCount=3, entriesStart=96, entrys=[ffffffff, 0, ffffffff], resTableEntries=[null, ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=4}, resValue=ResValue{size=8, res0=0, dataType=3, data=8}}, null]}
ResTableType{chunkHeader=ResChunkHeader{type=513, headerSize=84, size=112}, id=2, flags=0, reserved=0, entryCount=3, entriesStart=96, entrys=[ffffffff, 0, ffffffff], resTableEntries=[null, ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=4}, resValue=ResValue{size=8, res0=0, dataType=3, data=9}}, null]}
ResTableType{chunkHeader=ResChunkHeader{type=513, headerSize=84, size=112}, id=2, flags=0, reserved=0, entryCount=3, entriesStart=96, entrys=[ffffffff, 0, ffffffff], resTableEntries=[null, ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=4}, resValue=ResValue{size=8, res0=0, dataType=3, data=1}}, null]}
ResTableTypeSpec{chunkHeader=ResChunkHeader{type=514, headerSize=16, size=24}, id=3, res0=0, res1=0, entryCount=2, resTableTypes=[], spec=[0, 0]}
ResTableType{chunkHeader=ResChunkHeader{type=513, headerSize=84, size=124}, id=3, flags=0, reserved=0, entryCount=2, entriesStart=92, entrys=[0, 10], resTableEntries=[ResTableEntry{size=8, flags=4, key=ResStringPoolRef{index=6}, resValue=ResValue{size=8, res0=0, dataType=18, data=0}}, ResTableEntry{size=8, flags=4, key=ResStringPoolRef{index=7}, resValue=ResValue{size=8, res0=0, dataType=18, data=0}}]}
ResTableTypeSpec{chunkHeader=ResChunkHeader{type=514, headerSize=16, size=24}, id=4, res0=0, res1=0, entryCount=2, resTableTypes=[], spec=[0, 0]}
ResTableType{chunkHeader=ResChunkHeader{type=513, headerSize=84, size=124}, id=4, flags=0, reserved=0, entryCount=2, entriesStart=92, entrys=[0, 10], resTableEntries=[ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=8}, resValue=ResValue{size=8, res0=0, dataType=3, data=10}}, ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=9}, resValue=ResValue{size=8, res0=0, dataType=3, data=11}}]}
ResTableTypeSpec{chunkHeader=ResChunkHeader{type=514, headerSize=16, size=24}, id=5, res0=0, res1=0, entryCount=2, resTableTypes=[], spec=[1280, 1280]}
ResTableType{chunkHeader=ResChunkHeader{type=513, headerSize=84, size=124}, id=5, flags=0, reserved=0, entryCount=2, entriesStart=92, entrys=[0, 10], resTableEntries=[ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=10}, resValue=ResValue{size=8, res0=0, dataType=3, data=16}}, ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=11}, resValue=ResValue{size=8, res0=0, dataType=3, data=17}}]}
ResTableType{chunkHeader=ResChunkHeader{type=513, headerSize=84, size=124}, id=5, flags=0, reserved=0, entryCount=2, entriesStart=92, entrys=[0, 10], resTableEntries=[ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=10}, resValue=ResValue{size=8, res0=0, dataType=3, data=14}}, ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=11}, resValue=ResValue{size=8, res0=0, dataType=3, data=15}}]}
ResTableType{chunkHeader=ResChunkHeader{type=513, headerSize=84, size=124}, id=5, flags=0, reserved=0, entryCount=2, entriesStart=92, entrys=[0, 10], resTableEntries=[ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=10}, resValue=ResValue{size=8, res0=0, dataType=3, data=18}}, ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=11}, resValue=ResValue{size=8, res0=0, dataType=3, data=19}}]}
ResTableType{chunkHeader=ResChunkHeader{type=513, headerSize=84, size=124}, id=5, flags=0, reserved=0, entryCount=2, entriesStart=92, entrys=[0, 10], resTableEntries=[ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=10}, resValue=ResValue{size=8, res0=0, dataType=3, data=20}}, ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=11}, resValue=ResValue{size=8, res0=0, dataType=3, data=21}}]}
ResTableType{chunkHeader=ResChunkHeader{type=513, headerSize=84, size=124}, id=5, flags=0, reserved=0, entryCount=2, entriesStart=92, entrys=[0, 10], resTableEntries=[ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=10}, resValue=ResValue{size=8, res0=0, dataType=3, data=22}}, ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=11}, resValue=ResValue{size=8, res0=0, dataType=3, data=23}}]}
ResTableType{chunkHeader=ResChunkHeader{type=513, headerSize=84, size=124}, id=5, flags=0, reserved=0, entryCount=2, entriesStart=92, entrys=[0, 10], resTableEntries=[ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=10}, resValue=ResValue{size=8, res0=0, dataType=3, data=12}}, ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=11}, resValue=ResValue{size=8, res0=0, dataType=3, data=13}}]}
ResTableTypeSpec{chunkHeader=ResChunkHeader{type=514, headerSize=16, size=20}, id=6, res0=0, res1=0, entryCount=1, resTableTypes=[], spec=[0]}
ResTableType{chunkHeader=ResChunkHeader{type=513, headerSize=84, size=104}, id=6, flags=0, reserved=0, entryCount=1, entriesStart=88, entrys=[0], resTableEntries=[ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=12}, resValue=ResValue{size=8, res0=0, dataType=3, data=0}}]}

Copy the code

The output of ResTablePackage can be seen as id = 7f

ResTablePackage{chunkHeader=ResChunkHeader{type=512, headerSize=288, size=3044}, id=7f
Copy the code

Then look at the output of ResTableTypeSpec id=6:

ResTableTypeSpec{chunkHeader=ResChunkHeader{type=514, headerSize=16, size=20}, id=6, res0=0, res1=0, entryCount=1, resTableTypes=[], spec=[0]}
ResTableType{chunkHeader=ResChunkHeader{type=513, headerSize=84, size=104}, id=6, flags=0, reserved=0, entryCount=1, entriesStart=88, entrys=[0], resTableEntries=[ResTableEntry{size=8, flags=0, key=ResStringPoolRef{index=12}, resValue=ResValue{size=8, res0=0, dataType=3, data=0}}]}

Copy the code

If id=6, find the name of the resource item. Here is the printed resource item string pool:

---------value ==color---index:0
---------value ==drawable---index:1
---------value ==id---index:2
---------value ==layout---index:3
---------value ==mipmap---index:4
---------value ==string---index:5
Copy the code

The resource item index starts at 1, so the value of index = 6 is a string

Key =ResStringPoolRef{index=12}; entrys=[0];

---------value ==colorAccent---index:0
---------value ==colorPrimary---index:1
---------value ==colorPrimaryDark---index:2
---------value ==$ic_launcher_foreground__0---index:3
---------value ==ic_launcher_background---index:4
---------value ==ic_launcher_foreground---index:5
---------value ==btn_start_service---index:6
---------value ==btn_uInit---index:7
---------value ==activity_main---index:8
---------value ==second---index:9
---------value ==ic_launcher---index:10
---------value ==ic_launcher_round---index:11
---------value ==app_name---index:12
Copy the code

DataType =3; data=0; dataType=3; data=0;

---------value ==Demo---index:0
---------value ==res/drawable-anydpi-v21/ic_launcher_background.xml---index:1
---------value ==res/drawable-hdpi-v4/ic_launcher_background.png---index:2
---------value ==res/drawable-ldpi-v4/ic_launcher_background.png---index:3
---------value ==res/drawable-mdpi-v4/ic_launcher_background.png---index:4
Copy the code

Index =0, so value=Demo

So the index 0x7f060000 corresponds to R.string.appName=Demo, which is resolved

Inspired by the

Resources. Arsc (APKtool) is a resource that can be used only when the resource ID is not found. For example, if you define a string r.sing. test, and then use binary tools to open resources. Arse, find an index of this string, and then modify it, because it is not used, so it does not affect the normal use of APK.

Address of parsing tool :github.com/LiweiGogoin…