resources.arsc是Android编译后生成的产物,主要是用来创建资源映射关系,为了清晰地理解其中的映射逻辑,有必要剖析resources.arsc的结构。java
resources.arsc是一个二进制文件,其内部结构的定义在ResourceTypes.h文件中有详细的描述,文件的详细结构图已经有人画好了,这里直接拿来用,其中的后面一部分有一些错误,先忽略: git
下面详细解析上述结构。github
索引是指在R.java文件中生的资源ID,以下所示:数组
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;
}
}
复制代码
其格式为一个8位的16进制:0xPPTTEEEE 释义:bash
chunk Header是全部类型块都会有的结构,主要用于描述chunk的结构信息:app
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;
};
复制代码
1.type:资源类型,主要用于区分每一个chunk块的类型,定义以下:ide
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
};
复制代码
2.headerSize:每一个chunk块的header的大小 工具
3.size:每一个chunk块的大小ui
RES_TABLE_TYPE描述的是整个resources.arsc的属性: this
struct ResTable_header
{
struct ResChunk_header header;
// The number of ResTable_package structures.
uint32_t packageCount;
};
复制代码
packageCount:resources.arsc中有多少个ResTable_package,通常只有一个
字符串资源池,主要存储字符串,注意这里的字符串资源池不包括资源类型和资源名,举个例子:
<string name="app_name">Demo</string>
复制代码
这里只会存储Demo字符串,其中的资源类型string和app_name会在PACKAGE chunk块中,这个后面详细描述。 字符串资源池的结构以下:
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;
};
复制代码
stringCount:chunk中字符串的数量
styleCount:字符串的样式数量
flags:字符串的编码,UTF-8仍是UTF-16
stringsStart:字符串数据块在当前块内的偏移,这里主要是读取字符串的时候须要从这里开始读取
stylesStart:字符串样式数据块在当前块内的偏移
这里重点讲一下读取字符串的注意点:
int offset = chunkoffset + stringsStart + stringIndex[i]
复制代码
offset:是每一个字符串读取的开始位置
chunkoffset:当前chunk在整个resources.arsc中的起始位置
stringsStart:header中字符串的偏移
stringIndex:上面读取的字符串偏移数组
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;
}
复制代码
当字符串长度获取到后直接读取相应长度的字节而后根据编码格式转换成字符串便可
RES_TABLE_PACKAGE_TYPE是一个包的概念,这个结构包含了后面的RES_TABLE_TYPE_SPEC_TYPE和RES_TABLE_TYPE_TYPE以及字符串资源池,先看下结构图:
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 for public use by others.
uint32_t lastPublicKey;0xPPTTEEEE
uint32_t typeIdOffset;
};
复制代码
id:package id,通常为7f
name:包名
typeStrings:类型字符串池偏移,这里偏移和上面解释的同样,所谓类型字符串池就是attr,drawable,layout这种类型的字符串池,结构就是上面介绍的字符串资源池
keyStrings:关键字字符串池偏移,这个的字符串池存储的是关键子如R.string.appName中appName就存储在这个字符串池中
其余几个属性我在解析时没有用到,就没太仔细研究
RES_TABLE_TYPE_SPEC_TYPE 表明资源类型,Android中资源有attr,drawable,layout等,每个类型都有这样的一个结构,因此在PACKAGE中有多个,每一个RES_TABLE_TYPE_SPEC_TYPE结构后面会跟着RES_TABLE_TYPE_TYPE的数组,如drawable类型有多个尺寸的,因此有多少种尺寸后面就会跟着多少个RES_TABLE_TYPE_TYPE块,以下所示:
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,
};
};
复制代码
id:资源ID,这里就是上面介绍索引串0xPPTTEEEE中的TT的值 entryCount:这个值不是说后面RES_TABLE_TYPE_TYPE的数量,能够先不用管,基本也用不到,后面RES_TABLE_TYPE_TYPE中再介绍
RES_TABLE_TYPE_TYPE表明资源数据了,先看下结构:
这里有几个点先说一下:
<string name="app_name">Demo</string>
复制代码
下面是非bag类型:
<resources>
<attr name="custom_orientation">
<enum name="custom_vertical" value="100" />
<enum name="custom_horizontal" value="200" />
</attr>
</resources>
复制代码
下面先看下结构:
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 header where ResTable_entry data starts.
uint32_t entriesStart;
// Configuration this collection of entries is designed for. This must always be last.
ResTable_config config;
};
复制代码
id:type ID ,若是值为NO_ENTRY = 0xFFFFFFFF,则说明没有当前配置类型的 entryCount:后面ResTableEntry的数量 entriesStart:ResTableEntry的偏移,前面已经介绍过偏移的概念了 ResTable_config:配置信息,如语言,屏幕尺寸等,这里先无论 上面header读取结束后就是ResTableEntry的偏移数组了,这个也和上面分析字符串资源池同样,就不详细介绍了。 下面看下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;
};
复制代码
size:当前结构的大小
flags:判断当前是bag类型仍是非bag类型
ResStringPool_ref:这个结构里面有一个int值,是当前关键字字符串池中的索引,举个例子:R.string.appName,这个int就是appName在关键字字符串池中的索引
ResTable_entry后面跟着ResValue,若是ResTable_entry为bag类型,则后面跟着的是ResValue数组,先看下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; }; 复制代码
dataType:当前数据的类型,这个类型在上面有定义,这个很重要 data:数据,根据上面的数据类型定,若是类型为string,则当前的值为字符串资源池中的索引
上面就把resources.arsc的结构分析完了,其中有一些细枝末节的地方就没有再深刻去看了,不影响对总体的结构的分析。
根据上面的分析后直接写了一个分析工具,而后根据这个工具解析了一个Demo的resources.arsc,下面根据解析的输入来实战分析一下,先用JADX打开DEMO,找到resources.arsc,点开后以下所示:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<public type="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>
复制代码
先分析一下app_name 将id=2131099648转为16进制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}}]}
复制代码
其中ResTablePackage的输出能够看到id=7f
ResTablePackage{chunkHeader=ResChunkHeader{type=512, headerSize=288, size=3044}, id=7f
复制代码
而后再看下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}}]}
复制代码
id=6,先找下资源项的名称,下面是打印的资源项字符串池:
---------value ==color---index:0
---------value ==drawable---index:1
---------value ==id---index:2
---------value ==layout---index:3
---------value ==mipmap---index:4
---------value ==string---index:5
复制代码
资源项index是从1开始,因此index=6的value是string
上面的偏移数组是entrys=[0],因此EEEE=0000就找到了,就是上面输出里面的ResTableEntry,里面的key的内容是key=ResStringPoolRef{index=12},也就是资源项名称,资源项名称的输出以下:
---------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
复制代码
index=12的是app_name,再看下ResValue里面的内容,其中的dataType=3, data=0,前面分析过dataType=3的是字符串,因此data就是字符串池的索引:
---------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
复制代码
index=0,因此value=Demo
因此索引值0x7f060000 对应R.string.appName=Demo,这样就解析出来了
resources.arsc的结构分析完了,这里对咱们插件化处理资源,混淆资源缩减安装包都颇有启发,其中再简单说下防止反编译的作法,以前使用APKtool反编译会出现找不资源ID的错误,这个原理其实很简单,就是定义一个不使用的资源,如定义一个字符串R.string.test, 而后使用二进制工具打开resources.arse,找个这个字符串的索引,而后修改,因为是不使用,因此不影响APK的正常使用,可是反编译工具在解析的时候找不就报错了。
解析工具地址:github.com/LiweiGogoin…