美文网首页
Android 解析AndroidManifest.xml文件

Android 解析AndroidManifest.xml文件

作者: _凌浩雨 | 来源:发表于2018-06-28 17:33 被阅读106次

    1. 格式解析

    1). AndroidManifest.xml文件格式

    看雪大神--原图地址

    图1.png
    图中可以清晰的看出AndroidManifest.xml文件的格式,几个大的模块:
    • Magic Number 魔数
    • File Size 文件大小
    • String Chunk 字符串模块
    • ResourceId Chunk 资源模块
    • XmlContent Chunk XML模块
    2). 解析--主流程
    public class ParseMain {
        /**
         * 应用主入口
         */
        public static void main(String[] args) {
            // 读取AndroidManifest.xml文件为二进制数据
            byte[] byteSrc = Util.readXML("xml/AndroidManifest.xml");
            // 解析XML 头
            System.out.println("Parse XML Header -----------");
            ParseChunkUtil.parseXmlHeader(byteSrc);
            System.out.println();
            // 解析String Chunk
            System.out.println("Parse String Chunk -----------");
            ParseChunkUtil.parseStringChunk(byteSrc);
            System.out.println();
            // 解析Resource Chunk
            System.out.println("Parse Resource Chunk -----------");
            ParseChunkUtil.parseResourceChunk(byteSrc);
            System.out.println();
            // 解析XML内容
            System.out.println("Parse XML Content -----------");
            ParseChunkUtil.parseXmlContent(byteSrc);
            System.out.println();
            // 输出XML文件
            ParseChunkUtil.writeFormatXmlToFile();
        }
    
    }
    
    3). 头部
    • Magic Number 魔数:4个字节
    • File Size 文件大小:4个字节
        /** XML文件内容 */
        private static StringBuilder builder = new StringBuilder();
        /**
         * 解析xml的头部信息 
         *  1. Magic Number: 文件魔数,4个字节 
         *  2. File Size: 文件大小,4个字节
         * 
         * @param byteSrc
         */
        public static void parseXmlHeader(byte[] byteSrc) {
            // 1. Magic Number: 文件魔数,4个字节
            byte[] magic = Util.copyByte(byteSrc, 0, 4);
            System.out.println("magic number:" + Util.byteToHexString(magic));
            // 2. File Size: 文件大小,4个字节
            byte[] size = Util.copyByte(byteSrc, 4, 4);
            System.out.println("xml size:" + Util.byteToHexString(size));
    
            // 拼接文件内容
            builder.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
            builder.append("\n");
        }
    
    4). String Chunk 解析
        /** String Chunk 偏移量 */
        private static int STRING_CHUNK_OFFSET = 8;
        /** 字符串列表 */
        private static ArrayList<String> stringContentList;
        /** Resource Chunk 偏移量,动态计算 */
        private static int resourceChunkOffset;
        /**
         * 解析String Chunk 
         *  1. ChunkType:StringChunk的类型,固定四个字节:0x001C0001 
         *  2. ChunkSize:StringChunk的大小,四个字节 
         *  3. StringCount:StringChunk中字符串的个数,四个字节 
         *  4. StyleCount:StringChunk中样式的个数,四个字节,但是在实际解析过程中,这个值一直是0x00000000 
         *  5. Unknown:位置区域,四个字节,在解析的过程中,这里需要略过四个字节 
         *  6. StringPoolOffset:字符串池的偏移值,四个字节,这个偏移值是相对于StringChunk的头部位置 
         *  7. StylePoolOffset:样式池的偏移值,四个字节,这里没有Style,所以这个字段可忽略 
         *  8. StringOffsets:每个字符串的偏移值,所以他的大小应该是:StringCount*4个字节 
         *  9. SytleOffsets:每个样式的偏移值,所以他的大小应该是SytleCount*4个字节 
         *  10. String Pool 
         *  11. Style Pool
         * 
         * @param byteSrc 二进制数据
         */
        public static void parseStringChunk(byte[] byteSrc) {
            // 1. Chunk Type:String Chunk的类型,固定四个字节:0x001C0001
            byte[] type = Util.copyByte(byteSrc, STRING_CHUNK_OFFSET, 4);
            System.out.println("string chunktag:" + Util.byteToHexString(type));
    
            // 2. Chunk Size:String Chunk的大小,四个字节
            byte[] sizeByte = Util.copyByte(byteSrc, STRING_CHUNK_OFFSET + 4, 4);
            // 获取String Chunk大小
            int chunkSize = Util.byte2Int(sizeByte);
            System.out.println("chunk size:" + chunkSize);
    
            // 3. String Count:StringChunk中字符串的个数,四个字节
            byte[] stringCountByte = Util.copyByte(byteSrc, STRING_CHUNK_OFFSET + 8, 4);
            // 获取字符串个数
            int chunkStringCount = Util.byte2Int(stringCountByte);
            System.out.println("string count:" + chunkStringCount);
    
            // 4. Style Count:StringChunk中样式的个数,四个字节,但是在实际解析过程中,这个值一直是0x00000000
            // 5. Unknown:位置区域,四个字节,在解析的过程中,这里需要略过四个字节
            // 6. String Pool Offset:字符串池的偏移值,四个字节,这个偏移值是相对于StringChunk的头部位置
            // 创建指定字符串个数的列表
            stringContentList = new ArrayList<String>(chunkStringCount);
            // 7. Style Pool Offset:样式池的偏移值,四个字节,这里没有Style,所以这个字段可忽略
            // 8. String Offsets:每个字符串的偏移值,所以他的大小应该是:StringCount*4个字节
            byte[] stringOffsetByte = Util.copyByte(byteSrc, 28, 4);
            // 获取字符串内容开始位置
            int stringContentStart = 8 + Util.byte2Int(stringOffsetByte);
            System.out.println("start:" + stringContentStart);
            // 9. Sytle Offsets:每个样式的偏移值,所以他的大小应该是SytleCount*4个字节
            // 10. String Pool
            // 获取String Content
            byte[] stringContentByte = Util.copyByte(byteSrc, stringContentStart, chunkSize);
            // 获取内容开始位置, 字符串大小
            int start = 0, stringSize;
            // 字符串
            String text = "";
            // 循环结束条件,字符串列表中的数据个数等于字符串个数
            while (stringContentList.size() < chunkStringCount) {
                // 解析字符串时问题,编码:UTF-8和UTF-16,如果是UTF-8的话是以00结尾的,如果是UTF-16的话以00 00结尾的
                // 格式是:偏移值开始的两个字节是字符串的长度,接着是字符串的内容,后面跟着两个字符串的结束符00
                // 一个字符对应两个字节,所以要乘以2
                stringSize = Util.byte2Short(Util.copyByte(stringContentByte, start, 2)) * 2;
                System.out.println("string size : " + stringSize);
                // 获取字符串文本
                text = new String(Util.copyByte(stringContentByte, start + 2, stringSize + 2));
                System.out.println("text : " + Util.filterStringNull(text));
                // 字符串文本
                stringContentList.add(Util.filterStringNull(text));
                start += (2 + stringSize + 2);
            }
            // 11. Style Pool
    
            // 此处的代码是用来解析资源文件xml的
            /*int index = 0; 
            while(index < chunkStringCount){
                byte[] stringSizeByte = Util.copyByte(stringContentByte, start, 2);
                stringSize = (stringSizeByte[1] & 0x7F);
                System.out.println("string size:"+Util.byteToHexString(Util.int2Byte(stringSize)));
                if(stringSize != 0){ //这里注意是UTF-8编码的 String
                    text = ""; 
                    try{ 
                        text = new String(Util.copyByte(stringContentByte, start + 2,stringSize), "utf-8"); 
                    }catch(Exception e){
                        System.out.println("string encode error:"+e.toString());
                    }
                    stringContentList.add(text); 
                } else { 
                    stringContentList.add("");
                }
                start += (stringSize+3);
                index++;
            }
             */
            // 拼接Resource Chunk 偏移位置
            resourceChunkOffset = STRING_CHUNK_OFFSET + Util.byte2Int(sizeByte);
        }
    
    5). Resource Chunk 解析
        /** XMLContent Chunk 偏移量,动态计算 */
        private static int nextChunkOffset;
        /**
         * 解析Resource Chunk 1. Chunk Type: Resource Chunk 类型,4个字节,0x00080108 2.
         * Chunk Size: Resource Chunk 大小,4个字节 3. ResourceIds: 资源ID,
         * 大小是ResourceChunk大小除以4,减去头部的大小8个字节(ChunkType和ChunkSize)==(Chunk Size / 4 -
         * 2) * 4个字节
         * 
         * @param byteSrc
         *            二进制数据
         */
        public static void parseResourceChunk(byte[] byteSrc) {
            // 1. Chunk Type: Resource Chunk 类型,4个字节,0x00080108
            byte[] typeByte = Util.copyByte(byteSrc, resourceChunkOffset, 4);
            System.out.println("type: " + Util.byteToHexString(typeByte));
            // 2. Chunk Size: Resource Chunk 大小,4个字节
            byte[] sizeByte = Util.copyByte(byteSrc, resourceChunkOffset + 4, 4);
            // 文件大小
            int size = Util.byte2Int(sizeByte);
            System.out.println("size: " + size);
            // 3. ResourceIds: 资源ID,
            // 大小是ResourceChunk大小除以4,减去头部的大小8个字节(ChunkType和ChunkSize)==(Chunk Size /
            // 4 - 2) * 4个字节
            // chunk size包含chunk type 与 chunk size两个数组的字节,所以要剔除
            byte[] resourceIdByte = Util.copyByte(byteSrc, resourceChunkOffset + 8, size - 8);
            // 创建资源列表
            ArrayList<Integer> resourceIdList = new ArrayList<>(resourceIdByte.length / 4);
            // 遍历获取资源ID
            for (int i = 0; i < resourceIdByte.length; i += 4) {
                // 获取资源ID
                int resId = Util.byte2Int(Util.copyByte(resourceIdByte, i, 4));
                System.out.println("id: " + resId + ", hex: " + Util.byteToHexString(Util.copyByte(resourceIdByte, i, 4)));
                // 将资源ID添加到列表
                resourceIdList.add(resId);
            }
            // 计算XMLContent Chunk 偏移量
            nextChunkOffset = resourceChunkOffset + size;
        }
    
    6). XmlContent Chunk解析
        /**KEY为uri,VALUE为prefix的map*/
        private static Map<String,String> uriPrefixMap = new HashMap<>();
        /**KEY为prefix,VALUE为uri的map*/
        private static Map<String, String> prefixUriMap = new HashMap<>();
        /**
         * 开始解析XML的正文内容 
         *  1. type: 类型,4个字节 
         *  2. size: 文件大小,4个字节 
         *  3. start namaspace 
         *  4. start tag 
         *  5. end tag 
         *  6. end namespace
         * 
         * @param byteSrc 二进制数据
         */
        public static void parseXmlContent(byte[] byteSrc) {
            // 判断是否到结尾处了
            while (!isEnd(byteSrc.length)) {
                // 获取类型
                byte[] typeByte = Util.copyByte(byteSrc, nextChunkOffset, 4);
                // 获取节点类型
                int type = Util.byte2Int(typeByte);
                System.out.println("chunk type: " + Util.byteToHexString(typeByte));
                // 文件大小
                byte[] sizeByte = Util.copyByte(byteSrc, nextChunkOffset + 4, 4);
                // 获取文件大小
                int size = Util.byte2Int(sizeByte);
                System.out.println("size: " + size);
                switch (type) {
                    case ChunkMagicNumber.CHUNK_START_NS:
                        System.out.println("parse start namespace");
                        parseStartNameSpaceChunk(Util.copyByte(byteSrc, nextChunkOffset, size));
                        break;
                    case ChunkMagicNumber.CHUNK_START_TAG:
                        System.out.println("parse start tag");
                        parseStartTagChunk(Util.copyByte(byteSrc, nextChunkOffset, size));
                        break;
                    case ChunkMagicNumber.CHUNK_END_TAG:
                        System.out.println("parse end tag");
                        parseEndTagChunk(Util.copyByte(byteSrc, nextChunkOffset, size));
                        break;
                    case ChunkMagicNumber.CHUNK_END_NS:
                        System.out.println("parse end namespace");
                        parseEndNameSpaceChunk(Util.copyByte(byteSrc, nextChunkOffset, size));
                        break;
                    default:
                        break;
                }
                // 赋值
                nextChunkOffset += size;
            }
            System.out.println("parse xml: " + builder.toString());
        }
    
    7). Start Namespace Chunk解析
        /**
         * 解析命名空间 开始
         *  1. Chunk Type:类型,4个字节
         *  2. Chunk Size: 大小,4个字节
         *  3. Line Number: 在AndroidManifest.xml文件中的行号,4个字节
         *  4. Unkonwn(0xFFFFFFFF): 未知区域,4个字节
         *  5. Prefix: 命名空间前缀,4个字节。如:android
         *  6. Uri: 命令空间的Uri。如:http://schemas.android.com/apk/res/android
         * @param byteSrc 字节数组
         */
        private static void parseStartNameSpaceChunk(byte[] byteSrc) {
            // 1. Chunk Type:类型,4个字节
            byte[] typeByte = Util.copyByte(byteSrc, 0, 4);
            System.out.println("type: " + Util.byteToHexString(typeByte));
            // 2. Chunk Size: 大小,4个字节
            byte[] sizeByte = Util.copyByte(byteSrc, 4, 4);
            // 获取大小
            int size = Util.byte2Int(sizeByte);
            System.out.println("size: " + size);
            // 3. Line Number: 在AndroidManifest.xml文件中的行号,4个字节
            byte[] lineNumberByte = Util.copyByte(byteSrc, 8, 4);
            // 获取行号
            int lineNumber = Util.byte2Int(lineNumberByte);
            System.out.println("line number: " + lineNumber);
            // 4. Unkonwn(0xFFFFFFFF): 未知区域,4个字节
            // 5. Prefix: 命名空间前缀,4个字节。如:android
            byte[] prefixByte = Util.copyByte(byteSrc, 16, 4);
            // 获取前缀标识
            int prefixIndex = Util.byte2Int(prefixByte);
            // 获取前缀
            String prefix = stringContentList.get(prefixIndex);
            System.out.println("prefix: " + prefixIndex + ", prefix str: " + prefix);
            // 6. Uri: 命令空间的Uri。如:http://schemas.android.com/apk/res/android
            byte[] uriByte = Util.copyByte(byteSrc, 20, 4);
            // 获取uri标识
            int uriIndex = Util.byte2Int(uriByte);
            // 获取uri
            String uri = stringContentList.get(uriIndex);
            System.out.println("uri: " + uriIndex + ", uri str: " + uri);
            
            // 存入Map
            uriPrefixMap.put(uri, prefix);
            prefixUriMap.put(prefix, uri);      
        }
    
    8). Start Tag Chunk 解析
        /**
         * 解析TAG 开始
         *  1. Chunk Type: 类型,4个字节:0x00100102
         *  2. Chunk Size: 大小,4个字节
         *  3. Line Number: AndroidManifesta.xml文件中的行号,4个字节
         *  4. Unknown: 未知区域,4个字节
         *  5. NamespaceUri: 标签用到的命名空间的Uri,比如用到了android这个前缀,那么就需要用http://schemas.android.com/apk/res/android这个Uri去获取,四个字节
         *  6. Name: 标签名称(字符串中的索引值),4个字节
         *  7. Flags: 标签的类型,4个字节,开始/结束
         *  8. AttributeCount: 标签包含的属性个数,4个字节
         *  9. ClassAttribute: 标签包含的属性,4个字节
         *  10. Attributes: 属性内容,每个属性算是一个Entry,固定大小为5的字节数组
         *  [Namespace,Uri,Name,ValueString,Data],我们在解析的时候需要注意第四个值,要做一次处理:需要右移24位。所以这个字段的大小是:属性个数*5*4个字节
         * @param byteSrc 字节数组
         */
        private static void parseStartTagChunk(byte[] byteSrc) {
            // 1. Chunk Type: 类型,4个字节:0x00100102
            byte[] typeByte = Util.copyByte(byteSrc, 0, 4);
            System.out.println("type: " + Util.byteToHexString(typeByte));
            // 2. Chunk Size: 大小,4个字节
            byte[] sizeByte = Util.copyByte(byteSrc, 4, 4);
            // 获取大小
            int size = Util.byte2Int(sizeByte);
            System.out.println("size: " + size);
            // 3. Line Number: AndroidManifesta.xml文件中的行号,4个字节
            byte[] lineNumberByte = Util.copyByte(byteSrc, 8, 4);
            // 获取行号
            int lineNumber = Util.byte2Int(lineNumberByte);
            System.out.println("line number: " + lineNumber);
            // 4. Unknown: 未知区域,4个字节
            byte[] prefixByte = Util.copyByte(byteSrc, 8, 4);
            // 获取前缀标识
            int prefixIndex = Util.byte2Int(prefixByte);
            // 这里可能会返回-1, 如果返回-1的话,那就说明没有前缀
            if (-1 != prefixIndex && prefixIndex < stringContentList.size()) {
                System.out.println("prefix: " + prefixIndex);
                System.out.println("prefix str: " + stringContentList.get(prefixIndex));
            } else {
                System.out.println("prefix null");
            }
            // 5. NamespaceUri: 标签用到的命名空间的Uri,比如用到了android这个前缀,那么就需要用http://schemas.android.com/apk/res/android这个Uri去获取,四个字节
            byte[] uriByte = Util.copyByte(byteSrc, 16, 4);
            // 获取uri标识
            int uriIndex = Util.byte2Int(uriByte);
            // 如果前缀大的话,说明uri不存在
            if (-1 != uriIndex && prefixIndex < stringContentList.size()) {
                System.out.println("uri: " + uriIndex);
                System.out.println("uri str: " + stringContentList.get(uriIndex));
            } else {
                System.out.println("uri null");
            }
            // 6. Name: 标签名称(字符串中的索引值),4个字节
            byte[] tagNameByte = Util.copyByte(byteSrc, 20, 4);
            System.out.println(Util.byteToHexString(tagNameByte));
            // 获取标签名称标识
            int nameIndex = Util.byte2Int(tagNameByte);
            // 获取标签名称
            String name = stringContentList.get(nameIndex);
            if (-1 != nameIndex) {
                System.out.println("tag name index: " + nameIndex);
                System.out.println("tag name str: " + name);
            } else {
                System.out.println("tag name null");
            }
            // 7. Flags: 标签的类型,4个字节,开始/结束
            // 8. AttributeCount: 标签包含的属性个数,4个字节
            byte[] attrCountByte = Util.copyByte(byteSrc, 28, 4);
            // 获取属性个数
            int attrCount = Util.byte2Int(attrCountByte);
            System.out.println("attr count:" + attrCount);
            // 9. ClassAttribute: 标签包含的属性,4个字节
            // 10. Attributes: 属性内容,每个属性算是一个Entry,固定大小为5的字节数组
            // 创建指定长度的属性列表
            ArrayList<AttributeData> attrList = new ArrayList<>(attrCount);
            // 遍历
            for (int i = 0; i < attrCount; i++) {
                // 五个属性
                Integer[] values = new Integer[5];
                // 创建属性对象
                AttributeData attData = new AttributeData();
                // 遍历赋值
                for (int j = 0; j < 5; j++) {
                    // 5个属性,每个属性占4个字节,所以是i*5*4
                    int value = Util.byte2Int(Util.copyByte(byteSrc, 36 + i * 5 * 4 + j * 4, 4));
                    switch (j) {
                    case 0:
                        attData.nameSpaceUri = value;
                        break;
                    case 1:
                        attData.name = value;
                        break;
                    case 2:
                        attData.valueString = value;
                        break;
                    case 3:
                        // 获取到的type要右移24位
                        attData.type = (value >> 24);                   
                        break;
                    case 4:
                        attData.data = value;
                        break;
                    default:
                        break;
                    }
                    values[j] = value;
                }
                // 添加到属性列表
                attrList.add(attData);
            }
            // 构造XML结构
            builder.append(createStartTagXml(name, attrList));
        }
    
    9). End Tag Chunk解析
        /**
         * 解析TAG 结束
         *  1. Chunk Type(0x00100101): 4个字节
         *  2. Chunk Size: 4个字节
         *  3. Line Number: 4个字节
         *  4. Unknown(0xFFFFFFFF):4个字节
         *  5. Name: 4个字节
         *  6. Unknown: 4个字节
         *  7. Unknown: 4个字节
         * @param byteSrc 字节数组
         */
        private static void parseEndTagChunk(byte[] byteSrc) {
            // 1. Chunk Type(0x00100101): 4个字节
            byte[] tyepByte = Util.copyByte(byteSrc, 0, 4);
            System.out.println("type: " + Util.byteToHexString(tyepByte));
            // 2. Chunk Size: 4个字节
            byte[] sizeByte = Util.copyByte(byteSrc, 4, 4);
            // 获取长度
            int size = Util.byte2Int(sizeByte);
            System.out.println("size: " + size);
            // 3. Line Number: 4个字节
            byte[] lineNumberByte = Util.copyByte(byteSrc, 8, 4);
            // 获取行号
            int lineNumber = Util.byte2Int(lineNumberByte);
            System.out.println("line number:" + lineNumber);
            
            // 解析prefix
            byte[] prefixByte = Util.copyByte(byteSrc, 8, 4);
            // 获取前缀标识
            int prefixIndex = Util.byte2Int(prefixByte);
            // 可能返回-1,如果返回-1,则说明没有prefix
            if (-1 != prefixIndex && prefixIndex < stringContentList.size()) {
                System.out.println("prefix: " + prefixIndex);
                System.out.println("prefix str: " + stringContentList.get(prefixIndex));
            } else {
                System.out.println("prefix null.");
            }
            // 解析Uri
            byte[] uriByte = Util.copyByte(byteSrc, 16, 4);
            // 获取uri标识
            int uriIndex = Util.byte2Int(uriByte);
            // 如果前缀大的话,说明uri不存在
            if (-1 != uriIndex && prefixIndex < stringContentList.size()) {
                System.out.println("uri: " + uriIndex);
                System.out.println("uri str: " + stringContentList.get(uriIndex));
            } else {
                System.out.println("uri null");
            }
            
            // 4. Unknown(0xFFFFFFFF):4个字节
            // 5. Name: 4个字节
            byte[] tagNameByte = Util.copyByte(byteSrc, 20, 4);
            System.out.println("name: " + Util.byteToHexString(tagNameByte));
            // 获取tag名标识
            int tagNameIndex = Util.byte2Int(tagNameByte);
            // 获取tag名
            String tagName = stringContentList.get(tagNameIndex);
            if (-1 != tagNameIndex) {
                System.out.println("tag name index: " + tagNameIndex);
                System.out.println("tag name str: " + tagName);
            } else {
                System.out.println("tag name null");
            }
                
            builder.append(createEndTagXml(tagName));
        }
    

    2. xml-apk-parser解析

    1). 引包
    图2.png
    2). 使用
    public class APKParserDemo {
        /**指定要解析的文件*/
        private static final String DEFAULT_XML = "AndroidManifest.xml";
        public static void main(String[] args) {
            String apkPath = "xml/source.apk";
            String content = getManifestXMLFromAPK(apkPath);
            writeFormatXmlToFile(content);
        }
        
        /**
         * 获取AndroidManifest.xml文件
         * @param apkPath apk路径
         */
        private static String getManifestXMLFromAPK(String apkPath) {
            ZipFile zipFile = null;
            StringBuilder xmlBuilder = new StringBuilder();
            try {
                // apk文件
                File apkFile = new File(apkPath);
                // 获取压缩文件
                zipFile = new ZipFile(apkFile, ZipFile.OPEN_READ);
                // 获取指定文件
                ZipEntry entry = zipFile.getEntry(DEFAULT_XML);
                
                // 创建XML文件资源解析器
                AXmlResourceParser parser = new AXmlResourceParser();
                // 打开文件
                parser.open(zipFile.getInputStream(entry));
                
                StringBuilder sb = new StringBuilder();
                final String indentStep = " ";
                int type;
                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
                    switch (type) {
                    case XmlPullParser.START_DOCUMENT:
                        log(xmlBuilder, "<?xml version=\"1.0\" encoding=\"utf-8\"?>");
                        break;
                    case XmlPullParser.START_TAG:
                        log(false, xmlBuilder, "%s<%s%s", sb, getNamespacePrefix(parser.getPrefix()), parser.getName());
                        sb.append(indentStep);
                        
                        int nameSpaceCountBefore = parser.getNamespaceCount(parser.getDepth() - 1);
                        int nameSpaceCount = parser.getNamespaceCount(parser.getDepth());
                        
                        for (int i = nameSpaceCountBefore; i != nameSpaceCount; i++) {
                            log(xmlBuilder, "%sxmlns:%s=\"%s\"", 
                                    i == nameSpaceCountBefore ? "  " : sb, 
                                            parser.getNamespacePrefix(i), parser.getNamespaceUri(i));
                        }
                        
                        for (int i = 0, size = parser.getAttributeCount(); i != size; i++) {
                            log(false, xmlBuilder, "%s%s%s=\"%s\"", " ",
                                    getNamespacePrefix(parser.getAttributePrefix(i)),
                                    parser.getAttributeName(i),
                                    getAttributeValue(parser, i));
                        }
                        log(xmlBuilder, ">");
                        
                        break;
                    case XmlPullParser.END_TAG:
                        sb.setLength(sb.length() - indentStep.length());
                        log(xmlBuilder, "%s</%s%s>", sb,
                                getNamespacePrefix(parser.getPrefix()),
                                parser.getName());
                        break;
                    case XmlPullParser.TEXT:
                        log(xmlBuilder, "%s%s", sb, parser.getText());
                        break;
                    default:
                        break;
                    }
                    
                }
                parser.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return xmlBuilder.toString();
        }
        
        /**
         * 把构造好的XML写入文件中
         */
        public static void writeFormatXmlToFile(String content) {
            FileWriter fw = null;
            try {
                fw = new FileWriter("xml/ApkParser_format.xml");
                fw.write(content);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        
        /**
         * 获取属性
         * @param parser 解析器
         * @param index 标识
         * @return
         */
        private static String getAttributeValue(AXmlResourceParser parser,int index) {
            int type=parser.getAttributeValueType(index);
            int data=parser.getAttributeValueData(index);
            if (type==TypedValue.TYPE_STRING) {
                return parser.getAttributeValue(index);
            }
            if (type==TypedValue.TYPE_ATTRIBUTE) {
                return String.format("?%s%08X",getPackage(data),data);
            }
            if (type==TypedValue.TYPE_REFERENCE) {
                return String.format("@%s%08X",getPackage(data),data);
            }
            if (type==TypedValue.TYPE_FLOAT) {
                return String.valueOf(Float.intBitsToFloat(data));
            }
            if (type==TypedValue.TYPE_INT_HEX) {
                return String.format("0x%08X",data);
            }
            if (type==TypedValue.TYPE_INT_BOOLEAN) {
                return data!=0?"true":"false";
            }
            if (type==TypedValue.TYPE_DIMENSION) {
                return Float.toString(complexToFloat(data))+
                    DIMENSION_UNITS[data & TypedValue.COMPLEX_UNIT_MASK];
            }
            if (type==TypedValue.TYPE_FRACTION) {
                return Float.toString(complexToFloat(data))+
                    FRACTION_UNITS[data & TypedValue.COMPLEX_UNIT_MASK];
            }
            if (type>=TypedValue.TYPE_FIRST_COLOR_INT && type<=TypedValue.TYPE_LAST_COLOR_INT) {
                return String.format("#%08X",data);
            }
            if (type>=TypedValue.TYPE_FIRST_INT && type<=TypedValue.TYPE_LAST_INT) {
                return String.valueOf(data);
            }
            return String.format("<0x%X, type 0x%02X>",data,type);
        }
        
        private static String getPackage(int id) {
            if (id>>>24==1) {
                return "android:";
            }
            return "";
        }
        
        /**
         * 获取前缀
         * @param prefix
         * @return
         */
        private static String getNamespacePrefix(String prefix) {
            if (prefix==null || prefix.length()==0) {
                return "";
            }
            return prefix+":";
        }
        
        /**
         * 拼接字符串
         * @param xmlSb 字符串拼接器
         * @param format 格式化
         * @param arguments 参数
         */
        private static void log(StringBuilder xmlSb,String format,Object...arguments) {
            log(true,xmlSb, format, arguments);
        }
        
        /**
         * 拼接字符串
         * @param newLine 是否为新行
         * @param xmlSb 字符串拼接器
         * @param format 格式化
         * @param arguments 参数
         */
        private static void log(boolean newLine,StringBuilder xmlSb,String format,Object...arguments) {
            xmlSb.append(String.format(format, arguments));
            if(newLine) xmlSb.append("\n");
        }
        
        // ILLEGAL STUFF, DONT LOOK :)
        
        public static float complexToFloat(int complex) {
            return (float)(complex & 0xFFFFFF00)*RADIX_MULTS[(complex>>4) & 3];
        }
        
        private static final float RADIX_MULTS[]={
            0.00390625F,3.051758E-005F,1.192093E-007F,4.656613E-010F
        };
        private static final String DIMENSION_UNITS[]={
            "px","dip","sp","pt","in","mm","",""
        };
        private static final String FRACTION_UNITS[]={
            "%","%p","","","","","",""
        };
    }
    

    参考文章

    Android逆向之旅---解析编译之后的AndroidManifest文件格式

    代码下载

    相关文章

      网友评论

          本文标题:Android 解析AndroidManifest.xml文件

          本文链接:https://www.haomeiwen.com/subject/jgtnyftx.html