http://www.cnblogs.com/wanyuanchun/p/4084292.html

1简述

前段时间在bluebox的一份Android安全pdf中看到一个AndroidManifest Ambiguity方案。该方案基于android系统解析AXML的一个特点:android在解析AXML的属性的时候,是通过该属性的res id号而非属性名定位的。所谓的AXML就是AndroidManifest.xml对应的二进制文件,APK包中存储的就是AXML。比如属性:

它的属性名为name,id号为0x01010003。

该方案的大致原理如下图所示:

 

我简要概括一下:

我们在axml(注意是axml不是AndroidManifest.xml)中添加一个属性,该属性的属性名是name,属性的值是some.class,属性的ID号为0。根据前文所述,android系统对于非法的res ID号是不会解析的。所以我们添加这个无用的属性后,并不影响该APK的正常工作(上图左下角所示),但是对于apktool之类的逆向工具而言,他们却会对这个无用的属性进行解析(上图右下角所示)。所以,如果我们进行重打包的话,apktool就会将该属性变更为一个ID号0x01010003的可以被系统解析的属性。这样造成的后果就是:由于我们的APK中并没有实现trap.class类,所以APK启动时会报错“there is no trap.class~~”。

2 实现方案

该PDF虽然提出了这个方案,但并没有给出实现的代码(其实它就给了上面那张图~其它什么都木有了~),google也是空白。所以当我看懂原理之后,就想自己将它实现出来。哪知事情并没有我想的那么简单~~

2.1 AXML文件格式

遇到的第一个挑战就是:网上竟然搜不到AXML文件的格式!!!当时差点就放弃了,不过后来一想,既然apktool能解析AXML那就说明它是了解AXML的文件格式的,所以就上网搜索了一下解析AXML的各种解析代码,综合过后觉得Claud大大的AXML Parser代码比较利于总结AXML的文件格式。所以就以该代码问蓝本总结了一下文件格式,如下表所示:

0x0~0x3    magic: 0x03000800固定值

0x4~0x7    filesize: 文件整体大小

0x8~0xb     StringTag: 字符串块开始标志,0x01001c00固定值

0xc~0xf      StringChunkSize:字符串块大小

0x10~0x13   count of strings:字符串个数,

0x14~0x17   count of styles: 类型个数

0x18~0x1b   reserve field: 保留的,为0

0x1c~0x1f    string的起始偏移值:注意,这个偏移值是相对于stringChunk而言的!

0x20~0x23    styles的起始偏移值:同上

下面存储的就是n个连续的string的偏移值,每个偏移值占4字节,需要注意的是,这个偏移值加上string的起始偏移值和0x8才是真正的偏移值!n的大小就是0x10~0x13的大小

然后就是n个连续的style的偏移值,同上~

String数据块

........

Style数据块

........

注意:到这里,stringchunk就算是结束了

下面就是ResourceChunk了,里面保存的就是资源ID号

ResourceTag: 0x80010800  

ResourceChunkSize: 资源ID块的大小

连续 ResourceChunkSize/4 -2个res id值。-2主要是除去上面的8字节resourceChunkHeader。

每个res id占4字节

                           ResourceChunk结束

下面就是一些连续的chunk块了:

CHUNK_STARTNS:  doc开始标志,0x00011000

CHUNK SIZE:

line number

unknown, 0xffffffff

下面就是一个namespace record结构体,简称NsRecord

NsRecord->prefix

NsRecord->uri,

然后就是递归地进行chunk操作,因为一个命名空间里面往往含有很多子chunk

CHUNK_TYPE:0x02011000->0x00100102为CHUNK_STARTTAG

CHUNK_SIZE 

line number

unknown, 0xffffffff 

current tag's namespace's uri

当前tag的名字 所一个string索引值

flags, unknown usage

当前标签含有的attr个数,注意最后结果要&0x0000ffff

classAttribute, unknown usage

下面就是连续的n个attribution chunk,attribution的结构体如下:

/* attribute structure within tag */

typedef struct{

      uint32_t uri;        /* uri of its namespace  index  of strings*/

      uint32_t name;   /*属性名,索引值 index  of strings */

      uint32_t string;   /* attribute value if type == ATTR_STRING ,索引值*/

      uint32_t type;             /* attribute type, == ATTR_* * / 注意该值需要右移24位

      uint32_t data;            /* attribute value, encoded on type */

} Attribute_t;

依次类推

........

 

2.2修改AXML的注意事项

了解了AXML的文件格式,我们就可以想法进行属性插入了。不过在属性插入之前,我们必须规划好具体地实施方案,因为它涉及到的东西并不算少。

1)首先,需要对属性结构体做进一步分析。它的格式如下:

/* attribute structure within tag */

typedef struct{

         uint32_t uri;               /* uri of its namespace  index  of strings*/

         uint32_t name;      /*属性名,索引值 index  of strings */

         uint32_t string;     /* attribute value if type == ATTR_STRING ,索引值*/

         uint32_t type;            /* attribute type, == ATTR_* * / 注意该值需要右移24位

         uint32_t data;           /* attribute value, encoded on type */

} Attribute_t;

重点是name, string, data。我提取出了一个AXML中属性片段,如下所示:

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

0C 00 00 00  05 00 00 00   FF FF FF FF   08 00 00 01   01 00 06 7F 

0C 00 00 00  06 00 00 00   FF FF FF FF   08 00 00 01   00 00 05 7F

0C 00 00 00[w1]   04 00 00 00[w2]    17 00 00 00[w3]   08 00 00 03 [w4]   17 00 00 00[w5] 

 [w1]uri:命名空间的URI,是string的索引值

 [w2]name:属性名,也是一个string的索引值

 [w3]string:如果属性type为ATTR_STRING的话,此值就是属性android:name="xxx",xxx在string的索引值。其余情况均为0xffffffff

 [w4]type:属性的类型,对于android:name,类型值为0x03000008

 [w5]data:属性的数据值,对于ATTR_STRING而言,它的值就是string的值。

可以发现,结构体里面并没有一个叫做res ID的成员,那么系统又是如何获取某个属性的ID号的呢?原来这里的name成员是身兼两职,即作为属性名的一个string索引,又作为res ID的索引。比如这里name = 4,它对应StringChunk中的字符串为"name",对应ResourceChunk中的res ID 0x01010003。所以要插入一个属性名为name,ID号又为0的属性,我们就必须新建一个string,该string的值为name,再新建一个res ID,值为0,且两者在各自Chunk区域的索引值是相等的(这是重点)

2)其次,就是在StringChunk中string的对齐问题(最初被弄得脑洞大开~)。

AXML中几乎所有的成员都是uint32型的,除了使用UTF-16编码的string数据块之外。所以在加入string后必须对string数据块进行4字节对齐。而如果原AXML的string数据块已经进行过4字节对齐(即人为地填充了几个0x00)的话,我们就需要注意UTF-16编码的最后一个string的第一个字节的大小并不包含这几个填充的0x00(这个字节表示该string所占用的字节数,详情可查阅UTF-16编码相关资料)。为了绕过烦人的对齐问题,我们使用取巧的方式获取字符串的长度:

stringLen = stringChunkSize - stringOffset; //此时的stringLen肯定是4字节对齐的

当然,这是在没有style的情况下,如果有的话,还得采取额外的操作(实现代码中有~)。为了简便,我是直接将添加的string加在这个对齐后的字符串之后的,这样就只需要考虑添加的字符串是否需要对齐了~

3)然后,就是ResourceChunk的扩充。

在1)中已经提到插入的属性的name的值同时充当res ID索引值。而通常ResourceChunk中的res ID个数是远少于string 的个数的,那么这就需要我们将ResourceChunk进行扩充。扩充很简单,全部赋值为0即可。

4)最后,除了需要添加数据外,还需要修改原文件的某些“计量值”,这些计量值都是与数据块大小或偏移值有关的,总结如下:

①fileSize

②StringChunkSize

③count of string

④styles的起始偏移值(如果有style的话就需要修改)

⑤ResourceChunkSize

⑥application所属chunk的chunksize

⑦applicationh含有的属性个数

 

2.3 修改AXML步骤

1)修改StringChunk,添加UTF-16表示的字符串chouchou.class和name,并为这两个字符串添加偏移值条目。同时对StringChunkSize、count of string、styles的起始偏移值进行修复;

2)修改ResourceChunk,主要是进行res ID扩充和对ResourceChunkSize的修复

3)修改application所在的chunk,插入属性,同时对chunksize和applicationh含有的属性个数进行修复;

4)将不需要修改的部分copy到合适的位置;

5)修复fileSize

当然,具体地实现肯定比上诉步骤复杂一些,不过实现源码中有较为详细的注释,大家可参照源码阅读~

3 代码说明

AxmlParser.h/.c是Claud大大解析axml的源码,出于对作者的感谢以及让大家更详细地了解AXML的解析过程(其实,是我实在是不想自己写解析代码o(╯□╰)o),我将实现代码跟它合并到一块了。AxmlModify.c就是我写的实现AXML修改功能。

4 使用方法

当前代码还不完善,只是初步实现了插入application.attr("name", "chouchou.class",0x0)的功能。所以并非最终版。

代码只能在linux下运行,下载代码后make即可生成可执行文件manifestAmbiguity。然后直接运行./manifestAmbiguity可以得到完整的使用说明。

修改前:

 

修改后:

 

将修改后的xml覆盖原APK中的xml,然后删掉原来的签名文件夹再进行签名即可。这时候如果对按照此方案修改后的APK进行重打包,就会发现重打包的APK已经无法启动了。

5 下一步工作

由于目前的apk软件保护主要是基于dex代码加密和so库文件加密,对AndroidManifest.xml并没有进行任何操作,而AndroidManifest.xml作为apk的入口文件,其重要性是不言而喻的。所以我想能不能在此文件中做些“手脚”,然后结合相应的处理代码实现另一角度的软件保护。

比如,我们完全可以实现那个陷阱类trap.class,且这个类继承自application等等,以便被重打包的apk也可运行。只是,从一开始,该apk就运行在一个错误的环境中,至于之后的操作,那就可以尽情发挥了。

或者,我们可以在其他tag中插入一些不会影响apk运行的属性(即新添加的属性不可被系统识别,重打包后该属性能被系统识别但又不会影响apk的运行),然后在代码中检查AndroidManifest.xml是否含有该属性,如果有就说明软件被重打包了。

等等~

如果大家有好的建议或方法,请一定不吝赐教~谢谢!

代码地址:
https://github.com/wanchouchou/ManifestAmbiguity

更多相关文章

  1. android 自定义Toast,可设定显示时间
  2. Android(安卓)Studio常用快捷键---不断更新中
  3. 如何将当前布局用代码保存在png图像文件中?
  4. Android通过putExtra()传递JavaBean对象,属性值为null问题的解决
  5. Android中bool的使用
  6. Android(安卓)向服务器发送get请求乱码问题
  7. android常用属性的学习
  8. Android败笔之ListView设置分割线颜色
  9. Android(安卓)Studio开发工具快捷键

随机推荐

  1. Android软键盘显示模式及打开和关闭方式
  2. ANDROID音频系统散记
  3. Android性能测试
  4. android WebView总结
  5. Media Player of Android
  6. 【Android(安卓)文件管理】分区存储 ( Me
  7. Android(安卓)Permission denied(
  8. Android(安卓)SDK相关介绍
  9. AndroidUi(4)-圆环
  10. android视频处理相关资料