Android 应用程序加壳技术已经逐步趋于成熟,应用程序加壳成为一种常态。市场上比较多的企业提供安全加固服务,如梆梆安全、爱加密、360、百度、阿里、腾讯、几维安全、通付盾、网易等等,还有一些其余的加壳技术,如APKProtect。咱们有时也会发现有些恶意程序也利用了加壳技术,以达到逃避安全软件的检测,增长逆向难度的效果,本篇文章主要是分享Android 加壳技术原理的简单过程,以及加壳代码经常使用的保护手段,让你们对从概念上有个简单的认识,以便在平常工做中遇到这样的问题,知道该如何应对,因为篇幅缘由,分上下两部分进行分享。java
壳的简单模型:编程
图1 加壳程序的简单模型安全
如图1所示,这个是最初的加壳模型,将原程序的dex文件进行加密保存,而后修改应用程序的配置文件Manifest.xml,让程序的入口为壳的类,保证应用程序运行时,优先执行壳的代码,以便在壳的代码中实现对应用程序的加密数据进行还原与修复。微信
其中,在这个过程当中,主要涉及三个部分:app
(1)加壳程序:加密原应用程序核心代码,有的是单独存储,如:assets/路径下,有的是添加在壳代码的后面,而后修改指定的参数,修改应用程序的配置文件Manifest.xml替换原程序的Manifest.xml文件;函数
(2)解密程序:解密加密的原代码数据,运行时,经过DexClassLoader函数动态加载,解密完成后,再修复指定参数;工具
(3)原程序:待保护的代码。测试
加壳技术出现后,与之对应的脱壳技术也随之出现,各类大会分论坛都在介绍本身研究的通用脱壳方法,最先公开的是基于xposed的ZjDroid,以及基于源代码的DexHunter,还有各类五花八门的脱壳方法,如:drizzleDumper,inotifywait、dumpDex、dex2oat等等,固然加壳技术也在不断提升,对抗脱壳技术,下面咱们了解一下这些年加固技术的演进过程,也算是加固技术的发展史。flex
第一代:第一代加固技术主要是代码混淆技术,经过对源代码进行压缩、优化、混淆等操做,提升代码阅读的难度,简单运行原理如图2所示:优化
图2 第一代加固技术的原理简图
典型的内容是采用混淆技术,使用a、b、c、d这样简短而无心义的名称,对类、字段和方法进行重命名。
第二代:第二代加固技术主要是对原始应用程序中的dex文件加密,并外包一层壳,将核心代码进行隐藏,以达到保护应用程序的目的,简单运行原理如图3所示:
图3 第二代加固技术的原理简图
典型的内容是dex文件被加密后隐藏了dex文件中的类和方法函数,攻击者只能看到安全假装的入口类和方法函数,看不到被保护的原始应用程序文件里的类、方法函数以及方法内容。
第三代:第三代加固技术主要是基于类和方法的代码抽取技术,旨在解决第二代加固技术没法抵御攻击者经过动态分析方式进行攻击的问题,简单运行原理如图4所示:
图4 第三代加固技术的原理简图
典型内容:第三代加固技术对dex文件中全部的类及方法函数内容进行抽取、加密和隐藏,单独加密后存放在应用程序中的特定文件内。攻击者进行静态逆向分析时没法查看被保护的类内代码,当Android虚拟机要执行应用程序的某个方法时,加壳引擎才读取该方法被保护的代码进行解密,并将解密后的方法代码以不连续的碎片化代码形式存放在内存中。
前三代加固保护技术涉及的dex文件总体加密、dex类运行时保护和dex运行时方法保护等都属于程序加壳的范畴,从本质上来讲都是代码隐藏技术,最终仍是须要经过Android虚拟机执行“解壳后”的原始代码。攻击者能够对原生Android虚拟机进行修改,构建一个以“脱壳”为目标的定制化虚拟机。当加固后的App在定制化虚拟机中运行时,虚拟机中的“脱壳”程序实时捕获App实际运行时在内存中释放的原始执行代码,还原出App的原始dex文件,达到脱壳的目的。
第四代:为了应对攻击者经过动态分析实施攻击行为,第四代加固技术-dex虚拟机保护(dexvirtual machine protect,DVMP)技术应运而生。DVMP技术具备自定义虚拟机、指令集和解释器。当应用程序运行时,在自定义的虚拟机中,经过自定义的指令解释器对被保护的代码进行解释执行,攻击者经过内存转储只能还原出自定义的指令集,没法还原被保护的原始指令,简单运行原理如图5所示:
图5 第四代加固技术的原理简图
总体来看,从代码混淆,到文件保护,再到类保护、方法保护,最后到指令保护,从静态保护到动态保护,对应用程序的安全防御由浅入深演变。更通俗一点的比方:若是把应用程序看做咱们的家,把应用程序的核心资产看做家中的财物,那么四代加固技术就至关于家的四道安全防御。第一道防御,即代码混淆技术,能够看做咱们的小区大门,强度不够,只可以增长攻击者寻找地址的难度;第二道防御,即文件总体加密技术,能够看做家所在大楼的楼门,起到保护整栋楼的做用,可是强度有限,攻击面是整栋楼,找到突破点并不是难事;第三道防御,即代码抽取保护技术,能够看做家门,保护家里全部的财产,保护粒度更细,进一步增长了攻击者的成本;第四道防御,即虚拟机保护技术,能够看做家里的保险箱,保护家庭最核心的资产,强度最高。
更多详细的内容,请阅读《Android应用安全测试与防御》

加壳技术经常使用的保护手段
常见的保护手段有Manifest.xml文件反编译失败,伪加密、花指令、反调试、CRC校验、调试器检测、ELF Header修改,SMC技术等。
Manifest.xml文件反编译失败
一般使用apktool反编译,会报异常错误:java.io.IOException:Expected: 0x00080003, got: 0x000800XXX ,这种问题是AndroidManifest.xml的头部几个字节被修改了,致使apktool工具没法识别。
使用十六进制工具,如:010Editor,查看头是否是被修改了,【03 00 08 00】是正常的。
图6 修改前和修改后对比图
还有一种状况是,StringBlock中并不包含styleString,styleCount的值为0,若是是其余值也会报错。
伪加密
伪加密,这是早期的加密手段,实际上是压缩包某些字节被修改了,使用rar压缩软件打开须要密码,使用apktool反编译会失败。伪加密的APK压缩包打开后如图7所示,文件后面多了*号,须要解密密码,如图7所示。
图7 伪加密打开文件的界面
其修改原理很简单,是使用十六进制工具,打开压缩包文件,经过十六进制的方式查找连续4位字节 ”50 4B 01 02”,找到后,顺延到第5位字节,奇数表示不加密,偶数表示加密,修改成00便可,如图8所示。
可是,伪加密在Android4.2.x系统发布之后,这样伪加密的应用程序将没法安装,所以,这种方式就再也不适用了,你们知道有这个问题便可,没必要深究。
花指令
花指令是对抗静态反汇编分析的一种手段,在PC端使用很是普遍,目前移动端也存在,它是向正常代码中插入花指令,可以使静态反汇编工具对指令的分析失效,从而使分析结果出现混乱、错误,同时花指令能够增长逆向分析人员的工做量,以达到防御静态分析的效果。
花指令的去除方法,一般是用反汇编软件(一般指IDA Pro)载入ELF文件,分析代码指令的规律(一般的花指令都会存在必定的规律),而后编写IDC脚本或者IDAPython脚本将垃圾指令批量NOP掉,保留真实的代码,达到去除花指令的效果。
下面以某恶意软件为例,分析花指令的去除方法,如图9所示,该动态库文件使用IDA加载后存在大量的花指令代码:
图9 存在花指令的代码
通过分析发现,该样本的花指令模式是这样的:
LDMFD SP!, {R0}真正的指令 STMFD SP!, {R0}ADRL R0, loc_value SUB R0, R0, #4BX R0
也就是说,上图中MOV R0,#1为真实指令,若是不处理花指令,分析起来很是麻烦,幸亏IDA提供了一个脚本引擎,让用户从编程的角度对IDA的操做进行全面的控制,编写IDA脚本的语言简称IDC,IDC是IDA内置的类C语言的脚本语言,总共提供了100多个内建函数下面咱们利用IDC脚本撰写去除花指令的代码,再编写代码以前,咱们先简单了解一下IDC脚本经常使用的函数。
IDC脚本经常使用的函数:
ScreenEA() //得到当前IDA代码视图中的光标所在的地址。这个函数的做用一般能够做为脚本的起始地址。GetInputFileMD5() //得到当前载入文件的MD5值。这个函数的做用一般做为校验文件的修改变化。
Firstseg() //获取首个区段的起始地址。在脱壳解密区段时常用。NextSeg(long Address) //根据指定的Address返回下一个区段的起始地址。SegByName(long SegmentName) //根据指定的区段名,返回区段的起始地址。SegEnd(long Address) // 根据指定的Address,返回当前区段的结束地址。Segments() //返回各个区段首地址列表。
Functions(long StartAddress, long EndAddress) //返回起始地址StartAddress与结束地址EndAddress之间的全部函数。LocByName(long FunctionName) //根据指定的函数名称返回相应的函数地址。GetFunctionName(long Address) //根据指定的地址Address,返回该地址所在的函数名称。
CodeRefsTo(long Address, bool Flow) //根据指定的目标地址Address,返回一个指向此处的代码引用列表。CodeRefsFrom(long Address, bool Flow) //根据指定的源地址Address,返回一个由此地址发出的代码引用列表。DataRefsTo(long Address) //根据指定的目标地址Address,返回一个指向此处的数据引用列表。DataRefsFrom(long Address) //根据制定的源地址Address,返回一个由此地址发出的数据应用列表。
PatchByte(long ea, long value) //Patch一个字节。PatchWord(long ea, long value) //Patch一个字。PatchDword(long ea, long value) //Patch一个双字。FindBinary(long EA, bool downorup, long value) //查找二进制数据,并返回查找到的二进制数据的起始地址。
编写IDC脚本代码 ,去除花指令:
static main(void){ auto EA,Counter; auto Address; auto name; EA = ScreenEA(); Counter = 0; while(EA != BADADDR) { Address = FindBinary(EA, SEARCH_DOWN, "E2 04 00 80 E2 04 00 40 E2 10 FF 2F E1"); name = GetFunctionName(Address); Message("Address=%lx name=%s\n", Address, name); if(Address == BADADDR) break; PatchDword(Address - 15, 0xE1A00000); PatchDword(Address - 7, 0xE1A00000); PatchDword(Address - 3, 0xE1A00000); PatchDword(Address + 1, 0xE1A00000); PatchDword(Address + 5, 0xE1A00000); PatchDword(Address + 9, 0xE1A00000); Counter++; EA = Address + 1; EA++; } Message("这是 %d 花指令代码块.\n", Counter); Message("完成.\n");}
编写完成后,使用IDA->File->Script file加载执行便可,如图10所示:
图10 IDA加载IDC脚本方法
执行完后,花指令就NOP掉了,再看上面代码如图11所示,真实的指令并无几条:
图11 去除花指令的代码

本篇暂时分享到此,未完,待续……
本文分享自微信公众号 - App安全红宝书(apphongbaoshu)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。