AndroidManifest Ambiguity方案原理及代码

1简述

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

<public type="attr" name="name" id="0x01010003" />

它的属性名为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是否含有该属性,如果有就说明软件被重打包了。

等等~

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

时间: 2024-10-29 19:10:49

AndroidManifest Ambiguity方案原理及代码的相关文章

Java基础知识强化之集合框架笔记47:Set集合之TreeSet保证元素唯一性和比较器排序的原理及代码实现(比较器排序)

1. TreeSet保证元素唯一性和比较器排序的原理及代码实现(比较器排序) (1)Student.java: 1 package cn.itcast_07; 2 3 public class Student { 4 private String name; 5 private int age; 6 7 public Student() { 8 super(); 9 } 10 11 public Student(String name, int age) { 12 super(); 13 thi

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社

从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社 PPT链接: https://pan.baidu.com/s/1i5Jrr1N 视频链接: https://v.qq.com/x/page/n0386utnrb0.html?start=492

flume原理及代码实现

转载标明出处:http://www.cnblogs.com/adealjason/p/6240122.html 最近想玩一下流计算,先看了flume的实现原理及源码 源码可以去apache 官网下载 下面整理下flume的原理及代码实现: flume是一个实时数据收集工具,hadoop的生态圈之一,主要用来在分布式环境下各服务器节点做数据收集,然后汇总到统一的数据存储平台,flume支持多种部署架构模式,单点agent部署,分层架构模式部署,如通过一个负载均衡agent将收集的数据分发到各个子a

PHP网站安装程序的原理及代码

原文:PHP网站安装程序的原理及代码 原理: 其实PHP程序的安装原理无非就是将数据库结构和内容导入到相应的数据库中,从这个过程中重新配置连接数据库的参数和文件,为了保证不被别人恶意使用安装文件,当安装完成后需要修改安装文件. 步骤: 1.检查目录或文件的权限 2.修改或填加配置文件 3.检查配置文件正确性 4.导入数据库 5.锁定或删除安装文件 具体代码: 文件:由于只是展示原理,尽量让其简单化故用小Demo形式演示 install.html 为表单填写文件 doAction.php  为处理

潜在语义分析Latent semantic analysis note(LSA)原理及代码实现

文章参考:http://blog.sina.com.cn/s/blog_62a9902f0101cjl3.html Latent Semantic Analysis (LSA)也被叫做Latent Semantic Indexing(LSI),从字面上的意思理解就是通过分析文档去发现这些文档中潜在的意思和概念.假设每个词仅表示一个概念,并且每个概念仅仅被一个词所描述,LSA将非常简单(从词到概念存在一个简单的映射关系) 不幸的是,这个问题并没有如此简单,因为存在不同的词表示同一个意思(同义词),

DeepLearning tutorial(3)MLP多层感知机原理简介+代码详解

DeepLearning tutorial(3)MLP多层感知机原理简介+代码详解 @author:wepon @blog:http://blog.csdn.net/u012162613/article/details/43221829 本文介绍多层感知机算法,特别是详细解读其代码实现,基于python theano,代码来自:Multilayer Perceptron,如果你想详细了解多层感知机算法,可以参考:UFLDL教程,或者参考本文第一部分的算法简介. 经详细注释的代码:放在我的gith

DeepLearning tutorial(4)CNN卷积神经网络原理简介+代码详解

DeepLearning tutorial(4)CNN卷积神经网络原理简介+代码详解 @author:wepon @blog:http://blog.csdn.net/u012162613/article/details/43225445 本文介绍多层感知机算法,特别是详细解读其代码实现,基于python theano,代码来自:Convolutional Neural Networks (LeNet).经详细注释的代码和原始代码:放在我的github地址上,可下载. 一.CNN卷积神经网络原理

免费的Lucene 原理与代码分析完整版下载

Lucene是一个基于Java的高效的全文检索库.那么什么是全文检索,为什么需要全文检索?目前人们生活中出现的数据总的来说分为两类:结构化数据和非结构化数据.很容易理解,结构化数据是有固定格式和结构的或者有限长度的数据,比如数据库,元数据等.非结构化数据则是不定长或者没有固定格式的数据,如图片,邮件,文档等.还有一种较少的分类为半结构化数据,如XML,HTML等,在一定程度上我们可以将其按照结构化数据来处理,也可以抽取纯文本按照非结构化数据来处理.非结构化数据又称为全文数据.,对其搜索主要有两种

DeepLearning tutorial(1)Softmax回归原理简介+代码详解

DeepLearning tutorial(1)Softmax回归原理简介+代码详解 @author:wepon @blog:http://blog.csdn.net/u012162613/article/details/43157801 本文介绍Softmax回归算法,特别是详细解读其代码实现,基于python theano,代码来自:Classifying MNIST digits using Logistic Regression,参考UFLDL. 一.Softmax回归简介 关于算法的详