近年来,随着移动互联网的蓬勃发展,安全问题越来越严重,而在这些设备中,Android系统占了80%以上,各种反编译、脱壳、二次打包工具层出不穷,本文从Android Dex讲起,最后介绍爱加密新一代的移动安全纵深防护方案。
Android Dex简介
Android 开发中的中间层一般以Java实现,并且采用特殊的Dalvik虚拟机,Dalvik是非Java标准的Java虚拟机。JVM是基于堆栈实现的,而Dalvik是基于寄存器的,据说是Google为了规避法律风险为Android定制的。
Dex是Dalvik VM executes的全称,即Android平台上可执行文件的类型。Dex文件是Dalvik字节码集合并非Java的字节码。在编译Java代码之后,通过Android平台上的工具可以将Java字节码转换成Dex字节码。
Android So简介
一般来说,安全圈公认的Dex的安全性不如So,主要是因为Dex容易被各种反编译工具直接还原成通俗易懂的Java代码,而So中为原生机器指令,难以还原原始代码。
So的全称是Shared Object,即共享对象,是从一个或多个可重定位对象生成的不可分割单位。共享对象可以绑定到动态可执行文件以形成可运行的程序。顾名思义,共享对象可以被多个应用程序共享。部分类Unix操作系统如Linux的动态链接库是So,Android系统是一种基于Linux的开源操作系统,它的动态链接库也是So,类似于Windows上的Dll。
基于安全以及性能的原因,Android开发者一般会把核心算法和重要业务逻辑用C/C++编写放在So中。所以对于企业来说,这些C/C++代码的安全性就显得非常重要,因为它是APP最后的防线,一旦被攻破,将会造成不可估量的损失。
C/C++代码面临的风险
So虽然不能被反编译,是不是就是完全安全没有风险呢,下面我们一起来看看So的一些常见安全风险:
敏感信息可见
我们知道App是给人用的,这就涉及到人机交互UI,不管是控制台程序还是带图形界面的程序,都要给用户友好的提示,而提示信息一般是以字符串展现的。
如下图使用IDA打开So文件后,可以直接看到源码中的敏感字符串。
解决:开发者可以将字符串使用ASC2BCD保存为字节数组加密存放,在使用前用BCD2ASC解密。一般安全厂商使用基于OLLVM的字符串加密混淆,预先给字符串加密,在函数头或者使用前解密。
缺陷:攻击者可以通过动态调试跟踪,而且网上有很多针对OLLVM的开源反混淆的工具。
静态分析
So是由代码和数据构成一种ELF文件,我们可以使用IDA打开So文件查看反汇编代码进行静态分析,使用F5查看对应函数的C伪代码。
解决:对So进行混淆可以一定程序上对抗静态分析,加大逆向的难度,但是逆向者还是可以看到汇编代码。一般安全厂商使用So加壳加固,对代码段进行加密压缩处理,在So加载时自动调用壳代码进行解密。
缺陷:动态调试,在加载完壳后解密位置dump内存。
动态调试
使用IDA对So文件进行动态调试,能够看到内存的内容以及寄存器的值。
解决:运行时保护反调试,通过检测ptrace、调试器进程名、调试器端口、进程等等,如果发现是在调试运行,则退出应用。
缺陷:单一的防护手段太弱,一般可以通过IDA Patch So修改代码段或者运行时修改内存和寄存器的值绕过。
鉴于传统的防护手段加固强度不够,容易被逆向者破解,爱加密潜心研究,打造出了基于VMP的第7代纵深防御体系,包括:SecLLVM安全编译器、JAVA2CPP加固、So Linker加固等。
SecLLVM安全编译器
基于OLLVM深度定制的混淆,增加了块调度、间接跳转等,能防IDA F5。
基于LLVM开源框架实现的虚拟机编译器,在编译时直接对指定函数进行虚拟化保护;
自定义的IVM虚拟机指令集,支持随机指令集,每次编译产生不同的指令集;
虚拟机指令动态热更新(云更新),不写文件,直接将更新后的字节码在内存中解密运行;
基于以上特性,代码一旦加密,攻击者无法通过调试解密还原出代码,分析核心业务逻辑。
编译时将C/C++中的函数所对应的原始代码编译成虚拟机字节码,当程序执行到该代码时,调用虚拟机解释器执行相关字节码,执行完成后退出虚拟机继续执行本地代码。代码虚拟化保护能够使函数都变成统一的虚拟机入口调用,防止加固后的代码被反汇编和静态分析,有效C/C++保护代码逻辑,其实现原理如下图所示:
下图是原始的C++源码:
下图是上面的源码编译成SO后在IDA中F5看到的效果:
下图是使用SecLLVM安全编译器进行虚拟化保护后在IDA中F5看到的效果:
可以看到原始函数被虚拟化之后代码逻辑完全变为单一的虚拟机入口,不管多么复杂的函数成功虚拟化之后,最终出来都是这样一个无比单纯的白板。
JAVA2CPP加固方案
JAVA2CPP是新一代Android Dex加固方案,先将Dex文件中的Java代码翻译成C++代码,再把这些C++代码编译成动态库,并将原有的字节码进行清空,将JAVA中指定保护的方法转换成了Native方法。JAVA2CPP可以保护Dex静态条件下的安全,并且能有效的防止破解者通过动态调试的方式获取Dex的Java代码。同时配合我们的混淆、虚拟机及So Linker方案,使保护强度达到最大。
下图是从APP包中反编译出来的Java代码:
下图是上面的Java代码中的函数dispatchKeyEvent转为本地代码后在IDA中F5看到的效果:
可以看到Java代码已经转为了本地代码,逆向者不管用什么工具都无法将函数dispatchKeyEvent反编译回Java源码了。
So Linker方案
So Linker是新一代的Android So保护技术,其实现原理是先将要加固的So文件作为数据加密压缩插入另外一个So中,然后运行时先将数据解密,然后动态加载链接解析符号。So Linker加固后可以达到符号隐藏、反调试、反Dump功能。
下图是使用So Linker加固前代码段中的内容:
下图是使用So Linke加固后代码段中的内容:
可以看到代码段已经完全不一样了,因为我们已经偷天换日,换了一个So文件,不再是原来那个So了。
下图是未使用So Linker加固IDA中看到的函数列表:
下图是使用So Linker加固后IDA中看到的函数列表:
可以看到所有的函数名都已经隐藏起来了,因为新的So中缺省就是隐藏符号的。
下图是使用IDA attch到应用准备调试加固后的So时IDA的报错:
可以看到IDA已经不能调试加固后的So了。
注:本文来自 走进企业看安全 爱加密站 技术分享文章
本文作者:肖巍
爱加密虚拟机保护研发人员,负责安全产品中的虚拟机保护类产品的方案设计、监督以及落实。有丰富的虚拟机保护项目开发经验,资深程序员,对虚拟机有深入了解。
更多阅读:
[原创]2018CTF团队赛 第十一题 伊甸园
[原创]过用户层HOOK 驱动层SSDT HOOK (之进程保护篇)
看雪CTF.TSRC 2018 团队赛-第11题
[原创]利用无线ADB调试设计缺陷监控Android设备