李萍
(广东省轻工业技师学院,广州 513000)
Android应用防篡改机制的研究
李萍
(广东省轻工业技师学院,广州 513000)
面对开发者所编写的Android应用被第三方恶意破解、篡改、盗版的现状,为了找出如何保护Android应用的机制,先从篡改者的攻击手段入手,分析应用篡改的基本原理,结合Android系统自身的相关机制,针对性地提出如何保护应用的防御手段和检测手段,同时也说明这些技术手段的局限性以及引起局限性的根本原因,最终提出了针对一般破解者的防御策略。
Android应用;防篡改;机制
当前Android系统占据了智能系统市场的主要份额,但其盗版问题日益严重,影响了开发者的收益,推动了恶意木马的传播,恶化了整个应用的生态圈。本文着重分析Android应用被篡改的主要手段和机制,结合之前已有的保护手段,进一步研究更加有效的应用保护机制。
分析篡改机制之前,首先需要对Android应用的打包机制和包结构做一些简单的说明。
(一)Android打包机制说明
Android的安装包称为APK(android package file)的缩写。APK文件是标准的Zip文件,构造方式和JAR文件类似,包含一个Android应用运行所需的执行文件、动态链接库、资源文件、证书、清单文件,即:
1.AndroidManifest.xml:清单文件
2.Lib目录:存放动态链接库(so)文件
3.META-INF:签名信息的存放目录
4.res:资源存放目录(布局、图片、字符串)
5.Classes.dex:DEX格式的可执行文件
6.Resources.arsc:编译后的二进制资源文件
Android的应用开发主要使用两种语言:Java和C/C++,前者用于构造应用的主体,后者也是Android中NDK(native develop kit)所使用的语言,主要用于生成动态链接库(so)。此外还有AIDL(Android Interface definition language),用于编写进程间通信用的接口文件。
Android应用打包过程就是将以上相关代码和文件编译整合的过程[1]:
1.通过aapt工具打包资源文件,生成R.java
2.编译AIDL生成java文件
3.编译所有Java代码,生成class文件
4.通过dx工具转换class文件为一个dex文件
5.将资源、清单、dex、so统一打包为apk文件
6.通过jarsigner选择证书对apk签名
7.使用zipalign对apk做对齐操作
可以看到,APK文件结构是公开、易于分析修改,对于资源文件完全没有保护,因此也就为篡改应用打开了方便之门。
(二)篡改应用的手段
针对APK文件的篡改,通常有以下做法:
1.替换资源:由于APK文件中对于资源文件未做任何保护,篡改者可以轻易获取所有资源文件,找到对应的图片、文字等信息,将其替换,从而使得应用改头换面。
2.修改实现:对Dex文件通过反编译等手段,定位到关键业务代码,修改判断条件分支,使原有逻辑失效。这种一般用于游戏应用破解,绕开注册检查、计费检查等,生成所谓破解版,使开发者利益受损。
3.代码注入:通过静态注入代码等手段,修改和替换原有业务逻辑。常见的篡改方式有:替换原有广告为自己的,加入恶意扣费代码、木马数据、远程下载数据,窃取隐私数据。这种篡改的危害很大,不但开发者收益受损,还威胁到使用者的信息安全。这里篡改的核心技术手段就是对Dex文件的反编译和修改,而第三方工具的提供,使得这项工作的难度大为降低,甚至可以自动进行。
(三)通过反编译手段静态分析应用
常见的反编译工具主要有:
1.Apktool:Google官方提供的一款反编译和打包工具,以java实现,通过jar包提供使用。该工具可以将APK直接反编译为smali文件,还可以将修改后的文件重新打包为APK。
2.Dex2Jar:Dex2Jar是针对APK文件中的可执行文件classes.dex进行反编译,并生成为jar文件的工具。它的优势是,直接将dalvik字节码转换为更具有可读性的java字节码,然后就可以通过第三方java字节码的反编译工具来阅读和理解。
反编译命令:
反编译完成后,可以使用jd-gui等java反编译工具来直接查看阅读代码。
3.Baksmali/Smali:用于将classes.dex反编译为smali文件,以及将smali文件反编译为classes.dex文件。
反编译命令:
重编译命令:
(四)动态分析应用
反编译获取的smali文件或者jar文件在应用逻辑复杂的情况下,很难快速定位和分析,因此篡改者往往会运行应用,通过以下手段来动态分析:
1.日志分析:通过adb logcat可以查看应用的输出日志,如果应用未输出日志,则可以通过代码注入的方式在所需分析的smali文件中加入打印日志语句来获取所需信息。
2.动态调试:使用IDA Pro、gdb等工具动态加载应用,并动态调试(设置断点、单步跟踪、查看寄存器数据等)。
根据Android官方文档说明,如果需要调试Android应用,需要满足以下两个条件之一:AndroidManifest.xml中的Application标签节点具备属性:android:debuggable=”true”或系统default.prop文件中的ro.debuggable的值为1。见图1。
目前Android对开发者提供了基本的保护手段:代码混淆。它是Java代码的传统保护机制,不但可以降低代码在反编译后的可读性,还可以减少应用的大小,提高运行效率,可以说是一举多得。Android开发环境从2.3版本后集成了Proguard作为混淆工具,打开开关即可完成默认配置的混淆效果[2],见图2。
此外,第三方也提供了一些加强版的混淆工具(如DexGuard、Allatori),不但可以对函数名、类名、变量名做混淆,还可以对其中的字符串做混淆,将调用改为反射调用,对资源加密等。但这些工具一方面要收取不菲的费用,另一方面,仍然只是降低了可读性,并未完全保护应用。见图3。
鉴于Android自身的保护机制的不完善,这里进一步提出其他防篡改的机制:
(一)检测机制:
1.校验签名:检查Android的签名机制是检测应用有无被篡改的最有效手段。由于签名证书的安全性,私钥不会公开,第三方也无法获取私钥,从而无法伪造证书签名,只能使用其他证书进行签名。因此,只要在运行时校验当前应用的签名证书是否是原始证书即可,当然原始证书的信息(公钥信息)也可以保存在服务器端,通过远程验证方式来进一步确保其安全性。
APK的签名信息保存在META-INF目录的RSA文件中。校验签名的思路是:应用启动后,从安装后的APK文件中读取RSA文件,并从中拿到证书信息,然后与正确的证书信息比对,以下代码是读取RSA文件签名的代码示例:
2.校验应用指纹:通过校验APK的HASH值来确保应用未被非法修改。从系统路径找到应用对应的APK,通过PackageManager可以得到APK的路径,对APK文件计算HASH值,如对APK全文做MD5计算,将结果送到后台进行比对,比对成功后,继续业务逻辑。
3.检测运行环境是否处于虚拟机:篡改者的一种策略是在PC上的Android模拟器中运行被攻击应用,由于模拟器的环境可以任意修改,使得篡改者可以拥有更多的权限和信息。那么可以在应用启动时,加入相关的检测逻辑,一旦检测到当前环境处于模拟环境,则立刻退出应用。由于Android模拟器运行时主要依赖QEMU这个工具,所以可以检查当前进程或者系统变量是否包含相关信息。
以下的伪代码是通过获取系统的属性ro.kernel. qemu来判断当前是否是模拟器。
(二)静态防御机制:
1.破坏反编译机制:其思路是,针对常见的反编译工具,构造使其不能正常工作的代码,导致其反编译失败。通过测试大量反编译APK文件,可以找出能够让反编译工具无法正常工作的场景,然后根据此时产生的错误信息,找到线索来重现异常,并将这种机制放在自身代码中。但这种做法有一定的局限性:即寻找反编译工具的漏洞过程比较漫长,且结果不确定;对于开发者的要求较高;反编译工具会不停地演化升级,原有的破坏机制逐渐失效,需要寻找新的破坏机制。
2.NDK机制:NDK是android提供的一套Native语言开发工具包,也就是使用C/C++编写业务代码的开发包。开发者使用NDK编写代码,生成so为扩展名的动态链接库,然后在Java层通过JNI调用so文件中的函数方法。
NDK机制的保护力度比代码混淆要高得多,但它的缺点也很明显:即增加开发成本:开发人员必须要学习C/C++语言,并编写相关的实现;保护力度有限:只能针对核心业务逻辑(与界面无关),比如加解密、敏感数据、核心算法等进行保护。见图4。
(三)动态防御机制:
1.防止动态调试机制:Gdb等调试工具的原理是通过系统调用ptrace来注入到进程中,从而实现动态调试。为了防止动态调试,在应用启动时立刻Ptrace自身,这样如gdb等工具就无法成功ptrace应用,从而无法调试和动态注入。防御的相关实现需要放在so文件的JNI_OnLoad方法中,应用启动后立刻加载so文件,一旦加载,该逻辑立刻执行。
2.加壳机制:对于一个Android应用来说,主业务逻辑是保存在classes.dex中,可以将真实的classes. dex文件事先加密保存,而在运行之后,解密DEX文件,然后动态加载入内存运行。这种做法会使得篡改者针对应用的静态分析手段失效。为了保证安全性,主要的业务逻辑均需要以NDK编写,并在JNI_OnLoad方法中实现。
具体实现上,加壳流程(见图5)如下:
*编写StubApplication,从Application类继承,代码主要完成脱壳的操作
*修改原应用的清单文件,将其声明的Application入口改为StubApplication将原应用的classes.dex文件通过加密后放在assets目录下,如果需要对资源加密,可以做类似处理
*将Stub相关文件实现编译为smali或者so
*重新打包
脱壳时首先解密文件,然后通过Android框架提供的动态加载类DexClassLoader[3],解密后的dex文件同时还需要通过反射机制修改当前内存中相关变量,主要是将当前应用的ClassLoader替换为DexClassLoader。此外,还需要将Application对象替换为原Application对象。
Dex文件的加壳机制相对以上的其他防御机制来说是更有效的手段,第三方很难使用静态分析的手段来破解应用逻辑。但由于Dex文件在应用运行时被Dalvik虚拟机加载到内存执行,因此攻击者可以通过运行Dump内存的手段,将含有Dex文件的内存数据全部存储到文件中,然后通过找到Dex文件的Header以及文件长度偏移量,利用dd等工具将Dex文件从中抠取出来,从而获得了未加密的原始Dex文件。具体的破解步骤如下:
*在设备中运行目标应用
*在设备上运行gdb,并指向目标应用的进程ID:gdb-pid=xxx
*通过gcore dump出整个进程内存,转储为一个二进制文件
*在PC上使用工具查看该文件,并查找Dex文件头部(匹配字符串:dex?035)
*使用dd来抠取数据:
(四)虚拟机保护机制
由于Android的自身机制决定可以通过动态注入的方式来获取原始Dex文件,所以理论上可以通过修改Dalvik虚拟机生成自己私有的虚拟机模式,在虚拟机这一层来做Dex文件的加解密,可以在内存中保护Dex文件,甚至可以使得Dex文件动态组装、动态修改,使得攻击者无法通过dump或者注入的方式获取真实的Dex文件。这种机制理论上是最安全的保护机制,但其问题也很明显,即对Android的兼容性,由于Android 5.0开始,ART取代了Dalvik虚拟机模式,即APK在安装后就会被预先编译成本地可执行的机器码,而不再采用解释执行的虚拟机模式,从而提高了运行效率。而ART模式也决定了通过对Dex文件动态组装的虚拟机保护模式无法走通。
Android系统具有开放性和强大的扩展性,但其安全性尤其是对应用的保护方面差强人意。开发者通过混合使用本文中静态防御、动态防御、检测机制,可以提高破解、篡改者的技术门槛和篡改成本。但由于Android自身的机制决定,现有的保护手段都存在对应的破解方法。一方面,应用的保护机制越来越深入底层,才能应对各种各样的破解方法,但另一方面,底层机制的改动会降低应用对机型适配和兼容能力,目前来说,这是一个鱼和熊掌不可得兼的局面。如果需要打破这一局面,还是需要谷歌本身重视对应用的保护,在底层提供更多的保护机制给应用开发者。
[1]丰生强.Android软件安全与逆向分析[M].人民邮电出版社,2013.
[2][美]Anmol Misra.Android系统安全与攻防[M]机械工业出版社,2014.
[3]柯元旦.Android内核剖析[M]电子工业出版社,2011.
编辑 朱荣华
TN91
A
2095-8528(2015)05-067-04
2015-04-26
李萍(1975-),女,广东省轻工业技师学院计算机科学与技术讲师,研究方向为Android平台的应用安全与保护机制。