概述
现阶段。我们知道Android中的反编译工作越来越让人操作熟练,我们辛苦的开发出一个APK,结果被人反编译了,那心情真心不舒服。虽然我们混淆,做到native层,但是这都是治标不治本。反编译的技术在更新,那么保护APK的技术就不能停止。现在网上有很多APK加固的第三方平台,最有名的应当属于:爱加密、梆梆加固、360加固、百度加固、腾讯加固等。
什么是加壳?
移动平台攻防技术的发展基本是沿着PC端发展轨迹在进行,从windows平台的加壳脱壳反调试到Andriod的平台apk加固,反调试代码混淆等。
加壳是在二进制的程序中植入一段代码,在运行的时候优先取得程序的控制权,做一些额外的工作。大多数病毒就是基于此原理。PC EXE文件加壳的过程如下:
加壳作用和分类?
(1)作用:加壳的程序可以有效阻止对程序的反汇编分析,以达到它不可告人的目的。这种技术也常用来保护软件版权,防止被软件破解。
(2)分类:从App的加固技术来看:主流分为dex加密和so加密,目前来看保护dex文件更为重要,应为dex反编译后的java代码可读性更强。
Android APP应用所处的安全等级可以划分了几个层级:
-
原始社会时期–代码混淆:使用Proguard等混淆工具会把Java语言编写的代码的类名、变量名混淆为自己定义的格式,增加破解者在破解时阅读难度。目前我们浏览器的整体代码处于该等级。
-
奴隶社会时期–自我校验:在程序中加一些对自己应用的完整性校验,可以借助签名、或计算自己应用dex的md5值等来完成。这种方式可以避免APK在第二次打包之后无法运行;但该方式的缺陷在于,如果破解者对开发者校验的地方进行了修改,那么计算机也会按照新的逻辑执行,该方式的保护措施风险很大,所以也就逐渐没落了。
-
封建社会时期—dex文件变形:将代码混淆,将校验代码使用C/C++来实现;同时对dex文件、AndroidManifest文件进行处理。该方式在保证APK正常运行的同时,使得反编译工具如apktool等,在反编译时发生崩溃。
-
资本主义社会时期–dex保护和So保护:
(1)dex保护:包括隐藏dex文件(加壳)、对dex文件进行变形(使dex文件结构不完整)、对dex结构进行变形(在指令级别对dex文件进行修改)等;
(2)so保护:so是ELF文件,有些第三方加固保护是对so文件进行保护,具体做法是稍微修改ELF头或者节表信息;其次还有修改程序头表方式来保护so文件、进程防调试、或增加调试难度; -
社会主义时期:使用多重维度和深度对APK进行加固保护。因为隐藏dex、dex文件结构变化、ELF简单修改、对so文件添加UPX壳等方式都存在不同程度的弊端;采用多重维度和深度的方式对APK加固就非常必要了。
加壳基础
APK文件结构
每个文件及文件夹的作用如下表所示。
DEX文件格式
DEX文件是Android系统的可执行文件,包含应用程序的全部操作指令以及运行时数据。由于dalvik是一种针对嵌入式设备而特殊设计的java虚拟机,所以dex文件与标准的class文件在结构设计上有着本质的区别。
当Java程序编译成class后,还需要使用dx工具将所有的class文件整合到一个dex文件,目的是其中各个类能够共享数据,在一定程度上降低了冗余,同时也是文件结构更加经凑,实验表明,dex文件是传统jar文件大小的50%左右。
Dex文件整体结构如下:
Dex文件整体结构说明:
下面是DEX文件目录:
这里面,有3个成员我们需要特别关注,这在后面加固里会用到,它们分别是checksum、signature和fileSize。
- checksum字段
checksum是校验码字段,占4bytes,主要用来检查从该字段(不包含checksum字段,也就是从12bytes开始算起)开始到文件末尾,这段数据是否完整,也就是完整性校验。它使用alder32算法校验。 - signature字段
signature是SHA-1签名字段,占20bytes,作用跟checksum一样,也是做完整性校验。之所以有两个完整性校验字段,是由于先使用checksum字段校验可以先快速检查出错的dex文件,然后才使用第二个计算量更大的校验码进行计算检查。 - fileSize字段
占4bytes,保存classes.dex文件总长度。
这3个字段当我们修改dex文件的时候,这3个字段的值是需要更新的,否则在加载到Dalvik虚拟机的时候会报错。
为什么说我们只需要关注这三个字段呢?
因为我们需要将一个文件(加密之后的源Apk)写入到Dex中,那么我们肯定需要修改文件校验码(checksum)。因为他是检查文件是否有错误。那么signature也是一样,也是唯一识别文件的算法。还有就是需要修改dex文件的大小。
不过这里还需要一个操作,就是标注一下我们加密的APK的大小,因为我们在脱壳的时候,需要知道APK的大小,才能正确的得到APK。那么这个值放到哪呢?这个值直接放到文件的末尾就可以了。
所以总结一下我们给DEX文件加壳时需要做:修改Dex的三个文件头,将源APK的大小追加到壳dex的末尾就可以了。
APK打包流程
上图中涉及到的工具及其作用如下:
DEX加壳
加固原理
Dex文件整体加固原理如下:
在该过程中涉及到三个对象,分别如下:
- 源程序
源程序也就是我们的要加固的对象,这里面主要修改的是原apk文件中的classes.dex文件和AndroidManifest.xml文件。 - 壳程序
壳程序主要用于解密经过加密了的dex文件,并加载解密后的原dex文件,并正常启动原程序。 - 加密程序
加密程序主要是对原dex文件进行加密,加密算法可以是简单的异或操作、反转、rc4、des、rsa等加密算法。
加固过程
加固过程可以分为如下4个阶段:
- 加密阶段
- 合成新的dex文件
- 修改原apk文件并重打包签名
- 运行壳程序加载原dex文件
1、加密阶段
加密阶段主要是讲把原apk文件中提取出来的classes.dex文件通过加密程序进行加密。加密的时候如果使用DES对称加密算法,则需要注意处理好密钥的问题。同样的,如果采用非对称加密,也同样存在公钥保存的问题。
2、合成新的dex文件
这一阶段主要是讲上一步生成的加密的dex文件和我们的壳dex文件合并,将加密的dex文件追加在壳dex文件后面,并在文件末尾追加加密dex文件的大小数值。
【注意】在壳程序里面,有个重要的类:ProxyApplication类,该类继承Application类,也是应用程序最先运行的类。所以,我们就是在这个类里面,在原程序运行之前,进行一些解密dex文件和加载原dex文件的操作。
3、修改原APK文件并重打包签名
在这一阶段,我们首先将apk解压,会看到如下图的6个文件和目录。其中,我们需要修改的只有2个文件,分别是classes.dex和AndroidManifest.xml文件,其他文件和文件加都不需要改动。
首先,我们把解压后apk目录下原来的classes.dex文件替换成我们在0x02上一步合成的新的classes.dex文件。然后,由于我们程序运行的时候,首先加载的其实是壳程序里的ProxyApplication类。所以,我们需要修改AndroidManifest.xml文件,指定application为ProxyApplication,这样才能正常找到识别ProxyApplication类并运行壳程序。
4、运行壳程序加载原dex文件
Dalvik虚拟机会加载我们经过修改的新的classes.dex文件,并最先运行ProxyApplication类。在这个类里面,有2个关键的方法:attachBaseContext和onCreate方法。ProxyApplication显示运行attachBaseContext再运行onCreate方法。
(1)在attachBaseContext方法里,主要做两个工作:
- 读取classes.dex文件末尾记录加密dex文件大小的数值,则加密dex文件在新classes.dex文件中的位置为:len(新classes.dex文件)– len(加密dex文件大小)。然后将加密的dex文件读取出来,加密并保存到资源目录下
- 然后使用自定义的DexClassLoader加载解密后的原dex文件
(2)在onCreate方法中,主要做两个工作:
- 通过反射修改ActivityThread类,并将Application指向原dex文件中的Application
- 创建原Application对象,并调用原Application的onCreate方法启动原程序
App加固的利弊
1、正面:
- 保护自己核心代码算法,提高破解/盗版/二次打包的难度
- 缓解代码注入/动态调试/内存注入攻击
2、负面:
- 影响兼容性
- 影响程序运行效率.
- 部分流氓、病毒也会使用加壳技术来保护自己
- 部分应用市场会拒绝加壳后的应用上架