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

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

前言


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


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中,这也是我们这篇文章讨论的重点。


Start Tag Chunk Header


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

网络异常,图片无法展示
|


下面我们一步步解析它。

从上面的图中可以清楚的看到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的默认值。


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。


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

其中:

0D000000表示字符串池第15个是命名空间;03000000表示字符串池第4个是属性名称;FFFFFFFF表示没有rawValue。

0800表示TypeValue的大小是8byte;00是resId,固定值;01表示属性值是一个资源索引;3800077F就是资源索引,即resId是0x7F070038。

再结合实际代码,这个属性上面代码中的第一个属性,所以是android:id="@+id/image1"。所以它的属性值是@+id/image1,是一个资源索引,在

R.java中


public static final int image1=0x7F070038;
复制代码


可以看到image1正是0x7F070038。


总结


这样Start Tag Chunk结构我们就分析完了,有些细节没有说,比如属性值不同的type的表现形式,大体上是差不多的,大家有兴趣可以自己研究一下。


目录
相关文章
|
14天前
|
XML Java 数据库连接
mybatis中在xml文件中通用查询结果列如何使用
mybatis中在xml文件中通用查询结果列如何使用
14 0
|
16天前
|
XML JavaScript 前端开发
xml文件使用及解析
xml文件使用及解析
|
22天前
|
XML Java Android开发
Android实现自定义进度条(源码+解析)
Android实现自定义进度条(源码+解析)
51 1
|
1月前
|
XML C# 数据格式
使用C#操作XML文件
使用C#操作XML文件
11 0
|
1天前
|
存储 Java API
Android系统 文件访问权限笔记
Android系统 文件访问权限笔记
21 1
|
1天前
|
移动开发 Java Unix
Android系统 自动加载自定义JAR文件
Android系统 自动加载自定义JAR文件
14 1
|
1天前
|
Shell Android开发
Android系统 adb shell push/pull 禁止特定文件
Android系统 adb shell push/pull 禁止特定文件
7 1
|
1天前
|
Android开发
Android源代码定制:添加customize.mk文件进行分项目和分客户的定制
Android源代码定制:添加customize.mk文件进行分项目和分客户的定制
2 0
|
2天前
|
XML 数据格式
小米备份descript.xml文件
小米备份descript.xml文件
11 0
|
5天前
|
XML C# 数据格式
C# 解析XML文件
C# 解析XML文件
14 1