一种Android应用加固方案
2016-12-26朱洪军陈耀光华保健
朱洪军 陈耀光 华保健 陈 灏
1(中国科学技术大学软件学院 安徽 合肥 230051)2(中国科学技术大学苏州研究院 江苏 苏州 215123)
一种Android应用加固方案
朱洪军1,2陈耀光1华保健1,2陈 灏1
1(中国科学技术大学软件学院 安徽 合肥 230051)2(中国科学技术大学苏州研究院 江苏 苏州 215123)
Android应用安全问题日益突出,大量Android应用遭受逆向、非法复制及恶意代码注入等攻击。对Android应用安全机制进行研究,在分析静态逆向和动态逆向攻击原理的基础上,提出一种移动应用加固保护方案。方案综合运用加壳、反调试、签名校验及反编译等应用加固技术,对目标应用进行加固;经测试,该加固方案能够很好地对抗常见的静态分析和动态分析等逆向攻击。
加固 反调试 Android安全 加壳
0 引 言
2014年5月,中国工业和信息化部电信研究院发布《移动互联网白皮书》[1]。文献[1]指出:Android(安卓)在中国市场的主导优势更为凸显。2013年四季度 Android 以新增市场94.6%的绝对领先优势主导国内市场。同时,Android安全问题也日益严重,另据手机安全公司统计,2013年受恶意吸费代码感染的终端数量高达1400万,用户直接经济损失超过7000万元。
基于Android平台的应用程序代码经过编译器编译生成Android应用压缩包APK,APK极易被逆向工具,如Apktool(谷歌公司提供的一种用于APK反编译的工具)等进行反编译,从而开展逆向攻击。自2.3版本发布之后,Android平台引入了代码混淆机制;通过代码混淆仅增加了逆向分析的难度和工作量,应用程序核心逻辑仍然可以通过函数调用关系进行逆向破解与攻击[2,3]。因此,未经加固的Android应用程序无法很好地对抗恶意代码注入、非法拷贝等攻击。Android应用一旦遭受攻击,就可能被植入广告、程序后门,甚至窃取用户隐私数据,给用户带来严重损失。
文献[2]提出了一种基于classes.dex(APK压缩包中可执行代码包)文件动态加载的Android平台加固方案,可以有效地隐藏关键代码,生成目标代码保护壳,但其并未实现对壳文件的有效保护。文献[4]提出的Android加固方案是在加壳的基础上添加了对壳程序的保护措施,例如伪加密、代码混淆、签名校验等,但其提出的保护措施仅在Java层,通过简单逆向攻击手段,如直接修改smali代码,就可以轻易破解。
在此基础上,本文提出一种移动应用加固方案:通过将目标虚拟机可执行文件dex(Dalvik VM Executes)加密之后隐藏在如图片等资源文件中,使用非法属性识别符ID(Identifier)、反调试及签名校验等技术,对目标应用程序进行加固。实验结果表明:本加固方案可以很好地对抗反编译、逆向分析、非法复制及重打包等逆向攻击。
1 Android应用逆向分析概述
1.1 APK文件结构
APK文件是Android平台下的可执行文件,它本质上是一种zip压缩文件格式,其文件结构及作用大致如表1[5,6]所示。
表1 APK文件目录
其中classes.dex文件是Dalvik虚拟机字节码文件,是攻击者进行逆向分析与攻击的目标。dex文件主要结构如图1所示。
图1 dex主要结构
在dex header中checksum和signature分别是对dex文件的校验值和签名值,系统在加载dex时会校对dex的签名及检验值,避免加载损坏的dex,在一定程度上增加应用程序安全性。
1.2 静态分析
静态分析是指在不运行代码的情况下,采用词法分析、语法分析等各种技术手段对程序文件进行扫描从而生成程序的反汇编代码,然后阅读反汇编代码来掌握程序的一种技术[7,8]。
静态分析Android程序主要有两种方法:一是阅读反汇编生成的smali代码,二是阅读反汇编生成的Java代码。静态逆向分析的关键是要找到程序的关键代码,常用的定位关键代码的方法有以下几种[7]:
(1) 特征函数法
通过查找系统关键API来定位关键代码。
(2) 顺序查看法
从软件启动代码开始逐行分析代码。
(3) 代码注入法
通过smali注入代码使得APK自动输出关键数据。
(4) 栈跟踪法
通过输出运行时栈上的函数调用序列来分析执行流程。
(5) Method Profiling
分析程序性能,跟踪函数调用关系等。
在对抗静态分析时,常用的方法是找到某些静态分析工具自身漏洞,针对漏洞做出一些特定的反制措施。常用的静态分析工具有Apktool、Backsmali、smali、Dex2jar、AXMLPrinter2.jar等。
1.3 动态分析
动态分析是在没有软件源代码的情况下,通过调试器跟踪分析汇编代码。对于Android应用,动态跟踪调试又可分为动态调试Android SDK程序和动态调试Android原生程序[7];
动态逆向分析常用的工具有以下几种[7]:
(1) AndBug
AndBug是一款脚本式Android程序动态调试器,通过AndBug可以完成Android程序的无源码调试。
(2) IDA Pro
在IDA Pro6.6之后版本不仅能动态调试APK中的so动态链接库,还可以直接动态调试APK。
(3) Gdb
Android以Linux内核为基础,所以可以使用gdb配合gdbserver进行Android原生程序汇编级调试。
(4) NetBeans、Eclipse
通过apktool在APK中加入调试信息之后可以通过NetBeans或Eclipse完成smali代码的动态调试。
在对抗动态逆向分析时,常用的方法是检测APK是否被调试器附加,比如后文中提到的检测/proc/pid/status文件中的TracePid来判断是否被调试器附加。
2 加固方案设计
2.1 加固方案综述
加固方案共分为两个阶段,主要流程及各阶段完成的功能如图2所示。
图2 加固方案主要流程
加固阶段一主要完成对目标APK的加壳,对受保护的目标代码进行隐藏;加固阶段二通过加载libsecurity.so安全库来完成壳的自我保护,采用的技术包括反调试、防止重打包(通过签名校验保护方式)和防止反编译等。
在加固阶段一和二中主要涉及到5种对象:
(1) 目标APK
目标APK是要被加固的apk,目标APK中的dex文件称为目标dex文件。
(2) 加壳程序
加壳程序负责进行具体的加壳操作,完成的功能主要有:提取目标APK中的dex、lib、res等源数据;加密目标dex并嵌入图片;将加密密钥写入壳APK的dex末尾。
(3) 壳APK
在壳程序运行时完成两件事,首先加载安全库libsecurity.so,然后解密出目标dex并完成动态加载。主要的反调试功能放在安全库libsecurity.so中用C++语言实现,同时加入反调试,签名校验等安全措施。
(4) 防反编译程序
负责在壳APK中的axml文件中插入非法id值,防止APK被apktool反编译。
(5) 加固后的APK
经过签名之后生成最终加固好的APK,该APK可被正确安装及运行,且功能和目标APK相同。
2.2 加壳方案设计
由于Dalvik字节码容易被反编译,攻击者可以轻易地从反编译出的指令代码中找到程序的关键逻辑[9]。为了防止应用程序被反编译,同时对抗静态分析,通过加壳来隐藏真正的dex文件,可以很好地防止攻击者直接反编译出目标应用指令代码。
图3 目标dex加密流程
传统加壳方案直接将加密后的目标dex放在壳dex末尾[10,11],使得壳dex本身携带可疑代码,攻击者可以轻松地识别并静态剥离出加密后的目标dex,通过解密密钥解密出目标dex。为了解决此问题,可以将加密后目标dex隐藏在如图片等资源文件中,可以减弱目标可疑特征,从而对抗攻击者的攻击。目标dex加密流程如图3所示。
目标dex加密时,首先生成随机加密密钥,再读取目标dex并且用AES算法对其进行加密,将加密后的dex嵌入资源文件如png格式图片中,最后再将随机生成的密钥写入壳dex末尾。壳dex文件结构大致如图4所示。
图4 壳dex文件结构
由于壳dex嵌入了解密密钥,结构遭到破坏,需要重新计算壳dex头部中的checksum、signature和filesize几个属性值。
解密目标dex流程如图5所示。
图5 对目标dex解密的主要流程
解密时,首先从壳dex的末尾读取出加密密钥,然后在从存放目标dex的资源文件中读加密dex,使用AES算法对其进行解密,之后得到解密后的目标dex。
壳程序运行主要流程如图6所示。
图6 壳程序运行流程图
在壳程序运行时首先加载安全库libsecurity.so,libsecurity.so中首先通过fork创建子进程,子进程专门用做反调试和签名校验。父进程则负责动态加载dex,首先将原类加载器替换成DexClassLoader加载器,然后分别从壳dex末尾和图片中读出密钥和加密后的目标dex,用密钥解密出目标dex后,动态替换壳application中的关键数据结构,进而完成目标APK的动态加载。 在运行期间,若子进程检测到反调试或重打包,则直接杀死父进程退出程序。
2.3 壳自身保护方案设计
加壳可以隐藏目标APK,但是不能防止APK被动态调试。本节在加壳的基础上加入壳自身保护技术。通过ptrace检测/proc/pid/status和inotify监控关键文件来防止动态调试;通过签名校验防止APK被重打包;通过在AXML中插入非法ID来防止反编译;综合多种保护措施共同保护壳APK的安全。
2.3.1 反调试方案设计(1) 通过检查/proc目录反调试
本加固方案通过定期检测/proc/pid/status中的TracerPid来防止被调试,若检测到调试器则杀死主进程。该反调试流程如图7所示。
图7 检测/proc目录反调试流程图
在Linux系统下一个进程可以通过ptrace系统调用来监控另一个进程的执行流程,gdb等调试软件的核心原理即是通过ptrace系统调用。ptrace系统调用的第一个参数可以指定执行行为,比如当第一个参数为 PTRACE_TRACEME时表示本进程被其父进程跟踪,在父进程中可以使用PTRACE_ATTACH附加至子进程完成调试。ptrace系统调用可以用于进程调试,但是它有一个限制:一个进程不能被ptrace多次。根据这个原理可知,只要在进程中主动调用ptrace就可以实现基本的反调试。
Android使用Linux内核,而Linux内核提供了一种名为/proc的文件系统,通过/proc可以在运行时访问和改变某些内核内部数据结构。 由于proc文件系统只存在于内存中,所以其本质上是一个伪文件系统。在进程运行过程中一些内核状态是在不断变化的,所以通过proc文件系统读取的进程状态也可能是不断变化的。其中的/proc/pid/status文件就存储了进程号为pid的进程状态,通过定时检测/proc/pid/status中的TracerPid,若该字段不为0时表示调试进程的pid,若检测到TracerPid不为0则直接杀死主进程。
(2) 通过inotify反调试
在Linux操作系统下可以通过inotify来通知用户应用程序文件系统的某些变化,可以利用inotify来实现监控某些文件系统事件,比如文件的打开、读写和删除等。而/proc伪文件系统目录下有一些十分关键的的文件可能被破解者利用,比如破解者可以通过/proc/pid/maps查看进程地址空间分布情况,进而通过dd命令直接dump出关键内存区域,所以可以通过inotify来监控这些敏感文件的读取和打开,从而完成反调试。
通过inotify监控/proc/pid/maps文件的关键代码大致如下:
⋮
fd = inotify_init();
sprintf(buf, ″/proc/%d/maps″,ppid);
wd = inotify_add_watch(fd, buf, IN_ALL_EVENTS);
⋮
len = read(fd,readbuf,MAXLEN);
while(i < len){
struct inotify_event *event = (struct inotify_event*)&readbuf[i];
if((event->mask&IN_ACCESS) || (event->mask&IN_OPEN)){
int ret = kill(ppid,SIGKILL);
return;
}
i+=sizeof (struct inotify_event) + event->len;
}
⋮
2.3.2 防止重打包
早期的完整性校验直接放在Java层进行,这时通过直接修改smali代码可以很轻易的绕过校验,示例如下:
const-string v1, ″com/example/checksignature″
invoke-virtual{p0,v1}, Lcom/example/checksignature/MainActivity;->getSignature(Ljava/lang/String;)I
move-result v0
const v1, 0x7b7c6261
if-eq v0, v1, :cond_0
//相等则验证成功,否则验证失败
⋮
在破解时只需将if-eq改成if-ne即可绕过签名验证[12]。本文实现的加固方案中的完整性校验并不放在Java层,而是通过JNI编程放在动态链接安全库(libsecurity.so)中,主要原理是通过反射调用PackageManager类中的getPackageInfo()方法来获取应用程序签名。由于在加载安全库libsecurity.so时做了动态反调试,所以除非破解了反调试,否则无法通过简单的改写代码来绕过签名验证,这样大大增加了应用的安全性。
2.3.3 防止反编译
在Android的manifest.xml文件是Android应用中最重要的文件之一,它存储了一个应用程序运行所需的权限、应用程序的各个组件(activity,service等)、应用程序的入口点等重要信息。但是早期的加固方案没有对manifest做任何处理,这就导致破解者可以很轻易从manifest文件中找到一些关键信息。本文实现的加固方案对manifest文件做了一定的保护,可以有效地增加APK被破解的难度。
axml是AndroidManifest.xml对应的二进制文件,直接解压apk包获得的AndroidManifest文件即是amxl格式。apktool在解析axml文件时有一个漏洞,它会解析amxl中的所有属性id,当遇到非法属性id时就会解析失败。所以可以通过这个漏洞在axml文件中插入拥有非法id的属性,使得apktool等逆向工具解析失败,无法正常反编译。由于android系统不解析非法id,所以apk包还是能够正常安装。
当插入非法属性id时apktool解析失败,防止反编译效果如图8所示(插入非法ID值atest)。
图8 防反编译效果图
在axml文件中插入非法id值之后需要对apk包进行重签名,通过这种方法可以在一定程度上防止反编译,但是又不影响apk包的正常安装。
3 实现与评价
在Android应用市场(http://www.appchina.com/,截至2015年4月)随机选取游戏类、资讯阅读类、系统工具类、生活实用类等5款类型不同且大小也不同的apk(目标APK未加壳)进行加固方案测试,加固前后的应用大小变化如表2所示。
表2 加固后应用包大小变化(单位:字节)
测试结果表明:经过加固后应用程序大致增加了400~500 KB的容量。加壳后APK变大的主要增量是libsecurity.so动态库和解壳工程。
通过对常见的逆向工具测试,加固方案效果如表3所示。
表3 加固方案对抗逆向工具评测
注:√表示加固方法对某逆向工具有效,Χ表示加固方法对该逆向工具无效
测试结果表明:不同的加固方法对不同的逆向工具有效。通过apktool漏洞可以使得apktool反编译失败;加壳可以使得apktool、dex2jar或JEB无法反编译出真正的dex;通过ptrace检测/proc文件夹等反调试措施可以防止IDA Pro、NetBeans的动态调试;签名校验可以防止NetBeans动态调试。综合上述,所有加固方法可以对目标APK起到有效全面的保护。
4 结 语
通过对Android安全机制的研究,分析常见逆向攻击机制,设计了一种移动应用加固方案。该方案将加密后的目标dex嵌入图片可以有效隐藏目标dex,实验表明,加壳保护技术可以有效防止目标APK被静态分析。使用ptrace和检测/proc/pid/status和inotify监控关键文件,可以有效完成反调试保护。此外,加固方案还加入了JNI层的签名校验,能有效防止重打包。加固方案的最后一个阶段还利用apktool解析漏洞添加非法id值,使得壳APK无法被反编译。通过测试,加固后的APK文件会大致增加400~500 KB的容量,对APK运行或加载效率造成较小影响。本加固方案没有实现对抗dump内存的逆向攻击方法,攻击者仍然可以通过内存拷贝等方式,找到目标dex代码。
本加固方案仍然有需要改进和完善的地方,在接下来工作中将会着重研究:1) so加壳保护。dex加壳无法防止内存dump,但通过so加壳保护则能大大增加内存dump的难度。2) AndroidManifest.xml配置保护。AndroidManifest.xml配置文件作为应用程序的“说明书”,当前加固方案对其的保护不足,这是另一个研究重点。
[1] 工信部.移动互联网白皮书[R].北京,2014.
[2] 巫志文,李炜.基于Android平台的软件加固方案的设计与实现[J].电信工程技术与标准化,2015(1):33-37.
[3] 吴善崇,张权.Android平台安全机制浅析[J].实验科学与技术,2014,12(2):43-45.
[4] 徐剑,武爽,孙琦,等.面向Android应用程序的代码保护方法研究[J].信息网络安全,2014(10):11-17.
[5] 梅瑞,武学礼,文伟平.基于Android平台的代码保护技术研究[J].信息网络安全,2013(7):10-15.
[6] 伍景珠.基于Android平台的软件保护方案的研究与实现[D].北京邮电大学,2013.
[7] 丰生强.Android软件安全与逆向分析[M].北京:人民邮电出版社,2013.
[8] 张志远,万月亮,翁越龙,等.Android应用逆向分析方法研究[J].信息网络安全,2013(6):65-68.
[9] 刘劼.Java反编译技术和代码安全[J].现代电子技术,2004,27(10):22-24.
[10] 姚为光.软件加壳技术的研究[D].电子科技大学,2011.
[11] 李文.基于壳技术的软件保护研究[D].电子科技大学,2012.
[12] 李宇翔,林柏钢.基于Android重打包的应用程序安全策略加固系统设计[J].信息网络安全,2014(1):43-47.
AN ANDROID APPLICATION REINFORCEMENT SCHEME
Zhu Hongjun1,2Chen Yaoguang1Hua Baojian1,2Chen Hao1
1(School of Software Engineering,University of Science and Technology of China,Hefei 230051,Anhui,China)2(Suzhou Institute for Advanced Study,University of Science and Technology of China,Suzhou 215123,Jiangsu,China)
Nowadays, the problem of Android application security is increasingly prominent. A large number of Android applications encounter the attacks such as reverse, illegal copy and malicious code injection, etc. We studied the security mechanism of Android applications, based on analysing static reverse and dynamic reverse attacks principle, we put forward a reinforcement protection scheme for mobile applications. The scheme comprehensively uses the application reinforcement technologies of shelling, anti-debugging, signature verification and anti-decompiling, etc., to reinforce target applications. Through a series of tests, it is proved that this scheme can well resist common reverse attacks of static analysis and dynamic analysis.
Reinforcement Anti-debugging Android security Shelling
2015-06-25。教育部-谷歌校企合作专业综合改革项目([2014]14);苏州市科技计划应用基础研究项目(SYG201406)。朱洪军,讲师,主研领域:软件测试,移动应用安全。陈耀光,硕士生。华保健,讲师。陈灏,硕士生。
TP309
A
10.3969/j.issn.1000-386x.2016.11.067