AndroidManifest.xml文件在Android中我们称为清单文件。包含了应用的包名、权限、四大组件等信息。
一般我们直接将APK修改格式为zip后解压缩生成的AndroidManifest.xml打开(比如用Notepad)会是一堆乱码。是因为在APP打包的过程中,清单文件被编译成了二进制数据存储在安装包中。
所以,这里我们就来研究下AndroidManifest.xml的二进制文件结构,这样我们才可以获取到我们需要的信息。
(当然有一些开源工具,比如apktool、AXmlPrinter等可以直接读取编译后的AndroidManifest文件)
附上一张经典的结构图:
下面我们以WhatsApp的AndroidManifest.xml文件为例进行分析。
我们使用010 Edit打开WhatsApp的AndroidManifest.xml后,如下图:
结合之前的结构图,AndroidManifest.xml的总体结构大致可以分为:
1.HEADER header
2.STRINGCHUNK stringChunk
3.RESOURCEIDCHUNK resourceChunk
4.XMLCONTENT CHUNK XmlContent Chunk
逐个来看!
我们可以看到header包括了2个magicnumber(魔数)和filesize(文件大小)。各占了4个字节。
其中magicnumber始终为0x000080003。
filesize是指的文件总字节数,我们这里大小为0x0001747C,对应大约是95356字节(93KB左右),可以看下我们的AndroidManifest文件大小也大约是这个值。
从0x00000008开始为String Chunk的内容,String Chunk 主要存储了AndroidManifest文件中的所有字符串信息。
这是String Chunk的标识。占4个字节,并且也是固定的:0x001C0001
定义String Chunk的大小。也是占4个字节。这里的值是0x00009C18(即:39960 bytes)。
所以整个String Chunk的内容区间就是0x00000008 到 0x0009C16h
定义的是字符串的个数。同样是4个字节。这里是0x00000204,即一共有516个字符串。
定义的是样式的数量。占4个字节。这里是为0x0000000。
UNKNOW值,固定值且占4个字节。
字符串池的偏移量,这里是相对于String Chunk的开始处。
样式池的偏移量,上面我们的杨树数量scStyleCount为0,所以这里的偏移量为0x00000000。
int数组,大小就是scStringCount的值 516。
字符串池中的每个字符串的格式。
字符串的长度。这里是0x00000005,即为5。这个长度指的是字符串中字符的个数,并非字节数。
字符串的内容,每个字符对应两个字节,该字符串共有5个字符,所以一起是10字节。
我们这里来看第一个字符:0x0074,对应的二进制值为116 (A:97 ),所以对应的字母为“t”
结合在一起,字符串的内容就是theme。
字符串终止符。这里为0x0000。
ResourceId Chunk的标识符。4个字节。值是固定的,为0x00080180
ResourceId Chunk的大小。占4个字节。值为0x000000C8,即为200 bytes
int数组,它的大小为 (rcSize-8)/4 。这里为48。
这里的ResourceId对应的是源码中的/frameworks/base/core/res/res/values/public.xml
我们看其中某一个rcItem:
可以看到实际上是和我们源码中的public.xml一一对应的。
XML Content一共有五种类型的Chunk,分别是StartNamespaceChunk、startTagChunk、endTagChunk、endNamespaceChunk、TextChunk。
该Chunk的标识符。4个字节。固定值为0x00100100。
Chunk的大小,4个字节。这里值为0x00000018,即为24 bytes。
Chunk的行号。占4个字节。值为0x00000002。对应的是编码前文件的第2行的内容。
即是如下:
索引值,占4个字节。指向的是字符串池对应的字符串,表示命名空间的前缀。 我们这里的值是0x0000003D,即为61。这时我们看String Pool的索引也就是strItem[61]的值,就是“android” 同样是指向String Pool中对应索引的字符串,表示命名空间的Uri。 我们这里的值是0x000001E3,即为483。也是去看strItem[483]的值,是为“http://schemas.android.com/apk/res/android” 其中的字段不详细说了,同StartNamespaceChunk类似。 startTagChunk的标识符,4个字节且值唯一,为0x00100102 startTagChunk的大小,占4个字节。值为0x00000074(即为116) startTagChunk的行号,占4个字节。值为0x000006A8(即为1704) 标签名称在String Pool中的索引。这里值为0x000001F0(即为496),对应的strItem[496]的值,为“provider” 一个固定值:0x00140014,暂未知作用。 属性个数。占4个字节。这里的值为:0x00000004(即为4) 属性内容。以其中一个为例,先来看构造: 可以看到这个chunk占20 字节,有5个字段。 acNamespaceUri:属性的命名空间uri在String Pool中的索引。我们这里是0x000001E3(即为483)。对应的strItem[483]的值为“http://schemas.android.com/apk/res/android“ acName:属性名称在String Pool中的索引。这里值为0x00000003(即为3),对应的strItem[3]的值为”name“ acValueStr:属性值。也是需要对应的看String Pool中的索引。这里值为0x00000096(即为150)。对应的strItem[150]的值为”androidx.lifecycle.ProcessLifecycleOwnerInitializer“ acType:属性类型 acData:属性数据 所以。这个最终的startTagChunk就是如下: 和startTagChunk类似的。每一对TagChunk都是对应的,有start也肯定有end的。 rovider android:name=“androidx.lifecycle.ProcessLifecycleOwnerInitializer” android:exported=“false” android:multiprocess=“true” android:authorities=“com.whatsapp.lifecycle-process”/> 和startTagChunk类似的。每一对TagChunk都是对应的,有start也肯定有end的。d.sncPreflix
e.sncUri
B.endNamespaceChunk
C.startTagChunk
a.stcSignature
b.stcSize
c.stcLineNumber
d.stcName
e.stcFlags
f.stcAttributeCount
g.attributeChunk
D.endTagChunk
D.endTagChunk