第一、头部信息
1、文件魔数:四个字节
2、文件大小:四个字节
第二、String Chunk(存放文件中所有字符串信息)
1、ChunkType:stringchunk类型:0x001c0001
2、chunksize:stringchunk大小:四个字节
3、stringcount:stringchunk中字符串个数,四个字节
4、stylecount:stringchunk样式的个数,四个字节,实际解析中该值为0
5、unknown:未知区域,四个字节,解析过程中需要略过
6、stringpooloffset:字符串池的偏移值,四个字节,是相对于stringchunk头部位置的
7、stylepooloffset:样式池的偏移值,四个字节,这里没有style,所以该字段可忽略
8、stringoffsets:每个字符串的偏移值,所以他的大小应该是:stringcount*4字节
9、styleoffsets:每个样式的偏移值,所以他的大小应该是stylecount*4个字节
后面就开始是字符串内容和样式的内容了。
代码解析:
1、首先把androidmanifest.xml文件读到一个byte数组中;
2、解析头部信息;
3、解析StringChunk信息;
注意:
0.一个字符对应两个字节时,还是开始的两个字节是字符串的长度;
1.四个字节的unknown字段需要略过;
2.在解析字符串内容的时候,字符串内容的结束符是:0x0000;
3.每个字符串开始的前两个字节是字符串长度;
4.用一个全局的字符列表存储这些字符串的值,后面会用索引来获取这些字符串的值。
//就用了stringpooloffset?!
byte[] firstStringSizeByte=Utils.copy(chunkStringContentByte,0,2);
//读取第一个字符串大小
int firstStringSize=Utils.byte2short(firstStringByte)*2;
//把结束符也读进去
byte[] firstStringContentByte=Utils.copyByte(chunkStringContentByte,2,firstStringSize+2);
stringContentList.add(Utils.filterStringNull(firstStringContent));
//所有的字符串都放到stringContentList中
int endStringIndex=2+firstStringSize+2;
while(stringContentList.size()<chunkStringCount){
int stringSize=Utils.byte2short(Utils.copyByte(chunkStringContentByte,endStringIndex,2))*2;
String str=new String(Utils.copyByte(chunkStringContentByte,endStringIndex+2,stringSize+2));
stringContentList.add(Utils.filterStringNull(str));
endStringIndex+=(2+stringSize+2);
}
注意:为什么存在字符串偏移值,这里还要使用字符串池偏移来获取所有字符串到ArrayList中呢
第三、解析ResourceIdchunk
1、chunktype:ResourceIdChunk类型,四个固定字节:0x00080108
2、Chunksize:ResourceIdchunk的大小,四个字节
3、ResourceIds:ResourceId的内容,这里大小是Resourcechunk大小(先减去头部大小8个字节)除以4(ResourceId占四个字节啊!)
代码:
/**
* 解析Resource Chunk
* @param byteSrc
*/
public static void parseResourceChunk(byte[] byteSrc){
byte[] chunkTagByte = Utils.copyByte(byteSrc, resourceChunkOffset, 4);
byte[] chunkSizeByte = Utils.copyByte(byteSrc, resourceChunkOffset+4, 4);
int chunkSize = Utils.byte2int(chunkSizeByte);
//这里需要注意的是chunkSize是包含了chunkTag和chunkSize这两个字节的,所以需要剔除
byte[] resourceIdByte = Utils.copyByte(byteSrc, resourceChunkOffset+8, chunkSize-8);
ArrayList<Integer> resourceIdList = new ArrayList<Integer>(resourceIdByte.length/4);
for(int i=0;i<resourceIdByte.length;i+=4){
int resId = Utils.byte2int(Utils.copyByte(resourceIdByte, i, 4));
System.out.println("id:"+resId+",hex:"+Utils.bytesToHexString(Utils.copyByte(resourceIdByte, i, 4)));
resourceIdList.add(resId);
}
nextChunkOffset = (resourceChunkOffset+chunkSize);
}
只是把ResourceId全部添加到ArrayList里就可以了吗,没有任何索引信息啊
第四、解析StartNamespaceChunk
1、chunktype:固定四个字节:0x00100100
2、chunksize:chunk的大小,四个字节
3、LineNumber:在androidmanifest文件中的行号,四个字节
4、unknown:未知区域,四个字节
5、prefix:命名空间的前缀(在字符串中的索引值),比如:android
6、命名空间的URI(在字符串中的索引值):比如:http://schemas.android.com/apk/res/android
public static void parseStartNamespaceChunk(byte[] byteSrc){
//获取ChunkTag
byte[] chunkTagByte = Utils.copyByte(byteSrc, 0, 4);
System.out.println(Utils.bytesToHexString(chunkTagByte));
//获取ChunkSize
byte[] chunkSizeByte = Utils.copyByte(byteSrc, 4, 4);
int chunkSize = Utils.byte2int(chunkSizeByte);
System.out.println("chunk size:"+chunkSize);
//解析行号
byte[] lineNumberByte = Utils.copyByte(byteSrc, 8, 4);
int lineNumber = Utils.byte2int(lineNumberByte);
System.out.println("line number:"+lineNumber);
//解析prefix(这里需要注意的是行号后面的四个字节为FFFF,过滤)
byte[] prefixByte = Utils.copyByte(byteSrc, 16, 4);
int prefixIndex = Utils.byte2int(prefixByte);
String prefix = stringContentList.get(prefixIndex);
System.out.println("prefix:"+prefixIndex);
System.out.println("prefix str:"+prefix);
//解析Uri
byte[] uriByte = Utils.copyByte(byteSrc, 20, 4);
int uriIndex = Utils.byte2int(uriByte);
String uri = stringContentList.get(uriIndex);
System.out.println("uri:"+uriIndex);
System.out.println("uri str:"+uri);
uriPrefixMap.put(uri, prefix);
prefixUriMap.put(prefix, uri);
}
这下看来有字符串池偏移就够了啊,得到全部str到ArrayList,这里就可以直接给出索引值了啊
第五、startTagChunk
这个Chunk主要是存放了AndroidManifest.xml中的标签信息,也是最核心的内容
1、chunktype:chunk的类型,固定四个字节:0x00100102
2、chunksize:chunk大小,固定四个字节
3、LineNumber:对应于AndroidManifest中的行号,四个字节
4、unknown:未知区域,四个字节
5、NamespaceUri:这个标签用到的命名空间的Uri,比如用到了android这个前缀,那么就需要用http://schemas.android.com/apk/res/android这个Uri去获取,四个字节
6、Name:标签名称(在字符串中的索引值),四个字节
7、Flags:标签的类型,四个字节,比如是开始标签还是结束标签等
8、AttributeCount:标签包含的属性个数,四个字节
9、ClassAtrribute:标签包含的类属性,四个字节
10,Atrributes:属性内容,每个属性算是一个Entry,这个Entry固定大小是大小为5的字节数组:
[Namespace,Uri,Name,ValueString,Data]
代码分析: