You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C++中解析二进制XML格式的AndroidManifest.xml?

在C++中解析二进制AndroidManifest.xml的实现方案

我完全懂你的困扰——二进制格式的AndroidManifest.xml根本没法用pugixml这类常规XML解析器搞定,而且AAPT2在Visual Studio环境下的构建依赖一堆Android生态的东西,集成起来麻烦得要死。下面我给你一个轻量级的纯C++实现,不需要任何第三方库,直接就能在VS里编译运行,专门用来解析这种二进制Manifest文件。

实现思路

二进制AndroidManifest.xml的核心结构包括:

  • 文件头部:存储魔数、版本、各个数据块的偏移和大小
  • 字符串池:所有XML中的字符串(标签名、属性名、属性值等)都存在这里,用索引引用
  • XML节点块:存储元素、文本、结束标签等节点信息

下面的代码实现了最核心的解析逻辑,能把二进制Manifest转换成可读的XML格式输出。

完整代码示例

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <cstdint>
#include <locale>
#include <codecvt>

// 二进制XML文件头部结构,对应Android的BinaryXmlHeader
struct BinaryXmlHeader {
    uint32_t magic;          // 固定魔数: 0x00080003
    uint32_t version;        // 二进制XML版本
    uint32_t stringPoolSize; // 字符串池总字节数
    uint32_t stringPoolOffset; // 字符串池在文件中的偏移
    uint32_t resourceIdsSize; // 资源ID池大小(可选)
    uint32_t resourceIdsOffset; // 资源ID池偏移(可选)
    uint32_t xmlNodesSize;   // XML节点块总字节数
    uint32_t xmlNodesOffset; // XML节点块在文件中的偏移
};

// XML节点类型枚举,对应Android的XmlNodeType
enum XmlNodeType {
    START_ELEMENT = 0x0102, // 元素开始标签
    END_ELEMENT = 0x0103,   // 元素结束标签
    TEXT = 0x0104           // 文本节点
};

// 解析字符串池,返回所有字符串的列表
std::vector<std::string> parseStringPool(std::ifstream& file, uint32_t offset, uint32_t size) {
    std::vector<std::string> strings;
    file.seekg(offset);

    uint32_t stringCount, styleCount, flags, stringsStart, stylesStart;
    file.read(reinterpret_cast<char*>(&stringCount), sizeof(stringCount));
    file.read(reinterpret_cast<char*>(&styleCount), sizeof(styleCount));
    file.read(reinterpret_cast<char*>(&flags), sizeof(flags));
    file.read(reinterpret_cast<char*>(&stringsStart), sizeof(stringsStart));
    file.read(reinterpret_cast<char*>(&stylesStart), sizeof(stylesStart));

    // 读取每个字符串的偏移量
    std::vector<uint32_t> stringOffsets(stringCount);
    for (uint32_t i = 0; i < stringCount; ++i) {
        file.read(reinterpret_cast<char*>(&stringOffsets[i]), sizeof(stringOffsets[i]));
    }

    // 逐个读取字符串,处理UTF-16到UTF-8的转换
    std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter;
    for (uint32_t i = 0; i < stringCount; ++i) {
        file.seekg(offset + stringsStart + stringOffsets[i]);
        uint16_t len;
        file.read(reinterpret_cast<char*>(&len), sizeof(len));
        
        bool isUtf16 = (len & 0x8000) != 0;
        len &= 0x7FFF; // 实际字符串长度

        std::string str;
        if (isUtf16) {
            std::u16string u16str(len, '\0');
            file.read(reinterpret_cast<char*>(&u16str[0]), len * 2);
            str = converter.to_bytes(u16str);
        } else {
            str.resize(len);
            file.read(&str[0], len);
        }
        strings.push_back(str);
    }

    return strings;
}

// 递归解析XML节点并输出可读格式
void parseXmlNodes(std::ifstream& file, uint32_t offset, uint32_t size, const std::vector<std::string>& stringPool, int indent = 0) {
    file.seekg(offset);
    uint32_t currentOffset = 0;

    while (currentOffset < size) {
        uint16_t nodeType, headerSize;
        uint32_t lineNumber, commentRef;
        
        // 读取节点头部
        file.read(reinterpret_cast<char*>(&nodeType), sizeof(nodeType));
        file.read(reinterpret_cast<char*>(&headerSize), sizeof(headerSize));
        file.read(reinterpret_cast<char*>(&lineNumber), sizeof(lineNumber));
        file.read(reinterpret_cast<char*>(&commentRef), sizeof(commentRef));

        currentOffset += headerSize;

        if (nodeType == START_ELEMENT) {
            uint32_t namespaceRef, nameRef, attributeCount, classAttributeRef;
            file.read(reinterpret_cast<char*>(&namespaceRef), sizeof(namespaceRef));
            file.read(reinterpret_cast<char*>(&nameRef), sizeof(nameRef));
            file.read(reinterpret_cast<char*>(&attributeCount), sizeof(attributeCount));
            file.read(reinterpret_cast<char*>(&classAttributeRef), sizeof(classAttributeRef));

            // 输出缩进和开始标签
            std::cout << std::string(indent, ' ') << "<" << stringPool[nameRef];

            // 解析并输出属性
            for (uint32_t i = 0; i < attributeCount; ++i) {
                uint32_t attrNamespaceRef, attrNameRef, attrValueRef, attrResourceId, attrValueType, attrValueData;
                file.read(reinterpret_cast<char*>(&attrNamespaceRef), sizeof(attrNamespaceRef));
                file.read(reinterpret_cast<char*>(&attrNameRef), sizeof(attrNameRef));
                file.read(reinterpret_cast<char*>(&attrValueRef), sizeof(attrValueRef));
                file.read(reinterpret_cast<char*>(&attrResourceId), sizeof(attrResourceId));
                file.read(reinterpret_cast<char*>(&attrValueType), sizeof(attrValueType));
                file.read(reinterpret_cast<char*>(&attrValueData), sizeof(attrValueData));

                std::cout << " " << stringPool[attrNameRef] << "=\"" << stringPool[attrValueRef] << "\"";
            }

            std::cout << ">" << std::endl;

            // 递归解析子节点
            uint32_t childSize = size - currentOffset;
            parseXmlNodes(file, offset + currentOffset, childSize, stringPool, indent + 2);
            currentOffset += childSize;
        } else if (nodeType == END_ELEMENT) {
            uint32_t namespaceRef, nameRef;
            file.read(reinterpret_cast<char*>(&namespaceRef), sizeof(namespaceRef));
            file.read(reinterpret_cast<char*>(&nameRef), sizeof(nameRef));

            std::cout << std::string(indent, ' ') << "</" << stringPool[nameRef] << ">" << std::endl;
        } else if (nodeType == TEXT) {
            uint32_t textRef;
            file.read(reinterpret_cast<char*>(&textRef), sizeof(textRef));
            std::cout << std::string(indent, ' ') << stringPool[textRef] << std::endl;
        }

        // 对齐到4字节边界(二进制XML的要求)
        while (currentOffset % 4 != 0) {
            file.seekg(1, std::ios::cur);
            currentOffset++;
        }
    }
}

int main() {
    // 替换成你的AndroidManifest.xml文件路径
    const std::string filePath = "AndroidManifest.xml";
    std::ifstream file(filePath, std::ios::binary);

    if (!file.is_open()) {
        std::cerr << "错误:无法打开文件 " << filePath << std::endl;
        return EXIT_FAILURE;
    }

    // 读取并验证文件头部
    BinaryXmlHeader header;
    file.read(reinterpret_cast<char*>(&header), sizeof(header));
    if (header.magic != 0x00080003) {
        std::cerr << "错误:不是有效的二进制AndroidManifest.xml文件" << std::endl;
        return EXIT_FAILURE;
    }

    // 解析字符串池和XML节点
    auto stringPool = parseStringPool(file, header.stringPoolOffset, header.stringPoolSize);
    parseXmlNodes(file, header.xmlNodesOffset, header.xmlNodesSize, stringPool);

    file.close();
    return EXIT_SUCCESS;
}

使用说明

  1. 编译运行:在Visual Studio中新建一个空的C控制台项目,把上面的代码粘贴进去,设置C标准为C++17或更高(因为用到了std::codecvt_utf8_utf16)。
  2. 路径替换:把主函数中的filePath替换成你的二进制AndroidManifest.xml文件的实际路径。
  3. 输出结果:运行后会在控制台输出格式化后的可读XML内容。

扩展建议

  • 如果需要解析资源ID或复杂属性值(比如颜色、尺寸等),可以扩展代码处理resourceIds块和属性的valueType/valueData字段。
  • 如果需要更完整的功能,可以参考Android源码中libandroidfw库的BinaryXmlParser类,移植更多解析逻辑。

内容的提问来源于stack exchange,提问作者Hamidreza

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 20:07:37