作者:蒋晓燕,单位:中国移动智慧家庭运营中心成都业务支持中心
随着人们对智能手机依赖的程度越来越高,因智能手机软件安全导致用户信息泄露、财产损失事件频发,人们开始越来越关注App安全这个领域。从开发的角度来讲则是App源代码安全问题,基于代码审计层次的;从软件保护角度来讲app安全则是对源代码的代码混淆,资源加密,逻辑加固;从用户的角度来讲就是App本身的安全性,是否会对个人隐私财产造成损失。
Part 01● 为什么要进行加固? ●
Android操作系统全部开源,可以对Android应用程序Apk进行解包,对DEX文件进行逆向工程,就可以获取到原始的源代码和应用程序逻辑。通过逆向工程可以窃取应用程序的核心代码、攻破程序逻辑限制、篡改应用程序或注入恶意代码、嗅探应用服务器API接口。
通过应用加固可以增加软件的逆向成本,保护软件的利益不受损坏,保护软件版权,防止应用被破解,保护应用程序的关键信息不被窃取。
Part 02● Apk加固的原理 ●
应用程序Apk的关键信息都保存在DEX文件中,加固重点是保护DEX文件不被逆向。常用思路就是打包时使用私有算法改变DEX文件结构,无法使用常规手段读取DEX文件内容;由于DEX文件结构发生了变化,宿主机也无法读取识别DEX文件,就需要应用程序提供自定义的加载程序,完成DEX文件的解密与加载。
Apk加固后主DEX文件已经是看不到原码,这样就达到了加固的效果,但是这样做会导致主App的Application已经是没有效了,可以通过自定义Application生命周期里的创建函数attachBaseContext和onCreate函数,在attachBaseContext中将解密出来的DEX交给系统处理,onCreate的时候要将主App的Application创建出来,并替换系统中的Application引用,这样就可以保证Activity顺利加载执行。
Part 03● 加固发展过程 ●
Apk加固技术,前后经历了四代技术变更,保护级别在每一代都有所提升。
3.1 第一代加固:动态加载
原理➪ 第一代加固技术用于保护应用的逻辑不被逆向与分析,主要基于Java虚拟机提供的动态加载技术。开发阶段中将程序切分成加载(Loader)与关键逻辑(Payload)两部分,并分别打包;运行时加载部分(Loader)会先运行,释放出关键逻辑(Payload),然后使用Java的动态加载技术进行加载,并转交控制权。
缺陷:第一代加固技术的缺陷是动态加载机制要求其关键逻辑(Payload)部分必须解压,并且释放到文件系统,这就给了攻击者机会去获取对应的文件。攻击者可以通过自定义虚拟机,拦截动态加载机制所使用的关键函数,在这个函数内部,复制文件系统中的关键逻辑(Payload)文件。
3.2 第二代加固:不落地加载
原理➪ 第二代加固技术主要为了解决解密后的Payload需要写入文件系统被窃取的问题,由代码接管Application对象的创建,主要流程:
1)Loader被系统加载。
2)系统初始化Loader内的StubApplication。
3)StubApplication解密并且加载原始的DEX文件(Payload)。
4)StubApplication从原始的DEX文件(Payload)中找到原始的Application对象,创建并初始化。
5)借助Java反射机制,将系统内所有对StubApplication对象的引用使用替换成原始Application。
6)由Android系统进行其他组件的正常生命周期管理。
缺陷:第二代加固技术不会将解密后的DEX存储到文件系统,但是在应用启动时要处理大量的加解密加载操作,会造成应用长时间假死(黑屏),用户体验差。相对于第一代技术没有本质区别,虽然能防止第一代加固技术文件必须落地被复制的缺陷,但是逆向工作者也可以从内存中找到DEX文件,通过GDB等调试工具,相对于第一代加固提高了DEX获取的成本。
3.3 第三代加固:指令抽离
原理➪ 由于前两代加固技术均是对文件级别进行加密,DEX文件在内存中的Payload是连续的,可以被攻击者轻易获取。第三代加固技术保护级别深入到了函数级别。发布阶段将原始DEX内的函数内容(Code Item)清除,单独写入到一个文件中,App启动后,在运行阶段将函数内容重新恢复到对应的函数体。
缺陷:指令抽离技术使用了大量的虚拟内部结构与未被文档公开的特性,再加上Android复杂的厂商定制,给DEX文件窃取带来了很大的难度。但是指令抽离技术的某些方案与虚拟机的JIT性能优化冲突,无法达到最佳的运行性能。攻击者可以通过自定义Android虚拟机,在解释器的代码上记录每一个函数的内容(CodeItem),通过遍历触发所有函数,从而获取到全部的函数内容,最终重新组装成一个完整的DEX文件。目前已经有自动化工具可以指令抽离技术中脱壳。
3.4 第四代加固:指令转换
原理➪ 第四代加固技术提供了函数级别的级别的保护,但是攻击者借助Android虚拟机内的解释器执行代码,依然可以获取到DEX文件,第四代加固技术使用自定义的的解释器来替代标准解释器,由于自定义的解释器无法对Android系统内的其他函数进行直接调用,必须使用JAVA的JNI接口进行调用。
缺陷:指令转换技术必须通过虚拟机提供的JNI接口与虚拟机进行交互,攻击者可以直接将指令转换/VMP加固方案当作黑盒,通过自定义的JNI接口对象,对黑盒内部进行探测、记录和分析,进而得到完整DEX程序。
3.5 下一代加固:虚拟机源码保护
原理➪ 虚机源码保护加固是用虚机技术保护所有的代码,包括Java,Kotlin,C/C++,Objective-C,Swift等多种代码,具备极高的兼容性;使App得到更高安全级别的保护,运行更加稳定。虚机源码保护在App内部隔离出独立的执行环境,该核心代码的运行程序在此独立的执行环境里运行。即便App本身被破解,这部分核心代码仍然不可见。
虚机源码保护拥有独特的可变指令集,极大的提高了指令跟踪、逆向分析的难度。同时,虚机源码保护还提供了反调试能力和监控能力。虚机源码保护可以通过自身的探针感知到环境的变化,实时探测到外界对本环境的调试、注入等非正常执行流程变化,将调试动作引入程序陷阱,并发出警报,进而进行实时更新,提高安全强度。虚机源码保护加固作为当前领先的加固技术,在未来很长一段时间,能够为App提供足够强度的保护。
Part 04● 结束语 ●
Apk加固是一把双刃剑,一方面可以保护App的核心代码算法,提高破解/盗版/二次打包的难度,缓解通过代码注入/动态调试/内存注入等形式攻击;另一方面加固是改变标准执行流程,增加了许多防护机制,会影响兼容性,影响程序运行效率,增加App维护难度,部分应用市场会拒绝加壳后的应用上架。因此我们需要选择一个平衡点,不要追求一味的安全,也不要过于开放,要保护好自身的数据与知识产权。
参考文献
[1] 简书:DEX文件格式浅析,https://www.jianshu.com/p/55e107acd202,2022年10月24日.
[2] CSDN:Android APK加固原理,https://blog.csdn.net/kingwjh/article/details/128814421,2023年3月23日.