Android逆向:二进制xml文件解析 之 Start Tag Chunk

目录

1、xml结构

2、Start Tag Chunk Header

3、Start Tag Chunk Body

4、Atrributes

5、总结


在Android中,xml文件经过编译后都是不可读的二进制文件。今天我们来解析一下这个二进制文件的内容,看看如何与我们的源码进行对应。

1、xml结构

在Android中,xml文件经过编译后都是不可读的二进制文件。它的结构如图:

简单说一下大概的结构:

  • Header Chunk:8个byte,type+headersize+fileszie
  • String Chunk:字符串池
  • ResourceId Chunk: 系统的资源id,即0x01xxxxxx这种
  • XMLContent Chunk:xml内容的结构

其中XMLContent Chunk是主要部分,是一个集合,从图中可以看到它包含多种类型的数据结构,其中Start Namespace Chunk、End Namespace Chunk和Text Chunk这三种类型数据有可能不存在。而Start Tag Chunk和End Tag Chunk一定存在。

Start Tag Chunk和End Tag Chunk就是一个xml tag,如<View />或<View ></>,所以它们是成对出现的。

但是要注意不是一个Start Tag Chunk后面一定跟着一个End Tag Chunk,比如嵌套<Layout ><View /></Layout>,这种情况则是[Start Tag Chunk + Start Tag Chunk + End Tag Chunk + End Tag Chunk]。

所以End Tag Chunk中基本没有什么信息,大部分信息都在Start Tag Chunk中,这也是我们这篇文章讨论的重点。

2、Start Tag Chunk Header

下面是Start Tag Chunk的一个实例:

02011000 74000000 00000000 FFFFFFFF FFFFFFFF 09000000 14001400 04000100 

00000000 0D000000 03000000 FFFFFFFF 08000001 3800077F 0D000000 05000000 

FFFFFFFF 08000010 FEFFFFFF 0D000000 06000000 FFFFFFFF 08000010 FEFFFFFF 

0D000000 07000000 FFFFFFFF 08000001 5300067F

下面我们一步步解析它。

从上面的图中可以清楚的看到Start Tag Chunk的结构。不过没展示的是Start Tag Chunk包含header和body结构。

Header的结构如下:

struct ResXMLTree_node
    {
    struct ResChunk_header header; 
    uint32_t lineNumber;
    struct ResStringPool_ref comment;
};

这里type(2byte)+ headersize(2byte,header大小固定是16byte)+ chunksize(4byte)+ lineNum(4byte)+ comment(4byte)

结合示例来看,其中橙色部分02011000 74000000 00000000 FFFFFFFF便是header。其中:

0201是type;1000是headersize,考虑字节就是16;74000000是chunksize,是116byte,除去header的16byte,body的大小应该是100byte,后面会验证;00000000是lineNum;FFFFFFFF是没有comment的默认值。

3、Start Tag Chunk Body

header后面就是body,body主要分两部分,第一部分是有关tag的基本信息,第二部分则是Atrributes。

第一部分结构如下:

struct ResXMLTree_attrExt
    {
    struct ResStringPool_ref ns; 
    struct ResStringPool_ref name; 
    uint16_t attributeStart; 
    uint16_t attributeSize; 
    uint16_t attributeCount; 
    uint16_t idIndex;
    uint16_t classIndex;
    uint16_t styleIndex;
};

其中

  • ns: 命名空间,在字符串池中的索引。如果没有就是 0xFFFFFFFF 。(4byte)
  • name: 元素名称,在字符串池中的索引 。(4byte)
  • AttributeStart: 属性段的相对body的偏移,由于这部分大小固定,所以这个偏移也是固定的20byte 。(2byte)
  • AttributeSize: 每个属性的大小,固定是20byte 。(2byte)
  • AttributeCount: 属性的总数 。(2byte)
  • idIndex: 第几个属性表示id 。(2byte)
  • classIndex: 第几个属性表示class 。(2byte)
  • styleIndex: 第几个属性表示style 。(2byte)

结合上面的例子来看,其中绿色部分FFFFFFFF 09000000 14001400 04000100 00000000便是这一部分,其中:

FFFFFFFF表示没有命名空间;09000000表示元素名是字符串池第10个;第一个1400是偏移量,即20byte;第二个1400表示每个属性大小是20byte;0400表示一共有4个属性;0100表示第一个属性是id;00000000表示没有class和style。

这个tag实际是下面的代码:

    <ImageView
        android:id="@+id/image1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/fio" />

可以看到它一共有四个属性,而第一个属性是id。如果我们去字符串池中找,会发现第10字符串是ImageView。

注意:我们可以验证上面的body大小,这部分大小固定是20byte,而每个属性大小是20byte,一共四个,所以body的大小正好是上面说过的100byte。

4、Atrributes

接下就是Atrributes,Atrributes是一系列Entry结构的集合,每个Entry的结构如下:

struct ResXMLTree_attribute{
     struct ResStringPool_ref ns;

     struct ResStringPool_ref name;

     struct ResStringPool_ref rawValue; 

     struct Res_value typedValue;

};

其中

  • Ns: 属性的命名空间,在字符串池中的索引,比如“xmlns:android="http://schemas.android.com/apk/res/android"这种。(4byte)
  • Name:  属性的名称,在字符串池中的索引。(4byte)
  • rawValue: 属性的值的原始 XML 文字中字符串在 string indices 中的索引。(4byte)
  • TypeValue: 是一个表示属性值的结构体。(8byte)

TypeValue的结构如下

struct Res_value {

        uint16_t size;
        uint8_t res0;
        uint8_t dataType;
        uint32_t data;
} 

其中

  • Size:表示 Res_value 的大小。可以看到这里固定是8byte大小,所以size固定是0x80。(2byte)
  • Res0: 始终为 0 (1byte)
  • dataType: 值的类型。(1byte)
  • data:属性值。(4byte)

其中dataType类型如下:

public static final int TYPE_NULL = 0x00;
public static final int TYPE_REFERENCE = 0x01;
public static final int TYPE_ATTRIBUTE = 0x02;
public static final int TYPE_STRING = 0x03;
public static final int TYPE_FLOAT = 0x04;
public static final int TYPE_DIMENSION = 0x05;
public static final int TYPE_FRACTION = 0x06;
public static final int TYPE_DYNAMIC_REFERENCE = 0x07;

public static final int TYPE_FIRSTINT = 0x10;          // Beginning of integer flavors...

public static final int TYPE_INT_DEC = 0x10;           // n..n.
public static final int TYPE_INT_HEX = 0x11;           // 0xn..n.
public static final int TYPE_INT_BOOLEAN = 0x12;       // 0 or 1, "false" or "true"

public static final int TYPE_FIRST_COLOR_INT = 0x1c;   // Beginning of color integer flavors...
public static final int TYPE_INT_COLOR_ARGB8 = 0x1c;   // #aarrggbb.
public static final int TYPE_INT_COLOR_RGB8 = 0x1d;    // #rrggbb.
public static final int TYPE_INT_COLOR_ARGB4 = 0x1e;   // #argb.
public static final int TYPE_INT_COLOR_RGB4 = 0x1f;    // ##rgb.
public static final int TYPE_LAST_COLOR_INT = 0x1f;    // ..end of integer flavors.

public static final int TYPE_LAST_INT = 0x1f;          // ...end of integer flavors.

结合上面的例子来看,蓝色部分便是Atrributes,我们取其中一个来看,比如:

0D000000 03000000 FFFFFFFF 08000001 3800077F 

其中:

5、总结

二进制XML存储方案BinXML实现方法 BinXML是我个人杜撰出来的名字,不知道是否合适,也不知道是否已存在类似的解决方案。在vcer.net上我将前一段时间的这个BinXML方案贡献出来,希望能够与广大vcer分享。 当时问题的背景是这样的,项目需要确定存储方案,这种方案需要满足如下要求: · 生成的单机板exe尽可能的不依赖于其它软件,如:数据库管理系统; · 存储的数据最好能够方便的拷贝,以满足项目的上报、汇总的功能; · 存储的格式以后可能还会变,变化不频繁,但是最好能支持这种变化; · 只需要数据的持久化与反持久化的功能,不需要查询统计等复杂功能; · 存储的数据中可能包括一个或者多个的文件附件,如:word文档; · 存储的数据可能被不同的平台使用,如:windows或者linux; · 作为web项目的一部分,除了提供VC的接口之外,存储的数据格式需要提供java的接口; 我首先考虑到了MsAccess格式,文件型数据库,方便拷贝,而且表的设计很柔性化。在windows环境下,MsAccess似乎可以不需要再安装额外的驱动,但是在linux环境下,如何被java调用是个问题(当然,这个问题也是可以解决的,但是很别扭)。其次,使用RDB还有一个问题:数据的层次表达与多值问题,将树型数据扁平化存储的方案是有的,但是,将几层简单的节点拆分成N个表格,岂不是杀鸡用牛刀? 其次的考虑当然是XML,然而XML是基于可读文本的,如何解决二进制数据问题?当然可以通过编码的途径来解决,但是这样使用XML是不是太牵强了?而且,XML有个缺陷,数据都是文本型的。要使用数值型、布尔型、日期型的数据,需要做进一步的解析。 那么就使用自定义的数据文件格式?传统情况下,我们会用一个或者若干个struct将数据打包,一下子塞进文件。但是现在用户说了:我们现在定义的数据结构可能会变:) 看来,我们的存储方案还必须要足够的柔性化。 想到最后,我决定还是借鉴XML的树型标签形式,来实现一种二进制的存储结构,即BinXML:) 关于src包 对于BinXML-src里面的例子,数据的结构类似于: BinXML-src.zip包括两部分,一部分是vc的工程,一部分是java的工程,在Visual C++6.0和eclipse+JDK1.4中都已编译、运行通过。大家可以运行BinXML-bin.zip里面的exe程序,测试一下BinXML文件的加载和保存功能。 对象模型 BinXML的对象模型主要包括:文档(document)和节点(node)。每个文档包含一个根节点(root),根节点下面包含一个或者多个子节点,如此类推。 文档包含一个文件头,其中包含了一些标识串、操作系统版本、文件大小、文档创建时间等信息。 每个节点也包含一个节点头,表明该节点的名称以及大小。一般来说,用户不会直接接触到文件头和节点头这些信息。 如下为VC里的类声明: 在java里,定义了binxml.io包,其中的类、接口定义与以上相似。 你很容易想到,使用BinXML,可以很方便地完成CTreeCtrl的持久化。 如何使用? 如下是VC中加载BinXML的代码片断: 对应的,写入BinXML的代码片断: 是不是很简单?在java里面,BinXML的一个测试用例: TODO 希望BinXML能给你带来一定的帮助和启发,更多的是希望能带来启发:)希望有兴趣的哥们继续完善BinXML,别忘了在你的大名之前保留我的版权信息: BinXML需要完善的地方表现在: · 暂时只实现了string/long/byte[],还没有提供其它类型的解析与转换; · XML标签不支持属性,只支持子元素:) · 是不是可以提供一个oxm模型,完成BinXML与Object之间的直接映射,现在这个映射工作还是人工代码实现的,譬如:GetString/GetLong,等等。应该可以完善一个映射机制,自动根据定义好的类型进行转换;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BennuCTech

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值