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…