主持人导语:
近些年来,移动APP数量呈现爆炸式的增长,黑产也从原来的PC端转移到了移动端,伴随而来的逆向攻击手段也越来越高明。在解决加固产品容易被脱壳的方案中,代码混淆技术是对抗逆向攻击最有效的方式之一。但目前的移动端加固技术真能抵御黑客的攻击吗?
本报告将分享阿里巴巴集团安全部应用加固能力养成记,重点介绍Android加固对于端上的业务风险控制是如何做到自动化部署和分析,更快捷的感知安全风险,以便快速做出响应,减少不必要的业务损失。
有请阿里巴巴安全专家乱武。
嘉宾演讲正文:
非常感谢各位来宾来到这里,欢迎来到云栖大会移动安全论坛。
今天讲的大的标题叫【APP加固新方向】,副标题主要讲阿里巴巴对Android加固的基础介绍,以及在开发这个产品过程中遇到的一些问题和一些复杂场景的适配过程。由于大会给我的时间是25分钟到30分钟,整个安卓加固方面涉及的技术点还蛮多的,为了让这次的分享有一个比较聚焦的点,所以这次的分享主要是讲Android java代码方面的保护,也就是说编译成Android安装文件之后,Dex文件方面的一些基础保护点。
我这次分享的主要有三个部分,第一部分主要是要介绍一下Android java代码保护的技术。第二部分介绍一下应用加固在复杂业务场景下的挑战以及遇到的一些问题。第三部分说一下未来对于Android java代码保护的一些思路,跟大家分享一下,也许大家将来会碰到,或者有相应的一些启发。
第一部分主要讲Android java代码的技术保护,刚才主持人做了一个小的问卷调查,这里不少同学不是开发,我大概介绍一下这个上下文。
本身安卓手机大家不陌生,苹果和安卓,目前是业内主流的两大智能操作系统。安卓本身的开发环境是用java语言开发的,它有一个特点。由于java语言生成的文件是在虚拟机里面执行的,必然要保留大量的语义,虚拟机能够认识可执行文件的时候保留了很多的语义。这就带来一个问题,既然编译生成这种Dex格式,保留了大量的语义,而这种格式谷歌对外完全公开的,恶意者通过反编译达到看到原来Java代码的这种目的。不管是阿里巴巴还是其他安全加固的友商,对安卓java代码的一个保护,也就是安卓上面Dex文件的保护,从加固服务产生到现在一直是重点。
我简单介绍一下Android java代码以及Dex代码保护迭代介绍,业内主要总结了四代的保护方案。
第一代加固本身这种方案刚出来的时候,大概是2013到2014年之间,本身这个加固方案对于安卓本身的可执行文件,也就是Dex文件的保护,相当于是在打包的时候,就是生成整个安卓安装应用包的时候会对Dex进行加密。加密算法各种各样,可以用AES,也可以用其它的。在运行的时候通过一个自定义的类加载器进行解密,真正在运行的时候是完整的原来应用开发者编译出来的Dex文件。这种加固的特点一目了然,你拿到文件的时候,既不知道密钥,也不知道加密算法,看不出来文件的整个逻辑,这个时候一定程度上能够防住,通过一些开源的工具逆向分析这个Dex文件。
缺点也很明显,因为它简单,因为运行时用一个自定义的类加载器在加载的时候解密,把这点拦掉,就把这个壳脱了。
那通过一段时间的发展以后,又出现了第二代的保护方案,就是类级别的Dex保护。我前面介绍了其实Dex文件来说,它的格式是公开的,公开就意味着这里面的某些类、某些函数保护的code在哪一个位置,其实所有人都能够知道。所以第二代的保护方案,相当于把Dex里面要保护的核心函数抽离出来生成另外一个文件,利用一个虚拟机类加载机制。本身虚拟机有一个特点,它必然会掉到这个类里面的一个方法,我们已经在打包的时候,将这些核心函数保留在已知的位置,通过这个函数调用我们的修复函数,然后将所有的业务逻辑进行修复。这样的一个方案,其实主要的原理还是利用虚拟机类加载机制的特点来达到一定保护的效果。当然这种保护有一个特点,我们如果简单来说,即便是我把它抽离了,最后运行的指令是谷歌支持的Dex标准指令,这点大家要注意。
第三代跟前面两代完全不同,因为谷歌的Dex指令是开源的,不管第一代第二代,内存中运行的都是谷歌的标准指令,所以从原理上一定有办法将这个指令完全逆向出来,然后把它反编译出来。但是第三代就有一个质的区别了。其实第一步和第二代是一样的,也是在编译打包的时候将Dex的核心函数抽离的,抽离后,翻译成一种自己定义的指令,用自己的一种编译指令进行翻译,把这个指令变一个种,变成其他的指令,这个时候运行的时候通过自己的解释器来解释执行,是自己定义的相关指令,这是跟第二代有质的区别的。我在内存中运行的指令,在某些保护的函数里面就一定不是谷歌的标准指令了,这点能够很有效的防止内存直接拷贝等破解方案。
第四代,也是行业目前公认的方案,就是java 2C的保护方案,这种更简单直接,它的原理很清晰,我们如果作为一个开发者,在开发java代码的时候,不管是原来传统的PC上的java虚拟机还是谷歌的虚拟机,java代码一定是可以翻译成用C代码来表示的。比如说写一个java代码的函数,从原理来说其实只要不嫌麻烦,我一定能够利用虚拟机漏出的接口写成C代码,这种保护方案直接从根源上解决这个问题。你认为核心要保护的函数,我们直接在编译打包的时候将这些函数翻译成C语言的代码,然后再用编译器编译成一个so的文件,也就是这个CPU支持的一个二进制code,这样达到了比较好的保护。
这基本上就是我介绍目前安卓移动端对于java代码保护的四代技术。
因为第一代和第二代技术相对比较简单,我介绍一下第三代和第四代技术整个的框架流程图。
自定义解释器的Dex保护方法第一部分应用打包和普通的是没有什么区别的,最终生成的也是安卓系统能够认识的一个可执行的文件。但是到了第二步和第三步的时候就有点区别了,第二步要经过一个加固的工具链,因为也是一个安卓的可知性的文件,首先要找到一个Dex文件,抽取核心函数指令,然后埋点一些hook接口,接下来打包还回apk文件,签名后应用发布。蓝色的部分表示是在运行时,黄色的部分是没有安装到用户手上,蓝色的部分应用已经发布了,然后在用户的手机上执行的一个逻辑。因为第二步埋点了hook接口,就进入一个自定义的一个解释器,根据传译的二进制的code翻译成原来Dex文件想保护的java那个code的逻辑,完成了第三代的保护效果。真正在解释的时候解释执行的就是这种变种的指令,然后达到正常执行业务逻辑的效果,这是第三代的一个自定义解释器Dex保护方案的介绍。
说一下第四代java2C保护方法的介绍,前两步也跟第三代是一样的,就是开发者自己编译好的一个安卓可安装程序。不同的是,直接简单粗暴将需要保护的Dex核心函数直接翻译成C代码。比如一个编译好的Dex文件,直接把这个函数编译成C代码,可以自定义一个编译器翻译成一个C代码。C代码还是很成熟的,可以用各种各样的编译器,包括谷歌以及第三方编译器,这样完全去除了里面的核心指令,就变成看这个手机或者这个架构支持的Dex文件。这里面运行的时候又有不同,因为这个时候是需要把SO给打进APP里面,因为这些代码其实已经编成SO,要加到apk里面,所以核心函数一般加一个native标签,调到本身的SO里面,后面的执行顺理成章了,就是本地指令的执行保护函数,能够达到完全的正常执行的业务逻辑。
第一部分大概介绍完了,包括目前比较流行的新的技术,就是第三代和第四代,但是其实我这边要说一下第三代和第四代的一些缺点。
从刚才的介绍看起来比较美好,但是到了第三代的自定义解释器的时候,这个时候由于要hook系统的一些接口,普通的应用开发者要遇到很多碎片化的问题,对于加固要调用大量的系统私有api的安全服务来说,可能遇到的问题更显突兀,所以基本上在第三代的自定义解释器的保护方案,不是说不强,但可能遇到碎片化的东西比较多,到第四代其实反倒碎片化比较小,为什么大家比较推崇java2C的保护方案,是因为把java代码翻译成C代码再编译成SO,是完全符合很多虚拟机的开发规范的,这样的兼容性问题最小。当然这两个场景也有一个问题,它编译出来的函数可能会体积变大,可能执行效率变低,但是这些都是一些具体的细节,今天的时间有限,就不再详细赘述了。
第二部分介绍一下加固在复杂业务场景下的挑战。
安卓从几年前大家也不是特别看好的一个智能操作系统到现在成为全球第一大的智能操作系统来说,自身的操作系统是有一个不断的迭代,包括今年最新发布的android O,我们能看到它的进步。在安卓上面开发各种业务其实现在已经变得很复杂了,就以我们阿里巴巴公司比较旗舰类型的应用,比如手机淘宝和支付宝的应用,应用开发的流程和开发使用的黑科技的各种技术,已经是不亚于传统上面的一些PC上面一些比较复杂的客户端开发的程度了,所以加固在服务于这些应用的时候,也面临着一些复杂业务场景的挑战,我做一些简单的介绍,举两个例子。
Hotpatch应用场景,安卓应用如果是说在你的应用发布以后,到了用户的手机端上以后如果发现一些bug,按照传统的方案有一种解决方式,用户升级再更新一个版本、重新安装一遍把这个问题解决了。但是在对于很多复杂的应用来说,比如说像手机淘宝或者天猫或者支付宝,它本身的一个安装包就已经70多兆了,比如里面有一个很小的bug,比如哪一个页面显示不对,让用户完全的升级一遍,重新下载包升级一下,用户体验不是很好,他们业务方开始研究一种技术,当我发现某一个地方某一段代码有问题了,这个时候我就只修改这一部分代码,而不需要用户安装整个发布包。我们这个原始应用有三个类,分为A/B/C,当我有问题的时候,比如我发现的问题是某一个代码有bug了,假设B类有bug,我只需要把他弄成一个Dex文件,把这个下发下来,热部署以后把原始APP里面的Dex文件修改一下,让它变成只有A和C这两个类,然后再通过一些类加载器里面寻找Dex的顺序,达到首先执行我的热部署后的这个文件,当需要调用B的时候再执行B这个类,达到修复这个原有程序业务逻辑bug的目的,或者我想更新达到更新的效果,当然这种方案目前在安卓上面运行的还是比较多的,包括阿里也有发布热补丁的。原来这种在苹果上面也会应用,但整个来说苹果不让用了,谷歌想在明年P的版本可能会全面限制这种技术方案,但是目前来说发布O的版本目前还没有这个限制。但是这种方案其实在加固来说,从加固的原理来说有一点冲突,热部署后的dex文件会出现一点问题,这是加固服务发展过程中发现的一个比较复杂场景的举例。
第二种场景大家更不陌生,由于业务发展越来越复杂,开发规模稍微大一点的应用,需要插件化的部署,比如手机淘宝集成了各种各样的服务,这时候要多个团队协作开发,这就无可避免要利用我们所谓的插件化的思想,然后分步开发。这时候传统的加固面临一个问题,最初我们主要是保护主Dex文件一些根目录下的Dex文件,插件会埋到lib下面很深的地方,导致加固刺出现盲点,作为一个通用化的一个加固方案很难做到将所有的Dex文件也保护,但是有些是比较核心的。刚才我提的这两个例子,不管是阿里巴巴还是很多友商,这个问题都得到很好的解决,具体的细节我就不赘述了。
最后一部分介绍一下业内认为未来对于安卓java代码一种保护手段的思路。
第四代java2C保护方案是用编译器编译成一个标准的SO文件,其实标准的SO文件开发者都很清楚,这种elf格式也是透明的,相当于本身这种格式也是完全有规范的,是透明的,也是可以逆向的。既然可以把DEX文件翻译成c代码,那么也可以用支持vmp虚壳的编译器在编译成内置于vmp虚壳的保护的so。
风险设备的控制,我们做安全来说从原理来说,如果不是特别计较成本的话,其实只要在端上运行的东西,特别谷歌开源运行的,其实原理上是能够破解的,只不过你花费的时间长短和破解成本的高低,所以在手机淘宝和电商的超级应用,保护思路越来越从端上防护倾向于端和云联合的防护。在端上在打包的时候会埋入很多点,不管是安全adk还是本身加固工具链的方式,获取一个端上唯一的标识,我们称为一个设备指纹。像下单这种行为会加入这种设备指纹的请求,然后服务器通过这个指纹识别进行风险控制,比如我们认为是恶意者操控的设备,可以加入黑名单,不会让这个应用崩溃。比如你抢红包抢不到,你买东西买不成功,给用户一个反馈,这种其实保护效果是相当好的。
我的分享结束了,最后感谢大家的聆听,谢谢。
——————————————