Android逆向(一) —— AndroidManifest.xml 二进制解析

Androidni逆向 —— AndroidManifest.xml 解析

做过 Android 开发的同学对 AndroidManifest.xml 文件肯定很熟悉,我们也叫它 清单文件 ,之所以称之为清单文件,因为它的确是应用的 “清单”。它包含了应用的包名,版本号,权限信息,所有的四大组件等信息。在逆向的过程中,通过 apk 的清单文件,我们可以了解应用的一些基本信息,程序的入口 Activity,注册的服务,广播,内容提供者等等。如果你尝试查看过 apk 中的 AndroidManifest.xml 文件,你会发现你看到的是一堆乱码,已经不是我们开发过程中编写的清单文件了。因为在打包过程中,清单文件被编译成了二进制数据存储在安装包中。这就需要我们了解 AndroidManifest.xml 的二进制文件结构,才可以读取到我们需要的信息。当然,已经有一些不错的开源工具可以读取编译后的清单文件,像 AXmlPrinter , apktool 等等。当然,正是由于这些工具都是开源的,一些开发者会利用其中的漏洞对清单文件进行特定的处理,使得无法通过这些工具反编译清单文件。如果我们了解其二进制文件结构的话,就可以对症下药了。

和之前解析 Class 文件结构一样,仍然手写代码进行解析,这样才能真正的了解其文件结构。通过前辈们的资料和 010 editor 的使用,其实已经大大降低了解析的难度。首先上一张看雪大神 MindMac 的神图(原图链接):

这张图真的很经典,不妨可以打印出来对照着进行分析。

这篇文章以 QQ 的清单文件为例进行分析,下载 QQ 的安装包解压即可拿到清单文件。解析文件格式的惯例,首先用 010 editor 打开,基本结构如下图所示:

运行的 Template 是 AndroidManifest.bt。结合上面的结构图,对 AndroidManifest.xml 的总体结构应该有了大概的了解。总体上按顺序分为四大部分:

  • Header : 包括文件魔数和文件大小
  • String Chunk : 字符串资源池
  • ResourceId Chunk : 系统资源 id 信息
  • XmlContent Chunk : 清单文件中的具体信息,其中包含了五个部分,Start Namespace ChunkEnd Namespace ChunkStart Tag ChunkEnd Tag ChunkText Chunk

二进制 AndroidManifest.xml 大致上就是按照这几部分顺序排列组成的,下面就逐一部分详细解析。在这之前还需要知道的一点是,清单文件是小端表示的,ARM 平台下大多数都是小端表示的。

Header

头部由 Magic NumberFile Size 组成,各自都是 4 字节。

  • Magic Number 始终为 0x0008003
  • File Size 表示文件总字节数,

对应的解析代码:

private void parseHeader() {
    try {
        Xml.nameSpaceMap.clear();
        String magicNumber = reader.readHexString(4);
        log("magic number: %s", magicNumber);

        int fileSize = reader.readInt();
        log("file size: %d", fileSize);
    } catch (IOException e) {
        e.printStackTrace();
        log("parse header error!");
    }
}
复制代码

解析结果:

magic number: 0x00080003
file size: 273444
复制代码

String Chunk

先来看一下 010 editor 中这一块的内容:

对应看雪神图的 StringChunk 模块:

String Chunk 主要存储了清单文件中的所有字符串信息。结构还是很清晰的。结合上图逐条解释一下:

  • Chunk Type : 4 bytes,始终为 0x001c0001,标记这是 String Chunk
  • Chunk Size : 4 bytes,表示 String Chunk 的大小
  • String Count : 4 bytes,表示字符串的数量
  • Style Count : 4 bytes,表示样式的数量
  • Unkown : 4 bytes,固定值,0x00000000
  • String Pool Offset : 字符串池的偏移量,注意不是相对于文件开始处,而是相对于 String Chunk 的开始处
  • Style Pool Offset : 样式池的偏移量,同上,也是相对于 String Chunk 而言
  • String Offsets : int数组,大小为 String Count,存储每个字符串在字符串池中的相对偏移量
  • Style Offets : 同上,也是 int 数组。总大小为 Style Count * 4 bytes
  • String Pool : 字符串池,存储了所有的字符串
  • Style Pool : 样式池,存储了所有的样式

字符串池中的字符串存储也有特定的格式,以 versionName 为例:

前两个字节表示字符串的字符数,注意一个字符是两个字节。如上图所示,字符数为 11 ,则后面 22 个字节表示字符串内容,最后以 0000 结尾。如此循环。

样式池在解析过程中一般都为空,样式数量也为 0。

了解了 String Chunk 的结构之后,解析就很简单了。直接上代码:

private void parseStringChunk() {
        try {
            String chunkType = reader.readHexString(4);
            log("chunk type: %s", chunkType);

            int chunkSize = reader.readInt();
            log("chunk size: %d", chunkSize);

            int stringCount = reader.readInt();
            log("string count: %d", stringCount);

            int styleCount = reader.readInt();
            log("style count: %d", styleCount);

            reader.skip(4);  // unknown

            int stringPoolOffset = reader.readInt();
            log("string pool offset: %d", stringPoolOffset);

            int stylePoolOffset = reader.readInt();
            log("style pool offset: %d", stylePoolOffset);

            // 每个 string 的偏移量
            List<Integer> stringPoolOffsets = new ArrayList<>(stringCount)
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值